mirror of
https://github.com/kuroppoi/entralinked.git
synced 2026-03-21 17:24:40 -05:00
Add menu option to import save files for Memory Link
Some checks failed
Build / dist (push) Has been cancelled
Some checks failed
Build / dist (push) Has been cancelled
This commit is contained in:
parent
abc32e5051
commit
b37f8b5624
|
|
@ -7,10 +7,15 @@ import java.awt.Dimension;
|
|||
import java.awt.Font;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
|
|
@ -19,6 +24,7 @@ import javax.swing.JFrame;
|
|||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTabbedPane;
|
||||
|
|
@ -38,8 +44,14 @@ import com.formdev.flatlaf.intellijthemes.FlatOneDarkIJTheme;
|
|||
import com.formdev.flatlaf.util.ColorFunctions;
|
||||
|
||||
import entralinked.Entralinked;
|
||||
import entralinked.GameVersion;
|
||||
import entralinked.gui.FileChooser;
|
||||
import entralinked.gui.panels.DashboardPanel;
|
||||
import entralinked.model.player.Player;
|
||||
import entralinked.model.player.PlayerManager;
|
||||
import entralinked.utility.ConsumerAppender;
|
||||
import entralinked.utility.GsidUtility;
|
||||
import entralinked.utility.LEInputStream;
|
||||
import entralinked.utility.SwingUtility;
|
||||
|
||||
/**
|
||||
|
|
@ -52,9 +64,13 @@ public class MainView {
|
|||
public static final Color TEXT_COLOR_ERROR = Color.RED.darker();
|
||||
private final StyleContext styleContext = StyleContext.getDefaultStyleContext();
|
||||
private final AttributeSet fontAttribute = styleContext.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.FontFamily, "Consolas");
|
||||
private final Entralinked entralinked;
|
||||
private final JFrame frame;
|
||||
private final JLabel statusLabel;
|
||||
|
||||
public MainView(Entralinked entralinked) {
|
||||
this.entralinked = entralinked;
|
||||
|
||||
// Set look and feel
|
||||
FlatOneDarkIJTheme.setup();
|
||||
UIManager.getDefaults().put("Component.focusedBorderColor", UIManager.get("Component.borderColor"));
|
||||
|
|
@ -112,10 +128,15 @@ public class MainView {
|
|||
tabbedPane.addTab("Dashboard", new DashboardPanel(entralinked));
|
||||
|
||||
// Create window
|
||||
JFrame frame = new JFrame("Entralinked");
|
||||
frame = new JFrame("Entralinked");
|
||||
|
||||
// Create menu bar
|
||||
JMenuBar menuBar = new JMenuBar();
|
||||
JMenu toolsMenu = new JMenu("Tools");
|
||||
toolsMenu.add(SwingUtility.createAction("Import save file (Memory Link)", () -> FileChooser.showFileOpenDialog(frame, selection -> {
|
||||
importSaveFile(selection.file());
|
||||
})));
|
||||
menuBar.add(toolsMenu);
|
||||
JMenu helpMenu = new JMenu("Help");
|
||||
helpMenu.add(SwingUtility.createAction("Update PID (Error 60000)", () -> new PidToolDialog(entralinked, frame)));
|
||||
helpMenu.add(SwingUtility.createAction("GitHub", () -> {
|
||||
|
|
@ -159,4 +180,77 @@ public class MainView {
|
|||
public void setStatusLabelText(String text) {
|
||||
statusLabel.setText(text);
|
||||
}
|
||||
|
||||
private void importSaveFile(File file) {
|
||||
// Check file size
|
||||
if(file.length() != 524288) {
|
||||
JOptionPane.showMessageDialog(frame, "Invalid file length.\n"
|
||||
+ "Expected 524288 bytes, got %s.".formatted(file.length()), "Attention", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerManager playerManager = entralinked.getPlayerManager();
|
||||
Player player = null;
|
||||
GameVersion version = null;
|
||||
|
||||
try(LEInputStream inputStream = new LEInputStream(new FileInputStream(file))) {
|
||||
inputStream.skipNBytes(0x19400); // Skip to trainer info
|
||||
inputStream.skipNBytes(0x4);
|
||||
String name = inputStream.readUTF16(7);
|
||||
inputStream.skipNBytes(0x2);
|
||||
int trainerId = inputStream.readInt();
|
||||
int profileId = inputStream.readInt();
|
||||
inputStream.skipNBytes(0x2);
|
||||
int language = inputStream.read();
|
||||
int romCode = inputStream.read();
|
||||
version = GameVersion.lookup(romCode, language);
|
||||
|
||||
// Check game version
|
||||
if(version == null || version.isVersion2()) {
|
||||
JOptionPane.showMessageDialog(frame, "This is not a Black & White save file.", "Attention", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
String gameSyncId = GsidUtility.stringifyGameSyncId(profileId == 0 ? Objects.hash(name, trainerId) & 0x7FFFFFFF : profileId);
|
||||
player = playerManager.doesPlayerExist(gameSyncId) ? playerManager.getPlayer(gameSyncId) : playerManager.registerPlayer(gameSyncId, version);
|
||||
} catch(Exception e) {
|
||||
SwingUtility.showExceptionInfo(frame, "Failed to read save data.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if player exists
|
||||
if(player == null) {
|
||||
JOptionPane.showMessageDialog(frame, "Failed to create player data.", "Attention", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check version mismatch
|
||||
if(player.getGameVersion() != version) {
|
||||
if(!SwingUtility.showIgnorableConfirmDialog(frame,
|
||||
"The game version stored in the profile data does not match.\n"
|
||||
+ "Do you want to overwrite it and import the save file anyway?", "Attention")) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.setGameVersion(version);
|
||||
|
||||
// Try to save player data
|
||||
if(!playerManager.savePlayer(player)) {
|
||||
JOptionPane.showMessageDialog(frame, "Failed to save player data.", "Attention", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy save file
|
||||
try {
|
||||
Files.copy(file.toPath(), player.getSaveFile().toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch(Exception e) {
|
||||
SwingUtility.showExceptionInfo(frame, "Failed to import save data.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
JOptionPane.showMessageDialog(frame, "Save file has been imported successfully!\n"
|
||||
+ "You can now use Memory Link with the following Game Sync ID:\n\n%s".formatted(player.getGameSyncId()),
|
||||
"Attention", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,13 @@ public class PlayerManager {
|
|||
public Player registerPlayer(String gameSyncId, GameVersion version) {
|
||||
// Check for duplicate Game Sync ID
|
||||
if(playerMap.containsKey(gameSyncId)) {
|
||||
logger.warn("Attempted to register duplicate player {}", gameSyncId);
|
||||
logger.warn("Attempted to register duplicate Game Sync ID: {}", gameSyncId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if Game Sync ID is valid
|
||||
if(!GsidUtility.isValidGameSyncId(gameSyncId)) {
|
||||
logger.warn("Attempted to register invalid Game Sync ID: {}", gameSyncId);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,4 +48,23 @@ public class LEInputStream extends FilterInputStream {
|
|||
public double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
public String readUTF16(int length) throws IOException {
|
||||
char[] charBuffer = new char[length];
|
||||
int read = 0;
|
||||
|
||||
for(int i = 0; i < charBuffer.length; i++) {
|
||||
int c = readShort() & 0xFFFF;
|
||||
|
||||
if(c == 0xFFFF) {
|
||||
break;
|
||||
}
|
||||
|
||||
charBuffer[i] = (char)c;
|
||||
read++;
|
||||
}
|
||||
|
||||
skipNBytes((length - (read + 1)) * 2);
|
||||
return new String(charBuffer, 0, read);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user