1 /* 2 * Copyright (c) 2023-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.ohos.hapsigntool.zip; 17 18 import com.ohos.hapsigntool.error.ZipException; 19 20 import java.nio.ByteBuffer; 21 import java.nio.ByteOrder; 22 import java.nio.charset.StandardCharsets; 23 24 /** 25 * resolve zip CentralDirectory data 26 * CentralDirectory format for: 27 * central file header signature 4 bytes (0x02014b50) 28 * version made by 2 bytes 29 * version needed to extract 2 bytes 30 * general purpose bit flag 2 bytes 31 * compression method 2 bytes 32 * last mod file time 2 bytes 33 * last mod file date 2 bytes 34 * crc-32 4 bytes 35 * compressed size 4 bytes 36 * uncompressed size 4 bytes 37 * file name length 2 bytes 38 * extra field length 2 bytes 39 * file comment length 2 bytes 40 * disk number start 2 bytes 41 * internal file attributes 2 bytes 42 * external file attributes 4 bytes 43 * relative offset of local header 4 bytes 44 * file name (variable size) 45 * extra field (variable size) 46 * file comment (variable size) 47 * 48 * @since 2023/12/02 49 */ 50 public class CentralDirectory { 51 /** 52 * central directory invariable bytes length 53 */ 54 public static final int CD_LENGTH = 46; 55 56 /** 57 * 4 bytes , central directory signature 58 */ 59 public static final int SIGNATURE = 0x02014b50; 60 61 /** 62 * 2 bytes 63 */ 64 private short version; 65 66 /** 67 * 2 bytes 68 */ 69 private short versionExtra; 70 71 /** 72 * 2 bytes 73 */ 74 private short flag; 75 76 /** 77 * 2 bytes 78 */ 79 private short method; 80 81 /** 82 * 2 bytes 83 */ 84 private short lastTime; 85 86 /** 87 * 2 bytes 88 */ 89 private short lastDate; 90 91 /** 92 * 4 bytes 93 */ 94 private int crc32; 95 96 /** 97 * 4 bytes 98 */ 99 private long compressedSize; 100 101 /** 102 * 4 bytes 103 */ 104 private long unCompressedSize; 105 106 /** 107 * 2 bytes 108 */ 109 private int fileNameLength; 110 111 /** 112 * 2 bytes 113 */ 114 private int extraLength; 115 116 /** 117 * 2 bytes 118 */ 119 private int commentLength; 120 121 /** 122 * 2 bytes 123 */ 124 private int diskNumStart; 125 126 /** 127 * 2 bytes 128 */ 129 private short internalFile; 130 131 /** 132 * 4 bytes 133 */ 134 private int externalFile; 135 136 /** 137 * 4 bytes 138 */ 139 private long offset; 140 141 /** 142 * n bytes 143 */ 144 private String fileName; 145 146 /** 147 * n bytes 148 */ 149 private byte[] extraData; 150 151 /** 152 * n bytes 153 */ 154 private byte[] comment; 155 156 private int length; 157 158 /** 159 * get Central Directory 160 * 161 * @param bf ByteBuffer 162 * @return CentralDirectory 163 * @throws ZipException read Central Directory exception 164 */ getCentralDirectory(ByteBuffer bf)165 public static CentralDirectory getCentralDirectory(ByteBuffer bf) throws ZipException { 166 CentralDirectory cd = new CentralDirectory(); 167 if (bf.getInt() != SIGNATURE) { 168 throw new ZipException("find zip central directory failed"); 169 } 170 171 cd.setVersion(bf.getShort()); 172 cd.setVersionExtra(bf.getShort()); 173 cd.setFlag(bf.getShort()); 174 cd.setMethod(bf.getShort()); 175 cd.setLastTime(bf.getShort()); 176 cd.setLastDate(bf.getShort()); 177 cd.setCrc32(bf.getInt()); 178 cd.setCompressedSize(UnsignedDecimalUtil.getUnsignedInt(bf)); 179 cd.setUnCompressedSize(UnsignedDecimalUtil.getUnsignedInt(bf)); 180 cd.setFileNameLength(UnsignedDecimalUtil.getUnsignedShort(bf)); 181 cd.setExtraLength(UnsignedDecimalUtil.getUnsignedShort(bf)); 182 cd.setCommentLength(UnsignedDecimalUtil.getUnsignedShort(bf)); 183 cd.setDiskNumStart(UnsignedDecimalUtil.getUnsignedShort(bf)); 184 cd.setInternalFile(bf.getShort()); 185 cd.setExternalFile(bf.getInt()); 186 cd.setOffset(UnsignedDecimalUtil.getUnsignedInt(bf)); 187 if (cd.getFileNameLength() > 0) { 188 byte[] readFileName = new byte[cd.getFileNameLength()]; 189 bf.get(readFileName); 190 cd.setFileName(new String(readFileName, StandardCharsets.UTF_8)); 191 } 192 if (cd.getExtraLength() > 0) { 193 byte[] extra = new byte[cd.getExtraLength()]; 194 bf.get(extra); 195 cd.setExtraData(extra); 196 } 197 if (cd.getCommentLength() > 0) { 198 byte[] readComment = new byte[cd.getCommentLength()]; 199 bf.get(readComment); 200 cd.setComment(readComment); 201 } 202 cd.setLength(CD_LENGTH + cd.getFileNameLength() + cd.getExtraLength() + cd.getCommentLength()); 203 return cd; 204 } 205 206 /** 207 * change Central Directory to bytes 208 * 209 * @return bytes 210 */ toBytes()211 public byte[] toBytes() { 212 ByteBuffer bf = ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN); 213 bf.putInt(SIGNATURE); 214 UnsignedDecimalUtil.setUnsignedShort(bf, version); 215 UnsignedDecimalUtil.setUnsignedShort(bf, versionExtra); 216 UnsignedDecimalUtil.setUnsignedShort(bf, flag); 217 UnsignedDecimalUtil.setUnsignedShort(bf, method); 218 UnsignedDecimalUtil.setUnsignedShort(bf, lastTime); 219 UnsignedDecimalUtil.setUnsignedShort(bf, lastDate); 220 UnsignedDecimalUtil.setUnsignedInt(bf, crc32); 221 UnsignedDecimalUtil.setUnsignedInt(bf, compressedSize); 222 UnsignedDecimalUtil.setUnsignedInt(bf, unCompressedSize); 223 UnsignedDecimalUtil.setUnsignedShort(bf, fileNameLength); 224 UnsignedDecimalUtil.setUnsignedShort(bf, extraLength); 225 UnsignedDecimalUtil.setUnsignedShort(bf, commentLength); 226 UnsignedDecimalUtil.setUnsignedShort(bf, diskNumStart); 227 UnsignedDecimalUtil.setUnsignedShort(bf, internalFile); 228 UnsignedDecimalUtil.setUnsignedInt(bf, externalFile); 229 UnsignedDecimalUtil.setUnsignedInt(bf, offset); 230 if (fileNameLength > 0) { 231 bf.put(fileName.getBytes(StandardCharsets.UTF_8)); 232 } 233 if (extraLength > 0) { 234 bf.put(extraData); 235 } 236 if (commentLength > 0) { 237 bf.put(extraData); 238 } 239 return bf.array(); 240 } 241 getCdLength()242 public static int getCdLength() { 243 return CD_LENGTH; 244 } 245 getSIGNATURE()246 public static int getSIGNATURE() { 247 return SIGNATURE; 248 } 249 getVersion()250 public short getVersion() { 251 return version; 252 } 253 setVersion(short version)254 public void setVersion(short version) { 255 this.version = version; 256 } 257 getVersionExtra()258 public short getVersionExtra() { 259 return versionExtra; 260 } 261 setVersionExtra(short versionExtra)262 public void setVersionExtra(short versionExtra) { 263 this.versionExtra = versionExtra; 264 } 265 getFlag()266 public short getFlag() { 267 return flag; 268 } 269 setFlag(short flag)270 public void setFlag(short flag) { 271 this.flag = flag; 272 } 273 getMethod()274 public short getMethod() { 275 return method; 276 } 277 setMethod(short method)278 public void setMethod(short method) { 279 this.method = method; 280 } 281 getLastTime()282 public short getLastTime() { 283 return lastTime; 284 } 285 setLastTime(short lastTime)286 public void setLastTime(short lastTime) { 287 this.lastTime = lastTime; 288 } 289 getLastDate()290 public short getLastDate() { 291 return lastDate; 292 } 293 setLastDate(short lastDate)294 public void setLastDate(short lastDate) { 295 this.lastDate = lastDate; 296 } 297 getCrc32()298 public int getCrc32() { 299 return crc32; 300 } 301 setCrc32(int crc32)302 public void setCrc32(int crc32) { 303 this.crc32 = crc32; 304 } 305 getCompressedSize()306 public long getCompressedSize() { 307 return compressedSize; 308 } 309 setCompressedSize(long compressedSize)310 public void setCompressedSize(long compressedSize) { 311 this.compressedSize = compressedSize; 312 } 313 getUnCompressedSize()314 public long getUnCompressedSize() { 315 return unCompressedSize; 316 } 317 setUnCompressedSize(long unCompressedSize)318 public void setUnCompressedSize(long unCompressedSize) { 319 this.unCompressedSize = unCompressedSize; 320 } 321 getFileNameLength()322 public int getFileNameLength() { 323 return fileNameLength; 324 } 325 setFileNameLength(int fileNameLength)326 public void setFileNameLength(int fileNameLength) { 327 this.fileNameLength = fileNameLength; 328 } 329 getExtraLength()330 public int getExtraLength() { 331 return extraLength; 332 } 333 setExtraLength(int extraLength)334 public void setExtraLength(int extraLength) { 335 this.extraLength = extraLength; 336 } 337 getCommentLength()338 public int getCommentLength() { 339 return commentLength; 340 } 341 setCommentLength(int commentLength)342 public void setCommentLength(int commentLength) { 343 this.commentLength = commentLength; 344 } 345 getDiskNumStart()346 public int getDiskNumStart() { 347 return diskNumStart; 348 } 349 setDiskNumStart(int diskNumStart)350 public void setDiskNumStart(int diskNumStart) { 351 this.diskNumStart = diskNumStart; 352 } 353 getInternalFile()354 public short getInternalFile() { 355 return internalFile; 356 } 357 setInternalFile(short internalFile)358 public void setInternalFile(short internalFile) { 359 this.internalFile = internalFile; 360 } 361 getExternalFile()362 public int getExternalFile() { 363 return externalFile; 364 } 365 setExternalFile(int externalFile)366 public void setExternalFile(int externalFile) { 367 this.externalFile = externalFile; 368 } 369 getOffset()370 public long getOffset() { 371 return offset; 372 } 373 setOffset(long offset)374 public void setOffset(long offset) { 375 this.offset = offset; 376 } 377 getFileName()378 public String getFileName() { 379 return fileName; 380 } 381 setFileName(String fileName)382 public void setFileName(String fileName) { 383 this.fileName = fileName; 384 } 385 getExtraData()386 public byte[] getExtraData() { 387 return extraData; 388 } 389 setExtraData(byte[] extraData)390 public void setExtraData(byte[] extraData) { 391 this.extraData = extraData; 392 } 393 getComment()394 public byte[] getComment() { 395 return comment; 396 } 397 setComment(byte[] comment)398 public void setComment(byte[] comment) { 399 this.comment = comment; 400 } 401 getLength()402 public int getLength() { 403 return length; 404 } 405 setLength(int length)406 public void setLength(int length) { 407 this.length = length; 408 } 409 }