Initial commit of Let's Make Dangerous Dave. Utility finished. Initial game code.

This commit is contained in:
Josh K 2018-07-05 15:24:47 -04:00
commit 619aa4ba82
6 changed files with 554 additions and 0 deletions

82
Makefile Normal file
View File

@ -0,0 +1,82 @@
# Detect OS
ifeq ($(OS),Windows_NT)
ISWIN := 1
endif
ifdef ISWIN
CC := i686-w64-mingw32-gcc
else
CC := gcc
endif
RM ?= rm -f
RMDIR ?= rm -rf
MKDIR ?= mkdir -p
WARFLAGS := -Wall #-Wextra -pedantic
CFLAGS_g := $(WARFLAGS) -std=gnu11 -O2 -msse2 -ffast-math -mfpmath=sse -DNDEBUG \
-MMD
LDFLAGS_g := -s
# object output dir
OBJDIR_g := obj
SRCS := $(wildcard *.c)
OBJS_t := $(SRCS:.c=.o)
# include dirs
CFLAGS_g +=
# libs
ifdef ISWIN
LDFLAGS_g +=
else
CFLAGS_g += -I/usr/include/SDL2
LDFLAGS_g += -lSDL2
endif
# binary target
ifdef ISWIN
TARG_t := lmdave.exe
else
TARG_t := lmdave
endif
game: $(TARG_t)
@echo "*** Target '$@': '$^' is up to date!"
default: game
.PHONY: default clean game
# rewrite OBJS so it outputs object files to seperate dir
OBJS_t := $(patsubst %,$(OBJDIR_g)/%,$(OBJS_t))
# include the dependency rules generated by -MMD
-include $(OBJS_t:.o=.d)
clean:
@echo "*** Removing target binary and object directory..."
@$(RM) $(TARG_t)
@$(RMDIR) $(OBJDIR_g)
@echo "*** Done."
# Compile
$(OBJDIR_g)/%.o: %.c
@$(MKDIR) $(@D)
@echo "*** Compiling '$<' ..."
@$(CC) $(CFLAGS_g) -c $< -o $@
# Link
$(TARG_t): $(OBJS_t)
@$(MKDIR) $(@D)
@echo "\n*** Linking binary target '$@' ...\n"
@$(CC) $(OBJS_t) -o $@ $(LDFLAGS_g)

54
lmdave.c Normal file
View File

@ -0,0 +1,54 @@
#include "lmdave.h"
// global game state
game_state_t* gs = NULL;
// global game assets
game_assets_t* g_assets = NULL;
// initialize a new game state
void init_game() {
gs->quit = 1;
gs->current_level = 1;
}
// initialize assets
void init_assets( SDL_Renderer* r ) {
}
// poll input
void check_input() {
}
// update game logic
void update_game() {
}
// draw to renderer
void render( SDL_Renderer* r ) {
}
// rendering scale
const uint8_t R_SCALE = 3;
int main( int argc, char** argv ) {
gs = malloc( sizeof(game_state_t) );
init_game();
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_CreateWindowAndRenderer( 320 * R_SCALE, 200 * R_SCALE, 0, &window, &renderer );
g_assets = malloc( sizeof(game_assets_t) );
init_assets( renderer );
// main loop
while ( !gs->quit ) {
check_input();
update_game();
render( renderer );
}
free( g_assets );
free( gs );
}

32
lmdave.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef _LMDAVE_H
#define _LMDAVE_H
#include <SDL.h>
// level structure
// byte[256] path, two signed 8bit relative movement, 0xea 0xea for end
// byte[100x10] tile index data, 100 by 10, so more than one level could fit in a chunk
// byte[24] unsed padding
// note: player start and monster starts are hardcoded
typedef struct {
int8_t path[256];
uint8_t tiles[1000];
uint8_t pad[24];
} level_t;
// global game state
typedef struct {
uint8_t quit;
uint8_t current_level;
uint8_t view_x, view_y;
level_t levels[10];
} game_state_t;
// game assets
typedef struct {
SDL_Texture* tile_tx[158];
} game_assets_t;
#endif

82
util/Makefile Normal file
View File

@ -0,0 +1,82 @@
# Detect OS
ifeq ($(OS),Windows_NT)
ISWIN := 1
endif
ifdef ISWIN
CC := i686-w64-mingw32-gcc
else
CC := gcc
endif
RM ?= rm -f
RMDIR ?= rm -rf
MKDIR ?= mkdir -p
WARFLAGS := -Wall #-Wextra -pedantic
CFLAGS_g := $(WARFLAGS) -std=gnu11 -O2 -msse2 -ffast-math -mfpmath=sse -DNDEBUG \
-MMD
LDFLAGS_g := -s
# object output dir
OBJDIR_g := obj
#SRCS := $(wildcard *.c)
OBJS_t := tiles.o levels.o
# include dirs
CFLAGS_g +=
# libs
ifdef ISWIN
LDFLAGS_g +=
else
CFLAGS_g += -I/usr/include/SDL2
LDFLAGS_g += -lSDL2
endif
# binary target
ifdef ISWIN
TARG_t := tiles.exe
else
TARG_t := tiles
endif
utils: $(TARG_t)
@echo "*** Target '$@': '$^' is up to date!"
default: utils
.PHONY: default clean utils
# rewrite OBJS so it outputs object files to seperate dir
OBJS_t := $(patsubst %,$(OBJDIR_g)/%,$(OBJS_t))
# include the dependency rules generated by -MMD
-include $(OBJS_t:.o=.d)
clean:
@echo "*** Removing target binary and object directory..."
@$(RM) $(TARG_t)
@$(RMDIR) $(OBJDIR_g)
@echo "*** Done."
# Compile
$(OBJDIR_g)/%.o: %.c
@$(MKDIR) $(@D)
@echo "*** Compiling '$<' ..."
@$(CC) $(CFLAGS_g) -c $< -o $@
# Link
$(TARG_t): $(OBJS_t)
@$(MKDIR) $(@D)
@echo "\n*** Linking binary target '$@' ...\n"
@$(CC) $(OBJS_t) -o $@ $(LDFLAGS_g)

121
util/levels.c Normal file
View File

@ -0,0 +1,121 @@
// extract level data from uncompressed dave.exe
// named as levelxx.dat
#include <stdio.h>
#include <SDL.h>
//extern SDL_RWops* ddexe;
extern SDL_Surface* tile_sfc[];
// level structure
// byte[256] path, two signed 8bit relative movement, 0xea 0xea for end
// byte[100x10] tile index data, 100 by 10, so more than one level could fit in a chunk
// byte[24] unsed padding
// note: player start and monster starts are hardcoded
typedef struct {
int8_t path[256];
uint8_t tiles[1000];
uint8_t pad[24];
} level_t;
// all levels in the exe
level_t levels[10];
// export all levels to seperate .dat file
void SaveLevels() {
for ( int l = 0; l < 10; ++l ) {
char fname[1024];
snprintf( fname, 1024, "../levels/level%02u.dat", l );
printf( "Saving level %u to '%s'\n", l, fname );
SDL_RWops* lvlfile = SDL_RWFromFile( fname, "wb" );
// write path data
for ( int p = 0; p < 256; ++p ) {
SDL_RWwrite( lvlfile, &levels[l].path[p], 1, 1 );
}
// write tile data
for ( int t = 0; t < 1000; ++t ) {
SDL_RWwrite( lvlfile, &levels[l].tiles[t], 1, 1 );
}
// padding
SDL_RWwrite( lvlfile, levels[l].pad, 24, 1 );
SDL_RWclose( lvlfile );
}
}
// fill global level array with dat from exe
void LoadLevels() {
const uint32_t lvl_dat_addr = 0x26e0a;
SDL_RWops* ddexe = SDL_RWFromFile( "../DAVE.EXE", "rb" );
if ( ddexe == NULL ) { fprintf( stderr, "Error opening DAVE.EXE for levels.\n" ); return; }
// 10 levels @ 1280 bytes each
// 16x16 tiles, 320x160px (20x10 tiles) view
// off edges will wrap
// seek to start of data
SDL_RWseek( ddexe, lvl_dat_addr, RW_SEEK_SET );
memset( levels, 0, sizeof(levels) );
// read each level into array
for ( int l = 0; l < 10; ++l ) {
// read path data
for ( int p = 0; p < 256; ++p ) {
SDL_RWread( ddexe, &levels[l].path[p], 1, 1 );
}
// read tile data
for ( int t = 0; t < 1000; ++t ) {
SDL_RWread( ddexe, &levels[l].tiles[t], 1, 1 );
}
// padding
SDL_RWread( ddexe, levels[l].pad, 24, 1 );
}
// close exe
SDL_RWclose( ddexe );
}
// create a large world map image with all levels
void CreateWorldMap() {
// create big empty surface for containing entire world map
SDL_Surface* map = SDL_CreateRGBSurface( 0, 1600, 1600, 32, 0, 0, 0, 0 );
// level, row, column
for ( int l = 0; l < 10; ++l ) {
for ( int y = 0; y < 10; ++y ) {
for ( int x = 0; x < 100; ++x ) {
uint8_t til = levels[l].tiles[y * 100 + x];
SDL_Rect dst;
dst.x = x * 16;
dst.y = l * 160 + y * 16;
dst.w = 16; dst.h = 16;
SDL_BlitSurface( tile_sfc[til], NULL, map, &dst );
// hardcoded player and monster starts
if ( (l == 0) && ((x == 2 && y == 8) || (x == 20 && y == 0)) ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
if ( (l == 1) && ((x == 1 && y == 8) || (x == 51 && y == 0)) ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
if ( l == 2 && x == 2 && y == 5 ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
// l2 monsters
if ( l == 2 )
if ( (x == 44 || x == 59) && y == 4 ) {
SDL_BlitSurface( tile_sfc[89], NULL, map, &dst );
}
if ( l == 3 && x == 1 && y == 5 ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
if ( l == 4 && x == 2 && y == 8 ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
if ( (l == 5) && ((x == 2 && y == 8) || (x == 71 && y == 0)) ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
if ( (l == 6) && ((x == 1 && y == 2) || (x == 80 && y == 0)) ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
if ( l == 7 && x == 2 && y == 8 ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
if ( l == 8 && x == 6 && y == 1 ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
if ( l == 9 && x == 2 && y == 8 ) { SDL_BlitSurface( tile_sfc[53], NULL, map, &dst ); }
}
}
}
// save map file
SDL_SaveBMP( map, "./map.bmp" );
// free world map
SDL_FreeSurface( map );
}

183
util/tiles.c Normal file
View File

@ -0,0 +1,183 @@
// extract tilesxxx.bmp from uncompressed DAVE.EXE
#include <stdint.h>
#include <inttypes.h>
#include <SDL.h>
// all tiles in the exe
SDL_Surface* tile_sfc[158];
//SDL_Texture* tile_tx[158];
// level info
void LoadLevels();
void SaveLevels();
void CreateWorldMap();
// export all tiles to bmp
void SaveTiles() {
// Save out the all tile surfaces
for ( int curtil = 0; curtil < 158; ++curtil ) {
SDL_Surface* sfc = tile_sfc[curtil];
printf( "Saving tile%03d.bmp (%ux%u)...\n", curtil, sfc->w, sfc->h );
char fname[1024];
snprintf( fname, 1024, "../tiles/tile%03d.bmp", curtil );
SDL_SaveBMP( sfc, fname );
}
}
// convert all loaded tile surfaces to textures
/*void ConvertTiles( SDL_Renderer* r ) {
for ( int i = 0; i < 158; ++i ) {
tile_tx[i] = SDL_CreateTextureFromSurface( r, tile_sfc[i] );
SDL_FreeSurface( tile_sfc[i] );
}
}*/
// fill global tile array with tiles from exe
void LoadTiles() {
const uint32_t vga_data_addr = 0x120f0;
const uint32_t vga_pal_addr = 0x26b0a;
// exe assumed uncompressed
SDL_RWops* ddexe = SDL_RWFromFile( "../DAVE.EXE", "rb" );
if ( ddexe != NULL ) printf( "SUCCESS! (%"PRIi64" bytes)\n", SDL_RWsize( ddexe ) );
// tileset
SDL_RWseek( ddexe, vga_data_addr, RW_SEEK_SET );
uint32_t tssz;
SDL_RWread( ddexe, &tssz, 4, 1 );
printf( "Uncompressed tileset size: %u\n", tssz );
uint8_t* tsdat = malloc( tssz );
memset( tsdat, 0, tssz );
uint8_t bytebuf = 0;
uint8_t* tsbyte = tsdat; // current output byte to be written to
// uncompress RLE
while ( (tsbyte - tsdat) < tssz ) {
SDL_RWread( ddexe, &bytebuf, 1, 1 );
if ( bytebuf & 0x80 ) { // unchanged
uint32_t cnt = ((bytebuf & 0x7f) + 1);
while ( cnt-- ) {
SDL_RWread( ddexe, &bytebuf, 1, 1 );
*tsbyte = bytebuf; tsbyte++;
}
} else { // repeated
uint32_t cnt = bytebuf + 3;
SDL_RWread( ddexe, &bytebuf, 1, 1 ); // byte to be repeated
while ( cnt-- ) {
*tsbyte = bytebuf; tsbyte++;
}
}
}
// palette
SDL_RWseek( ddexe, vga_pal_addr, RW_SEEK_SET );
uint8_t paldat[768];
memset( paldat, 0, 768 );
// reach each color and convert from 6bit to 8bit
for ( int i = 0; i < 256; ++i ) {
SDL_RWread( ddexe, paldat + (i * 3), 3, 1 );
paldat[i*3] <<= 2;
paldat[i*3+1] <<= 2;
paldat[i*3+2] <<= 2;
}
// note: extra byte every 65536 bytes
// total tile count
SDL_RWops* tsfil = SDL_RWFromMem( tsdat, tssz );
uint32_t tscnt;
SDL_RWread( tsfil, &tscnt, 4, 1 );
printf( "Tile count: %u\n", tscnt );
// tile offsets
uint32_t tsoffs[tscnt+1];
for ( int i = 0; i < tscnt; ++i ) {
SDL_RWread( tsfil, &tsoffs[i], 4, 1 );
//printf( "ts%u: %u -- ", i, tsoffs[i] );
}
// last tile ends at EOF
tsoffs[tscnt] = tssz;
uint16_t tw, th;
uint32_t curbyte;
uint8_t curtil;
// read each tile
for ( curtil = 0; curtil < tscnt; curtil++ ) {
//curtilbyte = 0;
curbyte = tsoffs[curtil];
tw = 16; th = 16;
// skip extra byte
if ( curbyte > 65280 )
curbyte++;
// curbyte is now actual offset to raw image data
SDL_RWseek( tsfil, curbyte, RW_SEEK_SET );
// check if dimension thats not 16x16, assumed if current chunk size is not 256
if ( tsoffs[curtil+1] - tsoffs[curtil] != 256 ) {
SDL_RWread( tsfil, &tw, 2, 1 );
SDL_RWread( tsfil, &th, 2, 1 );
curbyte += 4;
}
// fill a new 32bpp surface using palette
SDL_Surface* tilsfc = SDL_CreateRGBSurface( 0, tw, th, 32, 0,0,0,0 );//SDL_PIXELFORMAT_RGBA32 );
SDL_LockSurface( tilsfc );
uint8_t* pxp = (uint8_t*)(tilsfc->pixels);
for ( int p = 0; p < tw * th; ++p ) {
uint8_t ix;
SDL_RWread( tsfil, &ix, 1, 1 );
size_t pos = p * 4;
// bgra
pxp[pos] = paldat[ix*3+2];
pxp[pos+1] = paldat[ix*3+1];
pxp[pos+2] = paldat[ix*3];
pxp[pos+3] = 255;//(ix == 0) ? 0 : 255;
}
SDL_UnlockSurface( tilsfc );
// store surface in global tile array
tile_sfc[curtil] = tilsfc;
//SDL_FreeSurface( tilsfc );
//printf( "ts%u (%ux%u) @ %u -- ", curtil, tw, th, curbyte );
}
SDL_RWclose( tsfil );
free( tsdat );
// done with exe
SDL_RWclose( ddexe );
}
// free all loaded exe's tile surfaces
void FreeTileSurfaces() {
for ( int i = 0; i < 158; ++i ) {
SDL_FreeSurface( tile_sfc[i] );
}
}
int main( int argc, char** argv ) {
// high scores
SDL_RWops* dscor = SDL_RWFromFile( "../DSCORES.DAV", "rb" );
uint8_t lscor[9];
while ( SDL_RWread( dscor, lscor, 9, 1 ) ) {
printf( "LVL %u - SCORE: %u%u%u%u%u - NAM: %.3s\n", lscor[0],
lscor[1], lscor[2], lscor[3], lscor[4], lscor[5], (char*)&lscor[6] );
}
SDL_RWclose( dscor );
LoadTiles();
//SaveTiles();
//ConvertTiles();
// LEVELS
LoadLevels();
//SaveLevels();
CreateWorldMap();
FreeTileSurfaces();
return 0;
}