Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 137 additions & 8 deletions thunks/zlib/zlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ typedef struct AAsset_impl{
zip_int64_t index;
zip_file_t *file;
int mode;
off_t current_offset;
bool persistent_read;
} AAsset_impl;

/*
Expand Down Expand Up @@ -70,6 +72,9 @@ ABI_ATTR void *AAssetManager_open_impl(void *mgr, const char *filename, int mode
AAsset_impl* asset = (AAsset_impl*)malloc(sizeof(AAsset_impl));
asset->mgr = asset_mgr;
asset->mode = mode;
asset->persistent_read = false;
asset->file = NULL;
asset->current_offset = 0;

if(!asset_mgr->apk)
{
Expand Down Expand Up @@ -116,26 +121,149 @@ ABI_ATTR off_t AAsset_read_impl(void *f, void *buf, size_t count)
{
AAsset_impl* asset = (AAsset_impl*)f;
int nread;
if((asset->file = zip_fopen_index(asset->mgr->apk, asset->index, ZIP_FL_UNCHANGED)) == NULL)
{
fprintf(stderr, "%s: cannot open the file (index = %ld)\n",__func__, asset->index);
return 0;

// Games that don't use seek need to reopen the file for every read
if (!asset->persistent_read || !asset->file) {

if((asset->file = zip_fopen_index(asset->mgr->apk, asset->index, ZIP_FL_UNCHANGED)) == NULL)
{
fprintf(stderr, "%s: cannot open the file (index = %ld)\n", __func__, asset->index);
return 0;
}
}

if((nread=zip_fread(asset->file, buf, count)) == -1)
{
fprintf(stderr, "%s: cannot read the file\n",__func__);
fprintf(stderr, "%s: cannot read the file\n", __func__);
return 0;
}


asset->current_offset += nread;

if (!asset->persistent_read && nread < count)
{
zip_fclose(asset->file);
asset->file = NULL;
}

return nread;
}

ABI_ATTR void AAsset_close_impl(void *f)
{
AAsset_impl* asset = (AAsset_impl*)f;
free(asset->file);
free(asset);
if (asset) {
if (asset->file) {
zip_fclose(asset->file);
asset->file = NULL;
}
free(asset);
}
}

ABI_ATTR off_t AAsset_seek_impl(void* asset_ptr, off_t offset, int whence)
{
AAsset_impl* asset = (AAsset_impl*)asset_ptr;
asset->persistent_read = true;

if (!asset) {
fprintf(stderr, "%s: asset not initialized\n", __func__);
return -1;
}

// Get file size for validation
zip_stat_t stt = {};
zip_stat_init(&stt);
if (zip_stat_index(asset->mgr->apk, asset->index, ZIP_STAT_SIZE, &stt) == -1) {
fprintf(stderr, "%s: cannot get file size for index %ld\n", __func__, asset->index);
return -1;
}
off_t file_size = stt.size;

// If file is open, ensure it matches the current index
if (asset->file) {
zip_file_t* new_file = zip_fopen_index(asset->mgr->apk, asset->index, ZIP_FL_UNCHANGED);
if (!new_file || new_file != asset->file) {
fprintf(stderr, "%s: file handle mismatch, resetting (index = %ld)\n", __func__, asset->index);
zip_fclose(asset->file);
asset->file = NULL;
asset->current_offset = 0; // Reset offset on mismatch
} else {
zip_fclose(new_file); // Close temp handle, we don’t need it
}
}

// Open file if not already open
if (!asset->file) {
asset->file = zip_fopen_index(asset->mgr->apk, asset->index, ZIP_FL_UNCHANGED);
if (!asset->file) {
fprintf(stderr, "%s: cannot open file for seeking (index = %ld)\n", __func__, asset->index);
return -1;
}
asset->current_offset = 0;
}

// Calculate new offset
off_t new_offset;
switch (whence) {
case SEEK_SET:
new_offset = offset;
break;
case SEEK_CUR:
new_offset = asset->current_offset + offset;
break;
case SEEK_END:
new_offset = file_size + offset;
break;
default:
fprintf(stderr, "%s: invalid whence value %d\n", __func__, whence);
return -1;
}

// Validate new offset
if (new_offset < 0 || new_offset > file_size) {
fprintf(stderr, "%s: seek offset %ld out of bounds (size=%ld)\n",
__func__, new_offset, file_size);
return -1;
}

// libzip doesn't support direct seeking, so we need to:
// 1. Close the current file handle
// 2. Reopen and read/skip to the desired position
if (asset->file) {
zip_fclose(asset->file);
}

// Reopen the file
asset->file = zip_fopen_index(asset->mgr->apk, asset->index, ZIP_FL_UNCHANGED);
if (!asset->file) {
fprintf(stderr, "%s: cannot reopen file for seeking (index = %ld)\n",
__func__, asset->index);
return -1;
}

// If not seeking to start, skip bytes to reach target position
if (new_offset > 0) {
char buffer[1024];
off_t remaining = new_offset;

while (remaining > 0) {
size_t to_read = remaining < sizeof(buffer) ? remaining : sizeof(buffer);
zip_int64_t nread = zip_fread(asset->file, buffer, to_read);

if (nread <= 0) {
fprintf(stderr, "%s: failed to seek to position %ld\n", __func__, new_offset);
zip_fclose(asset->file);
asset->file = NULL;
return -1;
}
remaining -= nread;
}
}

// Update the tracked offset and return it
asset->current_offset = new_offset;
return new_offset;
}

DynLibFunction symtable_zlib[] = {
Expand All @@ -158,6 +286,7 @@ DynLibFunction symtable_zlib[] = {
NO_THUNK("AAsset_getLength", (uintptr_t)&AAsset_getLength_impl),
NO_THUNK("AAsset_read", (uintptr_t)&AAsset_read_impl),
NO_THUNK("AAsset_close", (uintptr_t)&AAsset_close_impl),
NO_THUNK("AAsset_seek", (uintptr_t)&AAsset_seek_impl),

{NULL, (uintptr_t)NULL}
};