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