diff --git a/game/builder.c b/game/builder.c index f75879f..dbb533a 100644 --- a/game/builder.c +++ b/game/builder.c @@ -725,7 +725,7 @@ void crt_minimap_itr(int sector,int smer,int itrx,int itry, int automap) if (itrx<0 || itrx>VIEW3D_X*2 || itry>VIEW3D_Z) return; if (minimap[itry][itrx]) return; minimap[itry][itrx]=sector; - if (!set_halucination) map_coord[sector].flags|=automap & (itrx<=VIEW3D_X+1 && itrx>=VIEW3D_X-1) ; + if (automap && !set_halucination && (itrx<=VIEW3D_X+1 && itrx>=VIEW3D_X-1)) map_coord[sector].flags|=MC_AUTOMAP; if (itrx<=VIEW3D_X) { sector_temp=map_sectors[sector].step_next[dirs[0]]; @@ -734,7 +734,7 @@ void crt_minimap_itr(int sector,int smer,int itrx,int itry, int automap) { savee=enter; enter=-enter_tab[itry][itrx]; - crt_minimap_itr(sector_temp,smer,itrx-1,itry,automap &(sideflags & 1)); + crt_minimap_itr(sector_temp,smer,itrx-1,itry,automap &(sideflags & SD_AUTOMAP)); enter=savee; } } @@ -746,7 +746,7 @@ void crt_minimap_itr(int sector,int smer,int itrx,int itry, int automap) { savee=enter; enter=enter_tab[itry][itrx]; - crt_minimap_itr(sector_temp,smer,itrx+1,itry,automap &(sideflags & 1)); + crt_minimap_itr(sector_temp,smer,itrx+1,itry,automap &(sideflags & SD_AUTOMAP)); enter=savee; } } @@ -756,7 +756,7 @@ void crt_minimap_itr(int sector,int smer,int itrx,int itry, int automap) { savee=enter; enter-=(enter>0)-(enter<0); - crt_minimap_itr(sector_temp,smer,itrx,itry+1,automap &(sideflags & 1)); + crt_minimap_itr(sector_temp,smer,itrx,itry+1,automap &(sideflags & SD_AUTOMAP)); enter=savee; } } diff --git a/game/globals.h b/game/globals.h index 2c43ad6..128f380 100644 --- a/game/globals.h +++ b/game/globals.h @@ -755,7 +755,7 @@ void a_touch(int sector,int dir); int do_action(int action_numb,int sector,int direct,int flags,int nosend); void delay_action(int action_numb,int sector,int direct,int flags,int nosend,int delay); //int32_t load_section(FILE *f,void **section, int *sct_type,int32_t *sect_size); -uint32_t load_section_mem(TMPFILE_RD *f,const void **section, int *sct_type,uint32_t *sect_size); +int32_t load_section_mem(TMPFILE_RD *f,const void **section, int *sct_type,uint32_t *sect_size); TMPFILE_RD *open_ddl_file(const char *name, int group); int prepare_graphics(int *ofs,const char *names,int32_t size,ABLOCK_DECODEPROC decomp,int cls); void show_automap(char full); @@ -1228,7 +1228,7 @@ typedef struct tma_fireball typedef struct tma_loadlev { - uint8_t action,flags,eflags; //3+padding + uint8_t action,flags,lflags; //3+padding short start_pos; //6 char dir; //7 char name[13]; diff --git a/game/interfac.c b/game/interfac.c index 286d7d4..c83eafd 100644 --- a/game/interfac.c +++ b/game/interfac.c @@ -18,10 +18,11 @@ #include #include #include +#include #include #include #include -#include +#include #include "globals.h" #include "engine1.h" #include @@ -1627,81 +1628,90 @@ char ask_save_dialog(char *name_buffer, size_t name_size, char allow_remove) { } -#if 0 -//----------------- JRC LOGO ---------------------------------- +static char *launcher_ddl_file = NULL; +static void free_ddl_file_name(void) { + free(launcher_ddl_file); +} -#define SHOWDELAY 125 -#define SHOWDEND (SHOWDELAY-32) +const char *run_launcher() { + const char *str_label = texty[198]; + TSTR_LIST lst = create_list(100); + TSTR_LIST ddl_lst = create_list(100); + CTL3D ctl = {0,0,4,0}; + int selected = 0; + size_t item_count = 1; -typedef struct _hicolpal - { - unsigned blue:5; - unsigned green:5; - unsigned red:5; - }HICOLPAL; -void show_jrc_logo(char *filename) - { - char *pcx;word *pcxw; - char bnk=1; - int xp,yp,i; - word palette[256],*palw; - int cntr,cdiff,cpalf,ccc; - change_music(NULL); - curcolor=0;bar32(0,0,639,479); - showview(0,0,0,0);sleep_ms(1000); - const char *s = build_pathname(2, gpathtable[SR_VIDEO],filename); - if (open_pcx(s,A_8BIT,&pcx)) return; - pcxw=(word *)pcx; - xp=pcxw[0]; - yp=pcxw[1]; - palw=pcxw+3; - memcpy(palette,palw,256*sizeof(word)); - memset(palw,0,256*sizeof(word)); - xp/=2;yp/=2;xp=320-xp;yp=240-yp; - cntr=get_timer_value();ccc=0; - do - { - cdiff=(get_timer_value()-cntr)/2; - if (cdiffr) palw[i]=r;else palw[i]=k; - k=palette[i] & 0x7e0;if (k>g) palw[i]|=g;else palw[i]|=k; - k=palette[i] & 0x1f;if (k>b) palw[i]|=b;else palw[i]|=k; + UGCManager *ugc = UGC_create(); + size_t ugccount = UGC_Fetch(ugc);; + for (size_t i = 0; i < ugccount; ++i) { + UGCItem item = UGC_GetItem(ugc, i); + char buff[60]; + const char *title = item.name; + size_t tlen = strlen(title); + const char *author = item.author; + size_t alen = strlen(author); + size_t reserve = sizeof(buff)-4; + char d1 = 0; + char d2 = 0; + if (tlen + alen > reserve) { + if (alen < reserve/2) {tlen = reserve - alen - 3;d1 = 1;} + else if (tlen < reserve/2) {alen = reserve - tlen - 3;d2=1;} + else { + tlen = reserve/2-3; d1 = 1; + alen = reserve/2-3; d2 = 1; + } } + char *iter = buff; + for (size_t i = 0; i < tlen; ++i) *iter++ = title[i]; + if (d1) for (size_t i = 0; i < 3; ++i) *iter++='.'; + memcpy(iter, " - ",3); iter+=3; + for (size_t i = 0; i < alen; ++i) *iter++ = author[i]; + if (d2) for (size_t i = 0; i < 3; ++i) *iter++='.'; + *iter = 0; + str_add(&lst, buff); + str_add(&ddl_lst, item.ddl_path); + ++item_count; } - else if (ccc!=cdiff) - { - cpalf=SHOWDELAY-cdiff; - if (cpalf<32) - for (i=0;i<256;i++) - { - int r,g,b,k=32-cpalf; - b=palette[i];g=b>>5;b&=0x1f;r=g>>6;g&=0x1f; - b-=k;r-=k;g-=k; - if (b<0) b=0; - if (r<0) r=0; - if (g<0) g=0; - palw[i]=b | (r<<11) | (g<<6); - } + str_add(&lst, texty[199]); + str_add(&ddl_lst, ""); + + + curcolor = RGB555(0,0,0); + set_font(H_FBIG,RGB555_ALPHA(31,31,31)); + add_window(120,60,400,300,H_WINTXTR,3,20,20); + define(-1,20,10,1,1,0,label,str_label); + set_font(H_FKNIHA,RGB555_ALPHA(31,31,31)); + define(9,15,38,335,212,0,&listbox,lst,RGB555(16,16,16),0); + property(&ctl,NULL,NULL,RGB555(0,0,0));c_default(0); + if (item_count>19) { + define(10,355,38,20,212,0,scroll_bar_v,0,item_count-19,19,RGB555(8,8,8)); + property(&ctl,NULL,NULL,RGB555(10,10,10)); } - put_picture(xp, yp, pcx); - if (bnk) { - showview(xp, yp, pcxw[0], pcxw[1]); - } - ccc=cdiff; - mix_back_sound(0); - } - while (cdiffid; + get_value(0,9,&selected); + char *selddl = strdup(ddl_lst[selected]); + release_list(lst); + release_list(ddl_lst); + close_current(); + if (butt != 30 || selddl == NULL || selddl[0] == 0) { + free(selddl); + UGC_Destroy(ugc); + return butt != 30?NULL:""; + } + launcher_ddl_file = selddl; + UGC_StartPlay(ugc, selected); + UGC_Destroy(ugc); + atexit(&free_ddl_file_name); + return launcher_ddl_file; +} \ No newline at end of file diff --git a/game/realgame.c b/game/realgame.c index a2eae7a..df55606 100644 --- a/game/realgame.c +++ b/game/realgame.c @@ -117,7 +117,7 @@ int32_t load_section(FILE *f,void **section, int *sct_type,int32_t *sect_size) } */ -uint32_t load_section_mem(TMPFILE_RD *f,const void **section, int *sct_type,uint32_t *sect_size) { +int32_t load_section_mem(TMPFILE_RD *f,const void **section, int *sct_type,uint32_t *sect_size) { uint32_t s; char c[20]; @@ -284,8 +284,8 @@ int load_map(const char *filename) mapsize = 0; do { - uint32_t r=load_section_mem(f,&temp,§,&size); - if (r==size) + int32_t r=load_section_mem(f,&temp,§,&size); + if (r==(int32_t)size) switch (sect) { case A_SIDEMAP: diff --git a/game/skeldal.c b/game/skeldal.c index 305725e..4b4edde 100644 --- a/game/skeldal.c +++ b/game/skeldal.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "globals.h" #include "resources.h" // @@ -825,6 +826,8 @@ void cti_texty(void) //patch stringtable if (!texty[98]) str_replace(&texty,98,"Ulo\x91it hru jako"); if (!texty[99]) str_replace(&texty,99,"CRT Filter (>720p)"); + if (!texty[198]) str_replace(&texty,198,"Zvol dobrodru\x91stv\xA1"); + if (!texty[199]) str_replace(&texty,199,"Br\xA0ny Skeldalu (p\x96vodn\xA1 dobrodru\x91stv\xA1)"); str_replace(&texty, 144, "Zrychlit souboje"); str_replace(&texty, 51, "Celkov\x88 Hudba Efekty V\x98\xA8ky Basy Rychlost"); str_replace(&texty,0,"Byl nalezen p\xA9ipojen\x98 ovlada\x87\nPro aktivaci ovlada\x87""e stiskn\x88te kter\x82koliv tla\x87\xA1tko na ovlada\x87i"); @@ -888,14 +891,17 @@ const void *boldcz; void init_DDL_manager() { const char *ddlfile = build_pathname(2, gpathtable[SR_DATA],"SKELDAL.DDL"); - ddlfile = local_strdup(ddlfile); + ddlfile = local_strdup(ddlfile); init_manager(); - for (size_t sz = countof(patch_files); sz > 0;) { - --sz; - if (patch_files[sz]) { - if (!add_patch_file(patch_files[sz])) { - display_error("Can't open resource file (adv_patch): %s", patch_files[sz]); + if (!add_patch_file(ddlfile)) { + display_error("Can't open resource file (main): %s", ddlfile); + abort(); + } + for (size_t i = 0; i < countof(patch_files); ++i) { + if (patch_files[i]) { + if (!add_patch_file(patch_files[i])) { + display_error("Can't open resource file (adv_patch): %s", patch_files[i]); abort(); } } @@ -906,10 +912,6 @@ void init_DDL_manager() { gfx = local_strdup(gfx); add_patch_file(gfx); } - if (!add_patch_file(ddlfile)) { - display_error("Can't open resource file (main): %s", ddlfile); - abort(); - } SEND_LOG("(GAME) Memory manager initialized. Using DDL: '%s'",ddlfile); @@ -968,7 +970,12 @@ void sse_listener_watch(EVENT_MSG *msg, void **userdata) { if (msg->msg == E_WATCH) { const char *s = sse_receiver_receive(sse_receiver); if (s) { - send_message(E_EXTERNAL_MSG, s); + if (strcmp(s,"STOP") == 0) { + closemode(); + exit(0); + } else { + send_message(E_EXTERNAL_MSG, s); + } } } } @@ -1070,8 +1077,6 @@ void init_skeldal(const INI_CONFIG *cfg) load_shops(); memset(&loadlevel,0,sizeof(loadlevel)); - loadlevel.eflags = 0xFF; - } void wire_main_functs(); @@ -1128,29 +1133,35 @@ extern char running_battle; const char *m = va_arg(msg->data, const char *); char fname[13]; int sector; + int side; int i; - if (sscanf(m, "RELOAD %12s %d", fname, §or) == 2) { + if (sscanf(m, "RELOAD %12s %d %d", fname, §or, &side) == 3) { + reload_ddls(); strcopy_n(loadlevel.name,fname,sizeof(loadlevel.name)); - loadlevel.start_pos=sector; + loadlevel.start_pos=sector; + loadlevel.dir = side; for(i=0;idata, int); + EVENT_MSG *msg = task_wait_event(E_CLOSE_MAP); + end = msg != NULL?va_arg(msg->data, int):0; } send_message(E_DONE,E_RELOADMAP,reload_map_handler); @@ -1196,22 +1207,8 @@ void enter_game(void) } -static int update_config(void) - { - return 0; - } - -void help(void) - { - printf("Pouziti:\n\n S \n\n" - " jmeno mapy\n" - " Cislo startovaciho sektoru\n" - ); - exit(0); - } - extern char nofloors; /* @@ -1496,11 +1493,6 @@ static void start_from_mapedit(va_list args) } #endif -void disable_intro(void) - { - add_field_num(&cur_config,sinit[12].heslo,1); - update_config(); - } /* * -char def_path[]=""; @@ -1560,6 +1552,8 @@ const char *configure_pathtable(const INI_CONFIG *cfg) { if (ini_get_boolean(paths, "patch_mode", 0)) { mman_patch = 1; } + const char *ugc_path = ini_get_string(paths, "local_ugc", "adv"); + UGCSetLocalFoler(ugc_path); return groot; } @@ -1610,6 +1604,7 @@ int skeldal_gen_string_table_entry_point(const SKELDAL_CONFIG *start_cfg, const return 0; } +const char *run_launcher(); int skeldal_entry_point(const SKELDAL_CONFIG *start_cfg) { def_mman_group_table(gpathtable); @@ -1665,21 +1660,29 @@ int skeldal_entry_point(const SKELDAL_CONFIG *start_cfg) init_skeldal(cfg); + if (start_cfg->launcher) { + const char *ddl = run_launcher(); + if (ddl==NULL) goto cleanup; + if (ddl[0]) { + add_patch_file(ddl); + reload_ddls(); + } + } + int start_task = add_task(65536,start); escape(); term_task_wait(start_task); +cleanup:; closemode(); - - ini_close(cfg); - return 0; } + #include "version.h" diff --git a/game/skeldal.h b/game/skeldal.h index c2e12e7..b527b64 100644 --- a/game/skeldal.h +++ b/game/skeldal.h @@ -15,6 +15,7 @@ typedef struct { const char *patch_file; const char *sse_hostport; + char launcher; //0 ; ) { + --i; const TDDLMAP_INFO *nfo = &ddlmap[i]; if (nfo->ptr) { int sk = get_file_entry_in_table(&nfo->nametable, name); @@ -329,6 +334,7 @@ void reload_ddls(void) { } h->status = BK_NOT_LOADED; h->blockdata = NULL; + get_file_entry(h->path,h->src_file,h); } } } @@ -343,9 +349,19 @@ void reload_ddls(void) { } dinfo->ptr = bmf; dinfo->size = bmf_s; + ablock_free(dinfo->nametable.data); dinfo->nametable = load_file_table(bmf); } } + for(i=0;isrc_file[0]) { + get_file_entry(h->path,h->src_file,h); + } + } + } } @@ -630,8 +646,9 @@ void close_manager() for(j=0;jdone=string_list_done; } -/*void main() - { - TSTR_LIST test; - int i,j; - test=read_directory("c:\\windows\\system\\*.*",DIR_BREIF,_A_NORMAL); - j=str_count(test); - for(i=0;i #include #include +#include typedef struct ini_config_section_tag { using Section = std::map>; @@ -59,7 +60,8 @@ void parseIniStream(std::istream& input, Callback &&callback) { INI_CONFIG* ini_open(const char *filename) { - std::fstream input(filename); + std::filesystem::path fname(reinterpret_cast(filename)); + std::fstream input(fname); if (!input) return NULL; INI_CONFIG *c = new INI_CONFIG; parseIniStream(input, [&](std::string_view section, std::string_view key, std::string_view value) { diff --git a/platform/legacy_coroutines.cpp b/platform/legacy_coroutines.cpp index 2d76fbd..0230c3d 100644 --- a/platform/legacy_coroutines.cpp +++ b/platform/legacy_coroutines.cpp @@ -17,6 +17,7 @@ struct TaskInfo { bool exited = false; TaskInfo(int id):id(id) {} + ~TaskInfo(); }; using TaskList = std::unordered_map >; @@ -212,3 +213,11 @@ void task_sleep_for(unsigned int time_ms) { } } +TaskInfo::~TaskInfo() { + + //destructor is called only when task is already in cleanup state + if (!exited) { + thr.detach(); + } + +} diff --git a/platform/linux/app_start.cpp b/platform/linux/app_start.cpp index 9f80812..6f94e0b 100644 --- a/platform/linux/app_start.cpp +++ b/platform/linux/app_start.cpp @@ -22,6 +22,7 @@ void show_help(const char *arg0) { "-l set language (cz|en)\n" "-s generate string-tables (for localization) and exit\n" "-c connect to host:port for remote control (mapedit)\n" + "-L run launcher - select workshop adventure\n" "-h this help\n"); exit(1); } @@ -38,13 +39,15 @@ int main(int argc, char **argv) { std::string patch; std::string lang; std::string sse_hostport; - for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:p:c:")) != -1; ) { + bool launcher = false; + for (int optchr = -1; (optchr = getopt(argc, argv, "hLf:a:s:l:p:c:")) != -1; ) { switch (optchr) { case 'f': config_name = optarg;break; case 'a': adv_config_file = optarg;break; case 'h': show_help(argv[0]);break; case 'p': patch = optarg; break; case 'l': lang = optarg;break; + case 'L': launcher = true;break; case 's': gen_stringtable_path = optarg;break; case 'c': sse_hostport = optarg;break; default: show_help_short(); @@ -62,6 +65,7 @@ int main(int argc, char **argv) { cfg.lang_path = lang.empty()?NULL:lang.c_str(); cfg.patch_file = patch.empty()?NULL:patch.c_str(); cfg.sse_hostport = sse_hostport.empty()?NULL:sse_hostport.c_str(); + cfg.launcher = launcher?1:0; try { if (!gen_stringtable_path.empty()) { diff --git a/platform/sdl/BGraph2.cpp b/platform/sdl/BGraph2.cpp index e841088..9e9c71c 100644 --- a/platform/sdl/BGraph2.cpp +++ b/platform/sdl/BGraph2.cpp @@ -281,3 +281,9 @@ void game_display_disable_crt_effect_temporary(char disable) { auto &sdl = get_sdl_global_context(); sdl.disable_crt_effect_temprary(disable?true:false); } + +void game_display_focus() +{ + auto &sdl = get_sdl_global_context(); + sdl.raise_window(); +} diff --git a/platform/sdl/BGraph2.h b/platform/sdl/BGraph2.h index aabdbb3..41b20a8 100644 --- a/platform/sdl/BGraph2.h +++ b/platform/sdl/BGraph2.h @@ -39,6 +39,7 @@ void game_display_hide_sprite(int sprite_id); ///unload sprite and free index void game_display_unload_sprite(int sprite); void game_display_disable_crt_effect_temporary(char disable); +void game_display_focus(); void *DxPrepareWalk(int ypos); void DxZoomWalk(void *handle, int ypos, int *points,float phase, void *lodka); diff --git a/platform/sdl/sdl_context.cpp b/platform/sdl/sdl_context.cpp index 280700c..04bf17b 100644 --- a/platform/sdl/sdl_context.cpp +++ b/platform/sdl/sdl_context.cpp @@ -1354,3 +1354,12 @@ SDLContext::JoystickButton SDLContext::button_from_string(std::string_view s) { void SDLContext::disable_crt_effect_temprary(bool disable) { _disable_crt_tmp = disable; } + +void SDLContext::raise_window() const +{ + SDL_RaiseWindow(_window.get()); + SDL_SetWindowAlwaysOnTop(_window.get(),SDL_TRUE); + SDL_Delay(100); // malá pauza (volitelné) + SDL_SetWindowAlwaysOnTop(_window.get(),SDL_FALSE); + +} diff --git a/platform/sdl/sdl_context.h b/platform/sdl/sdl_context.h index d3cd613..1903cec 100644 --- a/platform/sdl/sdl_context.h +++ b/platform/sdl/sdl_context.h @@ -163,6 +163,8 @@ public: bool is_joystick_used() const; bool is_joystick_enabled() const; void disable_crt_effect_temprary(bool disable); + + void raise_window() const; protected: struct SDL_Deleter { diff --git a/platform/sdl/sound.cpp b/platform/sdl/sound.cpp index e66faab..01304bb 100644 --- a/platform/sdl/sound.cpp +++ b/platform/sdl/sound.cpp @@ -252,6 +252,7 @@ void stop_play_music() { if (music_source) { fade_music(); stop_music(); + current_music.reset(); } } diff --git a/platform/sse_receiver.c b/platform/sse_receiver.c index 50d5cdd..6566f2d 100644 --- a/platform/sse_receiver.c +++ b/platform/sse_receiver.c @@ -54,7 +54,7 @@ static void set_nonblocking(sock_t sock) { static int connect_and_handshake(SSE_RECEIVER *sse) { struct addrinfo hints = {0}, *res = NULL; - hints.ai_family = AF_INET; + hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(sse->host, sse->port, &hints, &res) != 0) { @@ -128,6 +128,12 @@ const char *sse_receiver_receive(SSE_RECEIVER *sse) { if (strncmp(r,"data:",5) == 0) { r+=5; while (*r && isspace(*r)) ++r; + char *s = strchr(r, 0)-1; + while (s > r && isspace(*s)) { + *s = 0; + --s; + } + if (*r) { return r; } diff --git a/platform/ugc.cpp b/platform/ugc.cpp new file mode 100644 index 0000000..1ccf0b9 --- /dev/null +++ b/platform/ugc.cpp @@ -0,0 +1,190 @@ +#include "ugc.h" +#include "config.h" +#include +#include +#include +#include + + +#include + +std::wstring toWideChar(std::string_view text) { + unsigned int codepoint = 0; + unsigned int bytes = 0; + std::wstring out; + for (char c : text) { + if ((c & 0x80) == 0) out.push_back(c); + else { + if ((c & 0xC0) == 0x80) { + codepoint = (codepoint << 6) | (c & 0x3F); + } else if ((c & 0xE0) == 0xC0) { + bytes=2; codepoint = c & 0x1F; + } else if ((c & 0xF0) == 0xE0) { + bytes=3; codepoint = c & 0x0F; + } else if ((c & 0xF8) == 0xF0) { + bytes=4; codepoint = c & 0x07; + } + --bytes; + if (!bytes) out.push_back(static_cast(codepoint)); + } + } + return out; +} + +constexpr std::pair cztable[]={ + {0x010C,'\x80'}, + {0x00FC,'u'}, + {0x00E9,'\x82'}, + {0x010F,'\x83'}, + {0x00E4,'a'}, + {0x010E,'\x85'}, + {0x0164,'\x86'}, + {0x010D,'\x87'}, + {0x011B,'\x88'}, + {0x011A,'\x89'}, + {0x0139,'L'}, + {0x00CD,'\x8B'}, + {0x013E,'l'}, + {0x013A,'l'}, + {0x00C4,'A'}, + {0x00C1,'\x8F'}, + {0x00C9,'\x90'}, + {0x017E,'\x91'}, + {0x017D,'\x92'}, + {0x00F4,'o'}, + {0x00F6,'o'}, + {0x00D3,'\x95'}, + {0x016F,'\x96'}, + {0x00DA,'\x97'}, + {0x00FD,'\x98'}, + {0x00D6,'O'}, + {0x00DC,'U'}, + {0x0160,'\x9b'}, + {0x013D,'L'}, + {0x00DD,'\x9d'}, + {0x0158,'\x9e'}, + {0x0165,'\x9f'}, + {0x00E1,'\xA0'}, + {0x00ED,'\xA1'}, + {0x00F3,'\xA2'}, + {0x00FA,'\xA3'}, + {0x0148,'\xA4'}, + {0x0147,'\xA5'}, + {0x016E,'\xA6'}, + {0x00D4,'O'}, + {0x0161,'\xA8'}, + {0x0159,'\xA9'}, + {0x0155,'r'}, + {0x0154,'R'}, + {0x00BC,'\xAC'}, + {0x00A7,'\xAD'}, + {0x00AB,'<'}, + {0x00BB,'>'}, +}; + + +std::string toKEYBCS2(const char *text) { + auto wstr = toWideChar(text); + std::string out; + out.resize(wstr.size()); + std::transform(wstr.begin(), wstr.end(), out.begin(), [](wchar_t c){ + if (c <= 0x80) return static_cast(c); + auto iter = std::find_if(std::begin(cztable), std::end(cztable), [c](const auto &x){return x.first == c;}); + if (iter == std::end(cztable)) return '_'; + return static_cast(iter->second); + }); + return out; +} + + +struct UGCItemEx : UGCItem { + std::unique_ptr text_data; + std::filesystem::path _stamp_file; +}; + +struct tag_UGCManager { + std::vector _list; +}; + +UGCManager *UGC_create() { + return new UGCManager; +} +void UGC_Destroy(UGCManager *inst) { + delete inst; +} + +static std::filesystem::path ugc_local_path; + +void UGCSetLocalFoler(const char *path) { + ugc_local_path = reinterpret_cast(path); +} + +size_t UGC_Fetch(UGCManager *manager) { + + manager->_list.clear(); + std::error_code ec; + auto iter = std::filesystem::directory_iterator(ugc_local_path,ec); + if (ec == std::error_code()) { + auto fend = std::filesystem::directory_iterator(); + while (iter != fend) { + const auto &entry = *iter; + if (entry.is_directory()) { + auto entry_path =std::filesystem::weakly_canonical(entry.path()) ; + auto info_path = entry_path / "content.ini"; + if (std::filesystem::is_regular_file(info_path)) { + INI_CONFIG *cfg = ini_open(reinterpret_cast(info_path.u8string().c_str())); + if (cfg) { + const INI_CONFIG_SECTION *section = ini_section_open(cfg, "description"); + std::string title = toKEYBCS2(ini_get_string(section, "title", NULL)); + std::string author = toKEYBCS2(ini_get_string(section, "author", "unknown author")); + const INI_CONFIG_SECTION *files = ini_section_open(cfg, "files"); + const char *ddl = ini_get_string(files, "ddlfile", NULL); + if (ddl && !title.empty()) { + UGCItemEx r; + std::filesystem::path ddlpath = entry_path / ddl; + auto pstr = ddlpath.u8string(); + ddl = reinterpret_cast(pstr.c_str()); + + auto tlen = title.size()+1; + auto alen = author.size()+1; + auto dlen = strlen(ddl)+1; + + r.text_data = std::make_unique(tlen+alen+dlen); + char *c = r.text_data.get(); + memcpy(c, title.c_str(), tlen);r.name = c;c+=tlen; + memcpy(c, author.c_str(), alen);r.author = c;c+=alen; + memcpy(c, ddl, dlen);r.ddl_path= c;c+=alen; + + auto stampfile = entry_path / "stamp"; + std::filesystem::file_time_type tp = std::filesystem::last_write_time(stampfile, ec); + auto sctp = std::chrono::time_point_cast(tp - std::filesystem::file_time_type::clock::now() + + std::chrono::system_clock::now()); + r.last_played = std::chrono::system_clock::to_time_t(sctp); + r._stamp_file = std::move(stampfile); + manager->_list.push_back(std::move(r)); + } + ini_close(cfg); + } + } + } + ++iter; + } + + } + std::sort(manager->_list.begin(), manager->_list.end(), [](const UGCItem &a, const UGCItem &b){ + return a.last_played > b.last_played; + }); + + return manager->_list.size(); + +} +UGCItem UGC_GetItem(UGCManager *manager, size_t pos) { + return manager->_list[pos]; +} + +void UGC_StartPlay(UGCManager *manager, size_t pos) { + std::ofstream out(manager->_list[pos]._stamp_file, std::ios::out|std::ios::trunc); +} + + + diff --git a/platform/ugc.h b/platform/ugc.h new file mode 100644 index 0000000..bff2cce --- /dev/null +++ b/platform/ugc.h @@ -0,0 +1,29 @@ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const char *name; + const char *ddl_path; + const char *author; + time_t last_played; +} UGCItem; + +typedef struct tag_UGCManager UGCManager; + + +void UGCSetLocalFoler(const char *path); +UGCManager *UGC_create(); +size_t UGC_Fetch(UGCManager *manager); +UGCItem UGC_GetItem(UGCManager *manager, size_t pos); +void UGC_StartPlay(UGCManager *manager, size_t pos); +void UGC_Destroy(UGCManager *inst); + + + + +#ifdef __cplusplus +} +#endif diff --git a/platform/windows/app_start.cpp b/platform/windows/app_start.cpp index 63795f3..1aa2462 100644 --- a/platform/windows/app_start.cpp +++ b/platform/windows/app_start.cpp @@ -25,6 +25,7 @@ void show_help(std::ostream &out, const char *arg0) { "-l set language (cz|en)\n" "-s generate string-tables (for localization) and exit\n" "-c connect to host:port for remote control (mapedit)\n" + "-L run launcher - select workshop adventure\n" "-h this help\n"; } @@ -40,14 +41,17 @@ int main(int argc, char **argv) { std::string lang; std::string patch; std::string sse_hostport; + bool launcher = false; std::ostringstream console; - for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:c:p:")) != -1; ) { + for (int optchr = -1; (optchr = getopt(argc, argv, "hLf:a:s:l:c:p:")) != -1; ) { switch (optchr) { case 'f': config_name = optarg;break; case 'a': adv_config_file = optarg;break; case 'h': show_help(console, argv[0]);break; case 'p': patch = optarg; break; case 'l': lang = optarg;break; + case 'L': launcher = true;break; + case 's': gen_stringtable_path = optarg;break; case 'c': sse_hostport = optarg;break; default: show_help_short(console);break; @@ -72,6 +76,7 @@ int main(int argc, char **argv) { cfg.lang_path = lang.empty()?NULL:lang.c_str(); cfg.patch_file = patch.empty()?NULL:patch.c_str(); cfg.sse_hostport = sse_hostport.empty()?NULL:sse_hostport.c_str(); + cfg.launcher = launcher?1:0; { std::string msg = console.str(); diff --git a/platform/windows/map_file.cpp b/platform/windows/map_file.cpp index 4f43e7a..7b7ab29 100644 --- a/platform/windows/map_file.cpp +++ b/platform/windows/map_file.cpp @@ -10,6 +10,7 @@ extern "C" { #include #include #include +#include // Funkce pro mapování souboru do paměti const void* map_file_to_memory_cpp(const char *name, size_t *sz) { @@ -17,8 +18,10 @@ const void* map_file_to_memory_cpp(const char *name, size_t *sz) { return NULL; } - HANDLE h = CreateFileA(name, GENERIC_READ, FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); - if (h == INVALID_HANDLE_VALUE) throw std::runtime_error(std::string("Failed to open file for mapping: ").append(name)); + std::filesystem::path p(reinterpret_cast(name)); + + HANDLE h = CreateFileW(p.c_str(), GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + if (h == INVALID_HANDLE_VALUE) throw std::runtime_error(std::string("Failed to open file for mapping: ")+p.string()); LARGE_INTEGER fsize; if (!GetFileSizeEx(h, &fsize)) { diff --git a/skeldal.ini b/skeldal.ini index c49ddbe..a6ecaa3 100644 --- a/skeldal.ini +++ b/skeldal.ini @@ -6,6 +6,7 @@ # data = relative path to skeldal.ddl # language = path language folders # savegame = path to savegame, if not defined, retrieved from platform settings +# local_ugc = path to local repository for user generated content (if team is enabled it also uses steam workshop path) [paths] @@ -15,6 +16,7 @@ # data=./ # language=./lang # savegame = determine default +# local_ugc=./adv #### video settings #