├── .gitignore ├── LICENSE.md ├── README.md ├── disunity-cli ├── pom.xml └── src │ └── main │ └── java │ └── info │ └── ata4 │ └── disunity │ └── cli │ ├── DisUnityCli.java │ ├── OutputFormat.java │ ├── OutputFormatDelegate.java │ ├── command │ ├── Command.java │ ├── DisUnityRoot.java │ ├── FileCommand.java │ ├── RecursiveFileCommand.java │ ├── asset │ │ ├── AssetBlocks.java │ │ ├── AssetCommand.java │ │ ├── AssetExternalRefs.java │ │ ├── AssetHeader.java │ │ ├── AssetObjectIDs.java │ │ ├── AssetObjects.java │ │ ├── AssetRoot.java │ │ ├── AssetTableCommand.java │ │ ├── AssetTypes.java │ │ └── AssetUnpack.java │ └── bundle │ │ ├── BundleCommand.java │ │ ├── BundleInfo.java │ │ ├── BundleList.java │ │ ├── BundlePack.java │ │ ├── BundleProps.java │ │ ├── BundleRoot.java │ │ └── BundleUnpack.java │ ├── converters │ └── PathConverter.java │ └── util │ ├── Formatters.java │ ├── JsonTablePrinter.java │ ├── TableBuilder.java │ ├── TableModel.java │ ├── TablePrinter.java │ ├── TextTableAlignment.java │ ├── TextTableFormat.java │ └── TextTablePrinter.java ├── disunity-core ├── pom.xml └── src │ ├── main │ ├── java │ │ └── info │ │ │ └── ata4 │ │ │ ├── disunity │ │ │ └── DisUnity.java │ │ │ ├── junity │ │ │ ├── UnityGUID.java │ │ │ ├── UnityHash128.java │ │ │ ├── UnityStruct.java │ │ │ ├── UnityTableStruct.java │ │ │ ├── UnityVersion.java │ │ │ ├── bundle │ │ │ │ ├── Bundle.java │ │ │ │ ├── BundleEntry.java │ │ │ │ ├── BundleEntryInfo.java │ │ │ │ ├── BundleEntryInfoFS.java │ │ │ │ ├── BundleException.java │ │ │ │ ├── BundleExternalEntry.java │ │ │ │ ├── BundleHeader.java │ │ │ │ ├── BundleInternalEntry.java │ │ │ │ ├── BundleReader.java │ │ │ │ ├── BundleUtils.java │ │ │ │ └── BundleWriter.java │ │ │ ├── progress │ │ │ │ └── Progress.java │ │ │ └── serialize │ │ │ │ ├── SerializedFile.java │ │ │ │ ├── SerializedFileException.java │ │ │ │ ├── SerializedFileHeader.java │ │ │ │ ├── SerializedFileMetadata.java │ │ │ │ ├── SerializedFileReader.java │ │ │ │ ├── SerializedFileWriter.java │ │ │ │ ├── SerializedObjectData.java │ │ │ │ ├── fileidentifier │ │ │ │ ├── FileIdentifier.java │ │ │ │ ├── FileIdentifierTable.java │ │ │ │ ├── FileIdentifierV1.java │ │ │ │ └── FileIdentifierV2.java │ │ │ │ ├── objectidentifier │ │ │ │ ├── ObjectIdentifier.java │ │ │ │ └── ObjectIdentifierTable.java │ │ │ │ ├── objectinfo │ │ │ │ ├── ObjectInfo.java │ │ │ │ ├── ObjectInfoTable.java │ │ │ │ ├── ObjectInfoTableV1.java │ │ │ │ ├── ObjectInfoTableV2.java │ │ │ │ ├── ObjectInfoV1.java │ │ │ │ ├── ObjectInfoV2.java │ │ │ │ └── ObjectInfoV3.java │ │ │ │ └── typetree │ │ │ │ ├── StringTable.java │ │ │ │ ├── Type.java │ │ │ │ ├── TypeRoot.java │ │ │ │ ├── TypeTree.java │ │ │ │ ├── TypeTreeV1.java │ │ │ │ ├── TypeTreeV2.java │ │ │ │ ├── TypeTreeV3.java │ │ │ │ ├── TypeV1.java │ │ │ │ └── TypeV2.java │ │ │ └── util │ │ │ ├── collection │ │ │ └── Node.java │ │ │ ├── function │ │ │ ├── IOConsumer.java │ │ │ ├── IOFunction.java │ │ │ └── Predicates.java │ │ │ ├── io │ │ │ └── DataBlock.java │ │ │ └── lz4 │ │ │ ├── ByteBufferUtils.java │ │ │ ├── LZ4ByteBufferUtils.java │ │ │ ├── LZ4Constants.java │ │ │ ├── LZ4Exception.java │ │ │ ├── LZ4FastDecompressor.java │ │ │ ├── LZ4JavaSafeFastDecompressor.java │ │ │ ├── LZ4SafeUtils.java │ │ │ ├── LZ4Utils.java │ │ │ ├── SafeUtils.java │ │ │ └── Utils.java │ └── resources │ │ └── strings │ │ └── 5.x.txt │ └── test │ ├── java │ └── info │ │ └── ata4 │ │ └── test │ │ ├── ParameterizedUtils.java │ │ └── junity │ │ ├── BundleTest.java │ │ └── SerializedFileTest.java │ └── resources │ ├── bundle │ ├── 1 │ │ ├── Chelsea.unity3d │ │ └── whitewash1.unity3d │ ├── 2 │ │ ├── DreamTree.unity3d │ │ └── dressingroom.unity3d │ └── 3 │ │ ├── MeatHookTrainer_v0.3.unity3d │ │ └── bluedoor.unity3d │ └── mainData │ ├── 5 │ ├── 44b11c4151b06d329ea50f043e58b5f9 │ ├── 9e7049ec231bfd1af06b7e23471f41a1 │ ├── a92551a39b0e9fa70084eb0c30cde088 │ └── cea0b7475b5ad4c5be503ebbed637f48 │ ├── 6 │ ├── 285a8c4349da67936943b06e24a75aa5 │ ├── 3e59963dfe80f214e8ae73ce418e8d7f │ ├── 5eb4694feb92d12aa27ef851c8b530f5 │ ├── 7c5ffcd199dfb2e2eba75156752e1ed0 │ ├── 9874912f38d9433edd5685bdf30736bf │ ├── a25da1d2ad1f177f73c265afe78da5bc │ ├── a946c49b09a5cc670e48e06f8c7932c7 │ ├── bb7f0c734f7f70255c8c8e53aea45a13 │ ├── cfef6433d2d415b9e2bb1dfe56fb0771 │ └── f183e15084591efcf42b9796aa9f018e │ ├── 8 │ ├── 0a688c2bd852f9cfd6747991ed0cbdce │ ├── 21a3dad289724b6db82a6152a7a80b37 │ ├── 4db150ee8ead84646647d0ba8e2274f6 │ ├── 775a55777d336cc66fc1b741e2023f79 │ ├── 7cb556e951b0df8fc6957f53a079a79b │ ├── a3a44be259aca6f98b13c51afee605aa │ ├── af5d44dfffe8596b900ec92541e719ee │ ├── ccd881a72d1dc7e39f1abbb6a578fef0 │ ├── edd21066c169344ae06f794484652322 │ └── f84209d75bbee3ef4a0715fdbfa6b422 │ ├── 9 │ ├── 00f3c960b949ed053dee5cd29ef30b9b │ ├── 05616b32f0d1c6d185ba47daef2d2871 │ ├── 19353bc1e35d00d49eddadddddff36cb │ ├── 1e906936c6d03971f5317a19c70300d2 │ ├── 25c311bcb0ad2dc2a147c722f5a2bdea │ ├── 497c2b1f8c30cd4630dfc4cd7341964e │ ├── 7e2f25e2c875564cd43e2583b08fd45c │ ├── a9c2c78a98823559ed1eae593bd64261 │ ├── d1f556b864ae617acbe6079bc41818a6 │ └── e89097444c46d1936351a610eb69a6e2 │ ├── 14 │ ├── 53c8c79bc0dfc2c24e66af587008ea3a │ └── 58e88e08b508c8ede1b1f198647de952 │ └── 15 │ ├── 241a333f70fa6f03d3de6b06cbf004a6 │ └── e8809d217ea31d6ea1c0bb1579fe6ee3 ├── disunity-dist └── pom.xml ├── manifest.mf ├── pom.xml └── scripts ├── disunity.bat └── disunity.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### NetBeans ### 4 | nbproject/ 5 | build/ 6 | nbbuild/ 7 | dist/ 8 | nbdist/ 9 | nbactions.xml 10 | nb-configuration.xml 11 | .nb-gradle/ 12 | build.xml 13 | 14 | ### Maven ### 15 | target/ 16 | pom.xml.tag 17 | pom.xml.releaseBackup 18 | pom.xml.versionsBackup 19 | pom.xml.next 20 | dependency-reduced-pom.xml 21 | release.properties 22 | 23 | # Other 24 | private/ -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | This is free and unencumbered software released into the public domain. 4 | 5 | Anyone is free to copy, modify, publish, use, compile, sell, or 6 | distribute this software, either in source code form or as a compiled 7 | binary, for any purpose, commercial or non-commercial, and by any 8 | means. 9 | 10 | In jurisdictions that recognize copyright laws, the author or authors 11 | of this software dedicate any and all copyright interest in the 12 | software to the public domain. We make this dedication for the benefit 13 | of the public at large and to the detriment of our heirs and 14 | successors. We intend this dedication to be an overt act of 15 | relinquishment in perpetuity of all present and future rights to this 16 | software under copyright law. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | For more information, please refer to 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DisUnity 2 | ======== 3 | 4 | An experimental command-line toolset for Unity asset and asset bundle files written in Java, mostly designed for extraction. 5 | 6 | ### Download 7 | 8 | The latest build can be found on the [releases page](https://github.com/ata4/disunity/releases). 9 | 10 | ### A note about the versions 11 | 12 | 0.3 works best with Unity 3.x and has most of the original extraction features. 13 | 14 | 0.4 is a somewhat incomplete and untested upgrade to support Unity 4 and 5 and has some of the extraction features of 0.3. 15 | 16 | 0.5 is a code rewrite to properly support all Unity games from 2 to 5 that also comes with unit tests. Right now, it only 17 | supports raw file reading and writing without any object deserialization, therefore it also can't extract any asset data directly. 18 | 19 | ### Usage 20 | 21 | disunity [options] 22 | 23 | **Note:** depending on the platform, you may need to run disunity.bat (Windows) or disunity.sh (Linux/MacOS). In case the launch script fails, try `java -jar disunity.jar`. 24 | 25 | ### Available commands (v0.5) 26 | 27 | ## Asset commands 28 | 29 | | Command | Purpose 30 | | :---------------- | :---------------- 31 | | asset blocks | List data block offsets and sizes. Could be useful for manual extraction. 32 | | asset externals | List asset file dependencies. 33 | | asset header | Display some information from the file header. 34 | | asset objectids | List object identifiers (Unity 5 and higher only). 35 | | asset objects | List object data entries. 36 | | asset types | Display embedded runtime type information. 37 | | asset unpack | Unpacks raw data blocks from a file. Could be useful for manual extraction. 38 | 39 | ## Asset bundle commands 40 | 41 | | Command | Purpose 42 | | :---------------- | :---------------- 43 | | bundle list | List bundled files. 44 | | bundle info | Display some information from the file header. 45 | | bundle pack | Pack files into a bundle. Requires a bundle property file. 46 | | bundle unpack | Unpack files from a bundle. 47 | -------------------------------------------------------------------------------- /disunity-cli/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | DisUnity CLI 5 | disunity-cli 6 | jar 7 | Command line interface for DisUnity. 8 | 9 | 10 | info.ata4.disunity 11 | disunity 12 | 0.5-SNAPSHOT 13 | 14 | 15 | 16 | 17 | info.ata4.disunity 18 | disunity-core 19 | 0.5-SNAPSHOT 20 | 21 | 22 | 23 | com.beust 24 | jcommander 25 | 1.48 26 | 27 | 28 | 29 | 1.8 30 | 1.8 31 | 32 | 33 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/DisUnityCli.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 July 05 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli; 11 | 12 | import com.beust.jcommander.JCommander; 13 | import com.beust.jcommander.ParameterException; 14 | import info.ata4.disunity.DisUnity; 15 | import info.ata4.disunity.cli.command.DisUnityRoot; 16 | import info.ata4.log.LogUtils; 17 | import java.io.OutputStreamWriter; 18 | import java.io.PrintWriter; 19 | import java.util.logging.Level; 20 | import java.util.logging.Logger; 21 | 22 | /** 23 | * DisUnity command line interface. 24 | * 25 | * @author Nico Bergemann 26 | */ 27 | public class DisUnityCli { 28 | 29 | private static final Logger L = LogUtils.getLogger(); 30 | 31 | /** 32 | * @param args the command line arguments 33 | */ 34 | public static void main(String[] args) { 35 | LogUtils.configure(); 36 | 37 | try (PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out), true)) { 38 | JCommander jc = new JCommander(); 39 | DisUnityRoot root = new DisUnityRoot(); 40 | root.init(jc, out); 41 | 42 | jc.setProgramName(DisUnity.getProgramName()); 43 | jc.addObject(root); 44 | jc.parse(args); 45 | 46 | root.run(); 47 | } catch (ParameterException ex) { 48 | L.log(Level.WARNING, "Parameter error: {0}", ex.getMessage()); 49 | } catch (Throwable t) { 50 | L.log(Level.SEVERE, "Fatal error", t); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/OutputFormat.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 December 17 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli; 11 | 12 | /** 13 | * CLI text output format enum 14 | * 15 | * @author Nico Bergemann 16 | */ 17 | public enum OutputFormat { 18 | TEXT, JSON 19 | } 20 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/OutputFormatDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli; 11 | 12 | import com.beust.jcommander.Parameter; 13 | import java.util.function.Supplier; 14 | 15 | /** 16 | * 17 | * @author Nico Bergemann 18 | */ 19 | public class OutputFormatDelegate implements Supplier { 20 | 21 | @Parameter( 22 | names = { "-f", "--output-format" }, 23 | description = "Set output text format." 24 | ) 25 | private OutputFormat outputFormat = OutputFormat.TEXT; 26 | 27 | @Override 28 | public OutputFormat get() { 29 | return outputFormat; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/Command.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 27 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command; 11 | 12 | import com.beust.jcommander.JCommander; 13 | import info.ata4.disunity.DisUnity; 14 | import info.ata4.junity.progress.Progress; 15 | import java.io.PrintWriter; 16 | import java.util.Objects; 17 | 18 | /** 19 | * Abstract class for command actions. 20 | * 21 | * @author Nico Bergemann 22 | */ 23 | public abstract class Command implements Runnable { 24 | 25 | private JCommander commander; 26 | private PrintWriter out; 27 | 28 | protected final Progress progress = (s, p) -> { 29 | if (s.isPresent()) { 30 | output().println(s.get()); 31 | } 32 | }; 33 | 34 | public void init(JCommander commander, PrintWriter out) { 35 | this.commander = Objects.requireNonNull(commander); 36 | this.out = Objects.requireNonNull(out); 37 | } 38 | 39 | public JCommander commander() { 40 | return commander; 41 | } 42 | 43 | @Override 44 | public void run() { 45 | String commandName = commander.getParsedCommand(); 46 | if (!commander.getCommands().isEmpty()) { 47 | JCommander command = commander.getCommands().get(commandName); 48 | if (command != null && !command.getObjects().isEmpty()) { 49 | Command commandObj = (Command) command.getObjects().get(0); 50 | commandObj.run(); 51 | } else { 52 | // no command selected, show usage 53 | usage(); 54 | } 55 | } 56 | } 57 | 58 | protected JCommander addSubCommand(String commandName, Command commandObj) { 59 | commander.addCommand(commandName, commandObj); 60 | JCommander subCommander = commander.getCommands().get(commandName); 61 | commandObj.init(subCommander, out); 62 | return subCommander; 63 | } 64 | 65 | protected void usage() { 66 | output().println(DisUnity.getSignature()); 67 | commander().usage(); 68 | } 69 | 70 | protected PrintWriter output() { 71 | return out; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/DisUnityRoot.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 30 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command; 11 | 12 | import com.beust.jcommander.JCommander; 13 | import com.beust.jcommander.Parameter; 14 | import com.beust.jcommander.Parameters; 15 | import info.ata4.disunity.cli.command.asset.AssetRoot; 16 | import info.ata4.disunity.cli.command.bundle.BundleRoot; 17 | import info.ata4.log.LogUtils; 18 | import java.io.PrintWriter; 19 | import java.util.logging.Level; 20 | 21 | /** 22 | * 23 | * @author Nico Bergemann 24 | */ 25 | @Parameters 26 | public class DisUnityRoot extends Command { 27 | 28 | @Parameter( 29 | names = {"-h", "--help"}, 30 | description = "Print this help.", 31 | help = true 32 | ) 33 | private boolean help; 34 | 35 | @Parameter( 36 | names = { "-v", "--verbose" }, 37 | description = "Show more verbose log output." 38 | ) 39 | private boolean verbose; 40 | 41 | @Override 42 | public void init(JCommander commander, PrintWriter out) { 43 | super.init(commander, out); 44 | 45 | addSubCommand("bundle", new BundleRoot()); 46 | addSubCommand("asset", new AssetRoot()); 47 | } 48 | 49 | @Override 50 | public void run() { 51 | // increase logging level if requested 52 | if (verbose) { 53 | LogUtils.configure(Level.ALL); 54 | } 55 | 56 | // display usage 57 | if (help) { 58 | usage(); 59 | return; 60 | } 61 | 62 | super.run(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/FileCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command; 11 | 12 | import com.beust.jcommander.Parameter; 13 | import info.ata4.disunity.cli.converters.PathConverter; 14 | import java.nio.file.Path; 15 | import java.util.List; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public abstract class FileCommand extends Command { 22 | 23 | @Parameter( 24 | description = " [file]...", 25 | converter = PathConverter.class, 26 | required = true 27 | ) 28 | private List filePaths; 29 | 30 | @Override 31 | public void run() { 32 | filePaths.forEach(this::runFile); 33 | } 34 | 35 | protected abstract void runFile(Path file); 36 | } 37 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/RecursiveFileCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 03 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command; 11 | 12 | import com.beust.jcommander.Parameter; 13 | import info.ata4.log.LogUtils; 14 | import java.io.IOException; 15 | import static java.nio.file.FileVisitOption.FOLLOW_LINKS; 16 | import java.nio.file.Files; 17 | import java.nio.file.Path; 18 | import java.util.logging.Level; 19 | import java.util.logging.Logger; 20 | 21 | /** 22 | * 23 | * @author Nico Bergemann 24 | */ 25 | public abstract class RecursiveFileCommand extends FileCommand { 26 | 27 | private static final Logger L = LogUtils.getLogger(); 28 | 29 | @Parameter( 30 | names = { "-r", "--recursive" }, 31 | description = "Scan directories and sub-directories for files as well." 32 | ) 33 | private boolean recursive = defaultRecursive(); 34 | 35 | @Parameter( 36 | names = { "-d", "--recursive-depth" }, 37 | description = "The maximum number of directory levels to visit." 38 | ) 39 | private int maxDepth = defaultMaxDepth(); 40 | 41 | @Override 42 | protected void runFile(Path file) { 43 | if (Files.isDirectory(file) && recursive) { 44 | try { 45 | Files.walk(file, maxDepth, FOLLOW_LINKS) 46 | .filter(this::fileFilter) 47 | .forEach(this::runFileRecursive); 48 | } catch (IOException ex) { 49 | L.log(Level.WARNING, "Can't walk directory " + file, ex); 50 | } 51 | } else { 52 | runFileRecursive(file); 53 | } 54 | } 55 | 56 | protected abstract void runFileRecursive(Path file); 57 | 58 | protected boolean fileFilter(Path file) { 59 | return Files.isRegularFile(file); 60 | } 61 | 62 | protected boolean defaultRecursive() { 63 | return false; 64 | } 65 | 66 | protected int defaultMaxDepth() { 67 | return 1024; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetBlocks.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.Parameters; 13 | import info.ata4.disunity.cli.util.Formatters; 14 | import info.ata4.disunity.cli.util.TableBuilder; 15 | import info.ata4.disunity.cli.util.TableModel; 16 | import info.ata4.disunity.cli.util.TextTableFormat; 17 | import info.ata4.junity.serialize.SerializedFile; 18 | import info.ata4.util.io.DataBlock; 19 | import java.util.LinkedHashMap; 20 | import java.util.Map; 21 | 22 | /** 23 | * 24 | * @author Nico Bergemann 25 | */ 26 | @Parameters( 27 | commandDescription = "List file data blocks." 28 | ) 29 | public class AssetBlocks extends AssetTableCommand { 30 | 31 | @Override 32 | protected TableModel tableModel(SerializedFile serialized) { 33 | Map blocks = new LinkedHashMap<>(); 34 | 35 | blocks.put("Header", serialized.headerBlock()); 36 | blocks.put("Type Tree", serialized.metadata().typeTreeBlock()); 37 | blocks.put("Object Info", serialized.metadata().objectInfoBlock()); 38 | blocks.put("External Refs", serialized.metadata().externalsBlock()); 39 | blocks.put("Object Data", serialized.objectDataBlock()); 40 | 41 | TableBuilder table = new TableBuilder(); 42 | table.row("Name", "Offset", "Size"); 43 | 44 | blocks.entrySet().stream() 45 | .sorted((e1, e2) -> 46 | Long.compare( 47 | e1.getValue().offset(), 48 | e2.getValue().offset() 49 | ) 50 | ) 51 | .forEach(e -> { 52 | DataBlock block = e.getValue(); 53 | table.row( 54 | e.getKey(), 55 | block.offset(), 56 | block.length() 57 | ); 58 | }); 59 | 60 | TableModel model = new TableModel("Blocks", table.get()); 61 | TextTableFormat format = model.format(); 62 | format.columnFormatter(1, Formatters::hex); 63 | return model; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import info.ata4.disunity.cli.command.RecursiveFileCommand; 13 | import info.ata4.junity.bundle.Bundle; 14 | import info.ata4.junity.bundle.BundleEntry; 15 | import info.ata4.junity.bundle.BundleReader; 16 | import info.ata4.junity.bundle.BundleUtils; 17 | import info.ata4.junity.serialize.SerializedFile; 18 | import info.ata4.junity.serialize.SerializedFileReader; 19 | import info.ata4.log.LogUtils; 20 | import static info.ata4.util.function.IOConsumer.uncheck; 21 | import static info.ata4.util.function.Predicates.not; 22 | import java.io.IOException; 23 | import java.io.UncheckedIOException; 24 | import java.nio.file.Path; 25 | import java.util.logging.Level; 26 | import java.util.logging.Logger; 27 | 28 | /** 29 | * 30 | * @author Nico Bergemann 31 | */ 32 | public abstract class AssetCommand extends RecursiveFileCommand { 33 | 34 | private static final Logger L = LogUtils.getLogger(); 35 | 36 | @Override 37 | protected void runFileRecursive(Path file) { 38 | if (BundleUtils.isBundle(file)) { 39 | // file is a bundle, load serialized files from it 40 | try (BundleReader bundleReader = new BundleReader(file)) { 41 | Bundle bundle = bundleReader.read(); 42 | bundle.entries().stream() 43 | .filter(not(BundleEntry::isLibrary)) 44 | .filter(not(BundleEntry::isResource)) 45 | .forEach(uncheck(entry -> { 46 | try (SerializedFileReader reader = new SerializedFileReader( 47 | BundleUtils.dataReaderForEntry(entry))) { 48 | SerializedFile serialized = reader.read(); 49 | runSerializedFile(file.resolve(entry.name()), serialized); 50 | } 51 | }) 52 | ); 53 | } catch (UncheckedIOException | IOException ex) { 54 | L.log(Level.WARNING, "Can't open asset bundle " + file, ex); 55 | } 56 | } else { 57 | // load file directly 58 | try (SerializedFileReader reader = new SerializedFileReader(file)) { 59 | SerializedFile serialized = reader.read(); 60 | runSerializedFile(file, serialized); 61 | } catch (IOException ex) { 62 | L.log(Level.WARNING, "Can't open asset file " + file, ex); 63 | } 64 | } 65 | } 66 | 67 | protected abstract void runSerializedFile(Path file, SerializedFile serialized); 68 | } 69 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetExternalRefs.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.Parameters; 13 | import info.ata4.disunity.cli.util.TableBuilder; 14 | import info.ata4.disunity.cli.util.TableModel; 15 | import info.ata4.junity.serialize.SerializedFile; 16 | import info.ata4.junity.serialize.fileidentifier.FileIdentifier; 17 | import info.ata4.junity.serialize.fileidentifier.FileIdentifierTable; 18 | import info.ata4.junity.serialize.fileidentifier.FileIdentifierV2; 19 | 20 | /** 21 | * 22 | * @author Nico Bergemann 23 | */ 24 | @Parameters( 25 | commandDescription = "List external references." 26 | ) 27 | public class AssetExternalRefs extends AssetTableCommand { 28 | 29 | @Override 30 | protected TableModel tableModel(SerializedFile serialized) { 31 | FileIdentifierTable externals = serialized.metadata().externals(); 32 | Class factory = externals.elementFactory(); 33 | 34 | boolean v2 = FileIdentifierV2.class.isAssignableFrom(factory); 35 | 36 | TableBuilder table = new TableBuilder(); 37 | table.row("File Path"); 38 | 39 | if (v2) { 40 | table.append("Asset Path"); 41 | } 42 | 43 | table.append("GUID", "Type"); 44 | 45 | externals.elements().forEach(external -> { 46 | table.row(external.filePath()); 47 | 48 | if (v2) { 49 | table.append(((FileIdentifierV2) external).assetPath()); 50 | } 51 | 52 | table.append(external.guid(), external.type()); 53 | }); 54 | 55 | return new TableModel("External References", table.get()); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.Parameters; 13 | import info.ata4.disunity.cli.util.TableBuilder; 14 | import info.ata4.disunity.cli.util.TableModel; 15 | import info.ata4.junity.serialize.SerializedFile; 16 | import info.ata4.junity.serialize.SerializedFileHeader; 17 | 18 | /** 19 | * 20 | * @author Nico Bergemann 21 | */ 22 | @Parameters( 23 | commandDescription = "Show file header info." 24 | ) 25 | public class AssetHeader extends AssetTableCommand { 26 | 27 | @Override 28 | protected TableModel tableModel(SerializedFile serialized) { 29 | SerializedFileHeader header = serialized.header(); 30 | 31 | TableBuilder table = new TableBuilder(); 32 | table.row("Field", "Value"); 33 | 34 | table.row("metadataSize", header.metadataSize()); 35 | table.row("fileSize", header.fileSize()); 36 | table.row("version", header.version()); 37 | table.row("dataOffset", header.dataOffset()); 38 | 39 | if (header.version() >= 9) { 40 | table.row("endianness", header.endianness()); 41 | } 42 | 43 | return new TableModel("Header", table.get()); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetObjectIDs.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.Parameters; 13 | import info.ata4.disunity.cli.util.TableBuilder; 14 | import info.ata4.disunity.cli.util.TableModel; 15 | import info.ata4.junity.serialize.SerializedFile; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | @Parameters( 22 | commandDescription = "List object IDs (Unity 5+ only)." 23 | ) 24 | public class AssetObjectIDs extends AssetTableCommand { 25 | 26 | @Override 27 | protected TableModel tableModel(SerializedFile serialized) { 28 | TableBuilder table = new TableBuilder(); 29 | table.row("Asset Index", "ID in file"); 30 | 31 | serialized.metadata().objectIDTable().elements().forEach(objectID -> { 32 | table.row(objectID.serializedFileIndex(), objectID.identifierInFile()); 33 | }); 34 | 35 | return new TableModel("Object IDs", table.get()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetObjects.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.Parameters; 13 | import info.ata4.disunity.cli.util.Formatters; 14 | import info.ata4.disunity.cli.util.TableBuilder; 15 | import info.ata4.disunity.cli.util.TableModel; 16 | import info.ata4.disunity.cli.util.TextTableFormat; 17 | import info.ata4.junity.serialize.SerializedFile; 18 | import info.ata4.junity.serialize.SerializedFileMetadata; 19 | import info.ata4.junity.serialize.objectinfo.ObjectInfo; 20 | import info.ata4.junity.serialize.objectinfo.ObjectInfoV2; 21 | import info.ata4.junity.serialize.objectinfo.ObjectInfoV3; 22 | import info.ata4.junity.serialize.typetree.Type; 23 | import info.ata4.junity.serialize.typetree.TypeRoot; 24 | 25 | /** 26 | * 27 | * @author Nico Bergemann 28 | */ 29 | @Parameters( 30 | commandDescription = "List serialized objects." 31 | ) 32 | public class AssetObjects extends AssetTableCommand { 33 | 34 | @Override 35 | protected TableModel tableModel(SerializedFile serialized) { 36 | SerializedFileMetadata metadata = serialized.metadata(); 37 | 38 | TableBuilder table = new TableBuilder(); 39 | table.row("Path ID", "Offset", "Length", "Type ID", "Class ID"); 40 | 41 | Class factory = metadata.objectInfoTable().elementFactory(); 42 | 43 | boolean typeTreePresent = metadata.typeTree().embedded(); 44 | boolean v2 = ObjectInfoV2.class.isAssignableFrom(factory); 45 | boolean v3 = ObjectInfoV3.class.isAssignableFrom(factory); 46 | 47 | if (typeTreePresent) { 48 | table.append("Class Name"); 49 | } 50 | 51 | if (v2) { 52 | table.append("Script Type ID"); 53 | } 54 | 55 | if (v3) { 56 | table.append("Stripped"); 57 | } 58 | 59 | metadata.objectInfoTable().infoMap().entrySet().stream().forEach(e -> { 60 | ObjectInfo info = e.getValue(); 61 | table.row(e.getKey(), info.offset(), info.length(), info.typeID(), 62 | info.classID()); 63 | 64 | if (typeTreePresent) { 65 | TypeRoot baseClass = metadata.typeTree().typeMap().get(info.typeID()); 66 | String className = baseClass.nodes().data().typeName(); 67 | table.append(className); 68 | } 69 | 70 | if (v2) { 71 | table.append(((ObjectInfoV2) info).scriptTypeIndex()); 72 | } 73 | 74 | if (v3) { 75 | table.append(((ObjectInfoV3) info).isStripped()); 76 | } 77 | }); 78 | 79 | TableModel model = new TableModel("Objects", table.get()); 80 | TextTableFormat format = model.format(); 81 | format.columnFormatter(1, Formatters::hex); 82 | 83 | return model; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetRoot.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.JCommander; 13 | import com.beust.jcommander.Parameters; 14 | import info.ata4.disunity.cli.command.Command; 15 | import java.io.PrintWriter; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | @Parameters 22 | public class AssetRoot extends Command { 23 | 24 | @Override 25 | public void init(JCommander commander, PrintWriter out) { 26 | super.init(commander, out); 27 | 28 | addSubCommand("blocks", new AssetBlocks()); 29 | addSubCommand("externals", new AssetExternalRefs()); 30 | addSubCommand("header", new AssetHeader()); 31 | addSubCommand("objectids", new AssetObjectIDs()); 32 | addSubCommand("objects", new AssetObjects()); 33 | addSubCommand("types", new AssetTypes()); 34 | addSubCommand("unpack", new AssetUnpack()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetTableCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.ParametersDelegate; 13 | import info.ata4.disunity.cli.OutputFormatDelegate; 14 | import info.ata4.disunity.cli.util.TableModel; 15 | import info.ata4.disunity.cli.util.TablePrinter; 16 | import info.ata4.junity.serialize.SerializedFile; 17 | import java.nio.file.Path; 18 | 19 | /** 20 | * 21 | * @author Nico Bergemann 22 | */ 23 | public abstract class AssetTableCommand extends AssetCommand { 24 | 25 | @ParametersDelegate 26 | private final OutputFormatDelegate outputFormat = new OutputFormatDelegate(); 27 | 28 | @Override 29 | protected void runSerializedFile(Path file, SerializedFile serialized) { 30 | TablePrinter tablePrinter = TablePrinter.fromOutputFormat( 31 | outputFormat.get(), output()); 32 | tablePrinter.file(file); 33 | tablePrinter.print(tableModel(serialized)); 34 | } 35 | 36 | protected abstract TableModel tableModel(SerializedFile serialized); 37 | } 38 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetTypes.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 20 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.Parameters; 13 | import com.beust.jcommander.ParametersDelegate; 14 | import com.google.gson.Gson; 15 | import com.google.gson.GsonBuilder; 16 | import com.google.gson.JsonArray; 17 | import com.google.gson.JsonObject; 18 | import info.ata4.disunity.cli.OutputFormatDelegate; 19 | import info.ata4.junity.serialize.SerializedFile; 20 | import info.ata4.junity.serialize.typetree.Type; 21 | import info.ata4.junity.serialize.typetree.TypeTree; 22 | import info.ata4.util.collection.Node; 23 | import java.nio.file.Path; 24 | import org.apache.commons.lang3.StringUtils; 25 | 26 | /** 27 | * 28 | * @author Nico Bergemann 29 | */ 30 | @Parameters( 31 | commandDescription = "List embedded runtime types." 32 | ) 33 | public class AssetTypes extends AssetCommand { 34 | 35 | @ParametersDelegate 36 | private final OutputFormatDelegate outputFormat = new OutputFormatDelegate(); 37 | 38 | @Override 39 | protected void runSerializedFile(Path file, SerializedFile serialized) { 40 | TypeTree typeTree = serialized.metadata().typeTree(); 41 | 42 | switch (outputFormat.get()) { 43 | case JSON: 44 | printJson(file, typeTree); 45 | break; 46 | 47 | default: 48 | printText(file, typeTree); 49 | } 50 | } 51 | 52 | private void printText(Path file, TypeTree typeTree) { 53 | output().println(file); 54 | 55 | if (!typeTree.embedded()) { 56 | output().println("File doesn't contain type information"); 57 | return; 58 | } 59 | 60 | typeTree.typeMap().forEach((path, typeRoot) -> { 61 | output().printf("pathID: %d, classID: %d%n", path, typeRoot.classID()); 62 | printTypeNodeText(typeRoot.nodes(), 0); 63 | output().println(); 64 | }); 65 | } 66 | 67 | private void printTypeNodeText(Node node, int level) { 68 | String indent = StringUtils.repeat(" ", level); 69 | Type type = node.data(); 70 | output().printf("% 4d: %s%s %s (metaFlag: %x)%n", type.index(), indent, 71 | type.typeName(), type.fieldName(), type.metaFlag()); 72 | node.forEach(t -> printTypeNodeText(t, level + 1)); 73 | } 74 | 75 | private void printJson(Path file, TypeTree typeTree) { 76 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 77 | 78 | JsonObject jsonTypeTree = new JsonObject(); 79 | jsonTypeTree.addProperty("file", file.toString()); 80 | 81 | if (typeTree.embedded()) { 82 | JsonArray jsonTypes = new JsonArray(); 83 | 84 | typeTree.typeMap().forEach((path, typeRoot) -> { 85 | JsonObject jsonTypeNode = new JsonObject(); 86 | jsonTypeNode.addProperty("pathID", path); 87 | jsonTypeNode.addProperty("classID", typeRoot.classID()); 88 | if (typeRoot.scriptID() != null) { 89 | jsonTypeNode.addProperty("scriptID", typeRoot.scriptID().toString()); 90 | } 91 | if (typeRoot.oldTypeHash() != null) { 92 | jsonTypeNode.addProperty("oldTypeHash", typeRoot.oldTypeHash().toString()); 93 | } 94 | 95 | jsonTypeNode.add("nodes", typeNodeToJson(typeRoot.nodes(), gson)); 96 | 97 | jsonTypes.add(jsonTypeNode); 98 | }); 99 | 100 | jsonTypeTree.add("types", jsonTypes); 101 | } 102 | 103 | gson.toJson(jsonTypeTree, output()); 104 | } 105 | 106 | private JsonObject typeNodeToJson(Node node, Gson gson) { 107 | JsonObject jsonNode = new JsonObject(); 108 | 109 | jsonNode.add("data", gson.toJsonTree(node.data())); 110 | 111 | if (!node.isEmpty()) { 112 | JsonArray jsonChildren = new JsonArray(); 113 | node.forEach(childNode -> { 114 | jsonChildren.add(typeNodeToJson(childNode, gson)); 115 | }); 116 | jsonNode.add("children", jsonChildren); 117 | } 118 | 119 | return jsonNode; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/asset/AssetUnpack.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.asset; 11 | 12 | import com.beust.jcommander.Parameter; 13 | import com.beust.jcommander.Parameters; 14 | import info.ata4.io.buffer.ByteBufferUtils; 15 | import info.ata4.io.util.PathUtils; 16 | import info.ata4.junity.serialize.SerializedFile; 17 | import info.ata4.junity.serialize.SerializedObjectData; 18 | import info.ata4.log.LogUtils; 19 | import info.ata4.util.io.DataBlock; 20 | import java.io.IOException; 21 | import java.nio.ByteBuffer; 22 | import java.nio.channels.FileChannel; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import static java.nio.file.StandardOpenOption.*; 26 | import java.util.logging.Level; 27 | import java.util.logging.Logger; 28 | 29 | /** 30 | * 31 | * @author Nico Bergemann 32 | */ 33 | @Parameters( 34 | commandDescription = "Split asset file into data blocks." 35 | ) 36 | public class AssetUnpack extends AssetCommand { 37 | 38 | private static final Logger L = LogUtils.getLogger(); 39 | 40 | @Parameter( 41 | names = {"-l", "--level"}, 42 | description = "Unpacking level" 43 | ) 44 | private int level = 1; 45 | 46 | @Override 47 | protected void runSerializedFile(Path file, SerializedFile asset) { 48 | try { 49 | Path outputDir = PathUtils.removeExtension(file); 50 | 51 | if (Files.isRegularFile(outputDir)) { 52 | outputDir = PathUtils.append(outputDir, "_"); 53 | } 54 | 55 | if (Files.notExists(outputDir)) { 56 | Files.createDirectory(outputDir); 57 | } 58 | 59 | try (FileChannel fc = FileChannel.open(file)) { 60 | DataBlock headerBlock = asset.headerBlock(); 61 | Path headerFile = outputDir.resolve("header.block"); 62 | copyBlock(headerBlock, headerFile, fc); 63 | 64 | if (level > 0) { 65 | DataBlock typeTreeBlock = asset.metadata().typeTreeBlock(); 66 | Path typeTreeFile = outputDir.resolve("type_tree.block"); 67 | copyBlock(typeTreeBlock, typeTreeFile, fc); 68 | 69 | DataBlock objectInfoBlock = asset.metadata().objectInfoBlock(); 70 | Path objectInfoFile = outputDir.resolve("object_info.block"); 71 | copyBlock(objectInfoBlock, objectInfoFile, fc); 72 | 73 | DataBlock objectIDBlock = asset.metadata().objectIDBlock(); 74 | if (objectIDBlock.length() > 0) { 75 | Path objectIDFile = outputDir.resolve("object_ids.block"); 76 | copyBlock(objectIDBlock, objectIDFile, fc); 77 | } 78 | 79 | DataBlock fileIdentBlock = asset.metadata().externalsBlock(); 80 | Path fileIdentFile = outputDir.resolve("linked_files.block"); 81 | copyBlock(fileIdentBlock, fileIdentFile, fc); 82 | } else { 83 | DataBlock metadataBlock = asset.metadataBlock(); 84 | Path metadataFile = outputDir.resolve("metadata.block"); 85 | copyBlock(metadataBlock, metadataFile, fc); 86 | } 87 | 88 | if (level < 2) { 89 | DataBlock objectDataBlock = asset.objectDataBlock(); 90 | Path objectDataFile = outputDir.resolve("object_data.block"); 91 | copyBlock(objectDataBlock, objectDataFile, fc); 92 | } 93 | } 94 | 95 | if (level > 1) { 96 | Path objectDataDir = outputDir.resolve("object_data"); 97 | 98 | if (Files.notExists(objectDataDir)) { 99 | Files.createDirectory(objectDataDir); 100 | } 101 | 102 | for (SerializedObjectData od : asset.objectData()) { 103 | String objectDataName = String.format("%010d", od.id()); 104 | Path objectDataFile = objectDataDir.resolve(objectDataName + ".block"); 105 | ByteBuffer objectDataBuffer = od.buffer(); 106 | objectDataBuffer.rewind(); 107 | 108 | ByteBufferUtils.save(objectDataFile, objectDataBuffer); 109 | } 110 | } 111 | } catch (IOException ex) { 112 | L.log(Level.WARNING, "Can't unpack asset file " + file, ex); 113 | } 114 | } 115 | 116 | private void copyBlock(DataBlock block, Path file, FileChannel fc) throws IOException { 117 | try (FileChannel fcOut = FileChannel.open(file, CREATE, WRITE)) { 118 | fc.transferTo(block.offset(), block.length(), fcOut); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/bundle/BundleCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 30 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.bundle; 11 | 12 | import info.ata4.disunity.cli.command.RecursiveFileCommand; 13 | import info.ata4.junity.bundle.Bundle; 14 | import info.ata4.junity.bundle.BundleReader; 15 | import info.ata4.junity.bundle.BundleUtils; 16 | import info.ata4.log.LogUtils; 17 | import java.io.IOException; 18 | import java.nio.file.Path; 19 | import java.util.logging.Level; 20 | import java.util.logging.Logger; 21 | 22 | /** 23 | * 24 | * @author Nico Bergemann 25 | */ 26 | public abstract class BundleCommand extends RecursiveFileCommand { 27 | 28 | private static final Logger L = LogUtils.getLogger(); 29 | 30 | @Override 31 | protected void runFileRecursive(Path file) { 32 | try (BundleReader reader = new BundleReader(file)) { 33 | Bundle bundle = reader.read(); 34 | runBundle(file, bundle); 35 | } catch (IOException ex) { 36 | L.log(Level.WARNING, "Can't open asset bundle " + file, ex); 37 | } 38 | } 39 | 40 | @Override 41 | protected boolean fileFilter(Path file) { 42 | return BundleUtils.isBundle(file); 43 | } 44 | 45 | protected abstract void runBundle(Path file, Bundle bundle); 46 | } 47 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/bundle/BundleInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.bundle; 11 | 12 | import com.beust.jcommander.Parameters; 13 | import com.beust.jcommander.ParametersDelegate; 14 | import com.google.common.collect.Table; 15 | import info.ata4.disunity.cli.OutputFormat; 16 | import info.ata4.disunity.cli.OutputFormatDelegate; 17 | import info.ata4.disunity.cli.util.TableBuilder; 18 | import info.ata4.disunity.cli.util.TableModel; 19 | import info.ata4.disunity.cli.util.TablePrinter; 20 | import info.ata4.junity.bundle.Bundle; 21 | import info.ata4.junity.bundle.BundleHeader; 22 | import java.nio.file.Path; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | import org.apache.commons.lang3.tuple.Pair; 26 | 27 | /** 28 | * 29 | * @author Nico Bergemann 30 | */ 31 | @Parameters( 32 | commandDescription = "Show bundle information." 33 | ) 34 | public class BundleInfo extends BundleCommand { 35 | 36 | @ParametersDelegate 37 | private final OutputFormatDelegate outputFormat = new OutputFormatDelegate(); 38 | 39 | @Override 40 | protected void runBundle(Path file, Bundle bundle) { 41 | if (outputFormat.get() == OutputFormat.TEXT) { 42 | output().println(file); 43 | } 44 | 45 | List tables = new ArrayList<>(); 46 | tables.add(new TableModel("Header", buildHeaderTable(bundle.header()))); 47 | 48 | TablePrinter tablePrinter = TablePrinter.fromOutputFormat(outputFormat.get(), output()); 49 | tablePrinter.file(file); 50 | tablePrinter.print(tables); 51 | } 52 | 53 | private Table buildHeaderTable(BundleHeader header) { 54 | TableBuilder table = new TableBuilder(); 55 | table.row("Field", "Value"); 56 | 57 | table.row("signature", header.signature()); 58 | table.row("streamVersion", header.streamVersion()); 59 | table.row("unityVersion", header.unityVersion()); 60 | table.row("unityRevision", header.unityRevision()); 61 | table.row("minimumStreamedBytes", header.minimumStreamedBytes()); 62 | table.row("headerSize", header.headerSize()); 63 | table.row("numberOfLevelsToDownload", header.numberOfLevelsToDownload()); 64 | table.row("numberOfLevels", header.numberOfLevels()); 65 | 66 | List> levelByteEnds = header.levelByteEnd(); 67 | for (int i = 0; i < levelByteEnds.size(); i++) { 68 | Pair levelByteEnd = levelByteEnds.get(i); 69 | table.row("levelByteEnd[" + i + "][0]", levelByteEnd.getLeft()); 70 | table.row("levelByteEnd[" + i + "][1]", levelByteEnd.getRight()); 71 | } 72 | 73 | if (header.streamVersion() >= 2) { 74 | table.row("completeFileSize", header.completeFileSize()); 75 | } 76 | 77 | if (header.streamVersion() >= 3) { 78 | table.row("dataHeaderSize", header.dataHeaderSize()); 79 | } 80 | 81 | return table.get(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/bundle/BundleList.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 30 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.bundle; 11 | 12 | import com.beust.jcommander.Parameters; 13 | import com.beust.jcommander.ParametersDelegate; 14 | import com.google.common.collect.Table; 15 | import info.ata4.disunity.cli.OutputFormat; 16 | import info.ata4.disunity.cli.OutputFormatDelegate; 17 | import info.ata4.disunity.cli.util.Formatters; 18 | import info.ata4.disunity.cli.util.TableBuilder; 19 | import info.ata4.disunity.cli.util.TableModel; 20 | import info.ata4.disunity.cli.util.TablePrinter; 21 | import info.ata4.disunity.cli.util.TextTableFormat; 22 | import info.ata4.junity.bundle.Bundle; 23 | import java.nio.file.Path; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | /** 28 | * 29 | * @author Nico Bergemann 30 | */ 31 | @Parameters( 32 | commandDescription = "List all files in bundles." 33 | ) 34 | public class BundleList extends BundleCommand { 35 | 36 | @ParametersDelegate 37 | private final OutputFormatDelegate outputFormat = new OutputFormatDelegate(); 38 | 39 | @Override 40 | protected void runBundle(Path file, Bundle bundle) { 41 | if (outputFormat.get() == OutputFormat.TEXT) { 42 | output().println(file); 43 | } 44 | 45 | TableModel tableModel = new TableModel("Files", buildEntryTable(bundle)); 46 | TextTableFormat format = tableModel.format(); 47 | format.columnFormatter(2, Formatters::hex); 48 | 49 | List tables = new ArrayList<>(); 50 | tables.add(tableModel); 51 | 52 | TablePrinter tablePrinter = TablePrinter.fromOutputFormat(outputFormat.get(), output()); 53 | tablePrinter.file(file); 54 | tablePrinter.print(tables); 55 | } 56 | 57 | private Table buildEntryTable(Bundle bundle) { 58 | TableBuilder table = new TableBuilder(); 59 | table.row("Name", "Size", "Offset"); 60 | 61 | bundle.entryInfos().forEach(entry -> { 62 | table.row(entry.name(), entry.size(), entry.offset()); 63 | }); 64 | 65 | return table.get(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/bundle/BundlePack.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.bundle; 11 | 12 | import com.beust.jcommander.Parameter; 13 | import com.beust.jcommander.Parameters; 14 | import info.ata4.disunity.cli.command.FileCommand; 15 | import info.ata4.disunity.cli.converters.PathConverter; 16 | import info.ata4.io.util.PathUtils; 17 | import info.ata4.junity.bundle.Bundle; 18 | import info.ata4.junity.bundle.BundleWriter; 19 | import info.ata4.log.LogUtils; 20 | import java.io.IOException; 21 | import java.nio.file.Path; 22 | import java.util.logging.Level; 23 | import java.util.logging.Logger; 24 | 25 | /** 26 | * 27 | * @author Nico Bergemann 28 | */ 29 | @Parameters( 30 | commandDescription = "Create bundle from a property file." 31 | ) 32 | public class BundlePack extends FileCommand { 33 | 34 | private static final Logger L = LogUtils.getLogger(); 35 | 36 | @Parameter( 37 | names = {"-o", "--output"}, 38 | description = "Asset bundle output file", 39 | converter = PathConverter.class 40 | ) 41 | private Path outFile; 42 | 43 | @Override 44 | protected void runFile(Path file) { 45 | if (outFile == null) { 46 | String fileName = PathUtils.getBaseName(file); 47 | outFile = file.getParent().resolve(fileName + ".unity3d"); 48 | } 49 | 50 | Bundle bundle = new Bundle(); 51 | try (BundleWriter bundleWriter = new BundleWriter(outFile)) { 52 | BundleProps.read(file, bundle); 53 | bundleWriter.write(bundle, progress); 54 | } catch (IOException ex) { 55 | L.log(Level.WARNING, "Can't pack asset bundle " + file, ex); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/bundle/BundleProps.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 06 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.bundle; 11 | 12 | import com.google.gson.Gson; 13 | import com.google.gson.GsonBuilder; 14 | import info.ata4.io.util.PathUtils; 15 | import info.ata4.junity.UnityVersion; 16 | import info.ata4.junity.bundle.Bundle; 17 | import info.ata4.junity.bundle.BundleExternalEntry; 18 | import info.ata4.junity.bundle.BundleHeader; 19 | import java.io.IOException; 20 | import java.io.Reader; 21 | import java.io.Writer; 22 | import java.nio.charset.Charset; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import static java.nio.file.StandardOpenOption.CREATE; 26 | import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; 27 | import static java.nio.file.StandardOpenOption.WRITE; 28 | import java.util.List; 29 | import java.util.stream.Collectors; 30 | 31 | /** 32 | * 33 | * @author Nico Bergemann 34 | */ 35 | class BundleProps { 36 | 37 | private static final Charset CHARSET = Charset.forName("US-ASCII"); 38 | 39 | boolean compressed; 40 | int streamVersion; 41 | String unityVersion; 42 | String unityRevision; 43 | List files; 44 | 45 | static void write(Path propsFile, Bundle bundle) throws IOException { 46 | BundleProps props = new BundleProps(); 47 | BundleHeader header = bundle.header(); 48 | props.compressed = header.compressed(); 49 | props.streamVersion = header.streamVersion(); 50 | props.unityVersion = header.unityVersion().toString(); 51 | props.unityRevision = header.unityRevision().toString(); 52 | 53 | props.files = bundle.entryInfos().stream() 54 | .map(entry -> entry.name()) 55 | .collect(Collectors.toList()); 56 | 57 | try (Writer writer = Files.newBufferedWriter(propsFile, 58 | CHARSET, WRITE, CREATE, TRUNCATE_EXISTING)) { 59 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 60 | gson.toJson(props, writer); 61 | } 62 | } 63 | 64 | static void read(Path propsFile, Bundle bundle) throws IOException { 65 | BundleProps props; 66 | 67 | try (Reader reader = Files.newBufferedReader(propsFile, CHARSET)) { 68 | props = new Gson().fromJson(reader, BundleProps.class); 69 | } 70 | 71 | BundleHeader header = bundle.header(); 72 | header.compressed(props.compressed); 73 | header.streamVersion(props.streamVersion); 74 | header.unityVersion(new UnityVersion(props.unityVersion)); 75 | header.unityRevision(new UnityVersion(props.unityRevision)); 76 | 77 | String bundleName = PathUtils.getBaseName(propsFile); 78 | Path bundleDir = propsFile.resolveSibling(bundleName); 79 | 80 | props.files.stream().map(bundleDir::resolve).forEach(file -> { 81 | bundle.entries().add(new BundleExternalEntry(file)); 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/bundle/BundleRoot.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 30 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.bundle; 11 | 12 | import com.beust.jcommander.JCommander; 13 | import com.beust.jcommander.Parameters; 14 | import info.ata4.disunity.cli.command.Command; 15 | import java.io.PrintWriter; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | @Parameters 22 | public class BundleRoot extends Command { 23 | 24 | @Override 25 | public void init(JCommander commander, PrintWriter out) { 26 | super.init(commander, out); 27 | 28 | addSubCommand("list", new BundleList()); 29 | addSubCommand("info", new BundleInfo()); 30 | addSubCommand("pack", new BundlePack()); 31 | addSubCommand("unpack", new BundleUnpack()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/command/bundle/BundleUnpack.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.command.bundle; 11 | 12 | import com.beust.jcommander.Parameter; 13 | import com.beust.jcommander.Parameters; 14 | import info.ata4.disunity.cli.command.FileCommand; 15 | import info.ata4.disunity.cli.converters.PathConverter; 16 | import info.ata4.io.util.PathUtils; 17 | import info.ata4.junity.bundle.Bundle; 18 | import info.ata4.junity.bundle.BundleReader; 19 | import info.ata4.log.LogUtils; 20 | import static info.ata4.util.function.IOConsumer.uncheck; 21 | import java.io.IOException; 22 | import java.io.UncheckedIOException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; 26 | 27 | import java.nio.file.Paths; 28 | import java.util.Optional; 29 | import java.util.concurrent.atomic.AtomicInteger; 30 | import java.util.logging.Level; 31 | import java.util.logging.Logger; 32 | 33 | /** 34 | * 35 | * @author Nico Bergemann 36 | */ 37 | @Parameters( 38 | commandDescription = "Extract files from bundles." 39 | ) 40 | public class BundleUnpack extends FileCommand { 41 | 42 | private static final Logger L = LogUtils.getLogger(); 43 | 44 | @Parameter( 45 | names = {"-o", "--output"}, 46 | description = "Output directory", 47 | converter = PathConverter.class 48 | ) 49 | private Path outputDir; 50 | 51 | @Parameter( 52 | names = {"-f", "--filename"}, 53 | description = "Extract file with this name only." 54 | ) 55 | private String filename; 56 | 57 | @Parameter( 58 | names = {"-p", "--prop"}, 59 | description = "Write bundle property file." 60 | ) 61 | private boolean writeProp; 62 | 63 | @Override 64 | protected void runFile(Path file) { 65 | try (BundleReader bundleReader = new BundleReader(file)) { 66 | Bundle bundle = bundleReader.read(); 67 | 68 | AtomicInteger done = new AtomicInteger(); 69 | long total = bundle.entryInfos().size(); 70 | 71 | // define output directory, if not yet defined 72 | if (outputDir == null) { 73 | // if there's only one file inside the bundle, then don't bother 74 | // with sub-directories 75 | if (bundle.entryInfos().size() == 1) { 76 | outputDir = file.getParent(); 77 | if (outputDir == null) { 78 | // Passed a filename only. Use the current directory. 79 | outputDir = Paths.get("."); 80 | } 81 | } else { 82 | String fileName = PathUtils.getBaseName(file); 83 | outputDir = file.resolveSibling(fileName); 84 | } 85 | } 86 | 87 | try { 88 | bundle.entries() 89 | .stream() 90 | .filter(entry -> filename == null || entry.name().equals(filename)) 91 | .forEach(uncheck(entry -> { 92 | progress.update(Optional.of(entry.name()), done.getAndIncrement() / (double) total); 93 | Path entryFile = outputDir.resolve(entry.name()); 94 | 95 | Files.createDirectories(entryFile.getParent()); 96 | Files.copy(entry.inputStream(), entryFile, REPLACE_EXISTING); 97 | 98 | if (done.get() == total) { 99 | progress.update(Optional.empty(), 1); 100 | } 101 | })); 102 | } catch (UncheckedIOException ex) { 103 | throw ex.getCause(); 104 | } 105 | 106 | if (writeProp && filename == null) { 107 | String bundleName = outputDir.getFileName().toString(); 108 | Path propsFile = outputDir.getParent().resolve(bundleName + ".json"); 109 | BundleProps.write(propsFile, bundle); 110 | } 111 | } catch (IOException ex) { 112 | L.log(Level.WARNING, "Can't unpack asset bundle " + file, ex); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/converters/PathConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 Juni 28 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.converters; 11 | 12 | import com.beust.jcommander.IStringConverter; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class PathConverter implements IStringConverter { 21 | 22 | @Override 23 | public Path convert(String value) { 24 | return Paths.get(value); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/util/Formatters.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 25 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.util; 11 | 12 | /** 13 | * 14 | * @author Nico Bergemann 15 | */ 16 | public class Formatters { 17 | 18 | private Formatters() { 19 | } 20 | 21 | public static String hex(Object v) { 22 | if (!(v instanceof Number)) { 23 | return String.valueOf(v); 24 | } 25 | Number n = (Number) v; 26 | return String.format("%08x", n.intValue()); 27 | } 28 | 29 | public static String byteCount(Object v) { 30 | if (!(v instanceof Number)) { 31 | return String.valueOf(v); 32 | } 33 | 34 | long bytes = ((Number) v).longValue(); 35 | 36 | int unit = 1024; 37 | if (bytes < unit) { 38 | return bytes + " B"; 39 | } 40 | 41 | int exp = (int) (Math.log(bytes) / Math.log(unit)); 42 | String pre = "KMGTPE".charAt(exp - 1) + "i"; 43 | return String.format("%.2f %sB", bytes / Math.pow(unit, exp), pre); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/util/JsonTablePrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 27 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.util; 11 | 12 | import com.google.common.collect.Table; 13 | import com.google.gson.Gson; 14 | import com.google.gson.GsonBuilder; 15 | import com.google.gson.JsonArray; 16 | import com.google.gson.JsonObject; 17 | import com.google.gson.JsonPrimitive; 18 | import java.io.PrintWriter; 19 | import java.nio.file.Path; 20 | import java.util.Arrays; 21 | import java.util.Collection; 22 | 23 | /** 24 | * 25 | * @author Nico Bergemann 26 | */ 27 | public class JsonTablePrinter extends TablePrinter { 28 | 29 | public JsonTablePrinter(PrintWriter out) { 30 | super(out); 31 | } 32 | 33 | public JsonTablePrinter withFile(Path file) { 34 | this.file = file; 35 | return this; 36 | } 37 | 38 | @Override 39 | public void print(TableModel model) { 40 | print(Arrays.asList(model)); 41 | } 42 | 43 | @Override 44 | public void print(Collection models) { 45 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 46 | JsonObject jsonRoot = new JsonObject(); 47 | 48 | if (file != null) { 49 | jsonRoot.add("file", new JsonPrimitive(file.toString())); 50 | } 51 | 52 | models.forEach(model -> { 53 | jsonRoot.add(model.name().toLowerCase(), tableToJson(model.table(), gson)); 54 | }); 55 | 56 | gson.toJson(jsonRoot, out); 57 | } 58 | 59 | private JsonArray tableToJson(Table table, Gson gson) { 60 | JsonArray jsonTable = new JsonArray(); 61 | 62 | table.rowMap().forEach((rk, r) -> { 63 | if (rk == 0) { 64 | return; 65 | } 66 | 67 | JsonObject jsonRow = new JsonObject(); 68 | 69 | table.columnMap().forEach((ck, c) -> { 70 | String key = String.valueOf(table.get(0, ck)).toLowerCase(); 71 | Object value = table.get(rk, ck); 72 | jsonRow.add(key, gson.toJsonTree(value)); 73 | }); 74 | 75 | jsonTable.add(jsonRow); 76 | }); 77 | 78 | JsonObject jsonRoot = new JsonObject(); 79 | 80 | if (file != null) { 81 | jsonRoot.add("file", new JsonPrimitive(file.toString())); 82 | } 83 | 84 | return jsonTable; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/util/TableBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 24 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.util; 11 | 12 | import com.google.common.collect.Table; 13 | import com.google.common.collect.TreeBasedTable; 14 | import java.util.Map; 15 | import java.util.OptionalInt; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public class TableBuilder { 22 | 23 | private final Table table = TreeBasedTable.create(); 24 | 25 | public TableBuilder row(T... value) { 26 | int row = table.rowKeySet().size(); 27 | for (int col = 0; col < value.length; col++) { 28 | table.put(row, col, value[col]); 29 | } 30 | return this; 31 | } 32 | 33 | public TableBuilder append(T... value) { 34 | int row = table.rowKeySet().size() - 1; 35 | 36 | // check for empty table 37 | if (row < 0) { 38 | return this; 39 | } 40 | 41 | Map rowMap = table.row(row); 42 | OptionalInt colMax = rowMap.keySet().stream().mapToInt(Integer::valueOf).max(); 43 | int colOffset = colMax.isPresent() ? colMax.getAsInt() + 1 : 0; 44 | 45 | for (int col = 0; col < value.length; col++) { 46 | table.put(row, colOffset + col, value[col]); 47 | } 48 | 49 | return this; 50 | } 51 | 52 | public Table get() { 53 | return table; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/util/TableModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 27 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.util; 11 | 12 | import com.google.common.collect.Table; 13 | 14 | /** 15 | * 16 | * @author Nico Bergemann 17 | */ 18 | public class TableModel { 19 | 20 | private final Table table; 21 | private final String name; 22 | private boolean columnHeader = true; 23 | private TextTableFormat format = new TextTableFormat(); 24 | 25 | public TableModel(String name, Table table) { 26 | this.name = name; 27 | this.table = table; 28 | } 29 | 30 | public Table table() { 31 | return table; 32 | } 33 | 34 | public TextTableFormat format() { 35 | return format; 36 | } 37 | 38 | public void format(TextTableFormat format) { 39 | this.format = format; 40 | } 41 | 42 | public boolean columnHeader() { 43 | return columnHeader; 44 | } 45 | 46 | public void columnHeader(boolean columnHeader) { 47 | this.columnHeader = columnHeader; 48 | } 49 | 50 | public String name() { 51 | return name; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/util/TablePrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.util; 11 | 12 | import info.ata4.disunity.cli.OutputFormat; 13 | import java.io.PrintWriter; 14 | import java.nio.file.Path; 15 | import java.util.Collection; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public abstract class TablePrinter { 22 | 23 | public static TablePrinter fromOutputFormat(OutputFormat format, PrintWriter out) { 24 | switch (format) { 25 | case JSON: 26 | return new JsonTablePrinter(out); 27 | 28 | default: 29 | return new TextTablePrinter(out); 30 | } 31 | } 32 | 33 | protected final PrintWriter out; 34 | protected Path file; 35 | 36 | public TablePrinter(PrintWriter out) { 37 | this.out = out; 38 | } 39 | 40 | public void file(Path file) { 41 | this.file = file; 42 | } 43 | 44 | public abstract void print(TableModel model); 45 | 46 | public void print(Collection models) { 47 | models.forEach(this::print); 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/util/TextTableAlignment.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 29 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.util; 11 | 12 | /** 13 | * 14 | * @author Nico Bergemann 15 | */ 16 | public enum TextTableAlignment { 17 | LEFT, CENTER, RIGHT, AUTO 18 | } 19 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/util/TextTableFormat.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 27 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.util; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.Optional; 15 | import java.util.function.Function; 16 | import java.util.stream.Collectors; 17 | import org.apache.commons.lang3.StringUtils; 18 | 19 | /** 20 | * 21 | * @author Nico Bergemann 22 | */ 23 | public class TextTableFormat { 24 | 25 | private final Map columnWidths = new HashMap<>(); 26 | private final Map columnAlignments = new HashMap<>(); 27 | private final Map> columnFormatters = new HashMap<>(); 28 | private int numColumns; 29 | 30 | public void columnWidth(int column, int width) { 31 | columnWidths.put(column, width); 32 | } 33 | 34 | public int columnWidth(int column) { 35 | return columnWidths.get(column); 36 | } 37 | 38 | public void columnAlignment(int column, TextTableAlignment align) { 39 | columnAlignments.put(column, align); 40 | } 41 | 42 | public TextTableAlignment columnAlignment(int column) { 43 | return columnAlignments.get(column); 44 | } 45 | 46 | public void columnFormatter(int column, Function formatter) { 47 | columnFormatters.put(column, formatter); 48 | } 49 | 50 | public Function columnFormatter(int column) { 51 | return columnFormatters.get(column); 52 | } 53 | 54 | void configure(TableModel model) { 55 | numColumns = 0; 56 | 57 | model.table().columnKeySet().stream().forEach(columnKey -> { 58 | if (!columnFormatters.containsKey(columnKey)) { 59 | columnFormatters.put(columnKey, String::valueOf); 60 | } 61 | 62 | // set minimum column width if not already defined 63 | if (!columnWidths.containsKey(columnKey)) { 64 | columnWidths.put(columnKey, model.table() 65 | .column(columnKey) 66 | .values() 67 | .stream() 68 | .map(columnFormatters.get(columnKey)) 69 | .mapToInt(String::length) 70 | .max() 71 | .getAsInt() 72 | ); 73 | } 74 | 75 | if (!columnAlignments.containsKey(columnKey) 76 | || columnAlignments.get(columnKey) == TextTableAlignment.AUTO) { 77 | // count class types 78 | Map columnTypeMap = model.table() 79 | .column(columnKey) 80 | .values() 81 | .stream() 82 | .skip(model.columnHeader() ? 1 : 0) // don't include type of column header 83 | .map(Object::getClass) 84 | .collect(Collectors.groupingBy(o -> o, Collectors.counting())); 85 | 86 | // get most occurring class 87 | Optional> topClassEntry = columnTypeMap 88 | .entrySet() 89 | .stream() 90 | .max((v1, v2) -> Long.compare(v1.getValue(), v2.getValue())); 91 | 92 | Class columnType = Object.class; 93 | 94 | if (topClassEntry.isPresent()) { 95 | columnType = topClassEntry.get().getKey(); 96 | } 97 | 98 | // align number columns to the right for better readability 99 | boolean isNumber = Number.class.isAssignableFrom(columnType); 100 | TextTableAlignment align; 101 | 102 | if (isNumber) { 103 | align = TextTableAlignment.RIGHT; 104 | } else { 105 | align = TextTableAlignment.LEFT; 106 | } 107 | 108 | columnAlignments.put(columnKey, align); 109 | } 110 | 111 | numColumns++; 112 | }); 113 | } 114 | 115 | int tableWidth(String cellSeparator) { 116 | return columnWidths.values().stream() 117 | .reduce(0, (a, b) -> a + b) 118 | + cellSeparator.length() * (columnWidths.size() - 1); 119 | } 120 | 121 | String formatCell(Object value, int column) { 122 | int width = columnWidths.get(column); 123 | TextTableAlignment align = columnAlignments.get(column); 124 | Function formatter = columnFormatters.get(column); 125 | String content = formatter.apply(value); 126 | 127 | if (content.length() > width) { 128 | // truncate 129 | content = StringUtils.abbreviate(content, width); 130 | } else if (content.length() < width) { 131 | // add padding 132 | switch (align) { 133 | case LEFT: 134 | content = StringUtils.rightPad(content, width); 135 | break; 136 | case RIGHT: 137 | content = StringUtils.leftPad(content, width); 138 | break; 139 | case CENTER: 140 | content = StringUtils.center(content, width); 141 | break; 142 | } 143 | } 144 | 145 | return content; 146 | } 147 | 148 | int numColumns() { 149 | return numColumns; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /disunity-cli/src/main/java/info/ata4/disunity/cli/util/TextTablePrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity.cli.util; 11 | 12 | import com.google.common.collect.Table; 13 | import java.io.PrintWriter; 14 | import org.apache.commons.lang3.StringUtils; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class TextTablePrinter extends TablePrinter { 21 | 22 | private final char rowSeparator = '-'; 23 | private final char nameSeparator = '='; 24 | private final String cellSeparator = " "; 25 | 26 | public TextTablePrinter(PrintWriter out) { 27 | super(out); 28 | } 29 | 30 | @Override 31 | public void print(TableModel model) { 32 | TextTableFormat format = model.format(); 33 | format.configure(model); 34 | 35 | out.println(file); 36 | 37 | // print table name 38 | String name = model.name(); 39 | name = " " + name + " "; 40 | int size = Math.max(name.length() + 2, format.tableWidth(cellSeparator)); 41 | name = StringUtils.center(name, size, nameSeparator); 42 | out.println(name); 43 | 44 | // print cells 45 | model.table().cellSet().forEach(cell -> printCell(model, format, cell)); 46 | out.println(); 47 | out.println(); 48 | } 49 | 50 | private void printCell(TableModel model, TextTableFormat format, Table.Cell cell) { 51 | int numColumns = format.numColumns(); 52 | int colKey = cell.getColumnKey(); 53 | int rowKey = cell.getRowKey(); 54 | 55 | // print new line after the last cell of a row 56 | if (colKey == 0) { 57 | if (rowKey != 0) { 58 | out.println(); 59 | } 60 | 61 | // print column header separator for first and second row 62 | if (model.columnHeader() && colKey == 0 && rowKey == 1) { 63 | for (int i = 0; i < numColumns; i++) { 64 | out.print(StringUtils.repeat(rowSeparator, format.columnWidth(i))); 65 | if (i != numColumns - 1) { 66 | out.print(cellSeparator); 67 | } 68 | } 69 | out.println(); 70 | } 71 | } 72 | 73 | out.print(format.formatCell(cell.getValue(), colKey)); 74 | 75 | // print cell separator unless it's the last cell 76 | if (colKey != numColumns - 1) { 77 | out.print(cellSeparator); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /disunity-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | DisUnity Core 5 | disunity-core 6 | jar 7 | Shared core libraries for DisUnity. 8 | 9 | 10 | info.ata4.disunity 11 | disunity 12 | 0.5-SNAPSHOT 13 | 14 | 15 | 16 | 17 | 18 | src/main/resources 19 | resources 20 | 21 | 22 | 23 | 24 | 25 | junit 26 | junit 27 | 4.11 28 | test 29 | jar 30 | 31 | 32 | org.hamcrest 33 | hamcrest-core 34 | 1.3 35 | test 36 | 37 | 38 | 39 | 1.8 40 | 1.8 41 | 42 | 43 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/disunity/DisUnity.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 June 17 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.disunity; 11 | 12 | /** 13 | * DisUnity program metadata. 14 | * 15 | * @author Nico Bergemann 16 | */ 17 | public class DisUnity { 18 | 19 | public static String getName() { 20 | return "DisUnity"; 21 | } 22 | 23 | public static String getProgramName() { 24 | return "disunity"; 25 | } 26 | 27 | public static String getVersion() { 28 | return "0.5.0"; 29 | } 30 | 31 | public static String getSignature() { 32 | return getName() + " v" + getVersion(); 33 | } 34 | 35 | private DisUnity() { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/UnityGUID.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 April 09 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.io.Struct; 15 | import java.io.IOException; 16 | import java.nio.ByteOrder; 17 | import java.util.Objects; 18 | import java.util.UUID; 19 | 20 | /** 21 | * 22 | * @author Nico Bergemann 23 | */ 24 | public class UnityGUID implements Struct { 25 | 26 | private UUID uuid; 27 | 28 | public UUID uuid() { 29 | return uuid; 30 | } 31 | 32 | public void uuid(UUID uuid) { 33 | this.uuid = Objects.requireNonNull(uuid); 34 | } 35 | 36 | @Override 37 | public void read(DataReader in) throws IOException { 38 | // read GUID as big-endian 39 | ByteOrder order = in.order(); 40 | in.order(ByteOrder.BIG_ENDIAN); 41 | long guidMost = in.readLong(); 42 | long guidLeast = in.readLong(); 43 | in.order(order); 44 | uuid = new UUID(guidMost, guidLeast); 45 | } 46 | 47 | @Override 48 | public void write(DataWriter out) throws IOException { 49 | // write GUID as big-endian 50 | ByteOrder order = out.order(); 51 | out.order(ByteOrder.BIG_ENDIAN); 52 | out.writeLong(uuid.getMostSignificantBits()); 53 | out.writeLong(uuid.getLeastSignificantBits()); 54 | out.order(order); 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return uuid().toString(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/UnityHash128.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 April 20 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.io.Struct; 15 | import java.io.IOException; 16 | import javax.xml.bind.DatatypeConverter; 17 | 18 | /** 19 | * 20 | * @author Nico Bergemann 21 | */ 22 | public class UnityHash128 implements Struct { 23 | 24 | private final byte[] hash = new byte[16]; 25 | 26 | public byte[] hash() { 27 | return hash; 28 | } 29 | 30 | @Override 31 | public void read(DataReader in) throws IOException { 32 | in.readBytes(hash); 33 | } 34 | 35 | @Override 36 | public void write(DataWriter out) throws IOException { 37 | out.writeBytes(hash); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return DatatypeConverter.printHexBinary(hash); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/UnityStruct.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity; 11 | 12 | import info.ata4.io.Struct; 13 | 14 | /** 15 | * 16 | * @author Nico Bergemann 17 | */ 18 | public abstract class UnityStruct implements Struct { 19 | 20 | protected final Class elementFactory; 21 | 22 | public UnityStruct(Class elementFactory) { 23 | this.elementFactory = elementFactory; 24 | } 25 | 26 | public Class elementFactory() { 27 | return elementFactory; 28 | } 29 | 30 | protected T createElement() { 31 | try { 32 | return elementFactory.newInstance(); 33 | } catch (IllegalAccessException | InstantiationException ex) { 34 | throw new RuntimeException(ex); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/UnityTableStruct.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.io.Struct; 15 | import java.io.IOException; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | * 21 | * @author Nico Bergemann 22 | */ 23 | public class UnityTableStruct extends UnityStruct { 24 | 25 | private final List elements = new ArrayList<>(); 26 | 27 | public UnityTableStruct(Class elementFactory) { 28 | super(elementFactory); 29 | } 30 | 31 | public List elements() { 32 | return elements; 33 | } 34 | 35 | @Override 36 | public void read(DataReader in) throws IOException { 37 | elements.clear(); 38 | int entries = in.readInt(); 39 | for (int i = 0; i < entries; i++) { 40 | elements.add(readEntry(in)); 41 | } 42 | } 43 | 44 | protected T readEntry(DataReader in) throws IOException { 45 | T element = createElement(); 46 | in.readStruct(element); 47 | return element; 48 | } 49 | 50 | @Override 51 | public void write(DataWriter out) throws IOException { 52 | int entries = elements.size(); 53 | out.writeInt(entries); 54 | for (T element : elements) { 55 | writeEntry(out, element); 56 | } 57 | } 58 | 59 | protected void writeEntry(DataWriter out, T element) throws IOException { 60 | out.writeStruct(element); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/UnityVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 February 03 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity; 11 | 12 | import java.util.Objects; 13 | 14 | /** 15 | * Unity engine version string container. 16 | * 17 | * @author Nico Bergemann 18 | */ 19 | public class UnityVersion implements Comparable { 20 | 21 | private byte major; 22 | private byte minor; 23 | private byte patch; 24 | private String build; 25 | private String raw; 26 | 27 | public UnityVersion(String version) { 28 | try { 29 | major = partFromString(version.substring(0, 1)); 30 | minor = partFromString(version.substring(2, 3)); 31 | patch = partFromString(version.substring(4, 5)); 32 | build = version.substring(5); 33 | } catch (NumberFormatException | IndexOutOfBoundsException ex) { 34 | // invalid format, save raw string 35 | raw = version; 36 | } 37 | } 38 | 39 | public UnityVersion() { 40 | this("1.0.0f1"); 41 | } 42 | 43 | private byte partFromString(String part) { 44 | if (part.equals("x")) { 45 | return -1; 46 | } else { 47 | return Byte.valueOf(part); 48 | } 49 | } 50 | 51 | private String partToString(byte part) { 52 | if (part == -1) { 53 | return "x"; 54 | } else { 55 | return String.valueOf(part); 56 | } 57 | } 58 | 59 | public boolean isValid() { 60 | return raw == null; 61 | } 62 | 63 | public byte major() { 64 | return major; 65 | } 66 | 67 | public void major(byte major) { 68 | this.major = major; 69 | } 70 | 71 | public byte minor() { 72 | return minor; 73 | } 74 | 75 | public void minor(byte minor) { 76 | this.minor = minor; 77 | } 78 | 79 | public byte patch() { 80 | return patch; 81 | } 82 | 83 | public void patch(byte patch) { 84 | this.patch = patch; 85 | } 86 | 87 | public String build() { 88 | return build; 89 | } 90 | 91 | public void build(String build) { 92 | this.build = build; 93 | } 94 | 95 | @Override 96 | public String toString() { 97 | if (raw != null) { 98 | return raw; 99 | } else { 100 | return String.format("%s.%s.%s%s", partToString(major), 101 | partToString(minor), partToString(patch), build); 102 | } 103 | } 104 | 105 | @Override 106 | public int hashCode() { 107 | if (raw != null) { 108 | return raw.hashCode(); 109 | } else { 110 | int hash = 5; 111 | hash = 97 * hash + this.major; 112 | hash = 97 * hash + this.minor; 113 | hash = 97 * hash + this.patch; 114 | hash = 97 * hash + Objects.hashCode(this.build); 115 | return hash; 116 | } 117 | } 118 | 119 | @Override 120 | public boolean equals(Object obj) { 121 | if (obj == null) { 122 | return false; 123 | } 124 | if (getClass() != obj.getClass()) { 125 | return false; 126 | } 127 | final UnityVersion other = (UnityVersion) obj; 128 | if (raw != null) { 129 | if (!Objects.equals(this.raw, other.raw)) { 130 | return false; 131 | } 132 | } else { 133 | if (this.major != other.major) { 134 | return false; 135 | } 136 | if (this.minor != other.minor) { 137 | return false; 138 | } 139 | if (this.patch != other.patch) { 140 | return false; 141 | } 142 | if (!Objects.equals(this.build, other.build)) { 143 | return false; 144 | } 145 | if (!Objects.equals(this.raw, other.raw)) { 146 | return false; 147 | } 148 | } 149 | return true; 150 | } 151 | 152 | @Override 153 | public int compareTo(UnityVersion that) { 154 | if (!this.isValid() && !that.isValid()) { 155 | return this.raw.compareTo(that.raw); 156 | } 157 | 158 | if (this.major < that.major) { 159 | return 1; 160 | } else if (this.major > that.major) { 161 | return -1; 162 | } else { 163 | if (this.minor < that.minor) { 164 | return 1; 165 | } else if (this.minor > that.minor) { 166 | return -1; 167 | } else { 168 | if (this.patch < that.patch) { 169 | return 1; 170 | } else if (this.patch > that.patch) { 171 | return -1; 172 | } else { 173 | return this.build.compareTo(that.build); 174 | } 175 | } 176 | } 177 | } 178 | 179 | public boolean lesserThan(UnityVersion that) { 180 | return this.compareTo(that) == 1; 181 | } 182 | 183 | public boolean greaterThan(UnityVersion that) { 184 | return this.compareTo(that) == -1; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/bundle/Bundle.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 29 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.bundle; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * 17 | * @author Nico Bergemann 18 | */ 19 | public class Bundle { 20 | 21 | private final BundleHeader header = new BundleHeader(); 22 | private final List entries = new ArrayList<>(); 23 | private final List entryInfos = new ArrayList<>(); 24 | 25 | public BundleHeader header() { 26 | return header; 27 | } 28 | 29 | public List entries() { 30 | return entries; 31 | } 32 | 33 | public List entryInfos() { 34 | return entryInfos; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/bundle/BundleEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 September 29 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.bundle; 11 | 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import org.apache.commons.io.FilenameUtils; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public abstract class BundleEntry { 21 | 22 | public static boolean isLibrary(BundleEntry entry) { 23 | String ext = FilenameUtils.getExtension(entry.name()); 24 | return ext.equals("dll") || ext.equals("mdb"); 25 | } 26 | 27 | public static boolean isResource(BundleEntry entry) { 28 | String ext = FilenameUtils.getExtension(entry.name()); 29 | return ext.equals("resource"); 30 | } 31 | 32 | public abstract String name(); 33 | 34 | public abstract long size(); 35 | 36 | public abstract InputStream inputStream() throws IOException; 37 | } 38 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/bundle/BundleEntryInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 September 25 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.bundle; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.io.Struct; 15 | import java.io.IOException; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | * @unity StreamingInfo 21 | */ 22 | public class BundleEntryInfo implements Struct { 23 | 24 | private String name; 25 | private long offset; 26 | private long size; 27 | 28 | public String name() { 29 | return name; 30 | } 31 | 32 | public void name(String name) { 33 | this.name = name; 34 | } 35 | 36 | public long offset() { 37 | return offset; 38 | } 39 | 40 | public void offset(long offset) { 41 | this.offset = offset; 42 | } 43 | 44 | public long size() { 45 | return size; 46 | } 47 | 48 | public void size(long size) { 49 | this.size = size; 50 | } 51 | 52 | @Override 53 | public void read(DataReader in) throws IOException { 54 | name = in.readStringNull(); 55 | offset = in.readUnsignedInt(); 56 | size = in.readUnsignedInt(); 57 | } 58 | 59 | @Override 60 | public void write(DataWriter out) throws IOException { 61 | out.writeStringNull(name); 62 | out.writeUnsignedInt(offset); 63 | out.writeUnsignedInt(size); 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return name(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/bundle/BundleEntryInfoFS.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 September 25 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.bundle; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | 15 | import java.io.IOException; 16 | 17 | /** 18 | * UnityFS-format bundle entry info 19 | */ 20 | public class BundleEntryInfoFS extends BundleEntryInfo { 21 | 22 | // unknown extra field, guessing flags 23 | private long flags; 24 | 25 | public long flags() { return flags; }; 26 | 27 | public void flags(long flags) { this.flags = flags; } 28 | 29 | @Override 30 | public void read(DataReader in) throws IOException { 31 | offset(in.readLong()); 32 | size(in.readLong()); 33 | flags = in.readUnsignedInt(); 34 | name(in.readStringNull()); 35 | } 36 | 37 | @Override 38 | public void write(DataWriter out) throws IOException { 39 | out.writeLong(offset()); 40 | out.writeLong(size()); 41 | out.writeUnsignedInt(flags); 42 | out.writeStringNull(name()); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return name(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/bundle/BundleException.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 April 08 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.bundle; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * IOException used for asset bundle errors. 16 | * 17 | * @author Nico Bergemann 18 | */ 19 | public class BundleException extends IOException { 20 | 21 | /** 22 | * Creates a new instance of 23 | * AssetException without detail message. 24 | */ 25 | public BundleException() { 26 | } 27 | 28 | /** 29 | * Constructs an instance of 30 | * AssetException with the specified detail message. 31 | * 32 | * @param msg the detail message. 33 | */ 34 | public BundleException(String msg) { 35 | super(msg); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/bundle/BundleExternalEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 December 03 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.bundle; 11 | 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public class BundleExternalEntry extends BundleEntry { 22 | 23 | private final Path file; 24 | 25 | public BundleExternalEntry(Path file) { 26 | this.file = file; 27 | } 28 | 29 | @Override 30 | public String name() { 31 | return file.getFileName().toString(); 32 | } 33 | 34 | @Override 35 | public long size() { 36 | try { 37 | return Files.size(file); 38 | } catch (IOException ex) { 39 | return 0; 40 | } 41 | } 42 | 43 | @Override 44 | public InputStream inputStream() throws IOException { 45 | return Files.newInputStream(file); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/bundle/BundleInternalEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 December 03 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.bundle; 11 | 12 | import info.ata4.util.function.IOFunction; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class BundleInternalEntry extends BundleEntry { 21 | 22 | private final BundleEntryInfo info; 23 | private final IOFunction inputStreamFactory; 24 | 25 | public BundleInternalEntry(BundleEntryInfo info, 26 | IOFunction isFactory) { 27 | this.info = info; 28 | this.inputStreamFactory = isFactory; 29 | } 30 | 31 | @Override 32 | public String name() { 33 | return info.name(); 34 | } 35 | 36 | @Override 37 | public long size() { 38 | return info.size(); 39 | } 40 | 41 | @Override 42 | public InputStream inputStream() throws IOException { 43 | return inputStreamFactory.apply(info); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return name(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/bundle/BundleUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 September 25 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.bundle; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataReaders; 14 | import info.ata4.io.buffer.ByteBufferChannel; 15 | import info.ata4.io.buffer.ByteBufferOutputStream; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.nio.ByteBuffer; 19 | import java.nio.channels.SeekableByteChannel; 20 | import java.nio.charset.Charset; 21 | import java.nio.file.Files; 22 | import java.nio.file.Path; 23 | import static java.nio.file.StandardCopyOption.*; 24 | import static java.nio.file.StandardOpenOption.*; 25 | import org.apache.commons.io.IOUtils; 26 | 27 | /** 28 | * Asset bundle file utility class. 29 | * 30 | * @author Nico Bergemann 31 | */ 32 | public class BundleUtils { 33 | 34 | private static final Charset PROP_CHARSET = Charset.forName("US-ASCII"); 35 | 36 | private BundleUtils() { 37 | } 38 | 39 | public static boolean isBundle(Path file) { 40 | if (!Files.isRegularFile(file)) { 41 | return false; 42 | } 43 | 44 | try (InputStream is = Files.newInputStream(file)) { 45 | byte[] header = new byte[8]; 46 | is.read(header); 47 | String headerString = new String(header, PROP_CHARSET); 48 | return headerString.equals(BundleHeader.SIGNATURE_WEB) 49 | || headerString.equals(BundleHeader.SIGNATURE_RAW); 50 | } catch (IOException ex) { 51 | return false; 52 | } 53 | } 54 | 55 | public static SeekableByteChannel byteChannelForEntry(BundleEntry entry) throws IOException { 56 | SeekableByteChannel chan; 57 | 58 | // check if the entry is larger than 128 MiB 59 | long size = entry.size(); 60 | if (size > 1 << 27) { 61 | // copy entry to temporary file 62 | Path tmpFile = Files.createTempFile("disunity", null); 63 | Files.copy(entry.inputStream(), tmpFile, REPLACE_EXISTING); 64 | chan = Files.newByteChannel(tmpFile, READ, DELETE_ON_CLOSE); 65 | } else { 66 | // copy entry to memory 67 | ByteBuffer bb = ByteBuffer.allocateDirect((int) size); 68 | IOUtils.copy(entry.inputStream(), new ByteBufferOutputStream(bb)); 69 | bb.flip(); 70 | chan = new ByteBufferChannel(bb); 71 | } 72 | 73 | return chan; 74 | } 75 | 76 | public static DataReader dataReaderForEntry(BundleEntry entry) throws IOException { 77 | return DataReaders.forSeekableByteChannel(BundleUtils.byteChannelForEntry(entry)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/progress/Progress.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.progress; 11 | 12 | import java.util.Optional; 13 | 14 | /** 15 | * 16 | * @author Nico Bergemann 17 | */ 18 | public interface Progress { 19 | 20 | void update(Optional stage, double complete); 21 | } 22 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/SerializedFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize; 11 | 12 | import info.ata4.util.io.DataBlock; 13 | import java.nio.ByteBuffer; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | * @unity SerializedFile 21 | */ 22 | public class SerializedFile { 23 | 24 | // struct fields 25 | private final SerializedFileHeader header = new SerializedFileHeader(); 26 | private final SerializedFileMetadata metadata = new SerializedFileMetadata(); 27 | 28 | // data block fields 29 | private final DataBlock headerBlock = new DataBlock(); 30 | private final DataBlock metadataBlock = new DataBlock(); 31 | private final DataBlock objectDataBlock = new DataBlock(); 32 | 33 | // misc fields 34 | private final List objectData = new ArrayList<>(); 35 | private ByteBuffer audioBuffer; 36 | 37 | public SerializedFileHeader header() { 38 | return header; 39 | } 40 | 41 | public SerializedFileMetadata metadata() { 42 | return metadata; 43 | } 44 | 45 | public List objectData() { 46 | return objectData; 47 | } 48 | 49 | public DataBlock headerBlock() { 50 | return headerBlock; 51 | } 52 | 53 | public DataBlock metadataBlock() { 54 | return metadataBlock; 55 | } 56 | 57 | public DataBlock objectDataBlock() { 58 | return objectDataBlock; 59 | } 60 | 61 | public List dataBlocks() { 62 | List blocks = new ArrayList<>(); 63 | blocks.add(headerBlock); 64 | blocks.addAll(metadata().dataBlocks()); 65 | return blocks; 66 | } 67 | 68 | public ByteBuffer audioBuffer() { 69 | return audioBuffer; 70 | } 71 | 72 | public void audioBuffer(ByteBuffer audioBuffer) { 73 | this.audioBuffer = audioBuffer; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/SerializedFileException.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 July 12 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * 16 | * @author Nico Bergemann 17 | */ 18 | public class SerializedFileException extends IOException { 19 | 20 | /** 21 | * Creates a new instance of 22 | * AssetException without detail message. 23 | */ 24 | public SerializedFileException() { 25 | } 26 | 27 | /** 28 | * Constructs an instance of 29 | * AssetException with the specified detail message. 30 | * 31 | * @param msg the detail message. 32 | */ 33 | public SerializedFileException(String msg) { 34 | super(msg); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/SerializedFileHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 June 16 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.io.Struct; 15 | import java.io.IOException; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | * @unity SerializedFileHeader 21 | */ 22 | public class SerializedFileHeader implements Struct { 23 | 24 | // size of the structure data 25 | private long metadataSize; 26 | 27 | // size of the whole asset file 28 | private long fileSize; 29 | 30 | // 5 = 1.2 - 2.0 31 | // 6 = 2.1 - 2.6 32 | // 7 = 3.0 (?) 33 | // 8 = 3.1 - 3.4 34 | // 9 = 3.5 - 4.5 35 | // 11 = pre-5.0 36 | // 12 = pre-5.0 37 | // 13 = pre-5.0 38 | // 14 = 5.0 39 | // 15 = 5.0 (p3 and newer) 40 | private int version; 41 | 42 | // offset to the serialized data 43 | private long dataOffset; 44 | 45 | // byte order of the serialized data? 46 | private byte endianness; 47 | 48 | // unused 49 | private final byte[] reserved = new byte[3]; 50 | 51 | public long metadataSize() { 52 | return metadataSize; 53 | } 54 | 55 | public void metadataSize(long metadataSize) { 56 | this.metadataSize = metadataSize; 57 | } 58 | 59 | public long fileSize() { 60 | return fileSize; 61 | } 62 | 63 | public void fileSize(long fileSize) { 64 | this.fileSize = fileSize; 65 | } 66 | 67 | public int version() { 68 | return version; 69 | } 70 | 71 | public void version(int version) { 72 | this.version = version; 73 | } 74 | 75 | public long dataOffset() { 76 | return dataOffset; 77 | } 78 | 79 | public void dataOffset(long dataOffset) { 80 | this.dataOffset = dataOffset; 81 | } 82 | 83 | public byte endianness() { 84 | return endianness; 85 | } 86 | 87 | public void endianness(byte endianness) { 88 | this.endianness = endianness; 89 | } 90 | 91 | @Override 92 | public void read(DataReader in) throws IOException { 93 | metadataSize = in.readInt(); 94 | fileSize = in.readUnsignedInt(); 95 | version = in.readInt(); 96 | dataOffset = in.readUnsignedInt(); 97 | if (version >= 9) { 98 | endianness = in.readByte(); 99 | in.readBytes(reserved); 100 | } 101 | } 102 | 103 | @Override 104 | public void write(DataWriter out) throws IOException { 105 | out.writeUnsignedInt(metadataSize); 106 | out.writeUnsignedInt(fileSize); 107 | out.writeInt(version); 108 | out.writeUnsignedInt(dataOffset); 109 | if (version >= 9) { 110 | out.writeByte(endianness); 111 | out.writeBytes(reserved); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/SerializedFileWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 03 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize; 11 | 12 | import info.ata4.io.DataWriter; 13 | import info.ata4.junity.serialize.objectinfo.ObjectInfo; 14 | import info.ata4.log.LogUtils; 15 | import info.ata4.util.io.DataBlock; 16 | import java.io.Closeable; 17 | import java.io.IOException; 18 | import java.nio.ByteBuffer; 19 | import java.nio.ByteOrder; 20 | import java.util.logging.Level; 21 | import java.util.logging.Logger; 22 | 23 | /** 24 | * 25 | * @author Nico Bergemann 26 | */ 27 | public class SerializedFileWriter implements Closeable { 28 | 29 | private static final Logger L = LogUtils.getLogger(); 30 | 31 | private static final int META_PADDING = 4096; 32 | private static final int META_ALIGN = 16; 33 | 34 | private final DataWriter out; 35 | private SerializedFile serialized; 36 | 37 | public SerializedFileWriter(DataWriter out) { 38 | this.out = out; 39 | } 40 | 41 | public void write(SerializedFile serialized) throws IOException { 42 | this.serialized = serialized; 43 | 44 | // header is always big endian 45 | out.order(ByteOrder.BIG_ENDIAN); 46 | 47 | writeHeader(out); 48 | 49 | SerializedFileHeader header = serialized.header(); 50 | 51 | // newer formats use little endian for the rest of the file 52 | if (header.version() > 5) { 53 | out.order(ByteOrder.LITTLE_ENDIAN); 54 | } 55 | 56 | // older formats store the object data before the structure data 57 | if (header.version() < 9) { 58 | header.dataOffset(0); 59 | 60 | writeObjects(out); 61 | out.writeUnsignedByte(header.version() > 5 ? 0 : 1); 62 | 63 | writeMetadata(out); 64 | out.writeUnsignedByte(0); 65 | } else { 66 | writeMetadata(out); 67 | 68 | long dataOffset = out.position(); 69 | 70 | // calculate padding 71 | if (dataOffset < META_PADDING) { 72 | dataOffset = META_PADDING; 73 | } else { 74 | dataOffset += META_ALIGN - (dataOffset % META_ALIGN); 75 | } 76 | 77 | header.dataOffset(dataOffset); 78 | 79 | out.position(dataOffset); 80 | writeObjects(out); 81 | 82 | // write updated path table 83 | out.position(serialized.metadata().objectInfoBlock().offset()); 84 | out.writeStruct(serialized.metadata().objectInfoTable()); 85 | } 86 | 87 | // update header 88 | header.fileSize(out.size()); 89 | 90 | // FIXME: the metadata size is slightly off in comparison to original files 91 | int metadataOffset = header.version() < 9 ? 2 : 1; 92 | 93 | header.metadataSize(serialized.metadataBlock().length() + metadataOffset); 94 | 95 | // write updated header 96 | out.order(ByteOrder.BIG_ENDIAN); 97 | out.position(serialized.headerBlock().offset()); 98 | out.writeStruct(header); 99 | } 100 | 101 | private void writeHeader(DataWriter out) throws IOException { 102 | DataBlock headerBlock = serialized.headerBlock(); 103 | headerBlock.markBegin(out); 104 | out.writeStruct(serialized.header()); 105 | headerBlock.markEnd(out); 106 | L.log(Level.FINER, "headerBlock: {0}", headerBlock); 107 | } 108 | 109 | private void writeMetadata(DataWriter out) throws IOException { 110 | SerializedFileMetadata metadata = serialized.metadata(); 111 | SerializedFileHeader header = serialized.header(); 112 | 113 | DataBlock metadataBlock = serialized.metadataBlock(); 114 | metadataBlock.markBegin(out); 115 | metadata.version(header.version()); 116 | out.writeStruct(metadata); 117 | metadataBlock.markEnd(out); 118 | L.log(Level.FINER, "metadataBlock: {0}", metadataBlock); 119 | } 120 | 121 | private void writeObjects(DataWriter out) throws IOException { 122 | long ofsMin = Long.MAX_VALUE; 123 | long ofsMax = Long.MIN_VALUE; 124 | 125 | for (SerializedObjectData data : serialized.objectData()) { 126 | ByteBuffer bb = data.buffer(); 127 | bb.rewind(); 128 | 129 | out.align(8); 130 | 131 | ofsMin = Math.min(ofsMin, out.position()); 132 | ofsMax = Math.max(ofsMax, out.position() + bb.remaining()); 133 | 134 | ObjectInfo info = data.info(); 135 | info.offset(out.position() - serialized.header().dataOffset()); 136 | info.length(bb.remaining()); 137 | 138 | out.writeBuffer(bb); 139 | } 140 | 141 | DataBlock objectDataBlock = serialized.objectDataBlock(); 142 | objectDataBlock.offset(ofsMin); 143 | objectDataBlock.endOffset(ofsMax); 144 | L.log(Level.FINER, "objectDataBlock: {0}", objectDataBlock); 145 | } 146 | 147 | @Override 148 | public void close() throws IOException { 149 | out.close(); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/SerializedObjectData.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 September 20 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize; 11 | 12 | import info.ata4.junity.serialize.objectinfo.ObjectInfo; 13 | import info.ata4.junity.serialize.typetree.Type; 14 | import info.ata4.util.collection.Node; 15 | import java.nio.ByteBuffer; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public class SerializedObjectData { 22 | 23 | private final long id; 24 | private ObjectInfo info; 25 | private ByteBuffer buffer; 26 | private Node typeTree; 27 | 28 | public SerializedObjectData(long id) { 29 | this.id = id; 30 | } 31 | 32 | public long id() { 33 | return id; 34 | } 35 | 36 | public ObjectInfo info() { 37 | return info; 38 | } 39 | 40 | public void info(ObjectInfo info) { 41 | this.info = info; 42 | } 43 | 44 | public ByteBuffer buffer() { 45 | return buffer; 46 | } 47 | 48 | public void buffer(ByteBuffer buffer) { 49 | this.buffer = buffer; 50 | } 51 | 52 | public Node typeTree() { 53 | return typeTree; 54 | } 55 | 56 | public void typeTree(Node typeTree) { 57 | this.typeTree = typeTree; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/fileidentifier/FileIdentifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 July 12 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.fileidentifier; 11 | 12 | import info.ata4.io.Struct; 13 | import info.ata4.junity.UnityGUID; 14 | import java.util.UUID; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | * @unity FileIdentifier 20 | */ 21 | public abstract class FileIdentifier implements Struct { 22 | 23 | // Globally unique identifier of the referred asset. Unity displays these 24 | // as simple 16 byte hex strings with each byte swapped, but they can also 25 | // be represented according to the UUID standard. 26 | protected final UnityGUID guid = new UnityGUID(); 27 | 28 | // Path to the asset file. Only used if "type" is 0. 29 | protected String filePath; 30 | 31 | // Reference type. Possible values are probably 0 to 3. 32 | protected int type; 33 | 34 | public UUID guid() { 35 | return guid.uuid(); 36 | } 37 | 38 | public void guid(UUID guid) { 39 | this.guid.uuid(guid); 40 | } 41 | 42 | public String filePath() { 43 | return filePath; 44 | } 45 | 46 | public void filePath(String filePath) { 47 | this.filePath = filePath; 48 | } 49 | 50 | public int type() { 51 | return type; 52 | } 53 | 54 | public void type(int type) { 55 | this.type = type; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/fileidentifier/FileIdentifierTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 August 16 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.fileidentifier; 11 | 12 | import info.ata4.junity.UnityTableStruct; 13 | 14 | /** 15 | * 16 | * @author Nico Bergemann 17 | */ 18 | public class FileIdentifierTable extends UnityTableStruct { 19 | 20 | public FileIdentifierTable(Class elementFactory) { 21 | super(elementFactory); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/fileidentifier/FileIdentifierV1.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.fileidentifier; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class FileIdentifierV1 extends FileIdentifier { 21 | 22 | @Override 23 | public void read(DataReader in) throws IOException { 24 | in.readStruct(guid); 25 | type = in.readInt(); 26 | filePath = in.readStringNull(); 27 | } 28 | 29 | @Override 30 | public void write(DataWriter out) throws IOException { 31 | out.writeStruct(guid); 32 | out.writeInt(type); 33 | out.writeStringNull(filePath); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/fileidentifier/FileIdentifierV2.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.fileidentifier; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class FileIdentifierV2 extends FileIdentifierV1 { 21 | 22 | // Path to the asset file? 23 | private String assetPath; 24 | 25 | @Override 26 | public void read(DataReader in) throws IOException { 27 | assetPath = in.readStringNull(); 28 | super.read(in); 29 | } 30 | 31 | @Override 32 | public void write(DataWriter out) throws IOException { 33 | out.writeStringNull(assetPath); 34 | super.write(out); 35 | } 36 | 37 | public String assetPath() { 38 | return assetPath; 39 | } 40 | 41 | public void assetPath(String assetPath) { 42 | this.assetPath = assetPath; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectidentifier/ObjectIdentifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectidentifier; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.io.Struct; 15 | import java.io.IOException; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | * @unity LocalSerializedObjectIdentifier 21 | */ 22 | public class ObjectIdentifier implements Struct { 23 | 24 | private int serializedFileIndex; 25 | private long identifierInFile; 26 | 27 | public int serializedFileIndex() { 28 | return serializedFileIndex; 29 | } 30 | 31 | public void serializedFileIndex(int serializedFileIndex) { 32 | this.serializedFileIndex = serializedFileIndex; 33 | } 34 | 35 | public long identifierInFile() { 36 | return identifierInFile; 37 | } 38 | 39 | public void identifierInFile(long identifierInFile) { 40 | this.identifierInFile = identifierInFile; 41 | } 42 | 43 | @Override 44 | public void read(DataReader in) throws IOException { 45 | serializedFileIndex = in.readInt(); 46 | identifierInFile = in.readLong(); 47 | } 48 | 49 | @Override 50 | public void write(DataWriter out) throws IOException { 51 | out.writeInt(serializedFileIndex); 52 | out.writeLong(identifierInFile); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectidentifier/ObjectIdentifierTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 01 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectidentifier; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.junity.UnityTableStruct; 15 | import java.io.IOException; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public class ObjectIdentifierTable extends UnityTableStruct { 22 | 23 | public ObjectIdentifierTable() { 24 | super(ObjectIdentifier.class); 25 | } 26 | 27 | @Override 28 | public void read(DataReader in) throws IOException { 29 | super.read(in); 30 | in.align(4); 31 | } 32 | 33 | @Override 34 | public void write(DataWriter out) throws IOException { 35 | super.write(out); 36 | out.align(4); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectinfo/ObjectInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 June 16 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectinfo; 11 | 12 | import info.ata4.io.Struct; 13 | 14 | /** 15 | * 16 | * @author Nico Bergemann 17 | * @unity SerializedFile::ObjectInfo 18 | */ 19 | public abstract class ObjectInfo implements Struct { 20 | 21 | // Object data offset 22 | protected long byteStart; 23 | 24 | // Object data size 25 | protected long byteSize; 26 | 27 | // Type ID, equal to classID if it's not a MonoBehaviour 28 | protected int typeID; 29 | 30 | // Class ID, probably something else in asset format <=5 31 | protected int classID; 32 | 33 | public long offset() { 34 | return byteStart; 35 | } 36 | 37 | public void offset(long offset) { 38 | this.byteStart = offset; 39 | } 40 | 41 | public long length() { 42 | return byteSize; 43 | } 44 | 45 | public void length(long length) { 46 | this.byteSize = length; 47 | } 48 | 49 | public boolean isScript() { 50 | return typeID < 0; 51 | } 52 | 53 | public int typeID() { 54 | return typeID; 55 | } 56 | 57 | public void typeID(int typeID) { 58 | this.typeID = typeID; 59 | } 60 | 61 | public int classID() { 62 | return classID; 63 | } 64 | 65 | public void classID(int classID) { 66 | this.classID = classID; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectinfo/ObjectInfoTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectinfo; 11 | 12 | import info.ata4.junity.UnityStruct; 13 | import java.util.LinkedHashMap; 14 | import java.util.Map; 15 | import java.util.Objects; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public abstract class ObjectInfoTable extends UnityStruct { 22 | 23 | protected Map infoMap = new LinkedHashMap<>(); 24 | 25 | public ObjectInfoTable(Class elementFactory) { 26 | super(elementFactory); 27 | } 28 | 29 | public Map infoMap() { 30 | return infoMap; 31 | } 32 | 33 | public void infoMap(Map infoMap) { 34 | this.infoMap = Objects.requireNonNull(infoMap); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectinfo/ObjectInfoTableV1.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 June 17 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectinfo; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | import java.util.Map; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public class ObjectInfoTableV1 extends ObjectInfoTable { 22 | 23 | public ObjectInfoTableV1(Class elementFactory) { 24 | super(elementFactory); 25 | } 26 | 27 | @Override 28 | public void read(DataReader in) throws IOException { 29 | int entries = in.readInt(); 30 | 31 | for (int i = 0; i < entries; i++) { 32 | long pathID = in.readUnsignedInt(); 33 | T info = createElement(); 34 | in.readStruct(info); 35 | infoMap.put(pathID, info); 36 | } 37 | } 38 | 39 | @Override 40 | public void write(DataWriter out) throws IOException { 41 | int entries = infoMap.size(); 42 | out.writeInt(entries); 43 | 44 | for (Map.Entry infoEntry : infoMap.entrySet()) { 45 | long pathID = infoEntry.getKey(); 46 | ObjectInfo info = infoEntry.getValue(); 47 | out.writeUnsignedInt(pathID); 48 | out.writeStruct(info); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectinfo/ObjectInfoTableV2.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectinfo; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | import java.util.Map; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public class ObjectInfoTableV2 extends ObjectInfoTableV1 { 22 | 23 | public ObjectInfoTableV2(Class elementFactory) { 24 | super(elementFactory); 25 | } 26 | 27 | @Override 28 | public void read(DataReader in) throws IOException { 29 | int entries = in.readInt(); 30 | for (int i = 0; i < entries; i++) { 31 | in.align(4); 32 | long pathID = in.readLong(); 33 | T info = createElement(); 34 | in.readStruct(info); 35 | infoMap.put(pathID, info); 36 | } 37 | } 38 | 39 | @Override 40 | public void write(DataWriter out) throws IOException { 41 | int entries = infoMap.size(); 42 | out.writeInt(entries); 43 | for (Map.Entry infoEntry : infoMap.entrySet()) { 44 | out.align(4); 45 | long pathID = infoEntry.getKey(); 46 | ObjectInfo info = infoEntry.getValue(); 47 | out.writeLong(pathID); 48 | out.writeStruct(info); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectinfo/ObjectInfoV1.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 26 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectinfo; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class ObjectInfoV1 extends ObjectInfo { 21 | 22 | // set to 1 if the object instance is destroyed? 23 | // (no longer stored in files starting with Unity 5) 24 | private short isDestroyed; 25 | 26 | public short isDestroyed() { 27 | return isDestroyed; 28 | } 29 | 30 | public void setDestroyed(short isDestroyed) { 31 | this.isDestroyed = isDestroyed; 32 | } 33 | 34 | @Override 35 | public void read(DataReader in) throws IOException { 36 | byteStart = in.readUnsignedInt(); 37 | byteSize = in.readUnsignedInt(); 38 | typeID = in.readInt(); 39 | classID = in.readShort(); 40 | isDestroyed = in.readShort(); 41 | } 42 | 43 | @Override 44 | public void write(DataWriter out) throws IOException { 45 | out.writeUnsignedInt(byteStart); 46 | out.writeUnsignedInt(byteSize); 47 | out.writeInt(typeID); 48 | out.writeShort((short) classID); 49 | out.writeShort(isDestroyed); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectinfo/ObjectInfoV2.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 26 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectinfo; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class ObjectInfoV2 extends ObjectInfo { 21 | 22 | private short scriptTypeIndex; 23 | 24 | public short scriptTypeIndex() { 25 | return scriptTypeIndex; 26 | } 27 | 28 | public void scriptTypeIndex(short scriptTypeIndex) { 29 | this.scriptTypeIndex = scriptTypeIndex; 30 | } 31 | 32 | @Override 33 | public void read(DataReader in) throws IOException { 34 | byteStart = in.readUnsignedInt(); 35 | byteSize = in.readUnsignedInt(); 36 | typeID = in.readInt(); 37 | classID = in.readShort(); 38 | scriptTypeIndex = in.readShort(); 39 | } 40 | 41 | @Override 42 | public void write(DataWriter out) throws IOException { 43 | out.writeUnsignedInt(byteStart); 44 | out.writeUnsignedInt(byteSize); 45 | out.writeInt(typeID); 46 | out.writeShort((short) classID); 47 | out.writeShort(scriptTypeIndex); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/objectinfo/ObjectInfoV3.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 29 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.objectinfo; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class ObjectInfoV3 extends ObjectInfoV2 { 21 | 22 | private boolean stripped; 23 | 24 | public boolean isStripped() { 25 | return stripped; 26 | } 27 | 28 | public void setStripped(boolean stripped) { 29 | this.stripped = stripped; 30 | } 31 | 32 | @Override 33 | public void read(DataReader in) throws IOException { 34 | super.read(in); 35 | stripped = in.readBoolean(); 36 | } 37 | 38 | @Override 39 | public void write(DataWriter out) throws IOException { 40 | super.write(out); 41 | out.writeBoolean(stripped); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/typetree/StringTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 26 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.typetree; 11 | 12 | import com.google.common.collect.BiMap; 13 | import com.google.common.collect.HashBiMap; 14 | import info.ata4.io.DataReader; 15 | import java.io.BufferedReader; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.io.InputStreamReader; 19 | import java.nio.charset.StandardCharsets; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.Objects; 23 | import java.util.concurrent.atomic.AtomicInteger; 24 | import java.util.stream.Collectors; 25 | 26 | /** 27 | * 28 | * @author Nico Bergemann 29 | */ 30 | public class StringTable { 31 | 32 | private static Map> commonStringMap = new HashMap<>(); 33 | 34 | private StringTable() { 35 | } 36 | 37 | private static BufferedReader resourceReader(String path) { 38 | return new BufferedReader(new InputStreamReader( 39 | StringTable.class.getResourceAsStream(path), StandardCharsets.US_ASCII)); 40 | } 41 | 42 | public static BiMap commonStrings(int version) throws IOException { 43 | // load default strings from resource files if required 44 | if (!commonStringMap.containsKey(version)) { 45 | AtomicInteger index = new AtomicInteger(1 << 31); 46 | String resourcePath = "/resources/strings/" + version + ".x.txt"; 47 | try (BufferedReader br = resourceReader(resourcePath)) { 48 | commonStringMap.put(version, br.lines().collect(Collectors.toMap( 49 | value -> index.getAndAdd(value.length() + 1), 50 | value -> value 51 | ))); 52 | } catch (NullPointerException ex) { 53 | throw new RuntimeException("No common strings file found for version " + version); 54 | } 55 | } 56 | 57 | return HashBiMap.create(commonStringMap.get(version)); 58 | } 59 | 60 | public static BiMap read(DataReader in, int length) throws IOException { 61 | BiMap map = HashBiMap.create(); 62 | 63 | // load strings from input 64 | long startPos = in.position(); 65 | long endPos = startPos + length; 66 | while (in.position() < endPos) { 67 | int index = (int) (in.position() - startPos); 68 | String string = in.readStringNull(); 69 | map.put(index, string); 70 | } 71 | 72 | return map; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/typetree/Type.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2013 June 16 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.typetree; 11 | 12 | import info.ata4.io.Struct; 13 | 14 | /** 15 | * Class for objects that contain the runtime type of a single field. 16 | * 17 | * @author Nico Bergemann 18 | * @unity TypeTree 19 | */ 20 | public abstract class Type implements Struct { 21 | 22 | public static final int FLAG_FORCE_ALIGN = 0x4000; 23 | 24 | // field type string 25 | protected String type; 26 | 27 | // field name string 28 | protected String name; 29 | 30 | // size of the field value in bytes or -1 if the field contains sub-fields only 31 | protected int size; 32 | 33 | // field index for the associated parent field 34 | protected int index; 35 | 36 | // set to 1 if "type" is "Array" or "TypelessData" 37 | protected boolean isArray; 38 | 39 | // type version, starts with 1 and is incremented when the type 40 | // information is updated in a new Unity release 41 | // 42 | // equal to serializedVersion in YAML format files 43 | protected int version; 44 | 45 | // field flags 46 | // observed values: 47 | // 0x1 48 | // 0x10 49 | // 0x800 50 | // 0x4000 51 | // 0x8000 52 | // 0x200000 53 | // 0x400000 54 | protected int metaFlag; 55 | 56 | public String typeName() { 57 | return type; 58 | } 59 | 60 | public void typeName(String type) { 61 | this.type = type; 62 | } 63 | 64 | public String fieldName() { 65 | return name; 66 | } 67 | 68 | public void fieldName(String name) { 69 | this.name = name; 70 | } 71 | 72 | public int size() { 73 | return size; 74 | } 75 | 76 | public void size(int size) { 77 | this.size = size; 78 | } 79 | 80 | public int index() { 81 | return index; 82 | } 83 | 84 | public void index(int index) { 85 | this.index = index; 86 | } 87 | 88 | public boolean isArray() { 89 | return isArray; 90 | } 91 | 92 | public void isArray(boolean isArray) { 93 | this.isArray = isArray; 94 | } 95 | 96 | public int version() { 97 | return version; 98 | } 99 | 100 | public void version(int version) { 101 | this.version = version; 102 | } 103 | 104 | public int metaFlag() { 105 | return metaFlag; 106 | } 107 | 108 | public void metaFlag(int metaFlag) { 109 | this.metaFlag = metaFlag; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/typetree/TypeRoot.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 April 15 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.typetree; 11 | 12 | import info.ata4.junity.UnityHash128; 13 | import info.ata4.util.collection.Node; 14 | import java.util.Objects; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | * @unity SerializedFile::Type 20 | */ 21 | public class TypeRoot { 22 | 23 | private int classID; 24 | private UnityHash128 scriptID; 25 | private UnityHash128 oldTypeHash; 26 | private Node nodes; 27 | 28 | public int classID() { 29 | return classID; 30 | } 31 | 32 | public void classID(int classID) { 33 | this.classID = classID; 34 | } 35 | 36 | public UnityHash128 scriptID() { 37 | return scriptID; 38 | } 39 | 40 | public void scriptID(UnityHash128 scriptID) { 41 | this.scriptID = scriptID; 42 | } 43 | 44 | public UnityHash128 oldTypeHash() { 45 | return oldTypeHash; 46 | } 47 | 48 | public void oldTypeHash(UnityHash128 oldTypeHash) { 49 | this.oldTypeHash = oldTypeHash; 50 | } 51 | 52 | public Node nodes() { 53 | return nodes; 54 | } 55 | 56 | public void nodes(Node nodes) { 57 | this.nodes = nodes; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/typetree/TypeTree.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 April 15 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.typetree; 11 | 12 | import info.ata4.junity.UnityStruct; 13 | import java.util.LinkedHashMap; 14 | import java.util.Map; 15 | import java.util.Objects; 16 | 17 | /** 18 | * Class for objects that hold the runtime type information of an asset file. 19 | * 20 | * @author Nico Bergemann 21 | * @unity RTTIClassHierarchyDescriptor, RTTIBaseClassDescriptor2, TypeTree 22 | */ 23 | public abstract class TypeTree extends UnityStruct { 24 | 25 | protected Map> typeMap = new LinkedHashMap<>(); 26 | protected boolean embedded; 27 | 28 | public TypeTree(Class elementFactory) { 29 | super(elementFactory); 30 | } 31 | 32 | public Map> typeMap() { 33 | return typeMap; 34 | } 35 | 36 | public void typeMap(Map> typeMap) { 37 | this.typeMap = Objects.requireNonNull(typeMap); 38 | } 39 | 40 | public boolean embedded() { 41 | return embedded; 42 | } 43 | 44 | public void embedded(boolean embedded) { 45 | this.embedded = embedded; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/typetree/TypeTreeV1.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.typetree; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.util.collection.Node; 15 | import java.io.IOException; 16 | 17 | /** 18 | * 19 | * @author Nico Bergemann 20 | */ 21 | public class TypeTreeV1 extends TypeTree { 22 | 23 | public TypeTreeV1(Class elementFactory) { 24 | super(elementFactory); 25 | } 26 | 27 | @Override 28 | public void read(DataReader in) throws IOException { 29 | int numBaseClasses = in.readInt(); 30 | for (int i = 0; i < numBaseClasses; i++) { 31 | int classID = in.readInt(); 32 | 33 | TypeRoot baseClass = new TypeRoot(); 34 | baseClass.classID(classID); 35 | 36 | Node node = new Node<>(); 37 | readNode(in, node); 38 | baseClass.nodes(node); 39 | 40 | typeMap.put(classID, baseClass); 41 | } 42 | 43 | embedded = numBaseClasses > 0; 44 | } 45 | 46 | private void readNode(DataReader in, Node node) throws IOException { 47 | T type = createElement(); 48 | in.readStruct(type); 49 | 50 | node.data(type); 51 | 52 | int numChildren = in.readInt(); 53 | for (int i = 0; i < numChildren; i++) { 54 | Node childNode = new Node<>(); 55 | readNode(in, childNode); 56 | node.add(childNode); 57 | } 58 | } 59 | 60 | @Override 61 | public void write(DataWriter out) throws IOException { 62 | // write empty type tree if types are not embedded 63 | if (!embedded) { 64 | out.writeInt(0); 65 | return; 66 | } 67 | 68 | int numBaseClasses = typeMap.size(); 69 | out.writeInt(numBaseClasses); 70 | 71 | for (TypeRoot bc : typeMap.values()) { 72 | int classID = bc.classID(); 73 | out.writeInt(classID); 74 | 75 | Node node = bc.nodes(); 76 | 77 | writeNode(out, node); 78 | } 79 | } 80 | 81 | private void writeNode(DataWriter out, Node node) throws IOException { 82 | T type = node.data(); 83 | out.writeStruct(type); 84 | 85 | int numChildren = node.size(); 86 | out.writeInt(numChildren); 87 | 88 | for (Node child : node) { 89 | writeNode(out, child); 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/typetree/TypeTreeV2.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.typetree; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import info.ata4.junity.UnityVersion; 15 | import java.io.IOException; 16 | import java.util.Objects; 17 | 18 | /** 19 | * 20 | * @author Nico Bergemann 21 | */ 22 | public class TypeTreeV2 extends TypeTreeV1 { 23 | 24 | protected UnityVersion revision = new UnityVersion(); 25 | protected int attributes; 26 | 27 | public TypeTreeV2(Class elementFactory) { 28 | super(elementFactory); 29 | } 30 | 31 | public UnityVersion revision() { 32 | return revision; 33 | } 34 | 35 | public void revision(UnityVersion revision) { 36 | this.revision = Objects.requireNonNull(revision); 37 | } 38 | 39 | public int attributes() { 40 | return attributes; 41 | } 42 | 43 | public void attributes(int attributes) { 44 | this.attributes = attributes; 45 | } 46 | 47 | @Override 48 | public void read(DataReader in) throws IOException { 49 | revision = new UnityVersion(in.readStringNull(255)); 50 | attributes = in.readInt(); 51 | 52 | super.read(in); 53 | 54 | // padding 55 | in.readInt(); 56 | } 57 | 58 | @Override 59 | public void write(DataWriter out) throws IOException { 60 | out.writeStringNull(revision.toString()); 61 | out.writeInt(attributes); 62 | 63 | super.write(out); 64 | 65 | // padding 66 | out.writeInt(0); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/typetree/TypeV1.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.typetree; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class TypeV1 extends Type { 21 | 22 | @Override 23 | public void read(DataReader in) throws IOException { 24 | type = in.readStringNull(256); 25 | name = in.readStringNull(256); 26 | size = in.readInt(); 27 | index = in.readInt(); 28 | isArray = in.readInt() == 1; 29 | version = in.readInt(); 30 | metaFlag = in.readInt(); 31 | } 32 | 33 | @Override 34 | public void write(DataWriter out) throws IOException { 35 | out.writeStringNull(type); 36 | out.writeStringNull(name); 37 | out.writeInt(size); 38 | out.writeInt(index); 39 | out.writeInt(isArray ? 1 : 0); 40 | out.writeInt(version); 41 | out.writeInt(metaFlag); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/junity/serialize/typetree/TypeV2.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 23 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.junity.serialize.typetree; 11 | 12 | import info.ata4.io.DataReader; 13 | import info.ata4.io.DataWriter; 14 | import java.io.IOException; 15 | 16 | /** 17 | * 18 | * @author Nico Bergemann 19 | */ 20 | public class TypeV2 extends TypeV1 { 21 | 22 | // Unity 5+, the level of this type within the type tree 23 | protected int treeLevel; 24 | 25 | // Unity 5+, offset to the type string 26 | protected int typeOffset; 27 | 28 | // Unity 5+, offset to the name string 29 | protected int nameOffset; 30 | 31 | public int treeLevel() { 32 | return treeLevel; 33 | } 34 | 35 | public void treeLevel(int treeLevel) { 36 | this.treeLevel = treeLevel; 37 | } 38 | 39 | public int typeOffset() { 40 | return typeOffset; 41 | } 42 | 43 | public void typeOffset(int typeOffset) { 44 | this.typeOffset = typeOffset; 45 | } 46 | 47 | public int nameOffset() { 48 | return nameOffset; 49 | } 50 | 51 | public void nameOffset(int nameOffset) { 52 | this.nameOffset = nameOffset; 53 | } 54 | 55 | @Override 56 | public void read(DataReader in) throws IOException { 57 | version = in.readShort(); 58 | treeLevel = in.readUnsignedByte(); 59 | isArray = in.readBoolean(); 60 | typeOffset = in.readInt(); 61 | nameOffset = in.readInt(); 62 | size = in.readInt(); 63 | index = in.readInt(); 64 | metaFlag = in.readInt(); 65 | } 66 | 67 | @Override 68 | public void write(DataWriter out) throws IOException { 69 | out.writeShort((short) version); 70 | out.writeUnsignedByte(treeLevel); 71 | out.writeBoolean(isArray); 72 | out.writeInt(typeOffset); 73 | out.writeInt(nameOffset); 74 | out.writeInt(size); 75 | out.writeInt(index); 76 | out.writeInt(metaFlag); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/collection/Node.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 September 22 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.util.collection; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Collection; 14 | import java.util.Collections; 15 | import java.util.Iterator; 16 | import java.util.List; 17 | import java.util.Objects; 18 | import java.util.Spliterator; 19 | import java.util.Spliterators; 20 | import java.util.function.Consumer; 21 | 22 | /** 23 | * 24 | * @author Nico Bergemann 25 | * @param 26 | */ 27 | public class Node implements Collection> { 28 | 29 | private T data; 30 | private Node parent; 31 | private final List> children = new ArrayList<>(); 32 | 33 | public Node() { 34 | } 35 | 36 | public Node(T data) { 37 | this.data = data; 38 | } 39 | 40 | public Node(Collection> children) { 41 | if (children != null) { 42 | this.children.addAll(children); 43 | } 44 | } 45 | 46 | public Node(T data, Collection> children) { 47 | this.data = data; 48 | if (children != null) { 49 | this.children.addAll(children); 50 | } 51 | } 52 | 53 | public T data() { 54 | return data; 55 | } 56 | 57 | public void data(T data) { 58 | this.data = data; 59 | } 60 | 61 | public Node parent() { 62 | return parent; 63 | } 64 | 65 | private void parent(Node parent) { 66 | this.parent = parent; 67 | } 68 | 69 | public void forEachData(Consumer action) { 70 | Objects.requireNonNull(action); 71 | action.accept(data()); 72 | children.forEach(node -> node.forEachData(action)); 73 | } 74 | 75 | @Override 76 | public int size() { 77 | return children.size(); 78 | } 79 | 80 | @Override 81 | public boolean isEmpty() { 82 | return children.isEmpty(); 83 | } 84 | 85 | @Override 86 | public boolean contains(Object o) { 87 | return children.contains(o); 88 | } 89 | 90 | @Override 91 | public Iterator> iterator() { 92 | return Collections.unmodifiableList(children).iterator(); 93 | } 94 | 95 | @Override 96 | public Spliterator> spliterator() { 97 | return Spliterators.spliterator(Collections.unmodifiableList(children), 98 | Spliterator.ORDERED | Spliterator.IMMUTABLE); 99 | } 100 | 101 | @Override 102 | public Object[] toArray() { 103 | return children.toArray(); 104 | } 105 | 106 | @Override 107 | public T[] toArray(T[] a) { 108 | return children.toArray(a); 109 | } 110 | 111 | @Override 112 | public boolean add(Node node) { 113 | boolean added = children.add(node); 114 | if (added) { 115 | node.parent(this); 116 | } 117 | return added; 118 | } 119 | 120 | @Override 121 | public boolean remove(Object o) { 122 | boolean removed = children.remove(o); 123 | if (removed) { 124 | ((Node) o).parent(null); 125 | } 126 | return removed; 127 | } 128 | 129 | @Override 130 | public boolean containsAll(Collection c) { 131 | return children.containsAll(c); 132 | } 133 | 134 | @Override 135 | public boolean addAll(Collection> c) { 136 | boolean added = children.addAll(c); 137 | if (added) { 138 | forEach(node -> node.parent(this)); 139 | } 140 | return added; 141 | } 142 | 143 | @Override 144 | public boolean removeAll(Collection c) { 145 | List> childrenOld = new ArrayList<>(children); 146 | boolean removed = children.removeAll(c); 147 | if (removed) { 148 | childrenOld.removeAll(children); 149 | childrenOld.forEach(node -> node.parent(null)); 150 | } 151 | return removed; 152 | } 153 | 154 | @Override 155 | public boolean retainAll(Collection c) { 156 | List> childrenOld = new ArrayList<>(children); 157 | boolean retained = children.retainAll(c); 158 | if (retained) { 159 | childrenOld.removeAll(children); 160 | childrenOld.forEach(node -> node.parent(null)); 161 | } 162 | return retained; 163 | } 164 | 165 | @Override 166 | public void clear() { 167 | children.clear(); 168 | } 169 | 170 | @Override 171 | public int hashCode() { 172 | int hash = 5; 173 | hash = 43 * hash + Objects.hashCode(this.data); 174 | hash = 43 * hash + Objects.hashCode(this.children); 175 | return hash; 176 | } 177 | 178 | @Override 179 | public boolean equals(Object obj) { 180 | if (obj == null) { 181 | return false; 182 | } 183 | if (getClass() != obj.getClass()) { 184 | return false; 185 | } 186 | final Node other = (Node) obj; 187 | if (!Objects.equals(this.data, other.data)) { 188 | return false; 189 | } 190 | if (!Objects.equals(this.children, other.children)) { 191 | return false; 192 | } 193 | return true; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/function/IOConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 29 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.util.function; 11 | 12 | import java.io.IOException; 13 | import java.io.UncheckedIOException; 14 | import java.util.function.Consumer; 15 | 16 | /** 17 | * Interface for consumers that throw IOExceptions, which are wrapped to 18 | * UncheckedIOExceptions. 19 | * 20 | * @author Nico Bergemann 21 | */ 22 | @FunctionalInterface 23 | public interface IOConsumer { 24 | 25 | void accept(T t) throws IOException; 26 | 27 | public static Consumer uncheck(IOConsumer consumer) { 28 | return t -> { 29 | try { 30 | consumer.accept(t); 31 | } catch (IOException ex) { 32 | throw new UncheckedIOException(ex); 33 | } 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/function/IOFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 November 29 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.util.function; 11 | 12 | import java.io.IOException; 13 | import java.io.UncheckedIOException; 14 | import java.util.function.Function; 15 | 16 | /** 17 | * Interface for functions that throw IOExceptions, which are wrapped to 18 | * UncheckedIOExceptions. 19 | * 20 | * @author Nico Bergemann 21 | */ 22 | @FunctionalInterface 23 | public interface IOFunction { 24 | 25 | R apply(T t) throws IOException; 26 | 27 | public static Function uncheck(IOFunction function) { 28 | return t -> { 29 | try { 30 | return function.apply(t); 31 | } catch (IOException ex) { 32 | throw new UncheckedIOException(ex); 33 | } 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/function/Predicates.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 13 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.util.function; 11 | 12 | import java.util.function.Predicate; 13 | 14 | /** 15 | * Small predicate utility class. 16 | * 17 | * @author Nico Bergemann 18 | */ 19 | public class Predicates { 20 | 21 | private Predicates() { 22 | } 23 | 24 | public static Predicate not(Predicate t) { 25 | return t.negate(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/io/DataBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2014 October 28 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.util.io; 11 | 12 | import info.ata4.io.Positionable; 13 | import java.io.IOException; 14 | 15 | /** 16 | * 17 | * @author Nico Bergemann 18 | */ 19 | public class DataBlock { 20 | 21 | private long offset; 22 | private long length; 23 | 24 | public long offset() { 25 | return offset; 26 | } 27 | 28 | public void offset(long offset) { 29 | this.offset = offset; 30 | } 31 | 32 | public long length() { 33 | return length; 34 | } 35 | 36 | public void length(long length) { 37 | this.length = length; 38 | } 39 | 40 | public void endOffset(long endOffset) { 41 | this.length = endOffset - offset; 42 | } 43 | 44 | public long endOffset() { 45 | return offset + length; 46 | } 47 | 48 | public boolean isIntersecting(DataBlock that) { 49 | return this.endOffset() > that.offset() && that.endOffset() > this.offset(); 50 | } 51 | 52 | public boolean isInside(DataBlock that) { 53 | return this.offset() >= that.offset() && this.endOffset() <= that.endOffset(); 54 | } 55 | 56 | public void markBegin(Positionable p) throws IOException { 57 | offset(p.position()); 58 | } 59 | 60 | public void markEnd(Positionable p) throws IOException { 61 | DataBlock.this.endOffset(p.position()); 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return DataBlock.this.offset() + " - " + endOffset() + " (" + DataBlock.this.length() + ")"; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/lz4/ByteBufferUtils.java: -------------------------------------------------------------------------------- 1 | /* Partial import of https://github.com/jpountz/lz4-java, Apache 2.0 licensed. */ 2 | 3 | package info.ata4.util.lz4; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | import java.nio.ReadOnlyBufferException; 8 | 9 | public enum ByteBufferUtils { 10 | ; 11 | 12 | public static void checkRange(ByteBuffer buf, int off, int len) { 13 | SafeUtils.checkLength(len); 14 | if (len > 0) { 15 | checkRange(buf, off); 16 | checkRange(buf, off + len - 1); 17 | } 18 | } 19 | 20 | public static void checkRange(ByteBuffer buf, int off) { 21 | if (off < 0 || off >= buf.capacity()) { 22 | throw new ArrayIndexOutOfBoundsException(off); 23 | } 24 | } 25 | 26 | public static ByteBuffer inLittleEndianOrder(ByteBuffer buf) { 27 | if (buf.order().equals(ByteOrder.LITTLE_ENDIAN)) { 28 | return buf; 29 | } else { 30 | return buf.duplicate().order(ByteOrder.LITTLE_ENDIAN); 31 | } 32 | } 33 | 34 | public static ByteBuffer inNativeByteOrder(ByteBuffer buf) { 35 | if (buf.order().equals(Utils.NATIVE_BYTE_ORDER)) { 36 | return buf; 37 | } else { 38 | return buf.duplicate().order(Utils.NATIVE_BYTE_ORDER); 39 | } 40 | } 41 | 42 | public static byte readByte(ByteBuffer buf, int i) { 43 | return buf.get(i); 44 | } 45 | 46 | public static void writeInt(ByteBuffer buf, int i, int v) { 47 | assert buf.order() == Utils.NATIVE_BYTE_ORDER; 48 | buf.putInt(i, v); 49 | } 50 | 51 | public static int readInt(ByteBuffer buf, int i) { 52 | assert buf.order() == Utils.NATIVE_BYTE_ORDER; 53 | return buf.getInt(i); 54 | } 55 | 56 | public static int readIntLE(ByteBuffer buf, int i) { 57 | assert buf.order() == ByteOrder.LITTLE_ENDIAN; 58 | return buf.getInt(i); 59 | } 60 | 61 | public static void writeLong(ByteBuffer buf, int i, long v) { 62 | assert buf.order() == Utils.NATIVE_BYTE_ORDER; 63 | buf.putLong(i, v); 64 | } 65 | 66 | public static long readLong(ByteBuffer buf, int i) { 67 | assert buf.order() == Utils.NATIVE_BYTE_ORDER; 68 | return buf.getLong(i); 69 | } 70 | 71 | public static long readLongLE(ByteBuffer buf, int i) { 72 | assert buf.order() == ByteOrder.LITTLE_ENDIAN; 73 | return buf.getLong(i); 74 | } 75 | 76 | public static void writeByte(ByteBuffer dest, int off, int i) { 77 | dest.put(off, (byte) i); 78 | } 79 | 80 | public static void writeShortLE(ByteBuffer dest, int off, int i) { 81 | dest.put(off, (byte) i); 82 | dest.put(off + 1, (byte) (i >>> 8)); 83 | } 84 | 85 | public static void checkNotReadOnly(ByteBuffer buffer) { 86 | if (buffer.isReadOnly()) { 87 | throw new ReadOnlyBufferException(); 88 | } 89 | } 90 | 91 | public static int readShortLE(ByteBuffer buf, int i) { 92 | return (buf.get(i) & 0xFF) | ((buf.get(i+1) & 0xFF) << 8); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/lz4/LZ4Constants.java: -------------------------------------------------------------------------------- 1 | /* Partial import of https://github.com/jpountz/lz4-java, Apache 2.0 licensed. */ 2 | 3 | package info.ata4.util.lz4; 4 | 5 | /* 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | enum LZ4Constants { 21 | ; 22 | 23 | static final int DEFAULT_COMPRESSION_LEVEL = 8+1; 24 | static final int MAX_COMPRESSION_LEVEL = 16+1; 25 | 26 | static final int MEMORY_USAGE = 14; 27 | static final int NOT_COMPRESSIBLE_DETECTION_LEVEL = 6; 28 | 29 | static final int MIN_MATCH = 4; 30 | 31 | static final int HASH_LOG = MEMORY_USAGE - 2; 32 | static final int HASH_TABLE_SIZE = 1 << HASH_LOG; 33 | 34 | static final int SKIP_STRENGTH = Math.max(NOT_COMPRESSIBLE_DETECTION_LEVEL, 2); 35 | static final int COPY_LENGTH = 8; 36 | static final int LAST_LITERALS = 5; 37 | static final int MF_LIMIT = COPY_LENGTH + MIN_MATCH; 38 | static final int MIN_LENGTH = MF_LIMIT + 1; 39 | 40 | static final int MAX_DISTANCE = 1 << 16; 41 | 42 | static final int ML_BITS = 4; 43 | static final int ML_MASK = (1 << ML_BITS) - 1; 44 | static final int RUN_BITS = 8 - ML_BITS; 45 | static final int RUN_MASK = (1 << RUN_BITS) - 1; 46 | 47 | static final int LZ4_64K_LIMIT = (1 << 16) + (MF_LIMIT - 1); 48 | static final int HASH_LOG_64K = HASH_LOG + 1; 49 | static final int HASH_TABLE_SIZE_64K = 1 << HASH_LOG_64K; 50 | 51 | static final int HASH_LOG_HC = 15; 52 | static final int HASH_TABLE_SIZE_HC = 1 << HASH_LOG_HC; 53 | static final int OPTIMAL_ML = ML_MASK - 1 + MIN_MATCH; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/lz4/LZ4Exception.java: -------------------------------------------------------------------------------- 1 | /* Partial import of https://github.com/jpountz/lz4-java, Apache 2.0 licensed. */ 2 | 3 | package info.ata4.util.lz4; 4 | 5 | /* 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /** 20 | * LZ4 compression or decompression error. 21 | */ 22 | public class LZ4Exception extends RuntimeException { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | public LZ4Exception(String msg, Throwable t) { 27 | super(msg, t); 28 | } 29 | 30 | public LZ4Exception(String msg) { 31 | super(msg); 32 | } 33 | 34 | public LZ4Exception() { 35 | super(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/lz4/LZ4FastDecompressor.java: -------------------------------------------------------------------------------- 1 | package info.ata4.util.lz4; 2 | 3 | /* Partial import of https://github.com/jpountz/lz4-java, Apache 2.0 licensed. */ 4 | 5 | import java.nio.ByteBuffer; 6 | 7 | /* 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * LZ4 decompressor that requires the size of the original input to be known. 23 | * Use LZ4SafeDecompressor if you only know the size of the 24 | * compressed stream. 25 | *

26 | * Instances of this class are thread-safe. 27 | */ 28 | public abstract class LZ4FastDecompressor { 29 | 30 | /** Decompress src[srcOff:] into dest[destOff:destOff+destLen] 31 | * and return the number of bytes read from src. 32 | * destLen must be exactly the size of the decompressed data. 33 | * 34 | * @param destLen the exact size of the original input 35 | * @return the number of bytes read to restore the original input 36 | */ 37 | public abstract int decompress(byte[] src, int srcOff, byte[] dest, int destOff, int destLen); 38 | 39 | /** Decompress src[srcOff:] into dest[destOff:destOff+destLen] 40 | * and return the number of bytes read from src. 41 | * destLen must be exactly the size of the decompressed data. 42 | * The positions and limits of the {@link ByteBuffer}s remain unchanged. 43 | * 44 | * @param destLen the exact size of the original input 45 | * @return the number of bytes read to restore the original input 46 | */ 47 | public abstract int decompress(ByteBuffer src, int srcOff, ByteBuffer dest, int destOff, int destLen); 48 | 49 | /** 50 | * Convenience method, equivalent to calling 51 | * {@link #decompress(byte[], int, byte[], int, int) decompress(src, 0, dest, 0, destLen)}. 52 | */ 53 | public final int decompress(byte[] src, byte[] dest, int destLen) { 54 | return decompress(src, 0, dest, 0, destLen); 55 | } 56 | 57 | /** 58 | * Convenience method, equivalent to calling 59 | * {@link #decompress(byte[], byte[], int) decompress(src, dest, dest.length)}. 60 | */ 61 | public final int decompress(byte[] src, byte[] dest) { 62 | return decompress(src, dest, dest.length); 63 | } 64 | 65 | /** 66 | * Convenience method which returns src[srcOff:?] 67 | * decompressed. 68 | *

Warning: this method has an 69 | * important overhead due to the fact that it needs to allocate a buffer to 70 | * decompress into.

71 | *

Here is how this method is implemented:

72 | *
 73 |    * final byte[] decompressed = new byte[destLen];
 74 |    * decompress(src, srcOff, decompressed, 0, destLen);
 75 |    * return decompressed;
 76 |    * 
77 | */ 78 | public final byte[] decompress(byte[] src, int srcOff, int destLen) { 79 | final byte[] decompressed = new byte[destLen]; 80 | decompress(src, srcOff, decompressed, 0, destLen); 81 | return decompressed; 82 | } 83 | 84 | /** 85 | * Convenience method, equivalent to calling 86 | * {@link #decompress(byte[], int, int) decompress(src, 0, destLen)}. 87 | */ 88 | public final byte[] decompress(byte[] src, int destLen) { 89 | return decompress(src, 0, destLen); 90 | } 91 | 92 | /** 93 | * Decompress src into dest. dest's 94 | * {@link ByteBuffer#remaining()} must be exactly the size of the decompressed 95 | * data. This method moves the positions of the buffers. 96 | */ 97 | public final void decompress(ByteBuffer src, ByteBuffer dest) { 98 | final int read = decompress(src, src.position(), dest, dest.position(), dest.remaining()); 99 | dest.position(dest.limit()); 100 | src.position(src.position() + read); 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return getClass().getSimpleName(); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/lz4/LZ4SafeUtils.java: -------------------------------------------------------------------------------- 1 | /* Partial import of https://github.com/jpountz/lz4-java, Apache 2.0 licensed. */ 2 | 3 | package info.ata4.util.lz4; 4 | 5 | /* 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static info.ata4.util.lz4.LZ4Constants.LAST_LITERALS; 20 | import static info.ata4.util.lz4.LZ4Constants.ML_BITS; 21 | import static info.ata4.util.lz4.LZ4Constants.ML_MASK; 22 | import static info.ata4.util.lz4.LZ4Constants.RUN_MASK; 23 | 24 | enum LZ4SafeUtils { 25 | ; 26 | 27 | static int hash(byte[] buf, int i) { 28 | return LZ4Utils.hash(SafeUtils.readInt(buf, i)); 29 | } 30 | 31 | static int hash64k(byte[] buf, int i) { 32 | return LZ4Utils.hash64k(SafeUtils.readInt(buf, i)); 33 | } 34 | 35 | static boolean readIntEquals(byte[] buf, int i, int j) { 36 | return buf[i] == buf[j] && buf[i+1] == buf[j+1] && buf[i+2] == buf[j+2] && buf[i+3] == buf[j+3]; 37 | } 38 | 39 | static void safeIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchLen) { 40 | for (int i = 0; i < matchLen; ++i) { 41 | dest[dOff + i] = dest[matchOff + i]; 42 | } 43 | } 44 | 45 | static void wildIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchCopyEnd) { 46 | do { 47 | copy8Bytes(dest, matchOff, dest, dOff); 48 | matchOff += 8; 49 | dOff += 8; 50 | } while (dOff < matchCopyEnd); 51 | } 52 | 53 | static void copy8Bytes(byte[] src, int sOff, byte[] dest, int dOff) { 54 | for (int i = 0; i < 8; ++i) { 55 | dest[dOff + i] = src[sOff + i]; 56 | } 57 | } 58 | 59 | static int commonBytes(byte[] b, int o1, int o2, int limit) { 60 | int count = 0; 61 | while (o2 < limit && b[o1++] == b[o2++]) { 62 | ++count; 63 | } 64 | return count; 65 | } 66 | 67 | static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) { 68 | int count = 0; 69 | while (o1 > l1 && o2 > l2 && b[--o1] == b[--o2]) { 70 | ++count; 71 | } 72 | return count; 73 | } 74 | 75 | static void safeArraycopy(byte[] src, int sOff, byte[] dest, int dOff, int len) { 76 | System.arraycopy(src, sOff, dest, dOff, len); 77 | } 78 | 79 | static void wildArraycopy(byte[] src, int sOff, byte[] dest, int dOff, int len) { 80 | try { 81 | for (int i = 0; i < len; i += 8) { 82 | copy8Bytes(src, sOff + i, dest, dOff + i); 83 | } 84 | } catch (ArrayIndexOutOfBoundsException e) { 85 | throw new LZ4Exception("Malformed input at offset " + sOff); 86 | } 87 | } 88 | 89 | static int encodeSequence(byte[] src, int anchor, int matchOff, int matchRef, int matchLen, byte[] dest, int dOff, int destEnd) { 90 | final int runLen = matchOff - anchor; 91 | final int tokenOff = dOff++; 92 | 93 | if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { 94 | throw new LZ4Exception("maxDestLen is too small"); 95 | } 96 | 97 | int token; 98 | if (runLen >= RUN_MASK) { 99 | token = (byte) (RUN_MASK << ML_BITS); 100 | dOff = writeLen(runLen - RUN_MASK, dest, dOff); 101 | } else { 102 | token = runLen << ML_BITS; 103 | } 104 | 105 | // copy literals 106 | wildArraycopy(src, anchor, dest, dOff, runLen); 107 | dOff += runLen; 108 | 109 | // encode offset 110 | final int matchDec = matchOff - matchRef; 111 | dest[dOff++] = (byte) matchDec; 112 | dest[dOff++] = (byte) (matchDec >>> 8); 113 | 114 | // encode match len 115 | matchLen -= 4; 116 | if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { 117 | throw new LZ4Exception("maxDestLen is too small"); 118 | } 119 | if (matchLen >= ML_MASK) { 120 | token |= ML_MASK; 121 | dOff = writeLen(matchLen - RUN_MASK, dest, dOff); 122 | } else { 123 | token |= matchLen; 124 | } 125 | 126 | dest[tokenOff] = (byte) token; 127 | 128 | return dOff; 129 | } 130 | 131 | static int lastLiterals(byte[] src, int sOff, int srcLen, byte[] dest, int dOff, int destEnd) { 132 | final int runLen = srcLen; 133 | 134 | if (dOff + runLen + 1 + (runLen + 255 - RUN_MASK) / 255 > destEnd) { 135 | throw new LZ4Exception(); 136 | } 137 | 138 | if (runLen >= RUN_MASK) { 139 | dest[dOff++] = (byte) (RUN_MASK << ML_BITS); 140 | dOff = writeLen(runLen - RUN_MASK, dest, dOff); 141 | } else { 142 | dest[dOff++] = (byte) (runLen << ML_BITS); 143 | } 144 | // copy literals 145 | System.arraycopy(src, sOff, dest, dOff, runLen); 146 | dOff += runLen; 147 | 148 | return dOff; 149 | } 150 | 151 | static int writeLen(int len, byte[] dest, int dOff) { 152 | while (len >= 0xFF) { 153 | dest[dOff++] = (byte) 0xFF; 154 | len -= 0xFF; 155 | } 156 | dest[dOff++] = (byte) len; 157 | return dOff; 158 | } 159 | 160 | static class Match { 161 | int start, ref, len; 162 | 163 | void fix(int correction) { 164 | start += correction; 165 | ref += correction; 166 | len -= correction; 167 | } 168 | 169 | int end() { 170 | return start + len; 171 | } 172 | } 173 | 174 | static void copyTo(Match m1, Match m2) { 175 | m2.len = m1.len; 176 | m2.start = m1.start; 177 | m2.ref = m1.ref; 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/lz4/LZ4Utils.java: -------------------------------------------------------------------------------- 1 | /* Partial import of https://github.com/jpountz/lz4-java, Apache 2.0 licensed. */ 2 | 3 | package info.ata4.util.lz4; 4 | 5 | /* 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static info.ata4.util.lz4.LZ4Constants.HASH_LOG; 20 | import static info.ata4.util.lz4.LZ4Constants.HASH_LOG_64K; 21 | import static info.ata4.util.lz4.LZ4Constants.HASH_LOG_HC; 22 | import static info.ata4.util.lz4.LZ4Constants.LAST_LITERALS; 23 | import static info.ata4.util.lz4.LZ4Constants.MIN_MATCH; 24 | import static info.ata4.util.lz4.LZ4Constants.ML_BITS; 25 | import static info.ata4.util.lz4.LZ4Constants.ML_MASK; 26 | import static info.ata4.util.lz4.LZ4Constants.RUN_MASK; 27 | 28 | enum LZ4Utils { 29 | ; 30 | 31 | private static final int MAX_INPUT_SIZE = 0x7E000000; 32 | 33 | static int maxCompressedLength(int length) { 34 | if (length < 0) { 35 | throw new IllegalArgumentException("length must be >= 0, got " + length); 36 | } else if (length >= MAX_INPUT_SIZE) { 37 | throw new IllegalArgumentException("length must be < " + MAX_INPUT_SIZE); 38 | } 39 | return length + length / 255 + 16; 40 | } 41 | 42 | static int hash(int i) { 43 | return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG); 44 | } 45 | 46 | static int hash64k(int i) { 47 | return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG_64K); 48 | } 49 | 50 | static int hashHC(int i) { 51 | return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG_HC); 52 | } 53 | 54 | static class Match { 55 | int start, ref, len; 56 | 57 | void fix(int correction) { 58 | start += correction; 59 | ref += correction; 60 | len -= correction; 61 | } 62 | 63 | int end() { 64 | return start + len; 65 | } 66 | } 67 | 68 | static void copyTo(Match m1, Match m2) { 69 | m2.len = m1.len; 70 | m2.start = m1.start; 71 | m2.ref = m1.ref; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/lz4/SafeUtils.java: -------------------------------------------------------------------------------- 1 | /* Partial import of https://github.com/jpountz/lz4-java, Apache 2.0 licensed. */ 2 | 3 | package info.ata4.util.lz4; 4 | 5 | /* 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteOrder; 20 | 21 | public enum SafeUtils { 22 | ; 23 | 24 | public static void checkRange(byte[] buf, int off) { 25 | if (off < 0 || off >= buf.length) { 26 | throw new ArrayIndexOutOfBoundsException(off); 27 | } 28 | } 29 | 30 | public static void checkRange(byte[] buf, int off, int len) { 31 | checkLength(len); 32 | if (len > 0) { 33 | checkRange(buf, off); 34 | checkRange(buf, off + len - 1); 35 | } 36 | } 37 | 38 | public static void checkLength(int len) { 39 | if (len < 0) { 40 | throw new IllegalArgumentException("lengths must be >= 0"); 41 | } 42 | } 43 | 44 | public static byte readByte(byte[] buf, int i) { 45 | return buf[i]; 46 | } 47 | 48 | public static int readIntBE(byte[] buf, int i) { 49 | return ((buf[i] & 0xFF) << 24) | ((buf[i+1] & 0xFF) << 16) | ((buf[i+2] & 0xFF) << 8) | (buf[i+3] & 0xFF); 50 | } 51 | 52 | public static int readIntLE(byte[] buf, int i) { 53 | return (buf[i] & 0xFF) | ((buf[i+1] & 0xFF) << 8) | ((buf[i+2] & 0xFF) << 16) | ((buf[i+3] & 0xFF) << 24); 54 | } 55 | 56 | public static int readInt(byte[] buf, int i) { 57 | if (Utils.NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { 58 | return readIntBE(buf, i); 59 | } else { 60 | return readIntLE(buf, i); 61 | } 62 | } 63 | 64 | public static long readLongLE(byte[] buf, int i) { 65 | return (buf[i] & 0xFFL) | ((buf[i+1] & 0xFFL) << 8) | ((buf[i+2] & 0xFFL) << 16) | ((buf[i+3] & 0xFFL) << 24) 66 | | ((buf[i+4] & 0xFFL) << 32) | ((buf[i+5] & 0xFFL) << 40) | ((buf[i+6] & 0xFFL) << 48) | ((buf[i+7] & 0xFFL) << 56); 67 | } 68 | 69 | public static void writeShortLE(byte[] buf, int off, int v) { 70 | buf[off++] = (byte) v; 71 | buf[off++] = (byte) (v >>> 8); 72 | } 73 | 74 | public static void writeInt(int[] buf, int off, int v) { 75 | buf[off] = v; 76 | } 77 | 78 | public static int readInt(int[] buf, int off) { 79 | return buf[off]; 80 | } 81 | 82 | public static void writeByte(byte[] dest, int off, int i) { 83 | dest[off] = (byte) i; 84 | } 85 | 86 | public static void writeShort(short[] buf, int off, int v) { 87 | buf[off] = (short) v; 88 | } 89 | 90 | public static int readShortLE(byte[] buf, int i) { 91 | return (buf[i] & 0xFF) | ((buf[i+1] & 0xFF) << 8); 92 | } 93 | 94 | public static int readShort(short[] buf, int off) { 95 | return buf[off] & 0xFFFF; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /disunity-core/src/main/java/info/ata4/util/lz4/Utils.java: -------------------------------------------------------------------------------- 1 | /* Partial import of https://github.com/jpountz/lz4-java, Apache 2.0 licensed. */ 2 | 3 | package info.ata4.util.lz4; 4 | 5 | /* 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteOrder; 20 | 21 | public enum Utils { 22 | ; 23 | 24 | public static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder(); 25 | 26 | private static final boolean unalignedAccessAllowed; 27 | static { 28 | String arch = System.getProperty("os.arch"); 29 | unalignedAccessAllowed = arch.equals("i386") || arch.equals("x86") 30 | || arch.equals("amd64") || arch.equals("x86_64"); 31 | } 32 | 33 | public static boolean isUnalignedAccessAllowed() { 34 | return unalignedAccessAllowed; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /disunity-core/src/main/resources/strings/5.x.txt: -------------------------------------------------------------------------------- 1 | AABB 2 | AnimationClip 3 | AnimationCurve 4 | AnimationState 5 | Array 6 | Base 7 | BitField 8 | bitset 9 | bool 10 | char 11 | ColorRGBA 12 | Component 13 | data 14 | deque 15 | double 16 | dynamic_array 17 | FastPropertyName 18 | first 19 | float 20 | Font 21 | GameObject 22 | Generic Mono 23 | GradientNEW 24 | GUID 25 | GUIStyle 26 | int 27 | list 28 | long long 29 | map 30 | Matrix4x4f 31 | MdFour 32 | MonoBehaviour 33 | MonoScript 34 | m_ByteSize 35 | m_Curve 36 | m_EditorClassIdentifier 37 | m_EditorHideFlags 38 | m_Enabled 39 | m_ExtensionPtr 40 | m_GameObject 41 | m_Index 42 | m_IsArray 43 | m_IsStatic 44 | m_MetaFlag 45 | m_Name 46 | m_ObjectHideFlags 47 | m_PrefabInternal 48 | m_PrefabParentObject 49 | m_Script 50 | m_StaticEditorFlags 51 | m_Type 52 | m_Version 53 | Object 54 | pair 55 | PPtr 56 | PPtr 57 | PPtr 58 | PPtr 59 | PPtr 60 | PPtr 61 | PPtr 62 | PPtr 63 | PPtr 64 | PPtr 65 | PPtr 66 | PPtr 67 | Prefab 68 | Quaternionf 69 | Rectf 70 | RectInt 71 | RectOffset 72 | second 73 | set 74 | short 75 | size 76 | SInt16 77 | SInt32 78 | SInt64 79 | SInt8 80 | staticvector 81 | string 82 | TextAsset 83 | TextMesh 84 | Texture 85 | Texture2D 86 | Transform 87 | TypelessData 88 | UInt16 89 | UInt32 90 | UInt64 91 | UInt8 92 | unsigned int 93 | unsigned long long 94 | unsigned short 95 | vector 96 | Vector2f 97 | Vector3f 98 | Vector4f 99 | m_ScriptingClassIdentifier 100 | -------------------------------------------------------------------------------- /disunity-core/src/test/java/info/ata4/test/ParameterizedUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 08 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.test; 11 | 12 | import java.io.IOException; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | 18 | /** 19 | * 20 | * @author Nico Bergemann 21 | */ 22 | public class ParameterizedUtils { 23 | 24 | private ParameterizedUtils() { 25 | } 26 | 27 | public static List getPathParameters(Path dir) throws IOException { 28 | return Files.walk(dir) 29 | .filter(Files::isRegularFile) 30 | .map(file -> new Path[] {file}) 31 | .collect(Collectors.toList()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /disunity-core/src/test/java/info/ata4/test/junity/BundleTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2015 December 06 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** May you do good and not evil. 7 | ** May you find forgiveness for yourself and forgive others. 8 | ** May you share freely, never taking more than you give. 9 | */ 10 | package info.ata4.test.junity; 11 | 12 | import com.google.common.io.CountingOutputStream; 13 | import info.ata4.junity.bundle.Bundle; 14 | import info.ata4.junity.bundle.BundleHeader; 15 | import info.ata4.junity.bundle.BundleReader; 16 | import info.ata4.test.ParameterizedUtils; 17 | import static info.ata4.util.function.IOConsumer.uncheck; 18 | import java.io.IOException; 19 | import java.nio.file.Files; 20 | import java.nio.file.Path; 21 | import java.nio.file.Paths; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import org.apache.commons.io.IOUtils; 25 | import org.apache.commons.io.output.NullOutputStream; 26 | import org.junit.After; 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertTrue; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | import org.junit.runner.RunWith; 32 | import org.junit.runners.Parameterized; 33 | 34 | /** 35 | * 36 | * @author Nico Bergemann 37 | */ 38 | @RunWith(Parameterized.class) 39 | public class BundleTest { 40 | 41 | @Parameterized.Parameters 42 | public static List data() throws IOException { 43 | List params = new ArrayList<>(); 44 | 45 | // public set 46 | Path bundleDir = Paths.get("src", "test", "resources", "bundle"); 47 | params.addAll(ParameterizedUtils.getPathParameters(bundleDir)); 48 | 49 | return params; 50 | } 51 | 52 | private final Path file; 53 | private Bundle bundle; 54 | private final BundleReader reader; 55 | 56 | public BundleTest(Path file) throws IOException { 57 | this.file = file; 58 | this.reader = new BundleReader(file); 59 | System.out.println(file); 60 | } 61 | 62 | @Before 63 | public void setUp() throws IOException { 64 | bundle = reader.read(); 65 | } 66 | 67 | @After 68 | public void tearDown() throws IOException { 69 | reader.close(); 70 | } 71 | 72 | @Test 73 | public void headerValid() throws IOException { 74 | BundleHeader header = bundle.header(); 75 | long fileSize = Files.size(file); 76 | 77 | assertTrue("Number of levels to download must be equal to number of levels or 1", 78 | header.numberOfLevelsToDownload() == header.numberOfLevels() || header.numberOfLevelsToDownload() == 1); 79 | 80 | assertTrue("Signatures should be valid", header.hasValidSignature()); 81 | 82 | assertTrue("Minimum streamed bytes must be smaller than or equal to file size", 83 | header.minimumStreamedBytes() <= fileSize); 84 | 85 | assertTrue("Header size must be smaller than file size", 86 | header.headerSize() < fileSize); 87 | 88 | if (header.streamVersion() >= 2) { 89 | assertEquals("Header file size and actual file size must be equal", 90 | header.completeFileSize(), fileSize); 91 | } 92 | 93 | assertEquals("Number of levels must match number of level end offsets", 94 | header.numberOfLevelsToDownload(), header.levelByteEnd().size()); 95 | 96 | header.levelByteEnd().forEach(lbe -> { 97 | assertTrue("Compressed offset must be smaller or equal to uncompressed offset", 98 | lbe.getLeft() <= lbe.getRight()); 99 | }); 100 | } 101 | 102 | @Test 103 | public void entriesValid() { 104 | assertEquals("Bundle entry lists must match in size", 105 | bundle.entries().size(), bundle.entryInfos().size()); 106 | 107 | bundle.entries().forEach(uncheck(entry -> { 108 | CountingOutputStream cos = new CountingOutputStream(new NullOutputStream()); 109 | IOUtils.copy(entry.inputStream(), cos); 110 | assertEquals("Entry size must match size of InputStream", entry.size(), cos.getCount()); 111 | })); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /disunity-core/src/test/resources/bundle/1/Chelsea.unity3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/bundle/1/Chelsea.unity3d -------------------------------------------------------------------------------- /disunity-core/src/test/resources/bundle/1/whitewash1.unity3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/bundle/1/whitewash1.unity3d -------------------------------------------------------------------------------- /disunity-core/src/test/resources/bundle/2/DreamTree.unity3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/bundle/2/DreamTree.unity3d -------------------------------------------------------------------------------- /disunity-core/src/test/resources/bundle/2/dressingroom.unity3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/bundle/2/dressingroom.unity3d -------------------------------------------------------------------------------- /disunity-core/src/test/resources/bundle/3/MeatHookTrainer_v0.3.unity3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/bundle/3/MeatHookTrainer_v0.3.unity3d -------------------------------------------------------------------------------- /disunity-core/src/test/resources/bundle/3/bluedoor.unity3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/bundle/3/bluedoor.unity3d -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/14/53c8c79bc0dfc2c24e66af587008ea3a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/14/53c8c79bc0dfc2c24e66af587008ea3a -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/14/58e88e08b508c8ede1b1f198647de952: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/14/58e88e08b508c8ede1b1f198647de952 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/15/241a333f70fa6f03d3de6b06cbf004a6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/15/241a333f70fa6f03d3de6b06cbf004a6 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/15/e8809d217ea31d6ea1c0bb1579fe6ee3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/15/e8809d217ea31d6ea1c0bb1579fe6ee3 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/5/44b11c4151b06d329ea50f043e58b5f9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/5/44b11c4151b06d329ea50f043e58b5f9 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/5/9e7049ec231bfd1af06b7e23471f41a1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/5/9e7049ec231bfd1af06b7e23471f41a1 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/5/a92551a39b0e9fa70084eb0c30cde088: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/5/a92551a39b0e9fa70084eb0c30cde088 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/5/cea0b7475b5ad4c5be503ebbed637f48: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/5/cea0b7475b5ad4c5be503ebbed637f48 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/285a8c4349da67936943b06e24a75aa5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/285a8c4349da67936943b06e24a75aa5 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/3e59963dfe80f214e8ae73ce418e8d7f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/3e59963dfe80f214e8ae73ce418e8d7f -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/5eb4694feb92d12aa27ef851c8b530f5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/5eb4694feb92d12aa27ef851c8b530f5 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/7c5ffcd199dfb2e2eba75156752e1ed0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/7c5ffcd199dfb2e2eba75156752e1ed0 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/9874912f38d9433edd5685bdf30736bf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/9874912f38d9433edd5685bdf30736bf -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/a25da1d2ad1f177f73c265afe78da5bc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/a25da1d2ad1f177f73c265afe78da5bc -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/a946c49b09a5cc670e48e06f8c7932c7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/a946c49b09a5cc670e48e06f8c7932c7 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/bb7f0c734f7f70255c8c8e53aea45a13: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/bb7f0c734f7f70255c8c8e53aea45a13 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/cfef6433d2d415b9e2bb1dfe56fb0771: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/cfef6433d2d415b9e2bb1dfe56fb0771 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/6/f183e15084591efcf42b9796aa9f018e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/6/f183e15084591efcf42b9796aa9f018e -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/0a688c2bd852f9cfd6747991ed0cbdce: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/0a688c2bd852f9cfd6747991ed0cbdce -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/21a3dad289724b6db82a6152a7a80b37: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/21a3dad289724b6db82a6152a7a80b37 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/4db150ee8ead84646647d0ba8e2274f6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/4db150ee8ead84646647d0ba8e2274f6 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/775a55777d336cc66fc1b741e2023f79: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/775a55777d336cc66fc1b741e2023f79 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/7cb556e951b0df8fc6957f53a079a79b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/7cb556e951b0df8fc6957f53a079a79b -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/a3a44be259aca6f98b13c51afee605aa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/a3a44be259aca6f98b13c51afee605aa -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/af5d44dfffe8596b900ec92541e719ee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/af5d44dfffe8596b900ec92541e719ee -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/ccd881a72d1dc7e39f1abbb6a578fef0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/ccd881a72d1dc7e39f1abbb6a578fef0 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/edd21066c169344ae06f794484652322: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/edd21066c169344ae06f794484652322 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/8/f84209d75bbee3ef4a0715fdbfa6b422: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/8/f84209d75bbee3ef4a0715fdbfa6b422 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/00f3c960b949ed053dee5cd29ef30b9b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/00f3c960b949ed053dee5cd29ef30b9b -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/05616b32f0d1c6d185ba47daef2d2871: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/05616b32f0d1c6d185ba47daef2d2871 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/19353bc1e35d00d49eddadddddff36cb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/19353bc1e35d00d49eddadddddff36cb -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/1e906936c6d03971f5317a19c70300d2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/1e906936c6d03971f5317a19c70300d2 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/25c311bcb0ad2dc2a147c722f5a2bdea: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/25c311bcb0ad2dc2a147c722f5a2bdea -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/497c2b1f8c30cd4630dfc4cd7341964e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/497c2b1f8c30cd4630dfc4cd7341964e -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/7e2f25e2c875564cd43e2583b08fd45c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/7e2f25e2c875564cd43e2583b08fd45c -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/a9c2c78a98823559ed1eae593bd64261: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/a9c2c78a98823559ed1eae593bd64261 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/d1f556b864ae617acbe6079bc41818a6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/d1f556b864ae617acbe6079bc41818a6 -------------------------------------------------------------------------------- /disunity-core/src/test/resources/mainData/9/e89097444c46d1936351a610eb69a6e2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ata4/disunity/6c1c3215419faaca427fa8d1e960f13cde76e766/disunity-core/src/test/resources/mainData/9/e89097444c46d1936351a610eb69a6e2 -------------------------------------------------------------------------------- /disunity-dist/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | DisUnity Dist 5 | disunity-dist 6 | jar 7 | Distribution module for DisUnity. 8 | 9 | 10 | info.ata4.disunity 11 | disunity 12 | 0.5-SNAPSHOT 13 | 14 | 15 | 16 | 17 | info.ata4.disunity 18 | disunity-core 19 | 0.5-SNAPSHOT 20 | 21 | 22 | info.ata4.disunity 23 | disunity-cli 24 | 0.5-SNAPSHOT 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 2.4.2 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | disunity 43 | 44 | 45 | info.ata4.disunity.cli.DisUnityCli 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 1.8 56 | 1.8 57 | 58 | 59 | -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | X-COMMENT: Main-Class will be added automatically by build 3 | 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | DisUnity 5 | disunity 6 | info.ata4.disunity 7 | pom 8 | 0.5-SNAPSHOT 9 | An experimental toolset for Unity asset and asset bundle files. 10 | https://github.com/ata4/disunity 11 | 12 | 13 | 14 | Unlicense 15 | http://unlicense.org 16 | repo 17 | 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 25 | 26 | 27 | 28 | jitpack.io 29 | https://jitpack.io 30 | 31 | 32 | 33 | 34 | 35 | commons-io 36 | commons-io 37 | 2.4 38 | 39 | 40 | org.apache.commons 41 | commons-lang3 42 | 3.3.1 43 | 44 | 45 | com.google.code.gson 46 | gson 47 | 2.5 48 | jar 49 | 50 | 51 | com.google.guava 52 | guava 53 | 19.0-rc2 54 | 55 | 56 | 57 | 58 | com.github.ata4 59 | lzmajio 60 | e6877e7e01 61 | 62 | 63 | com.github.ata4 64 | ioutils 65 | 047e401d73 66 | 67 | 68 | 69 | 70 | disunity-core 71 | disunity-cli 72 | disunity-dist 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /scripts/disunity.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | java -jar "%~dp0\disunity.jar" %* 3 | -------------------------------------------------------------------------------- /scripts/disunity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | BASEDIR=$(dirname "$0") 3 | java -jar "$BASEDIR/disunity.jar" "$@" 4 | --------------------------------------------------------------------------------