-
Notifications
You must be signed in to change notification settings - Fork 7
/
GMDPy.py
107 lines (95 loc) · 3.27 KB
/
GMDPy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import struct
import sys
BIGENDIAN = False
def readUInt(file):
if BIGENDIAN:
return struct.unpack(">I",file.read(4))[0]
else:
return struct.unpack("I", file.read(4))[0]
def readUInt64(file):
if BIGENDIAN:
return struct.unpack("Q",file.read(8))[0]
else:
return struct.unpack(">Q", file.read(8))[0]
def readNullTerminatedString(file):
currentByte = file.read(1)
string = bytearray()
while(1):
if currentByte == b'':
return ""
#this is python's method of EOF
elif currentByte == b'\x00':
return string.decode('utf-8')
else:
string.extend(currentByte)
currentByte = file.read(1)
def readAtOffset(file,offset):
#print(offset)
retAdd = file.tell()
file.seek(offset)
string = readNullTerminatedString(file)
file.seek(retAdd)
return string
class LabelEntry:
def __init__(self,offset,sectionID):
self.offset = offset
self.sectionID = sectionID
def ReadGMDv2(inFile):
magic = inFile.read(4)
if BIGENDIAN:
assert magic == b"DMG\x00"
else:
assert magic == b"GMD\x00"
version = inFile.read(1)
inFile.read(3) #unknown
inFile.read(4) #language
if version == 3:
inFile.read(8) #hash + null
LabelCount = readUInt(inFile)
SectionCount = readUInt(inFile)
LabelSize = readUInt(inFile)
SectionSize = readUInt(inFile)
NameSize = readUInt(inFile)
Name = readNullTerminatedString(inFile)
entries = list()
for _ in range(LabelCount):
sectionID = readUInt(inFile)
inFile.read(12)#2 hashes (likely one for label and section) and CDCDCDCD filler
LabelOffset = readUInt64(inFile)
inFile.read(8)#"ListLink"
entries.append(LabelEntry(LabelOffset,sectionID))
for _ in range(0x100):
inFile.read(8)#legitimately don't understand what these are for
labelOffset = inFile.tell()
inFile.seek(labelOffset+LabelSize)
sections = list()
for _ in range(SectionCount):
sections.append(readNullTerminatedString(inFile))
GMDOut = dict()
for label in entries:
GMDOut[readAtOffset(inFile,labelOffset+label.offset)] = sections[label.sectionID]
return GMDOut
else:
Unkn0 = readUInt(inFile)
StringCount = readUInt(inFile)
GroupNameSize = readUInt(inFile)
Unkn = readUInt(inFile)
NameSize = readUInt(inFile)
Name = readNullTerminatedString(inFile)
Unkn2 = readUInt(inFile)
Unkn3 = readUInt(inFile)
GroupName = readNullTerminatedString(inFile)
GMDOut = dict()
for i in range(StringCount):
GMDOut[i] = readNullTerminatedString(inFile)
return GMDOut
if __name__ == "__main__":
if len(sys.argv) == 1:
sys.argv.append(r"E:\roms\3DS\DotNet 3DS Toolkit\MonsterHunter3Ultimate\RomFS\arc\ID\msg\GUI\font\Arm_eng.gmd")
for i, path in enumerate(sys.argv):
if i == 0:
continue
gmd = ReadGMDv2(open(path, 'rb'))
output = open(path.replace(".gmd", ".csv"), 'w')
output.writelines(["Key,Data\n", *[f"{key},{data}\n" for key, data in gmd.items()]])
output.close()