From ed180e9279c432e8eeeb52c80b638cdfc2900e02 Mon Sep 17 00:00:00 2001 From: Alexis He Date: Thu, 26 Aug 2021 01:10:28 +0200 Subject: [PATCH] Add: Android pak installat via native code instead of shell script --- .github/workflows/nightly-android.yml | 12 +-- gui/pakinstaller.cc | 128 ++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 11 deletions(-) diff --git a/.github/workflows/nightly-android.yml b/.github/workflows/nightly-android.yml index e1863b57d..d4091eecb 100644 --- a/.github/workflows/nightly-android.yml +++ b/.github/workflows/nightly-android.yml @@ -1,9 +1,5 @@ name: Nightly build Android -env: - DEFAULT_PAKSETLINK: 'https://downloads.sourceforge.net/project/simutrans/pak64/122-0/simupak64-122-0.zip' - DEFAULT_PAKSETFILE: 'simupak64-122-0.zip' - on: push: @@ -78,7 +74,7 @@ jobs: - name: Checkout lib Android SDL by pelya (hard coded version) working-directory: /android-sdl run: | - git init && git remote add origin https://github.com/krosk/commandergenius.git && git fetch origin 4bc45825086deecb8f55f424d9125591d0e7a59b && git reset --hard FETCH_HEAD + git init && git remote add origin https://github.com/krosk/commandergenius.git && git fetch origin 0a516df844a2ffba02a81ecadd2849f957b38180 && git reset --hard FETCH_HEAD - name: Setup licenses for Gradle working-directory: /android-sdl @@ -93,12 +89,6 @@ jobs: svn checkout svn://servers.simutrans.org/simutrans/trunk project/jni/application/simutrans/simutrans # End of environment setup (dependencies installed, source code at correct location) - # Start of package customization - - name: Add default pakset - working-directory: /android-sdl - run: | - wget ${{ github.event.inputs.paksetLink || env.DEFAULT_PAKSETLINK }} - unzip ./${{ github.event.inputs.paksetFile || env.DEFAULT_PAKSETFILE }} -d project/jni/application/simutrans/simutrans/ # some temporary changes to Android SDL are stored in /.github/android - name: (Temporary) Patch Android SDL and replace version diff --git a/gui/pakinstaller.cc b/gui/pakinstaller.cc index d21c23d2d..c642b93a5 100644 --- a/gui/pakinstaller.cc +++ b/gui/pakinstaller.cc @@ -53,6 +53,7 @@ pakinstaller_t::pakinstaller_t() : /** * This method is called if an action is triggered */ +#ifndef __ANDROID__ bool pakinstaller_t::action_triggered(gui_action_creator_t*, value_t) { // now install @@ -88,3 +89,130 @@ bool pakinstaller_t::action_triggered(gui_action_creator_t*, value_t) finish_install = true; return false; } +#else +#include +#include +#include + +static void read_zip(const char* outfilename) { + zip_t * zip_archive; + int err; + + if ((zip_archive = zip_open(outfilename, ZIP_RDONLY, &err)) == NULL ) { + zip_error_t error; + zip_error_init_with_code(&error, err); + dbg->debug(__FUNCTION__, "cannot open zip archive: %s", zip_error_strerror(&error)); + zip_error_fini(&error); + } + + dbg->debug(__FUNCTION__, "zip archive opened"); + + zip_uint64_t nentry = (zip_uint64_t) zip_get_num_entries(zip_archive, 0); + for (zip_uint64_t idx = 0; idx < nentry; idx++) { + struct zip_stat st; + if (zip_stat_index(zip_archive, idx, 0, &st) == -1) { + dbg->debug(__FUNCTION__, "cannot read file stat %d in zip archive: %s", idx, zip_strerror(zip_archive)); + continue; + } + + zip_file_t *zip_file; + if ((zip_file = zip_fopen_index(zip_archive, idx, 0)) == NULL) { + dbg->debug(__FUNCTION__, "cannot open file %s (index %d) in zip_archive: %s", st.name, idx, zip_strerror(zip_archive)); + continue; + } + + char *contents = new char[st.size]; + zip_int64_t read_size; + if ((read_size = zip_fread(zip_file, contents, st.size)) == -1) { + dbg->debug(__FUNCTION__, "failed to read content in file %s (index %d) of zip_archive: %s", st.name, idx, zip_file_strerror(zip_file)); + } + else { + if (read_size != (zip_int64_t) st.size) { + dbg->debug(__FUNCTION__, "unexpected content size in file %s (index %d) of zip_archive; is %d, should be %d: %s", st.name, idx, read_size, st.size); + } + else { + // example of path: + // simutrans/pak64.german/tool/check_passenger_stops/cursor.script_tool_check_pass.pak + // For android platform, the simutrans/ at the start must be removed. +#ifdef __ANDROID__ + const char* target_filename = st.name + 10; +#else + const char* target_filename = st.name; +#endif + + char extracted_path[FILENAME_MAX]; + sprintf(extracted_path, "%s%s", env_t::data_dir, target_filename); + + dbg->debug(__FUNCTION__, "- %s: %d", extracted_path, st.size); + if (st.size == 0) { // likely a directory + cbuffer_t param; + param.append("mkdir "); + param.append(extracted_path); + const int retval = system( param ); + dbg->debug(__FUNCTION__, "Creating folder %s, ret %d", extracted_path, retval); + } + else if(!std::ofstream(extracted_path).write(contents, st.size)) { + dbg->debug(__FUNCTION__, "Error writing file"); + } + } + } + zip_fclose(zip_file); + } + + if (zip_close(zip_archive) == -1) { + dbg->debug(__FUNCTION__, "cannot close zip archive: %s", zip_strerror(zip_archive)); + } +} + +static size_t curl_write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) { + size_t written = fwrite(ptr, size, nmemb, stream); + return written; +} + +static CURLcode curl_download_file(CURL *curl, const char* target_file, const char* url) { + FILE *fp = fopen(target_file,"wb"); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + res = curl_easy_perform(curl); + fclose(fp); + return res; +} + +// native download (curl), extract (libzip) +bool pakinstaller_t::action_triggered(gui_action_creator_t*, value_t) +{ + CURL *curl = curl_easy_init(); // can only be called once during program lifecycle + if (curl) { + dr_chdir( env_t::data_dir ); + dbg->debug(__FUNCTION__, "libcurl initialized"); + + char outfilename[FILENAME_MAX]; + FOR(vector_tpl, i, paks.get_selections()) { + sprintf(outfilename, "%s%s.zip", env_t::data_dir, pakinfo[i*2 + 1]); + + CURLcode res = curl_download_file(curl, outfilename, pakinfo[i*2]); + dbg->debug(__FUNCTION__, "pak target %s", pakinfo[i*2]); + + if (res == 0) { + dbg->debug(__FUNCTION__, "download successful to %s, attempting extract", outfilename); + read_zip(outfilename); + } else { + dbg->debug(__FUNCTION__, "download failed with error code %s, check curl errors; skipping", curl_easy_strerror(res)); + } + } + curl_easy_cleanup(curl); + } + else { + dbg->debug(__FUNCTION__, "libcurl failed to initialize, pakset not downloaded"); + } + + finish_install = true; + return false; +} +#endif -- 2.28.0.windows.1