summaryrefslogtreecommitdiff
path: root/apps/codecs/asap/asap.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/asap/asap.c')
-rw-r--r--apps/codecs/asap/asap.c2000
1 files changed, 2000 insertions, 0 deletions
diff --git a/apps/codecs/asap/asap.c b/apps/codecs/asap/asap.c
new file mode 100644
index 0000000000..084708af4c
--- /dev/null
+++ b/apps/codecs/asap/asap.c
@@ -0,0 +1,2000 @@
1/*
2 * asap.c - ASAP engine
3 *
4 * Copyright (C) 2005-2008 Piotr Fusik
5 *
6 * This file is part of ASAP (Another Slight Atari Player),
7 * see http://asap.sourceforge.net
8 *
9 * ASAP is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License,
12 * or (at your option) any later version.
13 *
14 * ASAP is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 * See the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ASAP; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#if !defined(JAVA) && !defined(CSHARP)
25#include <string.h>
26#endif
27
28#include "asap_internal.h"
29#if !defined(JAVA) && !defined(CSHARP)
30#include "players.h"
31#endif
32
33#define CMR_BASS_TABLE_OFFSET 0x70f
34
35CONST_LOOKUP(byte, cmr_bass_table) = {
36 0x5C, 0x56, 0x50, 0x4D, 0x47, 0x44, 0x41, 0x3E,
37 0x38, 0x35, (byte) 0x88, 0x7F, 0x79, 0x73, 0x6C, 0x67,
38 0x60, 0x5A, 0x55, 0x51, 0x4C, 0x48, 0x43, 0x3F,
39 0x3D, 0x39, 0x34, 0x33, 0x30, 0x2D, 0x2A, 0x28,
40 0x25, 0x24, 0x21, 0x1F, 0x1E
41};
42
43ASAP_FUNC int ASAP_GetByte(ASAP_State PTR ast, int addr)
44{
45 switch (addr & 0xff0f) {
46 case 0xd20a:
47 return PokeySound_GetRandom(ast, addr);
48 case 0xd20e:
49 if ((addr & AST extra_pokey_mask) != 0)
50 return 0xff;
51 return AST irqst;
52 case 0xd20f:
53 return 0xff;
54 case 0xd40b:
55 return AST scanline_number >> 1;
56 default:
57 return dGetByte(addr);
58 }
59}
60
61ASAP_FUNC void ASAP_PutByte(ASAP_State PTR ast, int addr, int data)
62{
63 if ((addr >> 8) == 0xd2) {
64 if ((addr & (AST extra_pokey_mask + 0xf)) == 0xe) {
65 AST irqst |= data ^ 0xff;
66#define SET_TIMER_IRQ(ch) \
67 if ((data & AST irqst & ch) != 0) { \
68 if (AST timer##ch##_cycle == NEVER) { \
69 int t = AST base_pokey.tick_cycle##ch; \
70 while (t < AST cycle) \
71 t += AST base_pokey.period_cycles##ch; \
72 AST timer##ch##_cycle = t; \
73 if (AST nearest_event_cycle > t) \
74 AST nearest_event_cycle = t; \
75 } \
76 } \
77 else \
78 AST timer##ch##_cycle = NEVER;
79 SET_TIMER_IRQ(1);
80 SET_TIMER_IRQ(2);
81 SET_TIMER_IRQ(4);
82 }
83 else
84 PokeySound_PutByte(ast, addr, data);
85 }
86 else if ((addr & 0xff0f) == 0xd40a) {
87 if (AST cycle <= AST next_scanline_cycle - 8)
88 AST cycle = AST next_scanline_cycle - 8;
89 else
90 AST cycle = AST next_scanline_cycle + 106;
91 }
92 else
93 dPutByte(addr, data);
94}
95
96#define MAX_SONGS 32
97
98CONST_LOOKUP(int, perframe2fastplay) = { 312, 312 / 2, 312 / 3, 312 / 4 };
99
100FILE_FUNC abool load_native(ASAP_State PTR ast, ASAP_ModuleInfo PTR module_info,
101 const byte ARRAY module, int module_len, ASAP_OBX player)
102{
103#if defined(JAVA) || defined(CSHARP)
104 try
105#endif
106 {
107 int player_last_byte;
108 int block_len;
109 if (UBYTE(module[0]) != 0xff || UBYTE(module[1]) != 0xff)
110 return FALSE;
111#ifdef JAVA
112 try {
113 player.read();
114 player.read();
115 MODULE_INFO player = player.read();
116 MODULE_INFO player += player.read() << 8;
117 player_last_byte = player.read();
118 player_last_byte += player.read() << 8;
119 } catch (IOException e) {
120 throw new RuntimeException();
121 }
122#elif defined(CSHARP)
123 player.ReadByte();
124 player.ReadByte();
125 MODULE_INFO player = player.ReadByte();
126 MODULE_INFO player += player.ReadByte() << 8;
127 player_last_byte = player.ReadByte();
128 player_last_byte += player.ReadByte() << 8;
129#else
130 MODULE_INFO player = UBYTE(player[2]) + (UBYTE(player[3]) << 8);
131 player_last_byte = UBYTE(player[4]) + (UBYTE(player[5]) << 8);
132#endif
133 MODULE_INFO music = UBYTE(module[2]) + (UBYTE(module[3]) << 8);
134 if (MODULE_INFO music <= player_last_byte)
135 return FALSE;
136 block_len = UBYTE(module[4]) + (UBYTE(module[5]) << 8) + 1 - MODULE_INFO music;
137 if (6 + block_len != module_len) {
138 int info_addr;
139 int info_len;
140 if (MODULE_INFO type != 'r' || 11 + block_len > module_len)
141 return FALSE;
142 /* allow optional info for Raster Music Tracker */
143 info_addr = UBYTE(module[6 + block_len]) + (UBYTE(module[7 + block_len]) << 8);
144 if (info_addr != MODULE_INFO music + block_len)
145 return FALSE;
146 info_len = UBYTE(module[8 + block_len]) + (UBYTE(module[9 + block_len]) << 8) + 1 - info_addr;
147 if (10 + block_len + info_len != module_len)
148 return FALSE;
149 }
150 if (ast != NULL) {
151 COPY_ARRAY(AST memory, MODULE_INFO music, module, 6, block_len);
152#ifdef JAVA
153 int addr = MODULE_INFO player;
154 do {
155 int i;
156 try {
157 i = player.read(AST memory, addr, player_last_byte + 1 - addr);
158 } catch (IOException e) {
159 throw new RuntimeException();
160 }
161 if (i <= 0)
162 throw new RuntimeException();
163 addr += i;
164 } while (addr <= player_last_byte);
165#elif defined(CSHARP)
166 int addr = MODULE_INFO player;
167 do {
168 int i = player.Read(AST memory, addr, player_last_byte + 1 - addr);
169 if (i <= 0)
170 throw new Exception();
171 addr += i;
172 } while (addr <= player_last_byte);
173#else
174 COPY_ARRAY(AST memory, MODULE_INFO player, player, 6, player_last_byte + 1 - MODULE_INFO player);
175#endif
176 }
177 return TRUE;
178 }
179#ifdef JAVA
180 finally {
181 try {
182 player.close();
183 } catch (IOException e) {
184 throw new RuntimeException();
185 }
186 }
187#elif defined(CSHARP)
188 finally {
189 player.Close();
190 }
191#endif
192}
193
194FILE_FUNC void set_song_duration(ASAP_ModuleInfo PTR module_info, int player_calls)
195{
196 MODULE_INFO durations[MODULE_INFO songs] = (int) (player_calls * MODULE_INFO fastplay * 114000.0 / 1773447);
197 MODULE_INFO songs++;
198}
199
200#define SEEN_THIS_CALL 1
201#define SEEN_BEFORE 2
202#define SEEN_REPEAT 3
203
204FILE_FUNC void parse_cmc_song(ASAP_ModuleInfo PTR module_info, const byte ARRAY module, int pos)
205{
206 int tempo = UBYTE(module[0x19]);
207 int player_calls = 0;
208 int rep_start_pos = 0;
209 int rep_end_pos = 0;
210 int rep_times = 0;
211 NEW_ARRAY(byte, seen, 0x55);
212 INIT_ARRAY(seen);
213 while (pos >= 0 && pos < 0x55) {
214 int p1;
215 int p2;
216 int p3;
217 if (pos == rep_end_pos && rep_times > 0) {
218 for (p1 = 0; p1 < 0x55; p1++)
219 if (seen[p1] == SEEN_THIS_CALL || seen[p1] == SEEN_REPEAT)
220 seen[p1] = 0;
221 rep_times--;
222 pos = rep_start_pos;
223 }
224 if (seen[pos] != 0) {
225 if (seen[pos] != SEEN_THIS_CALL)
226 MODULE_INFO loops[MODULE_INFO songs] = TRUE;
227 break;
228 }
229 seen[pos] = SEEN_THIS_CALL;
230 p1 = UBYTE(module[0x206 + pos]);
231 p2 = UBYTE(module[0x25b + pos]);
232 p3 = UBYTE(module[0x2b0 + pos]);
233 if (p1 == 0xfe || p2 == 0xfe || p3 == 0xfe) {
234 pos++;
235 continue;
236 }
237 p1 >>= 4;
238 if (p1 == 8)
239 break;
240 if (p1 == 9) {
241 pos = p2;
242 continue;
243 }
244 if (p1 == 0xa) {
245 pos -= p2;
246 continue;
247 }
248 if (p1 == 0xb) {
249 pos += p2;
250 continue;
251 }
252 if (p1 == 0xc) {
253 tempo = p2;
254 pos++;
255 continue;
256 }
257 if (p1 == 0xd) {
258 pos++;
259 rep_start_pos = pos;
260 rep_end_pos = pos + p2;
261 rep_times = p3 - 1;
262 continue;
263 }
264 if (p1 == 0xe) {
265 MODULE_INFO loops[MODULE_INFO songs] = TRUE;
266 break;
267 }
268 p2 = rep_times > 0 ? SEEN_REPEAT : SEEN_BEFORE;
269 for (p1 = 0; p1 < 0x55; p1++)
270 if (seen[p1] == SEEN_THIS_CALL)
271 seen[p1] = (byte) p2;
272 player_calls += tempo << 6;
273 pos++;
274 }
275 set_song_duration(module_info, player_calls);
276}
277
278FILE_FUNC abool parse_cmc(ASAP_State PTR ast, ASAP_ModuleInfo PTR module_info,
279 const byte ARRAY module, int module_len, abool cmr)
280{
281 int last_pos;
282 int pos;
283 if (module_len < 0x306)
284 return FALSE;
285 MODULE_INFO type = cmr ? 'z' : 'c';
286 if (!load_native(ast, module_info, module, module_len, GET_OBX(cmc)))
287 return FALSE;
288 if (ast != NULL && cmr)
289 COPY_ARRAY(AST memory, 0x500 + CMR_BASS_TABLE_OFFSET, cmr_bass_table, 0, sizeof(cmr_bass_table));
290 /* auto-detect number of subsongs */
291 last_pos = 0x54;
292 while (--last_pos >= 0) {
293 if (UBYTE(module[0x206 + last_pos]) < 0xb0
294 || UBYTE(module[0x25b + last_pos]) < 0x40
295 || UBYTE(module[0x2b0 + last_pos]) < 0x40)
296 break;
297 }
298 MODULE_INFO songs = 0;
299 parse_cmc_song(module_info, module, 0);
300 for (pos = 0; pos < last_pos && MODULE_INFO songs < MAX_SONGS; pos++)
301 if (UBYTE(module[0x206 + pos]) == 0x8f || UBYTE(module[0x206 + pos]) == 0xef)
302 parse_cmc_song(module_info, module, pos + 1);
303 return TRUE;
304}
305
306FILE_FUNC void parse_mpt_song(ASAP_ModuleInfo PTR module_info, const byte ARRAY module,
307 abool ARRAY global_seen, int song_len, int pos)
308{
309 int addr_to_offset = UBYTE(module[2]) + (UBYTE(module[3]) << 8) - 6;
310 int tempo = UBYTE(module[0x1cf]);
311 int player_calls = 0;
312 NEW_ARRAY(byte, seen, 256);
313 NEW_ARRAY(int, pattern_offset, 4);
314 NEW_ARRAY(int, blank_rows, 4);
315 NEW_ARRAY(int, blank_rows_counter, 4);
316 INIT_ARRAY(seen);
317 INIT_ARRAY(blank_rows);
318 while (pos < song_len) {
319 int i;
320 int ch;
321 int pattern_rows;
322 if (seen[pos] != 0) {
323 if (seen[pos] != SEEN_THIS_CALL)
324 MODULE_INFO loops[MODULE_INFO songs] = TRUE;
325 break;
326 }
327 seen[pos] = SEEN_THIS_CALL;
328 global_seen[pos] = TRUE;
329 i = UBYTE(module[0x1d0 + pos * 2]);
330 if (i == 0xff) {
331 pos = UBYTE(module[0x1d1 + pos * 2]);
332 continue;
333 }
334 for (ch = 3; ch >= 0; ch--) {
335 i = UBYTE(module[0x1c6 + ch]) + (UBYTE(module[0x1ca + ch]) << 8) - addr_to_offset;
336 i = UBYTE(module[i + pos * 2]);
337 if (i >= 0x40)
338 break;
339 i <<= 1;
340 i = UBYTE(module[0x46 + i]) + (UBYTE(module[0x47 + i]) << 8);
341 pattern_offset[ch] = i == 0 ? 0 : i - addr_to_offset;
342 blank_rows_counter[ch] = 0;
343 }
344 if (ch >= 0)
345 break;
346 for (i = 0; i < song_len; i++)
347 if (seen[i] == SEEN_THIS_CALL)
348 seen[i] = SEEN_BEFORE;
349 for (pattern_rows = UBYTE(module[0x1ce]); --pattern_rows >= 0; ) {
350 for (ch = 3; ch >= 0; ch--) {
351 if (pattern_offset[ch] == 0 || --blank_rows_counter[ch] >= 0)
352 continue;
353 for (;;) {
354 i = UBYTE(module[pattern_offset[ch]++]);
355 if (i < 0x40 || i == 0xfe)
356 break;
357 if (i < 0x80)
358 continue;
359 if (i < 0xc0) {
360 blank_rows[ch] = i - 0x80;
361 continue;
362 }
363 if (i < 0xd0)
364 continue;
365 if (i < 0xe0) {
366 tempo = i - 0xcf;
367 continue;
368 }
369 pattern_rows = 0;
370 }
371 blank_rows_counter[ch] = blank_rows[ch];
372 }
373 player_calls += tempo;
374 }
375 pos++;
376 }
377 if (player_calls > 0)
378 set_song_duration(module_info, player_calls);
379}
380
381FILE_FUNC abool parse_mpt(ASAP_State PTR ast, ASAP_ModuleInfo PTR module_info,
382 const byte ARRAY module, int module_len)
383{
384 int track0_addr;
385 int pos;
386 int song_len;
387 /* seen[i] == TRUE if the track position i has been processed */
388 NEW_ARRAY(abool, global_seen, 256);
389 if (module_len < 0x1d0)
390 return FALSE;
391 MODULE_INFO type = 'm';
392 if (!load_native(ast, module_info, module, module_len, GET_OBX(mpt)))
393 return FALSE;
394 track0_addr = UBYTE(module[2]) + (UBYTE(module[3]) << 8) + 0x1ca;
395 if (UBYTE(module[0x1c6]) + (UBYTE(module[0x1ca]) << 8) != track0_addr)
396 return FALSE;
397 /* Calculate the length of the first track. Address of the second track minus
398 address of the first track equals the length of the first track in bytes.
399 Divide by two to get number of track positions. */
400 song_len = (UBYTE(module[0x1c7]) + (UBYTE(module[0x1cb]) << 8) - track0_addr) >> 1;
401 if (song_len > 0xfe)
402 return FALSE;
403 INIT_ARRAY(global_seen);
404 MODULE_INFO songs = 0;
405 for (pos = 0; pos < song_len && MODULE_INFO songs < MAX_SONGS; pos++) {
406 if (!global_seen[pos]) {
407 MODULE_INFO song_pos[MODULE_INFO songs] = (byte) pos;
408 parse_mpt_song(module_info, module, global_seen, song_len, pos);
409 }
410 }
411 return MODULE_INFO songs != 0;
412}
413
414CONST_LOOKUP(byte, rmt_volume_silent) = { 16, 8, 4, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 };
415
416FILE_FUNC int rmt_instrument_frames(const byte ARRAY module, int instrument, int volume, int volume_frame, abool extra_pokey)
417{
418 int addr_to_offset = UBYTE(module[2]) + (UBYTE(module[3]) << 8) - 6;
419 int per_frame = module[0xc];
420 int player_call;
421 int player_calls;
422 int index;
423 int index_end;
424 int index_loop;
425 int volume_slide_depth;
426 int volume_min;
427 abool looping;
428 int volume_slide;
429 abool silent_loop;
430 instrument = UBYTE(module[0xe]) + (UBYTE(module[0xf]) << 8) - addr_to_offset + (instrument << 1);
431 if (module[instrument + 1] == 0)
432 return 0;
433 instrument = UBYTE(module[instrument]) + (UBYTE(module[instrument + 1]) << 8) - addr_to_offset;
434 player_calls = player_call = volume_frame * per_frame;
435 index = UBYTE(module[instrument]) + 1 + player_call * 3;
436 index_end = UBYTE(module[instrument + 2]) + 3;
437 index_loop = UBYTE(module[instrument + 3]);
438 if (index_loop >= index_end)
439 return 0; /* error */
440 volume_slide_depth = UBYTE(module[instrument + 6]);
441 volume_min = UBYTE(module[instrument + 7]);
442 looping = index >= index_end;
443 if (looping)
444 index = (index - index_end) % (index_end - index_loop) + index_loop;
445 else {
446 do {
447 int vol = module[instrument + index];
448 if (extra_pokey)
449 vol >>= 4;
450 if ((vol & 0xf) >= rmt_volume_silent[volume])
451 player_calls = player_call + 1;
452 player_call++;
453 index += 3;
454 } while (index < index_end);
455 }
456 if (volume_slide_depth == 0)
457 return player_calls / per_frame;
458 volume_slide = 128;
459 silent_loop = FALSE;
460 for (;;) {
461 int vol;
462 if (index >= index_end) {
463 if (silent_loop)
464 break;
465 silent_loop = TRUE;
466 index = index_loop;
467 }
468 vol = module[instrument + index];
469 if (extra_pokey)
470 vol >>= 4;
471 if ((vol & 0xf) >= rmt_volume_silent[volume]) {
472 player_calls = player_call + 1;
473 silent_loop = FALSE;
474 }
475 player_call++;
476 index += 3;
477 volume_slide -= volume_slide_depth;
478 if (volume_slide < 0) {
479 volume_slide += 256;
480 if (--volume <= volume_min)
481 break;
482 }
483 }
484 return player_calls / per_frame;
485}
486
487FILE_FUNC void parse_rmt_song(ASAP_ModuleInfo PTR module_info, const byte ARRAY module,
488 abool ARRAY global_seen, int song_len, int pos_shift, int pos)
489{
490 int ch;
491 int addr_to_offset = UBYTE(module[2]) + (UBYTE(module[3]) << 8) - 6;
492 int tempo = UBYTE(module[0xb]);
493 int frames = 0;
494 int song_offset = UBYTE(module[0x14]) + (UBYTE(module[0x15]) << 8) - addr_to_offset;
495 int pattern_lo_offset = UBYTE(module[0x10]) + (UBYTE(module[0x11]) << 8) - addr_to_offset;
496 int pattern_hi_offset = UBYTE(module[0x12]) + (UBYTE(module[0x13]) << 8) - addr_to_offset;
497 int instrument_frames;
498 NEW_ARRAY(byte, seen, 256);
499 NEW_ARRAY(int, pattern_begin, 8);
500 NEW_ARRAY(int, pattern_offset, 8);
501 NEW_ARRAY(int, blank_rows, 8);
502 NEW_ARRAY(int, instrument_no, 8);
503 NEW_ARRAY(int, instrument_frame, 8);
504 NEW_ARRAY(int, volume_value, 8);
505 NEW_ARRAY(int, volume_frame, 8);
506 INIT_ARRAY(seen);
507 INIT_ARRAY(instrument_no);
508 INIT_ARRAY(instrument_frame);
509 INIT_ARRAY(volume_value);
510 INIT_ARRAY(volume_frame);
511 while (pos < song_len) {
512 int i;
513 int pattern_rows;
514 if (seen[pos] != 0) {
515 if (seen[pos] != SEEN_THIS_CALL)
516 MODULE_INFO loops[MODULE_INFO songs] = TRUE;
517 break;
518 }
519 seen[pos] = SEEN_THIS_CALL;
520 global_seen[pos] = TRUE;
521 if (UBYTE(module[song_offset + (pos << pos_shift)]) == 0xfe) {
522 pos = UBYTE(module[song_offset + (pos << pos_shift) + 1]);
523 continue;
524 }
525 for (ch = 0; ch < 1 << pos_shift; ch++) {
526 i = UBYTE(module[song_offset + (pos << pos_shift) + ch]);
527 if (i == 0xff)
528 blank_rows[ch] = 256;
529 else {
530 pattern_offset[ch] = pattern_begin[ch] = UBYTE(module[pattern_lo_offset + i])
531 + (UBYTE(module[pattern_hi_offset + i]) << 8) - addr_to_offset;
532 blank_rows[ch] = 0;
533 }
534 }
535 for (i = 0; i < song_len; i++)
536 if (seen[i] == SEEN_THIS_CALL)
537 seen[i] = SEEN_BEFORE;
538 for (pattern_rows = UBYTE(module[0xa]); --pattern_rows >= 0; ) {
539 for (ch = 0; ch < 1 << pos_shift; ch++) {
540 if (--blank_rows[ch] > 0)
541 continue;
542 for (;;) {
543 i = UBYTE(module[pattern_offset[ch]++]);
544 if ((i & 0x3f) < 62) {
545 i += UBYTE(module[pattern_offset[ch]++]) << 8;
546 if ((i & 0x3f) != 61) {
547 instrument_no[ch] = i >> 10;
548 instrument_frame[ch] = frames;
549 }
550 volume_value[ch] = (i >> 6) & 0xf;
551 volume_frame[ch] = frames;
552 break;
553 }
554 if (i == 62) {
555 blank_rows[ch] = UBYTE(module[pattern_offset[ch]++]);
556 break;
557 }
558 if ((i & 0x3f) == 62) {
559 blank_rows[ch] = i >> 6;
560 break;
561 }
562 if ((i & 0xbf) == 63) {
563 tempo = UBYTE(module[pattern_offset[ch]++]);
564 continue;
565 }
566 if (i == 0xbf) {
567 pattern_offset[ch] = pattern_begin[ch] + UBYTE(module[pattern_offset[ch]]);
568 continue;
569 }
570 /* assert(i == 0xff); */
571 pattern_rows = -1;
572 break;
573 }
574 if (pattern_rows < 0)
575 break;
576 }
577 if (pattern_rows >= 0)
578 frames += tempo;
579 }
580 pos++;
581 }
582 instrument_frames = 0;
583 for (ch = 0; ch < 1 << pos_shift; ch++) {
584 int frame = instrument_frame[ch];
585 frame += rmt_instrument_frames(module, instrument_no[ch], volume_value[ch], volume_frame[ch] - frame, ch >= 4);
586 if (instrument_frames < frame)
587 instrument_frames = frame;
588 }
589 if (frames > instrument_frames) {
590 if (frames - instrument_frames > 100)
591 MODULE_INFO loops[MODULE_INFO songs] = FALSE;
592 frames = instrument_frames;
593 }
594 if (frames > 0)
595 set_song_duration(module_info, frames);
596}
597
598FILE_FUNC abool parse_rmt(ASAP_State PTR ast, ASAP_ModuleInfo PTR module_info,
599 const byte ARRAY module, int module_len)
600{
601 int per_frame;
602 int pos_shift;
603 int song_len;
604 int pos;
605 NEW_ARRAY(abool, global_seen, 256);
606 if (module_len < 0x30 || module[6] != 'R' || module[7] != 'M'
607 || module[8] != 'T' || module[0xd] != 1)
608 return FALSE;
609 switch ((char) module[9]) {
610 case '4':
611 pos_shift = 2;
612 break;
613 case '8':
614 MODULE_INFO channels = 2;
615 pos_shift = 3;
616 break;
617 default:
618 return FALSE;
619 }
620 per_frame = module[0xc];
621 if (per_frame < 1 || per_frame > 4)
622 return FALSE;
623 MODULE_INFO type = 'r';
624 if (!load_native(ast, module_info, module, module_len,
625 MODULE_INFO channels == 2 ? GET_OBX(rmt8) : GET_OBX(rmt4)))
626 return FALSE;
627 song_len = UBYTE(module[4]) + (UBYTE(module[5]) << 8) + 1
628 - UBYTE(module[0x14]) - (UBYTE(module[0x15]) << 8);
629 if (pos_shift == 3 && (song_len & 4) != 0
630 && UBYTE(module[6 + UBYTE(module[4]) + (UBYTE(module[5]) << 8)
631 - UBYTE(module[2]) - (UBYTE(module[3]) << 8) - 3]) == 0xfe)
632 song_len += 4;
633 song_len >>= pos_shift;
634 if (song_len >= 0x100)
635 return FALSE;
636 INIT_ARRAY(global_seen);
637 MODULE_INFO songs = 0;
638 for (pos = 0; pos < song_len && MODULE_INFO songs < MAX_SONGS; pos++) {
639 if (!global_seen[pos]) {
640 MODULE_INFO song_pos[MODULE_INFO songs] = (byte) pos;
641 parse_rmt_song(module_info, module, global_seen, song_len, pos_shift, pos);
642 }
643 }
644 /* must set fastplay after song durations calculations, so they assume 312 */
645 MODULE_INFO fastplay = perframe2fastplay[per_frame - 1];
646 MODULE_INFO player = 0x600;
647 return MODULE_INFO songs != 0;
648}
649
650FILE_FUNC void parse_tmc_song(ASAP_ModuleInfo PTR module_info, const byte ARRAY module, int pos)
651{
652 int addr_to_offset = UBYTE(module[2]) + (UBYTE(module[3]) << 8) - 6;
653 int tempo = UBYTE(module[0x24]) + 1;
654 int frames = 0;
655 NEW_ARRAY(int, pattern_offset, 8);
656 NEW_ARRAY(int, blank_rows, 8);
657 while (UBYTE(module[0x1a6 + 15 + pos]) < 0x80) {
658 int ch;
659 int pattern_rows;
660 for (ch = 7; ch >= 0; ch--) {
661 int pat = UBYTE(module[0x1a6 + 15 + pos - 2 * ch]);
662 pattern_offset[ch] = UBYTE(module[0xa6 + pat]) + (UBYTE(module[0x126 + pat]) << 8) - addr_to_offset;
663 blank_rows[ch] = 0;
664 }
665 for (pattern_rows = 64; --pattern_rows >= 0; ) {
666 for (ch = 7; ch >= 0; ch--) {
667 if (--blank_rows[ch] >= 0)
668 continue;
669 for (;;) {
670 int i = UBYTE(module[pattern_offset[ch]++]);
671 if (i < 0x40) {
672 pattern_offset[ch]++;
673 break;
674 }
675 if (i == 0x40) {
676 i = UBYTE(module[pattern_offset[ch]++]);
677 if ((i & 0x7f) == 0)
678 pattern_rows = 0;
679 else
680 tempo = (i & 0x7f) + 1;
681 if (i >= 0x80)
682 pattern_offset[ch]++;
683 break;
684 }
685 if (i < 0x80) {
686 i = module[pattern_offset[ch]++] & 0x7f;
687 if (i == 0)
688 pattern_rows = 0;
689 else
690 tempo = i + 1;
691 pattern_offset[ch]++;
692 break;
693 }
694 if (i < 0xc0)
695 continue;
696 blank_rows[ch] = i - 0xbf;
697 break;
698 }
699 }
700 frames += tempo;
701 }
702 pos += 16;
703 }
704 if (UBYTE(module[0x1a6 + 14 + pos]) < 0x80)
705 MODULE_INFO loops[MODULE_INFO songs] = TRUE;
706 set_song_duration(module_info, frames);
707}
708
709FILE_FUNC abool parse_tmc(ASAP_State PTR ast, ASAP_ModuleInfo PTR module_info,
710 const byte ARRAY module, int module_len)
711{
712 int i;
713 int last_pos;
714 if (module_len < 0x1d0)
715 return FALSE;
716 MODULE_INFO type = 't';
717 if (!load_native(ast, module_info, module, module_len, GET_OBX(tmc)))
718 return FALSE;
719 MODULE_INFO channels = 2;
720 i = 0;
721 /* find first instrument */
722 while (module[0x66 + i] == 0) {
723 if (++i >= 64)
724 return FALSE; /* no instrument */
725 }
726 last_pos = (UBYTE(module[0x66 + i]) << 8) + UBYTE(module[0x26 + i])
727 - UBYTE(module[2]) - (UBYTE(module[3]) << 8) - 0x1b0;
728 if (0x1b5 + last_pos >= module_len)
729 return FALSE;
730 /* skip trailing jumps */
731 do {
732 if (last_pos <= 0)
733 return FALSE; /* no pattern to play */
734 last_pos -= 16;
735 } while (UBYTE(module[0x1b5 + last_pos]) >= 0x80);
736 MODULE_INFO songs = 0;
737 parse_tmc_song(module_info, module, 0);
738 for (i = 0; i < last_pos && MODULE_INFO songs < MAX_SONGS; i += 16)
739 if (UBYTE(module[0x1b5 + i]) >= 0x80)
740 parse_tmc_song(module_info, module, i + 16);
741 /* must set fastplay after song durations calculations, so they assume 312 */
742 i = module[0x25];
743 if (i < 1 || i > 4)
744 return FALSE;
745 if (ast != NULL)
746 AST tmc_per_frame = module[0x25];
747 MODULE_INFO fastplay = perframe2fastplay[i - 1];
748 return TRUE;
749}
750
751FILE_FUNC void parse_tm2_song(ASAP_ModuleInfo PTR module_info, const byte ARRAY module, int pos)
752{
753 int addr_to_offset = UBYTE(module[2]) + (UBYTE(module[3]) << 8) - 6;
754 int tempo = UBYTE(module[0x24]) + 1;
755 int player_calls = 0;
756 NEW_ARRAY(int, pattern_offset, 8);
757 NEW_ARRAY(int, blank_rows, 8);
758 for (;;) {
759 int ch;
760 int pattern_rows = UBYTE(module[0x386 + 16 + pos]);
761 if (pattern_rows == 0)
762 break;
763 if (pattern_rows >= 0x80) {
764 MODULE_INFO loops[MODULE_INFO songs] = TRUE;
765 break;
766 }
767 for (ch = 7; ch >= 0; ch--) {
768 int pat = UBYTE(module[0x386 + 15 + pos - 2 * ch]);
769 pattern_offset[ch] = UBYTE(module[0x106 + pat]) + (UBYTE(module[0x206 + pat]) << 8) - addr_to_offset;
770 blank_rows[ch] = 0;
771 }
772 while (--pattern_rows >= 0) {
773 for (ch = 7; ch >= 0; ch--) {
774 if (--blank_rows[ch] >= 0)
775 continue;
776 for (;;) {
777 int i = UBYTE(module[pattern_offset[ch]++]);
778 if (i == 0) {
779 pattern_offset[ch]++;
780 break;
781 }
782 if (i < 0x40) {
783 if (UBYTE(module[pattern_offset[ch]++]) >= 0x80)
784 pattern_offset[ch]++;
785 break;
786 }
787 if (i < 0x80) {
788 pattern_offset[ch]++;
789 break;
790 }
791 if (i == 0x80) {
792 blank_rows[ch] = UBYTE(module[pattern_offset[ch]++]);
793 break;
794 }
795 if (i < 0xc0)
796 break;
797 if (i < 0xd0) {
798 tempo = i - 0xbf;
799 continue;
800 }
801 if (i < 0xe0) {
802 pattern_offset[ch]++;
803 break;
804 }
805 if (i < 0xf0) {
806 pattern_offset[ch] += 2;
807 break;
808 }
809 if (i < 0xff) {
810 blank_rows[ch] = i - 0xf0;
811 break;
812 }
813 blank_rows[ch] = 64;
814 break;
815 }
816 }
817 player_calls += tempo;
818 }
819 pos += 17;
820 }
821 set_song_duration(module_info, player_calls);
822}
823
824FILE_FUNC abool parse_tm2(ASAP_State PTR ast, ASAP_ModuleInfo PTR module_info,
825 const byte ARRAY module, int module_len)
826{
827 int i;
828 int last_pos;
829 int c;
830 if (module_len < 0x3a4)
831 return FALSE;
832 MODULE_INFO type = 'T';
833 if (!load_native(ast, module_info, module, module_len, GET_OBX(tm2)))
834 return FALSE;
835 i = module[0x25];
836 if (i < 1 || i > 4)
837 return FALSE;
838 MODULE_INFO fastplay = perframe2fastplay[i - 1];
839 MODULE_INFO player = 0x500;
840 if (module[0x1f] != 0)
841 MODULE_INFO channels = 2;
842 last_pos = 0xffff;
843 for (i = 0; i < 0x80; i++) {
844 int instr_addr = UBYTE(module[0x86 + i]) + (UBYTE(module[0x306 + i]) << 8);
845 if (instr_addr != 0 && instr_addr < last_pos)
846 last_pos = instr_addr;
847 }
848 for (i = 0; i < 0x100; i++) {
849 int pattern_addr = UBYTE(module[0x106 + i]) + (UBYTE(module[0x206 + i]) << 8);
850 if (pattern_addr != 0 && pattern_addr < last_pos)
851 last_pos = pattern_addr;
852 }
853 last_pos -= UBYTE(module[2]) + (UBYTE(module[3]) << 8) + 0x380;
854 if (0x386 + last_pos >= module_len)
855 return FALSE;
856 /* skip trailing stop/jump commands */
857 do {
858 if (last_pos <= 0)
859 return FALSE;
860 last_pos -= 17;
861 c = UBYTE(module[0x386 + 16 + last_pos]);
862 } while (c == 0 || c >= 0x80);
863 MODULE_INFO songs = 0;
864 parse_tm2_song(module_info, module, 0);
865 for (i = 0; i < last_pos && MODULE_INFO songs < MAX_SONGS; i += 17) {
866 c = UBYTE(module[0x386 + 16 + i]);
867 if (c == 0 || c >= 0x80)
868 parse_tm2_song(module_info, module, i + 17);
869 }
870 return TRUE;
871}
872
873#if !defined(JAVA) && !defined(CSHARP)
874
875static abool parse_hex(int *retval, const char *p)
876{
877 int r = 0;
878 do {
879 char c = *p;
880 if (r > 0xfff)
881 return FALSE;
882 r <<= 4;
883 if (c >= '0' && c <= '9')
884 r += c - '0';
885 else if (c >= 'A' && c <= 'F')
886 r += c - 'A' + 10;
887 else if (c >= 'a' && c <= 'f')
888 r += c - 'a' + 10;
889 else
890 return FALSE;
891 } while (*++p != '\0');
892 *retval = r;
893 return TRUE;
894}
895
896static abool parse_dec(int *retval, const char *p, int minval, int maxval)
897{
898 int r = 0;
899 do {
900 char c = *p;
901 if (c >= '0' && c <= '9')
902 r = 10 * r + c - '0';
903 else
904 return FALSE;
905 if (r > maxval)
906 return FALSE;
907 } while (*++p != '\0');
908 if (r < minval)
909 return FALSE;
910 *retval = r;
911 return TRUE;
912}
913
914static abool parse_text(char *retval, const char *p)
915{
916 int i;
917 if (*p != '"')
918 return FALSE;
919 p++;
920 if (p[0] == '<' && p[1] == '?' && p[2] == '>' && p[3] == '"')
921 return TRUE;
922 i = 0;
923 while (*p != '"') {
924 if (i >= 127)
925 return FALSE;
926 if (*p == '\0')
927 return FALSE;
928 retval[i++] = *p++;
929 }
930 retval[i] = '\0';
931 return TRUE;
932}
933
934int ASAP_ParseDuration(const char *s)
935{
936 int r;
937 if (*s < '0' || *s > '9')
938 return -1;
939 r = *s++ - '0';
940 if (*s >= '0' && *s <= '9')
941 r = 10 * r + *s++ - '0';
942 if (*s == ':') {
943 s++;
944 if (*s < '0' || *s > '5')
945 return -1;
946 r = 60 * r + (*s++ - '0') * 10;
947 if (*s < '0' || *s > '9')
948 return -1;
949 r += *s++ - '0';
950 }
951 r *= 1000;
952 if (*s != '.')
953 return r;
954 s++;
955 if (*s < '0' || *s > '9')
956 return r;
957 r += 100 * (*s++ - '0');
958 if (*s < '0' || *s > '9')
959 return r;
960 r += 10 * (*s++ - '0');
961 if (*s < '0' || *s > '9')
962 return r;
963 r += *s - '0';
964 return r;
965}
966
967static char *two_digits(char *s, int x)
968{
969 s[0] = '0' + x / 10;
970 s[1] = '0' + x % 10;
971 return s + 2;
972}
973
974void ASAP_DurationToString(char *s, int duration)
975{
976 if (duration >= 0) {
977 int seconds = duration / 1000;
978 int minutes = seconds / 60;
979 s = two_digits(s, minutes);
980 *s++ = ':';
981 s = two_digits(s, seconds % 60);
982 duration %= 1000;
983 if (duration != 0) {
984 *s++ = '.';
985 s = two_digits(s, duration / 10);
986 duration %= 10;
987 if (duration != 0)
988 *s++ = '0' + duration;
989 }
990 }
991 *s = '\0';
992}
993
994#endif /* !defined(JAVA) && !defined(CSHARP) */
995
996FILE_FUNC abool parse_sap_header(ASAP_ModuleInfo PTR module_info,
997 const byte ARRAY module, int module_len)
998{
999 int module_index = 0;
1000 abool sap_signature = FALSE;
1001 int duration_index = 0;
1002 for (;;) {
1003 NEW_ARRAY(char, line, 256);
1004 int i;
1005#if !defined(JAVA) && !defined(CSHARP)
1006 char *p;
1007#endif
1008 if (module_index + 8 >= module_len)
1009 return FALSE;
1010 if (UBYTE(module[module_index]) == 0xff)
1011 break;
1012 i = 0;
1013 while (module[module_index] != 0x0d) {
1014 line[i++] = (char) module[module_index++];
1015 if (module_index >= module_len || i >= sizeof(line) - 1)
1016 return FALSE;
1017 }
1018 if (++module_index >= module_len || module[module_index++] != 0x0a)
1019 return FALSE;
1020
1021#ifdef JAVA
1022 String tag = new String(line, 0, i);
1023 String arg = null;
1024 i = tag.indexOf(' ');
1025 if (i >= 0) {
1026 arg = tag.substring(i + 1);
1027 tag = tag.substring(0, i);
1028 }
1029#define TAG_IS(t) tag.equals(t)
1030#define CHAR_ARG arg.charAt(0)
1031#define SET_HEX(v) v = Integer.parseInt(arg, 16)
1032#define SET_DEC(v, min, max) do { v = Integer.parseInt(arg); if (v < min || v > max) return FALSE; } while (FALSE)
1033#define SET_TEXT(v) v = arg.substring(1, arg.length() - 1)
1034#define DURATION_ARG parseDuration(arg)
1035#define ARG_CONTAINS(t) (arg.indexOf(t) >= 0)
1036#elif defined(CSHARP)
1037 string tag = new string(line, 0, i);
1038 string arg = null;
1039 i = tag.IndexOf(' ');
1040 if (i >= 0) {
1041 arg = tag.Substring(i + 1);
1042 tag = tag.Substring(0, i);
1043 }
1044#define TAG_IS(t) tag == t
1045#define CHAR_ARG arg[0]
1046#define SET_HEX(v) v = int.Parse(arg, System.Globalization.NumberStyles.HexNumber)
1047#define SET_DEC(v, min, max) do { v = int.Parse(arg); if (v < min || v > max) return FALSE; } while (FALSE)
1048#define SET_TEXT(v) v = arg.Substring(1, arg.Length - 1)
1049#define DURATION_ARG ParseDuration(arg)
1050#define ARG_CONTAINS(t) (arg.IndexOf(t) >= 0)
1051#else
1052 line[i] = '\0';
1053 for (p = line; *p != '\0'; p++) {
1054 if (*p == ' ') {
1055 *p++ = '\0';
1056 break;
1057 }
1058 }
1059#define TAG_IS(t) (strcmp(line, t) == 0)
1060#define CHAR_ARG *p
1061#define SET_HEX(v) do { if (!parse_hex(&v, p)) return FALSE; } while (FALSE)
1062#define SET_DEC(v, min, max) do { if (!parse_dec(&v, p, min, max)) return FALSE; } while (FALSE)
1063#define SET_TEXT(v) do { if (!parse_text(v, p)) return FALSE; } while (FALSE)
1064#define DURATION_ARG ASAP_ParseDuration(p)
1065#define ARG_CONTAINS(t) (strstr(p, t) != NULL)
1066#endif
1067
1068 if (TAG_IS("SAP"))
1069 sap_signature = TRUE;
1070 if (!sap_signature)
1071 return FALSE;
1072 if (TAG_IS("AUTHOR"))
1073 SET_TEXT(MODULE_INFO author);
1074 else if (TAG_IS("NAME"))
1075 SET_TEXT(MODULE_INFO name);
1076 else if (TAG_IS("DATE"))
1077 SET_TEXT(MODULE_INFO date);
1078 else if (TAG_IS("SONGS"))
1079 SET_DEC(MODULE_INFO songs, 1, MAX_SONGS);
1080 else if (TAG_IS("DEFSONG"))
1081 SET_DEC(MODULE_INFO default_song, 0, MAX_SONGS - 1);
1082 else if (TAG_IS("STEREO"))
1083 MODULE_INFO channels = 2;
1084 else if (TAG_IS("TIME")) {
1085 int duration = DURATION_ARG;
1086 if (duration < 0 || duration_index >= MAX_SONGS)
1087 return FALSE;
1088 MODULE_INFO durations[duration_index] = duration;
1089 if (ARG_CONTAINS("LOOP"))
1090 MODULE_INFO loops[duration_index] = TRUE;
1091 duration_index++;
1092 }
1093 else if (TAG_IS("TYPE"))
1094 MODULE_INFO type = CHAR_ARG;
1095 else if (TAG_IS("FASTPLAY"))
1096 SET_DEC(MODULE_INFO fastplay, 1, 312);
1097 else if (TAG_IS("MUSIC"))
1098 SET_HEX(MODULE_INFO music);
1099 else if (TAG_IS("INIT"))
1100 SET_HEX(MODULE_INFO init);
1101 else if (TAG_IS("PLAYER"))
1102 SET_HEX(MODULE_INFO player);
1103 }
1104 if (MODULE_INFO default_song >= MODULE_INFO songs)
1105 return FALSE;
1106 switch (MODULE_INFO type) {
1107 case 'B':
1108 case 'D':
1109 if (MODULE_INFO player < 0 || MODULE_INFO init < 0)
1110 return FALSE;
1111 break;
1112 case 'C':
1113 if (MODULE_INFO player < 0 || MODULE_INFO music < 0)
1114 return FALSE;
1115 break;
1116 case 'S':
1117 if (MODULE_INFO init < 0)
1118 return FALSE;
1119 MODULE_INFO fastplay = 78;
1120 break;
1121 default:
1122 return FALSE;
1123 }
1124 if (UBYTE(module[module_index + 1]) != 0xff)
1125 return FALSE;
1126 MODULE_INFO header_len = module_index;
1127 return TRUE;
1128}
1129
1130FILE_FUNC abool parse_sap(ASAP_State PTR ast, ASAP_ModuleInfo PTR module_info,
1131 const byte ARRAY module, int module_len)
1132{
1133 int module_index;
1134 if (!parse_sap_header(module_info, module, module_len))
1135 return FALSE;
1136 if (ast == NULL)
1137 return TRUE;
1138 ZERO_ARRAY(AST memory);
1139 module_index = MODULE_INFO header_len + 2;
1140 while (module_index + 5 <= module_len) {
1141 int start_addr = UBYTE(module[module_index]) + (UBYTE(module[module_index + 1]) << 8);
1142 int block_len = UBYTE(module[module_index + 2]) + (UBYTE(module[module_index + 3]) << 8) + 1 - start_addr;
1143 if (block_len <= 0 || module_index + block_len > module_len)
1144 return FALSE;
1145 module_index += 4;
1146 COPY_ARRAY(AST memory, start_addr, module, module_index, block_len);
1147 module_index += block_len;
1148 if (module_index == module_len)
1149 return TRUE;
1150 if (module_index + 7 <= module_len
1151 && UBYTE(module[module_index]) == 0xff && UBYTE(module[module_index + 1]) == 0xff)
1152 module_index += 2;
1153 }
1154 return FALSE;
1155}
1156
1157#define ASAP_EXT(c1, c2, c3) (((c1) + ((c2) << 8) + ((c3) << 16)) | 0x202020)
1158
1159FILE_FUNC int get_packed_ext(STRING filename)
1160{
1161#ifdef JAVA
1162 int i = filename.length();
1163 int ext = 0;
1164 while (--i > 0) {
1165 if (filename.charAt(i) == '.')
1166 return ext | 0x202020;
1167 ext = (ext << 8) + filename.charAt(i);
1168 }
1169 return 0;
1170#elif defined(CSHARP)
1171 int i = filename.Length;
1172 int ext = 0;
1173 while (--i > 0) {
1174 if (filename[i] == '.')
1175 return ext | 0x202020;
1176 ext = (ext << 8) + filename[i];
1177 }
1178 return 0;
1179#else
1180 const char *p;
1181 int ext;
1182 for (p = filename; *p != '\0'; p++);
1183 ext = 0;
1184 for (;;) {
1185 if (--p <= filename || *p <= ' ')
1186 return 0; /* no filename extension or invalid character */
1187 if (*p == '.')
1188 return ext | 0x202020;
1189 ext = (ext << 8) + (*p & 0xff);
1190 }
1191#endif
1192}
1193
1194FILE_FUNC abool is_our_ext(int ext)
1195{
1196 switch (ext) {
1197 case ASAP_EXT('C', 'M', 'C'):
1198 case ASAP_EXT('C', 'M', 'R'):
1199 case ASAP_EXT('D', 'M', 'C'):
1200 case ASAP_EXT('M', 'P', 'D'):
1201 case ASAP_EXT('M', 'P', 'T'):
1202 case ASAP_EXT('R', 'M', 'T'):
1203 case ASAP_EXT('S', 'A', 'P'):
1204 case ASAP_EXT('T', 'M', '2'):
1205 case ASAP_EXT('T', 'M', '8'):
1206 case ASAP_EXT('T', 'M', 'C'):
1207 return TRUE;
1208 default:
1209 return FALSE;
1210 }
1211}
1212
1213ASAP_FUNC abool ASAP_IsOurFile(STRING filename)
1214{
1215 int ext = get_packed_ext(filename);
1216 return is_our_ext(ext);
1217}
1218
1219ASAP_FUNC abool ASAP_IsOurExt(STRING ext)
1220{
1221#ifdef JAVA
1222 return ext.length() == 3
1223 && is_our_ext(ASAP_EXT(ext.charAt(0), ext.charAt(1), ext.charAt(2)));
1224#else
1225 return ext[0] > ' ' && ext[1] > ' ' && ext[2] > ' ' && ext[3] == '\0'
1226 && is_our_ext(ASAP_EXT(ext[0], ext[1], ext[2]));
1227#endif
1228}
1229
1230FILE_FUNC abool parse_file(ASAP_State PTR ast, ASAP_ModuleInfo PTR module_info,
1231 STRING filename, const byte ARRAY module, int module_len)
1232{
1233 int i;
1234#ifdef JAVA
1235 int basename = 0;
1236 int ext = -1;
1237 for (i = 0; i < filename.length(); i++) {
1238 int c = filename.charAt(i);
1239 if (c == '/' || c == '\\')
1240 basename = i + 1;
1241 else if (c == '.')
1242 ext = i;
1243 }
1244 if (ext < 0)
1245 ext = i;
1246 module_info.author = "";
1247 module_info.name = filename.substring(basename, ext);
1248 module_info.date = "";
1249#elif defined(CSHARP)
1250 int basename = 0;
1251 int ext = -1;
1252 for (i = 0; i < filename.Length; i++) {
1253 int c = filename[i];
1254 if (c == '/' || c == '\\')
1255 basename = i + 1;
1256 else if (c == '.')
1257 ext = i;
1258 }
1259 if (ext < 0)
1260 ext = i;
1261 module_info.author = string.Empty;
1262 module_info.name = filename.Substring(basename, ext - basename);
1263 module_info.date = string.Empty;
1264#else
1265 const char *p;
1266 const char *basename = filename;
1267 const char *ext = NULL;
1268 for (p = filename; *p != '\0'; p++) {
1269 if (*p == '/' || *p == '\\')
1270 basename = p + 1;
1271 else if (*p == '.')
1272 ext = p;
1273 }
1274 if (ext == NULL)
1275 ext = p;
1276 module_info->author[0] = '\0';
1277 i = ext - basename;
1278 memcpy(module_info->name, basename, i);
1279 module_info->name[i] = '\0';
1280 module_info->date[0] = '\0';
1281#endif
1282 MODULE_INFO channels = 1;
1283 MODULE_INFO songs = 1;
1284 MODULE_INFO default_song = 0;
1285 for (i = 0; i < MAX_SONGS; i++) {
1286 MODULE_INFO durations[i] = -1;
1287 MODULE_INFO loops[i] = FALSE;
1288 }
1289 MODULE_INFO type = '?';
1290 MODULE_INFO fastplay = 312;
1291 MODULE_INFO music = -1;
1292 MODULE_INFO init = -1;
1293 MODULE_INFO player = -1;
1294 switch (get_packed_ext(filename)) {
1295 case ASAP_EXT('C', 'M', 'C'):
1296 return parse_cmc(ast, module_info, module, module_len, FALSE);
1297 case ASAP_EXT('C', 'M', 'R'):
1298 return parse_cmc(ast, module_info, module, module_len, TRUE);
1299 case ASAP_EXT('D', 'M', 'C'):
1300 MODULE_INFO fastplay = 156;
1301 return parse_cmc(ast, module_info, module, module_len, FALSE);
1302 case ASAP_EXT('M', 'P', 'D'):
1303 MODULE_INFO fastplay = 156;
1304 return parse_mpt(ast, module_info, module, module_len);
1305 case ASAP_EXT('M', 'P', 'T'):
1306 return parse_mpt(ast, module_info, module, module_len);
1307 case ASAP_EXT('R', 'M', 'T'):
1308 return parse_rmt(ast, module_info, module, module_len);
1309 case ASAP_EXT('S', 'A', 'P'):
1310 return parse_sap(ast, module_info, module, module_len);
1311 case ASAP_EXT('T', 'M', '2'):
1312 return parse_tm2(ast, module_info, module, module_len);
1313 case ASAP_EXT('T', 'M', '8'):
1314 case ASAP_EXT('T', 'M', 'C'):
1315 return parse_tmc(ast, module_info, module, module_len);
1316 default:
1317 return FALSE;
1318 }
1319}
1320
1321ASAP_FUNC abool ASAP_GetModuleInfo(ASAP_ModuleInfo PTR module_info, STRING filename,
1322 const byte ARRAY module, int module_len)
1323{
1324 return parse_file(NULL, module_info, filename, module, module_len);
1325}
1326
1327ASAP_FUNC abool ASAP_Load(ASAP_State PTR ast, STRING filename,
1328 const byte ARRAY module, int module_len)
1329{
1330 AST silence_cycles = 0;
1331 return parse_file(ast, ADDRESSOF AST module_info, filename, module, module_len);
1332}
1333
1334ASAP_FUNC void ASAP_DetectSilence(ASAP_State PTR ast, int seconds)
1335{
1336 AST silence_cycles = seconds * ASAP_MAIN_CLOCK;
1337}
1338
1339FILE_FUNC void call_6502(ASAP_State PTR ast, int addr, int max_scanlines)
1340{
1341 AST cpu_pc = addr;
1342 /* put a CIM at 0xd20a and a return address on stack */
1343 dPutByte(0xd20a, 0xd2);
1344 dPutByte(0x01fe, 0x09);
1345 dPutByte(0x01ff, 0xd2);
1346 AST cpu_s = 0xfd;
1347 Cpu_RunScanlines(ast, max_scanlines);
1348}
1349
1350/* 50 Atari frames for the initialization routine - some SAPs are self-extracting. */
1351#define SCANLINES_FOR_INIT (50 * 312)
1352
1353FILE_FUNC void call_6502_init(ASAP_State PTR ast, int addr, int a, int x, int y)
1354{
1355 AST cpu_a = a & 0xff;
1356 AST cpu_x = x & 0xff;
1357 AST cpu_y = y & 0xff;
1358 call_6502(ast, addr, SCANLINES_FOR_INIT);
1359}
1360
1361ASAP_FUNC void ASAP_PlaySong(ASAP_State PTR ast, int song, int duration)
1362{
1363 AST current_song = song;
1364 AST current_duration = duration;
1365 AST blocks_played = 0;
1366 AST silence_cycles_counter = AST silence_cycles;
1367 AST extra_pokey_mask = AST module_info.channels > 1 ? 0x10 : 0;
1368 PokeySound_Initialize(ast);
1369 AST cycle = 0;
1370 AST cpu_nz = 0;
1371 AST cpu_c = 0;
1372 AST cpu_vdi = 0;
1373 AST scanline_number = 0;
1374 AST next_scanline_cycle = 0;
1375 AST timer1_cycle = NEVER;
1376 AST timer2_cycle = NEVER;
1377 AST timer4_cycle = NEVER;
1378 AST irqst = 0xff;
1379 switch (AST module_info.type) {
1380 case 'B':
1381 call_6502_init(ast, AST module_info.init, song, 0, 0);
1382 break;
1383 case 'C':
1384 case 'c':
1385 case 'z':
1386 call_6502_init(ast, AST module_info.player + 3, 0x70, AST module_info.music, AST module_info.music >> 8);
1387 call_6502_init(ast, AST module_info.player + 3, 0x00, song, 0);
1388 break;
1389 case 'D':
1390 case 'S':
1391 AST cpu_a = song;
1392 AST cpu_x = 0x00;
1393 AST cpu_y = 0x00;
1394 AST cpu_s = 0xff;
1395 AST cpu_pc = AST module_info.init;
1396 break;
1397 case 'm':
1398 call_6502_init(ast, AST module_info.player, 0x00, AST module_info.music >> 8, AST module_info.music);
1399 call_6502_init(ast, AST module_info.player, 0x02, AST module_info.song_pos[song], 0);
1400 break;
1401 case 'r':
1402 call_6502_init(ast, AST module_info.player, AST module_info.song_pos[song], AST module_info.music, AST module_info.music >> 8);
1403 break;
1404 case 't':
1405 case 'T':
1406 call_6502_init(ast, AST module_info.player, 0x70, AST module_info.music >> 8, AST module_info.music);
1407 call_6502_init(ast, AST module_info.player, 0x00, song, 0);
1408 AST tmc_per_frame_counter = 1;
1409 break;
1410 }
1411 ASAP_MutePokeyChannels(ast, 0);
1412}
1413
1414ASAP_FUNC void ASAP_MutePokeyChannels(ASAP_State PTR ast, int mask)
1415{
1416 PokeySound_Mute(ast, ADDRESSOF AST base_pokey, mask);
1417 PokeySound_Mute(ast, ADDRESSOF AST extra_pokey, mask >> 4);
1418}
1419
1420ASAP_FUNC abool call_6502_player(ASAP_State PTR ast)
1421{
1422 int s;
1423 PokeySound_StartFrame(ast);
1424 switch (AST module_info.type) {
1425 case 'B':
1426 call_6502(ast, AST module_info.player, AST module_info.fastplay);
1427 break;
1428 case 'C':
1429 case 'c':
1430 case 'z':
1431 call_6502(ast, AST module_info.player + 6, AST module_info.fastplay);
1432 break;
1433 case 'D':
1434 s = AST cpu_s;
1435#define PUSH_ON_6502_STACK(x) dPutByte(0x100 + s, x); s = (s - 1) & 0xff
1436#define RETURN_FROM_PLAYER_ADDR 0xd200
1437 /* save 6502 state on 6502 stack */
1438 PUSH_ON_6502_STACK(AST cpu_pc >> 8);
1439 PUSH_ON_6502_STACK(AST cpu_pc & 0xff);
1440 PUSH_ON_6502_STACK(((AST cpu_nz | (AST cpu_nz >> 1)) & 0x80) + AST cpu_vdi + \
1441 ((AST cpu_nz & 0xff) == 0 ? Z_FLAG : 0) + AST cpu_c + 0x20);
1442 PUSH_ON_6502_STACK(AST cpu_a);
1443 PUSH_ON_6502_STACK(AST cpu_x);
1444 PUSH_ON_6502_STACK(AST cpu_y);
1445 /* RTS will jump to 6502 code that restores the state */
1446 PUSH_ON_6502_STACK((RETURN_FROM_PLAYER_ADDR - 1) >> 8);
1447 PUSH_ON_6502_STACK((RETURN_FROM_PLAYER_ADDR - 1) & 0xff);
1448 AST cpu_s = s;
1449 dPutByte(RETURN_FROM_PLAYER_ADDR, 0x68); /* PLA */
1450 dPutByte(RETURN_FROM_PLAYER_ADDR + 1, 0xa8); /* TAY */
1451 dPutByte(RETURN_FROM_PLAYER_ADDR + 2, 0x68); /* PLA */
1452 dPutByte(RETURN_FROM_PLAYER_ADDR + 3, 0xaa); /* TAX */
1453 dPutByte(RETURN_FROM_PLAYER_ADDR + 4, 0x68); /* PLA */
1454 dPutByte(RETURN_FROM_PLAYER_ADDR + 5, 0x40); /* RTI */
1455 AST cpu_pc = AST module_info.player;
1456 Cpu_RunScanlines(ast, AST module_info.fastplay);
1457 break;
1458 case 'S':
1459 Cpu_RunScanlines(ast, AST module_info.fastplay);
1460 {
1461 int i = dGetByte(0x45) - 1;
1462 dPutByte(0x45, i);
1463 if (i == 0)
1464 dPutByte(0xb07b, dGetByte(0xb07b) + 1);
1465 }
1466 break;
1467 case 'm':
1468 case 'r':
1469 case 'T':
1470 call_6502(ast, AST module_info.player + 3, AST module_info.fastplay);
1471 break;
1472 case 't':
1473 if (--AST tmc_per_frame_counter <= 0) {
1474 AST tmc_per_frame_counter = AST tmc_per_frame;
1475 call_6502(ast, AST module_info.player + 3, AST module_info.fastplay);
1476 }
1477 else
1478 call_6502(ast, AST module_info.player + 6, AST module_info.fastplay);
1479 break;
1480 }
1481 PokeySound_EndFrame(ast, AST module_info.fastplay * 114);
1482 if (AST silence_cycles > 0) {
1483 if (PokeySound_IsSilent(ADDRESSOF AST base_pokey)
1484 && PokeySound_IsSilent(ADDRESSOF AST extra_pokey)) {
1485 AST silence_cycles_counter -= AST module_info.fastplay * 114;
1486 if (AST silence_cycles_counter <= 0)
1487 return FALSE;
1488 }
1489 else
1490 AST silence_cycles_counter = AST silence_cycles;
1491 }
1492 return TRUE;
1493}
1494
1495FILE_FUNC int milliseconds_to_blocks(int milliseconds)
1496{
1497 return milliseconds * (ASAP_SAMPLE_RATE / 100) / 10;
1498}
1499
1500ASAP_FUNC void ASAP_Seek(ASAP_State PTR ast, int position)
1501{
1502 int block = milliseconds_to_blocks(position);
1503 if (block < AST blocks_played)
1504 ASAP_PlaySong(ast, AST current_song, AST current_duration);
1505 while (AST blocks_played + AST samples - AST sample_index < block) {
1506 AST blocks_played += AST samples - AST sample_index;
1507 call_6502_player(ast);
1508 }
1509 AST sample_index += block - AST blocks_played;
1510 AST blocks_played = block;
1511}
1512
1513ASAP_FUNC int ASAP_Generate(ASAP_State PTR ast, VOIDPTR buffer, int buffer_len,
1514 ASAP_SampleFormat format)
1515{
1516 int block_shift;
1517 int buffer_blocks;
1518 int block;
1519 if (AST silence_cycles > 0 && AST silence_cycles_counter <= 0)
1520 return 0;
1521 block_shift = (AST module_info.channels - 1) + (format != ASAP_FORMAT_U8 ? 1 : 0);
1522 buffer_blocks = buffer_len >> block_shift;
1523 if (AST current_duration > 0) {
1524 int total_blocks = milliseconds_to_blocks(AST current_duration);
1525 if (buffer_blocks > total_blocks - AST blocks_played)
1526 buffer_blocks = total_blocks - AST blocks_played;
1527 }
1528 block = 0;
1529 do {
1530 int blocks = PokeySound_Generate(ast, buffer, block << block_shift, buffer_blocks - block, format);
1531 AST blocks_played += blocks;
1532 block += blocks;
1533 } while (block < buffer_blocks && call_6502_player(ast));
1534 return block << block_shift;
1535}
1536
1537#if !defined(JAVA) && !defined(CSHARP)
1538
1539abool ASAP_ChangeExt(char *filename, const char *ext)
1540{
1541 char *dest = NULL;
1542 while (*filename != '\0') {
1543 if (*filename == '/' || *filename == '\\')
1544 dest = NULL;
1545 else if (*filename == '.')
1546 dest = filename + 1;
1547 filename++;
1548 }
1549 if (dest == NULL)
1550 return FALSE;
1551 strcpy(dest, ext);
1552 return TRUE;
1553}
1554
1555abool ASAP_CanSetModuleInfo(const char *filename)
1556{
1557 int ext = get_packed_ext(filename);
1558 return ext == ASAP_EXT('S', 'A', 'P');
1559}
1560
1561static byte *put_string(byte *dest, const char *str)
1562{
1563 while (*str != '\0')
1564 *dest++ = *str++;
1565 return dest;
1566}
1567
1568static byte *put_dec(byte *dest, int value)
1569{
1570 if (value >= 10) {
1571 dest = put_dec(dest, value / 10);
1572 value %= 10;
1573 }
1574 *dest++ = '0' + value;
1575 return dest;
1576}
1577
1578static byte *put_text_tag(byte *dest, const char *tag, const char *value)
1579{
1580 dest = put_string(dest, tag);
1581 *dest++ = ' ';
1582 *dest++ = '"';
1583 if (*value == '\0')
1584 value = "<?>";
1585 while (*value != '\0') {
1586 if (*value < ' ' || *value > 'z' || *value == '"' || *value == '`')
1587 return NULL;
1588 *dest++ = *value++;
1589 }
1590 *dest++ = '"';
1591 *dest++ = '\r';
1592 *dest++ = '\n';
1593 return dest;
1594}
1595
1596static byte *put_hex_tag(byte *dest, const char *tag, int value)
1597{
1598 int i;
1599 if (value < 0)
1600 return dest;
1601 dest = put_string(dest, tag);
1602 *dest++ = ' ';
1603 for (i = 12; i >= 0; i -= 4) {
1604 int digit = (value >> i) & 0xf;
1605 *dest++ = (byte) (digit + (digit < 10 ? '0' : 'A' - 10));
1606 }
1607 *dest++ = '\r';
1608 *dest++ = '\n';
1609 return dest;
1610}
1611
1612static byte *put_dec_tag(byte *dest, const char *tag, int value)
1613{
1614 dest = put_string(dest, tag);
1615 *dest++ = ' ';
1616 dest = put_dec(dest, value);
1617 *dest++ = '\r';
1618 *dest++ = '\n';
1619 return dest;
1620}
1621
1622static byte *start_sap_header(byte *dest, const ASAP_ModuleInfo *module_info)
1623{
1624 dest = put_string(dest, "SAP\r\n");
1625 dest = put_text_tag(dest, "AUTHOR", module_info->author);
1626 if (dest == NULL)
1627 return NULL;
1628 dest = put_text_tag(dest, "NAME", module_info->name);
1629 if (dest == NULL)
1630 return NULL;
1631 dest = put_text_tag(dest, "DATE", module_info->date);
1632 if (dest == NULL)
1633 return NULL;
1634 if (module_info->songs > 1) {
1635 dest = put_dec_tag(dest, "SONGS", module_info->songs);
1636 if (module_info->default_song > 0)
1637 dest = put_dec_tag(dest, "DEFSONG", module_info->default_song);
1638 }
1639 if (module_info->channels > 1)
1640 dest = put_string(dest, "STEREO\r\n");
1641 return dest;
1642}
1643
1644static byte *put_durations(byte *dest, const ASAP_ModuleInfo *module_info)
1645{
1646 int song;
1647 for (song = 0; song < module_info->songs; song++) {
1648 if (module_info->durations[song] < 0)
1649 break;
1650 dest = put_string(dest, "TIME ");
1651 ASAP_DurationToString((char *) dest, module_info->durations[song]);
1652 while (*dest != '\0')
1653 dest++;
1654 if (module_info->loops[song])
1655 dest = put_string(dest, " LOOP");
1656 *dest++ = '\r';
1657 *dest++ = '\n';
1658 }
1659 return dest;
1660}
1661
1662static byte *put_sap_header(byte *dest, const ASAP_ModuleInfo *module_info, char type, int music, int init, int player)
1663{
1664 dest = start_sap_header(dest, module_info);
1665 if (dest == NULL)
1666 return NULL;
1667 dest = put_string(dest, "TYPE ");
1668 *dest++ = type;
1669 *dest++ = '\r';
1670 *dest++ = '\n';
1671 if (module_info->fastplay != 312)
1672 dest = put_dec_tag(dest, "FASTPLAY", module_info->fastplay);
1673 dest = put_hex_tag(dest, "MUSIC", music);
1674 dest = put_hex_tag(dest, "INIT", init);
1675 dest = put_hex_tag(dest, "PLAYER", player);
1676 dest = put_durations(dest, module_info);
1677 return dest;
1678}
1679
1680int ASAP_SetModuleInfo(const ASAP_ModuleInfo *module_info, const byte ARRAY module,
1681 int module_len, byte ARRAY out_module)
1682{
1683 byte *dest;
1684 int i;
1685 if (memcmp(module, "SAP\r\n", 5) != 0)
1686 return -1;
1687 dest = start_sap_header(out_module, module_info);
1688 if (dest == NULL)
1689 return -1;
1690 i = 5;
1691 while (i < module_len && module[i] != 0xff) {
1692 if (memcmp(module + i, "AUTHOR ", 7) == 0
1693 || memcmp(module + i, "NAME ", 5) == 0
1694 || memcmp(module + i, "DATE ", 5) == 0
1695 || memcmp(module + i, "SONGS ", 6) == 0
1696 || memcmp(module + i, "DEFSONG ", 8) == 0
1697 || memcmp(module + i, "STEREO", 6) == 0
1698 || memcmp(module + i, "TIME ", 5) == 0) {
1699 while (i < module_len && module[i++] != 0x0a);
1700 }
1701 else {
1702 int b;
1703 do {
1704 b = module[i++];
1705 *dest++ = b;
1706 } while (i < module_len && b != 0x0a);
1707 }
1708 }
1709 dest = put_durations(dest, module_info);
1710 module_len -= i;
1711 memcpy(dest, module + i, module_len);
1712 dest += module_len;
1713 return dest - out_module;
1714}
1715
1716#define RMT_INIT 0x0c80
1717#define TM2_INIT 0x1080
1718
1719const char *ASAP_CanConvert(const char *filename, const ASAP_ModuleInfo *module_info,
1720 const byte ARRAY module, int module_len)
1721{
1722 switch (module_info->type) {
1723 case 'B':
1724 if (module_info->init == 0x4f3 || module_info->init == 0xf4f3 || module_info->init == 0x4ef)
1725 return module_info->fastplay == 156 ? "mpd" : "mpt";
1726 if (module_info->init == RMT_INIT)
1727 return "rmt";
1728 if ((module_info->init == 0x4f5 || module_info->init == 0xf4f5 || module_info->init == 0x4f2)
1729 || ((module_info->init == 0x4e7 || module_info->init == 0xf4e7 || module_info->init == 0x4e4) && module_info->fastplay == 156)
1730 || ((module_info->init == 0x4e5 || module_info->init == 0xf4e5 || module_info->init == 0x4e2) && (module_info->fastplay == 104 || module_info->fastplay == 78)))
1731 return "tmc";
1732 if (module_info->init == TM2_INIT)
1733 return "tm2";
1734 break;
1735 case 'C':
1736 if (module_info->player == 0x500 || module_info->player == 0xf500) {
1737 if (module_info->fastplay == 156)
1738 return "dmc";
1739 return module[module_len - 170] == 0x1e ? "cmr" : "cmc";
1740 }
1741 break;
1742 case 'c':
1743 case 'z':
1744 case 'm':
1745 case 'r':
1746 case 't':
1747 case 'T':
1748 return "sap";
1749 default:
1750 break;
1751 }
1752 return NULL;
1753}
1754
1755int ASAP_Convert(const char *filename, const ASAP_ModuleInfo *module_info,
1756 const byte ARRAY module, int module_len, byte ARRAY out_module)
1757{
1758 int out_len;
1759 byte *dest;
1760 int addr;
1761 int player;
1762 static const int tmc_player[4] = { 3, -9, -10, -10 };
1763 static const int tmc_init[4] = { -14, -16, -17, -17 };
1764 switch (module_info->type) {
1765 case 'B':
1766 case 'C':
1767 out_len = module[module_info->header_len + 4] + (module[module_info->header_len + 5] << 8)
1768 - module[module_info->header_len + 2] - (module[module_info->header_len + 3] << 8) + 7;
1769 if (out_len < 7 || module_info->header_len + out_len >= module_len)
1770 return -1;
1771 memcpy(out_module, module + module_info->header_len, out_len);
1772 return out_len;
1773 case 'c':
1774 case 'z':
1775 dest = put_sap_header(out_module, module_info, 'C', module_info->music, -1, module_info->player);
1776 if (dest == NULL)
1777 return -1;
1778 memcpy(dest, module, module_len);
1779 dest += module_len;
1780 memcpy(dest, cmc_obx + 2, sizeof(cmc_obx) - 2);
1781 if (module_info->type == 'z')
1782 memcpy(dest + 4 + CMR_BASS_TABLE_OFFSET, cmr_bass_table, sizeof(cmr_bass_table));
1783 dest += sizeof(cmc_obx) - 2;
1784 return dest - out_module;
1785 case 'm':
1786 if (module_info->songs != 1) {
1787 addr = module_info->player - 17 - module_info->songs;
1788 dest = put_sap_header(out_module, module_info, 'B', -1, module_info->player - 17, module_info->player + 3);
1789 }
1790 else {
1791 addr = module_info->player - 13;
1792 dest = put_sap_header(out_module, module_info, 'B', -1, addr, module_info->player + 3);
1793 }
1794 if (dest == NULL)
1795 return -1;
1796 memcpy(dest, module, module_len);
1797 dest += module_len;
1798 *dest++ = (byte) addr;
1799 *dest++ = (byte) (addr >> 8);
1800 *dest++ = mpt_obx[4];
1801 *dest++ = mpt_obx[5];
1802 if (module_info->songs != 1) {
1803 memcpy(dest, module_info->song_pos, module_info->songs);
1804 dest += module_info->songs;
1805 *dest++ = 0x48; /* pha */
1806 }
1807 *dest++ = 0xa0; /* ldy #<music */
1808 *dest++ = (byte) module_info->music;
1809 *dest++ = 0xa2; /* ldx #>music */
1810 *dest++ = (byte) (module_info->music >> 8);
1811 *dest++ = 0xa9; /* lda #0 */
1812 *dest++ = 0;
1813 *dest++ = 0x20; /* jsr player */
1814 *dest++ = (byte) module_info->player;
1815 *dest++ = (byte) (module_info->player >> 8);
1816 if (module_info->songs != 1) {
1817 *dest++ = 0x68; /* pla */
1818 *dest++ = 0xa8; /* tay */
1819 *dest++ = 0xbe; /* ldx song2pos,y */
1820 *dest++ = (byte) addr;
1821 *dest++ = (byte) (addr >> 8);
1822 }
1823 else {
1824 *dest++ = 0xa2; /* ldx #0 */
1825 *dest++ = 0;
1826 }
1827 *dest++ = 0xa9; /* lda #2 */
1828 *dest++ = 2;
1829 memcpy(dest, mpt_obx + 6, sizeof(mpt_obx) - 6);
1830 dest += sizeof(mpt_obx) - 6;
1831 return dest - out_module;
1832 case 'r':
1833 dest = put_sap_header(out_module, module_info, 'B', -1, RMT_INIT, module_info->player + 3);
1834 if (dest == NULL)
1835 return -1;
1836 memcpy(dest, module, module_len);
1837 dest += module_len;
1838 *dest++ = (byte) RMT_INIT;
1839 *dest++ = (byte) (RMT_INIT >> 8);
1840 if (module_info->songs != 1) {
1841 addr = RMT_INIT + 10 + module_info->songs;
1842 *dest++ = (byte) addr;
1843 *dest++ = (byte) (addr >> 8);
1844 *dest++ = 0xa8; /* tay */
1845 *dest++ = 0xb9; /* lda song2pos,y */
1846 *dest++ = (byte) (RMT_INIT + 11);
1847 *dest++ = (byte) ((RMT_INIT + 11) >> 8);
1848 }
1849 else {
1850 *dest++ = (byte) (RMT_INIT + 8);
1851 *dest++ = (byte) ((RMT_INIT + 8) >> 8);
1852 *dest++ = 0xa9; /* lda #0 */
1853 *dest++ = 0;
1854 }
1855 *dest++ = 0xa2; /* ldx #<music */
1856 *dest++ = (byte) module_info->music;
1857 *dest++ = 0xa0; /* ldy #>music */
1858 *dest++ = (byte) (module_info->music >> 8);
1859 *dest++ = 0x4c; /* jmp player */
1860 *dest++ = (byte) module_info->player;
1861 *dest++ = (byte) (module_info->player >> 8);
1862 if (module_info->songs != 1) {
1863 memcpy(dest, module_info->song_pos, module_info->songs);
1864 dest += module_info->songs;
1865 }
1866 if (module_info->channels == 1) {
1867 memcpy(dest, rmt4_obx + 2, sizeof(rmt4_obx) - 2);
1868 dest += sizeof(rmt4_obx) - 2;
1869 }
1870 else {
1871 memcpy(dest, rmt8_obx + 2, sizeof(rmt8_obx) - 2);
1872 dest += sizeof(rmt8_obx) - 2;
1873 }
1874 return dest - out_module;
1875 case 't':
1876 player = module_info->player + tmc_player[module[0x25] - 1];
1877 addr = player + tmc_init[module[0x25] - 1];
1878 if (module_info->songs != 1)
1879 addr -= 3;
1880 dest = put_sap_header(out_module, module_info, 'B', -1, addr, player);
1881 if (dest == NULL)
1882 return -1;
1883 memcpy(dest, module, module_len);
1884 dest += module_len;
1885 *dest++ = (byte) addr;
1886 *dest++ = (byte) (addr >> 8);
1887 *dest++ = tmc_obx[4];
1888 *dest++ = tmc_obx[5];
1889 if (module_info->songs != 1)
1890 *dest++ = 0x48; /* pha */
1891 *dest++ = 0xa0; /* ldy #<music */
1892 *dest++ = (byte) module_info->music;
1893 *dest++ = 0xa2; /* ldx #>music */
1894 *dest++ = (byte) (module_info->music >> 8);
1895 *dest++ = 0xa9; /* lda #$70 */
1896 *dest++ = 0x70;
1897 *dest++ = 0x20; /* jsr player */
1898 *dest++ = (byte) module_info->player;
1899 *dest++ = (byte) (module_info->player >> 8);
1900 if (module_info->songs != 1) {
1901 *dest++ = 0x68; /* pla */
1902 *dest++ = 0xaa; /* tax */
1903 *dest++ = 0xa9; /* lda #0 */
1904 *dest++ = 0;
1905 }
1906 else {
1907 *dest++ = 0xa9; /* lda #$60 */
1908 *dest++ = 0x60;
1909 }
1910 switch (module[0x25]) {
1911 case 2:
1912 *dest++ = 0x06; /* asl 0 */
1913 *dest++ = 0;
1914 *dest++ = 0x4c; /* jmp player */
1915 *dest++ = (byte) module_info->player;
1916 *dest++ = (byte) (module_info->player >> 8);
1917 *dest++ = 0xa5; /* lda 0 */
1918 *dest++ = 0;
1919 *dest++ = 0xe6; /* inc 0 */
1920 *dest++ = 0;
1921 *dest++ = 0x4a; /* lsr @ */
1922 *dest++ = 0x90; /* bcc player+3 */
1923 *dest++ = 5;
1924 *dest++ = 0xb0; /* bcs player+6 */
1925 *dest++ = 6;
1926 break;
1927 case 3:
1928 case 4:
1929 *dest++ = 0xa0; /* ldy #1 */
1930 *dest++ = 1;
1931 *dest++ = 0x84; /* sty 0 */
1932 *dest++ = 0;
1933 *dest++ = 0xd0; /* bne player */
1934 *dest++ = 10;
1935 *dest++ = 0xc6; /* dec 0 */
1936 *dest++ = 0;
1937 *dest++ = 0xd0; /* bne player+6 */
1938 *dest++ = 12;
1939 *dest++ = 0xa0; /* ldy #3 */
1940 *dest++ = module[0x25];
1941 *dest++ = 0x84; /* sty 0 */
1942 *dest++ = 0;
1943 *dest++ = 0xd0; /* bne player+3 */
1944 *dest++ = 3;
1945 break;
1946 default:
1947 break;
1948 }
1949 memcpy(dest, tmc_obx + 6, sizeof(tmc_obx) - 6);
1950 dest += sizeof(tmc_obx) - 6;
1951 return dest - out_module;
1952 case 'T':
1953 dest = put_sap_header(out_module, module_info, 'B', -1, TM2_INIT, module_info->player + 3);
1954 if (dest == NULL)
1955 return -1;
1956 memcpy(dest, module, module_len);
1957 dest += module_len;
1958 *dest++ = (byte) TM2_INIT;
1959 *dest++ = (byte) (TM2_INIT >> 8);
1960 if (module_info->songs != 1) {
1961 *dest++ = (byte) (TM2_INIT + 16);
1962 *dest++ = (byte) ((TM2_INIT + 16) >> 8);
1963 *dest++ = 0x48; /* pha */
1964 }
1965 else {
1966 *dest++ = (byte) (TM2_INIT + 14);
1967 *dest++ = (byte) ((TM2_INIT + 14) >> 8);
1968 }
1969 *dest++ = 0xa0; /* ldy #<music */
1970 *dest++ = (byte) module_info->music;
1971 *dest++ = 0xa2; /* ldx #>music */
1972 *dest++ = (byte) (module_info->music >> 8);
1973 *dest++ = 0xa9; /* lda #$70 */
1974 *dest++ = 0x70;
1975 *dest++ = 0x20; /* jsr player */
1976 *dest++ = (byte) module_info->player;
1977 *dest++ = (byte) (module_info->player >> 8);
1978 if (module_info->songs != 1) {
1979 *dest++ = 0x68; /* pla */
1980 *dest++ = 0xaa; /* tax */
1981 *dest++ = 0xa9; /* lda #0 */
1982 *dest++ = 0;
1983 }
1984 else {
1985 *dest++ = 0xa9; /* lda #0 */
1986 *dest++ = 0;
1987 *dest++ = 0xaa; /* tax */
1988 }
1989 *dest++ = 0x4c; /* jmp player */
1990 *dest++ = (byte) module_info->player;
1991 *dest++ = (byte) (module_info->player >> 8);
1992 memcpy(dest, tm2_obx + 2, sizeof(tm2_obx) - 2);
1993 dest += sizeof(tm2_obx) - 2;
1994 return dest - out_module;
1995 default:
1996 return -1;
1997 }
1998}
1999
2000#endif /* !defined(JAVA) && !defined(CSHARP) */