From 4a8368abd71c718621838773eed914b5bbd4e1d8 Mon Sep 17 00:00:00 2001 From: Holger Schemel Date: Tue, 9 Mar 2010 15:16:26 +0100 Subject: [PATCH] rnd-20100309-1-src * added (hidden) function to save native Supaplex levels with tape as native *.sp file containing level with demo (saved with a file name similar to native R'n'D levels, but with ".sp" extension instead of ".level"); to use this functionality, enter ":save-native-level" or ":snl" from the main menu with the native Supaplex level loaded and the appropriate tape loaded to the tape recorder * added updating of game values on the panel to Supaplex game engine --- ChangeLog | 11 +++++++ src/conftime.h | 2 +- src/events.c | 5 ++++ src/files.c | 66 ++++++++++++++++++++++++++++++++++++++--- src/files.h | 1 + src/game_sp/export.h | 1 + src/game_sp/file.c | 70 ++++++++++++++++++++++++++++++++++++++++++-- src/libgame/misc.c | 11 +++++++ src/libgame/misc.h | 2 ++ src/libgame/setup.c | 10 +++++-- src/tape.c | 16 ++++++---- src/tape.h | 3 ++ 12 files changed, 182 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7ce8c682..0d4ad4f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2010-03-09 + * added (hidden) function to save native Supaplex levels with tape as + native *.sp file containing level with demo (saved with a file name + similar to native R'n'D levels, but with ".sp" extension instead of + ".level"); to use this functionality, enter ":save-native-level" or + ":snl" from the main menu with the native Supaplex level loaded and + the appropriate tape loaded to the tape recorder + +2010-03-03 + * added updating of game values on the panel to Supaplex game engine + 2010-02-23 * finished integrating R'n'D graphics engine into Supaplex game engine (although some animations do not support full customizability yet) diff --git a/src/conftime.h b/src/conftime.h index 58e80c90..303bcbd0 100644 --- a/src/conftime.h +++ b/src/conftime.h @@ -1 +1 @@ -#define COMPILE_DATE_STRING "2010-03-02 23:49" +#define COMPILE_DATE_STRING "2010-03-09 14:59" diff --git a/src/events.c b/src/events.c index 278704e6..2d9cbfcd 100644 --- a/src/events.c +++ b/src/events.c @@ -577,6 +577,11 @@ static void HandleKeysSpecial(Key key) { DumpTape(&tape); } + else if (is_string_suffix(cheat_input, ":save-native-level") || + is_string_suffix(cheat_input, ":snl")) + { + SaveNativeLevel(&level); + } } else if (game_status == GAME_MODE_PLAYING) { diff --git a/src/files.c b/src/files.c index 01c1b1ac..6304da1f 100644 --- a/src/files.c +++ b/src/files.c @@ -1877,18 +1877,23 @@ static boolean checkForPackageFromBasename(char *basename) return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN); } -static char *getSingleLevelBasename(int nr) +static char *getSingleLevelBasenameExt(int nr, char *extension) { static char basename[MAX_FILENAME_LEN]; if (nr < 0) - sprintf(basename, "template.%s", LEVELFILE_EXTENSION); + sprintf(basename, "template.%s", extension); else - sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION); + sprintf(basename, "%03d.%s", nr, extension); return basename; } +static char *getSingleLevelBasename(int nr) +{ + return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION); +} + static char *getPackedLevelBasename(int type) { static char basename[MAX_FILENAME_LEN]; @@ -4469,22 +4474,61 @@ void CopyNativeLevel_SP_to_RND(struct LevelInfo *level) level->yamyam_content[i].e[x][y] = EL_EMPTY; } +static void CopyNativeTape_RND_to_SP(struct LevelInfo *level) +{ + struct LevelInfo_SP *level_sp = level->native_sp_level; + struct DemoInfo_SP *demo = &level_sp->demo; + int i, j; + + /* always start with reliable default values */ + demo->is_available = FALSE; + demo->length = 0; + + if (TAPE_IS_EMPTY(tape)) + return; + + demo->level_nr = tape.level_nr; /* (currently not used) */ + + level_sp->header.DemoRandomSeed = tape.random_seed; + + demo->length = 0; + for (i = 0; i < tape.length; i++) + { + int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]); + int demo_repeat = tape.pos[i].delay; + + for (j = 0; j < demo_repeat / 16; j++) + demo->data[demo->length++] = 0xf0 | demo_action; + + if (demo_repeat % 16) + demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action; + } + + demo->data[demo->length++] = 0xff; + + demo->is_available = TRUE; +} + static void setTapeInfoToDefaults(); static void CopyNativeTape_SP_to_RND(struct LevelInfo *level) { struct LevelInfo_SP *level_sp = level->native_sp_level; struct DemoInfo_SP *demo = &level_sp->demo; + char *filename = level->file_info.filename; int i; /* always start with reliable default values */ setTapeInfoToDefaults(); + if (!demo->is_available) + return; + tape.level_nr = demo->level_nr; /* (currently not used) */ tape.length = demo->length - 1; /* without "end of demo" byte */ tape.random_seed = level_sp->header.DemoRandomSeed; - // tape.date = + TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename)); for (i = 0; i < demo->length - 1; i++) { @@ -6854,6 +6898,20 @@ void CopyNativeLevel_Native_to_RND(struct LevelInfo *level) CopyNativeLevel_SP_to_RND(level); } +void SaveNativeLevel(struct LevelInfo *level) +{ + if (level->game_engine_type == GAME_ENGINE_TYPE_SP) + { + char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp"); + char *filename = getLevelFilenameFromBasename(basename); + + CopyNativeLevel_RND_to_SP(level); + CopyNativeTape_RND_to_SP(level); + + SaveNativeLevel_SP(filename); + } +} + /* ------------------------------------------------------------------------- */ /* functions for loading generic level */ diff --git a/src/files.h b/src/files.h index 75dc3460..9edda322 100644 --- a/src/files.h +++ b/src/files.h @@ -38,6 +38,7 @@ void LoadLevel(int); void LoadLevelTemplate(int); void SaveLevel(int); void SaveLevelTemplate(); +void SaveNativeLevel(struct LevelInfo *); void DumpLevel(struct LevelInfo *); boolean SaveLevelChecked(int); diff --git a/src/game_sp/export.h b/src/game_sp/export.h index 8abe983b..9e374dc5 100644 --- a/src/game_sp/export.h +++ b/src/game_sp/export.h @@ -179,6 +179,7 @@ extern unsigned int InitEngineRandom_SP(long); extern void setLevelInfoToDefaults_SP(); extern void copyInternalEngineVars_SP(); extern boolean LoadNativeLevel_SP(char *, int); +extern void SaveNativeLevel_SP(char *); extern void BackToFront_SP(void); extern void BlitScreenToBitmap_SP(Bitmap *); diff --git a/src/game_sp/file.c b/src/game_sp/file.c index 26f74167..f7b833c1 100644 --- a/src/game_sp/file.c +++ b/src/game_sp/file.c @@ -7,6 +7,12 @@ /* functions for loading Supaplex level */ /* ------------------------------------------------------------------------- */ +void setTapeInfoToDefaults_SP() +{ + native_sp_level.demo.is_available = FALSE; + native_sp_level.demo.length = 0; +} + void setLevelInfoToDefaults_SP() { LevelInfoType *header = &native_sp_level.header; @@ -49,8 +55,7 @@ void setLevelInfoToDefaults_SP() for (i = 0; i < SP_HEADER_SIZE; i++) native_sp_level.header_raw_bytes[i] = 0x20; - native_sp_level.demo.is_available = FALSE; - native_sp_level.demo.length = 0; + setTapeInfoToDefaults_SP(); } void copyInternalEngineVars_SP() @@ -659,3 +664,64 @@ boolean LoadNativeLevel_SP(char *filename, int level_pos) return TRUE; } + +void SaveNativeLevel_SP(char *filename) +{ + LevelInfoType *header = &native_sp_level.header; + FILE *file; + int i, x, y; + + if (!(file = fopen(filename, MODE_WRITE))) + { + Error(ERR_WARN, "cannot save native level file '%s'", filename); + + return; + } + + /* write level playfield (width * height == 60 * 24 tiles == 1440 bytes) */ + for (y = 0; y < native_sp_level.height; y++) + for (x = 0; x < native_sp_level.width; x++) + putFile8Bit(file, native_sp_level.playfield[x][y]); + + /* write level header (96 bytes) */ + + WriteUnusedBytesToFile(file, 4); + + putFile8Bit(file, header->InitialGravity); + putFile8Bit(file, header->Version); + + for (i = 0; i < SP_LEVEL_NAME_LEN; i++) + putFile8Bit(file, header->LevelTitle[i]); + + putFile8Bit(file, header->InitialFreezeZonks); + putFile8Bit(file, header->InfotronsNeeded); + putFile8Bit(file, header->SpecialPortCount); + + for (i = 0; i < SP_MAX_SPECIAL_PORTS; i++) + { + SpecialPortType *port = &header->SpecialPort[i]; + + putFile16BitBE(file, port->PortLocation); + putFile8Bit(file, port->Gravity); + putFile8Bit(file, port->FreezeZonks); + putFile8Bit(file, port->FreezeEnemies); + + WriteUnusedBytesToFile(file, 1); + } + + putFile8Bit(file, header->SpeedByte); + putFile8Bit(file, header->CheckSumByte); + putFile16BitLE(file, header->DemoRandomSeed); + + /* also save demo tape, if available */ + + if (native_sp_level.demo.is_available) + { + putFile8Bit(file, native_sp_level.demo.level_nr); + + for (i = 0; i < native_sp_level.demo.length; i++) + putFile8Bit(file, native_sp_level.demo.data[i]); + } + + fclose(file); +} diff --git a/src/libgame/misc.c b/src/libgame/misc.c index d4ebcda7..27cf37c6 100644 --- a/src/libgame/misc.c +++ b/src/libgame/misc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -468,6 +469,16 @@ char *getRealName() return real_name; } +time_t getFileTimestampEpochSeconds(char *filename) +{ + struct stat file_status; + + if (stat(filename, &file_status) != 0) /* cannot stat file */ + return 0; + + return file_status.st_mtime; +} + /* ------------------------------------------------------------------------- */ /* path manipulation functions */ diff --git a/src/libgame/misc.h b/src/libgame/misc.h index 6ac3d994..34499610 100644 --- a/src/libgame/misc.h +++ b/src/libgame/misc.h @@ -92,6 +92,8 @@ unsigned int get_random_number(int, int); char *getLoginName(void); char *getRealName(void); +time_t getFileTimestampEpochSeconds(char *); + char *getBasePath(char *); char *getBaseName(char *); char *getBaseNamePtr(char *); diff --git a/src/libgame/setup.c b/src/libgame/setup.c index 9e35b4db..dbe124df 100644 --- a/src/libgame/setup.c +++ b/src/libgame/setup.c @@ -2850,14 +2850,18 @@ static char *getCacheToken(char *prefix, char *suffix) return token; } -static char *getFileTimestamp(char *filename) +static char *getFileTimestampString(char *filename) { +#if 1 + return getStringCopy(i_to_a(getFileTimestampEpochSeconds(filename))); +#else struct stat file_status; if (stat(filename, &file_status) != 0) /* cannot stat file */ return getStringCopy(i_to_a(0)); return getStringCopy(i_to_a(file_status.st_mtime)); +#endif } static boolean modifiedFileTimestamp(char *filename, char *timestamp_string) @@ -2974,8 +2978,8 @@ static void setArtworkInfoCacheEntry(TreeInfo *artwork_info, LEVELINFO_FILENAME); char *filename_artworkinfo = getPath2(getSetupArtworkDir(artwork_info), ARTWORKINFO_FILENAME(type)); - char *timestamp_levelinfo = getFileTimestamp(filename_levelinfo); - char *timestamp_artworkinfo = getFileTimestamp(filename_artworkinfo); + char *timestamp_levelinfo = getFileTimestampString(filename_levelinfo); + char *timestamp_artworkinfo = getFileTimestampString(filename_artworkinfo); token_main = getCacheToken(token_prefix, "TIMESTAMP_LEVELINFO"); setHashEntry(artworkinfo_cache_new, token_main, timestamp_levelinfo); diff --git a/src/tape.c b/src/tape.c index 49f06aa3..88f0f5a0 100644 --- a/src/tape.c +++ b/src/tape.c @@ -453,12 +453,16 @@ void TapeDeactivateDisplayOff(boolean redraw_display) /* tape control functions */ /* ========================================================================= */ -static void TapeSetDate() +void TapeSetDateFromEpochSeconds(time_t epoch_seconds) { - time_t epoch_seconds = time(NULL); - struct tm *now = localtime(&epoch_seconds); + struct tm *lt = localtime(&epoch_seconds); - tape.date = 10000 * (now->tm_year % 100) + 100 * now->tm_mon + now->tm_mday; + tape.date = 10000 * (lt->tm_year % 100) + 100 * lt->tm_mon + lt->tm_mday; +} + +void TapeSetDateFromNow() +{ + TapeSetDateFromEpochSeconds(time(NULL)); } void TapeErase() @@ -481,7 +485,7 @@ void TapeErase() tape.game_version = GAME_VERSION_ACTUAL; tape.engine_version = level.game_version; - TapeSetDate(); + TapeSetDateFromNow(); for (i = 0; i < MAX_PLAYERS; i++) tape.player_participates[i] = FALSE; @@ -562,7 +566,7 @@ static void TapeAppendRecording() tape.recording = TRUE; tape.changed = TRUE; - TapeSetDate(); + TapeSetDateFromNow(); DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date); DrawVideoDisplay(VIDEO_STATE_PLAY_OFF | VIDEO_STATE_REC_ON, 0); diff --git a/src/tape.h b/src/tape.h index fe636495..5243f268 100644 --- a/src/tape.h +++ b/src/tape.h @@ -138,6 +138,9 @@ void DrawCompleteVideoDisplay(void); void TapeDeactivateDisplayOn(); void TapeDeactivateDisplayOff(boolean); +void TapeSetDateFromEpochSeconds(time_t); +void TapeSetDateFromNow(); + void TapeStartRecording(long); void TapeHaltRecording(void); void TapeStopRecording(void); -- 2.34.1