Skip to content

Commit

Permalink
Application: Embed type metadata and file paths
Browse files Browse the repository at this point in the history
  • Loading branch information
ShadelessFox committed Jun 3, 2024
1 parent 8f462f0 commit d651380
Show file tree
Hide file tree
Showing 15 changed files with 58 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -96,13 +94,11 @@ public Compressor getCompressor() {

@NotNull
public Stream<String> listAllFiles() throws IOException {
final Path fileListingsPath = container.getFileListingsPath();
final BufferedReader reader = container.getFilePaths();

if (fileListingsPath != null) {
return getListedFiles(fileListingsPath);
} else {
return getPrefetchFiles();
}
return reader
.lines()
.onClose(IOUtils.asUncheckedRunnable(reader));
}

@NotNull
Expand Down Expand Up @@ -149,24 +145,6 @@ public void close() throws IOException {
compressor.close();
}

@NotNull
private Stream<String> getListedFiles(@NotNull Path path) throws IOException {
final BufferedReader reader = IOUtils.newCompressedReader(path);

try {
return reader.lines().onClose(() -> {
try {
reader.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
} catch (Exception e) {
reader.close();
throw e;
}
}

@NotNull
private Stream<String> getPrefetchFiles() throws IOException {
final RTTIObject list = getPrefetchList();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.shade.decima.model.app;

import com.shade.decima.model.base.GameType;
import com.shade.platform.model.util.IOUtils;
import com.shade.util.NotNull;
import com.shade.util.Nullable;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.UUID;

Expand All @@ -16,27 +19,20 @@ public class ProjectContainer {
private Path packfilesPath;
private Path compressorPath;

private Path typeMetadataPath;
private Path fileListingsPath;

public ProjectContainer(
@NotNull UUID id,
@NotNull String name,
@NotNull GameType type,
@NotNull Path executablePath,
@NotNull Path packfilesPath,
@NotNull Path compressorPath,
@NotNull Path typeMetadataPath,
@Nullable Path fileListingsPath
@NotNull Path compressorPath
) {
this.id = id;
this.name = name;
this.type = type;
this.executablePath = executablePath;
this.packfilesPath = packfilesPath;
this.compressorPath = compressorPath;
this.typeMetadataPath = typeMetadataPath;
this.fileListingsPath = fileListingsPath;
}

@NotNull
Expand Down Expand Up @@ -90,21 +86,31 @@ public void setPackfilesPath(@NotNull Path packfilesPath) {
}

@NotNull
public Path getTypeMetadataPath() {
return typeMetadataPath;
}

public void setTypeMetadataPath(@NotNull Path typeMetadataPath) {
this.typeMetadataPath = typeMetadataPath;
public BufferedReader getTypeMetadata() throws IOException {
final String path = switch (type) {
case DS -> "metadata/ds_types.json.gz";
case DSDC -> "metadata/dsdc_types.json.gz";
case HZD -> "metadata/hzd_types.json.gz";
};
final InputStream is = type.getClass().getClassLoader().getResourceAsStream(path);
if (is == null) {
throw new IllegalStateException("Internal error: failed to locate file containing type metadata for " + type);
}
return IOUtils.newCompressedReader(is);
}

@Nullable
public Path getFileListingsPath() {
return fileListingsPath;
}

public void setFileListingsPath(@Nullable Path fileListingsPath) {
this.fileListingsPath = fileListingsPath;
@NotNull
public BufferedReader getFilePaths() throws IOException {
final String path = switch (type) {
case DS -> "metadata/ds_paths.txt.gz";
case DSDC -> "metadata/dsdc_paths.txt.gz";
case HZD -> "metadata/hzd_paths.txt.gz";
};
final InputStream is = type.getClass().getClassLoader().getResourceAsStream(path);
if (is == null) {
throw new IllegalStateException("Internal error: failed to locate file containing file paths for " + type);
}
return IOUtils.newCompressedReader(is);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import com.shade.util.NotNull;

import java.nio.file.Path;

public enum GameType {
DS("Death Stranding"),
DSDC("Death Stranding (Director's Cut)"),
Expand All @@ -15,29 +13,6 @@ public enum GameType {
this.name = name;
}

// Note regarding "known" values:
// We do know that these files exist, and we may use this information wisely.
// I'd like to embed these files in resources and choose based on the selected
// game type, but it might be useful to be able to specify custom values for these

@NotNull
public Path getKnownRttiTypesPath() {
return switch (this) {
case DS -> Path.of("data/ds_types.json.gz");
case DSDC -> Path.of("data/dsdc_types.json.gz");
case HZD -> Path.of("data/hzd_types.json.gz");
};
}

@NotNull
public Path getKnownFileListingsPath() {
return switch (this) {
case DS -> Path.of("data/ds_paths.txt.gz");
case DSDC -> Path.of("data/dsdc_paths.txt.gz");
case HZD -> Path.of("data/hzd_paths.txt.gz");
};
}

@Override
public String toString() {
return name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class ExternalTypeProvider implements RTTITypeProvider {
@SuppressWarnings("unchecked")
@Override
public void initialize(@NotNull RTTITypeRegistry registry, @NotNull ProjectContainer container) throws IOException {
try (Reader reader = IOUtils.newCompressedReader(container.getTypeMetadataPath())) {
try (Reader reader = container.getTypeMetadata()) {
declarations.putAll(new Gson().fromJson(reader, Map.class));
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ public void run() {
"Type: " + container.getType() + '\n' +
"ExecutablePath: " + container.getExecutablePath() + '\n' +
"CompressorPath: " + container.getCompressorPath() + '\n' +
"PackfilesPath: " + container.getPackfilesPath() + '\n' +
"TypeMetadataPath: " + container.getTypeMetadataPath() + '\n' +
"FileListingsPath: " + container.getFileListingsPath() + '\n'
"PackfilesPath: " + container.getPackfilesPath() + '\n'
// @formatter:on
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ private static Project createTempProject(@NotNull Path path) throws IOException
type,
gamePath,
dataPath,
oodlePath,
type.getKnownRttiTypesPath(),
type.getKnownFileListingsPath()
oodlePath
);

manager.addProject(container);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ public class ProjectEditDialog extends BaseEditDialog {
private final JTextField archiveFolderPath;
private final JTextField compressorPath;
private final ColoredComponent compressorNote;
private final JTextField rttiInfoFilePath;
private final JTextField fileListingsPath;

public ProjectEditDialog(boolean persisted, boolean editable) {
super(persisted ? "Edit Project" : "New Project");
Expand All @@ -50,7 +48,6 @@ public ProjectEditDialog(boolean persisted, boolean editable) {

this.projectType = new JComboBox<>(GameType.values());
this.projectType.setEnabled(editable);
this.projectType.addItemListener(e -> fillValuesBasedOnGameType((GameType) e.getItem(), projectType.getItemAt(projectType.getSelectedIndex())));

this.executableFilePath = new JTextField();
this.executableFilePath.setEnabled(editable);
Expand All @@ -66,12 +63,6 @@ public ProjectEditDialog(boolean persisted, boolean editable) {
this.compressorPath = new JTextField();
this.compressorPath.setEnabled(editable);

this.rttiInfoFilePath = new JTextField();
this.rttiInfoFilePath.setEnabled(editable);

this.fileListingsPath = new JTextField();
this.fileListingsPath.setEnabled(editable);

this.compressorNote = new ColoredComponent();
this.compressorNote.setVisible(false);
this.compressorPath.getDocument().addDocumentListener((DocumentAdapter) e -> {
Expand All @@ -93,8 +84,6 @@ public ProjectEditDialog(boolean persisted, boolean editable) {
});

if (!persisted) {
projectType.addItemListener(e -> fillValuesBasedOnGameType((GameType) e.getItem(), projectType.getItemAt(projectType.getSelectedIndex())));

executableFilePath.getDocument().addDocumentListener((DocumentAdapter) e -> {
if (UIUtils.isValid(executableFilePath)) {
fillValuesBasedOnGameExecutable(Path.of(executableFilePath.getText()));
Expand All @@ -112,7 +101,7 @@ protected JComponent createContentsPane() {
panel.add(new LabeledSeparator("Project"), "span,wrap");

if (persisted) {
panel.add(new JLabel("UUID:"), "gap ind");
panel.add(new JLabel("Id:"), "gap ind");
panel.add(projectId, "wrap");

UIUtils.addCopyAction(projectId);
Expand All @@ -126,7 +115,7 @@ protected JComponent createContentsPane() {
}

{
panel.add(new JLabel("Type:"), "gap ind");
panel.add(new JLabel("Game:"), "gap ind");
panel.add(projectType, "wrap");
}

Expand Down Expand Up @@ -170,38 +159,6 @@ protected JComponent createContentsPane() {
UIUtils.installInputValidator(compressorPath, new ExistingFileValidator(compressorPath, filter), this);
}

panel.add(new LabeledSeparator("Metadata"), "span,wrap");

{
final FileExtensionFilter filter = new FileExtensionFilter("RTTI information", "json", "json.gz");

final JLabel label = new JLabel("Type information:");
label.setToolTipText("Path to a file containing information about all data types found in game files.");

panel.add(label, "gap ind");
panel.add(rttiInfoFilePath, "wrap");

UIUtils.addOpenFileAction(rttiInfoFilePath, "Select RTTI information file", filter);
UIUtils.installInputValidator(rttiInfoFilePath, new ExistingFileValidator(rttiInfoFilePath, filter), this);
}

{
final FileExtensionFilter filter = new FileExtensionFilter("File listings", "txt", "txt.gz");

final JLabel label = new JLabel("File listings:");
label.setToolTipText("<html>Path to a file containing information about the complete list of files.<br>This file is not required, but all projects will benefit from it as it includes files that can't be normally seen<br>under their original names (instead, you would see a bunch of files under the <kbd>&lt;unnamed&gt;</kbd> folder in the navigator tree)</html>");

panel.add(label, "gap ind");
panel.add(fileListingsPath, "wrap");

UIUtils.addOpenFileAction(fileListingsPath, "Select file containing file listings", filter);
UIUtils.installInputValidator(fileListingsPath, new ExistingFileValidator(fileListingsPath, filter, false), this);
}

if (!persisted) {
fillValuesBasedOnGameType(projectType.getItemAt(0), projectType.getItemAt(0));
}

return panel;
}

Expand Down Expand Up @@ -231,8 +188,6 @@ public void load(@NotNull ProjectContainer container) {
executableFilePath.setText(container.getExecutablePath().toString());
archiveFolderPath.setText(container.getPackfilesPath().toString());
compressorPath.setText(container.getCompressorPath().toString());
rttiInfoFilePath.setText(container.getTypeMetadataPath().toString());
fileListingsPath.setText(container.getFileListingsPath() == null ? null : container.getFileListingsPath().toString());
}

public void save(@NotNull ProjectContainer container) {
Expand All @@ -241,24 +196,16 @@ public void save(@NotNull ProjectContainer container) {
container.setExecutablePath(Path.of(executableFilePath.getText()));
container.setPackfilesPath(Path.of(archiveFolderPath.getText()));
container.setCompressorPath(Path.of(compressorPath.getText()));
container.setTypeMetadataPath(Path.of(rttiInfoFilePath.getText()));
container.setFileListingsPath(fileListingsPath.getText().isEmpty() ? null : Path.of(fileListingsPath.getText()));
}

@Override
public boolean isComplete() {
return UIUtils.isValid(projectName)
&& UIUtils.isValid(executableFilePath)
&& UIUtils.isValid(archiveFolderPath)
&& UIUtils.isValid(rttiInfoFilePath)
&& UIUtils.isValid(compressorPath);
}

private void fillValuesBasedOnGameType(@NotNull GameType oldType, @NotNull GameType newType) {
setIfEmptyOrOldValue(rttiInfoFilePath, oldType.getKnownRttiTypesPath(), newType.getKnownRttiTypesPath());
setIfEmptyOrOldValue(fileListingsPath, oldType.getKnownFileListingsPath(), newType.getKnownFileListingsPath());
}

private void fillValuesBasedOnGameExecutable(@NotNull Path path) {
final String newFilename = IOUtils.getBasename(path).toLowerCase(Locale.ROOT);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static class NewProjectItem extends MenuItem {
@Override
public void perform(@NotNull MenuItemContext ctx) {
final ProjectEditDialog dialog = new ProjectEditDialog(false, true);
final ProjectContainer container = new ProjectContainer(UUID.randomUUID(), "New project", GameType.values()[0], Path.of(""), Path.of(""), Path.of(""), Path.of(""), Path.of(""));
final ProjectContainer container = new ProjectContainer(UUID.randomUUID(), "New project", GameType.values()[0], Path.of(""), Path.of(""), Path.of(""));

dialog.load(container);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,22 @@ private static String getExtension(@NotNull String filename, boolean lastPartOnl
}

@NotNull
public static BufferedReader newCompressedReader(@NotNull Path path) throws IOException {
return new BufferedReader(new InputStreamReader(newCompressedInputStream(path), StandardCharsets.UTF_8));
public static BufferedReader newCompressedReader(@NotNull InputStream is) throws IOException {
return new BufferedReader(new InputStreamReader(newCompressedInputStream(is), StandardCharsets.UTF_8));
}

@NotNull
public static InputStream newCompressedInputStream(@NotNull Path path) throws IOException {
final InputStream is = new BufferedInputStream(Files.newInputStream(path));
public static InputStream newCompressedInputStream(@NotNull InputStream is) throws IOException {
final InputStream bis = new BufferedInputStream(is);

is.mark(2);
final int magic = is.read() | is.read() << 8;
is.reset();
bis.mark(2);
final int magic = bis.read() | bis.read() << 8;
bis.reset();

if (magic == GZIPInputStream.GZIP_MAGIC) {
return new GZIPInputStream(is);
return new GZIPInputStream(bis);
} else {
return is;
return bis;
}
}

Expand Down Expand Up @@ -277,6 +277,17 @@ public static <T> T unchecked(@NotNull Callable<T> supplier) {
}
}

@NotNull
public static Runnable asUncheckedRunnable(@NotNull Closeable c) {
return () -> {
try {
c.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
}

@SuppressWarnings("unchecked")
public static <T, E extends Throwable> T sneakyThrow(@NotNull Throwable throwable) throws E {
throw (E) throwable;
Expand Down

0 comments on commit d651380

Please sign in to comment.