mirror of
https://github.com/kuroppoi/entralinked.git
synced 2026-09-08 00:35:12 -05:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
306b9e6db6 | ||
|
|
c0cc952cc2 | ||
|
|
2eb8054ba7 | ||
|
|
d61196fea1 | ||
|
|
8726614fce | ||
|
|
76c5d0e1a5 | ||
|
|
86434b2cb1 | ||
|
|
724e30bfcc | ||
|
|
87a06a4c08 | ||
|
|
e06b83c944 | ||
|
|
b890d27bba |
14
README.md
14
README.md
@@ -1,12 +1,19 @@
|
||||
# Entralinked
|
||||
[](https://github.com/kuroppoi/entralinked/actions)
|
||||
[](https://github.com/kuroppoi/entralinked/releases/latest)
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/kuroppoi/entralinked/master/images/icon.png" alt="icon"/>
|
||||
</p>
|
||||
<h1 align="center">Entralinked</h1>
|
||||
<p align="center">
|
||||
<a href="https://github.com/kuroppoi/entralinked/actions"><img src="https://github.com/kuroppoi/entralinked/actions/workflows/dist-upload-artifact.yml/badge.svg" alt="build"/></a>
|
||||
<a href="https://github.com/kuroppoi/entralinked/releases/latest"><img src="https://img.shields.io/github/v/release/kuroppoi/entralinked?labelColor=30373D&label=Release&logoColor=959DA5&logo=github" alt="release"/></a>
|
||||
</p>
|
||||
|
||||
Entralinked is a standalone Game Sync emulator developed for use with Pokémon Black & White and its sequels.\
|
||||
Its purpose is to serve as a simple utility for downloading Pokémon, Items, C-Gear skins, Pokédex skins, Musicals\
|
||||
and, in Black 2 & White 2 only, Join Avenue visitors to your game without needing to edit your save file.\
|
||||
It can also be used to Memory Link with a Black or White save file if you don't have a second DS system.
|
||||
|
||||
For users: [Quick Setup Guide](https://github.com/kuroppoi/entralinked/wiki/Setup)
|
||||
|
||||
## Building
|
||||
|
||||
#### Prerequisites
|
||||
@@ -29,5 +36,4 @@ Entralinked has a built-in DNS server.\
|
||||
In order for your game to connect, you must configure the DNS settings of your DS.\
|
||||
By default, Entralinked is configured to automatically use the local host of the system.\
|
||||
This approach is not always accurate, however, and you may need to manually configure it in `config.json`.\
|
||||
If you receive error code `60000` when trying to connect, erase the WFC Configuration of your DS and try again.\
|
||||
After tucking in a Pokémon, navigate to http://localhost/dashboard/profile.html to configure Game Sync settings.
|
||||
|
||||
@@ -27,6 +27,9 @@ dependencies {
|
||||
implementation 'io.netty:netty-all:4.1.79.Final'
|
||||
implementation 'io.javalin:javalin:5.5.0'
|
||||
implementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.20.0'
|
||||
implementation 'com.formdev:flatlaf:3.1.1'
|
||||
implementation 'com.formdev:flatlaf-extras:3.1.1'
|
||||
implementation 'com.formdev:flatlaf-intellij-themes:3.1.1'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
BIN
images/icon.png
Normal file
BIN
images/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
@@ -43,8 +43,11 @@ public class Entralinked {
|
||||
private final GameSpyServer gameSpyServer;
|
||||
private final HttpServer httpServer;
|
||||
private MainView mainView;
|
||||
private boolean initialized;
|
||||
|
||||
public Entralinked(String[] args) {
|
||||
long beginTime = System.currentTimeMillis();
|
||||
|
||||
// Read command line arguments
|
||||
CommandLineArguments arguments = new CommandLineArguments(args);
|
||||
|
||||
@@ -92,16 +95,11 @@ public class Entralinked {
|
||||
userManager = new UserManager();
|
||||
playerManager = new PlayerManager();
|
||||
|
||||
// Start servers
|
||||
boolean started = true;
|
||||
|
||||
// Create DNS server
|
||||
dnsServer = new DnsServer(hostAddress);
|
||||
started &= dnsServer.start();
|
||||
|
||||
// Create GameSpy server
|
||||
gameSpyServer = new GameSpyServer(this);
|
||||
started &= gameSpyServer.start();
|
||||
|
||||
// Create HTTP server
|
||||
httpServer = new HttpServer(this);
|
||||
@@ -109,25 +107,43 @@ public class Entralinked {
|
||||
httpServer.addHandler(new PglHandler(this));
|
||||
httpServer.addHandler(new DlsHandler(this));
|
||||
httpServer.addHandler(new DashboardHandler(this));
|
||||
started &= httpServer.start();
|
||||
|
||||
// Handle post-startup GUI stuff
|
||||
if(mainView != null) {
|
||||
if(!started) {
|
||||
SwingUtilities.invokeLater(() -> mainView.setStatusLabelText(
|
||||
"ERROR: One or more servers failed to start! Please check the logs for info."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Start servers
|
||||
boolean started = startServers();
|
||||
|
||||
// Post-startup
|
||||
if(started) {
|
||||
logger.info("Startup complete! Took a total of {} milliseconds", System.currentTimeMillis() - beginTime);
|
||||
String hostIpAddress = hostAddress.getHostAddress();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
mainView.setDashboardButtonEnabled(true);
|
||||
mainView.setStatusLabelText("Configure your DS to use the following DNS server: %s".formatted(hostIpAddress));
|
||||
});
|
||||
|
||||
if(mainView == null) {
|
||||
logger.info("Configure your DS to use the following DNS server: {}", hostIpAddress);
|
||||
} else {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
mainView.setDashboardButtonEnabled(true);
|
||||
mainView.setStatusLabelText("Configure your DS to use the following DNS server: %s".formatted(hostIpAddress));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
stopServers();
|
||||
|
||||
if(mainView != null) {
|
||||
SwingUtilities.invokeLater(() -> mainView.setStatusLabelText(
|
||||
"ERROR: Entralinked failed to start. Please check the logs for info."));
|
||||
}
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public boolean startServers() {
|
||||
logger.info("Starting servers ...");
|
||||
return httpServer.start() && gameSpyServer.start() && dnsServer.start();
|
||||
}
|
||||
|
||||
public void stopServers() {
|
||||
logger.info("Stopping servers ...");
|
||||
|
||||
if(httpServer != null) {
|
||||
httpServer.stop();
|
||||
}
|
||||
@@ -179,4 +195,8 @@ public class Entralinked {
|
||||
public PlayerManager getPlayerManager() {
|
||||
return playerManager;
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package entralinked.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
@@ -9,16 +10,19 @@ import java.awt.event.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
@@ -27,30 +31,31 @@ import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
import javax.swing.text.StyleContext;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.formdev.flatlaf.intellijthemes.FlatOneDarkIJTheme;
|
||||
|
||||
import entralinked.Entralinked;
|
||||
import entralinked.utility.ConsumerAppender;
|
||||
import entralinked.utility.SwingUtility;
|
||||
|
||||
/**
|
||||
* Simple Swing user interface.
|
||||
*/
|
||||
public class MainView {
|
||||
|
||||
private static Logger logger = LogManager.getLogger();
|
||||
public static final Color TEXT_COLOR = Color.WHITE.darker();
|
||||
public static final Color TEXT_COLOR_WARN = Color.YELLOW.darker();
|
||||
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 JButton dashboardButton;
|
||||
private final JLabel statusLabel;
|
||||
|
||||
public MainView(Entralinked entralinked) {
|
||||
// Try set Look and Feel
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (ReflectiveOperationException | UnsupportedLookAndFeelException e) {
|
||||
logger.error("Could not set Look and Feel", e);
|
||||
}
|
||||
// Set look and feel
|
||||
FlatOneDarkIJTheme.setup();
|
||||
UIManager.getDefaults().put("Component.focusedBorderColor", UIManager.get("Component.borderColor"));
|
||||
|
||||
// Create dashboard button
|
||||
dashboardButton = new JButton("Open User Dashboard");
|
||||
@@ -87,8 +92,12 @@ public class MainView {
|
||||
// Create console output appender
|
||||
ConsumerAppender.addConsumer("GuiOutput", message -> {
|
||||
Document document = consoleOutputPane.getDocument();
|
||||
Level level = message.level();
|
||||
Color color = level == Level.ERROR ? TEXT_COLOR_ERROR : level == Level.WARN ? TEXT_COLOR_WARN : TEXT_COLOR;
|
||||
AttributeSet colorAttribute = styleContext.addAttribute(fontAttribute, StyleConstants.Foreground, color);
|
||||
|
||||
try {
|
||||
consoleOutputPane.getDocument().insertString(document.getLength(), message, fontAttribute);
|
||||
consoleOutputPane.getDocument().insertString(document.getLength(), message.formattedMessage(), colorAttribute);
|
||||
} catch(BadLocationException e) {}
|
||||
});
|
||||
|
||||
@@ -104,6 +113,15 @@ public class MainView {
|
||||
|
||||
// Create window
|
||||
JFrame frame = new JFrame("Entralinked");
|
||||
|
||||
// Create menu bar
|
||||
JMenuBar menuBar = new JMenuBar();
|
||||
JMenu helpMenu = new JMenu("Help");
|
||||
helpMenu.add(SwingUtility.createAction("Update PID (Error 60000)", () -> new PidToolDialog(entralinked, frame)));
|
||||
helpMenu.add(SwingUtility.createAction("GitHub", () -> openUrl("https://github.com/kuroppoi/entralinked")));
|
||||
menuBar.add(helpMenu);
|
||||
|
||||
// Set window properties
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent event) {
|
||||
@@ -119,8 +137,13 @@ public class MainView {
|
||||
});
|
||||
}
|
||||
});
|
||||
frame.setIconImages(List.of(
|
||||
new ImageIcon(getClass().getResource("/icon-64x.png")).getImage(),
|
||||
new ImageIcon(getClass().getResource("/icon-32x.png")).getImage(),
|
||||
new ImageIcon(getClass().getResource("/icon-16x.png")).getImage()));
|
||||
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
frame.setMinimumSize(new Dimension(512, 288));
|
||||
frame.setJMenuBar(menuBar);
|
||||
frame.add(panel);
|
||||
frame.pack();
|
||||
frame.setLocationRelativeTo(null);
|
||||
@@ -131,7 +154,6 @@ public class MainView {
|
||||
dashboardButton.setEnabled(enabled);
|
||||
}
|
||||
|
||||
|
||||
public void setStatusLabelText(String text) {
|
||||
statusLabel.setText(text);
|
||||
}
|
||||
|
||||
106
src/main/java/entralinked/gui/PidToolDialog.java
Normal file
106
src/main/java/entralinked/gui/PidToolDialog.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package entralinked.gui;
|
||||
|
||||
import java.awt.GridBagLayout;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.formdev.flatlaf.extras.components.FlatTextField;
|
||||
|
||||
import entralinked.Entralinked;
|
||||
import entralinked.model.user.User;
|
||||
import entralinked.model.user.UserManager;
|
||||
import entralinked.utility.SwingUtility;
|
||||
|
||||
public class PidToolDialog {
|
||||
|
||||
public static final Pattern WFC_ID_PATTERN = Pattern.compile("[0-9]{16}");
|
||||
public static final Pattern FRIEND_CODE_PATTERN = Pattern.compile("[0-9]{12}");
|
||||
|
||||
public PidToolDialog(Entralinked entralinked, JFrame frame) {
|
||||
// Create dialog
|
||||
JDialog dialog = new JDialog(frame, "PID Tool");
|
||||
|
||||
// Create input fields
|
||||
FlatTextField wfcIdField = new FlatTextField();
|
||||
wfcIdField.setPlaceholderText("XXXX-XXXX-XXXX-XXXX");
|
||||
FlatTextField friendCodeField = new FlatTextField();
|
||||
friendCodeField.setPlaceholderText("XXXX-XXXX-XXXX");
|
||||
|
||||
// Create logic
|
||||
JButton updateButton = new JButton("Update");
|
||||
updateButton.addActionListener(event -> {
|
||||
if(!entralinked.isInitialized()) {
|
||||
JOptionPane.showMessageDialog(dialog, "Please wait for Entralinked to finish starting.", "Attention", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
String userId = wfcIdField.getText().replace("-", "");
|
||||
String friendCode = friendCodeField.getText().replace("-", "");
|
||||
|
||||
// Make sure WFC ID is valid
|
||||
if(!WFC_ID_PATTERN.matcher(userId).matches()) {
|
||||
JOptionPane.showMessageDialog(dialog, "Please enter a valid Wi-Fi Connection ID.", "Attention", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure Friend Code is valid
|
||||
if(!FRIEND_CODE_PATTERN.matcher(friendCode).matches()) {
|
||||
JOptionPane.showMessageDialog(dialog, "Please enter a valid Friend Code.", "Attention", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
UserManager userManager = entralinked.getUserManager();
|
||||
User user = userManager.getUser(userId.substring(0, 13));
|
||||
int profileId = (int)(Long.parseLong(friendCode) & 0x7FFFFFFF);
|
||||
|
||||
// Make sure user exists
|
||||
if(user == null) {
|
||||
JOptionPane.showMessageDialog(dialog, "This Wi-Fi Connection ID does not exist.", "Attention", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show warning if this user has multiple profiles
|
||||
if(user.getProfiles().size() > 1) {
|
||||
if(JOptionPane.showConfirmDialog(dialog, "Multiple profiles detected. Do you want to update all of them?\n"
|
||||
+ "This may cause error 60000 to occur on your other game cartridges.",
|
||||
"Attention", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
userManager.updateProfileIdForUser(user, profileId);
|
||||
JOptionPane.showMessageDialog(dialog, "Profile has been updated. Please restart your game and use Game Sync.");
|
||||
});
|
||||
|
||||
// Create content panel
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
String infoLabel = """
|
||||
<html>
|
||||
Enter the Wi-Fi Connection ID found in the internet settings of your DS<br>
|
||||
as well as your Friend Code which you can view in-game using the Pal Pad.<br>
|
||||
Confirm that the input data is correct and press 'Update'<br>
|
||||
</html>
|
||||
""";
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
panel.add(new JLabel(infoLabel), SwingUtility.createConstraints(0, 0, 2, 1));
|
||||
panel.add(new JLabel("Wi-Fi Connection ID"), SwingUtility.createConstraints(0, 1, 1, 1, 0, 1));
|
||||
panel.add(wfcIdField, SwingUtility.createConstraints(1, 1));
|
||||
panel.add(new JLabel("Friend Code"), SwingUtility.createConstraints(0, 2, 1, 1, 0, 1));
|
||||
panel.add(friendCodeField, SwingUtility.createConstraints(1, 2));
|
||||
panel.add(updateButton, SwingUtility.createConstraints(0, 3, 2, 1));
|
||||
|
||||
// Set dialog properties
|
||||
dialog.setResizable(false);
|
||||
dialog.add(panel);
|
||||
dialog.pack();
|
||||
dialog.setLocationRelativeTo(frame);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package entralinked.model.user;
|
||||
|
||||
public class GameProfile {
|
||||
|
||||
private final int id;
|
||||
private int id;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String aimName;
|
||||
@@ -20,6 +20,10 @@ public class GameProfile {
|
||||
this.zipCode = zipCode;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public class UserManager {
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private final ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
|
||||
private final Map<String, User> users = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, GameProfile> profiles = new ConcurrentHashMap<>();
|
||||
private final Map<String, ServiceSession> serviceSessions = new ConcurrentHashMap<>();
|
||||
private final File dataDirectory = new File("users");
|
||||
|
||||
@@ -49,7 +48,7 @@ public class UserManager {
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Loaded {} user(s) with a total of {} profile(s)", users.size(), profiles.size());
|
||||
logger.info("Loaded {} user(s)", users.size());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,20 +74,8 @@ public class UserManager {
|
||||
throw new IOException("Duplicate user ID %s".formatted(id));
|
||||
}
|
||||
|
||||
// Check for duplicate profile IDs before indexing anything
|
||||
Collection<GameProfile> userProfiles = user.getProfiles();
|
||||
|
||||
if(userProfiles.stream().map(GameProfile::getId).anyMatch(profiles::containsKey)) {
|
||||
throw new IOException("Duplicate profile ID in user %s".formatted(id));
|
||||
}
|
||||
|
||||
// Index user
|
||||
users.put(id, user);
|
||||
|
||||
// Index profiles
|
||||
for(GameProfile profile : userProfiles) {
|
||||
profiles.put(profile.getId(), profile);
|
||||
}
|
||||
} catch(IOException e) {
|
||||
logger.error("Could not load user data at {}", inputFile.getAbsolutePath(), e);
|
||||
}
|
||||
@@ -233,7 +220,7 @@ public class UserManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
int profileId = nextProfileId();
|
||||
int profileId = (int)(Math.random() * Integer.MAX_VALUE);
|
||||
GameProfile profile = new GameProfile(profileId);
|
||||
user.addProfile(branchCode, profile);
|
||||
|
||||
@@ -243,22 +230,27 @@ public class UserManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
profiles.put(profileId, profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A unique random 32-bit profile ID.
|
||||
* This will forcibly set the profile id of all profiles of this user to the specified one.
|
||||
* Potentially a destructive operation; use with caution.
|
||||
*
|
||||
* @return {@code true} if the operation was successful, otherwise {@code false}.
|
||||
*/
|
||||
private int nextProfileId() {
|
||||
int profileId = (int)(Math.random() * Integer.MAX_VALUE);
|
||||
|
||||
// I live for that microscopic chance of StackOverflowError
|
||||
if(profiles.containsKey(profileId)) {
|
||||
return nextProfileId();
|
||||
public boolean updateProfileIdForUser(User user, int profileId) {
|
||||
// Set the id of all profiles
|
||||
for(GameProfile profile : user.getProfiles()) {
|
||||
profile.setId(profileId);
|
||||
}
|
||||
|
||||
return profileId;
|
||||
// Try to save user
|
||||
if(!saveUser(user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,7 +40,6 @@ public abstract class NettyServerBase {
|
||||
|
||||
public boolean start() {
|
||||
if(started) {
|
||||
logger.warn("start() was called while {} server was already running!", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -59,7 +58,6 @@ public abstract class NettyServerBase {
|
||||
|
||||
public boolean stop() {
|
||||
if(!started) {
|
||||
logger.warn("stop() was called while {} server wasn't running!", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ package entralinked.network.dns;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import entralinked.network.NettyServerBase;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
@@ -17,13 +14,11 @@ import io.netty.handler.codec.dns.DatagramDnsResponseEncoder;
|
||||
|
||||
public class DnsServer extends NettyServerBase {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private InetAddress hostAddress;
|
||||
|
||||
public DnsServer(InetAddress hostAddress) {
|
||||
super("DNS", 53);
|
||||
this.hostAddress = hostAddress;
|
||||
logger.info("DNS queries will be resolved to {}", hostAddress.getHostAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -75,7 +75,6 @@ public class HttpServer {
|
||||
|
||||
public boolean start() {
|
||||
if(started) {
|
||||
logger.warn("start() was called while HTTP server was already running!");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,7 +93,6 @@ public class HttpServer {
|
||||
|
||||
public boolean stop() {
|
||||
if(!started) {
|
||||
logger.warn("stop() was called while HTTP server wasn't running!");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ public class CertificateGenerator {
|
||||
+ "Fw2ewOxmIyw3d4HFGrLx9yvk6Q5HpTlSgvbgO22k/vo0N43dO7utgfn/e4Xo9alIAktnewGqj5nRm75H7iaLEJBo983jJdugeXdp6y5X6sDmaDmyw/p91TKPrKmD"
|
||||
+ "4ebbTY38D5WT67fONjW/X1OLNHg46GYYZa6etbxQ7671f7GypgwnI=";
|
||||
|
||||
|
||||
private static final String issuerPrivateKeyString =
|
||||
"eNod0Em2a0AAANAFGRBNYVgK0QalfWaIPproZfX/nH+XcG1dR4mnSxCaUu3JKUe5WLR1rND+Q+gwb3NO3ws5e0YXcydZcUtNV/35votzw9SsDstssI0TPxg5q1XPUq"
|
||||
+ "EpGgfib4UnATQKiAcZHsBOsEWg2nShyhd7CoLQznBPWW/+iLfW3bMujf723htqG+n0p30h/SClZIRZibH7I5hGgQHRqgvpuJ/IDWpH9HQZelCC0xPK8uXZ87jcwv"
|
||||
|
||||
@@ -29,9 +29,9 @@ public class ColorUtility {
|
||||
*/
|
||||
public static int convertBGR555ToRGB888(int color) {
|
||||
int red = (color & 0x1F) << 3;
|
||||
int green = ((color & 0x3E0) >> 5) << 3;
|
||||
int blue = ((color & 0x7C00) >> 10) << 3;
|
||||
return (red << 16) | (green << 8) | blue;
|
||||
int green = (color >> 5 & 0x1F) << 3;
|
||||
int blue = (color >> 10 & 0x1F) << 3;
|
||||
return ((red | red >> 5) << 16) | ((green | green >> 5) << 8) | (blue | blue >> 5);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.Core;
|
||||
import org.apache.logging.log4j.core.Filter;
|
||||
@@ -25,7 +26,8 @@ import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
printObject = true)
|
||||
public class ConsumerAppender extends AbstractAppender {
|
||||
|
||||
protected static final Map<String, List<Consumer<String>>> consumerMap = new ConcurrentHashMap<>();
|
||||
public record LogMessage(Level level, String rawMessage, String formattedMessage) {}
|
||||
protected static final Map<String, List<Consumer<LogMessage>>> consumerMap = new ConcurrentHashMap<>();
|
||||
|
||||
protected ConsumerAppender(String name, Filter filter, Layout<? extends Serializable> layout,
|
||||
boolean ignoreExceptions, Property[] properties) {
|
||||
@@ -41,8 +43,8 @@ public class ConsumerAppender extends AbstractAppender {
|
||||
return new ConsumerAppender(name, filter, layout, ignoreExceptions, null);
|
||||
}
|
||||
|
||||
public static void addConsumer(String appenderName, Consumer<String> consumer) {
|
||||
List<Consumer<String>> consumers = consumerMap.getOrDefault(appenderName, new ArrayList<>());
|
||||
public static void addConsumer(String appenderName, Consumer<LogMessage> consumer) {
|
||||
List<Consumer<LogMessage>> consumers = consumerMap.getOrDefault(appenderName, new ArrayList<>());
|
||||
consumers.add(consumer);
|
||||
consumerMap.putIfAbsent(appenderName, consumers);
|
||||
}
|
||||
@@ -50,11 +52,12 @@ public class ConsumerAppender extends AbstractAppender {
|
||||
@Override
|
||||
public void append(LogEvent event) {
|
||||
String formattedMessage = getLayout().toSerializable(event).toString();
|
||||
List<Consumer<String>> consumers = consumerMap.get(getName());
|
||||
List<Consumer<LogMessage>> consumers = consumerMap.get(getName());
|
||||
LogMessage logMessage = new LogMessage(event.getLevel(), event.getMessage().getFormattedMessage(), formattedMessage);
|
||||
|
||||
if(consumers != null) {
|
||||
for(Consumer<String> consumer : consumers) {
|
||||
consumer.accept(formattedMessage);
|
||||
for(Consumer<LogMessage> consumer : consumers) {
|
||||
consumer.accept(logMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
src/main/java/entralinked/utility/SwingUtility.java
Normal file
58
src/main/java/entralinked/utility/SwingUtility.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package entralinked.utility;
|
||||
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.Icon;
|
||||
|
||||
public class SwingUtility {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static Action createAction(String name, Icon icon, Runnable handler) {
|
||||
AbstractAction action = new AbstractAction(name, icon) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
handler.run();
|
||||
}
|
||||
};
|
||||
|
||||
if(icon != null) {
|
||||
action.putValue(Action.SHORT_DESCRIPTION, name);
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
public static Action createAction(String name, Runnable handler) {
|
||||
return createAction(name, null, handler);
|
||||
}
|
||||
|
||||
public static GridBagConstraints createConstraints(int x, int y) {
|
||||
return createConstraints(x, y, 1, 1);
|
||||
}
|
||||
|
||||
public static GridBagConstraints createConstraints(int x, int y, int width, int height) {
|
||||
return createConstraints(x, y, width, height, 1, 1);
|
||||
}
|
||||
|
||||
public static GridBagConstraints createConstraints(int x, int y, int width, int height, double weightX, double weightY) {
|
||||
return createConstraints(x, y, width, height, weightX, weightY, 8, 8);
|
||||
}
|
||||
|
||||
public static GridBagConstraints createConstraints(int x, int y, int width, int height, double weightX, double weightY,
|
||||
int paddingX, int paddingY) {
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
constraints.fill = GridBagConstraints.BOTH;
|
||||
constraints.gridx = x;
|
||||
constraints.gridy = y;
|
||||
constraints.gridwidth = width;
|
||||
constraints.gridheight = height;
|
||||
constraints.weightx = weightX;
|
||||
constraints.weighty = weightY;
|
||||
constraints.ipadx = paddingX;
|
||||
constraints.ipady = paddingY;
|
||||
return constraints;
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,7 @@
|
||||
<option value="WALK_LOOK_AROUND">Walk and Look Around</option>
|
||||
<option value="WALK_VERTICALLY">Walk Vertically</option>
|
||||
<option value="WALK_HORIZONTALLY">Walk Horizontally</option>
|
||||
<option value="WALK_HORIZONTALLY_LOOK_AROUND">Walk Horizontally and Look Around</option>
|
||||
<option value="WALK_LOOK_HORIZONTALLY">Walk Horizontally and Look Around</option>
|
||||
<option value="SPIN_RIGHT">Spin Right</option>
|
||||
<option value="SPIN_LEFT">Spin Left</option>
|
||||
</select>
|
||||
|
||||
@@ -509,7 +509,7 @@ const REGION_LIST = [];
|
||||
{id: 493, name: "Arceus", downloadable: true, gender: "unknown", forms: [
|
||||
"Normal", "Fighting", "Flying", "Poison", "Ground", "Rock", "Bug", "Ghost",
|
||||
"Steel", "Fire", "Water", "Grass", "Electric", "Psychic", "Ice", "Dragon", "Dark"]},
|
||||
{id: 494, name: "Victini", downloadable: true, gender: "unknown"},
|
||||
{id: 494, name: "Victini", downloadable: false, gender: "unknown"},
|
||||
{id: 495, name: "Snivy", downloadable: false},
|
||||
{id: 496, name: "Servine", downloadable: false},
|
||||
{id: 497, name: "Serperior", downloadable: false},
|
||||
|
||||
BIN
src/main/resources/icon-16x.png
Normal file
BIN
src/main/resources/icon-16x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 720 B |
BIN
src/main/resources/icon-32x.png
Normal file
BIN
src/main/resources/icon-32x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
src/main/resources/icon-64x.png
Normal file
BIN
src/main/resources/icon-64x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,93 @@
|
||||
package entralinked.serialization;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
public class GameSpyMessageFactoryTest {
|
||||
|
||||
protected ObjectMapper mapper;
|
||||
|
||||
@BeforeAll
|
||||
void before() {
|
||||
mapper = new ObjectMapper(new GameSpyMessageFactory());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if generator writes objects correctly")
|
||||
void testGeneratorWriteObject() throws IOException {
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("key", "value");
|
||||
data.put("emptyValue", "");
|
||||
data.put("hello", "world");
|
||||
data.put("numberTest", 123);
|
||||
assertEquals("\\key\\value\\emptyValue\\\\hello\\world\\numberTest\\123", mapper.writeValueAsString(data));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if generator throws exception when writing a nested object")
|
||||
void testGeneratorThrowsExceptionWhenWritingNestedObject() {
|
||||
Map<String, Object> data = Map.of("someKey", "someValue", "nestedObjectKey", Map.of("key", "value"));
|
||||
JsonMappingException exception = assertThrows(JsonMappingException.class, () -> mapper.writeValueAsString(data));
|
||||
assertEquals("this format does not support nested objects", exception.getOriginalMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if generator throws exception when writing an array")
|
||||
void testGeneratorThrowsExceptionWhenWritingArray() {
|
||||
Map<String, Object> data = Map.of("arrayKey", List.of(1, 2, 3, 4));
|
||||
JsonMappingException exception = assertThrows(JsonMappingException.class, () -> mapper.writeValueAsString(data));
|
||||
assertEquals("this format does not support arrays", exception.getOriginalMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if parser creates object from input string correctly")
|
||||
void testParserReadObject() throws IOException {
|
||||
String inputString = "\\key\\value\\emptyValue\\\\hello\\world\\numberTest\\123";
|
||||
Map<String, Object> data = mapper.readValue(inputString, new TypeReference<Map<String, Object>>(){});
|
||||
assertEquals("value", data.get("key"));
|
||||
assertEquals("", data.get("emptyValue"));
|
||||
assertEquals("world", data.get("hello"));
|
||||
assertEquals("123", data.get("numberTest"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if parser throws exception when reading an empty key")
|
||||
void testParserThrowsExceptionWhenReadingEmptyKey() {
|
||||
String inputString = "\\some\\value\\\\emptyKey";
|
||||
JsonParseException exception = assertThrows(JsonParseException.class, () -> mapper.readValue(inputString, Map.class));
|
||||
assertEquals("Unexpected character ('\\' (code 92)): expected field name", exception.getOriginalMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if parser throws exception when input doesn't start with backslash")
|
||||
void testParserThrowsExceptionWhenFirstCharacterNotBackslash() {
|
||||
String inputString = "key\\value";
|
||||
JsonParseException exception = assertThrows(JsonParseException.class, () -> mapper.readValue(inputString, Map.class));
|
||||
assertEquals("Unexpected character ('k' (code 107)): expected '\\' to open field name", exception.getOriginalMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if parser throws exception when field name isn't closed")
|
||||
void testParserThrowsExceptionWhenFieldNameNotClosed() {
|
||||
String inputString = "\\hello\\world\\key";
|
||||
JsonParseException exception = assertThrows(JsonParseException.class, () -> mapper.readValue(inputString, Map.class));
|
||||
assertEquals("Unexpected end-of-input in FIELD_NAME", exception.getOriginalMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package entralinked.serialization;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
public class UrlEncodedFormFactoryTest {
|
||||
|
||||
protected ObjectMapper mapper;
|
||||
protected ObjectMapper mapperNoBase64;
|
||||
|
||||
@BeforeAll
|
||||
void before() {
|
||||
mapper = new ObjectMapper(new UrlEncodedFormFactory());
|
||||
mapperNoBase64 = new ObjectMapper(new UrlEncodedFormFactory()
|
||||
.disable(UrlEncodedFormParser.Feature.BASE64_DECODE_VALUES)
|
||||
.disable(UrlEncodedFormGenerator.Feature.BASE64_ENCODE_VALUES));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if generator writes objects correctly")
|
||||
void testGeneratorWriteObject() throws IOException {
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("hello", "world");
|
||||
data.put("test", "space test");
|
||||
data.put("emptyValue", "");
|
||||
data.put("someNumber", 1234567890);
|
||||
assertEquals("hello=d29ybGQ*&test=c3BhY2UgdGVzdA**&emptyValue=&someNumber=MTIzNDU2Nzg5MA**", mapper.writeValueAsString(data));
|
||||
assertEquals("hello=world&test=space+test&emptyValue=&someNumber=1234567890", mapperNoBase64.writeValueAsString(data));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if generator throws exception when writing a nested object")
|
||||
void testGeneratorThrowsExceptionWhenWritingNestedObject() {
|
||||
Map<String, Object> data = Map.of("someKey", "someValue", "nestedObjectKey", Map.of("key", "value"));
|
||||
JsonMappingException exception = assertThrows(JsonMappingException.class, () -> mapper.writeValueAsString(data));
|
||||
assertEquals("this format does not support nested objects", exception.getOriginalMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if generator throws exception when writing an array")
|
||||
void testGeneratorThrowsExceptionWhenWritingArray() {
|
||||
Map<String, Object> data = Map.of("arrayKey", List.of(1, 2, 3, 4));
|
||||
JsonMappingException exception = assertThrows(JsonMappingException.class, () -> mapper.writeValueAsString(data));
|
||||
assertEquals("this format does not support arrays", exception.getOriginalMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if parser creates object from input string correctly")
|
||||
void testParserReadObject() throws IOException {
|
||||
String inputString = "hello=world&test=space+test&emptyValue=&someNumber=1234567890";
|
||||
Map<String, Object> data = mapperNoBase64.readValue(inputString, new TypeReference<Map<String, Object>>(){});
|
||||
assertEquals("world", data.get("hello"));
|
||||
assertEquals("space test", data.get("test"));
|
||||
assertEquals("", data.get("emptyValue"));
|
||||
assertEquals("1234567890", data.get("someNumber"));
|
||||
|
||||
// Test Base64
|
||||
inputString = "hello=d29ybGQ*&test=c3BhY2UgdGVzdA**&emptyValue=&someNumber=MTIzNDU2Nzg5MA**";
|
||||
assertEquals(data, mapper.readValue(inputString, Map.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if parser throws exception when reading an empty key")
|
||||
void testParserThrowsExceptionWhenReadingEmptyKey() {
|
||||
String inputString = "someKey=someValue&=emptyKey";
|
||||
JsonParseException exception = assertThrows(JsonParseException.class, () -> mapperNoBase64.readValue(inputString, Map.class));
|
||||
assertEquals("Unexpected character ('=' (code 61)): expected field name", exception.getOriginalMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if parser throws exception when field name isn't closed")
|
||||
void testParserThrowsExceptionWhenFieldNameNotClosed() {
|
||||
String inputString = "someKey=someValue¬Closed";
|
||||
JsonParseException exception = assertThrows(JsonParseException.class, () -> mapperNoBase64.readValue(inputString, Map.class));
|
||||
assertEquals("Unexpected end-of-input in FIELD_NAME", exception.getOriginalMessage());
|
||||
}
|
||||
}
|
||||
54
src/test/java/entralinked/utility/ColorUtilityTest.java
Normal file
54
src/test/java/entralinked/utility/ColorUtilityTest.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package entralinked.utility;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ColorUtilityTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if multiplied colors are correct")
|
||||
void testColorMultiplying() {
|
||||
assertEquals(0x7F7F7F, ColorUtility.multiplyColor(0xFFFFFF, 0.5));
|
||||
assertEquals(0xFEFEFE, ColorUtility.multiplyColor(0x7F7F7F, 2.0));
|
||||
|
||||
// Test clamping
|
||||
assertEquals(0xFFFFFF, ColorUtility.multiplyColor(0xFEFEFE, 2.0));
|
||||
assertEquals(0x000000, ColorUtility.multiplyColor(0x010101, 0.5));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if colors converted from RGB888 to BGR555 are correct")
|
||||
void testRGB88ToBGR555Converter() {
|
||||
// Black, white, red, green and blue
|
||||
assertEquals(0x0000, ColorUtility.convertRGB888ToBGR555(0x000000));
|
||||
assertEquals(0x7FFF, ColorUtility.convertRGB888ToBGR555(0xFFFFFF));
|
||||
assertEquals(0x001F, ColorUtility.convertRGB888ToBGR555(0xFF0000));
|
||||
assertEquals(0x03E0, ColorUtility.convertRGB888ToBGR555(0x00FF00));
|
||||
assertEquals(0x7C00, ColorUtility.convertRGB888ToBGR555(0x0000FF));
|
||||
|
||||
// Random colors
|
||||
assertEquals(0x07C7, ColorUtility.convertRGB888ToBGR555(0x39F20C));
|
||||
assertEquals(0x68B3, ColorUtility.convertRGB888ToBGR555(0x9E2BD3));
|
||||
assertEquals(0x6DFE, ColorUtility.convertRGB888ToBGR555(0xF07CDE));
|
||||
assertEquals(0x22A4, ColorUtility.convertRGB888ToBGR555(0x26AC44));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if colors converted from BGR555 to RGB888 are correct")
|
||||
void testBGR555ToRGB888Converter() {
|
||||
// Black, white, red, green and blue
|
||||
assertEquals(0x000000, ColorUtility.convertBGR555ToRGB888(0x0000));
|
||||
assertEquals(0xFFFFFF, ColorUtility.convertBGR555ToRGB888(0xFFFF));
|
||||
assertEquals(0xFF0000, ColorUtility.convertBGR555ToRGB888(0x001F));
|
||||
assertEquals(0x00FF00, ColorUtility.convertBGR555ToRGB888(0x03E0));
|
||||
assertEquals(0x0000FF, ColorUtility.convertBGR555ToRGB888(0x7C00));
|
||||
|
||||
// Random colors
|
||||
assertEquals(0x7BA59C, ColorUtility.convertBGR555ToRGB888(0x4E8F));
|
||||
assertEquals(0x948CFF, ColorUtility.convertBGR555ToRGB888(0xFE32));
|
||||
assertEquals(0xBD2121, ColorUtility.convertBGR555ToRGB888(0x1097));
|
||||
assertEquals(0x5A185A, ColorUtility.convertBGR555ToRGB888(0xAC6B));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package entralinked.utility;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class LittleEndianStreamTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test if LEOutputStream and LEInputStream produce correct values")
|
||||
void testLittleEndianStreams() throws IOException {
|
||||
short shortValue = 0x6A84;
|
||||
int intValue = 0xF827EC80;
|
||||
long longValue = 0x948EC1AB3F2C88L;
|
||||
|
||||
// Test writing
|
||||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||
LEOutputStream outputStream = new LEOutputStream(byteOutputStream);
|
||||
outputStream.writeShort(shortValue);
|
||||
outputStream.writeInt(intValue);
|
||||
outputStream.writeLong(longValue);
|
||||
|
||||
// Test reading
|
||||
LEInputStream inputStream = new LEInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()));
|
||||
assertEquals(shortValue, inputStream.readShort());
|
||||
assertEquals(intValue, inputStream.readInt());
|
||||
assertEquals(longValue, inputStream.readLong());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user