diff --git a/README.md b/README.md index e072b83..e20ba4a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ cmake .. make all ``` -### Fix scripting bug in BILA_VEZ.MAP (white tower) +### Fix scripting bug in BILA_VEZ.MAP (White Tower) A script error in the White Tower map in the puzzle located on the top floor of the tower has been fixed. The repository includes a corrected file BILA_VEZ.MAP, replace the original file in the "MAPS" directory of the original installation with this file. diff --git a/game/clk_map.c b/game/clk_map.c index 286fdce..f90ea07 100644 --- a/game/clk_map.c +++ b/game/clk_map.c @@ -339,7 +339,12 @@ char clk_saveload(int id,int xa,int ya,int xr,int yr) if (cur_mode==MD_ANOTHER_MAP) unwire_proc(),wire_proc(); unwire_proc(); cancel_render=1; - wire_save_load(id); + if (id == 1) { + do_save_dialog(); + wire_proc(); + } else { + wire_save_load(id); + } return 1; } diff --git a/game/gamesave.c b/game/gamesave.c index 4e33c17..fd2ed6a 100644 --- a/game/gamesave.c +++ b/game/gamesave.c @@ -21,6 +21,7 @@ #include #include +#include #define STATE_CUR_VER 2 #define _GAME_ST "_GAME.TMP" @@ -30,7 +31,7 @@ #define GM_MAPENABLE 0x1 -#define SAVE_SLOT_S 34 +#define SAVE_SLOT_S 32 #define LOAD_SLOT_S (372+34) #define SAVE_SLOT_E (34+203) #define LOAD_SLOT_E (372+34+203) @@ -39,6 +40,7 @@ static TMPFILE_WR *story=NULL; static char load_another; +static unsigned long current_campaign = 0; char reset_mobiles=0; typedef struct s_save @@ -715,7 +717,7 @@ int load_basic_info() strcopy_n(loadlevel.name,s.level_name,sizeof(loadlevel.name)); loadlevel.start_pos=viewsector; loadlevel.dir=viewdir; - send_message(E_CLOSE_MAP); + send_message(E_CLOSE_MAP,NULL); load_another=1; } else load_another=0; @@ -747,29 +749,42 @@ static int load_global_events() return 0; } -int save_game(int slotnum,char *gamename) +int save_game(int game_time,char *gamename, char skip_if_exists) { - char *ssn,*gn; + char *gn; FILE *svf; int r; - SEND_LOG("(SAVELOAD) Saving game slot %d",slotnum); + if (current_campaign == 0) { + time_t t; + time(&t); + current_campaign = (unsigned long) t; + } + + char str_buff[50]; + snprintf(str_buff,sizeof(str_buff),"sav.%010lx.%08X", current_campaign, game_time); + + + SEND_LOG("(SAVELOAD) Saving game slot %d",game_time); save_map_state(); - const char *sn = build_pathname(2,gpathtable[SR_SAVES],_SLOT_SAV); + const char *sn = build_pathname(2,gpathtable[SR_SAVES],str_buff); + sn = local_strdup(sn); + if (skip_if_exists && check_file_exists(sn)) { + SEND_LOG("(SAVELOAD) Save skipped - already exists"); + return 0; + } create_directories(gpathtable[SR_SAVES]); - ssn=alloca(strlen(sn)+3); - sprintf(ssn,sn,slotnum); gn=alloca(SAVE_NAME_SIZE); strcopy_n(gn,gamename,SAVE_NAME_SIZE); if ((r=save_shops())!=0) return r; if ((r=save_basic_info())!=0) return r; save_book(); save_global_events(); - svf=fopen_icase(ssn,"wb"); + svf=fopen_icase(sn,"wb"); if (svf==NULL) { char buff[256]; - sprintf(buff,"Nelze ulozit pozici na cestu: %s", ssn); + sprintf(buff,"Nelze ulozit pozici na cestu: %s", sn); display_error(buff); } else @@ -787,21 +802,20 @@ int save_game(int slotnum,char *gamename) extern char running_battle; -int load_game(int slotnum) +int load_game(const char *fname) { - char *ssn; FILE *svf; int r,t; - SEND_LOG("(SAVELOAD) Loading game slot %d",slotnum); + sscanf(fname,"sav.%lx", ¤t_campaign); + + SEND_LOG("(SAVELOAD) Loading file: %s",fname); if (battle) konec_kola(); battle=0; close_story_file(); purge_temps(0); - const char *sn = build_pathname(2, gpathtable[SR_SAVES], _SLOT_SAV); - ssn=alloca(strlen(sn)+3); - sprintf(ssn,sn,slotnum); - svf=fopen_icase(ssn,"rb"); + const char *sn = build_pathname(2, gpathtable[SR_SAVES], fname); + svf=fopen_icase(sn,"rb"); if (svf==NULL) return 1; fseek(svf,SAVE_NAME_SIZE,SEEK_CUR); r=unpack_all_status(svf); @@ -854,52 +868,26 @@ static ENUM_ALL_STATUS_CALLBACK_RESULT load_specific_file_callback(FILE *f, cons } -static void load_specific_file(int slot_num,char *filename,void **out,int32_t *size) //call it in task! - { - FILE *slot; - char *d; - - const char *c = build_pathname(2, gpathtable[SR_SAVES], _SLOT_SAV); - d=alloca(strlen(c)+6); - sprintf(d,c,slot_num); - slot=fopen_icase(d,"rb"); - if (slot==NULL) - { - *out=NULL; - return; - } - fseek(slot,SAVE_NAME_SIZE,SEEK_CUR); - - LOAD_SPECIFIC_FILE_CALLBACK_DATA ctx; - ctx.name = filename; - - *out = NULL; - *size = 0; - if (enum_all_status(slot, &load_specific_file_callback, &ctx) == 0){ - *out = ctx.data; - *size = ctx.size; - } - fclose(slot); - } - //------------------------ SAVE LOAD DIALOG ---------------------------- static char force_save; -static TSTR_LIST slot_list=NULL; +//static TSTR_LIST slot_list=NULL; static int last_select=-1; -static char used_pos[SLOTS_MAX]; + static TSTR_LIST story_text=NULL; static void *back_texture=NULL; static int cur_story_pos=0; static char load_mode; #define SLOT_SPACE 33 -#define SELECT_COLOR (RGB555(31,31,31)|FONT_TSHADOW) -#define NORMAL_COLOR (RGB555(12,31,12)|FONT_TSHADOW) +#define SLOT_FONT H_FBOLD +#define SELECT_COLOR RGB555(31,31,31) +#define NORMAL_COLOR RGB555(20,31,20) #define STORY_X 57 #define STORY_Y 50 #define STORY_XS (298-57) #define STORY_YS (302-50) +#if 0 void read_slot_list() { int i; @@ -928,12 +916,159 @@ void read_slot_list() } } -static void place_name(int c,int i,char show) +#endif +typedef struct { + TSTR_LIST files; + TSTR_LIST names; + size_t count; +} TSAVEGAME_LIST; + +static TSAVEGAME_LIST current_game_slot_list = {}; + +typedef struct { + TSTR_LIST files; + const char *prefix; + size_t prefix_len; + size_t count; +} TSAVEGAME_CB_STATE; + +static char is_same_prefix(const char *name, const char *prev_name) { + const char *sep1 = strrchr(name, '.'); + const char *sep2 = strrchr(prev_name, '.'); + if (!sep1 || !sep2) return 0; + if ((sep1 - name) != (sep2 - prev_name)) return 0; + if (strncmp(name, prev_name,sep1-name) == 0) return 1; + return 0; +} + +static int get_all_savegames_callback(const char *name, LIST_FILE_TYPE type , size_t size, void *ctx) { + if (istrncmp(name, "sav.", 4) != 0) return 1; + TSAVEGAME_CB_STATE *st = (TSAVEGAME_CB_STATE *)ctx; + if (st->prefix_len == 0 || strncmp(name, st->prefix, st->prefix_len) == 0) { + str_replace(&(st->files), st->count, name); + ++st->count; + } + return 0; +} + +static int compare_strings (const void *a, const void *b) { + return strcmp(*(const char **)a, *(const char **)b); +} + +static int dedup_strings_prefix(TSTR_LIST lst, int count) { + int j = -1; + for (int i = 0; i < count; ++i) { + if (j < 0 || !is_same_prefix(lst[j], lst[i])) { + ++j; + } + if (j != i) lst[j] = lst[i]; + } + ++j; + int r = j; + while (j != count) { + lst[j] = NULL; + ++j; + } + return r; +} + + +static void load_specific_file(int slot_num,char *filename,void **out,int32_t *size) //call it in task! + { + FILE *slot; + + if (current_game_slot_list.count <= (size_t)slot_num) { + *out = NULL; + return; + } + + slot=fopen_icase(build_pathname(2, gpathtable[SR_SAVES], current_game_slot_list.files[slot_num]),"rb"); + if (slot==NULL) + { + *out=NULL; + return; + } + fseek(slot,SAVE_NAME_SIZE,SEEK_CUR); + + LOAD_SPECIFIC_FILE_CALLBACK_DATA ctx; + ctx.name = filename; + + *out = NULL; + *size = 0; + if (enum_all_status(slot, &load_specific_file_callback, &ctx) == 0){ + *out = ctx.data; + *size = ctx.size; + } + fclose(slot); + } + + +static TSAVEGAME_LIST get_all_savegames(unsigned long kampan) { + //sav.creation_time.game_save_time + char prefix[50]; + snprintf(prefix,500,"sav.%010lx.",kampan); + TSAVEGAME_CB_STATE st; + st.files = create_list(32); + st.prefix = kampan?prefix:NULL; + st.prefix_len = kampan?strlen(prefix):0; + st.count = 0; + list_files(gpathtable[SR_SAVES], file_type_just_name|file_type_normal, get_all_savegames_callback, &st); + qsort(st.files, st.count, sizeof(char *), compare_strings); + if (kampan == 0) { + st.count =dedup_strings_prefix(st.files, (int)st.count); + } + TSTR_LIST files = create_list(st.count); + for (size_t i = 0; i < st.count; ++i) { + files[st.count-i-1] = st.files[i]; + st.files[i] = NULL; + } + release_list(st.files); + + TSTR_LIST names = create_list(st.count); + for (size_t i = 0; i < st.count; ++i) { + FILE *f=fopen_icase(build_pathname(2, gpathtable[SR_SAVES], files[i]), "rb"); + if (f!=NULL) { + char slotname[SAVE_NAME_SIZE+1]; + fread(slotname,1,SAVE_NAME_SIZE,f); + slotname[SAVE_NAME_SIZE] = 0; + fclose(f); + str_replace(&names, i, slotname); + } else { + str_replace(&names, i, texty[75]); + } + } + + TSAVEGAME_LIST out; + out.files = files; + out.names = names; + out.count = st.count; + return out; +} + +static void free_savegame_list(TSAVEGAME_LIST *lst) { + release_list(lst->files); + release_list(lst->names); + lst->files = 0; + lst->names = 0; +} + +static void place_name(int c,int i,char show, char sel) { int z,x; + if ((size_t)i >= current_game_slot_list.count) return; if (c) x=SAVE_SLOT_S;else x=LOAD_SLOT_S; if (show) schovej_mysku(); - position(x,z=i*SLOT_SPACE+21+SCREEN_OFFLINE);outtext(slot_list[i]); + const char *name = current_game_slot_list.names[i]; + set_font(SLOT_FONT,sel?SELECT_COLOR:NORMAL_COLOR); + int w = text_width(name); + int spc = 0; + if (w > 210) { + int len = strlen(name); + int extra = (w-210)/len; + spc = -extra-1; + } + position(x,z=i*SLOT_SPACE+21+SCREEN_OFFLINE); + outtext_ex(name,spc); if (show) { ukaz_mysku(); @@ -947,8 +1082,9 @@ static void redraw_save() schovej_mysku(); put_picture(0,SCREEN_OFFLINE,ablock(H_SAVELOAD)); put_picture(274,SCREEN_OFFLINE,ablock(H_SVITEK)); - set_font(H_FBOLD,NORMAL_COLOR); - for(i=0;i=0 && used_pos[id]) - send_message(E_CLOSE_MAP,id); + if (ms_last_event.event_type & 0x2 && id>=0 && (size_t)id < current_game_slot_list.count) + send_message(E_CLOSE_MAP,current_game_slot_list.files[id]); return 1; } @@ -1195,9 +1328,9 @@ static char clk_load_proc(int id,int xa,int ya,int xr,int yr) { id=bright_slot(yr-18); xa;ya;xr;yr; - if (ms_last_event.event_type & 0x2 && id>=0 && used_pos[id]) + if (ms_last_event.event_type & 0x2 && id>=0 && (size_t)id < current_game_slot_list.count) { - if (load_game(id)) + if (load_game(current_game_slot_list.files[id])) { message(1,0,0,"",texty[79],texty[80]); redraw_load(); @@ -1241,9 +1374,9 @@ void save_step_next(EVENT_MSG *msg,void **unused) if (c==13) { send_message(E_KEYBOARD,c); - save_game(slot_pos,global_gamename); + save_game(slot_pos,global_gamename,1); wire_proc(); - read_slot_list(); +// read_slot_list(); msg->msg=-2; } else if(c==27) @@ -1274,8 +1407,8 @@ static void save_it(char ok) { if (ok) { - save_game(slot_pos,global_gamename); - read_slot_list(); + save_game(slot_pos,global_gamename,1); +// read_slot_list(); wire_proc(); GlobEvent(MAGLOB_AFTERSAVE,viewsector,viewdir); } @@ -1303,7 +1436,7 @@ void wire_ask_gamename(int id) slot_pos=id; schovej_mysku(); put_picture(x,y,ablock(H_LOADTXTR)); - strcpy(global_gamename,slot_list[id]); + //strcpy(global_gamename,slot_list[id]); clk_ask_name[0].id=add_task(16384,type_text_v2,global_gamename,x,y,SAVE_SLOT_E-SAVE_SLOT_S,SAVE_NAME_SIZE,H_FBOLD,RGB555(31,31,0),save_it); change_click_map(clk_ask_name,CLK_ASK_NAME); ukaz_mysku(); @@ -1377,45 +1510,82 @@ static void saveload_keyboard(EVENT_MSG *msg,void **_) } } +static void saveload_keyboard_menu(EVENT_MSG *msg,void **_) + { + if (msg->msg == E_KEYBOARD) + { + int v = quit_request_as_escape(va_arg(msg->data, int)); + switch (v>>8) + { + case 1:send_message(E_CLOSE_MAP,NULL);break; + case 'H':if (last_select>0) bright_slot((last_select-1)*SLOT_SPACE+1);break; + case 'P':if (last_selectid==30; + close_current(); + return ret; + +} + #if 0 //----------------- JRC LOGO ---------------------------------- diff --git a/game/realgame.c b/game/realgame.c index 9ba3900..22c1371 100644 --- a/game/realgame.c +++ b/game/realgame.c @@ -1902,7 +1902,7 @@ void game_keyboard(EVENT_MSG *msg,void **usr) case 28:enforce_start_battle();break; case 82:group_all();break; case '<':if (!battle && GlobEvent(MAGLOB_CLICKSAVE,viewsector,viewdir)) - {unwire_proc();cancel_render=1;wire_save_load(1);}break; + {unwire_proc();cancel_render=1;do_save_dialog();wire_proc();}break; case '=':unwire_proc();cancel_render=1;wire_save_load(0);break; case '>':game_setup(0,0,0,0,0);break; case 0x2E: if (get_control_key_state() && get_shift_key_state()) { diff --git a/game/setup.c b/game/setup.c index b6015e1..e76afda 100644 --- a/game/setup.c +++ b/game/setup.c @@ -180,7 +180,7 @@ void new_setup() case 0:c_default(show_names);break; case 1:c_default(enable_sort);break; case 2:c_default(autoattack);break; - case 3:c_default(autosave_enabled);break; + case 3:c_default(1);break; case 4:c_default(level_preload);break; } } diff --git a/game/skeldal.c b/game/skeldal.c index aed920d..690e689 100644 --- a/game/skeldal.c +++ b/game/skeldal.c @@ -50,7 +50,7 @@ char *pathtable[]= char **texty; char skip_intro=0; -char autosave_enabled=0; +char autosave_enabled=1; int32_t game_time=0; int charmin=3; int charmax=3; @@ -1429,7 +1429,7 @@ static void wire_load_saved(void) static void load_saved_game(void) { - signed char game; + char *game; err: loadlevel.name[0]=0; @@ -1442,12 +1442,13 @@ static void load_saved_game(void) update_mysky(); { EVENT_MSG *msg = task_wait_event(E_CLOSE_MAP); - game = msg?va_arg(msg->data, int):-1; + const char *cgame = msg?va_arg(msg->data, const char *):NULL; + game = cgame?strdup(cgame):NULL; } unwire_proc(); disable_click_map(); task_wait_event(E_TIMER); - if (game!=-1) + if (game!=NULL) { reinit_kouzla_full(); open_story_file(); @@ -1458,6 +1459,7 @@ static void load_saved_game(void) task_wait_event(E_CLOSE_MAP); send_message(E_DONE,E_IDLE,load_error_report); exit_wait=0; + free(game); goto err; } pick_set_cursor(); @@ -1467,6 +1469,7 @@ static void load_saved_game(void) game_big_circle(1); exit_wait=1; } + free(game); } static int any_save_callback(const char *c, LIST_FILE_TYPE _, size_t __, void *___) { diff --git a/game/souboje.c b/game/souboje.c index 7311f25..a42c909 100644 --- a/game/souboje.c +++ b/game/souboje.c @@ -1372,6 +1372,7 @@ void jadro_souboje(EVENT_MSG *msg,void **unused) //!!!! Jadro souboje viewdir=postavy[i].direction; build_player_map(); GlobEvent(MAGLOB_AFTERBATTLE,viewsector,viewdir); + autosave(); } msg->msg=-2; } diff --git a/libs/bgraph.c b/libs/bgraph.c index 97b06c1..2758904 100644 --- a/libs/bgraph.c +++ b/libs/bgraph.c @@ -36,27 +36,31 @@ void position(word x,word y) writepos=getadr32(x,y); } -void outtext(const char *text) +void outtext_ex(const char *text, int space) { - byte pos; + int pos; if (fontdsize) while (*text) { char2_32(writepos,curfont,*text); - pos=(charsize(curfont,*text) & 0xff)<<1; - writepos+=pos; + pos=((charsize(curfont,*text) & 0xff)<<1)+space + writepos+=pos writeposx+=pos;text++; } else while (*text) { char_32(writepos,curfont,*text); - pos=charsize(curfont,*text) & 0xff; + pos=(charsize(curfont,*text) & 0xff)+space; writepos+=pos; writeposx+=pos;text++; } } +void outtext(const char *text) { + outtext_ex(text,0); + +} diff --git a/libs/bgraph.h b/libs/bgraph.h index 9929918..8765585 100644 --- a/libs/bgraph.h +++ b/libs/bgraph.h @@ -96,6 +96,7 @@ void showview32(word x,word y,word xs,word ys); void showview256(word x,word y,word xs,word ys); void showview_lo(word x,word y,word xs,word ys); void outtext(const char *text); +void outtext_ex(const char *text, int space); void outtext_w_nl(const char *text); int initmode(const INI_CONFIG_SECTION *, const char *app_name); int initmode32(void); diff --git a/libs/bgraph2.c b/libs/bgraph2.c index 24bd7b9..840822a 100644 --- a/libs/bgraph2.c +++ b/libs/bgraph2.c @@ -50,15 +50,15 @@ void rel_position_x(word x) } -void outtext(const char *text) +void outtext_ex(const char *text, int space) { - byte pos; + int pos; if (fontdsize) while (*text) { char2_32(writepos,curfont,*text); - pos=(charsize(curfont,*text) & 0xff)<<1; + pos=((charsize(curfont,*text) & 0xff)<<1)+space; writepos+=pos; writeposx+=pos;text++; } @@ -66,11 +66,15 @@ void outtext(const char *text) while (*text) { char_32(writepos,curfont,*text); - pos=charsize(curfont,*text) & 0xff; + pos=(charsize(curfont,*text) & 0xff)+space; writepos+=pos; writeposx+=pos;text++; } } +void outtext(const char *text) { + outtext_ex(text,0); + +} void outtext_w_nl(const char *text) { diff --git a/platform/istr.c b/platform/istr.c index 3d1fdfd..9fe5904 100644 --- a/platform/istr.c +++ b/platform/istr.c @@ -15,6 +15,22 @@ int istrcmp(const char *a, const char *b) { return 0; } +int istrncmp(const char *a, const char *b, size_t sz) { + while (*a && *b && sz) { + char ca = toupper(*a); + char cb = toupper(*b); + if (cacb) return 1; + ++a; + ++b; + --sz; + } + if (sz == 0) return 0; + if (*a) return 1; + if (*b) return -1; + return 0; +} + void strupper(char *c) { while (*c) { *c = toupper(*c); diff --git a/platform/platform.h b/platform/platform.h index fb2abe3..763cbb0 100644 --- a/platform/platform.h +++ b/platform/platform.h @@ -88,6 +88,7 @@ FILE *fopen_icase(const char *pathname, const char *mode); const char *file_icase_find(const char *pathname); int istrcmp(const char *a, const char *b); +int istrncmp(const char *a, const char *b, size_t sz); int imatch(const char *haystack, const char *needle); const char *strcopy_n(char *target, const char *source, int target_size); #define MIN(a, b) ((a)<(b)?(a):(b))