(cls, bio)
| 545 | |
| 546 | @classmethod |
| 547 | def read(cls, bio): |
| 548 | fml_start = bio.tell() |
| 549 | _fml = cls() |
| 550 | _fml.size = struct.unpack('<I', bio.read(4))[0] |
| 551 | _fml.version = struct.unpack('B', bio.read(1))[0] |
| 552 | _fml.count = struct.unpack('<I', bio.read(4))[0] |
| 553 | |
| 554 | for _ in range(_fml.count): |
| 555 | _fml.elements.append(FileManifest()) |
| 556 | |
| 557 | for fm in _fml.elements: |
| 558 | fm.filename = read_fstring(bio) |
| 559 | |
| 560 | # never seen this used in any of the manifests I checked but can't wait for something to break because of it |
| 561 | for fm in _fml.elements: |
| 562 | fm.symlink_target = read_fstring(bio) |
| 563 | |
| 564 | # For files this is actually the SHA1 instead of whatever it is for chunks... |
| 565 | for fm in _fml.elements: |
| 566 | fm.hash = bio.read(20) |
| 567 | |
| 568 | # Flags, the only one I've seen is for executables |
| 569 | for fm in _fml.elements: |
| 570 | fm.flags = struct.unpack('B', bio.read(1))[0] |
| 571 | |
| 572 | # install tags, no idea what they do, I've only seen them in the Fortnite manifest |
| 573 | for fm in _fml.elements: |
| 574 | _elem = struct.unpack('<I', bio.read(4))[0] |
| 575 | for _ in range(_elem): |
| 576 | fm.install_tags.append(read_fstring(bio)) |
| 577 | |
| 578 | # Each file is made up of "Chunk Parts" that can be spread across the "chunk stream" |
| 579 | for fm in _fml.elements: |
| 580 | _elem = struct.unpack('<I', bio.read(4))[0] |
| 581 | _offset = 0 |
| 582 | for _ in range(_elem): |
| 583 | chunkp = ChunkPart() |
| 584 | _start = bio.tell() |
| 585 | _size = struct.unpack('<I', bio.read(4))[0] |
| 586 | chunkp.guid = struct.unpack('<IIII', bio.read(16)) |
| 587 | chunkp.offset = struct.unpack('<I', bio.read(4))[0] |
| 588 | chunkp.size = struct.unpack('<I', bio.read(4))[0] |
| 589 | chunkp.file_offset = _offset |
| 590 | fm.chunk_parts.append(chunkp) |
| 591 | _offset += chunkp.size |
| 592 | if (diff := (bio.tell() - _start - _size)) > 0: |
| 593 | logger.warning(f'Did not read {diff} bytes from chunk part!') |
| 594 | bio.seek(diff) |
| 595 | |
| 596 | # MD5 hash + MIME type (Manifest feature level 19) |
| 597 | if _fml.version >= 1: |
| 598 | for fm in _fml.elements: |
| 599 | _has_md5 = struct.unpack('<I', bio.read(4))[0] |
| 600 | if _has_md5 != 0: |
| 601 | fm.hash_md5 = bio.read(16) |
| 602 | |
| 603 | for fm in _fml.elements: |
| 604 | fm.mime_type = read_fstring(bio) |
nothing calls this directly
no test coverage detected