diff options
author | Andree Buschmann <AndreeBuschmann@t-online.de> | 2011-08-07 20:01:04 +0000 |
---|---|---|
committer | Andree Buschmann <AndreeBuschmann@t-online.de> | 2011-08-07 20:01:04 +0000 |
commit | acb0917556fc33681c1df5a530cf754193e67705 (patch) | |
tree | 052a47097009a210e4aed9c207bd6aa4828cc000 /apps/codecs/libgme/nsfe_info.c | |
parent | 93c6f1329a5691a8be158cefe15641bd1daf9ef8 (diff) | |
download | rockbox-acb0917556fc33681c1df5a530cf754193e67705.tar.gz rockbox-acb0917556fc33681c1df5a530cf754193e67705.zip |
Submit initial patch from FS#12176. Adds support for several new game music formats (AY, GBS, HES, KSS, SGC, VGM and VGZ) and replaces the current NSF and NSFE with a new implementation based on a port of the Game Music Emu library 'GME'. This first submit does not cover the full functionality provided by the author's original patch: Coleco-SGV is not supported, some GME-specific m3u-support has been removed and IRAM is not used yet. Further changes are very likely to follow this submit. Thanks to Mauricio Garrido.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30264 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/libgme/nsfe_info.c')
-rw-r--r-- | apps/codecs/libgme/nsfe_info.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/apps/codecs/libgme/nsfe_info.c b/apps/codecs/libgme/nsfe_info.c new file mode 100644 index 0000000000..d22b763173 --- /dev/null +++ b/apps/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 | ||
9 | can redistribute it and/or modify it under the terms of the GNU Lesser | ||
10 | General Public License as published by the Free Software Foundation; either | ||
11 | version 2.1 of the License, or (at your option) any later version. This | ||
12 | module is distributed in the hope that it will be useful, but WITHOUT ANY | ||
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
14 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | ||
15 | details. You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this module; if not, write to the Free Software Foundation, | ||
17 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
18 | |||
19 | #include "blargg_source.h" | ||
20 | |||
21 | void Info_init( struct Nsfe_Info* this ) | ||
22 | { | ||
23 | this->playlist_disabled = false; | ||
24 | } | ||
25 | |||
26 | void 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... ? | ||
36 | void 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 | |||
44 | int 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 | |||
51 | const char eof_error [] = "Unexpected end of file"; | ||
52 | |||
53 | // Read n bytes from memory buffer | ||
54 | static 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 | |||
63 | static 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 | ||
74 | static 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 | |||
98 | struct 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 | |||
110 | blargg_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 | blargg_long chunk_size = get_le32( block_header [0] ); | ||
158 | blargg_long 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, (blargg_long) 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 | |||
251 | long Track_length( struct Nsf_Emu* this, int n ) | ||
252 | { | ||
253 | long 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 | } | ||