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