Add tools/ and manual/, move sources to src/
[dpadhero2.git] / tools / xm2btn / xm2btn.c
blob276f39a7260088603f2f82e94df17cb975d95886
1 /*
2 This file is part of xm2btn.
4 xm2btn is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 xm2btn is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with xm2btn. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <assert.h>
23 #include "xm.h"
24 #include "xm2btn.h"
26 enum MarkState {
27 NoMark,
28 BeginMark,
29 Marking,
30 EndMark
33 /* Converts the given slot to target types. */
34 static void slot_to_targets(const struct xm_pattern_slot *slot, int *target_types)
36 int lane_count = 0;
37 int lane_indexes[2]; /* Max 2 targets */
38 int i;
39 for (i = 0; i < 5; ++i)
40 target_types[i] = -1;
41 if (!slot->note)
42 return;
43 /* Note determines which lanes are used */
44 switch ((slot->note - 1) % 12) {
45 case 0: /* C = left */
46 lane_indexes[lane_count++] = 0;
47 break;
48 case 1: /* C# = left + B */
49 lane_indexes[lane_count++] = 0;
50 lane_indexes[lane_count++] = 3;
51 break;
52 case 2: /* D = right */
53 lane_indexes[lane_count++] = 1;
54 break;
55 case 3: /* D# = left + A */
56 lane_indexes[lane_count++] = 0;
57 lane_indexes[lane_count++] = 4;
58 break;
59 case 4: /* E = select */
60 lane_indexes[lane_count++] = 2;
61 break;
62 case 5: /* F = B */
63 lane_indexes[lane_count++] = 3;
64 break;
65 case 6: /* F# = right + B */
66 lane_indexes[lane_count++] = 1;
67 lane_indexes[lane_count++] = 3;
68 break;
69 case 7: /* G = A */
70 lane_indexes[lane_count++] = 4;
71 break;
72 case 8: /* G# = right + A */
73 lane_indexes[lane_count++] = 1;
74 lane_indexes[lane_count++] = 4;
75 break;
76 case 9: /* A, A#, B = B + A */
77 case 10:
78 case 11:
79 lane_indexes[lane_count++] = 3;
80 lane_indexes[lane_count++] = 4;
81 break;
83 /* Effect parameter determines target type */
84 assert(lane_count <= 2);
85 for (i = 0; i < lane_count; ++i) {
86 int type = (i == 0) ? (slot->effect_param >> 4) : (slot->effect_param & 0xF);
87 target_types[lane_indexes[i]] = type;
91 static int first_target_lane(int lanes_specifier)
93 switch (lanes_specifier) {
94 case 1: return 0;
95 case 2: return 1;
96 case 3: return 2;
97 case 4: return 3;
98 case 5: return 4;
99 case 6: return 0;
100 case 7: return 0;
101 case 8: return 1;
102 case 9: return 1;
103 case 10: return 3;
105 return -1;
108 static int second_target_lane(int lanes_specifier)
110 switch (lanes_specifier) {
111 case 6: return 3;
112 case 7: return 4;
113 case 8: return 3;
114 case 9: return 4;
115 case 10: return 4;
117 return -1;
120 static int targets_to_lanes_specifier(const int *target_types)
122 int mask = 0;
123 int i;
124 for (i = 0; i < 5; ++i) {
125 if (target_types[i] != -1)
126 mask |= 1 << i;
128 switch (mask) {
129 case 0x00:
130 return 0;
131 case 0x01:
132 return 1;
133 case 0x02:
134 return 2;
135 case 0x04:
136 return 3;
137 case 0x08:
138 return 4;
139 case 0x10:
140 return 5;
141 case 0x09:
142 return 6;
143 case 0x11:
144 return 7;
145 case 0x0A:
146 return 8;
147 case 0x12:
148 return 9;
149 case 0x18:
150 return 10;
151 default:
152 assert(0);
154 return 0;
157 struct output_stream {
158 FILE *fp;
159 int bit_index;
160 unsigned char bits;
161 int column;
164 static void output_stream_init(struct output_stream *stream, FILE *fp)
166 stream->fp = fp;
167 stream->bit_index = 0;
168 stream->bits = 0;
169 stream->column = 0;
172 static void output_stream_put_helper(struct output_stream *stream)
174 if (stream->column == 16) {
175 if (stream->fp)
176 fprintf(stream->fp, "\n");
177 stream->column = 0;
179 if (stream->fp)
180 fprintf(stream->fp, "%s$%.2X", (stream->column == 0) ? ".db " : ",", stream->bits);
181 ++stream->column;
182 stream->bits = 0;
185 static void output_stream_put_bits(struct output_stream *stream, int n, int v)
187 int i;
188 /* fprintf(stdout, "output %d %X\n", n, v); */
189 for (i = n-1; i >= 0; --i) {
190 if (!(stream->bit_index & 7) && stream->bit_index)
191 output_stream_put_helper(stream);
192 stream->bits |= ((v & (1 << i)) >> i) << (7 - (stream->bit_index & 7));
193 ++stream->bit_index;
197 static void output_stream_flush(struct output_stream *stream)
199 if (!stream->bit_index)
200 return;
201 output_stream_put_helper(stream);
204 static void output_delay(struct output_stream *stream, int delay, int *pospos)
206 int buf[256];
207 int pos = 0;
208 assert(delay);
209 while (delay) {
210 static int factors[8] = { 1, 2, 4, 8, 12, 16, 24, 32 };
211 int i;
212 for (i = 7; i >= 0; --i) {
213 int rem = delay % factors[i];
214 if (!rem) {
215 assert(pos < 256);
216 buf[pos++] = i;
217 delay -= factors[i];
218 break;
224 int i;
225 for (i = pos-1; i > 0; --i) {
226 output_stream_put_bits(stream, 3, buf[i]);
227 /* Insert empty row (effectively extending the delay) */
228 output_stream_put_bits(stream, 4, 0);
229 ++(*pospos);
231 output_stream_put_bits(stream, 3, buf[0]);
235 static void log_target(int difficulty, int order_pos, int row, int lane, int type,
236 int *target_counts)
238 ++target_counts[difficulty*5*8 + lane*8 + type];
241 struct marker_info {
242 int data_offset;
243 int order_pos;
244 int pattern_row;
247 static void init_marker(struct marker_info *m, int data_offset, int order_pos, int pattern_row)
249 m->data_offset = data_offset;
250 m->order_pos = order_pos;
251 m->pattern_row = pattern_row;
255 Converts the given \a xm to D-Pad hero button data; writes the 6502
256 assembly language representation of the song to \a out.
258 void convert_xm_to_btn(const struct xm *xm, const struct xm2btn_options *options, FILE *out)
260 int difficulty;
261 static const int channel_base = 5; /* Our data begins in channel 5 */
262 static const char *difficulty_strings[3] = { "easy", "normal", "hard" };
263 for (difficulty = 0; difficulty < 3; ++difficulty) {
264 int pass;
265 int pos;
266 int marker_count = 0;
267 struct marker_info markers[64];
268 enum MarkState mark_state = NoMark;
269 int marker_pos;
270 /* The 1st pass counts the number of items */
271 /* The 2nd pass outputs the data */
272 for (pass = 0; pass < 2; ++pass) {
273 int order_pos;
274 int aux_delay = 0;
275 struct output_stream stream;
276 output_stream_init(&stream, (pass == 1) ? out : 0);
277 /* 1st marker is always beginning of song */
278 init_marker(&markers[marker_count++], 0, 0, 0);
279 if (pass == 1) {
280 /* Output the header */
281 int items_per_chunk;
282 static const int chunk_count = 40; /* Total number of progress chunks (UI-dependent) */
283 int length = pos;
284 items_per_chunk = length / chunk_count;
285 assert(items_per_chunk < 256);
286 fprintf(out, "%s%s:\n", options->label_prefix, difficulty_strings[difficulty]);
287 fprintf(out, ".db $%.2X,$%.2X\n", xm->header.default_tempo + 1, items_per_chunk);
288 fprintf(out, ".dw %s%s_data\n", options->label_prefix, difficulty_strings[difficulty]);
289 /* Markers */
291 int i;
292 for (i = 0; i < marker_count; ++i) {
293 const struct marker_info *m = &markers[i];
294 fprintf(out, ".dw $%.4X : .db $%.2X,$%.2X\n",
295 m->data_offset, m->order_pos, m->pattern_row);
299 fprintf(out, "%s%s_data:\n", options->label_prefix, difficulty_strings[difficulty]);
301 pos = 0;
302 for (order_pos = 0; order_pos < xm->header.song_length; ++order_pos) {
303 int row;
304 int first = 1;
305 int delay = 0;
306 const struct xm_pattern *pattern = &xm->patterns[xm->header.pattern_order_table[order_pos]];
307 for (row = 0; row < pattern->row_count; ++row) {
308 int target_types[5];
309 int lanes;
310 const struct xm_pattern_slot *row_data = &pattern->data[row * xm->header.channel_count];
311 const struct xm_pattern_slot *slot = row_data + channel_base + difficulty;
312 if (slot->effect_type == 7) {
313 /* Marker begin/end */
314 if (mark_state == NoMark) {
315 mark_state = BeginMark;
316 } else {
317 if (mark_state != Marking) {
318 fprintf(stderr, "bad marker at order %.2X, row %.2X, difficulty %d\n",
319 order_pos, row, difficulty);
321 assert(mark_state == Marking);
322 mark_state = EndMark;
325 slot_to_targets(slot, target_types);
326 lanes = targets_to_lanes_specifier(target_types);
327 if (lanes) {
328 if (aux_delay != 0) {
329 /* "Flush" delay from previous pattern */
330 output_delay(&stream, aux_delay, &pos);
331 aux_delay = 0;
333 if (delay != 0) {
334 if (first) {
335 if (mark_state == BeginMark) {
336 marker_pos = pos;
337 if (pass == 0)
338 init_marker(&markers[marker_count++], stream.bit_index, order_pos, /*row=*/0);
339 mark_state = Marking;
340 } else if (mark_state == EndMark) {
341 output_stream_put_bits(&stream, 4, 0xE); /* end-marker */
342 mark_state = NoMark;
344 output_stream_put_bits(&stream, 4, 0); /* empty row */
345 ++pos;
347 output_delay(&stream, delay, &pos);
349 if (mark_state == BeginMark) {
350 marker_pos = pos;
351 if (pass == 0)
352 init_marker(&markers[marker_count++], stream.bit_index, order_pos, row);
353 mark_state = Marking;
354 } else if (mark_state == EndMark) {
355 output_stream_put_bits(&stream, 4, 0xE); /* end-marker */
356 mark_state = NoMark;
358 output_stream_put_bits(&stream, 4, lanes);
360 int l1 = first_target_lane(lanes);
361 assert(l1 != -1);
362 int t1 = target_types[l1];
363 output_stream_put_bits(&stream, 1, t1 ? 1 : 0); /* extended or normal */
364 if (t1)
365 output_stream_put_bits(&stream, 3, t1 - 1);
366 if (pass && options->log) {
367 log_target(difficulty, order_pos, row, l1, t1,
368 options->target_counts);
371 if (lanes > 5) {
372 int l2 = second_target_lane(lanes);
373 assert(l2 != -1);
374 int t2 = target_types[l2];
375 output_stream_put_bits(&stream, 1, t2 ? 1 : 0); /* extended or normal */
376 if (t2)
377 output_stream_put_bits(&stream, 3, t2 - 1);
378 if (pass && options->log) {
379 log_target(difficulty, order_pos, row, l2, t2,
380 options->target_counts);
383 ++pos;
384 delay = 1;
385 first = 0;
386 } else {
387 ++delay;
390 aux_delay = delay;
392 /* Terminate data: delay=0, end-of-data-marker=0x0F */
393 output_stream_put_bits(&stream, 3, 0);
394 output_stream_put_bits(&stream, 4, 0x0F);
395 output_stream_flush(&stream);
396 if (pass == 1)
397 fprintf(out, "\n");