Import blues from dd0f5cfb

This commit is contained in:
Gregory Montoir 2018-07-08 22:08:53 +08:00
parent 54717ad915
commit 191796a951
26 changed files with 7214 additions and 0 deletions

17
Makefile Normal file
View File

@ -0,0 +1,17 @@
SDL_CFLAGS := `sdl2-config --cflags`
SDL_LIBS := `sdl2-config --libs` -lSDL2_mixer
SRCS := decode.c fileio.c game.c level.c main.c opcodes.c resource.c screen.c sound.c staticres.c sys_sdl2.c triggers.c unpack.c util.c
OBJS := $(SRCS:.c=.o)
DEPS := $(SRCS:.c=.d)
CPPFLAGS := -Wall -Wpedantic -MMD $(SDL_CFLAGS) -g
blues: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(SDL_LIBS) -lmodplug
clean:
rm -f *.o *.d
-include $(DEPS)

34
README.md Normal file
View File

@ -0,0 +1,34 @@
# Blues Brothers
This is a rewrite of the game [Blues Brothers](https://www.mobygames.com/game/blues-brothers) developed by [Titus](https://www.mobygames.com/company/titus-interactive-sa).
![Screenshot1](blues1.png) ![Screenshot2](blues2.png)
## Requirements
The game data files of the DOS version (retail or demo) are required.
```
*.BIN, *.CK1, *.CK2, *.SQL, *.SQV, *.SQZ
```
For sounds and music, the Amiga version files need to be copied.
```
SOUND, ALMOST, GUNN, EVERY, SHOT
```
The [ExoticA](https://www.exotica.org.uk/wiki/The_Blues_Brothers) modules set can also be used.
## Running
By default, the executable loads the data files from the current directory.
This can be changed by using command line switch.
```
./blues --datapath DATA_dos/
```

BIN
blues1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
blues2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

32
decode.c Normal file
View File

@ -0,0 +1,32 @@
#include "decode.h"
void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *dst, int dst_pitch, int dst_x, int dst_y, int xflip) {
assert((src_pitch & 7) == 0);
src_pitch /= 8;
assert((w & 7) == 0);
w /= 8;
const int bitplane_size = w * h;
for (int y = 0; y < h; ++y) {
const int y_offset = dst_y + y;
for (int x = 0; x < w; ++x) {
for (int i = 0; i < 8; ++i) {
int color = 0;
const int mask = 1 << (7 - i);
for (int bit = 0; bit < 4; ++bit) {
if (src[bit * bitplane_size] & mask) {
color |= 1 << (3 - bit);
}
}
if (color != 0) {
const int x_offset = dst_x + (xflip ? (w * 8 - x * 8 - i) : (x * 8 + i));
if (x_offset >= 0 && x_offset < 320 && y_offset >= 0 && y_offset < 200) {
dst[y_offset * dst_pitch + x_offset] = color;
}
}
}
++src;
}
src += src_pitch - w;
}
}

9
decode.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef DECODE_H__
#define DECODE_H__
#include "intern.h"
extern void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *dst, int dst_pitch, int dst_x, int dst_y, int xflip);
#endif /* DECODE_H__ */

106
fileio.c Normal file
View File

@ -0,0 +1,106 @@
#include <sys/param.h>
#include "fileio.h"
#include "util.h"
#define MAX_FILEIO_SLOTS 2
struct fileio_slot_t {
FILE *fp;
int size;
} fileio_slot_t;
static const char *_data_path;
static struct fileio_slot_t _fileio_slots_table[MAX_FILEIO_SLOTS];
static int find_free_slot() {
int i, slot = -1;
for (i = 0; i < MAX_FILEIO_SLOTS; ++i) {
if (!_fileio_slots_table[i].fp) {
slot = i;
break;
}
}
return slot;
}
void fio_init(const char *data_path) {
_data_path = data_path;
memset(_fileio_slots_table, 0, sizeof(_fileio_slots_table));
}
void fio_fini() {
}
static FILE *fio_open_no_case(const char *filename) {
char buf[MAXPATHLEN];
snprintf(buf, sizeof(buf), "%s/%s", _data_path, filename);
FILE *fp = fopen(buf, "rb");
if (!fp) {
char *p = buf + strlen(_data_path) + 1;
string_upper(p);
fp = fopen(buf, "rb");
}
return fp;
}
int fio_open(const char *filename, int error_flag) {
int slot = find_free_slot();
if (slot < 0) {
print_error("Unable to find free slot for '%s'", filename);
} else {
struct fileio_slot_t *file_slot = &_fileio_slots_table[slot];
memset(file_slot, 0, sizeof(fileio_slot_t));
file_slot->fp = fio_open_no_case(filename);
if (!file_slot->fp) {
if (error_flag) {
print_error("Unable to open file '%s'", filename);
}
print_warning("Unable to open file '%s'", filename);
slot = -1;
} else {
fseek(file_slot->fp, 0, SEEK_END);
file_slot->size = ftell(file_slot->fp);
fseek(file_slot->fp, 0, SEEK_SET);
}
}
return slot;
}
void fio_close(int slot) {
if (slot >= 0) {
struct fileio_slot_t *file_slot = &_fileio_slots_table[slot];
assert(file_slot->fp);
fclose(file_slot->fp);
memset(file_slot, 0, sizeof(fileio_slot_t));
}
}
int fio_size(int slot) {
int size = 0;
if (slot >= 0) {
struct fileio_slot_t *file_slot = &_fileio_slots_table[slot];
assert(file_slot->fp);
size = file_slot->size;
}
return size;
}
int fio_read(int slot, void *data, int len) {
int sz = 0;
if (slot >= 0) {
struct fileio_slot_t *file_slot = &_fileio_slots_table[slot];
assert(file_slot->fp);
sz = fread(data, 1, len, file_slot->fp);
}
return sz;
}
int fio_exists(const char *filename) {
FILE *fp = fio_open_no_case(filename);
if (fp) {
fclose(fp);
return 1;
}
return 0;
}

15
fileio.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef FILEIO_H__
#define FILEIO_H__
#include "intern.h"
extern void fio_init(const char *datapath);
extern void fio_fini();
extern int fio_open(const char *filename, int error_flag);
extern void fio_close(int slot);
extern int fio_size(int slot);
extern int fio_read(int slot, void *data, int len);
extern int fio_exists(const char *filename);
#endif /* FILEIO_H__ */

315
game.c Normal file
View File

@ -0,0 +1,315 @@
#include "game.h"
#include "resource.h"
#include "sys.h"
struct vars_t g_vars;
void update_input() {
g_sys.process_events();
g_vars.inp_key_left = ((g_sys.input.direction & INPUT_DIRECTION_LEFT) != 0) || g_vars.inp_keyboard[0x4B] || g_vars.inp_keyboard[0x7A];
g_vars.inp_key_right = ((g_sys.input.direction & INPUT_DIRECTION_RIGHT) != 0) || g_vars.inp_keyboard[0x4D] || g_vars.inp_keyboard[0x79];
g_vars.inp_key_up = ((g_sys.input.direction & INPUT_DIRECTION_UP) != 0) || g_vars.inp_keyboard[0x48] || g_vars.inp_keyboard[0x7C];
g_vars.inp_key_down = ((g_sys.input.direction & INPUT_DIRECTION_DOWN) != 0) || g_vars.inp_keyboard[0x50] || g_vars.inp_keyboard[0x7B];
g_vars.inp_key_space = (g_sys.input.space != 0) || g_vars.inp_keyboard[0x39] || g_vars.inp_keyboard[0x77];
g_vars.inp_key_tab = g_vars.inp_keyboard[0xF] || g_vars.inp_keyboard[0x78];
}
void do_title_screen() {
// _timer_counter = 20;
load_img("pres.sqz");
fade_in_palette();
do {
update_input();
// if (_timer_counter <= 0) {
// break;
// }
} while (g_sys.input.space == 0 && g_sys.input.quit == 0);
// timer_unk3();
fade_out_palette();
g_sys.input.space = 0;
read_file("avtmag.sqv", g_res.avt_sqv);
}
static void check_cheat_code() {
static const uint8_t codes[] = { 0x14, 0x17, 0x27, 0x18, 0x14, 0x23, 0x12, 0x12 };
if ((g_vars.inp_code & 0x80) == 0 && g_vars.inp_code != 0) {
if (g_vars.inp_code == codes[g_vars.cheat_code_len]) {
++g_vars.cheat_code_len;
if (g_vars.cheat_code_len == ARRAYSIZE(codes)) {
g_vars.cheat_flag = 1;
}
} else {
g_vars.cheat_code_len = 0;
}
}
}
void do_select_player() {
int quit = 0;
int var9 = 0;
int state = 0;
int frame1 = 1;
int frame2 = 1;
const int color_rgb = 2;
const int colors_count = 25;
load_img("choix.sqz");
screen_clear_sprites();
do {
screen_unk4();
update_input();
switch (state) {
case 0:
screen_add_sprite(95, 155, animframe_0135[frame1]);
if (frame1 < animframe_0135[0]) {
++frame1;
} else {
frame1 = 1;
}
screen_add_sprite(190, 155, animframe_01d5[frame2]);
if (frame2 < animframe_01d5[0]) {
++frame2;
} else {
frame2 = 1;
}
g_vars.two_players_flag = 0;
level_data[g_vars.level * MAX_OBJECTS + OBJECT_NUM_PLAYER1] = PLAYER_JAKE;
level_data[g_vars.level * MAX_OBJECTS + OBJECT_NUM_PLAYER2] = 100;
g_vars.player = PLAYER_JAKE;
if (g_sys.input.direction & INPUT_DIRECTION_RIGHT) {
g_sys.input.direction &= ~INPUT_DIRECTION_RIGHT;
for (int i = 0; i < colors_count; ++i) {
screen_adjust_palette_color( 2, color_rgb, 1);
screen_adjust_palette_color( 9, color_rgb, 1);
screen_adjust_palette_color( 3, color_rgb, -1);
screen_adjust_palette_color(10, color_rgb, -1);
}
screen_vsync();
state = 1;
frame1 = frame2 = 1;
// _timer_counter = 20;
} else if (g_sys.input.direction & INPUT_DIRECTION_LEFT) {
g_sys.input.direction &= ~INPUT_DIRECTION_LEFT;
for (int i = 0; i < colors_count; ++i) {
screen_adjust_palette_color(2, color_rgb, 1);
screen_adjust_palette_color(9, color_rgb, 1);
}
screen_vsync();
state = 2;
frame1 = frame2 = 1;
// _timer_counter = 20;
}
break;
case 1:
screen_add_sprite(95, 155, animframe_00dd[frame1]);
if (frame1 < animframe_00dd[0]) {
++frame1;
} else {
frame1 = 4;
}
screen_add_sprite(190, 155, animframe_022d[frame2]);
if (frame2 < animframe_022d[0]) {
++frame2;
} else {
frame2 = 1;
}
g_vars.two_players_flag = 0;
level_data[g_vars.level * MAX_OBJECTS + OBJECT_NUM_PLAYER1] = PLAYER_ELWOOD;
level_data[g_vars.level * MAX_OBJECTS + OBJECT_NUM_PLAYER2] = 100;
g_vars.player = PLAYER_ELWOOD;
if (g_sys.input.direction & INPUT_DIRECTION_RIGHT) {
g_sys.input.direction &= ~INPUT_DIRECTION_RIGHT;
for (int i = 0; i < colors_count; ++i) {
screen_adjust_palette_color( 3, color_rgb, 1);
screen_adjust_palette_color(10, color_rgb, 1);
}
screen_vsync();
state = 2;
frame1 = frame2 = 1;
// _timer_counter = 20;
} else if (g_sys.input.direction & INPUT_DIRECTION_LEFT) {
g_sys.input.direction &= ~INPUT_DIRECTION_LEFT;
for (int i = 0; i < colors_count; ++i) {
screen_adjust_palette_color( 3, color_rgb, 1);
screen_adjust_palette_color(10, color_rgb, 1);
screen_adjust_palette_color( 2, color_rgb, -1);
screen_adjust_palette_color( 9, color_rgb, -1);
}
screen_vsync();
state = 0;
frame1 = frame2 = 1;
// _timer_counter = 20;
}
break;
case 2:
screen_add_sprite(95, 155, animframe_0135[frame1]);
if (frame1 < animframe_0135[0]) {
++frame1;
} else {
frame1 = 1;
}
screen_add_sprite(190, 155, animframe_022d[frame2]);
if (frame2 < animframe_022d[0]) {
++frame2;
} else {
frame2 = 1;
}
g_vars.two_players_flag = 1;
if (g_sys.input.direction & INPUT_DIRECTION_RIGHT) {
g_sys.input.direction &= ~INPUT_DIRECTION_RIGHT;
for (int i = 0; i < colors_count; ++i) {
screen_adjust_palette_color(2, color_rgb, -1);
screen_adjust_palette_color(9, color_rgb, -1);
}
screen_vsync();
state = 0;
frame1 = frame2 = 1;
// _timer_counter = 20;
} else if (g_sys.input.direction & INPUT_DIRECTION_LEFT) {
g_sys.input.direction &= ~INPUT_DIRECTION_LEFT;
for (int i = 0; i < colors_count; ++i) {
screen_adjust_palette_color( 3, color_rgb, -1);
screen_adjust_palette_color(10, color_rgb, -1);
}
screen_vsync();
state = 1;
frame1 = frame2 = 1;
// _timer_counter = 20;
}
break;
}
// state_default_:
if (var9 == 0) {
fade_in_palette();
var9 = 1;
for (int i = 0; i < colors_count; ++i) {
screen_adjust_palette_color( 3, color_rgb, 1);
screen_adjust_palette_color(10, color_rgb, 1);
}
continue;
}
screen_clear_last_sprite();
screen_redraw_sprites();
if (g_vars.cheat_flag) {
for (int i = 5; i >= 0; --i) {
if (g_vars.inp_keyboard[2 + i]) {
g_vars.level = i;
break;
}
}
// screen_draw_number_type1(_level + 1, 256, 148, 2);
} else {
check_cheat_code();
}
screen_flip();
screen_vsync();
screen_vsync();
screen_vsync();
g_vars.screen_draw_offset ^= 0x2000;
screen_clear_sprites();
if (g_sys.input.space || g_vars.play_demo_flag) {
quit = 1;
}
} while (!quit && g_sys.input.quit == 0);
}
static void do_inter_screen_helper(int a, int b, int c) {
if (c != 0) {
g_vars.screen_draw_offset ^= 0x2000;
}
int xpos = a + 20;
int ypos = b - 20;
for (int i = 0; i < 40; ++i) {
--xpos;
++ypos;
screen_add_sprite(xpos, ypos, 125);
screen_clear_last_sprite();
screen_redraw_sprites();
if (c != 0) {
screen_vsync();
}
screen_clear_sprites();
}
for (int i = 0; i < 40; ++i) {
++xpos;
++ypos;
screen_add_sprite(xpos, ypos, 125);
screen_clear_last_sprite();
screen_redraw_sprites();
if (c != 0) {
screen_vsync();
}
screen_clear_sprites();
}
if (c != 0) {
g_vars.screen_draw_offset ^= 0x2000;
}
}
static void do_inter_screen() {
static const uint8_t xpos[] = { 0xFA, 0x50, 0xF0, 0xC8, 0x50, 0x50 };
static const uint8_t ypos[] = { 0xAA, 0x37, 0x28, 0x5F, 0xA5, 0xAA };
load_img("inter.sqz");
g_vars.screen_h = 199;
screen_clear_sprites();
if (g_vars.level > 1) {
for (int i = 0; i < g_vars.level - 1; ++i) {
do_inter_screen_helper(xpos[i], ypos[i], 0);
}
}
if (g_vars.level == 5) {
do_inter_screen_helper(xpos[g_vars.level], ypos[g_vars.level], 0);
}
fade_in_palette();
if (g_vars.level > 0 && g_vars.level < 5) {
do_inter_screen_helper(xpos[g_vars.level - 1], ypos[g_vars.level - 1], 1);
}
g_vars.screen_draw_offset = 0x2000;
screen_do_transition2();
screen_flip();
if (g_vars.level < MAX_LEVELS - 1) {
play_sound(SOUND_2);
screen_add_sprite(xpos[g_vars.level], ypos[g_vars.level], 126);
screen_clear_last_sprite();
screen_redraw_sprites();
}
g_vars.screen_draw_offset = 0x2000;
screen_flip();
g_vars.screen_h = TILEMAP_SCREEN_H;
// _timer_counter = 4;
// while (_timer_counter != 0);
fade_out_palette();
}
void game_main() {
g_vars.screen_w = GAME_SCREEN_W;
g_vars.screen_h = GAME_SCREEN_H;
g_vars.screen_draw_offset = 0;
screen_flip();
screen_init();
g_sys.set_screen_size(GAME_SCREEN_W, GAME_SCREEN_H, "Blues Brothers");
g_vars.start_level = g_options.start_level;
load_sqv("sprite.sqv", g_res.spr_sqv, 0);
do_title_screen();
while (g_sys.input.quit == 0) {
if (!g_vars.level_completed_flag) {
// _level_cheat_code = 0;
g_vars.game_over_flag = 0;
g_vars.play_level_flag = 1;
if (!g_vars.play_demo_flag) {
g_vars.level = 0;
do_select_player();
} else {
g_vars.level = g_vars.start_level;
}
screen_unk5();
}
while (g_sys.input.quit == 0 && g_vars.play_level_flag) {
if (!g_vars.game_over_flag) {
do_inter_screen();
}
load_level_data(g_vars.level);
do_level();
}
}
}

296
game.h Normal file
View File

@ -0,0 +1,296 @@
#ifndef GAME_H__
#define GAME_H__
#include "intern.h"
#define GAME_SCREEN_W 320
#define GAME_SCREEN_H 200
#define TILEMAP_OFFSET_Y 14
#define TILEMAP_SCREEN_W 320
#define TILEMAP_SCREEN_H 160
#define PLAYER_JAKE 0
#define PLAYER_ELWOOD 1
#define MAX_DOORS 30
#define MAX_LEVELS 6
#define MAX_OBJECTS 41
#define MAX_SPRITES 40
#define SOUND_0 0
#define SOUND_2 2
#define SOUND_3 3
#define SOUND_4 4
#define SOUND_5 5
#define SOUND_6 6
#define SOUND_7 7
#define SOUND_8 8
#define SOUND_11 11
#define SOUND_12 12
#define SOUND_13 13
#define SOUND_14 14
#define SOUND_15 15
#define SOUND_16 16
#define CHEATS_NO_HIT (1 << 0)
struct options_t {
uint32_t cheats;
int start_level;
int start_xpos16;
int start_ypos16;
bool amiga_copper_bars;
bool amiga_colors;
};
extern struct options_t g_options;
struct sprite_t {
int16_t xpos;
int16_t ypos;
int16_t spr_w;
int16_t spr_h;
const uint8_t *frame;
struct sprite_t *next_sprite;
int16_t xflip;
int16_t unk16;
};
struct door_t {
uint16_t src_x16, src_y16;
uint16_t dst_x16, dst_y16;
};
#define OBJECT_DIRECTION_RIGHT 1
#define OBJECT_DIRECTION_LEFT 2
#define OBJECT_DIRECTION_UP 1
#define OBJECT_NUM_PLAYER1 39
#define OBJECT_NUM_PLAYER2 40
#define OBJECT_ANIM_DEAD 18
#define OBJECT_GRAB_CRATE 0
#define OBJECT_GRAB_BALLOON 2
#define OBJECT_GRAB_UMBRELLA 8
struct object_t {
uint8_t type;
uint8_t anim_frame;
uint8_t anim_num;
const uint8_t **animframes_ptr;
uint8_t facing_left;
int16_t xvelocity;
int16_t yvelocity;
int16_t xmaxvelocity;
int16_t ymaxvelocity;
int16_t screen_xpos;
int16_t xpos;
int16_t screen_ypos;
int16_t ypos;
int16_t xacc;
int16_t yacc;
int16_t unk1C; // xvelocity / 8
int16_t unk1E; // yvelocity / 8
uint8_t direction_lr;
uint8_t direction_ud;
int16_t xpos16; // tilemap_xpos
int16_t ypos16; // tilemap_ypos
// int16_t unk26;
int16_t floor_ypos16;
uint8_t yfriction;
uint8_t unk2B;
uint8_t sprite_type;
uint8_t unk2D; // xfriction
int16_t unk2E;
int8_t unk2F;
uint8_t op;
uint8_t grab_state; // 1:carry_crate
uint8_t grab_type;
uint8_t special_anim; // 2:flying
// uint8_t unk35;
int16_t player_xdist;
int16_t player_ydist;
uint8_t sprite3_counter;
uint8_t visible_flag;
uint8_t moving_direction; // 0:right 1:left
uint8_t unk3D; // 1:umbrella 2:balloon
uint8_t carry_crate_flag;
// int16_t unk3F;
// uint8_t unk41;
int16_t unk42;
uint8_t tile0_flags;
uint8_t tile1_flags;
uint8_t tile2_flags;
int16_t tile012_xpos;
int8_t elevator_direction; // -1,1
const uint8_t *trigger3;
uint8_t unk4D;
// int16_t unk4E;
int16_t unk50;
uint8_t data51; // health for obj39/40, horizontal direction for other objects
uint8_t unk53;
uint8_t unk54;
uint8_t unk55;
uint8_t unk56;
int16_t vinyls_count;
uint8_t collide_flag;
int16_t unk5A; // always zero
uint8_t unk5C;
uint8_t unk5D;
uint8_t blinking_counter;
uint8_t data5F; // music instrument number for obj39/40, counter for other objects
uint8_t unk60;
uint8_t lifes_count;
uint8_t flag_end_level; // level flag, set if music instrument was found
uint8_t unk63; // restart_level_flag
};
struct vars_t {
int level;
int player;
int start_level;
int16_t level_xpos[MAX_OBJECTS];
int16_t level_ypos[MAX_OBJECTS];
int screen_w, screen_h;
int screen_tilemap_w, screen_tilemap_h;
int screen_tilemap_w16, screen_tilemap_h16;
int screen_tilemap_xorigin, screen_tilemap_yorigin;
int screen_tilemap_xoffset, screen_tilemap_yoffset;
int screen_tilemap_size_w, screen_tilemap_size_h;
int screen_scrolling_dirmask, screen_unk1;
uint16_t screen_draw_offset;
uint16_t screen_tile_lut[256];
int level_completed_flag;
int play_level_flag;
int game_over_flag;
int found_music_instrument_flag;
int player2_dead_flag;
int player1_dead_flag;
int player2_scrolling_flag;
int play_demo_flag;
int quit_level_flag;
int music_num;
uint8_t inp_keyboard[256];
uint8_t inp_code;
int inp_key_space;
int inp_key_tab;
int inp_key_up;
int inp_key_down;
int inp_key_right;
int inp_key_left;
int inp_key_up_prev;
int inp_key_down_prev;
int inp_key_action;
struct door_t doors[MAX_DOORS];
int cheat_code_len;
int cheat_flag;
struct object_t objects[MAX_OBJECTS];
int two_players_flag;
int vinyls_count;
uint16_t timer_counter3;
int triggers_counter;
int update_objects_counter;
struct sprite_t sprites_table[MAX_SPRITES];
struct sprite_t *sprites[MAX_SPRITES];
int sprites_count;
};
extern struct vars_t g_vars;
/* staticres.c */
extern const uint8_t animframe_00dd[];
extern const uint8_t animframe_0135[];
extern const uint8_t animframe_01d5[];
extern const uint8_t animframe_022d[];
extern const int16_t level_xpos_magasin[];
extern const int16_t level_ypos_magasin[];
extern const int16_t level_xpos_concert[];
extern const int16_t level_ypos_concert[];
extern const int16_t level_xpos_ville[];
extern const int16_t level_ypos_ville[];
extern const int16_t level_xpos_egou[];
extern const int16_t level_ypos_egou[];
extern const int16_t level_xpos_prison[];
extern const int16_t level_ypos_prison[];
extern const int16_t level_xpos_ent[];
extern const int16_t level_ypos_ent[];
extern const int16_t level_dim[];
extern const uint8_t level_door[];
extern const uint16_t level_tilemap_start_xpos[];
extern const uint16_t level_tilemap_start_ypos[];
extern const uint8_t level_objtypes[];
extern uint8_t level_data[];
extern const uint8_t *animframes_059d[];
extern uint16_t level_obj_w[MAX_OBJECTS];
extern uint16_t level_obj_h[MAX_OBJECTS];
extern const uint16_t level_obj_type[MAX_OBJECTS];
extern uint8_t *level_tilesdata_1e8c[];
/* game.c */
extern void update_input();
extern void game_main();
extern void do_title_screen();
extern void do_select_player();
/* level.c */
extern void load_level_data(int num);
extern void do_level_update_tile(int x, int y, int num);
extern void do_level_update_panel_lifes(struct object_t *);
extern void do_level_enter_door(struct object_t *);
extern void do_level_player_hit(struct object_t *);
extern void do_level_drop_grabbed_object(struct object_t *);
extern void do_level_update_object38(struct object_t *obj);
extern void do_level();
/* opcodes.c */
extern void level_call_object_func(struct object_t *);
/* screen.c */
extern void screen_init();
extern void screen_clear_sprites(); // _sprites_count = 0
extern void screen_add_sprite(int x, int y, int frame);
extern void screen_clear_last_sprite();
extern void screen_redraw_sprites();
extern void fade_in_palette();
extern void fade_out_palette();
extern void screen_adjust_palette_color(int color, int b, int c);
extern void screen_vsync();
extern void screen_set_palette();
extern void screen_draw_frame(const uint8_t *frame, int a, int b, int c, int d);
extern void screen_flip();
extern void screen_unk4();
extern void screen_unk5();
extern void screen_unk6();
extern void screen_copy_tilemap2(int a, int b, int c, int d);
extern void screen_copy_tilemap(int a);
extern void screen_do_transition1(int a);
extern void screen_clear(int a);
extern void screen_draw_tile(int tile, int dst, int type);
extern void screen_do_transition2();
extern void screen_draw_number(int num, int x, int y, int color);
extern void screen_add_game_sprite1(int x, int y, int frame);
extern void screen_add_game_sprite2(int x, int y, int frame);
extern void screen_add_game_sprite3(int x, int y, int frame, int blinking_counter);
extern void screen_add_game_sprite4(int x, int y, int frame, int blinking_counter);
/* sound.c */
extern void sound_init(int rate);
extern void sound_fini();
extern void play_sound(int num);
extern void play_music(int num);
/* triggers.c */
extern uint16_t triggers_get_tile_type(int x, int y);
extern uint16_t triggers_get_next_tile_flags(int x, int y);
extern uint16_t triggers_get_tile_data(struct object_t *obj);
extern uint16_t triggers_get_next_tile_num(int x, int y);
extern void level_call_trigger_func(struct object_t *obj, int y);
extern void triggers_unk3(struct object_t *obj);
extern int16_t triggers_get_dy(struct object_t *obj);
extern void triggers_unk5(struct object_t *obj);
#endif /* GAME_H__ */

37
intern.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef INTERN_H__
#define INTERN_H__
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef ABS
#define ABS(a) (((a)<0)?(-(a)):(a))
#endif
#ifndef ARRAYSIZE
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
#endif
static inline uint16_t READ_BE_UINT16(const uint8_t *p) {
return (p[0] << 8) | p[1];
}
static inline uint32_t READ_BE_UINT32(const uint8_t *p) {
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}
static inline uint16_t READ_LE_UINT16(const uint8_t *p) {
return p[0] | (p[1] << 8);
}
#endif

2153
level.c Normal file

File diff suppressed because it is too large Load Diff

81
main.c Normal file
View File

@ -0,0 +1,81 @@
#include <getopt.h>
#include <sys/stat.h>
#include "fileio.h"
#include "game.h"
#include "resource.h"
#include "sys.h"
#include "util.h"
struct options_t g_options;
static const char *DEFAULT_DATA_PATH = ".";
static const char *USAGE =
"Usage: %s [OPTIONS]...\n"
" --datapath=PATH Path to data files (default '.')\n"
" --level=NUM Start at level NUM\n"
;
int main(int argc, char *argv[]) {
g_options.start_xpos16 = -1;
g_options.start_ypos16 = -1;
g_options.amiga_copper_bars = true;
g_options.amiga_colors = true;
const char *data_path = DEFAULT_DATA_PATH;
if (argc == 2) {
struct stat st;
if (stat(argv[1], &st) == 0 && S_ISDIR(st.st_mode)) {
data_path = strdup(argv[1]);
}
}
while (1) {
static struct option options[] = {
{ "datapath", required_argument, 0, 1 },
{ "level", required_argument, 0, 2 },
{ "debug", required_argument, 0, 3 },
{ "cheats", required_argument, 0, 4 },
{ "startpos", required_argument, 0, 5 },
{ 0, 0, 0, 0 },
};
int index;
const int c = getopt_long(argc, argv, "", options, &index);
if (c == -1) {
break;
}
switch (c) {
case 1:
data_path = strdup(optarg);
break;
case 2:
g_options.start_level = atoi(optarg);
break;
case 3:
g_debug_mask = atoi(optarg);
break;
case 4:
g_options.cheats = atoi(optarg);
break;
case 5:
sscanf(optarg, "%dx%d", &g_options.start_xpos16, &g_options.start_ypos16);
break;
default:
fprintf(stdout, USAGE, argv[0]);
return -1;
}
}
fio_init(data_path);
res_init();
g_sys.init();
sound_init(SYS_AUDIO_FREQ);
game_main();
sound_fini();
g_sys.fini();
res_fini();
fio_fini();
if (data_path != DEFAULT_DATA_PATH) {
free((char *)data_path);
}
return 0;
}

477
opcodes.c Normal file
View File

@ -0,0 +1,477 @@
/* objects update and logic */
#include "game.h"
#include "util.h"
static void object_func_op0(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 1) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
obj->xmaxvelocity = 40;
obj->special_anim = 1;
}
static void object_func_op1(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 1) {
obj->moving_direction ^= 1;
obj->xvelocity = 0;
}
obj->direction_lr = obj->moving_direction + 1;
obj->xmaxvelocity = 40;
if (obj->player_ydist <= 10 && obj->player_xdist > -26 && obj->player_xdist < 26) {
if (g_vars.objects[OBJECT_NUM_PLAYER1].special_anim != 18 && g_vars.objects[OBJECT_NUM_PLAYER2].special_anim != 18) {
if (obj->player_ydist > -20) {
obj->special_anim = 2;
obj->direction_lr = 0;
} else {
obj->special_anim = 1;
}
if (obj->player_ydist < -50) {
if (obj->player_xdist < 0) {
obj->moving_direction = 1;
obj->facing_left = 1;
} else {
obj->moving_direction = 0;
obj->facing_left = 0;
}
}
}
} else if (obj->player_ydist < 0 && obj->player_ydist > -50 && obj->player_xdist > -60 && obj->player_xdist < 60) {
obj->special_anim = 1;
}
}
// police
static void object_func_op2(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 1) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
obj->xmaxvelocity = 40;
if (obj->anim_num == 12 && obj->special_anim == 2) {
obj->special_anim = 3;
obj->anim_num = 1;
} else if (obj->anim_num == 12 && obj->special_anim == 3) {
obj->special_anim = 0;
}
if (obj->player_ydist <= 0 && obj->player_xdist > -(TILEMAP_SCREEN_W / 4) && obj->player_xdist < (TILEMAP_SCREEN_W / 4) && obj->direction_ud > 196) {
if (obj->special_anim == 0 || obj->anim_num == 12) {
obj->anim_num = 1;
obj->unk42 = 0;
do_level_update_object38(obj);
}
obj->facing_left = (obj->player_xdist < 0) ? 1 : 0;
}
if (obj->special_anim != 0) {
obj->direction_lr = 0;
}
}
static void object_func_op3(struct object_t *obj) {
if (obj->player_ydist <= 20 && obj->player_ydist > -(TILEMAP_SCREEN_W / 2) && obj->player_xdist < (TILEMAP_SCREEN_W / 2) && g_vars.objects[38].type == 100) {
if (obj->direction_ud > 226) {
if (obj->special_anim == 0 || obj->anim_num == 16) {
do_level_update_object38(obj);
}
obj->facing_left = (obj->player_xdist < 0) ? 1 : 0;
}
} else {
if (obj->anim_num == 16) {
obj->special_anim = 0;
}
}
if (obj->special_anim != 0) {
obj->direction_lr = 0;
}
}
// elevator
static void object_func_op4(struct object_t *obj) {
obj->unk2B = 0;
obj->unk1E = 0;
obj->yacc = 0;
if (obj->elevator_direction == 1) {
if (obj->moving_direction < 40) {
++obj->moving_direction;
obj->yvelocity = 0;
obj->special_anim = 1;
} else {
if (triggers_get_tile_type(obj->xpos16, obj->ypos16 + 1) == 10) {
obj->elevator_direction = -1;
obj->moving_direction = 1;
}
obj->yvelocity = 2;
obj->special_anim = 2;
}
}
if (obj->elevator_direction == -1) {
if (obj->moving_direction < 40) {
++obj->moving_direction;
obj->yvelocity = 0;
obj->special_anim = 1;
} else {
if (triggers_get_tile_type(obj->xpos16, obj->ypos16 - 4) == 10) {
obj->elevator_direction = 1;
obj->moving_direction = 1;
}
obj->yvelocity = -2;
obj->special_anim = 2;
}
}
}
static void object_func_op5(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 4 - obj->facing_left * 8, obj->ypos16) == 12) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
switch (obj->data5F) {
case 0:
obj->direction_lr |= 2;
obj->yvelocity = obj->data51 >> 3;
break;
case 1:
obj->direction_lr |= 1;
obj->yvelocity = (20 - obj->data51) >> 3;
break;
case 2:
obj->direction_lr |= 1;
obj->yvelocity = -(obj->data51 >> 3);
break;
case 3:
obj->direction_lr |= 2;
obj->yvelocity = (obj->data51 - 20) >> 3;
break;
}
if (obj->data51 > 20) {
obj->data51 = 0;
obj->data5F = (obj->data5F + 1) & 3;
}
obj->yacc = obj->yvelocity;
obj->special_anim = 1;
obj->xmaxvelocity = 32;
}
static void object_func_op8(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 1) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
obj->xmaxvelocity = 48;
obj->yacc = 0;
obj->yvelocity = 0;
if (obj->player_ydist <= 100 && obj->player_xdist > -90 && obj->player_xdist < 90 && obj->direction_ud > 196 && obj->special_anim == 0) {
obj->anim_num = 1;
obj->unk42 = 1;
do_level_update_object38(obj);
}
}
static void object_func_op10(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 1) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
obj->xmaxvelocity = 32;
if (obj->player_ydist <= 10 && obj->player_xdist > -(TILEMAP_SCREEN_W / 2) && obj->player_xdist < (TILEMAP_SCREEN_W / 2) && g_vars.objects[38].type == 100) {
if (obj->direction_ud > 226) {
if (obj->special_anim == 0 || obj->anim_num == 16) {
obj->anim_num = 1;
obj->unk42 = 0;
do_level_update_object38(obj);
}
obj->facing_left = (obj->player_xdist < 0) ? 1 : 0;
}
} else if (obj->anim_num == 16) {
obj->special_anim = 0;
}
if (obj->special_anim != 0) {
obj->direction_lr = 0;
}
}
static void object_func_op11(struct object_t *obj) {
extern uint8_t level_tiledata_1dbf[];
extern uint8_t level_tiledata_1dc7[];
// extern uint8_t level_tiledata_1dc0[];
// extern uint8_t level_tiledata_1dc8[];
obj->unk2B = 0;
obj->unk1E = 0;
obj->yacc = 0;
if (obj->elevator_direction == 1) {
if (obj->moving_direction < 40) {
++obj->moving_direction;
obj->yvelocity = 0;
obj->special_anim = 1;
level_tiledata_1dbf[0] = 1;
level_tiledata_1dc7[0] = 1;
// level_tiledata_1dc0[0] = 1;
// level_tiledata_1dc8[0] = 1;
} else {
// level_tiledata_1dc0[0] = 6;
// level_tiledata_1dc8[0] = 6;
if (triggers_get_tile_type(obj->xpos16, obj->ypos16 + 2) == 10) {
obj->elevator_direction = -1;
obj->moving_direction = 1;
}
obj->yvelocity = 4;
obj->special_anim = 2;
if (((obj->ypos - 8) & 15) == 0) {
do_level_update_tile(obj->xpos16, obj->ypos16 - 1, triggers_get_next_tile_num(obj->xpos16, obj->ypos16 - 2));
do_level_update_tile(obj->xpos16 - 1, obj->ypos16 - 1, triggers_get_next_tile_num(obj->xpos16 - 1, obj->ypos16 - 2));
}
}
}
if (obj->elevator_direction == -1) {
if (obj->moving_direction < 40) {
++obj->moving_direction;
obj->yvelocity = 0;
obj->special_anim = 1;
level_tiledata_1dbf[0] = 1;
level_tiledata_1dc7[0] = 1;
// level_tiledata_1dc0[0] = 1;
// level_tiledata_1dc8[0] = 1;
} else {
// level_tiledata_1dc0[0] = 6;
// level_tiledata_1dc8[0] = 6;
if (triggers_get_tile_type(obj->xpos16, obj->ypos16 - 4) == 10) {
obj->elevator_direction = 1;
obj->moving_direction = 1;
}
obj->yvelocity = -2;
obj->special_anim = 2;
if (((obj->ypos - 8) & 15) == 0) {
do_level_update_tile(obj->xpos16, obj->ypos16 - 1, triggers_get_next_tile_num(obj->xpos16 + 1, obj->ypos16 - 1));
do_level_update_tile(obj->xpos16 - 1, obj->ypos16 - 1, triggers_get_next_tile_num(obj->xpos16 + 2, obj->ypos16 - 1));
}
}
}
}
static void object_func_op12(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 1) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
obj->xmaxvelocity = 64;
if (obj->sprite_type != 0) {
obj->yfriction = 8;
}
obj->special_anim = 1;
}
static void object_func_op13(struct object_t *obj) {
obj->direction_lr = 0;
obj->xmaxvelocity = 0;
obj->special_anim = 1;
}
static void object_func_op14(struct object_t *obj) {
obj->special_anim = 2;
// sub_1AD3B(obj->xpos, level_ypos_egou[obj->unk5D] - _screen_tilemap_yorigin, obj->xpos, obj->ypos - 5, 3);
if (obj->elevator_direction == 1) {
if (obj->moving_direction < 25) {
++obj->moving_direction;
obj->yvelocity = 0;
} else if (triggers_get_tile_type(obj->xpos16, obj->ypos16 + 1) != 0) {
obj->elevator_direction = -1;
obj->moving_direction = 1;
obj->yacc = 0;
obj->yvelocity = 0;
} else {
obj->moving_direction = 1;
obj->yacc = 2;
obj->yvelocity = 2;
}
}
if (obj->elevator_direction == -1) {
if (obj->moving_direction < 10) {
++obj->moving_direction;
obj->yvelocity = 0;
} else if (obj->ypos <= level_ypos_egou[obj->unk5D]) {
obj->elevator_direction = 1;
obj->moving_direction = 1;
obj->yacc = 0;
obj->yvelocity = 0;
} else {
obj->moving_direction = 1;
obj->yacc = -2;
obj->yvelocity = -2;
}
}
}
static void object_func_op15(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 4 - obj->facing_left * 8, obj->ypos16) == 12) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
obj->yacc = obj->yvelocity;
obj->special_anim = 1;
obj->xmaxvelocity = 32;
}
static void object_func_op16(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 1) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
obj->xmaxvelocity = 64;
obj->special_anim = 1;
if (obj->tile0_flags != 0 && obj->sprite_type != 0) {
obj->yfriction = 6;
}
}
static void object_func_op17(struct object_t *obj) {
obj->unk2B = 0;
obj->unk1E = 0;
obj->yacc = 0;
if (obj->elevator_direction == 1) {
if (obj->moving_direction < 40) {
++obj->moving_direction;
obj->yvelocity = 0;
obj->special_anim = 1;
} else {
if (triggers_get_tile_type(obj->xpos16, obj->ypos16 + 1) == 1) {
obj->elevator_direction = -1;
obj->moving_direction = 1;
}
obj->yvelocity = 2;
obj->special_anim = 2;
if (((obj->ypos - 12) & 15) == 0) {
do_level_update_tile(obj->xpos16, obj->ypos16, triggers_get_next_tile_num(obj->xpos16, obj->ypos16 - 2));
do_level_update_tile(obj->xpos16 - 1, obj->ypos16, triggers_get_next_tile_num(obj->xpos16 - 1, obj->ypos16 - 2));
}
}
}
if (obj->elevator_direction == -1) {
if (obj->moving_direction < 40) {
++obj->moving_direction;
obj->yvelocity = 0;
obj->special_anim = 1;
} else {
if (triggers_get_tile_type(obj->xpos16, obj->ypos16 - 3) == 1) {
obj->elevator_direction = 1;
obj->moving_direction = 1;
}
obj->yvelocity = -2;
obj->special_anim = 2;
if ((obj->ypos & 15) == 0) {
do_level_update_tile(obj->xpos16, obj->ypos16 - 1, triggers_get_next_tile_num(obj->xpos16, obj->ypos16));
do_level_update_tile(obj->xpos16 - 1, obj->ypos16 - 1, triggers_get_next_tile_num(obj->xpos16 - 1, obj->ypos16));
}
}
}
}
static void object_func_op18(struct object_t *obj) {
if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 1) {
obj->moving_direction ^= 1;
}
obj->direction_lr = obj->moving_direction + 1;
obj->xmaxvelocity = 40;
obj->special_anim = 1;
if (obj->sprite_type != 0) {
obj->yfriction = 2;
}
}
static void object_func_op19(struct object_t *obj) {
obj->unk2B = 0;
obj->unk1E = 0;
obj->yacc = 0;
if (obj->elevator_direction == 1) {
if (obj->moving_direction < 40) {
++obj->moving_direction;
obj->yvelocity = 0;
obj->special_anim = 1;
} else {
if (triggers_get_tile_type(obj->xpos16, obj->ypos16 + 1) != 0) {
obj->elevator_direction = -1;
obj->moving_direction = 1;
}
obj->yvelocity = 2;
obj->special_anim = 2;
}
}
if (obj->elevator_direction == -1) {
if (obj->moving_direction < 40) {
++obj->moving_direction;
obj->yvelocity = 0;
obj->special_anim = 1;
} else {
if (triggers_get_tile_type(obj->xpos16, obj->ypos16 - 5) != 0) {
obj->elevator_direction = 1;
obj->moving_direction = 1;
}
obj->yvelocity = -2;
obj->special_anim = 2;
}
}
}
void level_call_object_func(struct object_t *obj) {
if (obj->collide_flag == 0) {
switch (obj->op - 1) {
case 0:
object_func_op0(obj);
break;
case 1:
case 9:
object_func_op1(obj);
break;
case 2:
object_func_op2(obj);
break;
case 3:
object_func_op3(obj);
break;
case 4:
object_func_op4(obj);
break;
case 5:
object_func_op5(obj);
break;
case 8:
object_func_op8(obj);
break;
case 10:
object_func_op10(obj);
break;
case 11:
object_func_op11(obj);
break;
case 12:
object_func_op12(obj);
break;
case 13:
object_func_op13(obj);
break;
case 14:
object_func_op14(obj);
break;
case 15:
object_func_op15(obj);
break;
case 16:
object_func_op16(obj);
break;
case 17:
object_func_op17(obj);
break;
case 18:
object_func_op18(obj);
break;
case 19:
object_func_op19(obj);
break;
default:
// print_warning("level_call_object_func: unimplemented opcode %d", obj->op - 1);
obj->special_anim = 1;
break;
}
}
}

279
resource.c Normal file
View File

@ -0,0 +1,279 @@
#include "fileio.h"
#include "resource.h"
#include "sys.h"
#include "unpack.h"
#include "util.h"
struct resource_data_t g_res;
void res_init() {
static const int SQL_SIZE = 1000 * 16;
g_res.sql = (uint8_t *)malloc(SQL_SIZE);
if (!g_res.sql) {
print_error("Failed to allocate sql buffer, %d bytes", SQL_SIZE);
}
static const int SPR_SQV_SIZE = 64000;
g_res.spr_sqv = (uint8_t *)malloc(SPR_SQV_SIZE);
if (!g_res.spr_sqv) {
print_error("Failed to allocate sprite buffer, %d bytes", SPR_SQV_SIZE);
}
static const int AVT_SQV_SIZE = 437 * 16;
g_res.avt_sqv = (uint8_t *)malloc(AVT_SQV_SIZE);
if (!g_res.avt_sqv) {
print_error("Failed to allocate avt buffer, %d bytes", AVT_SQV_SIZE);
}
static const int TMP_SIZE = 32000 + 64000;
g_res.tmp = (uint8_t *)malloc(TMP_SIZE);
if (!g_res.tmp) {
print_error("Failed to allocate tmp buffer, %d bytes", TMP_SIZE);
}
static const int VGA_SIZE = 320 * 200;
g_res.vga = (uint8_t *)malloc(VGA_SIZE);
if (!g_res.vga) {
print_error("Failed to allocate vga buffer, %d bytes", VGA_SIZE);
}
static const int TILES_SIZE = 640 * 200;
g_res.tiles = (uint8_t *)malloc(TILES_SIZE);
if (!g_res.tiles) {
print_error("Failed to allocate tiles buffer, %d bytes", TILES_SIZE);
}
static const char *filename = "sound";
if (fio_exists(filename)) {
g_res.snd = (uint8_t *)malloc(SOUND_SIZE);
if (!g_res.snd) {
print_warning("Failed to allocate sound buffer, %d bytes", SOUND_SIZE);
} else {
int f = fio_open(filename, 1);
const int filesize = fio_size(f);
if (filesize != SOUND_SIZE) {
print_warning("Unexpected '%s' file size %d", filename, filesize);
} else if (fio_read(f, g_res.snd, SOUND_SIZE) != SOUND_SIZE) {
print_error("Failed to read %d bytes from file '%s'", filesize, filename);
}
fio_close(f);
}
}
if (fio_exists("demomag.sql")) {
g_res.flags = RESOURCE_FLAGS_DEMO;
}
}
void res_fini() {
free(g_res.sql);
free(g_res.spr_sqv);
free(g_res.avt_sqv);
free(g_res.tmp);
free(g_res.vga);
free(g_res.tiles);
free(g_res.snd);
}
int read_file(const char *filename, uint8_t *dst) {
const int f = fio_open(filename, 1);
const int filesize = fio_size(f);
if (fio_read(f, dst, filesize) != filesize) {
print_error("Failed to read %d bytes from file '%s'", filesize, filename);
}
fio_close(f);
return filesize;
}
int read_compressed_file(const char *filename, uint8_t *dst) {
return unpack(filename, dst);
}
static void decode_bitplane_scanline(const uint8_t *src, int depth, int w, uint8_t *dst) {
const int plane_size = w / depth;
for (int x = 0; x < plane_size; ++x) {
for (int i = 0; i < 8; ++i) {
int color = 0;
const int mask = 1 << (7 - i);
for (int bit = 0; bit < depth; ++bit) {
if (src[bit * plane_size] & mask) {
color |= 1 << bit;
}
}
dst[i] = color;
}
++src;
dst += 8;
}
}
static void load_iff(const uint8_t *data, uint32_t size, uint8_t *dst, int dst_pitch) {
print_debug(DBG_RESOURCE, "load_iff size %d", size);
if (data && memcmp(data, "FORM", 4) == 0) {
int offset = 12;
while (offset < size) {
const uint8_t *buf = data + offset;
const int len = (READ_BE_UINT32(buf + 4) + 1) & ~1;
print_debug(DBG_RESOURCE, "tag '%c%c%c%c' len %d", buf[0], buf[1], buf[2], buf[3], len);
if (memcmp(buf, "BMHD", 4) == 0) {
buf += 8;
const int w = READ_BE_UINT16(buf);
const int h = READ_BE_UINT16(buf + 2);
const int planes = buf[8];
const int compression = buf[10];
print_debug(DBG_RESOURCE, "w %d h %d planes %d compression %d", w, h, planes, compression);
if (w != 320 || h < 200) {
print_error("Unhandled LBM dimensions %d,%d", w, h);
return;
}
if (planes != 4) {
print_error("Unhandled LBM planes count %d", planes);
return;
}
if (compression != 1) {
print_error("Unhandled LBM compression %d", compression);
return;
}
} else if (memcmp(buf, "CMAP", 4) == 0) {
buf += 8;
for (int i = 0; i < len; ++i) {
g_res.palette[i] = buf[i];
}
} else if (memcmp(buf, "BODY", 4) == 0) {
buf += 8;
int offset = 0;
int i = 0;
int y = 0;
uint8_t scanline[160];
while (i < len && offset < 32000) {
int code = (int8_t)buf[i++];
if (code != -128) {
if (code < 0) {
code = 1 - code;
memset(scanline + offset % 160, buf[i], code);
++i;
} else {
++code;
memcpy(scanline + offset % 160, buf + i, code);
i += code;
}
offset += code;
if ((offset % 160) == 0) {
decode_bitplane_scanline(scanline, 4, 160, dst + y * dst_pitch);
++y;
}
}
}
print_debug(DBG_RESOURCE, "scanlines %d", y);
}
offset += 8 + len;
}
}
}
void load_avt(const char *filename, uint8_t *dst, int offset) {
read_compressed_file(filename, dst);
const uint8_t *ptr = dst;
const int count = READ_LE_UINT16(ptr); ptr += 6;
print_debug(DBG_RESOURCE, "avt count %d", count);
for (int i = 0; i < count; ++i) {
g_res.avt[offset + i] = ptr;
ptr += 132;
}
}
static const uint8_t *trigger_lookup_table1(uint8_t num) {
extern const uint8_t *level_triggersdata_3356[];
assert(num < 4);
return level_triggersdata_3356[num];
}
static const uint8_t *trigger_lookup_table2(uint8_t num) {
if (num == 255) {
return 0;
}
extern uint8_t *level_tilesdata_1e8c[];
if (num < 128) {
assert(num < 86);
return level_tilesdata_1e8c[num];
}
extern uint8_t *level_tilesdata_1fe8[];
num -= 128;
assert(num < 17);
return level_tilesdata_1fe8[num];
}
static const uint8_t *trigger_lookup_table3(uint8_t num) {
if (num == 255) {
return 0;
}
extern const uint8_t *level_triggersdata_2030[];
assert(num < 61);
return level_triggersdata_2030[num];
}
void load_bin(const char *filename) {
uint8_t bin[MAX_TRIGGERS * 10];
const int size = read_file(filename, bin);
assert(size == MAX_TRIGGERS * 10);
const uint8_t *p = bin;
for (int i = 0; i < MAX_TRIGGERS; ++i) {
struct trigger_t *t = &g_res.triggers[i];
t->tile_type = p[0];
t->tile_flags = p[1];
t->op_func = p[2];
t->op_table1 = trigger_lookup_table1(p[3]);
t->op_table2 = trigger_lookup_table2(p[4]);
t->unk10 = p[5];
t->op_table3 = trigger_lookup_table3(p[6]);
t->unk16 = p[7];
t->tile_index = p[8];
t->foreground_tile_num = p[9];
p += 10;
}
}
void load_ck(const char *filename, uint16_t offset) {
const int size = read_compressed_file(filename, g_res.tmp);
switch (offset) {
case 0x6000: // page3
offset = 0;
break;
case 0x8000: // page4
offset = 320;
break;
default:
print_error("Unexpected offset 0x%x in load_ck()", offset);
break;
}
load_iff(g_res.tmp, size, g_res.tiles + offset, 640);
g_sys.set_screen_palette(g_res.palette, 16);
}
void load_img(const char *filename) {
const int size = read_compressed_file(filename, g_res.tmp);
assert(size <= 32000);
load_iff(g_res.tmp, size, g_res.tmp + 32000, 320);
g_sys.set_screen_palette(g_res.palette, 16);
g_sys.update_screen(g_res.tmp + 32000, 0);
memcpy(g_res.vga, g_res.tmp + 32000, 64000);
}
void load_sqv(const char *filename, uint8_t *dst, int offset) {
read_compressed_file(filename, dst);
const uint8_t *ptr = dst;
const int count = READ_LE_UINT16(ptr); ptr += 6;
print_debug(DBG_RESOURCE, "sqv count %d", count);
for (int i = 0; i < count; ++i) {
g_res.spr_frames[offset + i] = ptr;
const int h = READ_LE_UINT16(ptr - 4);
const int w = READ_LE_UINT16(ptr - 2);
assert((w & 3) == 0);
const int size = (w >> 1) * h + 4;
print_debug(DBG_RESOURCE, "sprite %d, dim %d,%d size %d", i, w, h, size);
ptr += size;
}
}
void load_sql(const char *filename) {
read_compressed_file(filename, g_res.sql);
}
uint8_t *lookup_sql(int x, int y) {
return g_res.sql + y * 128 + x;
}

60
resource.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef RESOURCE_H__
#define RESOURCE_H__
#include "intern.h"
#define TILE_NUM_CRATE 5
#define TILE_NUM_BALLOON 8
#define TILE_NUM_UMBRELLA 9
struct trigger_t {
int16_t tile_type;
int16_t tile_flags;
uint8_t op_func;
const uint8_t *op_table1; // dy for (x&15)
const uint8_t *op_table2; // tile_animation
int16_t unk10;
const uint8_t *op_table3; // tile_trigger
uint8_t unk16; // next_tile_num
uint8_t tile_index; // op_table2
uint8_t foreground_tile_num;
};
#define MAX_AVT 50
#define MAX_SPR_FRAMES 200
#define MAX_TRIGGERS 256
#define SOUND_SIZE 29376
#define RESOURCE_FLAGS_DEMO (1 << 0)
struct resource_data_t {
uint32_t flags;
uint8_t *sql;
uint8_t *spr_sqv;
uint8_t *avt_sqv;
uint8_t *tmp;
const uint8_t *avt[MAX_AVT];
const uint8_t *spr_frames[MAX_SPR_FRAMES];
uint8_t palette[16 * 3];
struct trigger_t triggers[MAX_TRIGGERS];
uint8_t *vga;
uint8_t *tiles;
uint8_t *snd;
};
extern struct resource_data_t g_res;
extern void res_init();
extern void res_fini();
extern int read_file(const char *filename, uint8_t *dst);
extern int read_compressed_file(const char *filename, uint8_t *dst);
extern void load_avt(const char *filename, uint8_t *dst, int offset);
extern void load_bin(const char *filename);
extern void load_ck(const char *filename, uint16_t offset);
extern void load_img(const char *filename);
extern void load_sqv(const char *filename, uint8_t *dst, int size);
extern void load_sql(const char *filename);
extern uint8_t * lookup_sql(int x, int y);
#endif /* RESOURCE_H__ */

183
screen.c Normal file
View File

@ -0,0 +1,183 @@
#include "decode.h"
#include "game.h"
#include "resource.h"
#include "sys.h"
#include "util.h"
#define HZ 30
void screen_init() {
for (int i = 0; i < MAX_SPRITES; ++i) {
g_vars.sprites[i] = &g_vars.sprites_table[i];
}
for (int i = 0; i < MAX_SPRITES - 1; ++i) {
g_vars.sprites[i]->next_sprite = g_vars.sprites[i] + 1;
}
g_vars.sprites[MAX_SPRITES - 1]->next_sprite = 0;
}
void screen_clear_sprites() {
g_vars.sprites_count = 0;
}
void screen_add_sprite(int x, int y, int frame) {
assert(g_vars.sprites_count < MAX_SPRITES);
struct sprite_t *spr = g_vars.sprites[g_vars.sprites_count];
spr->xpos = x;
spr->ypos = y;
spr->frame = g_res.spr_frames[frame];
spr->xflip = 0;
spr->unk16 = 0;
spr->next_sprite = spr + 1;
++g_vars.sprites_count;
}
void screen_clear_last_sprite() {
assert(g_vars.sprites_count >= 0);
if (g_vars.sprites_count > 0) {
g_vars.sprites[g_vars.sprites_count - 1]->next_sprite = 0;
}
}
void screen_redraw_sprites() {
for (int i = 0; i < g_vars.sprites_count; ++i) {
struct sprite_t *spr = g_vars.sprites[i];
const uint8_t *p = spr->frame;
const int h = READ_LE_UINT16(p - 4);
const int w = READ_LE_UINT16(p - 2);
const int y = spr->ypos - h;
const int x = spr->xpos - w / 2;
decode_ega_spr(p, w, w, h, g_res.vga, GAME_SCREEN_W, x, y, spr->xflip);
}
}
void fade_in_palette() {
g_sys.fade_in_palette();
}
void fade_out_palette() {
// g_sys.fade_out_palette();
}
void screen_adjust_palette_color(int color, int b, int c) {
g_res.palette[color * 3 + b] += c;
screen_vsync();
screen_set_palette();
}
void screen_vsync() {
}
void screen_set_palette() {
g_sys.set_screen_palette(g_res.palette, 16);
}
void screen_draw_frame(const uint8_t *frame, int a, int b, int c, int d) {
const int h = READ_LE_UINT16(frame - 4);
assert(a <= h);
const int w = READ_LE_UINT16(frame - 2);
assert(b <= w);
const int x = c;
const int y = d + a + 2;
decode_ega_spr(frame, w, b, h, g_res.vga, GAME_SCREEN_W, x, y, 0);
}
void screen_flip() {
g_sys.update_screen(g_res.vga, 1);
g_sys.sleep(1000 / HZ);
}
void screen_unk4() {
memcpy(g_res.vga, g_res.tmp + 32000, 64000);
}
void screen_unk5() {
screen_clear(0);
screen_do_transition2();
screen_clear(0);
}
void screen_unk6() {
// _screen_draw_offset -= 12;
// screen_do_transition2();
// _screen_draw_offset += 12;
g_sys.update_screen(g_res.vga, 1);
g_sys.sleep(1000 / HZ); // get_time
memset(g_res.vga, 0, GAME_SCREEN_W * GAME_SCREEN_H);
}
void screen_copy_tilemap2(int a, int b, int c, int d) {
}
void screen_copy_tilemap(int a) {
}
static void screen_unk13(int a) {
}
void screen_do_transition1(int a) {
int i, count, increment;
if (a != 0) {
i = 11;
count = 0;
increment = -1;
} else {
screen_clear(0);
i = 0;
count = 11;
increment = 1;
}
while (i != count) {
screen_unk13(i);
screen_unk13(19 - i);
screen_vsync();
i += increment;
}
}
void screen_clear(int a) {
memset(g_res.vga, 0, GAME_SCREEN_W * GAME_SCREEN_H);
}
void screen_draw_tile(int tile, int dst, int type) {
int y = (dst / 640) * 16 + TILEMAP_OFFSET_Y;
int x = (dst % 640) / 2 * 16;
const uint8_t *src = g_res.tiles + tile * 16;
if (type == 4) {
src += 320;
}
for (int i = 0; i < 16; ++i) {
memcpy(g_res.vga + (y + i) * GAME_SCREEN_W + x, src, 16);
src += 640;
}
}
void screen_do_transition2() {
print_warning("screen_do_transition2");
}
void screen_draw_number(int num, int x, int y, int color) {
extern const uint8_t font_data[];
y += TILEMAP_OFFSET_Y;
decode_ega_spr(font_data + (num / 10) * 32, 8, 8, 8, g_res.vga, GAME_SCREEN_W, x - 8, y, 0);
decode_ega_spr(font_data + (num % 10) * 32, 8, 8, 8, g_res.vga, GAME_SCREEN_W, x, y, 0);
}
void screen_add_game_sprite1(int x, int y, int frame) {
screen_add_sprite(x, y + TILEMAP_OFFSET_Y, frame);
g_vars.sprites[g_vars.sprites_count - 1]->xflip = 0;
}
void screen_add_game_sprite2(int x, int y, int frame) {
screen_add_sprite(x, y + TILEMAP_OFFSET_Y, frame);
g_vars.sprites[g_vars.sprites_count - 1]->xflip = 1;
}
void screen_add_game_sprite3(int x, int y, int frame, int blinking_counter) {
print_warning("screen_add_game_sprite3");
}
void screen_add_game_sprite4(int x, int y, int frame, int blinking_counter) {
print_warning("screen_add_game_sprite4");
}

123
sound.c Normal file
View File

@ -0,0 +1,123 @@
#include "fileio.h"
#include "game.h"
#include "resource.h"
#include "sys.h"
#include <libmodplug/modplug.h>
#define PAULA_FREQ 3546897
static const struct {
uint16_t offset; // words
uint16_t size;
} _sounds_amiga[] = {
{ 0x0000, 0x0256 },
{ 0x0000, 0x0256 },
{ 0x0000, 0x0256 },
{ 0x0000, 0x0256 },
{ 0x012b, 0x051a },
{ 0x03b8, 0x0532 },
{ 0x0651, 0x03e8 },
{ 0x0b51, 0x0c1c },
{ 0x115f, 0x0e40 },
{ 0x0000, 0x0256 },
{ 0x0000, 0x0256 },
{ 0x187f, 0x04c4 },
{ 0x1ae1, 0x0c14 },
{ 0x20eb, 0x1e1a },
{ 0x2ff8, 0x0610 },
{ 0x3300, 0x0420 },
{ 0x3510, 0x032c },
{ 0x36a6, 0x0574 }
};
// Amiga, unexotica
static const char *_modules[] = {
"ALMOST", "mod.almost",
"GUNN", "mod.bluesgunnbest",
"EVERY", "mod.every",
"SHOT", "mod.shot"
};
struct mixerchannel_t {
uint8_t *data;
uint32_t pos;
uint32_t step;
uint32_t size;
};
static int _rate = 22050;
static struct mixerchannel_t _channel;
static ModPlugFile *_mpf;
static void mix(void *param, uint8_t *buf, int len) {
memset(buf, 0, len);
if (_mpf) {
ModPlug_Read(_mpf, buf, len);
}
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(int rate) {
_rate = rate;
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() {
g_sys.stop_audio();
}
void play_sound(int num) {
if (g_res.snd) {
g_sys.lock_audio();
_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) {
g_sys.lock_audio();
if (_mpf) {
ModPlug_Unload(_mpf);
_mpf = 0;
}
const char *filename = _modules[num * 2 + 1];
if (fio_exists(filename)) {
int slot = fio_open(filename, 1);
int size = fio_size(slot);
uint8_t *buf = (uint8_t *)malloc(size);
if (buf) {
fio_read(slot, buf, size);
_mpf = ModPlug_Load(buf, size);
free(buf);
}
fio_close(slot);
}
g_sys.unlock_audio();
}

1571
staticres.c Normal file

File diff suppressed because it is too large Load Diff

48
sys.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef SYS_H__
#define SYS_H__
#include "intern.h"
#define INPUT_DIRECTION_LEFT (1 << 0)
#define INPUT_DIRECTION_RIGHT (1 << 1)
#define INPUT_DIRECTION_UP (1 << 2)
#define INPUT_DIRECTION_DOWN (1 << 3)
#define SYS_AUDIO_FREQ 22050
struct input_t {
char quit;
char escape;
char space;
char direction;
char functions[12];
char digits[10];
char alphabet[26];
};
typedef void (*sys_audio_cb)(void *, uint8_t *data, int len);
struct sys_t {
struct input_t input;
int (*init)();
void (*fini)();
void (*set_screen_size)(int w, int h, const char *caption);
void (*set_screen_palette)(const uint8_t *colors, int);
void (*set_palette_amiga)(const uint16_t *colors);
void (*set_copper_bars)(const uint16_t *data);
void (*fade_in_palette)();
void (*fade_out_palette)();
void (*update_screen)(const uint8_t *p, int present);
void (*process_events)();
void (*sleep)(int duration);
uint32_t (*get_timestamp)();
void (*start_audio)(sys_audio_cb callback, void *param);
void (*stop_audio)();
void (*lock_audio)();
void (*unlock_audio)();
};
extern struct sys_t g_sys;
#endif /* SYS_H__ */

285
sys_sdl2.c Normal file
View File

@ -0,0 +1,285 @@
#include <SDL.h>
#include "sys.h"
#include "util.h"
#define COPPER_BARS_H 80
static const int SCALE = 2;
static const int FADE_STEPS = 16;
static int _screen_w;
static int _screen_h;
static SDL_Window *_window;
static SDL_Renderer *_renderer;
static SDL_Texture *_texture;
static SDL_PixelFormat *_fmt;
static uint32_t _screen_palette[32];
static uint32_t *_screen_buffer;
static struct input_t *_input = &g_sys.input;
static int _copper_color;
static uint32_t _copper_palette[COPPER_BARS_H];
static int sdl2_init() {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
SDL_ShowCursor(SDL_DISABLE);
_screen_w = _screen_h = 0;
_window = 0;
_renderer = 0;
_texture = 0;
_fmt = 0;
memset(_screen_palette, 0, sizeof(_screen_palette));
_screen_buffer = 0;
_copper_color = -1;
return 0;
}
static void sdl2_fini() {
if (_renderer) {
SDL_DestroyRenderer(_renderer);
_renderer = 0;
}
if (_window) {
SDL_DestroyWindow(_window);
_window = 0;
}
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = 0;
}
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
free(_screen_buffer);
SDL_Quit();
}
static void sdl2_set_screen_size(int w, int h, const char *caption) {
assert(_screen_w == 0 && _screen_h == 0); // abort if called more than once
_screen_w = w;
_screen_h = h;
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); /* nearest pixel sampling */
const int window_w = w * SCALE;
const int window_h = h * SCALE; // * 4 / 3;
_window = SDL_CreateWindow(caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_w, window_h, 0);
_renderer = SDL_CreateRenderer(_window, -1, 0);
print_debug(DBG_SYSTEM, "set_screen_size %d,%d", _screen_w, _screen_h);
_screen_buffer = (uint32_t *)calloc(_screen_w * _screen_h, sizeof(uint32_t));
if (!_screen_buffer) {
print_error("Failed to allocate screen buffer");
}
static const uint32_t pfmt = SDL_PIXELFORMAT_RGB888;
_texture = SDL_CreateTexture(_renderer, pfmt, SDL_TEXTUREACCESS_STREAMING, _screen_w, _screen_h);
_fmt = SDL_AllocFormat(pfmt);
}
static uint32_t convert_amiga_color(uint16_t color) {
uint8_t r = (color >> 8) & 15;
r |= r << 4;
uint8_t g = (color >> 4) & 15;
g |= g << 4;
uint8_t b = color & 15;
b |= b << 4;
return SDL_MapRGB(_fmt, r, g, b);
}
static void sdl2_set_palette_amiga(const uint16_t *colors) {
for (int i = 0; i < 32; ++i) {
_screen_palette[i] = convert_amiga_color(colors[i]);
}
}
static void sdl2_set_copper_bars(const uint16_t *data) {
if (!data) {
_copper_color = -1;
} else {
_copper_color = (data[0] - 0x180) / 2;
const uint16_t *src = data + 1;
uint32_t *dst = _copper_palette;
for (int i = 0; i < 16; ++i) {
const int j = i + 1;
*dst++ = convert_amiga_color(src[j]);
*dst++ = convert_amiga_color(src[i]);
*dst++ = convert_amiga_color(src[j]);
*dst++ = convert_amiga_color(src[i]);
*dst++ = convert_amiga_color(src[j]);
}
}
}
static void sdl2_set_screen_palette(const uint8_t *colors, int count) {
for (int i = 0; i < count; ++i) {
_screen_palette[i] = SDL_MapRGB(_fmt, colors[0], colors[1], colors[2]);
colors += 3;
}
}
static void fade_palette_helper(int in) {
SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND);
SDL_Rect r;
r.x = r.y = 0;
SDL_GetRendererOutputSize(_renderer, &r.w, &r.h);
for (int i = 1; i <= FADE_STEPS; ++i) {
int alpha = 256 * i / FADE_STEPS;
if (in) {
alpha = 256 - alpha;
}
SDL_SetRenderDrawColor(_renderer, 0, 0, 0, alpha);
SDL_RenderClear(_renderer);
SDL_RenderCopy(_renderer, _texture, 0, 0);
SDL_RenderFillRect(_renderer, &r);
SDL_RenderPresent(_renderer);
SDL_Delay(30);
}
}
static void sdl2_fade_in_palette() {
fade_palette_helper(1);
}
static void sdl2_fade_out_palette() {
fade_palette_helper(0);
}
static void sdl2_update_screen(const uint8_t *p, int present) {
if (_copper_color != -1) {
for (int j = 0; j < _screen_h; ++j) {
for (int i = 0; i < _screen_w; ++i) {
if (*p == _copper_color && j / 2 < COPPER_BARS_H) {
_screen_buffer[j * _screen_w + i] = _copper_palette[j / 2];
} else {
_screen_buffer[j * _screen_w + i] = _screen_palette[*p];
}
++p;
}
}
} else {
for (int i = 0; i < _screen_w * _screen_h; ++i) {
_screen_buffer[i] = _screen_palette[p[i]];
}
}
SDL_UpdateTexture(_texture, 0, _screen_buffer, _screen_w * sizeof(uint32_t));
if (present) {
SDL_RenderClear(_renderer);
SDL_RenderCopy(_renderer, _texture, 0, 0);
SDL_RenderPresent(_renderer);
}
}
static void handle_keyevent(int keysym, int keydown) {
switch (keysym) {
case SDLK_LEFT:
if (keydown) {
_input->direction |= INPUT_DIRECTION_LEFT;
} else {
_input->direction &= ~INPUT_DIRECTION_LEFT;
}
break;
case SDLK_RIGHT:
if (keydown) {
_input->direction |= INPUT_DIRECTION_RIGHT;
} else {
_input->direction &= ~INPUT_DIRECTION_RIGHT;
}
break;
case SDLK_UP:
if (keydown) {
_input->direction |= INPUT_DIRECTION_UP;
} else {
_input->direction &= ~INPUT_DIRECTION_UP;
}
break;
case SDLK_DOWN:
if (keydown) {
_input->direction |= INPUT_DIRECTION_DOWN;
} else {
_input->direction &= ~INPUT_DIRECTION_DOWN;
}
break;
case SDLK_RETURN:
case SDLK_SPACE:
_input->space = keydown;
break;
}
}
static int handle_event(const SDL_Event *ev) {
switch (ev->type) {
case SDL_QUIT:
_input->quit = 1;
break;
case SDL_KEYUP:
handle_keyevent(ev->key.keysym.sym, 0);
break;
case SDL_KEYDOWN:
handle_keyevent(ev->key.keysym.sym, 1);
break;
default:
return -1;
}
return 0;
}
static void sdl2_process_events() {
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
handle_event(&ev);
if (_input->quit) {
break;
}
}
}
static void sdl2_sleep(int duration) {
SDL_Delay(duration);
}
static uint32_t sdl2_get_timestamp() {
return SDL_GetTicks();
}
static void sdl2_start_audio(sys_audio_cb callback, void *param) {
SDL_AudioSpec desired;
memset(&desired, 0, sizeof(desired));
desired.freq = SYS_AUDIO_FREQ;
desired.format = AUDIO_S16;
desired.channels = 1;
desired.samples = 2048;
desired.callback = callback;
desired.userdata = param;
if (SDL_OpenAudio(&desired, 0) == 0) {
SDL_PauseAudio(0);
}
}
static void sdl2_stop_audio() {
SDL_CloseAudio();
}
static void sdl2_lock_audio() {
SDL_LockAudio();
}
static void sdl2_unlock_audio() {
SDL_UnlockAudio();
}
struct sys_t g_sys = {
.init = sdl2_init,
.fini = sdl2_fini,
.set_screen_size = sdl2_set_screen_size,
.set_screen_palette = sdl2_set_screen_palette,
.set_palette_amiga = sdl2_set_palette_amiga,
.set_copper_bars = sdl2_set_copper_bars,
.fade_in_palette = sdl2_fade_in_palette,
.fade_out_palette = sdl2_fade_out_palette,
.update_screen = sdl2_update_screen,
.process_events = sdl2_process_events,
.sleep = sdl2_sleep,
.get_timestamp = sdl2_get_timestamp,
.start_audio = sdl2_start_audio,
.stop_audio = sdl2_stop_audio,
.lock_audio = sdl2_lock_audio,
.unlock_audio = sdl2_unlock_audio,
};

940
triggers.c Normal file
View File

@ -0,0 +1,940 @@
/* tiles update and object triggers */
#include "game.h"
#include "resource.h"
#include "util.h"
uint16_t triggers_get_tile_type(int x, int y) {
const uint8_t *p = lookup_sql(x, y);
const int num = p[0];
return g_res.triggers[num].tile_type;
}
uint16_t triggers_get_next_tile_flags(int x, int y) {
const uint8_t *p = lookup_sql(x, y);
int num = p[0];
num = g_res.triggers[num].unk16;
return g_res.triggers[num].tile_flags;
}
uint16_t triggers_get_tile_data(struct object_t *obj) {
const uint8_t *p = lookup_sql(obj->xpos16, obj->ypos16 + 1);
const int num = p[0];
return g_res.triggers[num].unk10;
}
uint16_t triggers_get_next_tile_num(int x, int y) {
const uint8_t *p = lookup_sql(x, y);
const int num = p[0];
return g_res.triggers[num].unk16;
}
static void trigger_func_op0(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->yfriction = 0;
}
obj->unk2F = 0;
obj->unk3D = 0;
}
static void trigger_func_op1(struct object_t *obj) {
if (obj->data5F == g_vars.level + 1) {
obj->carry_crate_flag = 1;
do_level_enter_door(obj);
}
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->yfriction = 0;
}
obj->unk2F = 0;
}
static void trigger_func_op2(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
if (!g_vars.screen_unk1) {
obj->unk1C = 5;
}
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->yfriction = 0;
}
obj->unk2F = 0;
if (g_vars.screen_unk1) {
obj->special_anim = 18;
obj->anim_num = 1;
}
}
static void trigger_func_op3(struct object_t *obj) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
if (obj->data5F != g_vars.level + 1) {
if (obj->yfriction == 0) {
obj->yfriction = 15;
}
} else {
obj->xpos16 = 101;
obj->ypos16 = 70;
obj->xpos = obj->xpos16 << 4;
obj->ypos = obj->ypos16 << 4;
obj->screen_xpos = obj->xpos - g_vars.screen_tilemap_xorigin;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
g_vars.screen_unk1 = 1;
}
}
static void trigger_func_op4(struct object_t *obj) {
if (obj->unk54 == 0) {
obj->unk54 = 0;
obj->unk55 = 0;
obj->unk56 = 8;
obj->sprite_type = 6;
} else {
obj->special_anim = 22;
obj->unk54 = 0;
obj->unk55 = 0;
obj->unk56 = 0;
obj->yfriction = 8;
obj->sprite_type = 0;
}
}
static void trigger_func_op5(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
}
if (obj->unk55 == 0) {
obj->unk54 = 0;
obj->unk55 = 2;
obj->unk56 = 9;
obj->sprite_type = 6;
} else if (obj->unk56 != 9) {
obj->special_anim = 22;
obj->unk54 = 0;
obj->unk55 = 0;
obj->unk56 = 0;
obj->sprite_type = 0;
}
}
static void trigger_func_op6(struct object_t *obj) {
if (obj->unk56 == 0) {
obj->unk54 = 2;
obj->unk55 = 0;
obj->unk56 = 7;
} else if (obj->unk56 != 7) {
obj->special_anim = 22;
obj->unk54 = 0;
obj->unk55 = 0;
obj->unk56 = 0;
obj->sprite_type = 0;
}
}
static void trigger_func_op7(struct object_t *obj) {
if (obj->unk56 == 2 || obj->unk56 == 7) {
obj->unk54 = 2;
}
obj->unk56 = 1;
}
static void trigger_func_op8(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
}
switch (obj->unk56 - 1) {
case 0:
obj->unk55 = 1;
break;
case 2:
obj->unk54 = 2;
break;
case 3:
obj->unk54 = 2;
break;
case 4:
obj->unk55 = 1;
break;
case 5:
obj->unk55 = 1;
break;
case 7:
obj->unk55 = 1;
break;
}
}
static void trigger_func_op9(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
}
obj->unk56 = 3;
obj->unk54 = 0;
}
static void trigger_func_op10(struct object_t *obj) {
obj->unk56 = 3;
obj->unk54 = 0;
}
static void trigger_func_op11(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
}
if (obj->unk56 == 1) {
obj->unk55 = 2;
} else if (obj->unk56 == 3) {
obj->unk54 = 2;
}
obj->unk56 = 4;
}
static void trigger_func_op12(struct object_t *obj) {
if (obj->unk56 == 1) {
obj->unk55 = 1;
obj->unk54 = 2;
} else if (obj->unk56 == 3 || obj->unk56 == 6) {
obj->unk54 = 1;
obj->unk55 = 0;
}
obj->unk56 = 5;
obj->sprite_type = 6;
}
static void trigger_func_op13(struct object_t *obj) {
if (obj->unk56 == 1) {
obj->unk55 = 2;
obj->unk54 = 2;
} else if (obj->unk56 == 3) {
obj->unk54 = 1;
}
obj->unk56 = 6;
obj->sprite_type = 6;
}
static void trigger_func_op14(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
}
obj->unk2F = 0;
}
static void trigger_func_op15(struct object_t *obj) {
if (obj->special_anim != 18) {
if (obj->floor_ypos16 < obj->ypos16 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
}
obj->sprite_type = 2;
obj->unk2F = 0;
}
}
static void trigger_func_op16(struct object_t *obj) {
if (obj->special_anim != 18) {
if (obj->floor_ypos16 < obj->ypos16 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
}
obj->sprite_type = 3;
obj->unk2F = 0;
}
}
static void trigger_func_op17(struct object_t *obj) {
if (obj->yvelocity < -2) {
obj->yvelocity -= 3;
obj->yacc = -3;
}
obj->xpos = (obj->xpos & ~15) + 7;
obj->screen_xpos = obj->xpos - g_vars.screen_tilemap_xorigin;
obj->xpos16 = obj->xpos >> 4;
}
static void trigger_func_op18(struct object_t *obj) {
if (obj->yvelocity < -2) {
obj->yvelocity -= 3;
obj->yacc = -3;
}
obj->xpos = (obj->xpos & ~15) + 17;
obj->screen_xpos = obj->xpos - g_vars.screen_tilemap_xorigin;
obj->xpos16 = obj->xpos >> 4;
}
static void trigger_func_op19(struct object_t *obj) {
if (obj->yvelocity < -2) {
obj->yvelocity -= 3;
obj->yacc = -3;
}
obj->xpos = (obj->xpos & ~15) - 1;
obj->screen_xpos = obj->xpos - g_vars.screen_tilemap_xorigin;
obj->xpos16 = obj->xpos >> 4;
}
static void trigger_func_op20(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 0;
}
obj->unk2F = 0;
}
static void trigger_func_op21(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->yfriction = 0;
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->yfriction = 0;
}
obj->unk2F = 0;
obj->sprite_type = 4;
obj->unk2D = 1;
}
static void trigger_func_op22(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
}
obj->unk2F = 0;
obj->sprite_type = 4;
obj->unk2D = 1;
}
static void trigger_func_op23(struct object_t *obj) {
obj->sprite_type = 4;
obj->unk2D = 1;
obj->unk2F = 0;
obj->yvelocity = 0;
}
static void trigger_func_op24(struct object_t *obj) {
obj->yfriction = (obj->yvelocity & 255) + 6;
if (obj->floor_ypos16 < obj->ypos16 && obj->unk2B == 0) {
play_sound(SOUND_14);
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
}
obj->unk3D = 0;
}
// speedwalk (right)
static void trigger_func_op27(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->unk1C = 4;
obj->sprite_type = 1;
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
}
obj->unk2F = 0;
}
// speedwalk (left)
static void trigger_func_op28(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->unk1C = -4;
obj->sprite_type = 1;
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
}
obj->unk2F = 0;
}
static void trigger_func_op29(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->unk2D = 1;
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
}
obj->unk2F = 0;
}
static void trigger_func_op30(struct object_t *obj) {
if (obj->floor_ypos16 < obj->ypos16 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->unk2D = 2;
}
obj->unk2F = 0;
}
static void trigger_func_op31(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj);
obj->ypos16 = obj->ypos >> 4;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->sprite_type = 1;
obj->unk1C = 1;
obj->xvelocity = 1;
}
}
static void trigger_func_op32(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj);
obj->ypos16 = obj->ypos >> 4;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->sprite_type = 1;
obj->unk2F = -3;
obj->unk2D = 0;
obj->facing_left = 1;
}
}
static void trigger_func_op33(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 2;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->unk2F = -3;
obj->unk2D = 0;
obj->facing_left = 1;
}
}
static void trigger_func_op34(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj);
obj->ypos16 = obj->ypos >> 4;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->sprite_type = 1;
obj->unk2F = 3;
obj->unk2D = 0;
obj->facing_left = 0;
}
}
static void trigger_func_op35(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 2;
obj->ypos16 = obj->ypos >> 4;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->sprite_type = 1;
obj->unk2F = 3;
obj->unk2D = 0;
obj->facing_left = 0;
}
}
static void trigger_func_op36(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj);
obj->ypos16 = obj->ypos >> 4;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->sprite_type = 4;
obj->unk2D = 2;
}
}
static void trigger_func_op37(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 2;
obj->ypos16 = obj->ypos >> 4;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->sprite_type = 4;
obj->unk2D = 2;
}
}
static void trigger_func_op38(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj);
obj->ypos16 = obj->ypos >> 4;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->sprite_type = 4;
obj->unk2D = 2;
}
}
static void trigger_func_op39(struct object_t *obj) {
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 2;
obj->ypos16 = obj->ypos >> 4;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->sprite_type = 4;
obj->unk2D = 2;
}
}
static void trigger_func_op40(struct object_t *obj) {
static int counter = 0;
g_vars.music_num = 0;
if (obj->yvelocity > 0 && obj->unk2B == 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) - 1;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
obj->sprite_type = 1;
obj->yfriction = 0;
} else if (obj->yvelocity < 0) {
obj->yvelocity = 0;
obj->ypos = (obj->ypos & ~15) + 15;
obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin;
obj->ypos16 = obj->ypos >> 4;
}
obj->unk2F = 0;
obj->special_anim = 21;
// if (_timer_counter != _timer_sync) {
// _timer_sync = _timer_counter;
// }
if (counter == 0) {
play_music(g_vars.music_num);
}
if (counter == 400) {
if (!g_vars.two_players_flag) {
g_vars.level_completed_flag = 1;
} else if (g_vars.objects[OBJECT_NUM_PLAYER1].flag_end_level != 0 && g_vars.objects[OBJECT_NUM_PLAYER2].flag_end_level != 0) {
g_vars.level_completed_flag = 1;
}
}
++counter;
}
void level_call_trigger_func(struct object_t *obj, int y) {
const uint8_t *p = lookup_sql(obj->xpos16, obj->ypos16 + y);
int num = p[0];
num = g_res.triggers[num].unk16;
switch (g_res.triggers[num].op_func) {
case 0:
trigger_func_op0(obj);
break;
case 1:
trigger_func_op1(obj);
break;
case 2:
trigger_func_op2(obj);
break;
case 3:
trigger_func_op3(obj);
break;
case 4:
trigger_func_op4(obj);
break;
case 5:
trigger_func_op5(obj);
break;
case 6:
trigger_func_op6(obj);
break;
case 7:
trigger_func_op7(obj);
break;
case 8:
trigger_func_op8(obj);
break;
case 9:
trigger_func_op9(obj);
break;
case 10:
trigger_func_op10(obj);
break;
case 11:
trigger_func_op11(obj);
break;
case 12:
trigger_func_op12(obj);
break;
case 13:
trigger_func_op13(obj);
break;
case 14:
trigger_func_op14(obj);
break;
case 15:
trigger_func_op15(obj);
break;
case 16:
trigger_func_op16(obj);
break;
case 17:
trigger_func_op17(obj);
break;
case 18:
trigger_func_op18(obj);
break;
case 19:
trigger_func_op19(obj);
break;
case 20:
trigger_func_op20(obj);
break;
case 21:
trigger_func_op21(obj);
break;
case 22:
trigger_func_op22(obj);
break;
case 23:
trigger_func_op23(obj);
break;
case 24:
trigger_func_op24(obj);
break;
case 25:
break;
case 27:
trigger_func_op27(obj);
break;
case 28:
trigger_func_op28(obj);
break;
case 29:
trigger_func_op29(obj);
break;
case 30:
trigger_func_op30(obj);
break;
case 31:
trigger_func_op31(obj);
break;
case 32:
trigger_func_op32(obj);
break;
case 33:
trigger_func_op33(obj);
break;
case 34:
trigger_func_op34(obj);
break;
case 35:
trigger_func_op35(obj);
break;
case 36:
trigger_func_op36(obj);
break;
case 37:
trigger_func_op37(obj);
break;
case 38:
trigger_func_op38(obj);
break;
case 39:
trigger_func_op39(obj);
break;
case 40:
trigger_func_op40(obj);
break;
default:
print_warning("level_call_trigger_func: op_func %d unimplemented", g_res.triggers[num].op_func);
break;
}
}
void triggers_unk3(struct object_t *obj) {
struct object_t *obj35 = &g_vars.objects[35];
struct object_t *obj37 = &g_vars.objects[37];
if (obj->yvelocity >= 0 && obj->special_anim != 18) {
obj->special_anim = 0;
}
int _di = 0;
const uint8_t *p = lookup_sql(obj->xpos16, obj->ypos16);
int num = p[0];
num = g_res.triggers[num].unk16;
struct trigger_t *t = &g_res.triggers[num];
if (!t->op_table3 && obj->unk2E != 0) {
p = lookup_sql(obj->xpos16, obj->ypos16 - 1);
num = p[0];
t = &g_res.triggers[num];
_di = 1;
}
if (!t->op_table3) {
return;
}
p = t->op_table3;
if (p[1] == 11) {
if (obj->unk60 == 0) {
obj->unk60 = p[9];
}
if (p[8] != 0) {
if (obj->data5F == g_vars.level + 1) { // instrument found
if (obj->flag_end_level == 0) {
play_sound(SOUND_13);
obj->flag_end_level = 1;
if (_di == 0) {
do_level_update_tile(obj->xpos16, obj->ypos16, p[2]);
} else {
do_level_update_tile(obj->xpos16, obj->ypos16 - 1, p[2]);
}
obj->special_anim = p[6];
if (obj->special_anim == 22) {
play_sound(SOUND_11);
}
if (obj->type != 0 && g_vars.two_players_flag) {
obj37 = &g_vars.objects[36];
}
if (obj37->type != 100) {
do_level_drop_grabbed_object(obj);
}
g_vars.triggers_counter = 0;
}
} else if (g_vars.triggers_counter == 0) {
g_vars.triggers_counter = 1;
}
} else {
if (_di == 0) {
do_level_update_tile(obj->xpos16, obj->ypos16, p[2]);
} else {
do_level_update_tile(obj->xpos16, obj->ypos16 - 1, p[2]);
}
if (p[3] != 0) {
play_sound(SOUND_6);
}
obj->vinyls_count += p[3];
obj->vinyls_count -= p[4];
if (obj->vinyls_count < 0) {
obj->vinyls_count = 0;
}
if (p[5] != 0) {
play_sound(SOUND_12);
if (!g_vars.two_players_flag && obj->data51 < 5) {
++obj->data51;
}
if (g_vars.two_players_flag && obj->data51 < 3) {
++obj->data51;
}
do_level_update_panel_lifes(obj);
}
obj->special_anim = p[6];
if (obj->special_anim == 21) {
play_sound(SOUND_8);
}
if (obj->data5F == 0 && p[7] != 0) { // music instrument found
if (!g_vars.two_players_flag) {
obj->data5F = p[7];
} else {
g_vars.objects[OBJECT_NUM_PLAYER1].data5F = p[7];
g_vars.objects[OBJECT_NUM_PLAYER2].data5F = p[7];
}
screen_draw_frame(g_res.spr_frames[140 + g_vars.level], 12, 16, 80 + g_vars.level * 32, -12);
g_vars.screen_draw_offset ^= 0x2000;
screen_draw_frame(g_res.spr_frames[140 + g_vars.level], 12, 16, 80 + g_vars.level * 32, -12);
g_vars.screen_draw_offset ^= 0x2000;
g_vars.found_music_instrument_flag = 1;
g_vars.triggers_counter = 0;
play_sound(SOUND_12);
}
}
} else if (p[1] == 12) {
if (obj->type != 0 && g_vars.two_players_flag) {
obj35 = &g_vars.objects[34];
}
obj35->type = 2;
obj35->visible_flag = 1;
obj35->facing_left = 0;
obj35->grab_type = p[9];
if (obj->facing_left != 0) {
obj35->xpos = obj->xpos;
obj35->screen_xpos = obj->screen_xpos;
} else {
obj35->xpos = obj->xpos;
obj35->screen_xpos = obj->screen_xpos;
}
obj35->ypos = obj->ypos - 18;
obj35->screen_ypos = obj->screen_ypos - 18;
obj35->xpos16 = obj->xpos16;
obj35->ypos16 = obj->ypos16;
obj35->anim_num = 1;
obj35->animframes_ptr = animframes_059d + obj35->type * 116 / 4;
// seg003:12A3
if (_di == 0) {
do_level_update_tile(obj->xpos16, obj->ypos16, p[2]);
} else {
do_level_update_tile(obj->xpos16, obj->ypos16 - 1, p[2]);
}
if (p[3] != 0) {
play_sound(SOUND_7);
obj->vinyls_count += p[3];
} else if (p[4] != 0) {
play_sound(SOUND_8);
obj->vinyls_count -= p[4];
}
if (obj->vinyls_count < 0) {
obj->vinyls_count = 0;
}
if (p[5] != 0) {
play_sound(SOUND_12);
if (!g_vars.two_players_flag && obj->data51 < 5) {
++obj->data51;
}
do_level_update_panel_lifes(obj);
}
obj->special_anim = p[6];
if (p[10] != 0) {
play_sound(SOUND_12);
++obj->lifes_count;
if (!g_vars.two_players_flag) {
screen_draw_number(obj->lifes_count - 1, 64, 163, 2);
g_vars.screen_draw_offset ^= 0x2000;
screen_draw_number(obj->lifes_count - 1, 64, 163, 2);
g_vars.screen_draw_offset ^= 0x2000;
} else if (obj->type == 0) {
screen_draw_number(obj->lifes_count - 1, 48, 163, 2);
g_vars.screen_draw_offset ^= 0x2000;
screen_draw_number(obj->lifes_count - 1, 48, 163, 2);
g_vars.screen_draw_offset ^= 0x2000;
} else {
screen_draw_number(obj->lifes_count - 1, 216, 163, 2);
g_vars.screen_draw_offset ^= 0x2000;
screen_draw_number(obj->lifes_count - 1, 216, 163, 2);
g_vars.screen_draw_offset ^= 0x2000;
}
}
} else {
obj->unk4D = p[0];
}
}
int16_t triggers_get_dy(struct object_t *obj) {
const uint8_t *p = lookup_sql(obj->xpos16, obj->ypos16);
const int num = p[0];
return g_res.triggers[num].op_table1[obj->xpos & 15] - 1;
}
void triggers_unk5(struct object_t *obj) {
int offset = 2;
int _si = offset;
const uint8_t *p = obj->trigger3;
if (p[1] < 10) {
int count = obj->unk4D - 1;
_si += (p[1] << 2) * count;
while ((p[1] << 2) > offset) {
do_level_update_tile(p[_si], p[_si + 1], p[_si + 2]);
offset += 4;
_si += 4;
}
} else if (!(g_options.cheats & CHEATS_NO_HIT)) {
obj->yfriction = p[_si];
obj->yvelocity = p[_si + 1];
obj->special_anim = p[_si + 2];
if (obj->special_anim == 22) {
play_sound(SOUND_11);
}
obj->anim_num = 1;
obj->unk2F = 0;
obj->xvelocity = p[3] - 100;
obj->xmaxvelocity = ABS(obj->xvelocity);
do_level_player_hit(obj);
}
obj->unk4D = 0;
}

60
unpack.c Normal file
View File

@ -0,0 +1,60 @@
#include "fileio.h"
#include "unpack.h"
#include "util.h"
struct unpack_t {
uint8_t dict_buf[0x200 * 2];
uint8_t rd[0x1000];
int size;
int dict_len;
};
static struct unpack_t g_unpack;
int unpack(const char *filename, uint8_t *dst) {
const int f = fio_open(filename, 1);
fio_read(f, g_unpack.rd, 6);
g_unpack.size = (READ_LE_UINT16(g_unpack.rd) << 16) + READ_LE_UINT16(g_unpack.rd + 2);
const int dict_len = READ_LE_UINT16(g_unpack.rd + 4);
print_debug(DBG_UNPACK, "unpack '%s' size %d dict_len %d", filename, g_unpack.size, dict_len);
fio_read(f, g_unpack.dict_buf, dict_len);
const uint8_t *src = g_unpack.rd;
int len = 1;
int bytes_count = 2;
uint16_t bits = 0;
uint16_t val = 0;
while (1) {
--len;
if (len == 0) {
bytes_count -= 2;
if (bytes_count == 0) {
bytes_count = fio_read(f, g_unpack.rd, 0x1000);
if (bytes_count == 0) {
break;
}
bytes_count += (bytes_count & 1);
src = g_unpack.rd;
}
bits = READ_BE_UINT16(src); src += 2;
len = 17;
continue;
}
const int carry = (bits & 0x8000) != 0;
bits <<= 1;
if (carry) {
val += 2;
}
assert(val < 0x400);
val = READ_LE_UINT16(g_unpack.dict_buf + val);
if ((val & 0x8000) == 0) {
continue;
}
*dst++ = val & 255;
val = 0;
}
fio_close(f);
return g_unpack.size;
}

9
unpack.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef UNPACK_H__
#define UNPACK_H__
#include "intern.h"
extern int unpack(const char *filename, uint8_t *dst);
#endif /* UNPACK_H__ */

60
util.c Normal file
View File

@ -0,0 +1,60 @@
#include <stdarg.h>
#include "util.h"
int g_debug_mask = 0;
void string_lower(char *p) {
for (; *p; ++p) {
if (*p >= 'A' && *p <= 'Z') {
*p += 'a' - 'A';
}
}
}
void string_upper(char *p) {
for (; *p; ++p) {
if (*p >= 'a' && *p <= 'z') {
*p += 'A' - 'a';
}
}
}
void print_debug(int debug_channel, const char *msg, ...) {
if (g_debug_mask & debug_channel) {
char buf[256];
va_list va;
va_start(va, msg);
vsprintf(buf, msg, va);
va_end(va);
fprintf(stdout, "%s\n", buf);
}
}
void print_warning(const char *msg, ...) {
char buf[256];
va_list va;
va_start(va, msg);
vsprintf(buf, msg, va);
va_end(va);
fprintf(stderr, "WARNING: %s\n", buf);
}
void print_error(const char *msg, ...) {
char buf[256];
va_list va;
va_start(va, msg);
vsprintf(buf, msg, va);
va_end(va);
fprintf(stderr, "ERROR: %s!\n", buf);
exit(-1);
}
void print_info(const char *msg, ...) {
char buf[256];
va_list va;
va_start(va, msg);
vsprintf(buf, msg, va);
va_end(va);
fprintf(stdout, "%s\n", buf);
}

24
util.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef UTIL_H__
#define UTIL_H__
#include "intern.h"
#define DBG_GAME (1 << 0)
#define DBG_FILEIO (1 << 1)
#define DBG_RESOURCE (1 << 2)
#define DBG_MIXER (1 << 3)
#define DBG_SYSTEM (1 << 4)
#define DBG_UNPACK (1 << 5)
#define DBG_SCREEN (1 << 6)
extern int g_debug_mask;
extern void string_lower(char *p);
extern void string_upper(char *p);
extern void print_debug(int debug_channel, const char *msg, ...);
extern void print_warning(const char *msg, ...);
extern void print_error(const char *msg, ...);
extern void print_info(const char *msg, ...);
#endif /* UTIL_H__ */