summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/nsfe_info.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/nsfe_info.c')
-rw-r--r--lib/rbcodec/codecs/libgme/nsfe_info.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/nsfe_info.c b/lib/rbcodec/codecs/libgme/nsfe_info.c
new file mode 100644
index 0000000000..337b1e580a
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/nsfe_info.c
@@ -0,0 +1,272 @@
1// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
2
3#include "nsf_emu.h"
4
5#include "blargg_endian.h"
6#include <string.h>
7
8/* Copyright (C) 2005-2006 Shay Green. This module is free software; you
9can redistribute it and/or modify it under the terms of the GNU Lesser
10General Public License as published by the Free Software Foundation; either
11version 2.1 of the License, or (at your option) any later version. This
12module is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15details. You should have received a copy of the GNU Lesser General Public
16License along with this module; if not, write to the Free Software Foundation,
17Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18
19#include "blargg_source.h"
20
21void Info_init( struct Nsfe_Info* this )
22{
23 this->playlist_disabled = false;
24}
25
26void Info_unload( struct Nsfe_Info* this )
27{
28 memset(this->playlist, 0, 256);
29 memset(this->track_times, 0, 256 * sizeof(int32_t));
30
31 this->playlist_size = 0;
32 this->track_times_size = 0;
33}
34
35// TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ?
36void Info_disable_playlist( struct Nsfe_Info* this, bool b )
37{
38 this->playlist_disabled = b;
39 this->track_count = this->playlist_size;
40 if ( !this->track_count || this->playlist_disabled )
41 this->track_count = this->actual_track_count_;
42}
43
44int Info_remap_track( struct Nsfe_Info* this, int track )
45{
46 if ( !this->playlist_disabled && (unsigned) track < (unsigned) this->playlist_size )
47 track = this->playlist [track];
48 return track;
49}
50
51const char eof_error [] = "Unexpected end of file";
52
53// Read n bytes from memory buffer
54static blargg_err_t in_read( void* dst, long bytes, void* data, long* offset, long size )
55{
56 if ((*offset + bytes) > size) return eof_error;
57
58 memcpy(dst, (char*) data + *offset, bytes);
59 *offset += bytes;
60 return 0;
61}
62
63static blargg_err_t in_skip( long bytes, long *offset, long size )
64{
65 if ((*offset + bytes) > size) return eof_error;
66
67 *offset += bytes;
68 return 0;
69}
70
71// Skip n bytes from memory buffer
72
73// Read multiple strings and separate into individual strings
74static int read_strs( void* data, long bytes, long* offset, long size,
75 const char* strs [4] )
76{
77 char* chars = (char*) data + *offset;
78 chars [bytes - 1] = 0; // in case last string doesn't have terminator
79
80 if ( in_skip( bytes, offset, size) )
81 return -1;
82
83 int count = 0, i;
84 for ( i = 0; i < bytes; i++ )
85 {
86 strs [count] = &chars [i];
87 while ( i < bytes && chars [i] )
88 i++;
89
90 count++;
91 if (count >= 4)
92 break;
93 }
94
95 return count;
96}
97
98struct nsfe_info_t
99{
100 byte load_addr [2];
101 byte init_addr [2];
102 byte play_addr [2];
103 byte speed_flags;
104 byte chip_flags;
105 byte track_count;
106 byte first_track;
107 byte unused [6];
108};
109
110blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Nsf_Emu* nsf_emu )
111{
112 long offset = 0;
113 int const nsfe_info_size = 16;
114 assert( offsetof (struct nsfe_info_t,unused [6]) == nsfe_info_size );
115
116 // check header
117 byte signature [4];
118 blargg_err_t err = in_read( signature, sizeof signature, data, &offset, size );
119 if ( err )
120 return (err == eof_error ? gme_wrong_file_type : err);
121 if ( memcmp( signature, "NSFE", 4 ) ) {
122 }
123
124 // free previous info
125 /* TODO: clear track_names */
126 memset(this->playlist, 0, 256);
127 memset(this->track_times, 0, 256 * sizeof(int32_t));
128
129 this->playlist_size = 0;
130 this->track_times_size = 0;
131
132 // default nsf header
133 static const struct header_t base_header =
134 {
135 {'N','E','S','M','\x1A'},// tag
136 1, // version
137 1, 1, // track count, first track
138 {0,0},{0,0},{0,0}, // addresses
139 "","","", // strings
140 {0x1A, 0x41}, // NTSC rate
141 {0,0,0,0,0,0,0,0}, // banks
142 {0x20, 0x4E}, // PAL rate
143 0, 0, // flags
144 {0,0,0,0} // unused
145 };
146
147 memcpy( &nsf_emu->header, &base_header, sizeof base_header );
148
149 // parse tags
150 int phase = 0;
151 while ( phase != 3 )
152 {
153 // read size and tag
154 byte block_header [2] [4];
155 RETURN_ERR( in_read( block_header, sizeof block_header, data, &offset, size ) );
156
157 int chunk_size = get_le32( block_header [0] );
158 int tag = get_le32( block_header [1] );
159
160 switch ( tag )
161 {
162 case BLARGG_4CHAR('O','F','N','I'): {
163 check( phase == 0 );
164 if ( chunk_size < 8 )
165 return "Corrupt file";
166
167 struct nsfe_info_t finfo;
168 finfo.track_count = 1;
169 finfo.first_track = 0;
170
171 RETURN_ERR( in_read( &finfo, min( chunk_size, nsfe_info_size ),
172 (char*) data, &offset, size ) );
173
174 if ( chunk_size > nsfe_info_size )
175 RETURN_ERR( in_skip( chunk_size - nsfe_info_size, &offset, size ) );
176
177 phase = 1;
178 nsf_emu->header.speed_flags = finfo.speed_flags;
179 nsf_emu->header.chip_flags = finfo.chip_flags;
180 nsf_emu->header.track_count = finfo.track_count;
181 this->actual_track_count_ = finfo.track_count;
182 nsf_emu->header.first_track = finfo.first_track;
183 memcpy( nsf_emu->header.load_addr, finfo.load_addr, 2 * 3 );
184 break;
185 }
186
187 case BLARGG_4CHAR('K','N','A','B'):
188 if ( chunk_size > (int) sizeof nsf_emu->header.banks )
189 return "Corrupt file";
190 RETURN_ERR( in_read( nsf_emu->header.banks, chunk_size, data, &offset, size ) );
191 break;
192
193 case BLARGG_4CHAR('h','t','u','a'): {
194 const char* strs [4];
195 int n = read_strs( data, chunk_size, &offset, size, strs );
196 if ( n < 0 )
197 return eof_error;
198 break;
199 }
200
201 case BLARGG_4CHAR('e','m','i','t'):
202 this->track_times_size = chunk_size / 4;
203 RETURN_ERR( in_read( this->track_times, this->track_times_size * 4, data, &offset, size ) );
204 break;
205
206 case BLARGG_4CHAR('l','b','l','t'):
207 RETURN_ERR( in_skip( chunk_size, &offset, size ) );
208 break;
209
210 case BLARGG_4CHAR('t','s','l','p'):
211 this->playlist_size = chunk_size;
212 RETURN_ERR( in_read( &this->playlist [0], chunk_size, data, &offset, size ) );
213 break;
214
215 case BLARGG_4CHAR('A','T','A','D'): {
216 check( phase == 1 );
217 phase = 2;
218 if ( !nsf_emu )
219 {
220 RETURN_ERR( in_skip( chunk_size, &offset, size ) );
221 }
222 else
223 {
224 // Avoid unexpected end of file
225 if ( (offset + chunk_size) > size )
226 return eof_error;
227
228 RETURN_ERR( Rom_load( &nsf_emu->rom, (char*) data + offset, chunk_size, 0, 0, 0 ) );
229 RETURN_ERR( Nsf_post_load( nsf_emu ) );
230 offset += chunk_size;
231 }
232 break;
233 }
234
235 case BLARGG_4CHAR('D','N','E','N'):
236 check( phase == 2 );
237 phase = 3;
238 break;
239
240 default:
241 // tags that can be skipped start with a lowercase character
242 check( islower( (tag >> 24) & 0xFF ) );
243 RETURN_ERR( in_skip( chunk_size, &offset, size ) );
244 break;
245 }
246 }
247
248 return 0;
249}
250
251int Track_length( struct Nsf_Emu* this, int n )
252{
253 int length = 0;
254 if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
255 struct entry_t* entry = &this->m3u.entries [n];
256 length = entry->length;
257 }
258 else if ( (this->info.playlist_size > 0) && (n < this->info.playlist_size) ) {
259 int remapped = Info_remap_track( &this->info, n );
260 if ( (unsigned) remapped < (unsigned) this->info.track_times_size )
261 length = (int32_t) get_le32( &this->info.track_times [remapped] );
262 }
263 else if( (unsigned) n < (unsigned) this->info.track_times_size )
264 length = (int32_t) get_le32( &this->info.track_times [n] );
265
266 /* Length will be 2,30 minutes for one track songs,
267 and 1,45 minutes for multitrack songs */
268 if ( length <= 0 )
269 length = (this->track_count > 1 ? 105 : 150) * 1000;
270
271 return length;
272}