Просмотр исходного кода

android: Use enough zip64 format info to see APKs with files that are > 4gb.

(cherry picked from commit 40e60452ade1c82c3ce728ce4631f8824182f609)
Ryan C. Gordon 2 недель назад
Родитель
Сommit
79dacf6630
1 измененных файлов с 40 добавлено и 7 удалено
  1. 40 7
      src/core/android/SDL_android.c

+ 40 - 7
src/core/android/SDL_android.c

@@ -2023,6 +2023,7 @@ static SDL_Time ZipDosTimeToSDLTime(Uint32 dostime)
 #define ZIP_END_OF_CENTRAL_DIR_SIG                  0x06054b50
 #define ZIP64_END_OF_CENTRAL_DIR_SIG                0x06064b50
 #define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG  0x07064b50
+#define ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG         0x0001
 
 static bool ProcessZip(SDL_IOStream *io, APKNode *root)
 {
@@ -2037,7 +2038,7 @@ static bool ProcessZip(SDL_IOStream *io, APKNode *root)
         return false;
     }
 
-    //bool zip64 = false;
+    bool zip64 = false;
     Sint64 centraldir = -1;
     Uint64 num_entries = 0;
     Uint16 val16 = 0;
@@ -2079,7 +2080,7 @@ static bool ProcessZip(SDL_IOStream *io, APKNode *root)
             goto ioerr;
         }
 
-        //zip64 = true;
+        zip64 = true;
         centraldir = (Sint64) val64;
     } else if (SDL_SeekIO(io, eocd + 4 + 6, SDL_IO_SEEK_SET) < 0) {  // skip back to where we were, plus skip some fields we don't care about.
         goto ioerr;
@@ -2102,7 +2103,8 @@ static bool ProcessZip(SDL_IOStream *io, APKNode *root)
         Uint16 extralen = 0;
         Uint16 commentlen = 0;
         Uint32 dosmodtime = 0;
-        Uint32 uncompressed = 0;
+        Uint32 uncompressed32 = 0;
+        Uint64 uncompressed64 = 0;
 
         // we don't care about most of this information, just parse through it to get what we need.
         if (SDL_SeekIO(io, centraldir, SDL_IO_SEEK_SET) < 0) {
@@ -2125,7 +2127,7 @@ static bool ProcessZip(SDL_IOStream *io, APKNode *root)
             goto ioerr;
         } else if (!SDL_ReadU32LE(io, &val32)) {  // compressed size
             goto ioerr;
-        } else if (!SDL_ReadU32LE(io, &uncompressed)) {  // uncompressed size
+        } else if (!SDL_ReadU32LE(io, &uncompressed32)) {  // uncompressed size
             goto ioerr;
         } else if (!SDL_ReadU16LE(io, &fnamelen)) {  // filename length
             goto ioerr;
@@ -2148,14 +2150,45 @@ static bool ProcessZip(SDL_IOStream *io, APKNode *root)
             goto ioerr;
         }
 
-        // !!! FIXME: parse out the extralen section for zip64 file sizes; needed if a file is > 4 gigabytes.
-
         // technically zip files might have '\\' dir separators, but these were mostly old DOS files and not Android APKs, I think. Revisit if necessary.
 
         fnamebuf[fnamelen] = '\0';  // make sure the string is null-terminated.
 
         //SDL_Log("ANDROID: Saw ZIP entry '%s'", fnamebuf);
 
+        uncompressed64 = (Uint64) uncompressed32;
+        if (zip64 && (uncompressed32 == 0xFFFFFFFF)) {  // file is larger than 4gig, find the zip64 extended info field in the extra section.
+            bool found = false;
+            Uint16 remaining = extralen;
+            while (remaining > 4) {  // Two 16-bit values at a minimum, tag and len.
+                Uint16 tag, len;
+                if (!SDL_ReadU16LE(io, &tag) || !SDL_ReadU16LE(io, &len)) {
+                    goto ioerr;
+                } else if (remaining < (len + 4)) {
+                    goto corrupterr;
+                } else if (tag != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG) {  // not the field we need, skip over it.
+                    if (SDL_SeekIO(io, (Sint64) len, SDL_IO_SEEK_CUR) < 0) {
+                        goto ioerr;
+                    }
+                } else if (len < 8) {
+                    goto corrupterr;
+                } else if ((uncompressed32 == 0xFFFFFFFF) && !SDL_ReadU64LE(io, &uncompressed64)) {
+                    goto ioerr;
+
+                // there are other values in here, but we don't care about them and we're done, so don't try to skip over them.
+
+                } else {
+                    found = true;
+                    break;  // got what we need, drop out.
+                }
+                remaining -= len + 4;
+            }
+
+            if (!found) {
+                goto corrupterr;
+            }
+        }
+
         char *ptr = fnamebuf;
         while (*ptr == '/') {  // drop absolute paths.
             ptr++;
@@ -2180,7 +2213,7 @@ static bool ProcessZip(SDL_IOStream *io, APKNode *root)
                     goto ioerr;  // (probably out of memory.)
                 }
                 node->info.type = SDL_PATHTYPE_FILE;
-                node->info.size = (Uint64) uncompressed;
+                node->info.size = uncompressed64;
             }
             node->info.create_time = node->info.modify_time = node->info.access_time = modtime;
         }