Import blues from 436ab46f

This commit is contained in:
Gregory Montoir 2022-03-13 06:53:43 +08:00
parent b0274d5ce2
commit d4b94bbc15
18 changed files with 234 additions and 336 deletions

View File

@ -37,7 +37,7 @@ game_p2.o: $(P2_SRCS:.c=.o)
ld -r -o $@ $^ ld -r -o $@ $^
objcopy --localize-hidden $@ objcopy --localize-hidden $@
blues: main.o sys_sdl2.o util.o game_bb.o game_ja.o game_p2.o blues: main.o mixer.o sys_sdl2.o util.o game_bb.o game_ja.o game_p2.o
$(CC) $(LDFLAGS) -o $@ $^ $(SDL_LIBS) $(MODPLUG_LIBS) $(CC) $(LDFLAGS) -o $@ $^ $(SDL_LIBS) $(MODPLUG_LIBS)
clean: clean:

View File

@ -266,7 +266,7 @@ static void do_inter_screen() {
fade_out_palette(); fade_out_palette();
} }
void game_main() { static void game_run() {
play_music(0); play_music(0);
screen_init(); screen_init();
g_vars.start_level = 0; g_vars.start_level = 0;
@ -351,15 +351,11 @@ void game_main() {
} }
} }
static void game_run(const char *data_path) {
res_init(data_path, GAME_SCREEN_W * GAME_SCREEN_H);
sound_init();
game_main();
sound_fini();
res_fini();
}
EXPORT_SYMBOL struct game_t game_bb = { EXPORT_SYMBOL struct game_t game_bb = {
"Blues Brothers", "Blues Brothers",
res_init,
res_fini,
sound_init,
sound_fini,
game_run game_run
}; };

View File

@ -203,7 +203,6 @@ extern uint8_t *level_tilesdata_1e8c[];
/* game.c */ /* game.c */
extern void update_input(); extern void update_input();
extern void game_main();
/* level.c */ /* level.c */
extern void load_level_data(int num); extern void load_level_data(int num);

View File

@ -1,11 +1,9 @@
#include "game.h" #include "game.h"
#include "resource.h" #include "resource.h"
#include "sys.h" #include "mixer.h"
#include "util.h" #include "util.h"
#include <libmodplug/modplug.h>
#define PAULA_FREQ 3546897 #define PAULA_FREQ 3546897
static const struct { static const struct {
@ -40,17 +38,6 @@ static const char *_modules[] = {
"shot", "mod.shot" "shot", "mod.shot"
}; };
struct mixerchannel_t {
uint8_t *data;
uint32_t pos;
uint32_t step;
uint32_t size;
};
static const int _rate = SYS_AUDIO_FREQ;
static struct mixerchannel_t _channel;
static ModPlugFile *_mpf;
static uint8_t *load_file(const char *filename, int *size, uint8_t *buffer) { static uint8_t *load_file(const char *filename, int *size, uint8_t *buffer) {
FILE *fp = fopen_nocase(g_res.datapath, filename); FILE *fp = fopen_nocase(g_res.datapath, filename);
if (!fp) { if (!fp) {
@ -76,63 +63,19 @@ static uint8_t *load_file(const char *filename, int *size, uint8_t *buffer) {
return buffer; return buffer;
} }
static void mix(void *param, uint8_t *buf, int len) {
memset(buf, 0, len);
if (_mpf) {
const int count = ModPlug_Read(_mpf, buf, len);
if (count == 0) {
ModPlug_SeekOrder(_mpf, 0);
}
}
if (_channel.data) {
for (int i = 0; i < len; i += sizeof(int16_t)) {
const int pos = _channel.pos >> 16;
if (pos >= _channel.size) {
_channel.data = 0;
break;
}
const int sample = *(int16_t *)(buf + i) + ((int8_t)_channel.data[pos]) * 256;
*(int16_t *)(buf + i) = (sample < -32768 ? -32768 : (sample > 32767 ? 32767 : sample));
_channel.pos += _channel.step;
}
}
}
void sound_init() { void sound_init() {
ModPlug_Settings mp_settings;
memset(&mp_settings, 0, sizeof(mp_settings));
ModPlug_GetSettings(&mp_settings);
mp_settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION;
mp_settings.mChannels = 1;
mp_settings.mBits = 16;
mp_settings.mFrequency = _rate;
mp_settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
mp_settings.mLoopCount = -1;
ModPlug_SetSettings(&mp_settings);
g_sys.start_audio(mix, 0);
} }
void sound_fini() { void sound_fini() {
g_sys.stop_audio();
} }
void play_sound(int num) { void play_sound(int num) {
if (g_res.snd) { if (g_res.snd) {
g_sys.lock_audio(); g_mix.play_sound(g_res.snd + _sounds_amiga[num].offset * 2, _sounds_amiga[num].size, PAULA_FREQ / 0x358, 0);
_channel.data = g_res.snd + _sounds_amiga[num].offset * 2;
_channel.pos = 0;
_channel.step = ((PAULA_FREQ / 0x358) << 16) / _rate;
_channel.size = _sounds_amiga[num].size;
g_sys.unlock_audio();
} }
} }
void play_music(int num) { void play_music(int num) {
g_sys.lock_audio();
if (_mpf) {
ModPlug_Unload(_mpf);
_mpf = 0;
}
const char *filename = _modules[num * 2]; // Amiga const char *filename = _modules[num * 2]; // Amiga
int size = 0; int size = 0;
uint8_t *buf = load_file(filename, &size, 0); uint8_t *buf = load_file(filename, &size, 0);
@ -167,16 +110,15 @@ void play_music(int num) {
size += samples[i].size; size += samples[i].size;
} }
} }
_mpf = ModPlug_Load(buf, size); g_mix.play_music(buf, size);
free(buf); free(buf);
} }
} else { // ExoticA } else { // ExoticA
filename = _modules[num * 2 + 1]; filename = _modules[num * 2 + 1];
buf = load_file(filename, &size, 0); buf = load_file(filename, &size, 0);
if (buf) { if (buf) {
_mpf = ModPlug_Load(buf, size); g_mix.play_music(buf, size);
free(buf); free(buf);
} }
} }
g_sys.unlock_audio();
} }

View File

@ -63,7 +63,11 @@ struct options_t {
struct game_t { struct game_t {
const char *name; const char *name;
void (*run)(const char *data_path); void (*res_init)(const char *data_path, int vga_size);
void (*res_fini)();
void (*snd_init)();
void (*snd_fini)();
void (*run)();
}; };
#ifdef _WIN32 #ifdef _WIN32

View File

@ -309,7 +309,7 @@ void do_game_win_screen() {
} }
} }
void game_main() { static void game_run() {
play_music(0); play_music(0);
do_splash_screen(); do_splash_screen();
g_sys.set_screen_palette(common_palette_data, 0, 128, 6); g_sys.set_screen_palette(common_palette_data, 0, 128, 6);
@ -343,15 +343,11 @@ void game_main() {
} }
} }
static void game_run(const char *data_path) {
res_init(data_path, GAME_SCREEN_W * GAME_SCREEN_H);
sound_init();
game_main();
sound_fini();
res_fini();
}
EXPORT_SYMBOL struct game_t game_ja = { EXPORT_SYMBOL struct game_t game_ja = {
"Blues Brothers : Jukebox Adventure", "Blues Brothers: Jukebox Adventure",
res_init,
res_fini,
sound_init,
sound_fini,
game_run game_run
}; };

View File

@ -199,7 +199,6 @@ extern const uint8_t *player_anim_table[];
/* game.c */ /* game.c */
extern void update_input(); extern void update_input();
extern void game_main();
extern void do_game_over_screen(); extern void do_game_over_screen();
extern void do_game_win_screen(); extern void do_game_win_screen();
extern void do_difficulty_screen(); extern void do_difficulty_screen();

View File

@ -1,11 +1,9 @@
#include "game.h" #include "game.h"
#include "resource.h" #include "resource.h"
#include "sys.h" #include "mixer.h"
#include "util.h" #include "util.h"
#include <libmodplug/modplug.h>
#define MAX_SOUNDS 16 #define MAX_SOUNDS 16
static const char *_modules[] = { static const char *_modules[] = {
@ -16,63 +14,25 @@ static const char *_modules[] = {
"shoot.mod" "shoot.mod"
}; };
struct mixerchannel_t {
uint8_t *data;
uint32_t pos;
uint32_t step;
uint32_t size;
const int8_t *seq;
};
static struct { static struct {
uint16_t offset; uint16_t offset;
uint16_t size; uint16_t size;
} _samples[MAX_SOUNDS]; } _samples[MAX_SOUNDS];
static const int _rate = SYS_AUDIO_FREQ; static const int8_t *_seq;
static struct mixerchannel_t _channel;
static ModPlugFile *_mpf;
static void mix(void *param, uint8_t *buf, int len) { static void repeat_sound_cb(const uint8_t **data, uint32_t *size) {
memset(buf, 0, len); const int next = _seq ? *_seq++ : -1;
if (_mpf) { if (next < 0) {
const int count = ModPlug_Read(_mpf, buf, len); *data = 0;
if (count == 0) { *size = 0;
ModPlug_SeekOrder(_mpf, 0); } else {
} *data = g_res.samples + _samples[next].offset;
} *size = _samples[next].size;
if (_channel.data) {
for (int i = 0; i < len; i += sizeof(int16_t)) {
int pos = _channel.pos >> 16;
if (pos >= _channel.size) {
const int next = _channel.seq ? *_channel.seq++ : -1;
if (next < 0) {
_channel.data = 0;
break;
}
_channel.data = g_res.samples + _samples[next].offset;
_channel.pos = pos = 0;
_channel.size = _samples[next].size;
}
const int sample = *(int16_t *)(buf + i) + ((int8_t)_channel.data[pos]) * 256;
*(int16_t *)(buf + i) = (sample < -32768 ? -32768 : (sample > 32767 ? 32767 : sample));
_channel.pos += _channel.step;
}
} }
} }
void sound_init() { void sound_init() {
ModPlug_Settings mp_settings;
memset(&mp_settings, 0, sizeof(mp_settings));
ModPlug_GetSettings(&mp_settings);
mp_settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION;
mp_settings.mChannels = 1;
mp_settings.mBits = 16;
mp_settings.mFrequency = _rate;
mp_settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
mp_settings.mLoopCount = -1;
ModPlug_SetSettings(&mp_settings);
g_sys.start_audio(mix, 0);
uint16_t offset = 0; uint16_t offset = 0;
for (int i = 0; i < MAX_SOUNDS; ++i) { for (int i = 0; i < MAX_SOUNDS; ++i) {
const int num = i; const int num = i;
@ -83,41 +43,28 @@ void sound_init() {
} }
void sound_fini() { void sound_fini() {
g_sys.stop_audio();
} }
void play_sound(int num) { void play_sound(int num) {
assert(num < MAX_SOUNDS); assert(num < MAX_SOUNDS);
const int sample_offset = _samples[num].offset; const int sample_offset = _samples[num].offset;
const int sample_size = _samples[num].size; const int sample_size = _samples[num].size;
print_debug(DBG_MIXER, "sample num %d offset 0x%x size %d", num, sample_offset, sample_size); print_debug(DBG_SOUND, "sample num %d offset 0x%x size %d", num, sample_offset, sample_size);
if (sample_size == 0) { if (sample_size == 0) {
return; return;
} }
g_sys.lock_audio(); if (num == 10) {
_channel.data = g_res.samples + sample_offset;
_channel.pos = 0;
_channel.step = (8000 << 16) / _rate;
_channel.size = sample_size;
if (num == 10) {
static const int8_t seq[] = { 10, 10, 10, 6, -1 }; static const int8_t seq[] = { 10, 10, 10, 6, -1 };
_channel.seq = seq + 1; _seq = seq + 1;
} else { } else {
_channel.seq = 0; _seq = 0;
} }
g_sys.unlock_audio(); g_mix.play_sound(g_res.samples + sample_offset, sample_size, 8000, _seq ? repeat_sound_cb : 0);
} }
void play_music(int num) { void play_music(int num) {
g_sys.lock_audio();
if (_mpf) {
ModPlug_Unload(_mpf);
_mpf = 0;
}
const int size = load_file(_modules[num]); const int size = load_file(_modules[num]);
_mpf = ModPlug_Load(g_res.tmp, size); if (size != 0) {
if (_mpf) { g_mix.play_music(g_res.tmp, size);
print_debug(DBG_MIXER, "Loaded module '%s'", ModPlug_GetName(_mpf));
} }
g_sys.unlock_audio();
} }

27
main.c
View File

@ -2,6 +2,7 @@
#include <getopt.h> #include <getopt.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "sys.h" #include "sys.h"
#include "mixer.h"
#include "util.h" #include "util.h"
struct options_t g_options; struct options_t g_options;
@ -36,9 +37,9 @@ static struct game_t *detect_game(const char *data_path) {
const char *filename; const char *filename;
uint16_t size; uint16_t size;
} games[] = { } games[] = {
{ &game_bb, "AVTMAG.SQV", 3069 }, { &game_bb, "MAGASIN.BIN", 2560 },
{ &game_ja, "JARDIN.EAT", 24876 }, { &game_ja, "JARDIN.EAT", 24876 },
{ &game_p2, "MOTIF.SQZ", 9396 }, { &game_p2, "MOTIF.SQZ", 9396 },
{ 0, 0, 0 } { 0, 0, 0 }
}; };
for (int i = 0; games[i].game; ++i) { for (int i = 0; games[i].game; ++i) {
@ -51,18 +52,6 @@ static struct game_t *detect_game(const char *data_path) {
return 0; return 0;
} }
#if defined(PSP)
/* stubs */
void sound_init() {
}
void sound_fini() {
}
void play_sound(int num) {
}
void play_music(int num) {
}
#endif
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
const char *data_path = DEFAULT_DATA_PATH; const char *data_path = DEFAULT_DATA_PATH;
int scale_factor = DEFAULT_SCALE_FACTOR; int scale_factor = DEFAULT_SCALE_FACTOR;
@ -168,7 +157,13 @@ int main(int argc, char *argv[]) {
} else { } else {
g_sys.init(); g_sys.init();
g_sys.set_screen_size(GAME_SCREEN_W, GAME_SCREEN_H, game->name, scale_factor, scale_filter, fullscreen); g_sys.set_screen_size(GAME_SCREEN_W, GAME_SCREEN_H, game->name, scale_factor, scale_filter, fullscreen);
game->run(data_path); game->res_init(data_path, GAME_SCREEN_W * GAME_SCREEN_H);
game->snd_init();
g_mix.init();
game->run();
g_mix.fini();
game->snd_fini();
game->res_fini();
g_sys.fini(); g_sys.fini();
} }
if (data_path != DEFAULT_DATA_PATH) { if (data_path != DEFAULT_DATA_PATH) {

94
mixer.c Normal file
View File

@ -0,0 +1,94 @@
#include "mixer.h"
#include "sys.h"
#include "util.h"
#include <libmodplug/modplug.h>
struct mixerchannel_t {
const uint8_t *data;
uint32_t pos;
uint32_t step;
uint32_t size;
mix_repeat_sound_cb repeat_cb;
};
static const int _rate = SYS_AUDIO_FREQ;
static struct mixerchannel_t _channel;
static ModPlugFile *_mpf;
static void mix(void *param, uint8_t *buf, int len) {
memset(buf, 0, len);
if (_mpf) {
const int count = ModPlug_Read(_mpf, buf, len);
if (count == 0) {
ModPlug_SeekOrder(_mpf, 0);
}
}
if (_channel.data) {
for (int i = 0; i < len; i += sizeof(int16_t)) {
int pos = _channel.pos >> 16;
if (pos >= _channel.size) {
_channel.data = 0;
if (_channel.repeat_cb) {
_channel.repeat_cb(&_channel.data, &_channel.size);
}
if (!_channel.data) {
break;
}
_channel.pos = pos = 0;
}
const int sample = *(int16_t *)(buf + i) + ((int8_t)_channel.data[pos]) * 256;
*(int16_t *)(buf + i) = (sample < -32768 ? -32768 : (sample > 32767 ? 32767 : sample));
_channel.pos += _channel.step;
}
}
}
static void mixer_init() {
ModPlug_Settings mp_settings;
memset(&mp_settings, 0, sizeof(mp_settings));
ModPlug_GetSettings(&mp_settings);
mp_settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION;
mp_settings.mChannels = 1;
mp_settings.mBits = 16;
mp_settings.mFrequency = _rate;
mp_settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
mp_settings.mLoopCount = -1;
ModPlug_SetSettings(&mp_settings);
g_sys.start_audio(mix, 0);
}
static void mixer_fini() {
g_sys.stop_audio();
}
static void play_sound(const uint8_t *data, uint32_t size, int freq, mix_repeat_sound_cb repeat_cb) {
g_sys.lock_audio();
_channel.data = data;
_channel.pos = 0;
_channel.step = (freq << 16) / _rate;
_channel.size = size;
_channel.repeat_cb = repeat_cb;
g_sys.unlock_audio();
}
static void play_music(const uint8_t *data, uint32_t size) {
g_sys.lock_audio();
if (_mpf) {
ModPlug_Unload(_mpf);
_mpf = 0;
}
_mpf = ModPlug_Load(data, size);
if (_mpf) {
print_debug(DBG_MIXER, "Loaded module '%s'", ModPlug_GetName(_mpf));
}
g_sys.unlock_audio();
}
struct mixer_t g_mix = {
.init = mixer_init,
.fini = mixer_fini,
.play_sound = play_sound,
.play_music = play_music,
};

18
mixer.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef MIXER_H__
#define MIXER_H__
#include "intern.h"
typedef void (*mix_repeat_sound_cb)(const uint8_t **data, uint32_t *size);
struct mixer_t {
void (*init)();
void (*fini)();
void (*play_sound)(const uint8_t *data, uint32_t size, int freq, mix_repeat_sound_cb repeat_cb);
void (*play_music)(const uint8_t *data, uint32_t size);
};
extern struct mixer_t g_mix;
#endif

View File

@ -16,10 +16,6 @@ void update_input() {
g_vars.input.key_down = (g_sys.input.direction & INPUT_DIRECTION_DOWN) != 0 ? 0xFF : 0; g_vars.input.key_down = (g_sys.input.direction & INPUT_DIRECTION_DOWN) != 0 ? 0xFF : 0;
g_vars.input.key_space = g_sys.input.space ? 0xFF : 0; g_vars.input.key_space = g_sys.input.space ? 0xFF : 0;
g_vars.input.key_jump = g_sys.input.jump ? 0xFF : 0; g_vars.input.key_jump = g_sys.input.jump ? 0xFF : 0;
g_vars.input.keystate[2] = g_sys.input.digit1;
g_vars.input.keystate[3] = g_sys.input.digit2;
g_vars.input.keystate[4] = g_sys.input.digit3;
} }
static void wait_input(int timeout) { static void wait_input(int timeout) {
@ -182,9 +178,8 @@ void do_gameover_screen() {
uint8_t *data = load_file("GAMEOVER.SQZ"); uint8_t *data = load_file("GAMEOVER.SQZ");
if (data) { if (data) {
video_copy_img(data); video_copy_img(data);
video_copy_background();
g_sys.set_screen_palette(gameover_palette_data, 0, 16, 6); g_sys.set_screen_palette(gameover_palette_data, 0, 16, 6);
g_sys.copy_bitmap(g_res.vga, GAME_SCREEN_W, GAME_SCREEN_H); update_screen_img(g_res.background, 0);
do_gameover_animation(); do_gameover_animation();
g_sys.fade_out_palette(); g_sys.fade_out_palette();
free(data); free(data);
@ -214,16 +209,15 @@ static bool do_menu() {
update_screen_img(data + 768, 0); update_screen_img(data + 768, 0);
g_sys.fade_in_palette(); g_sys.fade_in_palette();
free(data); free(data);
memset(g_vars.input.keystate, 0, sizeof(g_vars.input.keystate));
const uint32_t start = g_sys.get_timestamp(); const uint32_t start = g_sys.get_timestamp();
while (!g_sys.input.quit) { while (!g_sys.input.quit) {
update_input(); update_input();
if (g_vars.input.keystate[2] || g_vars.input.keystate[0x4F] || g_sys.input.space) { if (g_sys.input.digit1 || g_sys.input.space) {
g_sys.input.space = 0; g_sys.input.space = 0;
g_sys.fade_out_palette(); g_sys.fade_out_palette();
break; break;
} }
if (g_vars.input.keystate[3] || g_vars.input.keystate[0x50]) { if (g_sys.input.digit2 || g_vars.input.key_down) {
g_sys.fade_out_palette(); g_sys.fade_out_palette();
break; break;
} }
@ -241,13 +235,13 @@ static void do_photos_screen() {
} }
void input_check_ctrl_alt_e() { void input_check_ctrl_alt_e() {
if (g_vars.input.keystate[0x1D] && g_vars.input.keystate[0x38] && g_vars.input.keystate[0x12]) { if (0) {
do_photos_screen(); do_photos_screen();
} }
} }
void input_check_ctrl_alt_w() { void input_check_ctrl_alt_w() {
if (g_vars.input.keystate[0x1D] && g_vars.input.keystate[0x38] && g_vars.input.keystate[0x11]) { if (0) {
do_credits(); do_credits();
wait_input(60); wait_input(60);
} }
@ -318,9 +312,8 @@ uint16_t random_get_number3(uint16_t x) {
return rol16(x, 3); return rol16(x, 3);
} }
static void game_run(const char *data_path) { static void game_run() {
res_init(data_path, GAME_SCREEN_W * GAME_SCREEN_H); video_init();
sound_init();
video_convert_tiles(g_res.uniondat, g_res.unionlen); video_convert_tiles(g_res.uniondat, g_res.unionlen);
g_vars.level_num = g_options.start_level; g_vars.level_num = g_options.start_level;
do_programmed_in_1992_screen(); do_programmed_in_1992_screen();
@ -359,12 +352,13 @@ static void game_run(const char *data_path) {
print_debug(DBG_GAME, "previous level %d current %d", level_num, g_vars.level_num); print_debug(DBG_GAME, "previous level %d current %d", level_num, g_vars.level_num);
} while (!g_res.dos_demo && g_vars.level_num != level_num); } while (!g_res.dos_demo && g_vars.level_num != level_num);
} }
sound_fini();
res_fini();
} }
EXPORT_SYMBOL struct game_t game_p2 = { EXPORT_SYMBOL struct game_t game_p2 = {
"Prehistorik 2", "Prehistorik 2",
res_init,
res_fini,
sound_init,
sound_fini,
game_run game_run
}; };

View File

@ -119,7 +119,6 @@ struct vars_t {
uint16_t d, e; uint16_t d, e;
} random; } random;
struct { struct {
bool keystate[128];
uint8_t key_left, key_right, key_down, key_up, key_space, key_jump; uint8_t key_left, key_right, key_down, key_up, key_space, key_jump;
uint8_t key_vdir, key_hdir; uint8_t key_vdir, key_hdir;
uint16_t demo_offset; uint16_t demo_offset;
@ -322,7 +321,6 @@ extern void random_reset();
extern uint8_t random_get_number(); extern uint8_t random_get_number();
extern uint16_t random_get_number2(); extern uint16_t random_get_number2();
extern uint16_t random_get_number3(uint16_t x); extern uint16_t random_get_number3(uint16_t x);
extern void game_main();
/* level.c */ /* level.c */
extern void do_level(); extern void do_level();
@ -337,6 +335,7 @@ extern void monster_change_next_anim(struct object_t *obj);
extern void monster_change_prev_anim(struct object_t *obj); extern void monster_change_prev_anim(struct object_t *obj);
/* screen.c */ /* screen.c */
extern void video_init();
extern void video_draw_string(int offset, int hspace, const char *s); extern void video_draw_string(int offset, int hspace, const char *s);
extern void video_clear(); extern void video_clear();
extern void video_copy_img(const uint8_t *src); extern void video_copy_img(const uint8_t *src);
@ -352,6 +351,7 @@ extern void video_load_front_tiles();
extern void video_transition_close(); extern void video_transition_close();
extern void video_transition_open(); extern void video_transition_open();
extern void video_load_sprites(); extern void video_load_sprites();
extern void video_set_sprite_pos_flags(int flag);
extern void video_draw_sprite(int num, int x, int y, int flag); extern void video_draw_sprite(int num, int x, int y, int flag);
extern void video_put_pixel(int x, int y, uint8_t color); extern void video_put_pixel(int x, int y, uint8_t color);

View File

@ -12,7 +12,6 @@ static const bool _demo_inputs = false;
static const bool _expert = true; static const bool _expert = true;
static const bool _redraw_tilemap = true;
static const bool _redraw_panel = true; static const bool _redraw_panel = true;
static const uint16_t _undefined = 0x55AA; static const uint16_t _undefined = 0x55AA;
@ -323,51 +322,23 @@ static void level_update_tilemap() {
} else if (g_vars.level_animated_tiles_current_tbl == g_vars.tile_tbl3) { } else if (g_vars.level_animated_tiles_current_tbl == g_vars.tile_tbl3) {
g_vars.level_animated_tiles_current_tbl = g_vars.tile_tbl1; g_vars.level_animated_tiles_current_tbl = g_vars.tile_tbl1;
} }
} else {
if (!_redraw_tilemap) {
return;
}
} }
video_copy_background(); video_copy_background();
g_vars.tile_attr2_flags = 0; g_vars.tile_attr2_flags = 0;
uint16_t offset = (g_vars.tilemap.y << 8) | g_vars.tilemap.x; uint16_t offset = (g_vars.tilemap.y << 8) | g_vars.tilemap.x;
for (int y = 0; y < (TILEMAP_SCREEN_H / 16) + 1; ++y) { int tilemap_w = (TILEMAP_SCREEN_W / 16) + 1;
for (int x = 0; x < (TILEMAP_SCREEN_W / 16) + 1; ++x) { int tilemap_h = (TILEMAP_SCREEN_H / 16) + 1;
if (g_vars.level_num == 9) { /* minotaur boss expects original screen resolution */
offset = 0;
tilemap_w = 320 / 16;
tilemap_h = (200 - PANEL_H) / 16;
}
for (int y = 0; y < tilemap_h; ++y) {
for (int x = 0; x < tilemap_w; ++x) {
const uint8_t tile_num = level_get_tile(offset + x); const uint8_t tile_num = level_get_tile(offset + x);
g_vars.tile_attr2_flags |= g_res.level.tile_attributes2[tile_num]; g_vars.tile_attr2_flags |= g_res.level.tile_attributes2[tile_num];
if (_redraw_tilemap || g_vars.animated_tile_flag_tbl[tile_num] != 0) { const uint8_t num = g_vars.level_animated_tiles_current_tbl[tile_num];
const uint8_t num = g_vars.level_animated_tiles_current_tbl[tile_num]; level_draw_tile(num, x, y);
level_draw_tile(num, x, y);
}
}
offset += 256;
}
}
static void level_draw_tilemap() {
if (_redraw_tilemap) {
return;
}
if (g_vars.tilemap.redraw_flag1 == 0) {
const bool changed = (g_vars.tilemap.x != g_vars.tilemap.prev_x) || (g_vars.tilemap.y != g_vars.tilemap.prev_y);
if (!changed) {
return;
}
g_vars.tilemap.prev_x = g_vars.tilemap.x;
g_vars.tilemap.prev_y = g_vars.tilemap.y;
if (g_vars.tilemap.redraw_flag2 == 0) {
return;
}
}
g_vars.tilemap.redraw_flag1 = 0;
g_vars.tilemap.redraw_flag2 = 0;
g_vars.tile_attr2_flags = 0;
uint16_t offset = (g_vars.tilemap.y << 8) | g_vars.tilemap.x;
for (int y = 0; y < (TILEMAP_SCREEN_H / 16) + 1; ++y) {
for (int x = 0; x < TILEMAP_SCREEN_W / 16; ++x) {
const uint8_t tile_num = level_get_tile(offset + x);
g_vars.tile_attr2_flags |= g_res.level.tile_attributes2[tile_num];
level_draw_tile(tile_num, x, y);
} }
offset += 256; offset += 256;
} }
@ -621,7 +592,6 @@ static void level_init_tilemap() {
g_vars.tilemap.redraw_flag2 = 1; g_vars.tilemap.redraw_flag2 = 1;
g_vars.tilemap.prev_x = _undefined; g_vars.tilemap.prev_x = _undefined;
g_vars.tilemap.prev_y = _undefined; g_vars.tilemap.prev_y = _undefined;
level_draw_tilemap();
video_transition_open(); video_transition_open();
} }
@ -2388,17 +2358,6 @@ static void level_update_player_flying() {
static void level_update_player() { static void level_update_player() {
g_vars.objects_tbl[0].spr_num = 0xFFFF; g_vars.objects_tbl[0].spr_num = 0xFFFF;
if (g_vars.input.keystate[0x3B]) {
if (g_vars.restart_level_flag == 0) {
level_player_die();
g_vars.restart_level_flag = 1;
return;
}
}
if (g_vars.input.keystate[0x3C]) {
g_vars.player_death_flag = 1;
return;
}
input_check_ctrl_alt_w(); input_check_ctrl_alt_w();
if (_demo_inputs) { if (_demo_inputs) {
if (g_vars.input.demo_offset < g_res.keyblen) { if (g_vars.input.demo_offset < g_res.keyblen) {
@ -3014,7 +2973,6 @@ static void level_update_gates() {
g_vars.boss_level5.idle_counter = 8; g_vars.boss_level5.idle_counter = 8;
memset(&g_vars.objects_tbl[91], 0xFF, sizeof(struct object_t) * 6); memset(&g_vars.objects_tbl[91], 0xFF, sizeof(struct object_t) * 6);
} }
level_draw_tilemap();
level_update_objects_decors(); level_update_objects_decors();
level_update_objects_items(); level_update_objects_items();
level_update_objects_monsters(); level_update_objects_monsters();
@ -3267,16 +3225,20 @@ static void level_update_light_palette() {
} }
} }
static void level_sync() { static void level_wait() {
update_input(); update_input();
g_sys.copy_bitmap(g_res.vga, GAME_SCREEN_W, GAME_SCREEN_H);
g_sys.update_screen();
g_sys.render_clear_sprites();
const int diff = (g_vars.timestamp + (1000 / 30)) - g_sys.get_timestamp(); const int diff = (g_vars.timestamp + (1000 / 30)) - g_sys.get_timestamp();
g_sys.sleep(MAX(diff, 10)); g_sys.sleep(MAX(diff, 10));
g_vars.timestamp = g_sys.get_timestamp(); g_vars.timestamp = g_sys.get_timestamp();
} }
static void level_sync() {
g_sys.copy_bitmap(g_res.vga, GAME_SCREEN_W, GAME_SCREEN_H);
g_sys.update_screen();
g_sys.render_clear_sprites();
level_wait();
}
static void level_draw_objects() { static void level_draw_objects() {
++g_vars.level_draw_counter; ++g_vars.level_draw_counter;
for (int i = OBJECTS_COUNT - 1; i >= 0; --i) { for (int i = OBJECTS_COUNT - 1; i >= 0; --i) {
@ -3482,7 +3444,6 @@ static void level_player_death_animation() {
level_update_objects_bonuses(); level_update_objects_bonuses();
level_update_objects_bonus_scores(); level_update_objects_bonus_scores();
level_update_tilemap(); level_update_tilemap();
level_draw_tilemap();
level_draw_panel(); level_draw_panel();
level_draw_orbs(); level_draw_orbs();
level_draw_objects(); level_draw_objects();
@ -3572,6 +3533,7 @@ static int level_completed_bonuses_animation_helper() {
} }
static void level_completed_bonuses_animation() { static void level_completed_bonuses_animation() {
video_set_sprite_pos_flags(1); /* center */
if (g_vars.light.state != 0) { if (g_vars.light.state != 0) {
g_vars.light.palette_flag1 = 0; g_vars.light.palette_flag1 = 0;
g_vars.light.palette_flag2 = 1; g_vars.light.palette_flag2 = 1;
@ -3728,6 +3690,7 @@ static void level_completed_bonuses_animation() {
g_vars.objects_tbl[1].x_pos += 2; g_vars.objects_tbl[1].x_pos += 2;
} }
} while (g_vars.objects_tbl[2].x_pos > -52); } while (g_vars.objects_tbl[2].x_pos > -52);
video_set_sprite_pos_flags(0);
} }
static void update_object_demo_animation(struct object_t *obj) { static void update_object_demo_animation(struct object_t *obj) {
@ -3868,6 +3831,7 @@ void do_gameover_animation() {
g_vars.objects_tbl[i].spr_num = 0xFFFF; g_vars.objects_tbl[i].spr_num = 0xFFFF;
} }
video_load_sprites(); video_load_sprites();
video_set_sprite_pos_flags(1); /* center */
g_vars.tilemap.x = g_vars.tilemap.scroll_dx = 0; g_vars.tilemap.x = g_vars.tilemap.scroll_dx = 0;
g_vars.tilemap.y = g_vars.tilemap.scroll_dy = 0; g_vars.tilemap.y = g_vars.tilemap.scroll_dy = 0;
static const char *gameover = "GAMEOVER"; static const char *gameover = "GAMEOVER";
@ -3909,11 +3873,14 @@ void do_gameover_animation() {
do_gameover_animation_helper(); do_gameover_animation_helper();
level_draw_objects(); level_draw_objects();
level_update_panel(); level_update_panel();
level_sync(); g_sys.update_screen();
g_sys.render_clear_sprites();
level_wait();
if (g_sys.input.quit) { if (g_sys.input.quit) {
return; return;
} }
} while (timer_counter < 630 && !g_sys.input.space); } while (timer_counter < 630 && !g_sys.input.space);
video_set_sprite_pos_flags(0);
} }
void do_level() { void do_level() {
@ -3949,7 +3916,6 @@ void do_level() {
g_vars.tilemap_adjust_player_pos_flag = false; g_vars.tilemap_adjust_player_pos_flag = false;
level_update_scrolling(); level_update_scrolling();
level_update_tilemap(); level_update_tilemap();
level_draw_tilemap();
level_draw_panel(); level_draw_panel();
level_draw_orbs(); level_draw_orbs();
level_draw_objects(); level_draw_objects();

View File

@ -11,6 +11,15 @@
#define MAX_SPRITESHEET_H 1024 #define MAX_SPRITESHEET_H 1024
#define MAX_FRONT_TILES 168 #define MAX_FRONT_TILES 168
static int _spr_pos_flags;
static int _offset_x_center, _offset_y_center;
void video_init() {
_offset_x_center = (GAME_SCREEN_W > 320) ? (GAME_SCREEN_W - 320) / 2 : 0;
_offset_y_center = (GAME_SCREEN_H > 200) ? (GAME_SCREEN_H - 200) / 2 : 0;
}
static void decode_planar(const uint8_t *src, uint8_t *dst, int dst_pitch, int w, int h, uint8_t transparent_color) { static void decode_planar(const uint8_t *src, uint8_t *dst, int dst_pitch, int w, int h, uint8_t transparent_color) {
const int plane_size = h * w / 8; const int plane_size = h * w / 8;
for (int y = 0; y < h; ++y) { for (int y = 0; y < h; ++y) {
@ -279,7 +288,15 @@ void video_load_sprites() {
} }
} }
void video_set_sprite_pos_flags(int flags) {
_spr_pos_flags = flags;
}
void video_draw_sprite(int num, int x, int y, int flag) { void video_draw_sprite(int num, int x, int y, int flag) {
if (_spr_pos_flags) {
x += _offset_x_center;
y += _offset_y_center;
}
g_sys.render_add_sprite(RENDER_SPR_GAME, num, x, y, flag != 0); g_sys.render_add_sprite(RENDER_SPR_GAME, num, x, y, flag != 0);
} }

View File

@ -1,31 +1,16 @@
#include "game.h" #include "game.h"
#include "resource.h" #include "resource.h"
#include "sys.h" #include "mixer.h"
#include "util.h" #include "util.h"
#include <libmodplug/modplug.h>
#define MAX_SOUNDS 11 #define MAX_SOUNDS 11
static const bool _volume = false;
static const uint16_t sound_sizes_tbl[] = { static const uint16_t sound_sizes_tbl[] = {
0x188E, 0x1C80, 0x235E, 0x19E6, 0x0AB2, 0x0912, 0x0000, 0x35D2, 0x188E, 0x1C80, 0x235E, 0x19E6, 0x0AB2, 0x0912, 0x0000, 0x35D2,
0x06C4, 0x1C86, 0x0E2E 0x06C4, 0x1C86, 0x0E2E
}; };
static const uint8_t sound_volume_tbl[] = {
0x3F,0x37,0x32,0x2F,0x2C,0x2A,0x28,0x27,0x26,0x24,0x23,0x22,0x21,0x21,0x20,0x1F,
0x1E,0x1E,0x1D,0x1C,0x1C,0x1B,0x1B,0x1A,0x1A,0x19,0x19,0x19,0x18,0x18,0x17,0x17,
0x17,0x16,0x16,0x16,0x15,0x15,0x15,0x15,0x14,0x14,0x14,0x14,0x13,0x13,0x13,0x13,
0x12,0x12,0x12,0x12,0x11,0x11,0x11,0x11,0x11,0x10,0x10,0x10,0x10,0x10,0x0F,0x0F,
0x0F,0x0F,0x0F,0x0F,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,
0x0D,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,
0x0B,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x09,0x09,0x09,0x09,0x09,0x09,
0x09,0x09,0x09,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x07,0x07
};
static const char *trk_names_tbl[] = { static const char *trk_names_tbl[] = {
"PRES.TRK", "PRES.TRK",
"CODE.TRK", "CODE.TRK",
@ -47,85 +32,35 @@ static const char *trk_names_tbl[] = {
"BOULA.TRK" "BOULA.TRK"
}; };
struct mixerchannel_t {
uint8_t *data;
uint32_t pos;
uint32_t step;
uint32_t size;
};
static const int _rate = SYS_AUDIO_FREQ;
static struct mixerchannel_t _channel;
static ModPlugFile *_mpf;
static int _music_num; static int _music_num;
static uint16_t sound_offsets_tbl[MAX_SOUNDS]; static uint16_t sound_offsets_tbl[MAX_SOUNDS];
static void mix(void *param, uint8_t *buf, int len) {
memset(buf, 0, len);
if (_mpf) {
const int count = ModPlug_Read(_mpf, buf, len);
if (count == 0) {
ModPlug_SeekOrder(_mpf, 0);
}
}
if (_channel.data) {
for (int i = 0; i < len; i += sizeof(int16_t)) {
const int pos = _channel.pos >> 16;
if (pos >= _channel.size) {
_channel.data = 0;
break;
}
const int8_t pcm = _volume ? sound_volume_tbl[(_channel.data[pos] ^ 0x80) >> 1] : _channel.data[pos];
const int sample = *(int16_t *)(buf + i) + pcm * 256;
*(int16_t *)(buf + i) = (sample < -32768 ? -32768 : (sample > 32767 ? 32767 : sample));
_channel.pos += _channel.step;
}
}
}
void sound_init() { void sound_init() {
ModPlug_Settings mp_settings;
memset(&mp_settings, 0, sizeof(mp_settings));
ModPlug_GetSettings(&mp_settings);
mp_settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION;
mp_settings.mChannels = 1;
mp_settings.mBits = 16;
mp_settings.mFrequency = _rate;
mp_settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
mp_settings.mLoopCount = -1;
ModPlug_SetSettings(&mp_settings);
uint16_t offset = 0; uint16_t offset = 0;
for (int i = 0; i < MAX_SOUNDS; ++i) { for (int i = 0; i < MAX_SOUNDS; ++i) {
sound_offsets_tbl[i] = offset; sound_offsets_tbl[i] = offset;
offset += sound_sizes_tbl[i]; offset += sound_sizes_tbl[i];
} }
_music_num = -1; _music_num = -1;
g_sys.start_audio(mix, 0);
} }
void sound_fini() { void sound_fini() {
g_sys.stop_audio();
} }
void play_sound(int num) { void play_sound(int num) {
assert(num < MAX_SOUNDS); assert(num < MAX_SOUNDS);
print_debug(DBG_MIXER, "play_sound %d", num); print_debug(DBG_SOUND, "play_sound %d", num);
if (!g_res.samples) { /* no SAMPLE. file with demo */ if (!g_res.samples) { /* no SAMPLE. file with demo */
return; return;
} }
const int sample_offset = sound_offsets_tbl[num]; const int sample_offset = sound_offsets_tbl[num];
const int sample_size = sound_sizes_tbl[num]; const int sample_size = sound_sizes_tbl[num];
print_debug(DBG_MIXER, "sample num %d offset 0x%x size %d", num, sample_offset, sample_size); print_debug(DBG_SOUND, "sample num %d offset 0x%x size %d", num, sample_offset, sample_size);
if (sample_size == 0) { if (sample_size == 0) {
return; return;
} }
g_sys.lock_audio(); g_mix.play_sound(g_res.samples + sample_offset, sample_size, 8000, 0);
_channel.data = g_res.samples + sample_offset;
_channel.pos = 0;
_channel.step = (8000 << 16) / _rate;
_channel.size = sample_size;
g_sys.unlock_audio();
} }
void play_music(int num) { void play_music(int num) {
@ -137,21 +72,12 @@ void play_music(int num) {
} }
const char *filename = trk_names_tbl[num]; const char *filename = trk_names_tbl[num];
if (filename) { if (filename) {
print_debug(DBG_MIXER, "play_music '%s'", filename); print_debug(DBG_SOUND, "play_music '%s'", filename);
g_sys.lock_audio();
if (_mpf) {
ModPlug_Unload(_mpf);
_mpf = 0;
}
uint8_t *data = load_file(filename); uint8_t *data = load_file(filename);
if (data) { if (data) {
_mpf = ModPlug_Load(data, g_uncompressed_size); g_mix.play_music(data, g_uncompressed_size);
if (_mpf) {
print_debug(DBG_MIXER, "Loaded module '%s'", ModPlug_GetName(_mpf));
}
free(data); free(data);
_music_num = num; _music_num = num;
} }
g_sys.unlock_audio();
} }
} }

16
util.c
View File

@ -73,13 +73,17 @@ FILE *fopen_nocase(const char *path, const char *filename) {
snprintf(buf, sizeof(buf), "%s/%s", path, filename); snprintf(buf, sizeof(buf), "%s/%s", path, filename);
FILE *fp = fopen(buf, "rb"); FILE *fp = fopen(buf, "rb");
if (!fp) { if (!fp) {
static void (*const str[3])(char *) = {
string_upper,
string_lower,
0
};
char *p = buf + strlen(path) + 1; char *p = buf + strlen(path) + 1;
string_upper(p); for (int i = 0; str[i] && !fp; ++i) {
fp = fopen(buf, "rb"); (str[i])(p);
if (!fp) { if (strcmp(filename, p) != 0) {
char *p = buf + strlen(path) + 1; fp = fopen(buf, "rb");
string_lower(p); }
fp = fopen(buf, "rb");
} }
} }
return fp; return fp;

1
util.h
View File

@ -11,6 +11,7 @@
#define DBG_SYSTEM (1 << 4) #define DBG_SYSTEM (1 << 4)
#define DBG_UNPACK (1 << 5) #define DBG_UNPACK (1 << 5)
#define DBG_SCREEN (1 << 6) #define DBG_SCREEN (1 << 6)
#define DBG_SOUND (1 << 7)
extern int g_debug_mask; extern int g_debug_mask;