Last active
December 12, 2025 08:09
-
-
Save scturtle/3ac8798853566c4954c7350d49fb0359 to your computer and use it in GitHub Desktop.
pal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from struct import pack, unpack_from | |
| class MKFDecoder(object): | |
| def __init__(self, path=None): | |
| assert path or data | |
| with open(path, "rb") as f: | |
| self._content = memoryview(f.read()) | |
| self.count = unpack_from("<I", self._content, 0)[0] // 4 | |
| self.indexes = tuple( | |
| unpack_from("<I", self._content, i << 2)[0] for i in range(self.count) | |
| ) | |
| self.count -= 1 | |
| def __len__(self): | |
| return self.count | |
| def read(self, index): | |
| data = self._content[self.indexes[index] : self.indexes[index + 1]] | |
| return bytearray(data.tobytes()) | |
| class Palette(MKFDecoder): | |
| def __init__(self): | |
| super(Palette, self).__init__("Pat.mkf") | |
| def get_palette(self, index=0, is_night=False): | |
| data = self.read(index) | |
| assert len(data) == 768 or len(data) == 1536 # with night | |
| palette_data = bytearray() | |
| for i in range(0, 768, 3): | |
| r = data[(256 * 3 if is_night else 0) + i] << 2 | |
| g = data[(256 * 3 if is_night else 0) + i + 1] << 2 | |
| b = data[(256 * 3 if is_night else 0) + i + 2] << 2 | |
| palette_data.extend(pack("BBB", b, g, r)) | |
| return palette_data | |
| pat = Palette() | |
| class RLEDecoder(object): | |
| def __init__(self, data): | |
| self.data = data | |
| @property | |
| def width(self): | |
| return unpack_from("H", self.data, 0)[0] | |
| @property | |
| def height(self): | |
| return unpack_from("H", self.data, 2)[0] | |
| def dump_tga(self, fn): | |
| width = self.width | |
| height = self.height | |
| idx = 4 | |
| to_idx = 0 | |
| to_data = bytearray(width * height * 4) # BGRA | |
| palette = pat.get_palette(0, is_night=False) | |
| while idx < len(self.data) and to_idx < width * height: | |
| code = unpack_from("B", self.data, idx)[0] | |
| idx += 1 | |
| if code > 0x80: # transparent | |
| count = code - 0x80 | |
| to_idx += count # skip | |
| else: | |
| count = code | |
| for i in range(count): | |
| color = self.data[idx] | |
| dest_pos = to_idx * 4 | |
| to_data[to_idx * 4 + 0] = palette[color * 3] | |
| to_data[to_idx * 4 + 1] = palette[color * 3 + 1] | |
| to_data[to_idx * 4 + 2] = palette[color * 3 + 2] | |
| to_data[to_idx * 4 + 3] = 255 | |
| idx += 1 | |
| to_idx += 1 | |
| header = pack("<BBBHHBHHHHBB", 0, 0, 2, 0, 0, 0, 0, 0, width, height, 32, 0x20) | |
| with open(fn, "wb") as f: | |
| f.write(header) | |
| f.write(to_data) | |
| class SubPlace(MKFDecoder): | |
| def __init__(self, data): | |
| (self.count,) = unpack_from("H", data, 0) | |
| self._content = memoryview(data) | |
| self.indexes = [ | |
| (x << 1 if x != 0x18444 >> 1 else 0x18444 & 0xFFFF) | |
| if i < self.count | |
| else len(data) | |
| for i, x in enumerate(unpack_from("H" * (self.count + 1), self._content, 0)) | |
| ] | |
| if self.indexes[-2] <= self.indexes[-3]: | |
| self.indexes = self.indexes[:-2] + self.indexes[-1:] | |
| self.count -= 1 | |
| def __getitem__(self, index): | |
| return RLEDecoder(self.read(index)) | |
| class Data(MKFDecoder): | |
| def __init__(self): | |
| super(Data, self).__init__("Data.mkf") | |
| if __name__ == "__main__": | |
| sub = SubPlace(Data().read(9)) | |
| for i in range(len(sub)): | |
| sub[i].dump_tga(f"{i}.tga") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| import struct | |
| class PalMessages: | |
| def __init__(self, pal_path): | |
| self.pal_path = pal_path | |
| mkf_path = os.path.join(self.pal_path, 'Sss.mkf') | |
| self._index_data = self._read_mkf_subfile(mkf_path, 3) | |
| msg_path = os.path.join(self.pal_path, 'M.msg') | |
| self._message_data = open(msg_path, 'rb').read() | |
| word_path = os.path.join(self.pal_path, 'Word.dat') | |
| self._words_data = open(word_path, 'rb').read() | |
| def _read_mkf_subfile(self, mkf_file, sub_id): | |
| with open(mkf_file, 'rb') as f: | |
| f.seek(0) | |
| f.seek(sub_id * 4) | |
| offset_bytes = f.read(8) | |
| start_offset = struct.unpack('<I', offset_bytes[0:4])[0] | |
| end_offset = struct.unpack('<I', offset_bytes[4:8])[0] | |
| length = end_offset - start_offset | |
| f.seek(start_offset) | |
| return f.read(length) | |
| def get_text(self, index): | |
| pos = index * 4 | |
| start_pos = struct.unpack('<I', self._index_data[pos:pos+4])[0] | |
| end_pos = struct.unpack('<I', self._index_data[pos+4:pos+8])[0] | |
| length = end_pos - start_pos | |
| raw_msg = self._message_data[start_pos:end_pos] | |
| return raw_msg.decode('gbk', errors='replace') | |
| def get_word(self, index): | |
| record_size = 10 | |
| pos = index * record_size | |
| raw_word = self._words_data[pos : pos + record_size] | |
| return raw_word.decode('gbk') | |
| def __getitem__(self, index): | |
| return self.get_text(index) | |
| if __name__ == "__main__": | |
| pal_msg = PalMessages(pal_path=".") | |
| for i in range(13513): | |
| print(f"Text[{i}]: {repr(pal_msg[i])}") | |
| for i in range(565): | |
| print(f"Word[{i}]: {repr(pal_msg.get_word(i))}") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| sudo apt install libpulse-dev libegl-dev libxkbcommon-dev libwayland-dev |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment