1 #include <miniz.h>
2 #include "n64.h"
3 #include "n64_analyzer.h"
4
5 // https://level42.ca/projects/ultra64/Documentation/man/pro-man/pro09/index9.3.html
6 // http://en64.shoutwiki.com/wiki/ROM#Cartridge_ROM_Header
7
8 #define N64_KUSEG_START_ADDR 0x00000000 // TLB mapped
9 #define N64_KUSEG_SIZE 0x7fffffff
10
11 #define N64_KSEG0_START_ADDR 0x80000000 // Direct mapped, cached
12 #define N64_KSEG0_SIZE 0x1FFFFFFF
13
14 #define N64_KSEG1_START_ADDR 0xa0000000 // Direct mapped, uncached
15 #define N64_KSEG1_SIZE 0x1FFFFFFF
16
17 #define N64_KSSEG_START_ADDR 0xc0000000 // TLB mapped
18 #define N64_KSSEG_SIZE 0x1FFFFFFF
19
20 #define N64_KSEG3_START_ADDR 0xe0000000 // TLB mapped
21 #define N64_KSEG3_SIZE 0x1FFFFFFF
22
23 #define N64_SEGMENT_AREA(name) N64_##name##_START_ADDR, N64_##name##_SIZE
24
25 #define N64_MAGIC_BS 0x37804012
26 #define N64_MAGIC_BE 0x80371240
27 #define N64_MAGIC_LE 0x40123780
28 #define N64_MAGIC_BE_B1 0x80
29 #define N64_MAGIC_BS_B1 0x37
30 #define N64_MAGIC_LE_B1 0x40
31
32 namespace REDasm {
33
LOADER_PLUGIN_TEST(N64Loader,N64RomHeader)34 LOADER_PLUGIN_TEST(N64Loader, N64RomHeader)
35 {
36 u32 magic = header->magic;
37
38 if((magic != N64_MAGIC_BS) && (magic != N64_MAGIC_BE) && (magic != N64_MAGIC_LE))
39 return false;
40
41 MemoryBuffer swappedbuffer;
42
43 if(magic != N64_MAGIC_BE)
44 {
45 Buffer::swapEndianness<u16>(request.view.buffer(), &swappedbuffer, sizeof(N64RomHeader)); // Swap the header
46 header = static_cast<const N64RomHeader*>(swappedbuffer);
47 }
48
49 if(!N64Loader::checkMediaType(header) || !N64Loader::checkCountryCode(header))
50 return false;
51
52 if(!swappedbuffer.empty()) // Swap all
53 {
54 Buffer::swapEndianness<u16>(request.view.buffer(), &swappedbuffer);
55 header = static_cast<const N64RomHeader*>(swappedbuffer);
56
57 BufferView swappedview = swappedbuffer.view();
58 return N64Loader::checkChecksum(header, swappedview);
59 }
60
61 return N64Loader::checkChecksum(header, request.view);
62 }
63
N64Loader(AbstractBuffer * buffer)64 N64Loader::N64Loader(AbstractBuffer *buffer): LoaderPluginT<N64RomHeader>(buffer) { }
assembler() const65 std::string N64Loader::assembler() const { return "mips64be"; }
createAnalyzer(DisassemblerAPI * disassembler) const66 Analyzer *N64Loader::createAnalyzer(DisassemblerAPI *disassembler) const { return new N64Analyzer(disassembler); }
67
load()68 void N64Loader::load()
69 {
70 if(m_header->magic_0 != N64_MAGIC_BE_B1)
71 Buffer::swapEndianness<u16>(m_buffer.get());
72
73 m_document->segment("KSEG0", N64_ROM_HEADER_SIZE, this->getEP(), m_buffer->size() - N64_ROM_HEADER_SIZE, SegmentType::Code | SegmentType::Data);
74 // TODO: map other segments
75 m_document->entry(this->getEP());
76 }
77
getEP()78 u32 N64Loader::getEP()
79 {
80 u32be pc = m_header->program_counter;
81 u32 cic_version = N64Loader::getCICVersion(m_header);
82
83 if(cic_version != 0)
84 {
85 if(cic_version == 6103) // CIC 6103 EP manipulation
86 pc -= 0x100000;
87 else if (cic_version == 6106) // CIC 6106 EP manipulation
88 pc -= 0x200000;
89 }
90
91 return pc;
92 }
93
calculateChecksum(const N64RomHeader * header,const BufferView & view,u32 * crc)94 u32 N64Loader::calculateChecksum(const N64RomHeader* header, const BufferView &view, u32 *crc) // Adapted from n64crc (http://n64dev.org/n64crc.html)
95 {
96 u32 bootcode, i, seed, t1, t2, t3, t4, t5, t6, r;
97 u32 d;
98
99 if(!N64Loader::getBootcodeAndSeed(header, &bootcode, &seed))
100 return 1;
101
102 t1 = t2 = t3 = t4 = t5 = t6 = seed;
103 i = N64_ROM_CHECKSUM_START;
104
105 while (i < (N64_ROM_CHECKSUM_START + N64_ROM_CHECKSUM_LENGTH))
106 {
107 d = static_cast<u32be>(view + i);
108
109 if((t6 + d) < t6)
110 t4++;
111
112 t6 += d;
113 t3 ^= d;
114 r = REDasm::rol(d, (d & 0x1F));
115 t5 += r;
116 if (t2 > d) t2 ^= r;
117 else t2 ^= t6 ^ d;
118
119 if(bootcode == 6105)
120 t1 += static_cast<u32be>(view + (N64_ROM_HEADER_SIZE + 0x0710 + (i & 0xFF))) ^ d;
121 else
122 t1 += t5 ^ d;
123
124 i += 4;
125 }
126
127 if(bootcode == 6103)
128 {
129 crc[0] = (t6 ^ t4) + t3;
130 crc[1] = (t5 ^ t2) + t1;
131 }
132 else if(bootcode == 6106)
133 {
134 crc[0] = (t6 * t4) + t3;
135 crc[1] = (t5 * t2) + t1;
136 }
137 else
138 {
139 crc[0] = t6 ^ t4 ^ t3;
140 crc[1] = t5 ^ t2 ^ t1;
141 }
142
143 return 0;
144 }
145
checkChecksum(const N64RomHeader * header,const BufferView & view)146 bool N64Loader::checkChecksum(const N64RomHeader *header, const BufferView &view)
147 {
148 u32 crc[2] = { 0 };
149
150 if(!N64Loader::calculateChecksum(header, view, crc))
151 {
152 if((crc[0] == header->crc1) && (crc[1] == header->crc2))
153 return true;
154 }
155
156 return false;
157 }
158
getBootcodeAndSeed(const N64RomHeader * header,u32 * bootcode,u32 * seed)159 bool N64Loader::getBootcodeAndSeed(const N64RomHeader *header, u32 *bootcode, u32 *seed)
160 {
161 switch((*bootcode = N64Loader::getCICVersion(header)))
162 {
163 case 6101:
164 case 7102:
165 case 6102:
166 *seed = N64_ROM_CHECKSUM_CIC_6102;
167 break;
168
169 case 6103:
170 *seed = N64_ROM_CHECKSUM_CIC_6103;
171 break;
172
173 case 6105:
174 *seed = N64_ROM_CHECKSUM_CIC_6105;
175 break;
176
177 case 6106:
178 *seed = N64_ROM_CHECKSUM_CIC_6106;
179 break;
180
181 default:
182 *seed = 0;
183 return false;
184 }
185
186 return true;
187 }
188
getCICVersion(const N64RomHeader * header)189 u32 N64Loader::getCICVersion(const N64RomHeader* header)
190 {
191 u64 bootcodecrc = crc32(0L, reinterpret_cast<const u8*>(header->boot_code), N64_BOOT_CODE_SIZE);
192 u32 cic = 0;
193
194 switch(bootcodecrc)
195 {
196 case N64_BOOT_CODE_CIC_6101_CRC:
197 cic = 6101;
198 break;
199
200 case N64_BOOT_CODE_CIC_7102_CRC:
201 cic = 7102;
202 break;
203
204 case N64_BOOT_CODE_CIC_6102_CRC:
205 cic = 6102;
206 break;
207
208 case N64_BOOT_CODE_CIC_6103_CRC:
209 cic = 6103;
210 break;
211
212 case N64_BOOT_CODE_CIC_6105_CRC:
213 cic = 6105;
214 break;
215
216 case N64_BOOT_CODE_CIC_6106_CRC:
217 cic = 6106;
218 break;
219
220 default:
221 break;
222 }
223
224 return cic;
225 }
226
checkMediaType(const N64RomHeader * header)227 bool N64Loader::checkMediaType(const N64RomHeader *header)
228 {
229 switch(header->media_format[3])
230 {
231 case 'N': // Cart
232 case 'D': // 64DD disk
233 case 'C': // Cartridge part of expandable game
234 case 'E': // 64DD expansion for cart
235 case 'Z': // Aleck64 cart
236 return true;
237
238 default:
239 break;
240 }
241
242 return false;
243 }
244
checkCountryCode(const N64RomHeader * header)245 bool N64Loader::checkCountryCode(const N64RomHeader *header)
246 {
247 /*
248 * 0x37 '7' "Beta"
249 * 0x41 'A' "Asian (NTSC)"
250 * 0x42 'B' "Brazilian"
251 * 0x43 'C' "Chinese"
252 * 0x44 'D' "German"
253 * 0x45 'E' "North America"
254 * 0x46 'F' "French"
255 * 0x47 'G': Gateway 64 (NTSC)
256 * 0x48 'H' "Dutch"
257 * 0x49 'I' "Italian"
258 * 0x4A 'J' "Japanese"
259 * 0x4B 'K' "Korean"
260 * 0x4C 'L': Gateway 64 (PAL)
261 * 0x4E 'N' "Canadian"
262 * 0x50 'P' "European (basic spec.)"
263 * 0x53 'S' "Spanish"
264 * 0x55 'U' "Australian"
265 * 0x57 'W' "Scandinavian"
266 * 0x58 'X' "European"
267 * 0x59 'Y' "European"
268 */
269
270 if(header->country_code == 0x37)
271 return true;
272 if((header->country_code >= 0x41) && (header->country_code <= 0x4C))
273 return true;
274 if((header->country_code == 0x4E) || (header->country_code == 0x50))
275 return true;
276 if((header->country_code == 0x53) || (header->country_code == 0x55))
277 return true;
278 if((header->country_code >= 0x57) && (header->country_code <= 0x59))
279 return true;
280
281 return false;
282 }
283
284 }
285