diff --git a/CMakeLists.txt b/CMakeLists.txt index 074cdb2..57a68c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ find_package(CURL REQUIRED) option(PKGI_ENABLE_DEBUG "enables debug logging over udp multicast" OFF) if(PKGI_ENABLE_DEBUG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPKGI_ENABLE_LOGGING") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPKGI_ENABLE_LOGGING=1") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} source/loadpng.c source/libfont.c source/ttf_fonts.c + source/zip_util.c source/pkgi.c source/pkgi_aes.c source/pkgi_db.c @@ -81,6 +82,7 @@ target_link_libraries(${PROJECT_NAME} pspnet pspnet_apctl mini18n + zip bz2 z ) diff --git a/include/font-8x16.h b/include/font-8x16.h index 9b7fdfd..abb01ee 100644 --- a/include/font-8x16.h +++ b/include/font-8x16.h @@ -1,5 +1,5 @@ // https://github.com/idispatch/raster-fonts -unsigned char console_font_8x16[] = { +static const unsigned char console_font_8x16[] = { /* * code=0, hex=0x00, ascii="^@" diff --git a/include/pkgi.h b/include/pkgi.h index 05b21f3..17372d0 100644 --- a/include/pkgi.h +++ b/include/pkgi.h @@ -5,7 +5,7 @@ #include "pkgi_dialog.h" #define PKGI_UPDATE_URL "https://api.github.com/repos/bucanero/pkgi-psp/releases/latest" -#define PKGI_VERSION "1.0.0" +#define PKGI_VERSION "1.1.0" #define PKGI_BUTTON_SELECT 0x000001 #define PKGI_BUTTON_START 0x000008 @@ -17,10 +17,10 @@ #define PKGI_BUTTON_LT 0x000100 // L1 #define PKGI_BUTTON_RT 0x000200 // R1 -#define PKGI_BUTTON_X 0x004000 // cross -#define PKGI_BUTTON_O 0x002000 // circle -#define PKGI_BUTTON_T 0x001000 // triangle -#define PKGI_BUTTON_S 0x008000 // square +#define PKGI_BUTTON_X 0x004000 // cross +#define PKGI_BUTTON_O 0x002000 // circle +#define PKGI_BUTTON_T 0x001000 // triangle +#define PKGI_BUTTON_S 0x008000 // square #define PKGI_UNUSED(x) (void)(x) @@ -73,7 +73,7 @@ int pkgi_is_incomplete(const char* titleid); int pkgi_is_installed(const char* titleid); int pkgi_install(int iso_mode, int remove_pkg); -uint32_t pkgi_time_msec(); +uint32_t pkgi_time_msec(void); typedef void pkgi_thread_entry(void); void pkgi_start_thread(const char* name, pkgi_thread_entry* start); diff --git a/include/pkgi_download.h b/include/pkgi_download.h index 723f9c9..d47b9ee 100644 --- a/include/pkgi_download.h +++ b/include/pkgi_download.h @@ -12,3 +12,4 @@ void progress_screen_refresh(void); void update_install_progress(const char *filename, int64_t progress); int install_psp_pkg(const char *file); int convert_psp_pkg_iso(const char* pkg_arg, int cso); +int extract_zip(const char* zip_file); diff --git a/source/loadpng.c b/source/loadpng.c index 4cbb5e7..336eaf7 100644 --- a/source/loadpng.c +++ b/source/loadpng.c @@ -141,7 +141,7 @@ static rawImage_t *imgLoadPngFromBuffer(const void *buffer) if(png_sig_cmp((png_byte *)buffer, 0, PNG_SIGSIZE) != 0) return NULL; - uint64_t buffer_address=(uint64_t)buffer+PNG_SIGSIZE; + uint64_t buffer_address=(uint32_t)buffer+PNG_SIGSIZE; return imgLoadPngGeneric((void *)&buffer_address, imgReadPngFromBuffer); } diff --git a/source/pkgi.c b/source/pkgi.c index 4b1e871..e700d7f 100644 --- a/source/pkgi.c +++ b/source/pkgi.c @@ -711,6 +711,7 @@ int main(int argc, char* argv[]) pkgi_load_config(&config, (char*) &refresh_url, sizeof(refresh_url[0])); pkgi_load_language(config.language); + pkgi_is_psp_go(config.storage); pkgi_dialog_init(); font_height = pkgi_text_height("M"); diff --git a/source/pkgi_download.c b/source/pkgi_download.c index 55232ed..128ac05 100644 --- a/source/pkgi_download.c +++ b/source/pkgi_download.c @@ -312,11 +312,17 @@ static int create_rap(const char* contentid, const uint8_t* rap) return 1; } +static int is_zip(const char* filename) +{ + const char* extension = pkgi_strrchr(filename, '.'); + return extension && (pkgi_stricmp(extension, ".zip") == 0); +} + int pkgi_download(const DbItem* item) { int result = 0; - pkgi_snprintf(root, sizeof(root), "%s.pkg", item->content); + pkgi_snprintf(root, sizeof(root), "%s.%s", item->content, is_zip(item->url) ? "zip" : "pkg"); LOG("package installation file: %s", root); pkgi_snprintf(resume_file, sizeof(resume_file), "%s%s/%s.resume", pkgi_get_storage_device(), pkgi_get_temp_folder(), item->content); @@ -385,7 +391,12 @@ int pkgi_install(int iso_mode, int remove_pkg) download_offset = 0; total_size = download_size; - result = iso_mode ? convert_psp_pkg_iso(item_path, (iso_mode == 2)) : install_psp_pkg(item_path); + // check if it's a zip file + if (is_zip(item_path)) + result = extract_zip(item_path); + else + result = iso_mode ? convert_psp_pkg_iso(item_path, (iso_mode == 2)) : install_psp_pkg(item_path); + if (result && remove_pkg) { pkgi_rm(item_path); diff --git a/source/pkgi_psp.c b/source/pkgi_psp.c index cf237af..0e9c5bb 100644 --- a/source/pkgi_psp.c +++ b/source/pkgi_psp.c @@ -74,6 +74,7 @@ typedef struct char * strcasestr(const char *haystack, const char *needle); +static char disp_list[0x10000] __attribute__((aligned(64))); static SceLwMutexWorkarea g_dialog_lock; static int g_ok_button; @@ -399,12 +400,8 @@ void pkgi_dialog_input_text(const char* title, const char* text) if (sceUtilityOskInitStart(¶ms) < 0) return; - void* list = aligned_alloc(16, 0x100000); - if (!list) - return; - do { - sceGuStart(GU_DIRECT, list); + sceGuStart(GU_DIRECT, disp_list); sceGuClearColor(0xFF68260D); sceGuClearDepth(0); sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT); @@ -427,8 +424,6 @@ void pkgi_dialog_input_text(const char* title, const char* text) sceGuSwapBuffers(); } while (done != PSP_UTILITY_DIALOG_FINISHED); - free(list); - if (data.result == PSP_UTILITY_OSK_RESULT_CANCELLED) return; @@ -487,13 +482,9 @@ static int Net_DisplayNetDialog(void) return 0; } - void* list = aligned_alloc(16, 0x100000); - if (!list) - return 0; - while(!done) { - sceGuStart(GU_DIRECT, list); + sceGuStart(GU_DIRECT, disp_list); sceGuClearColor(0xFF68260D); sceGuClearDepth(0); sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT); @@ -530,8 +521,6 @@ static int Net_DisplayNetDialog(void) sceDisplayWaitVblankStart(); sceGuSwapBuffers(); } - free(list); - done = PSP_NET_APCTL_STATE_DISCONNECTED; if ((ret = sceNetApctlGetState(&done)) < 0) { LOG("sceNetApctlGetState() failed: 0x%08x", ret); @@ -905,7 +894,7 @@ int pkgi_is_installed(const char* titleid) return (pkgi_dir_exists(path)); } -uint32_t pkgi_time_msec() +uint32_t pkgi_time_msec(void) { struct timeval tv; @@ -913,9 +902,9 @@ uint32_t pkgi_time_msec() return (((uint32_t)tv.tv_sec)*1000)+(tv.tv_usec/1000); } -void pkgi_thread_exit() +void pkgi_thread_exit(void) { - sceKernelExitThread(0); + sceKernelExitDeleteThread(0); } void pkgi_start_thread(const char* name, pkgi_thread_entry* start) diff --git a/source/zip_util.c b/source/zip_util.c new file mode 100644 index 0000000..50bc00a --- /dev/null +++ b/source/zip_util.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include + +#include "pkgi.h" +#include "pkgi_download.h" + +#define UNZIP_BUF_SIZE 0x20000 + +static inline uint64_t min64(uint64_t a, uint64_t b) +{ + return a < b ? a : b; +} + +int extract_zip(const char* zip_file) +{ + char path[256]; + uint8_t* buffer; + int64_t zsize = pkgi_get_size(zip_file); + struct zip* archive = zip_open(zip_file, ZIP_RDONLY | ZIP_CHECKCONS, NULL); + int files = zip_get_num_files(archive); + + if (files <= 0) { + LOG("Empty ZIP file."); + zip_close(archive); + return 0; + } + + buffer = malloc(UNZIP_BUF_SIZE); + if (!buffer) + return 0; + + LOG("Extracting %s to <%s>...", zip_file, dest_path); + + for (int i = 0; i < files; i++) { + const char* filename = zip_get_name(archive, i, 0); + + update_install_progress(filename, (zsize * i)/files); + LOG("Unzip [%d/%d] '%s'...", i+1, files, filename); + + if (!filename) + continue; + + if (filename[0] == '/') + filename++; + + if (strncasecmp(filename, "PSP/GAME/", 9) == 0) + filename += 9; + + snprintf(path, sizeof(path)-1, "%s/PSP/GAME/%s", pkgi_get_storage_device(), filename); + char* slash = strrchr(path, '/'); + *slash = 0; + pkgi_mkdirs(path); + *slash = '/'; + + if (filename[strlen(filename) - 1] == '/') + continue; + + struct zip_stat st; + if (zip_stat_index(archive, i, 0, &st)) { + LOG("Unable to access file %s in zip.", filename); + continue; + } + struct zip_file* zfd = zip_fopen_index(archive, i, 0); + if (!zfd) { + LOG("Unable to open file %s in zip.", filename); + continue; + } + + FILE* tfd = fopen(path, "wb"); + if(!tfd) { + free(buffer); + zip_fclose(zfd); + zip_close(archive); + LOG("Error opening temporary file '%s'.", path); + return 0; + } + + uint64_t pos = 0, count; + while (pos < st.size) { + count = min64(UNZIP_BUF_SIZE, st.size - pos); + if (zip_fread(zfd, buffer, count) != count) { + free(buffer); + fclose(tfd); + zip_fclose(zfd); + zip_close(archive); + LOG("Error reading from zip."); + return 0; + } + + fwrite(buffer, count, 1, tfd); + pos += count; + } + + zip_fclose(zfd); + fclose(tfd); + + update_install_progress(NULL, zsize * (i+1)/files); + } + + if (archive) { + zip_close(archive); + } + + update_install_progress(NULL, zsize); + free(buffer); + + return files; +}