1 package org.robolectric.res.android; 2 3 import static org.robolectric.res.android.Asset.toIntExact; 4 import static org.robolectric.res.android.Errors.NAME_NOT_FOUND; 5 import static org.robolectric.res.android.Errors.NO_ERROR; 6 import static org.robolectric.res.android.Util.ALOGW; 7 import static org.robolectric.res.android.Util.isTruthy; 8 9 import java.io.IOException; 10 import java.util.Enumeration; 11 import java.util.zip.ZipEntry; 12 import java.util.zip.ZipFile; 13 14 public class ZipFileRO { 15 16 static final int kCompressStored = 0; 17 static final int kCompressDeflated = 8; 18 19 final ZipArchiveHandle mHandle; 20 final String mFileName; 21 ZipFileRO(ZipArchiveHandle handle, String fileName)22 ZipFileRO(ZipArchiveHandle handle, String fileName) { 23 this.mHandle = handle; 24 this.mFileName = fileName; 25 } 26 27 static class ZipEntryRO { 28 ZipEntry entry; 29 String name; 30 Object cookie; 31 ZipEntryRO()32 ZipEntryRO() { 33 } 34 35 // ~ZipEntryRO() { 36 @Override finalize()37 protected void finalize() { 38 // EndIteration(cookie); 39 } 40 41 // private: 42 // ZipEntryRO(final ZipEntryRO& other); 43 // ZipEntryRO& operator=(final ZipEntryRO& other); 44 }; 45 46 // ~ZipFileRO() { 47 @Override finalize()48 protected void finalize() { 49 CloseArchive(mHandle); 50 // free(mFileName); 51 } 52 OpenArchive(String zipFileName, Ref<ZipArchiveHandle> mHandle)53 static int OpenArchive(String zipFileName, Ref<ZipArchiveHandle> mHandle) { 54 try { 55 mHandle.set(new ZipArchiveHandle(new ZipFile(zipFileName))); 56 return NO_ERROR; 57 } catch (IOException e) { 58 return NAME_NOT_FOUND; 59 } 60 } 61 CloseArchive(ZipArchiveHandle mHandle)62 private static void CloseArchive(ZipArchiveHandle mHandle) { 63 throw new UnsupportedOperationException(); 64 } 65 ErrorCodeString(int error)66 private static String ErrorCodeString(int error) { 67 return "error " + error; 68 } 69 FindEntry(ZipArchiveHandle mHandle, String name, Ref<ZipEntry> zipEntryRef)70 static int FindEntry(ZipArchiveHandle mHandle, String name, Ref<ZipEntry> zipEntryRef) { 71 ZipEntry entry = mHandle.zipFile.getEntry(name); 72 zipEntryRef.set(entry); 73 if (entry == null) { 74 return NAME_NOT_FOUND; 75 } 76 return NO_ERROR; 77 } 78 79 /* 80 * Open the specified file read-only. We memory-map the entire thing and 81 * close the file before returning. 82 */ 83 /* static */ open(final String zipFileName)84 static ZipFileRO open(final String zipFileName) 85 { 86 final Ref<ZipArchiveHandle> handle = new Ref<>(null); 87 final int error = OpenArchive(zipFileName, handle); 88 if (isTruthy(error)) { 89 ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error)); 90 CloseArchive(handle.get()); 91 return null; 92 } 93 94 return new ZipFileRO(handle.get(), zipFileName); 95 } 96 97 // /* static */ ZipFileRO* ZipFileRO::openFd(int fd, String debugFileName, 98 // boolean assume_ownership) 99 // { 100 // ZipArchiveHandle handle; 101 // int error = OpenArchiveFd(fd, debugFileName, &handle, assume_ownership); 102 // if (error) { 103 // ALOGW("Error opening archive fd %d %s: %s", fd, debugFileName, ErrorCodeString(error)); 104 // CloseArchive(handle); 105 // return NULL; 106 // } 107 // 108 // return new ZipFileRO(handle, strdup(debugFileName)); 109 // } 110 findEntryByName(final String entryName)111 org.robolectric.res.android.ZipFileRO.ZipEntryRO findEntryByName(final String entryName) 112 { 113 ZipEntryRO data = new ZipEntryRO(); 114 115 data.name = String(entryName); 116 117 final Ref<ZipEntry> zipEntryRef = new Ref<>(data.entry); 118 final int error = FindEntry(mHandle, data.name, zipEntryRef); 119 if (isTruthy(error)) { 120 return null; 121 } 122 123 data.entry = zipEntryRef.get(); 124 return data; 125 } 126 127 /* 128 * Get the useful fields from the zip entry. 129 * 130 * Returns "false" if the offsets to the fields or the contents of the fields 131 * appear to be bogus. 132 */ getEntryInfo(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry, Ref<Short> pMethod, final Ref<Long> pUncompLen, Ref<Long> pCompLen, Ref<Long> pOffset, final Ref<Long> pModWhen, Ref<Long> pCrc32)133 boolean getEntryInfo(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry, Ref<Short> pMethod, 134 final Ref<Long> pUncompLen, Ref<Long> pCompLen, Ref<Long> pOffset, 135 final Ref<Long> pModWhen, Ref<Long> pCrc32) 136 { 137 final ZipEntryRO zipEntry = /*reinterpret_cast<ZipEntryRO*>*/(entry); 138 final ZipEntry ze = zipEntry.entry; 139 140 if (pMethod != null) { 141 pMethod.set((short) ze.getMethod()); 142 } 143 if (pUncompLen != null) { 144 pUncompLen.set(ze.getSize()); // uncompressed_length 145 } 146 if (pCompLen != null) { 147 pCompLen.set(ze.getCompressedSize()); 148 } 149 if (pOffset != null) { 150 throw new UnsupportedOperationException("Figure out offset"); 151 // pOffset = ze.offset; 152 } 153 if (pModWhen != null) { 154 // todo pModWhen.set(ze.getLastModifiedTime().toMillis()); 155 } 156 if (pCrc32 != null) { 157 pCrc32.set(ze.getCrc()); 158 } 159 160 return true; 161 } 162 startIteration(Ref<Enumeration<? extends ZipEntry>> cookie)163 boolean startIteration(Ref<Enumeration<? extends ZipEntry>> cookie) { 164 return startIteration(cookie, null, null); 165 } 166 startIteration( Ref<Enumeration<? extends ZipEntry>> cookie, final String prefix, final String suffix)167 boolean startIteration(/* void** */ Ref<Enumeration<? extends ZipEntry>> cookie, final String prefix, final String suffix) 168 { 169 cookie.set(this.mHandle.zipFile.entries()); 170 // ZipEntryRO* ze = new ZipEntryRO; 171 // String pe(prefix ? prefix : ""); 172 // String se(suffix ? suffix : ""); 173 // int error = StartIteration(mHandle, &(ze.cookie), 174 // prefix ? &pe : null, 175 // suffix ? &se : null); 176 // if (error) { 177 // ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error)); 178 // delete ze; 179 // return false; 180 // } 181 // 182 // *cookie = ze; 183 return true; 184 } 185 nextEntry( Enumeration<? extends ZipEntry> cookie)186 org.robolectric.res.android.ZipFileRO.ZipEntryRO nextEntry(/*void* */ Enumeration<? extends ZipEntry> cookie) 187 { 188 if (!cookie.hasMoreElements()) { 189 return null; 190 } 191 ZipEntryRO zipEntryRO = new ZipEntryRO(); 192 zipEntryRO.entry = cookie.nextElement(); 193 return zipEntryRO; 194 // ZipEntryRO ze = /*reinterpret_cast<ZipEntryRO*>*/(ZipEntryRO) cookie; 195 // int error = Next(ze.cookie, &(ze.entry), &(ze.name)); 196 // if (error) { 197 // if (error != -1) { 198 // ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error)); 199 // } 200 // return null; 201 // } 202 // 203 // return &(ze.entry); 204 } 205 endIteration( Object cookie)206 void endIteration(/*void**/ Object cookie) 207 { 208 // delete reinterpret_cast<ZipEntryRO*>(cookie); 209 } 210 releaseEntry(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry)211 void releaseEntry(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry) 212 { 213 // delete reinterpret_cast<ZipEntryRO*>(entry); 214 } 215 216 /* 217 * Copy the entry's filename to the buffer. 218 */ getEntryFileName(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry, Ref<String> buffer)219 int getEntryFileName(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry, Ref<String> buffer) 220 { 221 buffer.set(entry.entry.getName()); 222 223 // final ZipEntryRO* zipEntry = reinterpret_cast<ZipEntryRO*>(entry); 224 // final uint16_t requiredSize = zipEntry.name.name_length + 1; 225 // 226 // if (bufLen < requiredSize) { 227 // ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize); 228 // return requiredSize; 229 // } 230 // 231 // memcpy(buffer, zipEntry.name.name, requiredSize - 1); 232 // buffer[requiredSize - 1] = '\0'; 233 // 234 return 0; 235 } 236 237 /* 238 * Create a new FileMap object that spans the data in "entry". 239 */ ZipFileRO(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry)240 /*FileMap*/ ZipFileRO(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry) 241 { 242 throw new UnsupportedOperationException("Implememnt me"); 243 244 // final ZipEntryRO *zipEntry = reinterpret_cast<ZipEntryRO*>(entry); 245 // final ZipEntry& ze = zipEntry.entry; 246 // int fd = GetFileDescriptor(mHandle); 247 // size_t actualLen = 0; 248 // 249 // if (ze.method == kCompressStored) { 250 // actualLen = ze.uncompressed_length; 251 // } else { 252 // actualLen = ze.compressed_length; 253 // } 254 // 255 // FileMap* newMap = new FileMap(); 256 // if (!newMap.create(mFileName, fd, ze.offset, actualLen, true)) { 257 // delete newMap; 258 // return null; 259 // } 260 // 261 // return newMap; 262 } 263 264 /* 265 * Create a new FileMap object that spans the data in "entry". 266 */ createEntryFileMap(ZipEntryRO entry)267 FileMap createEntryFileMap(ZipEntryRO entry) 268 { 269 // final _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); 270 // const ZipEntry& ze = zipEntry->entry; 271 ZipEntry ze = entry.entry; 272 // int fd = GetFileDescriptor(mHandle); 273 int fd = -1; 274 int actualLen = 0; 275 276 if (ze.getMethod() == kCompressStored) { 277 actualLen = toIntExact(ze.getSize()); 278 } else { 279 actualLen = toIntExact(ze.getCompressedSize()); 280 } 281 282 FileMap newMap = new FileMap(); 283 if (!newMap.createFromZip(mFileName, mHandle.zipFile, entry.entry, actualLen, true)) { 284 // delete newMap; 285 return null; 286 } 287 288 return newMap; 289 } 290 291 /* 292 * Uncompress an entry, in its entirety, into the provided output buffer. 293 * 294 * This doesn't verify the data's CRC, which might be useful for 295 * uncompressed data. The caller should be able to manage it. 296 */ uncompressEntry(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry, Object buffer, int size)297 boolean uncompressEntry(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry, Object buffer, int size) 298 { 299 throw new UnsupportedOperationException("Implememnt me"); 300 // ZipEntryRO *zipEntry = reinterpret_cast<ZipEntryRO*>(entry); 301 // final int error = ExtractToMemory(mHandle, &(zipEntry.entry), 302 // (uint8_t*) buffer, size); 303 // if (error) { 304 // ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error)); 305 // return false; 306 // } 307 // 308 // return true; 309 } 310 311 /* 312 * Uncompress an entry, in its entirety, to an open file descriptor. 313 * 314 * This doesn't verify the data's CRC, but probably should. 315 */ uncompressEntry(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry, int fd)316 boolean uncompressEntry(org.robolectric.res.android.ZipFileRO.ZipEntryRO entry, int fd) 317 { 318 throw new UnsupportedOperationException("Implememnt me"); 319 // ZipEntryRO *zipEntry = reinterpret_cast<ZipEntryRO*>(entry); 320 // final int error = ExtractEntryToFile(mHandle, &(zipEntry.entry), fd); 321 // if (error) { 322 // ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error)); 323 // return false; 324 // } 325 // 326 // return true; 327 } 328 String(String string)329 static String String(String string) { 330 return string; 331 } 332 } 333