Search in sources :

Example 1 with BundleHeader

use of info.ata4.junity.bundle.BundleHeader in project disunity by ata4.

the class BundleWriter method write.

public void write(Bundle bundle, Progress progress) throws IOException {
    this.bundle = bundle;
    // add offset placeholders
    levelOffsetMap.clear();
    bundle.entries().stream().filter(entry -> {
        if (bundle.entries().size() == 1) {
            return true;
        }
        String name = entry.name();
        return name.equals("mainData") || name.startsWith("level");
    }).forEach(entry -> levelOffsetMap.put(entry, new MutablePair<>(0L, 0L)));
    BundleHeader header = bundle.header();
    header.levelByteEnd().clear();
    header.levelByteEnd().addAll(levelOffsetMap.values());
    header.numberOfLevelsToDownload(levelOffsetMap.size());
    // write header
    out.writeStruct(header);
    header.headerSize((int) out.position());
    // write bundle data
    if (header.compressed()) {
        // write data to temporary file
        try (DataWriter outData = DataWriters.forFile(dataFile, CREATE, WRITE, TRUNCATE_EXISTING)) {
            writeData(outData, progress);
        }
        // configure LZMA encoder
        LzmaEncoderProps props = new LzmaEncoderProps();
        // 8 MiB
        props.setDictionarySize(1 << 23);
        // maximum
        props.setNumFastBytes(273);
        props.setUncompressedSize(Files.size(dataFile));
        props.setEndMarkerMode(true);
        // stream the temporary bundle data compressed into the bundle file
        try (OutputStream os = new LzmaOutputStream(new BufferedOutputStream(out.stream()), props)) {
            Files.copy(dataFile, os);
        }
        for (MutablePair<Long, Long> levelOffset : levelOffsetMap.values()) {
            levelOffset.setLeft(out.size());
        }
    } else {
        // write data directly to file
        writeData(out, progress);
    }
    // update header
    int fileSize = (int) out.size();
    header.completeFileSize(fileSize);
    header.minimumStreamedBytes(fileSize);
    out.position(0);
    out.writeStruct(header);
}
Also used : DataWriters(info.ata4.io.DataWriters) OutputStream(java.io.OutputStream) LzmaOutputStream(net.contrapunctus.lzma.LzmaOutputStream) Progress(info.ata4.junity.progress.Progress) DataWriter(info.ata4.io.DataWriter) Files(java.nio.file.Files) StandardOpenOption(java.nio.file.StandardOpenOption) IOException(java.io.IOException) BufferedOutputStream(java.io.BufferedOutputStream) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) IOUtils(org.apache.commons.io.IOUtils) List(java.util.List) MutablePair(org.apache.commons.lang3.tuple.MutablePair) Closeable(java.io.Closeable) Map(java.util.Map) Optional(java.util.Optional) LzmaEncoderProps(info.ata4.io.lzma.LzmaEncoderProps) Path(java.nio.file.Path) InputStream(java.io.InputStream) MutablePair(org.apache.commons.lang3.tuple.MutablePair) OutputStream(java.io.OutputStream) LzmaOutputStream(net.contrapunctus.lzma.LzmaOutputStream) BufferedOutputStream(java.io.BufferedOutputStream) LzmaEncoderProps(info.ata4.io.lzma.LzmaEncoderProps) LzmaOutputStream(net.contrapunctus.lzma.LzmaOutputStream) BufferedOutputStream(java.io.BufferedOutputStream) DataWriter(info.ata4.io.DataWriter)

Example 2 with BundleHeader

use of info.ata4.junity.bundle.BundleHeader in project disunity by ata4.

the class BundleProps method read.

static void read(Path propsFile, Bundle bundle) throws IOException {
    BundleProps props;
    try (Reader reader = Files.newBufferedReader(propsFile, CHARSET)) {
        props = new Gson().fromJson(reader, BundleProps.class);
    }
    BundleHeader header = bundle.header();
    header.compressed(props.compressed);
    header.streamVersion(props.streamVersion);
    header.unityVersion(new UnityVersion(props.unityVersion));
    header.unityRevision(new UnityVersion(props.unityRevision));
    String bundleName = PathUtils.getBaseName(propsFile);
    Path bundleDir = propsFile.resolveSibling(bundleName);
    props.files.stream().map(bundleDir::resolve).forEach(file -> {
        bundle.entries().add(new BundleExternalEntry(file));
    });
}
Also used : Path(java.nio.file.Path) BundleExternalEntry(info.ata4.junity.bundle.BundleExternalEntry) BundleHeader(info.ata4.junity.bundle.BundleHeader) Reader(java.io.Reader) Gson(com.google.gson.Gson) UnityVersion(info.ata4.junity.UnityVersion)

Example 3 with BundleHeader

use of info.ata4.junity.bundle.BundleHeader in project disunity by ata4.

the class BundleProps method write.

static void write(Path propsFile, Bundle bundle) throws IOException {
    BundleProps props = new BundleProps();
    BundleHeader header = bundle.header();
    props.compressed = header.compressed();
    props.streamVersion = header.streamVersion();
    props.unityVersion = header.unityVersion().toString();
    props.unityRevision = header.unityRevision().toString();
    props.files = bundle.entryInfos().stream().map(entry -> entry.name()).collect(Collectors.toList());
    try (Writer writer = Files.newBufferedWriter(propsFile, CHARSET, WRITE, CREATE, TRUNCATE_EXISTING)) {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        gson.toJson(props, writer);
    }
}
Also used : BundleHeader(info.ata4.junity.bundle.BundleHeader) GsonBuilder(com.google.gson.GsonBuilder) Gson(com.google.gson.Gson) Writer(java.io.Writer)

Example 4 with BundleHeader

use of info.ata4.junity.bundle.BundleHeader in project disunity by ata4.

the class BundleReader method read.

public Bundle read() throws BundleException, IOException {
    bundle = new Bundle();
    in.position(0);
    BundleHeader header = bundle.header();
    in.readStruct(header);
    // check signature
    if (!header.hasValidSignature()) {
        throw new BundleException("Invalid signature");
    }
    List<BundleEntryInfo> entryInfos = bundle.entryInfos();
    if (header.compressedDataHeaderSize() > 0) {
        if (header.dataHeaderAtEndOfFile()) {
            in.position(header.completeFileSize() - header.compressedDataHeaderSize());
        }
        // build an input stream for the uncompressed data header
        InputStream headerIn = new BoundedInputStream(in.stream(), header.compressedDataHeaderSize());
        DataReader inData;
        switch(header.dataHeaderCompressionScheme()) {
            default:
            case 0:
                // Not compressed
                inData = DataReaders.forInputStream(headerIn);
            case 1:
                // LZMA
                inData = DataReaders.forInputStream(new CountingInputStream(new LzmaInputStream(headerIn)));
            case 3:
                // LZ4
                byte[] compressed = new byte[header.compressedDataHeaderSize()];
                byte[] decompressed = new byte[(int) header.dataHeaderSize()];
                headerIn.read(compressed);
                LZ4JavaSafeFastDecompressor.INSTANCE.decompress(compressed, decompressed);
                inData = DataReaders.forByteBuffer(ByteBuffer.wrap(decompressed));
        }
        // Block info: not captured for now
        {
            // 16 bytes unknown
            byte[] unknown = new byte[16];
            inData.readBytes(unknown);
            int storageBlocks = inData.readInt();
            for (int i = 0; i < storageBlocks; ++i) {
                inData.readUnsignedInt();
                inData.readUnsignedInt();
                inData.readUnsignedShort();
            }
        }
        int files = inData.readInt();
        for (int i = 0; i < files; i++) {
            BundleEntryInfo entryInfo = new BundleEntryInfoFS();
            inData.readStruct(entryInfo);
            entryInfos.add(entryInfo);
        }
    } else {
        // raw or web header
        long dataHeaderSize = header.dataHeaderSize();
        if (dataHeaderSize == 0) {
            // old stream versions don't store the data header size, so use a large
            // fixed number instead
            dataHeaderSize = 4096;
        }
        InputStream is = dataInputStream(0, dataHeaderSize);
        DataReader inData = DataReaders.forInputStream(is);
        int files = inData.readInt();
        for (int i = 0; i < files; i++) {
            BundleEntryInfo entryInfo = new BundleEntryInfo();
            inData.readStruct(entryInfo);
            entryInfos.add(entryInfo);
        }
    }
    // sort entries by offset so that they're in the order in which they
    // appear in the file, which is convenient for compressed bundles
    entryInfos.sort((a, b) -> Long.compare(a.offset(), b.offset()));
    List<BundleEntry> entries = bundle.entries();
    entryInfos.forEach(entryInfo -> {
        entries.add(new BundleInternalEntry(entryInfo, this::inputStreamForEntry));
    });
    return bundle;
}
Also used : LzmaInputStream(net.contrapunctus.lzma.LzmaInputStream) BoundedInputStream(org.apache.commons.io.input.BoundedInputStream) CountingInputStream(org.apache.commons.io.input.CountingInputStream) InputStream(java.io.InputStream) CountingInputStream(org.apache.commons.io.input.CountingInputStream) DataReader(info.ata4.io.DataReader) LzmaInputStream(net.contrapunctus.lzma.LzmaInputStream) BoundedInputStream(org.apache.commons.io.input.BoundedInputStream)

Example 5 with BundleHeader

use of info.ata4.junity.bundle.BundleHeader in project disunity by ata4.

the class BundleInfo method buildHeaderTable.

private Table<Integer, Integer, Object> buildHeaderTable(BundleHeader header) {
    TableBuilder table = new TableBuilder();
    table.row("Field", "Value");
    table.row("signature", header.signature());
    table.row("streamVersion", header.streamVersion());
    table.row("unityVersion", header.unityVersion());
    table.row("unityRevision", header.unityRevision());
    table.row("minimumStreamedBytes", header.minimumStreamedBytes());
    table.row("headerSize", header.headerSize());
    table.row("numberOfLevelsToDownload", header.numberOfLevelsToDownload());
    table.row("numberOfLevels", header.numberOfLevels());
    List<Pair<Long, Long>> levelByteEnds = header.levelByteEnd();
    for (int i = 0; i < levelByteEnds.size(); i++) {
        Pair<Long, Long> levelByteEnd = levelByteEnds.get(i);
        table.row("levelByteEnd[" + i + "][0]", levelByteEnd.getLeft());
        table.row("levelByteEnd[" + i + "][1]", levelByteEnd.getRight());
    }
    if (header.streamVersion() >= 2) {
        table.row("completeFileSize", header.completeFileSize());
    }
    if (header.streamVersion() >= 3) {
        table.row("dataHeaderSize", header.dataHeaderSize());
    }
    return table.get();
}
Also used : TableBuilder(info.ata4.disunity.cli.util.TableBuilder) Pair(org.apache.commons.lang3.tuple.Pair)

Aggregations

BundleHeader (info.ata4.junity.bundle.BundleHeader)3 Gson (com.google.gson.Gson)2 InputStream (java.io.InputStream)2 Path (java.nio.file.Path)2 GsonBuilder (com.google.gson.GsonBuilder)1 TableBuilder (info.ata4.disunity.cli.util.TableBuilder)1 DataReader (info.ata4.io.DataReader)1 DataWriter (info.ata4.io.DataWriter)1 DataWriters (info.ata4.io.DataWriters)1 LzmaEncoderProps (info.ata4.io.lzma.LzmaEncoderProps)1 UnityVersion (info.ata4.junity.UnityVersion)1 BundleExternalEntry (info.ata4.junity.bundle.BundleExternalEntry)1 Progress (info.ata4.junity.progress.Progress)1 BufferedOutputStream (java.io.BufferedOutputStream)1 Closeable (java.io.Closeable)1 IOException (java.io.IOException)1 OutputStream (java.io.OutputStream)1 Reader (java.io.Reader)1 Writer (java.io.Writer)1 Files (java.nio.file.Files)1