Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/quicknes/nes_emu/Nes_Cart.cpp
2 views
1
2
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
3
4
#include "Nes_Cart.h"
5
6
#include <stdlib.h>
7
#include <string.h>
8
9
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
10
can redistribute it and/or modify it under the terms of the GNU Lesser
11
General Public License as published by the Free Software Foundation; either
12
version 2.1 of the License, or (at your option) any later version. This
13
module is distributed in the hope that it will be useful, but WITHOUT ANY
14
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
16
more details. You should have received a copy of the GNU Lesser General
17
Public License along with this module; if not, write to the Free Software
18
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
19
20
#include "blargg_source.h"
21
22
char const Nes_Cart::not_ines_file [] = "Not an iNES file";
23
24
Nes_Cart::Nes_Cart()
25
{
26
prg_ = NULL;
27
chr_ = NULL;
28
clear();
29
}
30
31
Nes_Cart::~Nes_Cart()
32
{
33
clear();
34
}
35
36
void Nes_Cart::clear()
37
{
38
free( prg_ );
39
prg_ = NULL;
40
41
free( chr_ );
42
chr_ = NULL;
43
44
prg_size_ = 0;
45
chr_size_ = 0;
46
mapper = 0;
47
}
48
49
long Nes_Cart::round_to_bank_size( long n )
50
{
51
n += bank_size - 1;
52
return n - n % bank_size;
53
}
54
55
blargg_err_t Nes_Cart::resize_prg( long size )
56
{
57
if ( size != prg_size_ )
58
{
59
// padding allows CPU to always read operands of instruction, which
60
// might go past end of data
61
void* p = realloc( prg_, round_to_bank_size( size ) + 2 );
62
CHECK_ALLOC( p || !size );
63
prg_ = (byte*) p;
64
prg_size_ = size;
65
}
66
return 0;
67
}
68
69
blargg_err_t Nes_Cart::resize_chr( long size )
70
{
71
if ( size != chr_size_ )
72
{
73
void* p = realloc( chr_, round_to_bank_size( size ) );
74
CHECK_ALLOC( p || !size );
75
chr_ = (byte*) p;
76
chr_size_ = size;
77
}
78
return 0;
79
}
80
81
// iNES reading
82
83
struct ines_header_t {
84
BOOST::uint8_t signature [4];
85
BOOST::uint8_t prg_count; // number of 16K PRG banks
86
BOOST::uint8_t chr_count; // number of 8K CHR banks
87
BOOST::uint8_t flags; // MMMM FTBV Mapper low, Four-screen, Trainer, Battery, V mirror
88
BOOST::uint8_t flags2; // MMMM --XX Mapper high 4 bits
89
BOOST::uint8_t zero [8]; // if zero [7] is non-zero, treat flags2 as zero
90
};
91
BOOST_STATIC_ASSERT( sizeof (ines_header_t) == 16 );
92
93
blargg_err_t Nes_Cart::load_ines( Auto_File_Reader in )
94
{
95
RETURN_ERR( in.open() );
96
97
ines_header_t h;
98
RETURN_ERR( in->read( &h, sizeof h ) );
99
100
if ( 0 != memcmp( h.signature, "NES\x1A", 4 ) )
101
return not_ines_file;
102
103
if ( h.zero [7] ) // handle header defaced by a fucking idiot's handle
104
h.flags2 = 0;
105
106
set_mapper( h.flags, h.flags2 );
107
108
if ( h.flags & 0x04 ) // skip trainer
109
RETURN_ERR( in->skip( 512 ) );
110
111
RETURN_ERR( resize_prg( h.prg_count * 16 * 1024L ) );
112
RETURN_ERR( resize_chr( h.chr_count * 8 * 1024L ) );
113
114
RETURN_ERR( in->read( prg(), prg_size() ) );
115
RETURN_ERR( in->read( chr(), chr_size() ) );
116
117
return 0;
118
}
119
120
// IPS patching
121
122
// IPS patch file format (integers are big-endian):
123
// 5 "PATCH"
124
// n blocks
125
//
126
// normal block:
127
// 3 offset
128
// 2 size
129
// n data
130
//
131
// repeated byte block:
132
// 3 offset
133
// 2 0
134
// 2 size
135
// 1 fill value
136
//
137
// end block (optional):
138
// 3 "EOF"
139
//
140
// A block can append data to the file by specifying an offset at the end of
141
// the current file data.
142
143
typedef BOOST::uint8_t byte;
144
static blargg_err_t apply_ips_patch( Data_Reader& patch, byte** file, long* file_size )
145
{
146
byte signature [5];
147
RETURN_ERR( patch.read( signature, sizeof signature ) );
148
if ( memcmp( signature, "PATCH", sizeof signature ) )
149
return "Not an IPS patch file";
150
151
while ( patch.remain() )
152
{
153
// read offset
154
byte buf [6];
155
RETURN_ERR( patch.read( buf, 3 ) );
156
long offset = buf [0] * 0x10000 + buf [1] * 0x100 + buf [2];
157
if ( offset == 0x454F46 ) // 'EOF'
158
break;
159
160
// read size
161
RETURN_ERR( patch.read( buf, 2 ) );
162
long size = buf [0] * 0x100 + buf [1];
163
164
// size = 0 signals a run of identical bytes
165
int fill = -1;
166
if ( size == 0 )
167
{
168
RETURN_ERR( patch.read( buf, 3 ) );
169
size = buf [0] * 0x100 + buf [1];
170
fill = buf [2];
171
}
172
173
// expand file if new data is at exact end of file
174
if ( offset == *file_size )
175
{
176
*file_size = offset + size;
177
void* p = realloc( *file, *file_size );
178
CHECK_ALLOC( p );
179
*file = (byte*) p;
180
}
181
182
//dprintf( "Patch offset: 0x%04X, size: 0x%04X\n", (int) offset, (int) size );
183
184
if ( offset < 0 || *file_size < offset + size )
185
return "IPS tried to patch past end of file";
186
187
// read/fill data
188
if ( fill < 0 )
189
RETURN_ERR( patch.read( *file + offset, size ) );
190
else
191
memset( *file + offset, fill, size );
192
}
193
194
return 0;
195
}
196
197
blargg_err_t Nes_Cart::load_patched_ines( Auto_File_Reader in, Auto_File_Reader patch )
198
{
199
RETURN_ERR( in.open() );
200
RETURN_ERR( patch.open() );
201
202
// read file into memory
203
long size = in->remain();
204
byte* ines = (byte*) malloc( size );
205
CHECK_ALLOC( ines );
206
const char* err = in->read( ines, size );
207
208
// apply patch
209
if ( !err )
210
err = apply_ips_patch( *patch, &ines, &size );
211
212
// load patched file
213
if ( !err )
214
{
215
Mem_File_Reader patched( ines, size );
216
err = load_ines( patched );
217
}
218
219
free( ines );
220
221
return err;
222
}
223
224
blargg_err_t Nes_Cart::apply_ips_to_prg( Auto_File_Reader patch )
225
{
226
RETURN_ERR( patch.open() );
227
228
long size = prg_size();
229
230
byte* prg_copy = (byte*) malloc( size );
231
CHECK_ALLOC( prg_copy );
232
memcpy( prg_copy, prg(), size );
233
234
const char* err = apply_ips_patch( *patch, &prg_copy, &size );
235
236
if ( !err )
237
{
238
resize_prg( size );
239
memcpy( prg(), prg_copy, size );
240
}
241
242
free( prg_copy );
243
244
return err;
245
}
246
247
blargg_err_t Nes_Cart::apply_ips_to_chr( Auto_File_Reader patch )
248
{
249
RETURN_ERR( patch.open() );
250
251
long size = chr_size();
252
253
byte* chr_copy = (byte*) malloc( size );
254
CHECK_ALLOC( chr_copy );
255
memcpy( chr_copy, chr(), size );
256
257
const char* err = apply_ips_patch( *patch, &chr_copy, &size );
258
259
if ( !err )
260
{
261
resize_chr( size );
262
memcpy( chr(), chr_copy, size );
263
}
264
265
free( chr_copy );
266
267
return err;
268
}
269
270