diff --git a/README.md b/README.md
index e262364..d181f7a 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,10 @@ This is a Java program requiring Java 8 or higher.
* Right-click to copy name, size, or GUID
* History.ini saves the last directory used
+Alternatively, it can extract everything from the command line using the `--extract-all` command
+```
+java -jar UnityPackageViewer.x.x.x.jar path/to/file.unitypackage --extract-all
+```
# Disclaimers
diff --git a/pom.xml b/pom.xml
index b72ec6b..9735dc9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
m35-projects
unity-package-viewer
- 0.0.1
+ 0.0.2
jar
UTF-8
diff --git a/src/main/java/unitypackage/model/UnityArchiveInputStream.java b/src/main/java/unitypackage/model/UnityArchiveInputStream.java
new file mode 100644
index 0000000..98a71e0
--- /dev/null
+++ b/src/main/java/unitypackage/model/UnityArchiveInputStream.java
@@ -0,0 +1,72 @@
+/*
+ * Basic .unitypackage Viewer
+ * Copyright (C) 2024 Michael Sabin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package unitypackage.model;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+
+/**
+ * Like {@link TarArchiveInputStream} but for .unitypackage files,
+ * except it skips all directories.
+ */
+public class UnityArchiveInputStream extends FilterInputStream {
+
+ private final TarArchiveInputStream tarInputStream;
+ private final Map tarPathsToUnityAsset = new TreeMap<>();
+
+ public UnityArchiveInputStream(UnityPackage unityPackage) throws IOException {
+ this(unityPackage.getTarInputStream(), unityPackage.getUnityAssetList());
+ }
+
+ public UnityArchiveInputStream(TarArchiveInputStream tarInputStream, List unityAssetsToRead) {
+ super(tarInputStream);
+ this.tarInputStream = tarInputStream;
+
+ for (UnityAsset unityAsset : unityAssetsToRead) {
+ if (!unityAsset.isProbablyDirectory()) {
+ tarPathsToUnityAsset.put(unityAsset.getTarPathOf_asset_File(), unityAsset);
+ }
+ }
+ }
+
+ /**
+ * Like {@link TarArchiveInputStream#getNextEntry()} but for Unity asset files,
+ * except it only returns actual files, skipping directories.
+ */
+ public UnityAsset getNextEntry() throws IOException {
+
+ TarArchiveEntry entry;
+ // Seek for the file of interest
+ while ((entry = tarInputStream.getNextEntry()) != null) {
+ String tarEntryName = entry.getName();
+ // Check if this file in the tar is an asset payload
+ UnityAsset unityAsset = tarPathsToUnityAsset.get(tarEntryName);
+ if (unityAsset != null) {
+ return unityAsset;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/unitypackage/model/UnityAssetBuilder.java b/src/main/java/unitypackage/model/UnityAssetBuilder.java
index dba0531..eca7e3a 100644
--- a/src/main/java/unitypackage/model/UnityAssetBuilder.java
+++ b/src/main/java/unitypackage/model/UnityAssetBuilder.java
@@ -102,18 +102,16 @@ public BufferedImage getPreview() {
return _preview;
}
- public UnityAssetBuilder(String guidBaseDirectory) {
- this.guidBaseDirectory = guidBaseDirectory;
+ public UnityAssetBuilder(String directoryGuidName) {
+ this.guidBaseDirectory = directoryGuidName;
}
- public UnityAssetBuilder(TarArchiveEntry tarEntry,
+ public UnityAssetBuilder(String guidBaseDirectory, String fileName, TarArchiveEntry tarEntry,
TarArchiveInputStream tarInputStream) throws IOException
{
- String rawFilePath = tarEntry.getName();
- File rawFile = new File(rawFilePath);
- guidBaseDirectory = rawFile.getParent();
+ this.guidBaseDirectory = guidBaseDirectory;
- addFileFoundInDirectory(tarEntry, tarInputStream);
+ addFileFoundInDirectory(this.guidBaseDirectory, fileName, tarEntry, tarInputStream);
}
public UnityAsset makeUnityAsset() {
@@ -123,34 +121,28 @@ public UnityAsset makeUnityAsset() {
/**
* Sanity check that only files that belong in this directory are being added.
*/
- public void assertGuidMatchesDirectoryName(String guid) {
- if (!guid.equals(guidBaseDirectory)) {
- throw new IllegalArgumentException("Argument guid " + guid + " != this guid" +
+ public void assertGuidMatchesDirectoryName(String directoryGuidName) {
+ if (!directoryGuidName.equals(guidBaseDirectory)) {
+ throw new IllegalArgumentException("Argument guid " + directoryGuidName + " != this guid" +
" dir " + guidBaseDirectory);
}
}
- public void addFileFoundInDirectory(TarArchiveEntry tarEntry,
+ public void addFileFoundInDirectory(String directoryGuidName, String fileName, TarArchiveEntry tarEntry,
TarArchiveInputStream tarInputStream) throws IOException {
- String rawFilePath = tarEntry.getName();
- File rawFile = new File(rawFilePath);
-
- String directoryGuidName = rawFile.getParent();
assertGuidMatchesDirectoryName(directoryGuidName);
- String rawFileName = rawFile.getName();
-
- switch (rawFileName) {
+ switch (fileName) {
case "asset":
asset_fileSize = tarEntry.getRealSize();
- rawPathTo_asset_file = rawFilePath;
+ rawPathTo_asset_file = tarEntry.getName();
asset_dateModified = tarEntry.getLastModifiedDate();
break;
case "asset.meta":
asset_meta_guid = findGuidIn_asset_meta_File(tarEntry, tarInputStream);
if (!asset_meta_guid.equals(guidBaseDirectory)) {
- // afaik the directory guid should match the guid in the asset.meta file
+ // Usually the directory guid matches the guid in the asset.meta file, but not always
String s = "Corrupted .unitypackage? directory guid" + " " + guidBaseDirectory + " != " + "asset.meta guid " + asset_meta_guid;
if (STRICT) {
throw new RuntimeException(s);
@@ -166,7 +158,7 @@ public void addFileFoundInDirectory(TarArchiveEntry tarEntry,
_preview = ImageIO.read(tarInputStream);
break;
default:
- throw new RuntimeException("File name not recognized " + rawFilePath);
+ throw new RuntimeException("File name not recognized " + tarEntry.getName());
}
}
diff --git a/src/main/java/unitypackage/model/UnityPackage.java b/src/main/java/unitypackage/model/UnityPackage.java
new file mode 100644
index 0000000..62e2df5
--- /dev/null
+++ b/src/main/java/unitypackage/model/UnityPackage.java
@@ -0,0 +1,171 @@
+/*
+ * Basic .unitypackage Viewer
+ * Copyright (C) 2024 Michael Sabin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package unitypackage.model;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+import java.util.zip.GZIPInputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+
+/**
+ * Indexes a .unitypackage and provides methods to read assets out of it.
+ */
+public class UnityPackage {
+
+ private static final String ROOT_ICON = ".icon.png";
+
+ private final File unitypackageFile;
+ private final List unityAssetList;
+
+ public UnityPackage(File unitypackageFile) throws IOException {
+ this.unitypackageFile = unitypackageFile;
+
+ // TODO are empty asset directories possible?
+ // that would break this program
+
+ TreeMap rootGuidDirectories = new TreeMap<>();
+
+ try (TarArchiveInputStream tarInput = getTarInputStream()) {
+
+ boolean hasDotRootDirectory = false;
+
+ TarArchiveEntry tarEntry;
+ while ((tarEntry = tarInput.getNextEntry()) != null) {
+
+ final String rawFilePathString = tarEntry.getName();
+ final boolean isDirectory = tarEntry.isDirectory();
+
+ Path rawPath = Paths.get(rawFilePathString);
+
+ if (rawPath.getNameCount() == 1) {
+ String shouldBeDirName = rawPath.getName(0).toString();
+ if (isDirectory) {
+ if (".".equals(shouldBeDirName)) {
+ hasDotRootDirectory = true;
+ continue;
+ }
+ } else if (!ROOT_ICON.equals(shouldBeDirName)) {
+ // I don't know if the root ".icon.png" would be next to a root "." or under it
+ throw new RuntimeException("Found root path that is not a directory or " + ROOT_ICON + ": " + rawFilePathString);
+ }
+
+ }
+
+ if (hasDotRootDirectory) {
+ // Trim off the "." before continuing
+ rawPath = rawPath.subpath(1, rawPath.getNameCount());
+ }
+
+ Path rawPathParent = rawPath.getParent();
+
+ String guidDirectory;
+ String fileName;
+
+ if (isDirectory) {
+ if (rawPathParent != null) {
+ throw new RuntimeException("Found nested directory " + rawFilePathString);
+ }
+ guidDirectory = rawPath.toString();
+ fileName = null;
+ } else {
+ fileName = rawPath.getFileName().toString();
+ guidDirectory = rawPathParent == null ? null : rawPathParent.toString();
+ }
+
+ if (guidDirectory == null) {
+ if (fileName.equals(ROOT_ICON)) {
+ // Image icon exists in the root
+ // TODO do something with this
+ // For now ignore it
+ continue;
+ } else {
+ throw new RuntimeException("Found nested directory " + rawFilePathString);
+ }
+ }
+
+ UnityAssetBuilder builder = rootGuidDirectories.get(guidDirectory);
+
+ if (builder == null) {
+ if (isDirectory) {
+ builder = new UnityAssetBuilder(guidDirectory);
+ } else {
+ // Do .tar archives always put a directory definition before any files under it?
+ // In any case, be flexible.
+ builder = new UnityAssetBuilder(guidDirectory, fileName, tarEntry, tarInput);
+ }
+ rootGuidDirectories.put(guidDirectory, builder);
+ } else {
+ if (isDirectory)
+ builder.assertGuidMatchesDirectoryName(guidDirectory);
+ else
+ builder.addFileFoundInDirectory(guidDirectory, fileName, tarEntry, tarInput);
+ }
+ }
+ }
+
+ List assets = rootGuidDirectories
+ .values()
+ .stream()
+ .map(tad -> tad.makeUnityAsset())
+ .collect(Collectors.toList());
+
+ unityAssetList = Collections.unmodifiableList(assets);
+ }
+
+ public File getUnitypackageFile() {
+ return unitypackageFile;
+ }
+
+ public List getUnityAssetList() {
+ return unityAssetList;
+ }
+
+ final public TarArchiveInputStream getTarInputStream() throws IOException {
+ return new TarArchiveInputStream(new GZIPInputStream(new FileInputStream(unitypackageFile)));
+ }
+
+ public UnityArchiveInputStream getUnityArchiveInputStream() throws IOException {
+ return new UnityArchiveInputStream(this);
+ }
+
+ public InputStream getFileStream(UnityAsset assetToExtract) throws IOException {
+
+ UnityArchiveInputStream unityInputStream = new UnityArchiveInputStream(getTarInputStream(),
+ Collections.singletonList(assetToExtract));
+
+ UnityAsset assetFound = unityInputStream.getNextEntry();
+
+ // Sanity check
+ if (assetToExtract != assetFound) {
+ throw new IllegalArgumentException("This should never happen");
+ }
+
+ return unityInputStream;
+ }
+
+}
diff --git a/src/main/java/unitypackage/model/UnitypackageReader.java b/src/main/java/unitypackage/model/UnitypackageReader.java
deleted file mode 100644
index 24d2523..0000000
--- a/src/main/java/unitypackage/model/UnitypackageReader.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Basic .unitypackage Viewer
- * Copyright (C) 2024 Michael Sabin
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package unitypackage.model;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
-import java.util.zip.GZIPInputStream;
-import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
-import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
-
-/**
- * .unitypackage files are actually pretty simple.
- *
- * It's a .tar file with a flat list of directories of the GUIDs in the package.
- * Inside each directory will contain files with these names:
- *
- * "pathname" = Contains one line of text that is the full path of the asset where it will appear when imported into Unity.
- * "asset.meta" = The corresponding .meta file.
- * "asset" = Contains the actual asset payload. Won't exist for directories.
- * "preview.png" = Optional preview of some types of assets.
- */
-public class UnitypackageReader {
-
- /**
- * Gets the stream of the file being requested from the given unitypackage.
- * @param unitypackageFile Should be the original file passed to {@link #readAssetsList(File)}.
- * If asset is not found will throw {@link IllegalArgumentException}.
- */
- public static InputStream getFileStream(UnityAsset assetToExtract, File unitypackageFile) throws IOException {
-
- TarArchiveInputStream tarInput = new TarArchiveInputStream(new GZIPInputStream(new FileInputStream(unitypackageFile)));
-
- String tarPathOf_asset_file = assetToExtract.getTarPathOf_asset_File();
-
- TarArchiveEntry entry;
- // Seek for the file of interest
- while ((entry = tarInput.getNextEntry()) != null) {
- if (entry.getName().equals(tarPathOf_asset_file)) {
- return tarInput;
- }
- }
-
- tarInput.close();
-
- throw new IllegalArgumentException("Could not find asset " + assetToExtract.getFullPath());
- }
-
- public static List readAssetsList(File unitypackageFile) throws IOException {
-
- // TODO are empty asset directories possible?
- // that would break this program
-
- TreeMap rootGuidDirectories = new TreeMap<>();
-
- try (TarArchiveInputStream tarInput = new TarArchiveInputStream(new GZIPInputStream(new FileInputStream(unitypackageFile)))) {
- TarArchiveEntry tarEntry;
-
- while ((tarEntry = tarInput.getNextEntry()) != null) {
-
- String rawFilePath = tarEntry.getName();
- File rawFile = new File(rawFilePath);
-
- final boolean isDirectory = tarEntry.isDirectory();
-
- String guidBaseDirectory;
- if (isDirectory) {
- if (rawFile.getParent() != null) {
- // afaik .unitypackage should only have 1 level of directories
- throw new RuntimeException("Found nested directory " + rawFilePath);
- }
- guidBaseDirectory = rawFile.getPath();
- } else {
- guidBaseDirectory = rawFile.getParent();
- }
-
- if (guidBaseDirectory == null) {
- if (".icon.png".equals(rawFilePath)) {
- // Image icon exists in the root
- // TODO do something with this
- // For now ignore it
- } else {
- throw new RuntimeException("Found nested directory " + rawFilePath);
- }
-
- } else {
- UnityAssetBuilder builder = rootGuidDirectories.get(guidBaseDirectory);
-
- if (builder == null) {
- if (isDirectory) {
- builder = new UnityAssetBuilder(guidBaseDirectory);
- } else {
- // Do .tar archives always put a directory definition before any files under it?
- // In any case, be flexible.
- builder = new UnityAssetBuilder(tarEntry, tarInput);
- }
- rootGuidDirectories.put(guidBaseDirectory, builder);
- } else {
- if (isDirectory)
- builder.assertGuidMatchesDirectoryName(guidBaseDirectory);
- else
- builder.addFileFoundInDirectory(tarEntry, tarInput);
- }
- }
- }
- }
-
- List assets = rootGuidDirectories
- .values()
- .stream()
- .map(tad -> tad.makeUnityAsset())
- .collect(Collectors.toList());
-
- return assets;
- }
-
-
-}
diff --git a/src/main/java/unitypackage/viewer/Main.java b/src/main/java/unitypackage/viewer/Main.java
index a099aea..4cbb800 100644
--- a/src/main/java/unitypackage/viewer/Main.java
+++ b/src/main/java/unitypackage/viewer/Main.java
@@ -18,20 +18,38 @@
package unitypackage.viewer;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Properties;
+import unitypackage.model.UnityArchiveInputStream;
+import unitypackage.model.UnityAsset;
+import unitypackage.model.UnityPackage;
import unitypackage.viewer.gui.MainWindow;
+import unitypackage.viewer.gui.UnitypackageFileName;
public class Main {
- public static String VERSION = "(development)";
+ private static final String VERSION_PROPERTY_FILE = "app.properties";
+ public static String DEVELOPMENT_VERSION = "(development)";
+ public static String VERSION = DEVELOPMENT_VERSION;
+ private static final String EXTRACT_ALL_COMMAND = "--extract-all";
+
+ /**
+ * Looks for the file {@link #VERSION_PROPERTY_FILE} that should have been filtered
+ * into the build with the application's version. But if not found, will default
+ * to {@link #DEVELOPMENT_VERSION}.
+ */
private static void initVersion() {
- String resource = "app.properties";
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
- try (InputStream propertyStream = classLoader.getResourceAsStream(resource)) {
+ try (InputStream propertyStream = classLoader.getResourceAsStream(VERSION_PROPERTY_FILE)) {
if (propertyStream != null) {
Properties properties = new Properties();
properties.load(propertyStream);
@@ -45,14 +63,61 @@ private static void initVersion() {
}
}
- public static void main(String[] args) {
+ public static void main(String[] args) throws IOException {
initVersion();
+ ArrayList argsList = new ArrayList<>(Arrays.asList(args));
+
+ boolean hasExtractAllCommand = false;
+ String fileToOpen = null;
+
+ if (!argsList.isEmpty()) {
+ hasExtractAllCommand = argsList.remove(EXTRACT_ALL_COMMAND);
+ if (!argsList.isEmpty()) {
+ fileToOpen = argsList.get(0);
+ }
+ }
+
+ if (hasExtractAllCommand) {
+ if (fileToOpen == null) {
+ System.out.println(EXTRACT_ALL_COMMAND + " expects a file to extract");
+ System.exit(1);
+ }
+ extractAll(fileToOpen);
+ } else {
+ runGui(fileToOpen);
+ }
+ }
+
+ private static void extractAll(String fileToOpen) throws IOException {
+ File file = new File(fileToOpen);
+ if (!UnitypackageFileName.isUnitypackage(file)) {
+ System.out.println("File \""+fileToOpen+"\" doesn't have a normal unitypackage file name.");
+ }
+
+ UnityPackage unityPackage = new UnityPackage(file);
+ try (UnityArchiveInputStream unityIS = unityPackage.getUnityArchiveInputStream()) {
+ UnityAsset nextAsset;
+ while ((nextAsset = unityIS.getNextEntry()) != null) {
+ Path assetPath = nextAsset.getFullPathAsPath();
+ Path parentDirectory = assetPath.getParent();
+ if (parentDirectory != null) {
+ Files.createDirectories(parentDirectory);
+ }
+
+ Files.copy(unityIS, assetPath, StandardCopyOption.REPLACE_EXISTING);
+ System.out.println("Extracted " + assetPath);
+ }
+ }
+ }
+
+ private static void runGui(String fileToOpen) {
+
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
- MainWindow frame = new MainWindow(args.length > 0 ? args[0] : null );
+ MainWindow frame = new MainWindow(fileToOpen);
frame.setVisible(true);
}
});
diff --git a/src/main/java/unitypackage/viewer/gui/MainWindow.java b/src/main/java/unitypackage/viewer/gui/MainWindow.java
index e66a773..b6e1b0d 100644
--- a/src/main/java/unitypackage/viewer/gui/MainWindow.java
+++ b/src/main/java/unitypackage/viewer/gui/MainWindow.java
@@ -471,7 +471,7 @@ private void guiExportButtonActionPerformed(java.awt.event.ActionEvent evt) {//G
UnityAsset asset = assetNode.getAsset();
String assetFileName = asset.getFileName();
- Path outputFile = guiModel.getCurrentUnitypackageFile().toPath().getParent().resolve(assetFileName);
+ Path outputFile = guiModel.getCurrentUnitypackage().toPath().getParent().resolve(assetFileName);
boolean exists = Files.exists(outputFile);
if (exists) {
int dialogResult = JOptionPane.showConfirmDialog(null,
diff --git a/src/main/java/unitypackage/viewer/gui/model/UnitypackageGuiModel.java b/src/main/java/unitypackage/viewer/gui/model/UnitypackageGuiModel.java
index db62c39..82436d9 100644
--- a/src/main/java/unitypackage/viewer/gui/model/UnitypackageGuiModel.java
+++ b/src/main/java/unitypackage/viewer/gui/model/UnitypackageGuiModel.java
@@ -35,18 +35,18 @@
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import unitypackage.model.UnityAsset;
-import unitypackage.model.UnitypackageReader;
+import unitypackage.model.UnityPackage;
public class UnitypackageGuiModel {
- private File currentUnitypackageFile;
+ private UnityPackage currentUnitypackage;
- public File getCurrentUnitypackageFile() {
- return currentUnitypackageFile;
+ public File getCurrentUnitypackage() {
+ return currentUnitypackage.getUnitypackageFile();
}
public void extractFile(UnityAsset asset, Path outputFile) throws IOException {
- try (InputStream is = UnitypackageReader.getFileStream(asset, currentUnitypackageFile)) {
+ try (InputStream is = currentUnitypackage.getFileStream(asset)) {
Files.copy(is, outputFile, StandardCopyOption.REPLACE_EXISTING);
}
}
@@ -88,10 +88,10 @@ public int compare(UnityAsset o1, UnityAsset o2) {
* @param unitypackagePath Path to the ".unitypackage" file.
*/
public DefaultTreeModel buildTreeModel(File unitypackagePath) throws IOException {
- currentUnitypackageFile = unitypackagePath;
- List unityAssets = UnitypackageReader.readAssetsList(unitypackagePath);
+ currentUnitypackage = new UnityPackage(unitypackagePath);
+ List unityAssets = currentUnitypackage.getUnityAssetList();
UnityTreeNode.Directory root = new UnityTreeNode.Directory(Paths.get("(root)"));
@@ -142,6 +142,7 @@ public DefaultTreeModel buildTreeModel(File unitypackagePath) throws IOException
private static UnityTreeNode.Directory findOrCreateDirectoryNode(UnityTreeNode parent, Path relativePathFromParent) {
+ @SuppressWarnings("unchecked")
Enumeration kids = parent.children();
while (kids.hasMoreElements()) {
Object nextKid = kids.nextElement();