mirror of https://github.com/zeldaret/tp.git
183 lines
5.7 KiB
Python
183 lines
5.7 KiB
Python
from dataclasses import dataclass, field
|
|
from typing import List
|
|
|
|
from ..builder import AsyncBuilder
|
|
from .. import util
|
|
from ..types import *
|
|
from .base import *
|
|
from .identifier import *
|
|
|
|
def string_to_cstr(data):
|
|
return "".join(data)
|
|
|
|
|
|
def escape_char(v):
|
|
if v == "\n":
|
|
return "\\n"
|
|
elif v == "\t":
|
|
return "\\t"
|
|
elif v == "\v":
|
|
return "\\v"
|
|
elif v == "\b":
|
|
return "\\b"
|
|
elif v == "\r":
|
|
return "\\r"
|
|
elif v == "\f":
|
|
return "\\f"
|
|
elif v == "\a":
|
|
return "\\a"
|
|
elif v == "\\":
|
|
return "\\\\"
|
|
elif v == "\"":
|
|
return "\\\""
|
|
elif ord(v) < 32:
|
|
return "\"\"\\x%02X\"\"" % ord(v)
|
|
else:
|
|
return v
|
|
|
|
|
|
def escape_char_hard(v):
|
|
return "\"\"\\x%02X\"\"" % v
|
|
|
|
|
|
def escape_string(data):
|
|
return [escape_char(x) for x in data]
|
|
|
|
|
|
def escape_char_hex(v):
|
|
if v == 0:
|
|
return "\\0"
|
|
return "\\x%02X" % v
|
|
|
|
|
|
def escape_full_string(data):
|
|
return [escape_char_hex(x) for x in data]
|
|
|
|
|
|
@dataclass(eq=False)
|
|
class String(ArbitraryData):
|
|
encoding: str = None
|
|
decoded_string: str = None
|
|
string_base: "StringBase" = None
|
|
|
|
def array_type(self):
|
|
return self.element_type()
|
|
|
|
def asm_reference(self, addr):
|
|
if self.string_base:
|
|
return self.string_base.asm_reference(addr)
|
|
else:
|
|
return super().asm_reference(addr)
|
|
|
|
def cpp_reference(self, accessor, addr):
|
|
if self.string_base:
|
|
return self.string_base.cpp_reference(accessor, addr)
|
|
else:
|
|
return super().cpp_reference(accessor, addr)
|
|
|
|
async def export_declaration(self, exporter, builder: AsyncBuilder, force_active=True):
|
|
if force_active:
|
|
await builder.write("#pragma push")
|
|
await builder.write("#pragma force_active on")
|
|
sjis = self.decoded_string.encode("shift_jisx0213")
|
|
if 0x5c in sjis:
|
|
await builder.write("// MWCC ignores mapping of some japanese characters using the ")
|
|
await builder.write("// byte 0x5C (ASCII '\\'). This is why this string is hex-encoded.")
|
|
data = escape_full_string(sjis)
|
|
else:
|
|
data = escape_string(self.decoded_string)
|
|
|
|
await builder.write_nonewline("SECTION_DEAD ")
|
|
if self.is_static:
|
|
await self.export_static(builder)
|
|
await builder.write_nonewline(self.array_type().decl(self.identifier.label))
|
|
await String.export_string(builder, data)
|
|
|
|
if self.padding > 0:
|
|
assert len(self.padding_data) == self.padding
|
|
assert self.padding_data[-1] == 0
|
|
data = escape_full_string(self.padding_data[:-1])
|
|
await builder.write("/* @stringBase0 padding */")
|
|
await builder.write_nonewline("SECTION_DEAD ")
|
|
await builder.write_nonewline("static ")
|
|
await builder.write_nonewline(self.array_type().decl(f"pad_{self.end:08X}"))
|
|
await String.export_string(builder, data)
|
|
if force_active:
|
|
await builder.write("#pragma pop")
|
|
|
|
@staticmethod
|
|
async def export_string(builder: AsyncBuilder, data: List[str]):
|
|
if len(data) < 32:
|
|
await builder.write(f" = \"{string_to_cstr(data)}\";")
|
|
else:
|
|
await builder.write(f" = ")
|
|
data_chunks = util.chunks(data, 48)
|
|
|
|
lines = []
|
|
for chunk in data_chunks:
|
|
lines += [f" \"{string_to_cstr(chunk)}\""]
|
|
lines[-1] += ";"
|
|
|
|
for line in lines:
|
|
await builder.write(line)
|
|
|
|
|
|
@dataclass(eq=False)
|
|
class StringBase(ArbitraryData):
|
|
strings: List[Symbol] = field(default_factory=list, repr=False)
|
|
|
|
def valid_reference(self, addr):
|
|
return addr >= self.start and addr < self.end
|
|
|
|
def array_type(self):
|
|
return self.element_type()
|
|
|
|
@property
|
|
def is_static(self):
|
|
# having @stringBase0 as satatic would remove the forward reference to the lcf generated symbol,
|
|
# this will not compile correctly.
|
|
return False
|
|
|
|
|
|
def set_mlts(self, module: int, library: str, translation_unit: str, section: str):
|
|
super().set_mlts(module, library, translation_unit, section)
|
|
for string in self.strings:
|
|
string.set_mlts(module, library, translation_unit, section)
|
|
|
|
async def export_declaration(self, exporter, builder: AsyncBuilder):
|
|
pass
|
|
"""
|
|
await builder.write("#pragma push")
|
|
await builder.write("#pragma force_active on")
|
|
for string in self.strings:
|
|
# if the @stringBase0 is static (which it will almost always be), setup
|
|
# so that the sub-strings are static.
|
|
if self.is_static:
|
|
string.reference_count.make_static()
|
|
await string.export_declaration(exporter, builder)
|
|
|
|
if self.padding > 0:
|
|
assert len(self.padding_data) == self.padding
|
|
assert self.padding_data[-1] == 0
|
|
data = escape_full_string(self.padding_data[:-1])
|
|
await builder.write("/* @stringBase0 padding */")
|
|
await builder.write_nonewline("SECTION_DEAD ")
|
|
await builder.write_nonewline("static ")
|
|
await builder.write_nonewline(self.array_type().decl(f"pad_{self.end:08X}"))
|
|
await String.export_string(builder, data)
|
|
await builder.write("#pragma pop")
|
|
"""
|
|
|
|
@staticmethod
|
|
def create(symbol, strings, data, padding_data):
|
|
return StringBase(
|
|
Identifier("stringBase", symbol.addr, symbol.name),
|
|
symbol.addr,
|
|
symbol.size,
|
|
data=data,
|
|
data_type=PointerType(ConstType(CHAR)),
|
|
padding=len(padding_data),
|
|
padding_data=padding_data,
|
|
strings=strings)
|
|
|