source: abuse/trunk/src/sdl2port/hmi.cpp @ 731

Last change on this file since 731 was 731, checked in by jjsimpso, 4 years ago

sdlport: Add support for SDL2, inculding the game controller API. Configure
script will check for SDL2 and use it if present, otherwise it will fall
back to SDL v1. Some old SDL v1 features not implemented yet on SDL2, such
as saving screenshots.

core: Small change for SDL2 game controller API to disable the game
controller in the save/load game menu.

Summary of game controller API changes:

  • Enable with -gamepad
  • Tested with PS3 dualshock 3.
  • D-pad moves, right analog controls aiming
  • X: change weapon, []: jump, R1: fire, L1: special ability, /\:use/activate
  • All menu navigation requires the mouse, which is disabled during normal game play.
File size: 9.6 KB
Line 
1//
2//  Abuse - dark 2D side-scrolling platform game
3//
4//  Copyright (c) 2011 Jochen Schleu <jjs@jjs.at>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9//
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include <cstring>
16#include <cstdlib>
17#include <cstdio>
18
19#include "common.h"
20
21// Load Abuse HMI files and covert them to standard Midi format
22//
23// HMI files differ from Midi files in the following ways:
24// - there is a header giving offsets to the tracks and various other
25//   information (unknown)
26// - note-on events include the duration of the note instead of dedicated
27//   note-off events
28// - additional 0xFE event with variable length, purpose unknown
29//
30// This converter does the bare minimum to get Abuse HMI files to convert.
31// The bpm and header information is fixed and not read from the file (except
32// the number of tracks). HMI files make use of running status notation, the
33// converted files don't.
34
35#define MAX_NOTE_OFF_EVENTS 30
36
37struct NoteOffEvent
38{
39    uint32_t time;
40    uint8_t command;
41    uint8_t note;
42};
43
44NoteOffEvent note_off_events[MAX_NOTE_OFF_EVENTS];
45
46static uint32_t get_int_from_buffer(uint8_t* buffer)
47{
48    return (buffer[3] << 24) + (buffer[2] << 16)
49             + (buffer[1] << 8) + (buffer[0]);
50}
51
52static void write_big_endian_number(uint32_t le, uint8_t* buffer)
53{
54    buffer[3] = (le & 0x000000FF);
55    buffer[2] = (le & 0x0000FF00) >> 8;
56    buffer[1] = (le & 0x00FF0000) >> 16;
57    buffer[0] = (le & 0xFF000000) >> 24;
58}
59
60static int compare_times(const void* a, const void* b)
61{
62    NoteOffEvent const *ea = (NoteOffEvent const *)a;
63    NoteOffEvent const *eb = (NoteOffEvent const *)b;
64
65    return ea->time < eb->time ? -1 : ea->time == eb->time ? 0 : 1;
66}
67
68// Variable length number code
69// from: http://www.chriswareham.demon.co.uk/midifiles/variable_length.html
70static uint32_t read_time_value(uint8_t* &buffer)
71{
72    uint32_t value;
73    uint8_t c;
74
75    if ((value = *buffer++) & 0x80)
76    {
77        value &= 0x7F;
78        do
79        {
80            value = (value << 7) + ((c = *buffer++) & 0x7F);
81        }
82        while (c & 0x80);
83    }
84
85    return value;
86}
87
88static void write_time_value(uint32_t time, uint8_t* &buffer)
89{
90    uint32_t value_buffer = time & 0x7F;
91
92    while (time >>= 7)
93    {
94        value_buffer <<= 8;
95        value_buffer |= ((time & 0x7F) | 0x80);
96    }
97
98    while (1)
99    {
100        *buffer++ = value_buffer;
101        if (value_buffer & 0x80)
102            value_buffer >>= 8;
103        else
104            break;
105    }
106}
107
108static void remember_note_off_event(uint32_t time, uint8_t cmd, uint8_t note)
109{
110    for (int i = 0; i < MAX_NOTE_OFF_EVENTS; i++)
111    {
112        if (note_off_events[i].time == 0xFFFFFFFF)
113        {
114            note_off_events[i].time = time;
115            note_off_events[i].command = cmd;
116            note_off_events[i].note = note;
117            break;
118        }
119    }
120
121    // Sort the note off array by the time
122    qsort(note_off_events, MAX_NOTE_OFF_EVENTS, sizeof(NoteOffEvent), compare_times);
123}
124
125static void check_for_note_off_events(uint32_t &current_time,
126                                      uint32_t &last_time, uint8_t* &buffer)
127{
128    for (int i = 0; i < MAX_NOTE_OFF_EVENTS; i++)
129    {
130        if (note_off_events[i].time == 0xFFFFFFFF)
131            break;
132
133        if (note_off_events[i].time < current_time)
134        {
135            // Add event
136            write_time_value(note_off_events[i].time - last_time, buffer);
137            last_time = note_off_events[i].time;
138
139            *buffer++ = note_off_events[i].command;
140            *buffer++ = note_off_events[i].note;
141            *buffer++ = 0x00;
142
143            // Remove event from queue
144            note_off_events[i].time = 0xFFFFFFFF;
145        }
146    }
147
148    // Sort the note off array by the time
149    qsort(note_off_events, MAX_NOTE_OFF_EVENTS, sizeof(NoteOffEvent), compare_times);
150}
151
152static void convert_hmi_track(uint8_t* input,
153                              uint32_t input_size, uint8_t* &output)
154{
155    int done = 0;
156    uint8_t current_command = 0;
157    uint8_t current_value = 0;
158    uint32_t current_time = 0;
159    uint32_t last_time = 0;
160    uint8_t* start_of_buffer = output;
161    uint8_t* start_of_input = input;
162
163    memset(note_off_events, 0xFF, sizeof(NoteOffEvent) * MAX_NOTE_OFF_EVENTS);
164
165    // Midi data offset is at 0x57 from track start
166    input += input[0x57];
167
168    // Write track header, leave length as zero for now
169    uint8_t track_header[] = { 0x4D, 0x54, 0x72, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00};
170    memcpy(output, track_header, 8);
171    output += 8;
172
173    while (!done)
174    {
175        // Read variable length time
176        current_time += read_time_value(input);
177
178        // Next comes either a command (>= 0x80) or data (running status)
179        current_value = *input++;
180        if (current_value >= 0x80)
181        {
182            // Is command, make current, increase data pointer
183            current_command = current_value;
184            current_value = *input++;
185        }
186
187        // Check if note off events have to be inserted here
188        check_for_note_off_events(current_time, last_time, output);
189
190        if (current_command != 0xFE)
191        {
192            // Write variable length time to output
193            write_time_value(current_time - last_time, output);
194            last_time = current_time;
195        }
196
197        // Write command, no running status in output
198        if (current_command != 0xFE)
199            *output++ = current_command;
200
201        switch (current_command & 0xF0)
202        {
203            // 1 data byte
204        case 0xC0: // Program change
205        case 0xD0: // Channel aftertouch
206            *output++ = current_value;
207            break;
208
209            // 2 data bytes
210        case 0x80: // Note off, does not occur in HMI
211        case 0xA0: // Aftertouch
212        case 0xB0: // Controller
213        case 0xE0: // Pitch bend
214            *output++ = current_value;
215            *output++ = *input++;
216            break;
217
218            // 3 data bytes
219        case 0x90: // Note on, non-standard, HMI files specify the duration as a third param
220            *output++ = current_value;
221            *output++ = *input++;
222            remember_note_off_event(current_time + read_time_value(input), current_command, current_value);
223            break;
224
225        case 0xF0: // Meta event
226            if (current_command == 0xFE)
227            {
228                // HMI specific event, variable length depending on type
229                switch (current_value)
230                {
231                case 0x10:
232                    input += 2;
233                    input += *input;
234                    input += 5;
235                    break;
236                case 0x14:
237                    input += 2;
238                    break;
239                case 0x15:
240                    input += 6;
241                    break;
242                }
243            }
244            else
245            {
246                // Only process end marker
247                *output++ = current_value;
248                *output++ = *input++;
249                done = 1;
250            }
251            break;
252
253        default:
254            // error?
255            break;
256        }
257
258        if ((uint32_t)(input - start_of_input) >= input_size)
259            break;
260    }
261
262    // Write end marker if necessary
263    if (done != 1)
264    {
265        uint8_t end_marker[] = { 0x00, 0xFF, 0x2F, 0x00 };
266        memcpy(output, end_marker, 4);
267        output += 4;
268    }
269
270    // Update header with length of track
271    write_big_endian_number((uint32_t)(output - start_of_buffer - 8), &start_of_buffer[4]);
272}
273
274uint8_t* load_hmi(char const *filename, uint32_t &data_size)
275{
276    uint8_t* input_buffer;
277    uint8_t* output_buffer;
278
279    FILE* hmifile = fopen(filename, "rb");
280
281    if (hmifile == NULL)
282        return NULL;
283
284    fseek(hmifile, 0, SEEK_END);
285    uint32_t buffersize = ftell(hmifile);
286    fseek(hmifile, 0, SEEK_SET);
287
288    input_buffer = (uint8_t*)malloc(buffersize);
289    fread(input_buffer, 1, buffersize, hmifile);
290    fclose(hmifile);
291
292    output_buffer = (uint8_t*)malloc(buffersize * 10); // Midi files can be larger than HMI files
293    uint8_t* output_buffer_ptr = output_buffer;
294
295    // Offset to tracks is at 0x113
296    uint32_t offset_tracks = get_int_from_buffer(&input_buffer[0xE8]);
297    uint32_t next_offset = get_int_from_buffer(&input_buffer[0xF4]);
298
299    uint8_t num_tracks = (next_offset - offset_tracks) / sizeof(uint32_t);
300
301    // Write Midi file header
302    uint8_t midi_header[] = { 0x4D, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, (num_tracks + 1), 0x00, 0xC0 };
303    memcpy(output_buffer_ptr, midi_header, 14);
304    output_buffer_ptr += 14;
305
306    // Write additional first track with bpm info
307    uint8_t bpm_track[] = { 0x4D, 0x54, 0x72, 0x6B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0xFF, 0x51, 0x03, 0x18, 0x7F, 0xFF, 0x00, 0xFF, 0x2F, 0x00 };
308    memcpy(output_buffer_ptr, bpm_track, sizeof(bpm_track));
309    output_buffer_ptr += sizeof(bpm_track);
310
311    for (int i = 0; i < num_tracks; i++)
312    {
313        uint32_t trackposition = get_int_from_buffer(&input_buffer[offset_tracks + i * (sizeof(uint32_t))]);
314        uint32_t tracksize;
315        if (i == num_tracks - 1)
316            tracksize = buffersize - trackposition;
317        else
318            tracksize = get_int_from_buffer(&input_buffer[offset_tracks + (i + 1) * (sizeof(uint32_t))]) - trackposition;
319
320        convert_hmi_track(&input_buffer[trackposition], tracksize, output_buffer_ptr);
321    }
322
323    data_size = (uint32_t)(output_buffer_ptr - output_buffer);
324    output_buffer = (uint8_t*)realloc(output_buffer, data_size);
325
326    free(input_buffer);
327
328    return output_buffer;
329}
330
Note: See TracBrowser for help on using the repository browser.