Big improvement to IIDX Music DB handling, getting rid of all but one magic constant in the code.
This commit is contained in:
parent
2e3e6bfbd3
commit
1c54a329fe
@ -17,6 +17,8 @@ class IIDXSong:
|
||||
"""
|
||||
Initialize a IIDX Song. Everything is self-explanatory except difficulties, which
|
||||
is a list of integers representing the difficulty for SPN, SPH, SPA, DPN, DPH, DPA.
|
||||
For IIDX 27 and above, there are 4 additional charts in the difficulties list for
|
||||
B7, L7, B14 and L14.
|
||||
"""
|
||||
self.id = songid
|
||||
self.title = title
|
||||
@ -31,7 +33,7 @@ class IIDXMusicDB:
|
||||
def __init__(self, data: bytes) -> None:
|
||||
self.__songs: Dict[int, Tuple[IIDXSong, int]] = {}
|
||||
self.__data = data
|
||||
self.__parse_db(data)
|
||||
self.__version = self.__parse_db(data)
|
||||
|
||||
def get_new_db(self) -> bytes:
|
||||
# Write out a new music DB based on any possible changes to songs
|
||||
@ -53,86 +55,115 @@ class IIDXMusicDB:
|
||||
data = copy_over(data, format_string(song.genre), offset, 128)
|
||||
data = copy_over(data, format_string(song.artist), offset, 192)
|
||||
data = copy_over(data, bytes([song.folder]), offset, 280)
|
||||
data = copy_over(data, bytes(song.difficulties), offset, 288)
|
||||
if self.__version < 27:
|
||||
# This is easy.
|
||||
data = copy_over(data, bytes(song.difficulties), offset, 288)
|
||||
elif self.__version >= 27:
|
||||
# This is gross, but I'm too lazy to do it right.
|
||||
data = copy_over(data, bytes([song.difficulties[6]]), offset, 288)
|
||||
data = copy_over(data, bytes(song.difficulties[0:3]), offset, 289)
|
||||
data = copy_over(data, bytes(song.difficulties[7:9]), offset, 292)
|
||||
data = copy_over(data, bytes(song.difficulties[3:6]), offset, 294)
|
||||
data = copy_over(data, bytes([song.difficulties[9]]), offset, 297)
|
||||
|
||||
return data
|
||||
|
||||
def __parse_db(self, data: bytes) -> None:
|
||||
def __parse_string(self, string: bytes) -> str:
|
||||
for i in range(len(string)):
|
||||
if string[i] == 0:
|
||||
string = string[:i]
|
||||
break
|
||||
|
||||
return string.decode("shift-jis")
|
||||
|
||||
def __parse_db(self, data: bytes) -> int:
|
||||
# Verify the signature
|
||||
sig = struct.unpack_from(
|
||||
"<4s",
|
||||
magic, gameversion, songcount, indexcount = struct.unpack_from(
|
||||
"<4sBxxxHHxxxx",
|
||||
data,
|
||||
0,
|
||||
)
|
||||
# Offset and difference lookup (not sure this is always right)
|
||||
gameversion = data[4]
|
||||
if gameversion == 20:
|
||||
offset = 0xA420
|
||||
leap = 0x320
|
||||
elif gameversion == 21:
|
||||
offset = 0xABF0
|
||||
leap = 0x320
|
||||
elif gameversion == 22:
|
||||
offset = 0xB3C0
|
||||
leap = 0x340
|
||||
elif gameversion == 23:
|
||||
offset = 0xBB90
|
||||
leap = 0x340
|
||||
elif gameversion == 24:
|
||||
offset = 0xC360
|
||||
leap = 0x340
|
||||
elif gameversion == 25:
|
||||
offset = 0xCB30
|
||||
leap = 0x340
|
||||
elif gameversion == 26:
|
||||
offset = 0xD300
|
||||
leap = 0x344
|
||||
else:
|
||||
|
||||
if magic != b"IIDX":
|
||||
raise Exception(f"Invalid signature '{magic}' found!")
|
||||
|
||||
# Stride lookup, which appears unfortunately hardcoded in the game DLL.
|
||||
leap = {
|
||||
20: 0x320,
|
||||
21: 0x320,
|
||||
22: 0x340,
|
||||
23: 0x340,
|
||||
24: 0x340,
|
||||
25: 0x340,
|
||||
26: 0x344,
|
||||
27: 0x52C,
|
||||
28: 0x52C,
|
||||
}.get(gameversion)
|
||||
if leap is None:
|
||||
raise Exception(f"Unsupported game version {gameversion} found!")
|
||||
|
||||
if sig[0] != b"IIDX":
|
||||
raise Exception(f"Invalid signature '{sig[0]}' found!")
|
||||
|
||||
def parse_string(string: bytes) -> str:
|
||||
for i in range(len(string)):
|
||||
if string[i] == 0:
|
||||
string = string[:i]
|
||||
break
|
||||
|
||||
return string.decode("shift-jis")
|
||||
# Skip past index nodes, which are all 16-bit integers, and past 16 byte header.
|
||||
offset = (indexcount * 2) + 0x10
|
||||
|
||||
# Load songs
|
||||
while True:
|
||||
try:
|
||||
for songid in range(songcount):
|
||||
songoffset = offset + (songid * leap)
|
||||
if gameversion < 27:
|
||||
songdata = struct.unpack_from(
|
||||
"<64s64s64s64s24xB7xBBBBBB162xH",
|
||||
data,
|
||||
offset,
|
||||
songoffset,
|
||||
)
|
||||
song = IIDXSong(
|
||||
songid=songdata[11],
|
||||
title=self.__parse_string(songdata[0]),
|
||||
english_title=self.__parse_string(songdata[1]),
|
||||
genre=self.__parse_string(songdata[2]),
|
||||
artist=self.__parse_string(songdata[3]),
|
||||
difficulties=[
|
||||
songdata[5],
|
||||
songdata[6],
|
||||
songdata[7],
|
||||
songdata[8],
|
||||
songdata[9],
|
||||
songdata[10],
|
||||
],
|
||||
folder=songdata[4],
|
||||
)
|
||||
elif gameversion >= 27:
|
||||
# Heroic Verse and above have a completely different structure for song entries
|
||||
songdata = struct.unpack_from(
|
||||
"<64s64s64s64s24xB7x10B646xH",
|
||||
data,
|
||||
songoffset,
|
||||
)
|
||||
song = IIDXSong(
|
||||
songid=songdata[15],
|
||||
title=self.__parse_string(songdata[0]),
|
||||
english_title=self.__parse_string(songdata[1]),
|
||||
genre=self.__parse_string(songdata[2]),
|
||||
artist=self.__parse_string(songdata[3]),
|
||||
difficulties=[
|
||||
songdata[6],
|
||||
songdata[7],
|
||||
songdata[8],
|
||||
songdata[11],
|
||||
songdata[12],
|
||||
songdata[13],
|
||||
songdata[5],
|
||||
songdata[9],
|
||||
songdata[10],
|
||||
songdata[14],
|
||||
],
|
||||
folder=songdata[4],
|
||||
)
|
||||
except struct.error:
|
||||
# Out of input!
|
||||
break
|
||||
|
||||
songoffset = offset
|
||||
offset = offset + leap
|
||||
song = IIDXSong(
|
||||
songdata[11],
|
||||
parse_string(songdata[0]),
|
||||
parse_string(songdata[1]),
|
||||
parse_string(songdata[2]),
|
||||
parse_string(songdata[3]),
|
||||
[
|
||||
songdata[5],
|
||||
songdata[6],
|
||||
songdata[7],
|
||||
songdata[8],
|
||||
songdata[9],
|
||||
songdata[10],
|
||||
],
|
||||
songdata[4],
|
||||
)
|
||||
if song.artist == "event_data" and song.genre == "event_data":
|
||||
continue
|
||||
self.__songs[songdata[11]] = (song, songoffset)
|
||||
|
||||
self.__songs[song.id] = (song, songoffset)
|
||||
|
||||
return gameversion
|
||||
|
||||
@property
|
||||
def songs(self) -> List[IIDXSong]:
|
||||
|
Loading…
x
Reference in New Issue
Block a user