-
Notifications
You must be signed in to change notification settings - Fork 0
/
SaveFileHack.py
200 lines (164 loc) · 5.75 KB
/
SaveFileHack.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/usr/bin/python3
# Code created by Zephiles
import binascii
import sys
# stringToInt taken from text_to_bits from here:
# https://stackoverflow.com/questions/7396849/convert-binary-to-ascii-and-vice-versa
def stringToInt(string, encoding="utf-8", errors="surrogatepass"):
bits = bin(int(binascii.hexlify(string.encode(encoding, errors)), 16))[2:]
return int(bits.zfill(8 * ((len(bits) + 7) // 8)), 2)
def verifyVersionString(string):
if string == "eu0":
return True
elif string == "eu1":
return True
elif string == "jp0":
return True
elif string == "jp1":
return True
elif string == "kr0":
return True
elif string == "us0":
return True
elif string == "us1":
return True
elif string == "us2":
return True
else:
return False
# Make sure something was passed in
if len(sys.argv) < 2:
input("You must pass in a proper SPM save file. Press Enter to close this window.")
sys.exit("")
# Check if the version number was passed in
VersionString = ""
if len(sys.argv) < 3:
# Prompt for the version number to use
while (VersionString == ""):
VersionString = input("Enter the version of the game to hack\n(eu0, eu1, jp0, jp1, kr0, us0, us1, us2): ")
# Make sure the input is valid
if (not verifyVersionString(VersionString)):
VersionString = ""
else:
VersionString = sys.argv[2]
# Make sure the input is valid
if (not verifyVersionString(VersionString)):
VersionString = ""
# Prompt for the version number to use
while (VersionString == ""):
VersionString = input("Enter the version of the game to hack\n(eu0, eu1, jp0, jp1, kr0, us0, us1, us2): ")
# Make sure the input is valid
if (not verifyVersionString(VersionString)):
VersionString = ""
# Set version-specific values
if (VersionString == "eu0") or (VersionString == "eu1"):
InitAsmFunctionPointer = 0x80526294
TextBufferPointerOffset = 0x1B0
TextBufferPointer = 0x805256FC
BinVersion = "EU"
ItemId = 0x6E59
elif VersionString == "jp0":
InitAsmFunctionPointer = 0x804B8594
TextBufferPointerOffset = 0x17C
TextBufferPointer = 0x804B79C8
BinVersion = "JP_0"
ItemId = 0x6D0A
elif VersionString == "jp1":
InitAsmFunctionPointer = 0x804B9B94
TextBufferPointerOffset = 0x174
TextBufferPointer = 0x804B8FC0
BinVersion = "JP_1"
ItemId = 0x6D24
elif VersionString == "kr0":
InitAsmFunctionPointer = 0x8055DBF4
TextBufferPointerOffset = 0x170
TextBufferPointer = 0x8055D01C
BinVersion = "KR"
ItemId = 0x70D9
elif VersionString == "us0":
InitAsmFunctionPointer = 0x804E3294
TextBufferPointerOffset = 0x1A4
TextBufferPointer = 0x804E26F0
BinVersion = "US_0"
ItemId = 0x6D08
elif VersionString == "us1":
InitAsmFunctionPointer = 0x804E4B14
TextBufferPointerOffset = 0x1AC
TextBufferPointer = 0x804E3F78
BinVersion = "US_1"
ItemId = 0x6D26
elif VersionString == "us2":
InitAsmFunctionPointer = 0x804E4C94
TextBufferPointerOffset = 0x174
TextBufferPointer = 0x804E40C0
BinVersion = "US_2"
ItemId = 0x6D24
f = open(sys.argv[1], "r+b")
# Clear all of the bytes in the file
f.seek(0, 0)
f.write((0).to_bytes(0x25B0, byteorder="big", signed=False))
# Set the default efb width and height, as they can apparently effect being able to open the item menu
f.seek(0x20, 0)
f.write((0x26001E0).to_bytes(4, byteorder="big", signed=False))
# Write the new file name
FileNameString = "REL Loader\0"
f.seek(0x28, 0)
f.write(stringToInt(FileNameString).to_bytes(len(FileNameString), byteorder="big", signed=False))
# Write the map name
MapNameString = "dos_01\0"
f.seek(0x4C, 0)
f.write(stringToInt(MapNameString).to_bytes(len(MapNameString), byteorder="big", signed=False))
# Set Mario's level to 1, to prevent leveling up immediately
f.seek(0x1B14, 0)
f.write((1).to_bytes(4, byteorder="big", signed=False))
# Set the flip timer to 10 to prevent counting up immediately
f.seek(0x1B24, 0)
f.write((10).to_bytes(4, byteorder="big", signed=False))
# Write the item id
f.seek(0x1B70, 0)
f.write(ItemId.to_bytes(2, byteorder="big", signed=False))
# Write the pointer to text buffer
f.seek(TextBufferPointerOffset, 0)
f.write(TextBufferPointer.to_bytes(4, byteorder="big", signed=False))
# Write the text buffer
f.seek(TextBufferPointerOffset + 0x4, 0)
for i in range(0x94):
f.write((0x33).to_bytes(1, byteorder="big", signed=False))
# Write the pointer to the init asm function
f.seek(TextBufferPointerOffset + 0x98, 0)
f.write(InitAsmFunctionPointer.to_bytes(4, byteorder="big", signed=False))
# Write the init asm function
# The init function is the same for all versions except for Korean
if VersionString == "kr0":
InitAsmFuncBinName = "Init_KR"
else:
InitAsmFuncBinName = "Init_Main"
InitAsmFuncOffset = 0xD4C
g = open("bin/" + InitAsmFuncBinName + ".bin", "rb")
# Perform the write
Func = g.read()
f.seek(InitAsmFuncOffset, 0)
for b in Func:
f.write(b.to_bytes(1, byteorder="big", signed=False))
g.close()
# Write the main asm function
g = open("bin/Main_" + BinVersion + ".bin", "rb")
# Perform the write
Func = g.read()
f.seek(InitAsmFuncOffset + 0x24, 0)
for b in Func:
f.write(b.to_bytes(1, byteorder="big", signed=False))
g.close()
# Get the sum of the bytes for the data field
f.seek(0x8, 0)
DataFieldSum = 0x3FC
DataField = f.read(0x25A8)
for b in DataField:
DataFieldSum += b
# Set the checksum of the bytes for the data field
f.seek(0x25B0, 0)
f.write(DataFieldSum.to_bytes(4, byteorder="big", signed=False))
# Set the inverted checksum of the bytes for the data field
f.seek(0x25B4, 0)
f.write((~DataFieldSum).to_bytes(4, byteorder="big", signed=True))
f.close()