예제 #1
0
  public void split() throws IOException {
    List<ObjectPath> pathTable = asset.getPaths();
    TypeTree typeTree = asset.getTypeTree();

    // assets with just one object can't be split any further
    if (pathTable.size() == 1) {
      L.warning("Asset doesn't contain sub-assets!");
      return;
    }

    for (ObjectPath path : pathTable) {
      // skip filtered classes
      if (cf != null && !cf.accept(path)) {
        continue;
      }

      String className = ClassID.getNameForID(path.getClassID(), true);

      AssetFile subAsset = new AssetFile();
      subAsset.getHeader().setFormat(asset.getHeader().getFormat());

      ObjectPath subFieldPath = new ObjectPath();
      subFieldPath.setClassID1(path.getClassID1());
      subFieldPath.setClassID2(path.getClassID2());
      subFieldPath.setLength(path.getLength());
      subFieldPath.setOffset(0);
      subFieldPath.setPathID(1);
      subAsset.getPaths().add(subFieldPath);

      TypeTree subTypeTree = subAsset.getTypeTree();
      subTypeTree.setEngineVersion(typeTree.getEngineVersion());
      subTypeTree.setVersion(-2);
      subTypeTree.setFormat(typeTree.getFormat());
      subTypeTree.getFields().put(path.getClassID(), typeTree.getFields().get(path.getClassID()));

      subAsset.setDataBuffer(asset.getPathBuffer(path));

      Path subAssetDir = outputDir.resolve(className);
      if (Files.notExists(subAssetDir)) {
        Files.createDirectories(subAssetDir);
      }

      // probe asset name
      String subAssetName = getObjectName(asset, path);
      if (subAssetName != null) {
        // remove any chars that could cause troubles on various file systems
        subAssetName = FilenameSanitizer.sanitizeName(subAssetName);
      } else {
        // use numeric names
        subAssetName = String.format("%06d", path.getPathID());
      }
      subAssetName += ".asset";

      Path subAssetFile = subAssetDir.resolve(subAssetName);
      if (Files.notExists(subAssetFile)) {
        L.log(Level.INFO, "Writing {0}", subAssetFile);
        subAsset.save(subAssetFile);
      }
    }
  }
예제 #2
0
  public void extract(boolean raw) throws IOException {
    List<ObjectPath> paths = asset.getPaths();
    Deserializer deser = new Deserializer(asset);

    for (AssetExtractHandler extractHandler : extractHandlerMap.values()) {
      extractHandler.setAssetFile(asset);
      extractHandler.setOutputDir(outputDir);
    }

    for (ObjectPath path : paths) {
      // skip filtered classes
      if (cf != null && !cf.accept(path)) {
        continue;
      }

      String className = ClassID.getNameForID(path.getClassID(), true);

      // write just the serialized object data or parsed and extracted content?
      if (raw) {
        String assetFileName = String.format("%06d.bin", path.getPathID());
        Path classDir = outputDir.resolve(className);
        if (Files.notExists(classDir)) {
          Files.createDirectories(classDir);
        }

        Path assetFile = classDir.resolve(assetFileName);

        L.log(Level.INFO, "Writing {0} {1}", new Object[] {className, assetFileName});

        ByteBuffer bbAsset = asset.getPathBuffer(path);

        try {
          ByteBufferUtils.save(assetFile, bbAsset);
        } catch (Exception ex) {
          L.log(Level.WARNING, "Can't write " + path + " to " + assetFile, ex);
        }
      } else {
        AssetExtractHandler handler = getHandler(className);

        if (handler != null) {
          UnityObject obj;

          try {
            obj = deser.deserialize(path);
          } catch (Exception ex) {
            L.log(Level.WARNING, "Can't deserialize " + path, ex);
            continue;
          }

          try {
            handler.setObjectPath(path);
            handler.extract(obj);
          } catch (Exception ex) {
            L.log(Level.WARNING, "Can't extract " + path, ex);
          }
        }
      }
    }
  }
예제 #3
0
  private void testWrite(AssetFile asset) throws IOException {
    Path tmpFile = Files.createTempFile("disunity", null);

    try {
      asset.save(tmpFile);

      if (!FileUtils.contentEquals(asset.getSourceFile().toFile(), tmpFile.toFile())) {
        throw new IOException("Files are not equal");
      }
    } finally {
      Files.deleteIfExists(tmpFile);
    }
  }
예제 #4
0
  public void printStats(PrintStream ps) {
    AssetObjectPathTable pathTable = asset.getObjectPaths();
    Map<String, Integer> classCounts = new HashMap<>();
    Map<String, Integer> classSizes = new HashMap<>();

    for (AssetObjectPath path : pathTable) {
      String className = ClassID.getInstance().getNameForID(path.classID2, true);

      if (!classCounts.containsKey(className)) {
        classCounts.put(className, 0);
        classSizes.put(className, 0);
      }

      classCounts.put(className, classCounts.get(className) + 1);
      classSizes.put(className, classSizes.get(className) + path.length);
    }

    ps.println("Classes by quantity:");
    Map<String, Integer> classCountsSorted = MapUtils.sortByValue(classCounts, true);
    for (Map.Entry<String, Integer> entry : classCountsSorted.entrySet()) {
      String className = entry.getKey();
      int classCount = entry.getValue();
      ps.printf("  %s: %d\n", className, classCount);
    }

    ps.println("Classes by data size:");
    Map<String, Integer> classSizesSorted = MapUtils.sortByValue(classSizes, true);
    for (Map.Entry<String, Integer> entry : classSizesSorted.entrySet()) {
      String className = entry.getKey();
      String classSize = humanReadableByteCount(entry.getValue(), true);
      ps.printf("  %s: %s\n", className, classSize);
    }
    ps.println();
  }
예제 #5
0
  @Override
  public void processAsset(AssetFile asset) throws IOException {
    Path assetFile = asset.getSourceFile();
    Path assetDir = assetFile.getParent();
    String assetFileName = assetFile.getFileName().toString();
    String assetPath;

    if (assetDir == null) {
      assetPath = "";
    } else {
      assetPath = assetDir.toAbsolutePath().toString();
      assetPath = FilenameUtils.separatorsToUnix(assetPath) + "/";
    }

    // fix path for all assets with (shared)assets extension
    boolean changed = false;
    for (AssetRef ref : asset.getReferences()) {
      Path refFile = Paths.get(ref.getFilePath());
      String refExt = FilenameUtils.getExtension(refFile.getFileName().toString());
      if (refExt.endsWith("assets") && Files.notExists(refFile)) {
        String filePathOld = ref.getFilePath();
        String filePathNew = assetPath + FilenameUtils.getName(ref.getFilePath());
        Path refFileNew = Paths.get(filePathNew);

        if (Files.exists(refFileNew)) {
          L.log(Level.FINE, "Fixed reference: {0} -> {1}", new Object[] {filePathOld, filePathNew});
          ref.setFilePath(filePathNew);
          changed = true;
        } else {
          L.log(Level.FINE, "Fixed reference not found: {0}", refFileNew);
        }
      }
    }

    if (!changed) {
      L.fine("No references changed, skipping saving");
      return;
    }

    // create backup by renaming the original file
    Path assetFileBackup = assetFile.resolveSibling(assetFileName + ".bak");
    Files.move(assetFile, assetFileBackup, StandardCopyOption.REPLACE_EXISTING);

    // save asset
    asset.save(assetFile);
  }
예제 #6
0
  @Override
  public void handleFile(Path file) throws IOException {
    AssetFile asset = new AssetFile();
    asset.load(file);

    try {
      testDeserialize(asset);
    } catch (Throwable t) {
      L.log(
          Level.WARNING,
          "{0} failed deserialization test: {1}",
          new Object[] {asset.getSourceFile(), t.getMessage()});
    }

    try {
      testWrite(asset);
    } catch (Throwable t) {
      L.log(
          Level.WARNING,
          "{0} failed write test: {1}",
          new Object[] {asset.getSourceFile(), t.getMessage()});
    }
  }
예제 #7
0
  public void printInfo(PrintStream ps) {
    AssetObjectPathTable objTable = asset.getObjectPaths();
    AssetRefTable refTable = asset.getReferences();
    AssetHeader header = asset.getHeader();
    AssetTypeTree fieldTree = asset.getTypeTree();

    ps.println("Header");
    ps.println("  File size: " + humanReadableByteCount(header.fileSize, true));
    ps.println("  Tree size: " + humanReadableByteCount(header.treeSize, true));
    ps.println("  Format: " + header.format);
    ps.println("  Data offset: " + header.dataOffset);
    ps.println("  Unknown: " + header.unknown);
    ps.println();

    ps.println("Serialized data");
    ps.println("  Revision: " + fieldTree.revision);
    ps.println("  Version: " + fieldTree.version);
    ps.println("  Standalone: " + (fieldTree.isStandalone() ? "yes" : "no"));
    ps.println("  Objects: " + objTable.size());
    ps.println();

    if (!refTable.isEmpty()) {
      ps.println("External references");
      for (AssetRef ref : refTable) {
        if (!ref.assetPath.isEmpty()) {
          ps.printf("  Asset path: \"%s\"\n", ref.assetPath);
        }
        if (!ref.filePath.isEmpty()) {
          ps.printf("  File path: \"%s\"\n", ref.filePath);
        }
        ps.printf("  GUID: %s\n", DatatypeConverter.printHexBinary(ref.guid));
        ps.printf("  Type: %d\n", ref.type);
        ps.println();
      }
    }
  }
예제 #8
0
 private void testDeserialize(AssetFile asset) throws IOException {
   for (ObjectData obj : asset.objects()) {
     obj.instance();
   }
 }