diff --git a/built/launcher/res/btn.png b/built/launcher/res/btn.png new file mode 100644 index 0000000..f68b8a0 Binary files /dev/null and b/built/launcher/res/btn.png differ diff --git a/built/launcher/res/btnhover.png b/built/launcher/res/btnhover.png new file mode 100644 index 0000000..75814fa Binary files /dev/null and b/built/launcher/res/btnhover.png differ diff --git a/built/launcher/res/btnpress.png b/built/launcher/res/btnpress.png new file mode 100644 index 0000000..2a6b800 Binary files /dev/null and b/built/launcher/res/btnpress.png differ diff --git a/built/launcher/res/exit.png b/built/launcher/res/exit.png new file mode 100644 index 0000000..328384c Binary files /dev/null and b/built/launcher/res/exit.png differ diff --git a/built/launcher/res/forebutton.png b/built/launcher/res/forebutton.png new file mode 100644 index 0000000..d38eddc Binary files /dev/null and b/built/launcher/res/forebutton.png differ diff --git a/built/launcher/res/forebuttonon.png b/built/launcher/res/forebuttonon.png new file mode 100644 index 0000000..5c0c010 Binary files /dev/null and b/built/launcher/res/forebuttonon.png differ diff --git a/built/launcher/res/forums.png b/built/launcher/res/forums.png new file mode 100644 index 0000000..5611d84 Binary files /dev/null and b/built/launcher/res/forums.png differ diff --git a/built/launcher/res/help.png b/built/launcher/res/help.png new file mode 100644 index 0000000..0ef04bf Binary files /dev/null and b/built/launcher/res/help.png differ diff --git a/built/launcher/res/info.ttf b/built/launcher/res/info.ttf new file mode 100644 index 0000000..c809d80 Binary files /dev/null and b/built/launcher/res/info.ttf differ diff --git a/built/launcher/res/launcher.png b/built/launcher/res/launcher.png new file mode 100644 index 0000000..72a1c40 Binary files /dev/null and b/built/launcher/res/launcher.png differ diff --git a/built/launcher/res/lockbutton.png b/built/launcher/res/lockbutton.png new file mode 100644 index 0000000..1bddb89 Binary files /dev/null and b/built/launcher/res/lockbutton.png differ diff --git a/built/launcher/res/log.ttf b/built/launcher/res/log.ttf new file mode 100644 index 0000000..77bf6af Binary files /dev/null and b/built/launcher/res/log.ttf differ diff --git a/built/launcher/res/options.png b/built/launcher/res/options.png new file mode 100644 index 0000000..b349c23 Binary files /dev/null and b/built/launcher/res/options.png differ diff --git a/built/launcher/res/playoff.png b/built/launcher/res/playoff.png new file mode 100644 index 0000000..d5ef220 Binary files /dev/null and b/built/launcher/res/playoff.png differ diff --git a/built/launcher/res/playon.png b/built/launcher/res/playon.png new file mode 100644 index 0000000..48a57fc Binary files /dev/null and b/built/launcher/res/playon.png differ diff --git a/built/launcher/res/showcase.ttf b/built/launcher/res/showcase.ttf new file mode 100644 index 0000000..3566ebf Binary files /dev/null and b/built/launcher/res/showcase.ttf differ diff --git a/built/launcher/res/update.png b/built/launcher/res/update.png new file mode 100644 index 0000000..c47f0ca Binary files /dev/null and b/built/launcher/res/update.png differ diff --git a/built/launcher/res/verify.png b/built/launcher/res/verify.png new file mode 100644 index 0000000..207bd09 Binary files /dev/null and b/built/launcher/res/verify.png differ diff --git a/built/launcher/res/versions.png b/built/launcher/res/versions.png new file mode 100644 index 0000000..f080aac Binary files /dev/null and b/built/launcher/res/versions.png differ diff --git a/built/launcher/res/website.png b/built/launcher/res/website.png new file mode 100644 index 0000000..58ad3a7 Binary files /dev/null and b/built/launcher/res/website.png differ diff --git a/games/endlauncher/src/main.cpp b/games/endlauncher/src/main.cpp new file mode 100644 index 0000000..44e57a2 --- /dev/null +++ b/games/endlauncher/src/main.cpp @@ -0,0 +1,356 @@ +#include +#include +#include +#include "netio.h" +#include +#include +#include + +using namespace enginend; +using namespace enginend::nodes; +netio nete{}; +/* + + */ +struct background: public virtual textured { + background(Texture2D* texture,float x,float y,float w, float h) { + this->pos=Vector2{x,y};this->size=Vector2{w,h}; + this->texture=texture; + this->justclicked=false; + } + void boot()override{ + img=LoadImageFromTexture(*this->texture); + } + void tick()override { + Vector2 mp=Vector2(GetMousePositionDesktop()); + Vector2 wp=GetWindowPosition(); + Vector2 rp=Vector2{mp.x-wp.x,mp.y-wp.y}; + if (IsMouseButtonDown(0)) { + if (rp.x>=0&&rp.y>=0&&rp.x<600&&rp.y<300) { + Color pix=GetImageColor(img, (int)rp.x, (int)(300-rp.y)); + if (pix.a!=0) { + if (clicked==false) { + clicked=true; + relmouse=rp; + } + } + } + }else { + clicked=false; + } + if (clicked) { + Vector2 nwp{ + mp.x-relmouse.x, mp.y-relmouse.y + }; + int cm=GetCurrentMonitor(); + Vector2 mop=GetMonitorPosition(cm); + int newwpx=nwp.x-mop.x; + int newwpy=nwp.y-mop.y; + int maxwpx=GetMonitorWidth(cm)-GetScreenWidth(); + int maxwpy=GetMonitorHeight(cm)-GetScreenHeight(); + newwpx=newwpx>maxwpx?maxwpx:newwpx<0?0:newwpx; + newwpy=newwpy>maxwpy?maxwpy:newwpy<0?0:newwpy; + SetWindowPosition(newwpx,newwpy); + } + } + void draw()override{this->textured::draw();} + void exit()override{} +private: + Image img;bool justclicked;bool clicked=false; + Vector2 relmouse; +}; + +struct logi: public virtual textfield { + logi(Texture2D* texture,Color textcol,Color color,float x,float y,float w,float h,Font f,float fs,std::string txt) { + this->pos=Vector2{x,y};this->size=Vector2{w,h};this->content=txt; + this->texture=texture;this->txc=textcol;this->c=color; + this->font=f; + this->fs=fs; + } + void draw() override { + Vector2 p=pos; + float spacing=fs/2.0f; + std::stringstream ss(content); + std::string line; + int lines=0; + + while (std::getline(ss, line)) { + if (lines%2==0) { + Vector2 charPos=p; + for (char& c : line) { + char str[2]={c, '\0'}; + Color fc=Color{255,0,255,255}; + if (c=='/'||c==':') { + fc=Color{255,255,0,255}; + } + + DrawTextEx(font, str, charPos, (float)fs, spacing, fc); + charPos.x+=MeasureTextEx(font, str, (float)fs, spacing).x; + } + } else { + DrawTextEx(font, line.c_str(), p, (float)fs, spacing, Color{0,255,255,255}); + } + + p.y+=(fs+2); + lines++; + } + } +}; +void playbuttonfunc(); +void quit(){exit(0);} +#include +class launcher:public program { +public: + Texture2D bg; + Texture2D buttonfore; + Texture2D buttonlock; + Texture2D playbtn[2]; + Texture2D menubtn[3]; + button* buttons[8]; + button* playbutton; + Texture2D buttonslabel[8]; + Font gamename,changelog,information; + bool vsync=true; + RenderTexture2D target; + text version; + std::string selectedversion=""; + Image img; + bool captured=true; + const char* CONF() final{return "test.tdf";} + launcher(){}; + void boot() override { + tickrate=15; + framerate=15; + SetConfigFlags(FLAG_WINDOW_UNDECORATED|FLAG_WINDOW_TRANSPARENT|FLAG_WINDOW_TOPMOST); + tiny::error("is transparent lol"); + InitWindow(600,300,"test");target=LoadRenderTexture(600, 300); + img=GenImageColor(600, 300, rl::BLANK); + SetTargetFPS(GetMonitorRefreshRate(GetCurrentMonitor())); + this->tickrate=GetMonitorRefreshRate(GetCurrentMonitor()); + bg=LoadTexture("res/launcher.png"); + buttonfore=LoadTexture("res/forebuttonon.png"); + buttonlock=LoadTexture("res/lockbutton.png"); + gamename=LoadFont("res/showcase.ttf"); + changelog=LoadFont("res/log.ttf"); + information=LoadFont("res/info.ttf"); + menubtn[0]=LoadTexture("res/btn.png"); + menubtn[1]=LoadTexture("res/btnhover.png"); + menubtn[2]=LoadTexture("res/btnpress.png"); + selectedversion=nete.currentversion; + version= text (nullptr,Color{255,255,255,255},Color{0,0,0,0},96,16*17,1,1,information,20,"version: "+selectedversion); + SetTraceLogLevel(LOG_ERROR); + buttons[0]=new button(&menubtn[0], {255,255,255,255},468,58,96,16,nullptr); + buttonslabel[0]=LoadTexture("res/options.png"); + buttons[1]=new button(&menubtn[0], {255,255,255,255},468,58+(18*1),96,16,std::function([]() { + OpenURL("http://kosumi.ddns.net:60000/"); + })); + buttonslabel[1]=LoadTexture("res/website.png"); + buttons[2]=new button(&menubtn[0], {255,255,255,255},468,58+(18*2),96,16,std::function([]() { + OpenURL("http://kosumi.ddns.net:60001/"); + })); + buttonslabel[2]=LoadTexture("res/forums.png"); + buttons[3]=new button(&menubtn[0], {255,255,255,255},468,58+(18*3),96,16,std::function([]() { + OpenURL("https://github.com/kin-fuyuki/allgames/issues"); + })); + buttonslabel[3]=LoadTexture("res/help.png"); + buttons[4]=new button(&menubtn[0], {255,255,255,255},468,58+(18*4),96,16,std::function([]() { + nete.download(); + })); + buttonslabel[4]=LoadTexture("res/update.png"); + buttons[5]=new button(&menubtn[0], {255,255,255,255},468,58+(18*5),96,16,nullptr); + buttonslabel[5]=LoadTexture("res/verify.png"); + buttons[6]=new button(&menubtn[0], {255,255,255,255},468,58+(18*6)+15,96,16,nullptr); + buttonslabel[6]=LoadTexture("res/versions.png"); + buttons[7]=new button(&menubtn[0], {255,255,255,255},468,58+(18*7)+17,96,16,std::function([]() { + quit(); + })); + buttonslabel[7]=LoadTexture("res/exit.png"); + playbtn[0]=LoadTexture("res/playoff.png"); + playbtn[1]=LoadTexture("res/playon.png"); + playbutton= new button(&playbtn[0], {255,255,255,255},406,(18*11)+17+9,153,59,std::function(playbuttonfunc)); + currentscene->nodes=std::list{ + new background(&bg,0,0,600,300), + new textured(&buttonfore,3,36,62,62), + new textured(&buttonlock,3,36+((62+4)*1),62,62), + new textured(&buttonlock,3,36+((62+4)*2),62,62), + new textured(&buttonlock,3,36+((62+4)*3),62,62), + new text(nullptr,Color{0,255,0,255},{0,0,0,0},232,19,1,1,gamename,16,"FORESPEND"), + buttons[0],new textured(&buttonslabel[0],468,58,96,16), + buttons[1],new textured(&buttonslabel[1],468,58+(18*1),96,16), + buttons[2],new textured(&buttonslabel[2],468,58+(18*2),96,16), + buttons[3],new textured(&buttonslabel[3],468,58+(18*3),96,16), + buttons[4],new textured(&buttonslabel[4],468,58+(18*4),96,16), + buttons[5],new textured(&buttonslabel[5],468,58+(18*5),96,16), + buttons[6],new textured(&buttonslabel[6],468,58+(18*6)+15,96,16), + buttons[7],new textured(&buttonslabel[7],468,58+(18*7)+17,96,16), + playbutton, + new logi(nullptr,Color{255,0,255,255},Color{0,0,0,0},90,58,466,159,changelog,8,nete.changelog), + new text(nullptr,Color{255,255,255,255},Color{0,0,0,0},96+16,(16*14)-3,1,1,information,20,"downloaded verified"), + &version, + }; + currentscene->boot(); + } + int buttondelay=0; + void tick() override { + if (nete.currentversion!=selectedversion) { + selectedversion=nete.currentversion; + this->version.content="version: "+selectedversion; + tiny::echo("selecting version: %s",selectedversion.c_str()); + } + { + for (button* b : buttons) { + if (b->hover) { + if (b->pressed) { + b->texture=&menubtn[2]; + buttondelay=5; + }else { + if (b->texture->id==menubtn[2].id) { + if(buttondelay<0) { + b->texture=&menubtn[1]; + } + }else { + b->texture=&menubtn[1]; + } + } + }else { + if (b->texture->id==menubtn[2].id) { + if(buttondelay<0) { + b->texture=&menubtn[0]; + } + }else { + b->texture=&menubtn[0]; + } + } + } + } + { + if (playbutton->pressed) { + playbutton->texture=&playbtn[1]; + buttondelay=5; + }else { + if(buttondelay<0) { + playbutton->texture=&playbtn[0]; + } + } + } + currentscene->tick(); + buttondelay--; + } + bool changedmenu=true; + bool itemchanged=true; + void draw() override { + BeginTextureMode(target); + currentscene->draw(); + EndTextureMode(); + BeginDrawing(); + ClearBackground(rl::BLANK); + DrawTexturePro(target.texture, {0, 0, 600, -300}, {0, 0, 600, 300}, {0, 0}, 0, rl::WHITE); + EndDrawing(); + Vector2 mp=Vector2(GetMousePositionDesktop()); + Vector2 wp=GetWindowPosition(); + Vector2 rp=Vector2{mp.x-wp.x,mp.y-wp.y}; + if (rp.x>=0&&rp.y>=0&&rp.x<600&&rp.y<300) { + if (changedmenu) { + UnloadImage(img); + img=LoadImageFromTexture(target.texture); + changedmenu=false; + } + Color pix=GetImageColor(img, (int)rp.x, (int)(300-rp.y)); + if (pix.a==0) { + SetWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH); + captured=false; + } else { + ClearWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH); + captured=true; + } + } + } + void exit() override { + currentscene->exit(); + } +}; + +launcher* launch; +tiny::ErrorLevel tiny::level{8}; + +int main(int argc, char *argv[]) { + tiny::startup((char*)"enginend test",(char*)"1.0"); + tiny::echo ("checking if cfg.tdf exists, if not create"); + std::ofstream tdffile("cfg.tdf"); + tdffile.close(); + + tiny::echo ("read cfg.tdf"); + tiny::TDF_FILE config;config.filepath="cfg.tdf";config.read(); + std::string version; + tiny::echo ("getting version"); + try {version=config.getstring({"version"});} + catch (tiny::TDF_ERR e) {version="NULL";} + if (version!="NULL") { + nete.currentversion=version; + config.setstring({"version"},nete.currentversion); + } + launcher e; + launch=&e; + tiny::echo ("starting net"); + nete.start(); + tiny::echo ("downloading github info"); + nete.github(); + tiny::echo ("starting launcher"); + e.boot(); + std::atomic running{true}; + + std::thread tickthread([&e, &running]() { + double tickrate=1.0/e.tickrate; + auto lasttick=std::chrono::high_resolution_clock::now(); + + while (running) { + auto now=std::chrono::high_resolution_clock::now(); + double elapsed=std::chrono::duration_cast>(now-lasttick).count(); + + if (elapsed>=tickrate) { + e.tick(); + lasttick=now; + } else { + std::this_thread::sleep_for(std::chrono::duration(tickrate-elapsed)); + } + } + }); + + double framerate=1.0/e.framerate; + auto lastframe=std::chrono::high_resolution_clock::now(); + + while (!WindowShouldClose()) { + auto now=std::chrono::high_resolution_clock::now(); + double elapsed=std::chrono::duration_cast>(now-lastframe).count(); + + if (elapsed>=framerate) { + e.draw(); + lastframe=now; + } else { + WaitTime(framerate-elapsed); + } + } + + running=false; + tickthread.join(); + + e.exit(); + config.setstring({"version"},nete.currentversion); + config.save(); + config.close(); + return 0; +} +void playbuttonfunc() { + if (nete.currentversion!="NULL") { + FILE* gameexe = fopen(("forespend/versions/"+nete.currentversion+"/bin/game").c_str(), "r"); + if (gameexe) { + fclose(gameexe); + + std::thread thread([]() { + boost::process::system("bin/game",boost::process::start_dir=("forespend/versions/"+nete.currentversion)); + }); + exit(0); + } + + } +} diff --git a/games/endlauncher/src/netio.cpp b/games/endlauncher/src/netio.cpp new file mode 100644 index 0000000..a0175ef --- /dev/null +++ b/games/endlauncher/src/netio.cpp @@ -0,0 +1,5 @@ +#include "netio.h" +size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) { + size_t written = fwrite(ptr, size, nmemb, stream); + return written; +}; \ No newline at end of file diff --git a/games/endlauncher/src/netio.h b/games/endlauncher/src/netio.h new file mode 100644 index 0000000..335d074 --- /dev/null +++ b/games/endlauncher/src/netio.h @@ -0,0 +1,229 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#ifndef GAMEPLATFORM +#define GAMEPLATFORM "l5.64" +#endif +extern size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream); +inline void makedirs(std::string path) { + size_t pos = 0; + while ((pos = path.find_first_of('/', pos)) != std::string::npos) { + std::string dir = path.substr(0, pos); + if (!dir.empty()) { +#ifdef _WIN32 + mkdir(dir.c_str(), 0); +#else + mkdir(dir.c_str(), 0755); +#endif + } + pos++; + } +} +struct netio { + netio()= default; + std::string url="https://github.com/kin-fuyuki/forespend_cpp/releases/download/"; + std::vector *versionhistory; + std::string changelog="could not connect to github\nplease check your internet connection"; + std::string currentversion="NULL"; + std::string newestversion=""; + + void start() { + const int MAX_RETRIES = 3; + int attempts = 0; + bool download_success = false; + + while (attempts < MAX_RETRIES && !download_success) { + CURL* curltdf = curl_easy_init(); + if (curltdf) { + FILE* history = fopen("versions.tdf", "wb"); + if (!history) break; + + curl_easy_setopt(curltdf, CURLOPT_URL, "https://raw.githubusercontent.com/kin-fuyuki/allgames/refs/heads/master/versions.tdf"); + curl_easy_setopt(curltdf, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curltdf, CURLOPT_WRITEDATA, history); + + CURLcode ret = curl_easy_perform(curltdf); + fclose(history); + curl_easy_cleanup(curltdf); + + if (ret == CURLE_OK) { + download_success = true; + } else { + attempts++; + tiny::warning("Version fetch failed (Attempt %d/%d): %s", attempts, MAX_RETRIES, curl_easy_strerror(ret)); + } + } + } + + if (!download_success) { + tiny::fatal("Could not download versions.tdf after %d attempts.", MAX_RETRIES); + return; + } + + // Processing the file + tiny::TDF_FILE fetchversions; + fetchversions.filepath = "versions.tdf"; + fetchversions.read(); + + boost::unordered_map* foreversions = fetchversions.getclass({"forespend"}); + if (!foreversions) return; + + std::vector versions; + for (auto& version : *foreversions) { + if (version.second.type == tiny::TDF_Type::TDF_DEFINES) { + versions = *(std::vector*)version.second.datapointer; + } + } + + if (versions.empty()) return; + + std::sort(versions.begin(), versions.end(), [](const std::string& a, const std::string& b) { + int ra = 0, rb = 0; + float va = 0.0f, vb = 0.0f; + char pa = ' ', pb = ' '; + char rea = ' ', reb = ' '; + sscanf(a.c_str(), "%d.%f.%c.%c", &ra, &va, &pa, &rea); + sscanf(b.c_str(), "%d.%f.%c.%c", &rb, &vb, &pb, &reb); + if (ra != rb) return ra > rb; + if (va != vb) return va > vb; + if (pa != pb) return pa > pb; + return rea < reb; + }); + + this->newestversion = versions[0]; // Sort is descending, index 0 is newest + if (this->newestversion != "") { + this->currentversion = newestversion; + } + + tiny::success("\n\nforespend versions found:"); + for (auto& version : versions) { + tiny::warning("%s", version.c_str()); + } + + // Use a member or persistent copy to avoid pointer to local variable + static std::vector persistent_versions; + persistent_versions = versions; + versionhistory = &persistent_versions; +} + void github() { + CURL* curl; + CURLcode res; + FILE* commitsjson = fopen("commitsjson", "wb"); + curl = curl_easy_init(); + if (curl) { + std::string u = "https://api.github.com/repos/kin-fuyuki/allgames/commits"; + struct curl_slist* headers = NULL; + headers = curl_slist_append(headers, "User-Agent: C++-App"); // Required by GitHub + curl_easy_setopt(curl, CURLOPT_URL, u.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, commitsjson); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L); + res = curl_easy_perform(curl); + fclose(commitsjson); + changelog="changelog download error"; + if (res == CURLE_OK) { + try { + changelog="json read error"; + FILE* thecommits=fopen("commitsjson","rb"); + changelog="json parse error"; + nlohmann::json commits = nlohmann::json::parse(thecommits); + std::ostringstream oss; + std::vector monthNames = { + "", "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec" + }; + std::string lastMonth = ""; + std::string currentYear = ""; + int limit = std::min((int)commits.size(), 8); + for (int i = 0; i < limit; ++i) { + auto& item = commits[i]; + std::string message = item["commit"]["message"]; + std::string date = item["commit"]["author"]["date"]; // Format: 2025-12-21T13:08:50Z + // Extract components: YYYY-MM-DD and HH:MM:SS + std::string y = date.substr(0, 4); + std::string m = date.substr(5, 2); + std::string d = date.substr(8, 2); + std::string t = date.substr(11, 8); + // Format: MM/DD/YYYY HH:MM:SS + std::string first_line = message.substr(0, message.find('\n')); + oss << m << "/" << d << "/" << y << " " << t << "\n" << " " << first_line << "\n"; + } + changelog = oss.str(); + tiny::fatal("%s", changelog.c_str() ); + } catch (nlohmann::json::parse_error& e) { + tiny::fatal("JSON Parse Error: %s", e.what()); + } + } + + } + } + void download() { + CURLcode ret; + std::string version=versionhistory->at(0); + tiny::success("using version: %s\ndownloading from: %s",version.c_str(),(url+version+"/"+GAMEPLATFORM+"."+version+".zip").c_str()); + + CURL*curl = curl_easy_init(); + FILE* file = fopen(("fore."+version+".zip").c_str(), "wb"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L); + curl_easy_setopt(curl, CURLOPT_URL, "https://github.com/kin-fuyuki/forespend_cpp/releases/download/0.03.a.g/l5.64.0.03.a.g.zip"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); + ret = curl_easy_perform(curl); + curl_easy_cleanup(curl); + fclose(file); + + + makedirs("forespend/versions/"+version+"/"); + int err=0; + zip *z=zip_open("fore.0.03.a.g.zip",0,&err); + if (z) { + + zip_int64_t it=zip_get_num_entries(z, 0); + for (zip_int64_t i = 0; i < it; ++i) { + struct zip_stat st; + zip_stat_init(&st); + if (zip_stat_index(z, i, 0, &st) != 0) continue; + std::string name = st.name; + std::string full = "forespend/versions/"+version+"/" + name; + if (name.back() == '/') { +#ifdef _WIN32 + mkdir(full.c_str(), 0); +#else + mkdir(full.c_str(), 0755); +#endif + continue; + } + size_t pos = full.find_last_of('/'); + if (pos != std::string::npos) { + std::string dir = full.substr(0, pos); +#ifdef _WIN32 + mkdir(dir.c_str(), 0); +#else + mkdir(dir.c_str(), 0755); +#endif + } + zip_file_t *zf = zip_fopen_index(z, i, 0); + if (zf) { + std::ofstream ofs(full, std::ios::binary); + char buff[1 << 16]; + zip_int64_t read; + while ((read = zip_fread(zf, buff, sizeof(buff))) > 0) { + ofs.write(buff, read); + } + ofs.close(); + zip_fclose(zf); + } + } + zip_close(z); + } + } + +}; \ No newline at end of file