mirror of
https://github.com/Escartem/AnimeWwise.git
synced 2026-06-04 23:40:25 +08:00
214 lines
6.2 KiB
Python
214 lines
6.2 KiB
Python
# reader for the .map format i've made to improve reading speed and mapping size
|
|
import io
|
|
import json
|
|
from filereader import FileReader
|
|
|
|
|
|
class Mapper:
|
|
def __init__(self, mapping_file):
|
|
|
|
### NORMAL MAP LOADING ###
|
|
|
|
self.load_data(mapping_file)
|
|
self.load_map("MAP")
|
|
|
|
# you can use the following to load your own mapping instead of the official one
|
|
# for both cases, comment the 2 lines in the normal mode and uncomment the 2 in your mode
|
|
# and for both make sure of the following
|
|
#
|
|
# key -> the hashed path
|
|
# value -> without the .wem extension, by default the language is also removed and
|
|
# obtained back via addLang param but you can keep it in the path
|
|
#
|
|
# example : 3fe302b037275600 -> voice\\chapter4\\76\\player\\chapter4_76_player_118_f
|
|
|
|
### JSON MAP LOADING ###
|
|
|
|
# make sure the json is in the format {hash: path}
|
|
# format the path to use *double backward slashes*
|
|
#
|
|
# example
|
|
# {"3fe302b037275600": "voice\\chapter4\\76\\player\\chapter4_76_player_118_f"}
|
|
|
|
# self.load_data("maps/yourmap.json")
|
|
# self.load_map("JSON")
|
|
|
|
### TSV MAP LOADING ###
|
|
|
|
# make sure it is in the format "hash \t path"
|
|
# format the path to use *single backward slashes*
|
|
#
|
|
# example
|
|
# 3fe302b037275600 voice\chapter4\76\player\chapter4_76_player_118_f
|
|
|
|
# self.load_data("maps/yourmap.tsv")
|
|
# self.load_map("TSV")
|
|
|
|
def load_map(self, mode):
|
|
self.mode = mode
|
|
|
|
if self.mode == "MAP":
|
|
self.process_map()
|
|
elif self.mode == "JSON":
|
|
self.keys = json.loads(self.data)
|
|
elif self.mode == "TSV":
|
|
self.keys = {
|
|
parts[0]:parts[1]
|
|
for e in self.data.decode("utf-8").splitlines()
|
|
if (parts := e.split("\t")) and len(parts) >= 2}
|
|
|
|
def load_data(self, file):
|
|
file = open(file, "rb")
|
|
self.data = file.read()
|
|
file.close()
|
|
|
|
def process_map(self):
|
|
reader = FileReader(io.BytesIO(self.data), "little")
|
|
|
|
# check file
|
|
if reader.ReadBytes(4) != b"ESFM":
|
|
raise Exception("mapping was invalid")
|
|
|
|
reader.ReadBytes(2)
|
|
|
|
map_version = reader.ReadBytes(2)
|
|
if map_version != b"\x33\x31":
|
|
print(f"Error: you are using an unknown / unsupported version of the mapping that is no longer supported, please use a newer one or download an older version of this tool.")
|
|
raise Exception("incompatible mapping")
|
|
|
|
reader.ReadBytes(2)
|
|
|
|
games = {
|
|
"hk4e": "Genshin",
|
|
"hkrpg": "Star Rail",
|
|
"nap": "Zenless Zone Zero",
|
|
"beyond": "Arknights Endfield"
|
|
# more later
|
|
}
|
|
|
|
# read config
|
|
game_size = reader.ReadInt8()
|
|
game = reader.ReadBytes(game_size).decode("utf-8")
|
|
|
|
infos = {
|
|
"game": games[game],
|
|
"version": ".".join(list(str(reader.ReadInt8())))
|
|
}
|
|
|
|
print(f"> Loading mapping for {infos['game']} v{infos['version']}, this may take a few seconds...")
|
|
|
|
# sectors
|
|
def int24():
|
|
val = reader.ReadBytes(3)
|
|
if val == b"\xFF" * 3:
|
|
val = reader.ReadBytes(4)
|
|
return int.from_bytes(val, "big")
|
|
|
|
names = ["languages", "strings", "words", "files", "keys", "music"]
|
|
sectors = {n: [int24(), int24()] for n in names}
|
|
if list(sectors.values())[0][0] == 0:
|
|
offset = reader.GetBufferPos()
|
|
for v in sectors.values():
|
|
v[0] += offset
|
|
|
|
# languages
|
|
reader.SetBufferPos(sectors["languages"][0])
|
|
self.languages = []
|
|
|
|
n_langs = reader.ReadInt8()
|
|
for i in range(n_langs):
|
|
size = reader.ReadInt8()
|
|
name = bytes([b ^ (0x97 + size) for b in reader.ReadBytes(size)]).decode("utf-8")
|
|
self.languages.append(name)
|
|
|
|
# alloc sectors
|
|
reader.SetBufferPos(sectors["strings"][0])
|
|
self.strings = bytearray(reader.ReadBytes(sectors["strings"][1]))
|
|
reader.SetBufferPos(sectors["words"][0])
|
|
self.words = bytearray(reader.ReadBytes(sectors["words"][1]))
|
|
reader.SetBufferPos(sectors["files"][0])
|
|
self.files = bytearray(reader.ReadBytes(sectors["files"][1]))
|
|
|
|
# read keys
|
|
reader.SetBufferPos(sectors["keys"][0])
|
|
key_size = reader.ReadInt8()
|
|
n_keys = (sectors["keys"][1]-1) // key_size
|
|
n_files = n_keys // n_langs
|
|
|
|
keys_data = bytearray(reader.ReadBytes(sectors["keys"][1]-1))
|
|
self.keys = {keys_data[i+3:i+key_size].hex(): int.from_bytes(keys_data[i:i+3], "big") for i in range(0, len(keys_data), key_size)}
|
|
|
|
# music
|
|
self.music_keys = {}
|
|
hasMusic = sectors["music"][1] > 0
|
|
|
|
if hasMusic:
|
|
reader.SetBufferPos(sectors["music"][0])
|
|
root_size = reader.ReadInt8()
|
|
root = reader.ReadBytes(root_size).decode("utf-8")
|
|
n_music = int.from_bytes(reader.ReadBytes(2), "big")
|
|
|
|
for i in range(n_music):
|
|
key = int.from_bytes(reader.ReadBytes(4), "big")
|
|
name_size = reader.ReadInt8()
|
|
name = bytes([b ^ (0x97 + name_size) for b in reader.ReadBytes(name_size)]).decode("utf-8")
|
|
self.music_keys[str(key)] = f"{root}\\{name}"
|
|
|
|
# done
|
|
print(f"> Finished loading mapping")
|
|
print(f"=-=-= Voicelines sector =-=-=")
|
|
print(f": {n_langs} languages")
|
|
print(f": {n_files} mapped files")
|
|
print(f": {n_keys} keys")
|
|
if hasMusic:
|
|
print(f": {n_music} musics")
|
|
|
|
def get_key(self, key, addLang=False):
|
|
if self.mode == "MAP":
|
|
if (not key in self.keys) and (not key in self.music_keys):
|
|
return None
|
|
|
|
if key in self.music_keys:
|
|
return [self.music_keys[key], ""]
|
|
|
|
lang, offset = (self.keys[key] >> 22) & 0x03, self.keys[key] & 0x3FFFFF
|
|
|
|
parts = int.from_bytes(self.files[offset:offset+1], "big")
|
|
name = []
|
|
|
|
for i in range(parts):
|
|
word_offset = int.from_bytes(self.files[offset+1+(3*i):offset+4+(3*i)], "big")
|
|
word_parts = int.from_bytes(self.words[word_offset:word_offset+1], "big")
|
|
word = []
|
|
|
|
for j in range(word_parts):
|
|
string_offset = int.from_bytes(self.words[word_offset+1+(2*j):word_offset+3+(2*j)], "big")
|
|
string_size = int.from_bytes(self.strings[string_offset:string_offset+1], "big")
|
|
if string_size > 128:
|
|
string = str(int.from_bytes(self.strings[string_offset+1:string_offset+1+(string_size-128)], "big"))
|
|
else:
|
|
string = bytes([b ^ (0x97 + string_size) for b in self.strings[string_offset+1:string_offset+1+string_size]]).decode("utf-8")
|
|
word.append(string)
|
|
|
|
word = "_".join(word)
|
|
name.append(word)
|
|
|
|
name = ["\\".join(name)]
|
|
|
|
if addLang:
|
|
name.append(self.languages[lang])
|
|
|
|
return name
|
|
elif self.mode in ["JSON", "TSV"]:
|
|
if not key in self.keys:
|
|
return None
|
|
return [self.keys[key]]
|
|
|
|
def reset(self):
|
|
self.reader = None
|
|
self.languages.clear()
|
|
self.strings.clear()
|
|
self.words.clear()
|
|
self.files.clear()
|
|
self.music_keys.clear()
|