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 * updateLength 160 */ updateLength()161 public void updateLength() { 162 length = CD_LENGTH + fileNameLength + extraLength + commentLength; 163 } 164 165 /** 166 * get Central Directory 167 * 168 * @param bf ByteBuffer 169 * @return CentralDirectory 170 * @throws ZipException read Central Directory exception 171 */ getCentralDirectory(ByteBuffer bf)172 public static CentralDirectory getCentralDirectory(ByteBuffer bf) throws ZipException { 173 CentralDirectory cd = new CentralDirectory(); 174 if (bf.getInt() != SIGNATURE) { 175 throw new ZipException("find zip central directory failed"); 176 } 177 178 cd.setVersion(bf.getShort()); 179 cd.setVersionExtra(bf.getShort()); 180 cd.setFlag(bf.getShort()); 181 cd.setMethod(bf.getShort()); 182 cd.setLastTime(bf.getShort()); 183 cd.setLastDate(bf.getShort()); 184 cd.setCrc32(bf.getInt()); 185 cd.setCompressedSize(UnsignedDecimalUtil.getUnsignedInt(bf)); 186 cd.setUnCompressedSize(UnsignedDecimalUtil.getUnsignedInt(bf)); 187 cd.setFileNameLength(UnsignedDecimalUtil.getUnsignedShort(bf)); 188 cd.setExtraLength(UnsignedDecimalUtil.getUnsignedShort(bf)); 189 cd.setCommentLength(UnsignedDecimalUtil.getUnsignedShort(bf)); 190 cd.setDiskNumStart(UnsignedDecimalUtil.getUnsignedShort(bf)); 191 cd.setInternalFile(bf.getShort()); 192 cd.setExternalFile(bf.getInt()); 193 cd.setOffset(UnsignedDecimalUtil.getUnsignedInt(bf)); 194 if (cd.getFileNameLength() > 0) { 195 byte[] readFileName = new byte[cd.getFileNameLength()]; 196 bf.get(readFileName); 197 cd.setFileName(new String(readFileName, StandardCharsets.UTF_8)); 198 } 199 if (cd.getExtraLength() > 0) { 200 byte[] extra = new byte[cd.getExtraLength()]; 201 bf.get(extra); 202 cd.setExtraData(extra); 203 } 204 if (cd.getCommentLength() > 0) { 205 byte[] readComment = new byte[cd.getCommentLength()]; 206 bf.get(readComment); 207 cd.setComment(readComment); 208 } 209 cd.updateLength(); 210 return cd; 211 } 212 213 /** 214 * change Central Directory to bytes 215 * 216 * @return bytes 217 */ toBytes()218 public byte[] toBytes() { 219 ByteBuffer bf = ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN); 220 bf.putInt(SIGNATURE); 221 UnsignedDecimalUtil.setUnsignedShort(bf, version); 222 UnsignedDecimalUtil.setUnsignedShort(bf, versionExtra); 223 UnsignedDecimalUtil.setUnsignedShort(bf, flag); 224 UnsignedDecimalUtil.setUnsignedShort(bf, method); 225 UnsignedDecimalUtil.setUnsignedShort(bf, lastTime); 226 UnsignedDecimalUtil.setUnsignedShort(bf, lastDate); 227 UnsignedDecimalUtil.setUnsignedInt(bf, crc32); 228 UnsignedDecimalUtil.setUnsignedInt(bf, compressedSize); 229 UnsignedDecimalUtil.setUnsignedInt(bf, unCompressedSize); 230 UnsignedDecimalUtil.setUnsignedShort(bf, fileNameLength); 231 UnsignedDecimalUtil.setUnsignedShort(bf, extraLength); 232 UnsignedDecimalUtil.setUnsignedShort(bf, commentLength); 233 UnsignedDecimalUtil.setUnsignedShort(bf, diskNumStart); 234 UnsignedDecimalUtil.setUnsignedShort(bf, internalFile); 235 UnsignedDecimalUtil.setUnsignedInt(bf, externalFile); 236 UnsignedDecimalUtil.setUnsignedInt(bf, offset); 237 if (fileNameLength > 0) { 238 bf.put(fileName.getBytes(StandardCharsets.UTF_8)); 239 } 240 if (extraLength > 0) { 241 bf.put(extraData); 242 } 243 if (commentLength > 0) { 244 bf.put(extraData); 245 } 246 return bf.array(); 247 } 248 getCdLength()249 public static int getCdLength() { 250 return CD_LENGTH; 251 } 252 getSIGNATURE()253 public static int getSIGNATURE() { 254 return SIGNATURE; 255 } 256 getVersion()257 public short getVersion() { 258 return version; 259 } 260 setVersion(short version)261 public void setVersion(short version) { 262 this.version = version; 263 } 264 getVersionExtra()265 public short getVersionExtra() { 266 return versionExtra; 267 } 268 setVersionExtra(short versionExtra)269 public void setVersionExtra(short versionExtra) { 270 this.versionExtra = versionExtra; 271 } 272 getFlag()273 public short getFlag() { 274 return flag; 275 } 276 setFlag(short flag)277 public void setFlag(short flag) { 278 this.flag = flag; 279 } 280 getMethod()281 public short getMethod() { 282 return method; 283 } 284 setMethod(short method)285 public void setMethod(short method) { 286 this.method = method; 287 } 288 getLastTime()289 public short getLastTime() { 290 return lastTime; 291 } 292 setLastTime(short lastTime)293 public void setLastTime(short lastTime) { 294 this.lastTime = lastTime; 295 } 296 getLastDate()297 public short getLastDate() { 298 return lastDate; 299 } 300 setLastDate(short lastDate)301 public void setLastDate(short lastDate) { 302 this.lastDate = lastDate; 303 } 304 getCrc32()305 public int getCrc32() { 306 return crc32; 307 } 308 setCrc32(int crc32)309 public void setCrc32(int crc32) { 310 this.crc32 = crc32; 311 } 312 getCompressedSize()313 public long getCompressedSize() { 314 return compressedSize; 315 } 316 setCompressedSize(long compressedSize)317 public void setCompressedSize(long compressedSize) { 318 this.compressedSize = compressedSize; 319 } 320 getUnCompressedSize()321 public long getUnCompressedSize() { 322 return unCompressedSize; 323 } 324 setUnCompressedSize(long unCompressedSize)325 public void setUnCompressedSize(long unCompressedSize) { 326 this.unCompressedSize = unCompressedSize; 327 } 328 getFileNameLength()329 public int getFileNameLength() { 330 return fileNameLength; 331 } 332 setFileNameLength(int fileNameLength)333 public void setFileNameLength(int fileNameLength) { 334 this.fileNameLength = fileNameLength; 335 } 336 getExtraLength()337 public int getExtraLength() { 338 return extraLength; 339 } 340 setExtraLength(int extraLength)341 public void setExtraLength(int extraLength) { 342 this.extraLength = extraLength; 343 } 344 getCommentLength()345 public int getCommentLength() { 346 return commentLength; 347 } 348 setCommentLength(int commentLength)349 public void setCommentLength(int commentLength) { 350 this.commentLength = commentLength; 351 } 352 getDiskNumStart()353 public int getDiskNumStart() { 354 return diskNumStart; 355 } 356 setDiskNumStart(int diskNumStart)357 public void setDiskNumStart(int diskNumStart) { 358 this.diskNumStart = diskNumStart; 359 } 360 getInternalFile()361 public short getInternalFile() { 362 return internalFile; 363 } 364 setInternalFile(short internalFile)365 public void setInternalFile(short internalFile) { 366 this.internalFile = internalFile; 367 } 368 getExternalFile()369 public int getExternalFile() { 370 return externalFile; 371 } 372 setExternalFile(int externalFile)373 public void setExternalFile(int externalFile) { 374 this.externalFile = externalFile; 375 } 376 getOffset()377 public long getOffset() { 378 return offset; 379 } 380 setOffset(long offset)381 public void setOffset(long offset) { 382 this.offset = offset; 383 } 384 getFileName()385 public String getFileName() { 386 return fileName; 387 } 388 setFileName(String fileName)389 public void setFileName(String fileName) { 390 this.fileName = fileName; 391 } 392 getExtraData()393 public byte[] getExtraData() { 394 return extraData; 395 } 396 setExtraData(byte[] extraData)397 public void setExtraData(byte[] extraData) { 398 this.extraData = extraData; 399 } 400 getComment()401 public byte[] getComment() { 402 return comment; 403 } 404 setComment(byte[] comment)405 public void setComment(byte[] comment) { 406 this.comment = comment; 407 } 408 getLength()409 public int getLength() { 410 return length; 411 } 412 setLength(int length)413 public void setLength(int length) { 414 this.length = length; 415 } 416 }