Rely on DLC game code & type instead of names not being duplicate

This commit is contained in:
kuroppoi
2023-07-08 02:09:05 +02:00
parent 0e12cb54d7
commit 329e1de30a
6 changed files with 77 additions and 56 deletions

View File

@@ -6,11 +6,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -22,7 +21,7 @@ import entralinked.utility.Crc16;
public class DlcList {
private static final Logger logger = LogManager.getLogger();
private final Map<String, Dlc> dlcMap = new ConcurrentHashMap<>();
private final List<Dlc> dlcList = new ArrayList<>();
private final File dataDirectory = new File("dlc");
public DlcList() {
@@ -83,26 +82,20 @@ public class DlcList {
// Load DLC data
Dlc dlc = loadDlcFile(file.getName(), subFile.getName(), index, dlcFile);
// Index DLC object if loading succeeded
if(dlc != null) {
dlcMap.put(dlc.name(), dlc);
dlcList.add(dlc);
index++;
}
}
}
}
logger.info("Loaded {} DLC file(s)", dlcMap.size());
logger.info("Loaded {} DLC file(s)", dlcList.size());
}
private Dlc loadDlcFile(String gameCode, String type, int index, File dlcFile) {
String name = dlcFile.getName();
if(dlcMap.containsKey(name)) {
logger.warn("Duplicate DLC name {}", name);
return null;
}
if(dlcFile.isDirectory()) {
logger.warn("Directory '{}' in {} DLC folder", name, gameCode);
return null;
@@ -123,6 +116,7 @@ public class DlcList {
int checksumInFile = (bytes[bytes.length - 2] & 0xFF) | ((bytes[bytes.length - 1] & 0xFF) << 8);
if(checksum != checksumInFile) {
logger.warn("Checksum mismatch in DLC '{}'", name);
projectedSize += 2;
checksum = Crc16.calc(bytes, 0, bytes.length);
checksumEmbedded = false;
@@ -167,19 +161,18 @@ public class DlcList {
return builder.toString();
}
public Dlc getDlc(String name) {
return dlcMap.get(name);
public Dlc getDlc(String gameCode, String type, String name) {
List<Dlc> dlcList = getDlcList(gameCode, type).stream()
.filter(dlc -> dlc.name().equals(name)).collect(Collectors.toList());
return dlcList.isEmpty() ? null : dlcList.get(0);
}
public int getDlcIndex(String name) {
return dlcExists(name) ? getDlc(name).index() : 0;
}
public boolean dlcExists(String name) {
return name != null && dlcMap.containsKey(name);
public int getDlcIndex(String gameCode, String type, String name) {
Dlc dlc = getDlc(gameCode, type, name);
return dlc == null ? 0 : dlc.index();
}
public Collection<Dlc> getDlc() {
return Collections.unmodifiableCollection(dlcMap.values());
return Collections.unmodifiableCollection(dlcList);
}
}

View File

@@ -49,7 +49,7 @@ public class DashboardHandler implements HttpHandler {
505, 507, 510, 511, 513, 515, 519, 523, 525, 527, 529, 531, 533, 535, 538, 539, 542, 545, 546, 548,
550, 553, 556, 558, 559, 561, 564, 569, 572, 575, 578, 580, 583, 587, 588, 594, 596, 600, 605, 607,
610, 613, 616, 618, 619, 621, 622, 624, 626, 628, 630, 631, 632);
private final Map<String, BufferedImage> skinPreviewCache = new HashMap<>();
private final Map<Dlc, BufferedImage> skinPreviewCache = new HashMap<>();
private final DlcList dlcList;
private final PlayerManager playerManager;
@@ -67,7 +67,7 @@ public class DashboardHandler implements HttpHandler {
skin.type().equals("ZUKAN") ? TiledImageReader.readDexSkin(inputStream) :
skin.type().equals("CGEAR") ? TiledImageReader.readCGearSkin(inputStream, true) :
TiledImageReader.readCGearSkin(inputStream, false); // CGEAR2
skinPreviewCache.put(skin.name(), image);
skinPreviewCache.put(skin, image);
} catch(IOException | IndexOutOfBoundsException e) {
logger.error("Could not load image for skin {} of type {}", skin.name(), skin.type(), e);
}
@@ -110,16 +110,25 @@ public class DashboardHandler implements HttpHandler {
* GET request handler for {@code /dashboard/previewskin}
*/
private void handlePreviewSkin(Context ctx) throws IOException {
// Make sure that the name is present and exists
String type = ctx.queryParam("type");
String name = ctx.queryParam("name");
if(name == null || !skinPreviewCache.containsKey(name)) {
// Make sure query parameters are present
if(type == null || name == null) {
ctx.status(404);
return;
}
Dlc dlc = dlcList.getDlc("IRAO", type, name);
// Check if DLC exists
if(dlc == null) {
ctx.status(404);
return;
}
// Write cached image data
ImageIO.write(skinPreviewCache.get(name), "png", ctx.outputStream());
ImageIO.write(skinPreviewCache.get(dlc), "png", ctx.outputStream());
}
/**

View File

@@ -67,19 +67,8 @@ public class DlsHandler implements HttpHandler {
* POST handler for {@code /download action=list}
*/
private void handleRetrieveDlcList(DlsRequest request, Context ctx) throws IOException {
String gameCode = switch(request.dlcGameCode()) {
case "IRAJ" -> "IRAO";
default -> request.dlcGameCode();
};
// Map to generic type, I doubt there is a real difference between the language codes anyway.
String type = switch(request.dlcType()) {
case "CGEAR_E", "CGEAR_F", "CGEAR_I", "CGEAR_G", "CGEAR_S", "CGEAR_J", "CGEAR_K" -> "CGEAR";
case "CGEAR2_E", "CGEAR2_F", "CGEAR2_I", "CGEAR2_G", "CGEAR2_S", "CGEAR2_J", "CGEAR2_K" -> "CGEAR2";
case "ZUKAN_E", "ZUKAN_F", "ZUKAN_I", "ZUKAN_G", "ZUKAN_S", "ZUKAN_J", "ZUKAN_K" -> "ZUKAN";
case "MUSICAL_E", "MUSICAL_F", "MUSICAL_I", "MUSICAL_G", "MUSICAL_S", "MUSICAL_J", "MUSICAL_K" -> "MUSICAL";
default -> request.dlcType();
};
String gameCode = getDlcGameCode(request.dlcGameCode());
String type = getRegionlessDlcType(request.dlcType());
// TODO NOTE: I assume that in a conventional implementation, certain DLC attributes may be omitted from the request.
ctx.result(dlcList.getDlcListString(dlcList.getDlcList(gameCode, type, request.dlcIndex())));
@@ -89,9 +78,11 @@ public class DlsHandler implements HttpHandler {
* POST handler for {@code /download action=contents}
*/
private void handleRetrieveDlcContent(DlsRequest request, Context ctx) throws IOException {
// Check if the requested DLC exists
Dlc dlc = dlcList.getDlc(request.dlcName());
String gameCode = getDlcGameCode(request.dlcGameCode());
String type = getRegionlessDlcType(request.dlcType());
Dlc dlc = dlcList.getDlc(gameCode, type, request.dlcName());
// Check if the requested DLC exists
if(dlc == null) {
ctx.status(HttpStatus.NOT_FOUND);
return;
@@ -108,4 +99,27 @@ public class DlsHandler implements HttpHandler {
}
}
}
/**
* @return The game serial that should be used for downloading DLC based on the provided input.
*/
private String getDlcGameCode(String gameCode) {
return switch(gameCode) {
case "IRAJ" -> "IRAO";
default -> gameCode;
};
}
/**
* @return The DLC type without the region identifier, or the input if it is an unknown type.
*/
private String getRegionlessDlcType(String dlcType) {
return switch(dlcType) {
case "CGEAR_E", "CGEAR_F", "CGEAR_I", "CGEAR_G", "CGEAR_S", "CGEAR_J", "CGEAR_K" -> "CGEAR";
case "CGEAR2_E", "CGEAR2_F", "CGEAR2_I", "CGEAR2_G", "CGEAR2_S", "CGEAR2_J", "CGEAR2_K" -> "CGEAR2";
case "ZUKAN_E", "ZUKAN_F", "ZUKAN_I", "ZUKAN_G", "ZUKAN_S", "ZUKAN_J", "ZUKAN_K" -> "ZUKAN";
case "MUSICAL_E", "MUSICAL_F", "MUSICAL_I", "MUSICAL_G", "MUSICAL_S", "MUSICAL_J", "MUSICAL_K" -> "MUSICAL";
default -> dlcType;
};
}
}

View File

@@ -228,9 +228,9 @@ public class PglHandler implements HttpHandler {
// Write misc stuff and DLC information
outputStream.writeShort(player.getLevelsGained());
outputStream.write(0); // Unknown
outputStream.write(dlcList.getDlcIndex(player.getMusical()));
outputStream.write(dlcList.getDlcIndex(player.getCGearSkin()));
outputStream.write(dlcList.getDlcIndex(player.getDexSkin()));
outputStream.write(dlcList.getDlcIndex("IRAO", "MUSICAL", player.getMusical()));
outputStream.write(dlcList.getDlcIndex("IRAO", player.getGameVersion().isVersion2() ? "CGEAR2" : "CGEAR", player.getCGearSkin()));
outputStream.write(dlcList.getDlcIndex("IRAO", "ZUKAN", player.getDexSkin()));
outputStream.write(decorList.isEmpty() ? 0 : 1); // Seems to be a flag for indicating whether or not decor data is present
outputStream.write(0); // Must be zero?

View File

@@ -124,13 +124,13 @@
<!-- Misc Configurations -->
<div class="grid-container">
<div>
<label>C-Gear Skin | </label><a href="#" onclick="return previewSkin('cgear-skin')">Preview</a>
<label>C-Gear Skin | </label><a href="#" onclick="return previewSkin('cgear-skin', 'CGEAR')">Preview</a>
<select id="cgear-skin">
<option value="none">Do not change</option>
</select>
</div>
<div>
<label>Pokédex Skin | </label><a href="#" onclick="return previewSkin('dex-skin')">Preview</a>
<label>Pokédex Skin | </label><a href="#" onclick="return previewSkin('dex-skin', 'ZUKAN')">Preview</a>
<select id="dex-skin">
<option value="none">Do not change</option>
</select>

View File

@@ -325,7 +325,7 @@ function closeItemForm() {
window.location.href = "#";
}
function previewSkin(inputElementId) {
function previewSkin(inputElementId, type) {
let value = document.getElementById(inputElementId).value;
if(value == "none") {
@@ -333,10 +333,18 @@ function previewSkin(inputElementId) {
return false;
}
window.open("/dashboard/previewskin?name=" + value);
if(type == "CGEAR" && isVersion2()) {
type = "CGEAR2";
}
window.open("/dashboard/previewskin?type=" + type + "&name=" + value);
return false;
}
function isVersion2() {
return profile.gameVersion.includes("2");
}
async function fetchData(path) {
return fetchData(path, "GET", null);
}
@@ -363,24 +371,22 @@ async function fetchData(path, method, body) {
}
function fetchDlcData() {
let cgearType = profile.gameVersion.includes("2") ? "CGEAR2" : "CGEAR"; // Not a good way to do this!
// Fetch CGear skins
fetchData("/dashboard/dlc?type=" + cgearType).then((response) => {
// Fetch C-Gear skins
fetchData("/dashboard/dlc?type=" + (isVersion2() ? "CGEAR2" : "CGEAR")).then((response) => {
addValuesToComboBox(ELEMENT_CGEAR_SKIN_INPUT, response);
ELEMENT_CGEAR_SKIN_INPUT.value = profile.cgearSkin;
ELEMENT_CGEAR_SKIN_INPUT.value = response.includes(profile.cgearSkin) ? profile.cgearSkin : "none";
});
// Fetch Dex skins
fetchData("/dashboard/dlc?type=ZUKAN").then((response) => {
addValuesToComboBox(ELEMENT_DEX_SKIN_INPUT, response);
ELEMENT_DEX_SKIN_INPUT.value = profile.dexSkin;
ELEMENT_DEX_SKIN_INPUT.value = response.includes(profile.dexSkin) ? profile.dexSkin : "none";
});
// Fetch musicals
fetchData("/dashboard/dlc?type=MUSICAL").then((response) => {
addValuesToComboBox(ELEMENT_MUSICAL_INPUT, response);
ELEMENT_MUSICAL_INPUT.value = profile.musical;
ELEMENT_MUSICAL_INPUT.value = response.includes(profile.musical) ? profile.musical : "none";
});
}
@@ -402,8 +408,7 @@ function fetchProfileData() {
profile.gameVersion = gameVersion;
ELEMENT_GAME_SUMMARY.innerHTML = "Game Card in use: " + gameVersion;
// Still don't like this!
if(gameVersion.includes("2")) {
if(isVersion2()) {
ELEMENT_ENCOUNTER_SPECIES.max = 649;
ELEMENT_ITEM_ID.max = 638;