8 Commits

Author SHA1 Message Date
kuroppoi
0f4160459e Add option to configure Join Avenue visitor region 2023-07-09 22:00:39 +02:00
kuroppoi
3dea11ff50 Sort large lists alphabetically to make them easier to navigate 2023-07-09 04:35:50 +02:00
kuroppoi
b38b26f9ce Refactor dashboard stuff & ditch magic numbers for drop-down lists (#9) 2023-07-08 18:22:15 +02:00
kuroppoi
a95e7e2f01 Switcheroo 2023-07-08 15:09:45 +02:00
kuroppoi
53f05acab9 Center popup forms 2023-07-08 02:35:11 +02:00
kuroppoi
49a15a0156 Change DLC names to be clearer & add missing skins 2023-07-08 02:13:17 +02:00
kuroppoi
329e1de30a Rely on DLC game code & type instead of names not being duplicate 2023-07-08 02:09:05 +02:00
kuroppoi
0e12cb54d7 Check if address is actually a local address before returning it 2023-07-07 03:34:44 +02:00
76 changed files with 2905 additions and 511 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

@@ -45,11 +45,7 @@ import io.javalin.json.JavalinJackson;
public class DashboardHandler implements HttpHandler {
private static final Logger logger = LogManager.getLogger();
private final Set<Integer> availableBlackAndWhiteSpecies = Set.of(
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 +63,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 +106,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());
}
/**
@@ -248,14 +253,8 @@ public class DashboardHandler implements HttpHandler {
}
for(DreamEncounter encounter : request.encounters()) {
if(encounter.species() < 1) {
if(encounter.species() < 1 || encounter.species() > 649) {
return "Species is out of range.";
} else if(encounter.species() > 493) {
if(!player.getGameVersion().isVersion2()) {
return "Sorry, Generation V Pokémon are exclusive to Black Version 2 and White Version 2.";
} else if(!availableBlackAndWhiteSpecies.contains(encounter.species())) {
return "You have selected one or more Pokémon species that cannot be downloaded.";
}
} else if(encounter.move() < 0 || encounter.move() > 559) {
return "Move ID is out of range.";
} else if(encounter.gender() == null) {
@@ -275,11 +274,7 @@ public class DashboardHandler implements HttpHandler {
for(DreamItem item : request.items()) {
if(item.id() < 0 || item.id() > 638) {
return "Item ID is out of range";
} else if(item.id() > 626 && !player.getGameVersion().isVersion2()) {
return "You have selected one or more items that are exclusive to Black Version 2 and White Version 2.";
}
if(item.quantity() < 0 || item.quantity() > 20) {
} else if(item.quantity() < 0 || item.quantity() > 20) {
return "Item quantity is out of range.";
}
}

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

@@ -18,7 +18,14 @@ public class NetworkUtility {
public static InetAddress getLocalHost() {
try(Socket socket = new Socket()){
socket.connect(new InetSocketAddress("github.com", 80));
return socket.getLocalAddress();
InetAddress address = socket.getLocalAddress();
// Fall back to the network interface method if this is not a private IP address
if(!address.isSiteLocalAddress()) {
return getLocalHostFromNetworkInterfaces();
}
return address;
} catch(IOException e) {
logger.error("Couldn't get local host using socket - falling back to the network interface method", e);
return getLocalHostFromNetworkInterfaces();
@@ -42,8 +49,8 @@ public class NetworkUtility {
while(addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
// Return if IPv4
if(address instanceof Inet4Address) {
// Return this address if it is a local IPv4 address
if(address.isSiteLocalAddress() && address instanceof Inet4Address) {
return address;
}
}
@@ -52,6 +59,7 @@ public class NetworkUtility {
logger.error("Could not determine local host - falling back to loopback address", e);
}
logger.warn("No local host candidate could be found - loopback address will be used");
return InetAddress.getLoopbackAddress();
}
}

View File

@@ -1,16 +0,0 @@
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles/list.css">
</head>
<body onload="fillTableWithItems()">
<div id="main-container" class="root-container">
<label class="big-label">List of items that can be downloaded</label>
<label>NOTE: Item IDs greater than 626 are exclusive to Black Version 2 and White Version 2</label>
<table id="item-table">
<!-- Filled by list.js -->
</table>
</div>
</body>
<script src="scripts/list.js"></script>
</html>

View File

@@ -12,5 +12,6 @@
</div>
</div>
</body>
<script src="scripts/utility.js"></script>
<script src="scripts/login.js"></script>
</html>

View File

@@ -3,7 +3,7 @@
<meta charset="UTF-8">
<link rel="stylesheet" href="styles/profile.css">
</head>
<body onload="fetchProfileData()">
<body>
<div id="main-container" class="root-container" style="display:none;">
<div>
<div>
@@ -124,21 +124,24 @@
<!-- 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>
<option disabled>──────────────────────</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>
<option disabled>──────────────────────</option>
</select>
</div>
<div>
<label>Musical Show</label>
<select id="musical">
<option value="none">Do not change</option>
<option disabled>──────────────────────</option>
</select>
</div>
<div>
@@ -152,18 +155,26 @@
<button id="logout" class="big-button" onclick="postLogout()">Log Out</button>
</div>
</div>
</div>
</div>
<!-- Entree Forest Encounter Configuration Form -->
<div id="configureEncounter" class="popup">
<div class="content">
<button class="close-button" onclick="closeEncounterForm()">X</button>
<form id="encounter-form">
<label for="encounter-form-species">Species ID | </label><a href="/dashboard/species.html" target="_blank">View list</a>
<input id="encounter-form-species" name="species" type="number" value="1" min="1" max="493"/>
<label for="encounter-form-move">Move ID</label>
<input id="encounter-form-move" name="move" type="number" value="0" min="0" max="559"/>
<label for="encounter-form-form">Forme Index</label>
<input id="encounter-form-form" name="form" type="number" value="0" min="0" max="31"/>
<label for="encounter-form-species">Species</label>
<select id="encounter-form-species" name="species" value="1">
<!-- Filled by profile.js -->
</select>
<label for="encounter-form-move">Special Move</label>
<select id="encounter-form-move" name="move" value="0">
<option value="0">None</option>
<!-- Filled by profile.js -->
</select>
<label for="encounter-form-form">Form</label>
<select id="encounter-form-form" name="form" value="0">
<option value="0">N/A</option>
<!-- Filled by profile.js -->
</select>
<label for="encounter-form-gender">Gender</label>
<select id="encounter-form-gender" name="gender" value="GENDERLESS">
<option value="MALE">Male</option>
@@ -191,8 +202,10 @@
<div class="content">
<button class="close-button" onclick="closeEncounterForm()">X</button>
<form id="item-form">
<label for="item-form-id">Item ID | </label><a href="/dashboard/items.html" target="_blank">View list</a>
<input id="item-form-id" name="id" type="number" value="1" min="1" max="626"/>
<label for="item-form-id">Item</label>
<select id="item-form-id" name="id" value="1">
<!-- Filled by profile.js -->
</select>
<label for="item-form-quantity">Quantity</label>
<input id="item-form-quantity" name="quantity" type="number" value="1" min="1" max="20"/>
</form>
@@ -243,15 +256,28 @@
<option value="BLACK_2_ENGLISH">Black Version 2</option>
<option value="WHITE_2_ENGLISH">White Version 2</option>
</select>
<label for="visitor-form-region">Country</label>
<select id="visitor-form-region" name="region" value="1">
<!-- Filled by profile.js -->
</select>
<label for="visitor-form-subregion">State/Province</label>
<select id="visitor-form-subregion" name="subregion" value="0">
<option value="0">N/A</option>
<!-- Filled by profile.js -->
</select>
<label for="visitor-form-personality">Personality (Affects phrases used)</label>
<input id="visitor-form-personality" name="personality" type="number" value="0" min="0" max="7"/>
<label for="visitor-form-dreamer">Tucked-in Pokémon Species</label>
<input id="visitor-form-dreamer" name="personality" type="number" value="1" min="1" max="649"/>
<select id="visitor-form-dreamer" name="dreamerSpecies" value="1">
<!-- Filled by profile.js -->
</select>
</form>
<button class="big-button" onclick="saveVisitor()">Confirm</button>
<button class="big-button" onclick="removeVisitor()">Remove</button>
</div>
</div>
</body>
<script src="scripts/utility.js"></script>
<script src="scripts/pokedata.js"></script>
<script src="scripts/profile.js"></script>
</html>

View File

@@ -1,61 +0,0 @@
const AVAILABLE_GENERATION_V_POKEMON = new Array(
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);
const ITEMS_TO_EXCLUDE = new Array(113, 114, 115, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 426, 427);
function fillTable(tableId, rowSize, elementCount, htmlAppender) {
let tableHTML = "";
let index = 0;
for(let value = 1; value <= elementCount; value++) {
// Call the provided HTML appender to see what should be appended to the table
let toAppend = htmlAppender(value);
// If there is nothing to append (that is, the function has decided this element should be skipped)
// then continue to the next loop.
if(!toAppend) {
continue;
}
// Open a new table row if it should
if(index % rowSize == 0) {
tableHTML += "<tr>";
}
// Append the HTML data
tableHTML += toAppend;
// Close the table row if it has reached the maximum number of elements
if(index % rowSize == rowSize) {
tableHTML += "<tr>";
}
index++;
}
// Set the inner HTML
document.getElementById(tableId).innerHTML = tableHTML;
}
function fillTableWithSpecies() {
fillTable("species-table", 10, 649, (species) => {
// Skip this element if species is from Generation V and cannot be downloaded
if(species > 493 && !AVAILABLE_GENERATION_V_POKEMON.includes(species)) {
return false;
}
return "<td style='width:96px;height:96px;'><image src='/sprites/pokemon/normal/" + species + ".png'/><br>#" + species + "</td>";
});
}
function fillTableWithItems() {
fillTable("item-table", 20, 638, (item) => {
if(ITEMS_TO_EXCLUDE.includes(item)) {
return false;
}
return "<td style='width:48px;height:48px;'><image src='/sprites/items/" + item + ".png'/><br>#" + item + "</td>"
});
}

View File

@@ -1,20 +1,11 @@
const ELEMENT_GSID_INPUT = document.getElementById("gsid");
function postLogin() {
let loginData = {
fetchData("POST", "/dashboard/login", new URLSearchParams({
gsid: ELEMENT_GSID_INPUT.value
}
fetch("/dashboard/login", {
method: "POST",
body: new URLSearchParams(loginData)
}).then((response) => {
return response.json();
}).then((response) => {
console.log(response);
if(response.error) {
alert(response.message);
})).then((response) => {
if(response.error) {
window.alert(response.message);
} else {
window.location.href = "/dashboard/profile.html";
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
// HTML document elements
const ELEMENT_GAME_SUMMARY = document.getElementById("game-summary");
// Dreamer elements
const ELEMENT_DREAMER_SPRITE = document.getElementById("dreamer-sprite");
const ELEMENT_DREAMER_SPECIES = document.getElementById("dreamer-species");
const ELEMENT_DREAMER_NATURE = document.getElementById("dreamer-nature");
@@ -10,58 +10,29 @@ const ELEMENT_DREAMER_TRAINER = document.getElementById("dreamer-trainer");
const ELEMENT_DREAMER_TRAINER_ID = document.getElementById("dreamer-trainer-id");
const ELEMENT_DREAMER_LEVEL = document.getElementById("dreamer-level");
// Encounter form elements
const ELEMENT_ENCOUNTER_SPECIES = document.getElementById("encounter-form-species");
const ELEMENT_ENCOUNTER_MOVE = document.getElementById("encounter-form-move");
const ELEMENT_ENCOUNTER_FORM = document.getElementById("encounter-form-form");
const ELEMENT_ENCOUNTER_GENDER = document.getElementById("encounter-form-gender");
const ELEMENT_ENCOUNTER_ANIMATION = document.getElementById("encounter-form-animation");
// Item form elements
const ELEMENT_ITEM_ID = document.getElementById("item-form-id");
const ELEMENT_ITEM_QUANTITY = document.getElementById("item-form-quantity");
// Join Avenue Visitor form elements
const ELEMENT_VISITOR_NAME = document.getElementById("visitor-form-name");
const ELEMENT_VISITOR_TYPE = document.getElementById("visitor-form-type");
const ELEMENT_VISITOR_SHOP_TYPE = document.getElementById("visitor-form-shop-type");
const ELEMENT_VISITOR_GAME = document.getElementById("visitor-form-game");
const ELEMENT_VISITOR_REGION = document.getElementById("visitor-form-region");
const ELEMENT_VISITOR_SUBREGION = document.getElementById("visitor-form-subregion");
const ELEMENT_VISITOR_PERSONALITY = document.getElementById("visitor-form-personality");
const ELEMENT_VISITOR_DREAMER = document.getElementById("visitor-form-dreamer");
// Misc input elements
const ELEMENT_CGEAR_SKIN_INPUT = document.getElementById("cgear-skin");
const ELEMENT_DEX_SKIN_INPUT = document.getElementById("dex-skin");
const ELEMENT_MUSICAL_INPUT = document.getElementById("musical");
const ELEMENT_LEVEL_GAIN_INPUT = document.getElementById("level-gain-input");
// Create event listeners
ELEMENT_ENCOUNTER_SPECIES.addEventListener("change", clampValue);
ELEMENT_ENCOUNTER_MOVE.addEventListener("change", clampValue);
ELEMENT_ITEM_ID.addEventListener("change", clampValue);
ELEMENT_ITEM_QUANTITY.addEventListener("change", clampValue);
ELEMENT_LEVEL_GAIN_INPUT.addEventListener("change", clampValue);
ELEMENT_VISITOR_PERSONALITY.addEventListener("change", clampValue);
ELEMENT_VISITOR_DREAMER.addEventListener("change", clampValue);
function clampValue() {
let value = parseInt(this.value);
if(value < this.min) {
this.value = this.min;
} else if(value > this.max) {
console.log(value);
this.value = this.max;
}
}
// Other constant stuff
const AVAILABLE_GENERATION_V_POKEMON = new Array(
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); // Defining this 3 times is a brilliant idea.
// Local variables
var encounterTableIndex = -1;
var itemTableIndex = -1;
@@ -72,12 +43,136 @@ var profile = {
visitors: []
};
(async function() {
// Create event listeners
clampOnChange(ELEMENT_ITEM_QUANTITY);
clampOnChange(ELEMENT_LEVEL_GAIN_INPUT);
clampOnChange(ELEMENT_VISITOR_PERSONALITY);
// Fetch profile data
await fetchData("GET", "/dashboard/profile").then((response) => {
// Update game summary
profile.gameVersion = response.gameVersion;
ELEMENT_GAME_SUMMARY.innerHTML = "Game Card in use: " + profile.gameVersion;
// Update dreamer summary
if(response.dreamerInfo) {
let dreamerInfo = response.dreamerInfo;
ELEMENT_DREAMER_SPRITE.innerHTML = "<image src='" + response.dreamerSprite + "'/>";
ELEMENT_DREAMER_SPECIES.innerHTML = POKE_SPECIES_LIST[dreamerInfo.species - 1].name;
ELEMENT_DREAMER_NATURE.innerHTML = stringToWord(dreamerInfo.nature);
ELEMENT_DREAMER_NAME.innerHTML = dreamerInfo.nickname;
ELEMENT_DREAMER_GENDER.innerHTML = stringToWord(dreamerInfo.gender);
ELEMENT_DREAMER_TRAINER.innerHTML = dreamerInfo.trainerName;
ELEMENT_DREAMER_TRAINER_ID.innerHTML = ("0000" + dreamerInfo.trainerId).slice(-5);
ELEMENT_DREAMER_LEVEL.innerHTML = dreamerInfo.level;
}
// Update encounter table
if(response.encounters){
profile.encounters = response.encounters;
updateEncounterTable(0, 10);
}
// Update item table
if(response.items){
profile.items = response.items;
updateItemTable(0, 20);
}
// Update Join Avenue visitor table
if(response.avenueVisitors) {
profile.visitors = response.avenueVisitors;
updateVisitorTable(0, 12);
}
// Update selected DLC
profile.cgearSkin = response.cgearSkin ? response.cgearSkin : "none";
profile.dexSkin = response.dexSkin ? response.dexSkin : "none";
profile.musical = response.musical ? response.musical : "none";
fetchDlcData();
ELEMENT_LEVEL_GAIN_INPUT.value = response.levelsGained;
// Show Join Avenue visitor table if Black 2 or White 2
if(isVersion2()) {
document.getElementById("visitor-table-container").style.display = "block";
}
// Show div
document.getElementById("main-container").style.display = "flex";
});
// Sort data lists alphabetically
let sortedSpecies = [...POKE_SPECIES_LIST].sort((a, b) => a.name.localeCompare(b.name));
let sortedMoves = [...POKE_MOVE_LIST].sort((a, b) => a.name.localeCompare(b.name));
let sortedItems = [...ITEM_LIST].sort((a, b) => a.name.localeCompare(b.name));
// Add species data
for(let i in sortedSpecies) {
let species = sortedSpecies[i];
ELEMENT_VISITOR_DREAMER.options[ELEMENT_VISITOR_DREAMER.options.length] = new Option(species.name, species.id);
if(species.downloadable && (isVersion2() || species.id <= 493)) {
ELEMENT_ENCOUNTER_SPECIES.options[ELEMENT_ENCOUNTER_SPECIES.options.length] = new Option(species.name, species.id);
}
}
// Add move data
for(let i in sortedMoves) {
let move = sortedMoves[i];
ELEMENT_ENCOUNTER_MOVE.options[ELEMENT_ENCOUNTER_MOVE.options.length] = new Option(move.name, move.id);
}
// Add item data
for(let i in sortedItems) {
let item = sortedItems[i];
if(isVersion2() || item.id <= 626) {
ELEMENT_ITEM_ID.options[ELEMENT_ITEM_ID.options.length] = new Option(item.name, item.id);
}
}
// Add region data (already sorted alphabetically)
for(let i in REGION_LIST) {
let region = REGION_LIST[i];
ELEMENT_VISITOR_REGION.options[ELEMENT_VISITOR_REGION.options.length] = new Option(region.name, region.id);
}
// Event listener for changing the form selector contents when species changes
ELEMENT_ENCOUNTER_SPECIES.addEventListener("change", function() {
updateEncounterFormOptions();
ELEMENT_ENCOUNTER_FORM.value = 0;
});
// Same thing, but for Join Avenue visitor region & subregion
ELEMENT_VISITOR_REGION.addEventListener("change", function() {
ELEMENT_VISITOR_SUBREGION.value = updateVisitorSubregionOptions();
});
})();
/**
* Encounter configuration stuff
*/
function updateEncounterFormOptions() {
clearSelectOptions(ELEMENT_ENCOUNTER_FORM);
let species = POKE_SPECIES_MAP[ELEMENT_ENCOUNTER_SPECIES.value];
// Update special form options
if(species.forms) {
for(let i in species.forms) {
ELEMENT_ENCOUNTER_FORM.options[ELEMENT_ENCOUNTER_FORM.options.length] = new Option(species.forms[i], i);
}
} else {
ELEMENT_ENCOUNTER_FORM.options[ELEMENT_ENCOUNTER_FORM.options.length] = new Option("N/A", 0);
}
}
function configureEncounter(index) {
encounterTableIndex = Math.min(10, Math.min(index, profile.encounters.length));
// Load existing settings
let encounter = profile.encounters[encounterTableIndex];
ELEMENT_ENCOUNTER_SPECIES.value = encounter ? encounter.species : 1;
updateEncounterFormOptions();
ELEMENT_ENCOUNTER_MOVE.value = encounter ? encounter.move : 0;
ELEMENT_ENCOUNTER_FORM.value = encounter ? encounter.form : 0;
ELEMENT_ENCOUNTER_GENDER.value = encounter ? encounter.gender : "GENDERLESS";
@@ -86,64 +181,37 @@ function configureEncounter(index) {
function saveEncounter() {
if(encounterTableIndex < 0) {
closeEncounterForm();
return;
}
// Check if this species can be downloaded
let species = parseInt(ELEMENT_ENCOUNTER_SPECIES.value);
if(species > 493 && !AVAILABLE_GENERATION_V_POKEMON.includes(species)) {
alert("This Pokémon species cannot be downloaded. Click 'View list' in the encounter form to view a list of available Pokémon.");
return;
}
// Create encounter data
let encounterData = {
species: species,
profile.encounters[encounterTableIndex] = {
species: ELEMENT_ENCOUNTER_SPECIES.value,
move: ELEMENT_ENCOUNTER_MOVE.value,
form: ELEMENT_ENCOUNTER_FORM.value,
gender: ELEMENT_ENCOUNTER_GENDER.value,
animation: ELEMENT_ENCOUNTER_ANIMATION.value
}
// Set form to highest form available if it too great
let maxForm = 0;
switch(species) {
case 201: maxForm = 27; break; // Unown
case 386: maxForm = 3; break; // Deoxys
case 412:
case 413: maxForm = 2; break; // Burmy & Wormadam
case 422:
case 423:
case 487: maxForm = 1; break; // Shellos, Gastrodon & Giratina
case 479: maxForm = 5; break; // Rotom
case 493: maxForm = 16; break; // Arceus
case 550: maxForm = 1; break; // Basculin
}
if(encounterData.form > maxForm) {
encounterData.form = maxForm;
}
profile.encounters[encounterTableIndex] = encounterData;
};
updateEncounterCell(encounterTableIndex);
closeEncounterForm();
}
function removeEncounter() {
if(encounterTableIndex < 0) {
closeEncounterForm();
return;
}
let oldLength = profile.encounters.length;
profile.encounters.splice(encounterTableIndex, 1);
for(let i = encounterTableIndex; i < oldLength; i++) {
updateEncounterTable(encounterTableIndex, oldLength);
closeEncounterForm();
}
function updateEncounterTable(startIndex, endIndex) {
for(let i = startIndex; i < endIndex; i++) {
updateEncounterCell(i);
}
closeEncounterForm();
}
function updateEncounterCell(index) {
@@ -155,6 +223,7 @@ function updateEncounterCell(index) {
if(encounterData) {
spriteImage = spriteBase + encounterData.species + ".png";
// Use unique form sprite if it exists
if(encounterData.form > 0) {
let formSpriteImage = spriteBase + encounterData.species + "-" + encounterData.form + ".png";
@@ -172,21 +241,48 @@ function closeEncounterForm() {
window.location.href = "#";
}
/**
* Join Avenue visitor configuration stuff
*/
function updateVisitorSubregionOptions() {
clearSelectOptions(ELEMENT_VISITOR_SUBREGION);
let region = REGION_MAP[ELEMENT_VISITOR_REGION.value];
// Update subregion options
if(region.subregions) {
let sortedSubregions = [...region.subregions].sort((a, b) => a.name.localeCompare(b.name));
for(let i in sortedSubregions) {
let subregion = sortedSubregions[i];
ELEMENT_VISITOR_SUBREGION.options[ELEMENT_VISITOR_SUBREGION.options.length] = new Option(subregion.name, subregion.id);
}
return 1;
} else {
ELEMENT_VISITOR_SUBREGION.options[ELEMENT_VISITOR_SUBREGION.options.length] = new Option("N/A", 0);
}
return 0;
}
function configureVisitor(index) {
visitorTableIndex = Math.min(12, Math.min(index, profile.visitors.length));
// Load existing settings
let visitor = profile.visitors[visitorTableIndex];
ELEMENT_VISITOR_NAME.value = visitor ? visitor.name : "";
ELEMENT_VISITOR_TYPE.value = visitor ? visitor.type : "ACE_TRAINER_MALE";
ELEMENT_VISITOR_SHOP_TYPE.value = visitor ? visitor.shopType : "RAFFLE";
ELEMENT_VISITOR_GAME.value = visitor ? visitor.gameVersion : "BLACK_ENGLISH";
ELEMENT_VISITOR_REGION.value = visitor ? visitor.countryCode : 1;
updateVisitorSubregionOptions();
ELEMENT_VISITOR_SUBREGION.value = visitor ? visitor.stateProvinceCode : 0;
ELEMENT_VISITOR_PERSONALITY.value = visitor ? visitor.personality : 0;
ELEMENT_VISITOR_DREAMER.value = visitor ? visitor.dreamerSpecies : 1;
}
function saveVisitor() {
if(visitorTableIndex < 0) {
closeVisitorForm();
return;
}
@@ -208,36 +304,36 @@ function saveVisitor() {
}
}
// I'll make country codes configurable later... probably
let visitorData = {
profile.visitors[visitorTableIndex] = {
name: ELEMENT_VISITOR_NAME.value,
type: ELEMENT_VISITOR_TYPE.value,
shopType: ELEMENT_VISITOR_SHOP_TYPE.value,
gameVersion: ELEMENT_VISITOR_GAME.value,
countryCode: 220, // United States
stateProvinceCode: 48, // Washington, D.C.
countryCode: ELEMENT_VISITOR_REGION.value,
stateProvinceCode: ELEMENT_VISITOR_SUBREGION.value,
personality: ELEMENT_VISITOR_PERSONALITY.value,
dreamerSpecies: ELEMENT_VISITOR_DREAMER.value
}
profile.visitors[visitorTableIndex] = visitorData;
};
updateVisitorCell(visitorTableIndex);
closeVisitorForm();
}
function removeVisitor() {
if(visitorTableIndex < 0) {
closeVisitorForm();
return;
}
let oldLength = profile.visitors.length;
profile.visitors.splice(visitorTableIndex, 1);
for(let i = visitorTableIndex; i < oldLength; i++) {
updateVisitorTable(visitorTableIndex, oldLength);
closeVisitorForm();
}
function updateVisitorTable(startIndex, endIndex) {
for(let i = startIndex; i < endIndex; i++) {
updateVisitorCell(i);
}
closeVisitorForm();
}
function updateVisitorCell(index) {
@@ -262,10 +358,12 @@ function closeVisitorForm() {
window.location.href = "#";
}
/**
* Item configuration stuff
*/
function configureItem(index) {
itemTableIndex = Math.min(20, Math.min(index, profile.items.length));
// Loadg existing settings
let item = profile.items[itemTableIndex];
ELEMENT_ITEM_ID.value = item ? item.id : 1;
ELEMENT_ITEM_QUANTITY.value = item ? item.quantity : 1;
@@ -273,32 +371,34 @@ function configureItem(index) {
function saveItem() {
if(itemTableIndex < 0) {
closeItemForm();
return;
}
let itemData = {
profile.items[itemTableIndex] = {
id: ELEMENT_ITEM_ID.value,
quantity: ELEMENT_ITEM_QUANTITY.value
}
profile.items[itemTableIndex] = itemData;
};
updateItemCell(itemTableIndex);
closeItemForm();
}
function removeItem() {
if(itemTableIndex < 0) {
closeItemForm();
return;
}
let oldLength = profile.items.length;
profile.items.splice(itemTableIndex, 1);
for(let i = itemTableIndex; i < oldLength; i++) {
updateItemTable(itemTableIndex, oldLength);
closeItemForm();
}
function updateItemTable(startIndex, endIndex) {
for(let i = startIndex; i < endIndex; i++) {
updateItemCell(i);
}
closeItemForm();
}
function updateItemCell(index) {
@@ -325,7 +425,11 @@ function closeItemForm() {
window.location.href = "#";
}
function previewSkin(inputElementId) {
/**
* Miscellaneous stuff
*/
function previewSkin(inputElementId, type) {
let value = document.getElementById(inputElementId).value;
if(value == "none") {
@@ -333,149 +437,43 @@ 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;
}
async function fetchData(path) {
return fetchData(path, "GET", null);
}
async function fetchData(path, method, body) {
let response = await fetch(path, {
method: method,
body: body
});
// Return to login page if unauthorized
if(response.status == 401) {
window.location.href = "/dashboard/login.html";
return;
}
try {
return await response.json();
} catch(error) {
window.alert(error);
}
return null;
}
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) => {
addValuesToComboBox(ELEMENT_CGEAR_SKIN_INPUT, response);
ELEMENT_CGEAR_SKIN_INPUT.value = profile.cgearSkin;
// Fetch C-Gear skins
fetchData("GET", "/dashboard/dlc?type=" + (isVersion2() ? "CGEAR2" : "CGEAR")).then((response) => {
addDlcNames(ELEMENT_CGEAR_SKIN_INPUT, response);
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;
fetchData("GET", "/dashboard/dlc?type=ZUKAN").then((response) => {
addDlcNames(ELEMENT_DEX_SKIN_INPUT, response);
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;
fetchData("GET", "/dashboard/dlc?type=MUSICAL").then((response) => {
addDlcNames(ELEMENT_MUSICAL_INPUT, response);
ELEMENT_MUSICAL_INPUT.value = response.includes(profile.musical) ? profile.musical : "none";
});
}
// TODO
function fetchProfileData() {
fetchData("/dashboard/profile").then((response) => {
let gameVersion = response["gameVersion"];
let dreamerSprite = response["dreamerSprite"];
let dreamerInfo = response["dreamerInfo"];
let encounters = response["encounters"];
let items = response["items"];
let visitors = response["avenueVisitors"];
let cgearSkin = response["cgearSkin"];
let dexSkin = response["dexSkin"];
let musical = response["musical"];
let levelsGained = response["levelsGained"];
// Update game summary
profile.gameVersion = gameVersion;
ELEMENT_GAME_SUMMARY.innerHTML = "Game Card in use: " + gameVersion;
// Still don't like this!
if(gameVersion.includes("2")) {
ELEMENT_ENCOUNTER_SPECIES.max = 649;
ELEMENT_ITEM_ID.max = 638;
// Show Join Avenue visitor table
document.getElementById("visitor-table-container").style.display = "block";
}
// Update dreamer summary
if(dreamerInfo) {
let species = dreamerInfo["species"];
let nature = dreamerInfo["nature"];
let nickname = dreamerInfo["nickname"];
let gender = dreamerInfo["gender"];
let trainerName = dreamerInfo["trainerName"];
let trainerId = dreamerInfo["trainerId"];
let level = dreamerInfo["level"];
// Set element values
ELEMENT_DREAMER_SPRITE.innerHTML = "<image src='" + dreamerSprite + "'/>";
ELEMENT_DREAMER_SPECIES.innerHTML = "#" + species;
ELEMENT_DREAMER_NATURE.innerHTML = stringToWord(nature);
ELEMENT_DREAMER_NAME.innerHTML = nickname;
ELEMENT_DREAMER_GENDER.innerHTML = stringToWord(gender);
ELEMENT_DREAMER_TRAINER.innerHTML = trainerName;
ELEMENT_DREAMER_TRAINER_ID.innerHTML = ("0000" + trainerId).slice(-5);
ELEMENT_DREAMER_LEVEL.innerHTML = level;
}
// Update encounter table
if(encounters){
profile.encounters = encounters;
for(let i = 0; i < 10; i++) {
updateEncounterCell(i);
}
}
// Update item table
if(items){
profile.items = items;
for(let i = 0; i < 20; i++) {
updateItemCell(i);
}
}
// Update Join Avenue visitor table
if(visitors) {
profile.visitors = visitors;
for(let i = 0; i < 12; i++) {
updateVisitorCell(i);
}
}
// Update selected DLC
profile.cgearSkin = cgearSkin ? cgearSkin : "none";
profile.dexSkin = dexSkin ? dexSkin : "none";
profile.musical = musical ? musical : "none";
fetchDlcData();
// Update level gain
ELEMENT_LEVEL_GAIN_INPUT.value = levelsGained;
// Show div
document.getElementById("main-container").style.display = "flex";
});
function addDlcNames(selectElement, names) {
for(let i in names) {
let name = names[i];
selectElement.options[selectElement.options.length] = new Option(name.replace(/\.[^/.]+$/, ""), name);
}
}
function postProfileData() {
// Construct body
let profileData = {
fetchData("POST", "/dashboard/profile", JSON.stringify({
encounters: profile.encounters,
items: profile.items,
avenueVisitors: profile.visitors,
@@ -483,36 +481,18 @@ function postProfileData() {
dexSkin: ELEMENT_DEX_SKIN_INPUT.value,
musical: ELEMENT_MUSICAL_INPUT.value,
gainedLevels: ELEMENT_LEVEL_GAIN_INPUT.value
}
// Send data
fetchData("/dashboard/profile", "POST", JSON.stringify(profileData)).then((response) => {
})).then((response) => {
alert(response.message);
});
}
function postLogout() {
fetchData("/dashboard/logout", "POST", null).then((response) => {
fetchData("POST", "/dashboard/logout").then((response) => {
// Assume it succeeded
window.location.href = "/dashboard/login.html";
});
}
// TODO bad
function checkURL(url) {
var request = new XMLHttpRequest();
request.open('HEAD', url, false);
request.send();
return request.status == 200;
}
function stringToWord(string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}
function addValuesToComboBox(selectorElement, values) {
for(i in values) {
let value = values[i];
selectorElement.options[selectorElement.options.length] = new Option(value, value);
}
function isVersion2() {
return profile.gameVersion.includes("2");
}

View File

@@ -0,0 +1,59 @@
function clampOnChange(inputElement) {
inputElement.addEventListener("change", function() {
let value = parseInt(this.value);
if(value < this.min) {
this.value = this.min;
} else if(value > this.max) {
this.value = this.max;
}
});
}
function clearSelectOptions(selectElement) {
let startIndex = selectElement.options.length - 1;
for(i = startIndex; i >= 0; i--) {
selectElement.remove(i);
}
}
function stringToWord(string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}
function checkURL(url) {
var request = new XMLHttpRequest();
request.open('HEAD', url, false);
request.send();
return request.status == 200;
}
async function fetchData(method, path) {
return fetchData(method, path, null);
}
async function fetchData(method, path, body) {
let response = await fetch(path, {
method: method,
body: body
});
if(response.status != 200) {
if(response.status == 401) {
window.location.href = "/dashboard/login.html"; // TODO not epic idea to put this here
return {};
}
window.alert("Server returned status code " + response.status + " while fetching " + path);
return {};
}
try {
return await response.json();
} catch(error) {
window.alert("Could not deserialize JSON response: " + error);
}
return {};
}

View File

@@ -1,16 +0,0 @@
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles/list.css">
</head>
<body onload="fillTableWithSpecies()">
<div id="main-container" class="root-container"">
<label class="big-label">List of Pokémon species that can be downloaded</label>
<label>NOTE: Generation V Pokémon are exclusive to Black Version 2 and White Version 2</label>
<table id="species-table">
<!-- Filled by list.js -->
</table>
</div>
</body>
<script src="scripts/list.js"></script>
</html>

View File

@@ -1,37 +0,0 @@
body {
background-color: #191919;
color: white;
font-size: 20px;
}
img {
image-rendering: pixelated;
}
label {
text-align: center;
}
table {
border-spacing: 8px;
margin-top: 15px;
margin-bottom: 20px;
}
td {
background-color: #262626;
border-radius: 10px;
text-align: center;
}
.root-container {
display: grid;
position: absolute;
left: 50%;
transform: translate(-50%, 0%);
width: 1136; /* Hack-ish */
}
.big-label {
font-size: 36px;
}

View File

@@ -72,16 +72,15 @@ a:link, a:visited {
margin-top: 3px;
margin-left: -2px;
margin-bottom: 20px;
table-layout: fixed;
}
.dreamer-summary th {
text-align: left;
padding-left: 10px;
min-width: 76px;
}
.dreamer-summary td {
width: 50%;
background-color: #262626;
border-radius: 10px;
padding-left: 5px;
@@ -128,8 +127,8 @@ a:link, a:visited {
background-color: #191919;
position: absolute;
left: 50%;
top: 25%;
transform: translate(-50%, 0%);
top: 50%;
transform: translate(-50%, -50%);
padding: 20px;
width: 500px;
}

View File

@@ -1,44 +1,59 @@
/dlc/IRAO/CGEAR/C003_munna_1_en.bin
/dlc/IRAO/CGEAR/C004_pikachu_1_e.bin
/dlc/IRAO/CGEAR/C005_chirami_1_e.bin
/dlc/IRAO/CGEAR/C007_tabunne_1_e.bin
/dlc/IRAO/CGEAR/C008_pocchama_1_e.bin
/dlc/IRAO/CGEAR/C009_guregguru_1_e.bin
/dlc/IRAO/CGEAR/C010_fushigibana_1_e.bin
/dlc/IRAO/CGEAR/C011_rizadon_1_e.bin
/dlc/IRAO/CGEAR/C012_kamekkusu_1_e.bin
/dlc/IRAO/CGEAR/C013_zekurom_1_e_v02.bin
/dlc/IRAO/CGEAR/C014_reshiram_1_e.bin
/dlc/IRAO/CGEAR/C015_vikutini_1_e.bin
/dlc/IRAO/CGEAR/C017_porigonz_0_e.bin
/dlc/IRAO/CGEAR/C018_WCS_1_e.bin
/dlc/IRAO/CGEAR/C019_shikijikaharu_1_e.bin
/dlc/IRAO/CGEAR/C020_shikijikanatu_1_e.bin
/dlc/IRAO/CGEAR/C021_shikijikaaki_1_e.bin
/dlc/IRAO/CGEAR/C022_shikijikafuyu_1_e.bin
/dlc/IRAO/CGEAR/C023_iwaparesu_1_e.bin
/dlc/IRAO/CGEAR/C024_zoroark_1_e.bin
/dlc/IRAO/CGEAR/C025_giaru_1_e.bin
/dlc/IRAO/CGEAR/C030_kerudhio_1_en.bin
/dlc/IRAO/CGEAR/C031-1_meroetta_1_e.bin
/dlc/IRAO/CGEAR/C032-1_wcs2012_1_e.bin
/dlc/IRAO/CGEAR/C100_defo_1_en.bin
/dlc/IRAO/CGEAR2/C003-2_munna_1_en.bin
/dlc/IRAO/CGEAR2/C013-2_zekrom_1_e.bin
/dlc/IRAO/CGEAR2/C014-2_reshiram_1_e.bin
/dlc/IRAO/CGEAR2/C015-2_victini_1_e.bin
/dlc/IRAO/CGEAR2/C030-2_kerudhio_1_en.bin
/dlc/IRAO/CGEAR2/C031-2_meroetta_1_e.bin
/dlc/IRAO/CGEAR2/C033_halloween_1_en.bin
/dlc/IRAO/CGEAR2/C035_BK_1_e.bin
/dlc/IRAO/CGEAR2/C036_WK_1_e.bin
/dlc/IRAO/CGEAR2/C100-2_default_1_e.bin
/dlc/IRAO/MUSICAL/M010_munna_1_e_02.bin
/dlc/IRAO/MUSICAL/M013_meloetta_1_e.bin
/dlc/IRAO/ZUKAN/Z003_BWsyokigirl_1_en.bin
/dlc/IRAO/ZUKAN/Z004_BWsyokiboy_1_en.bin
/dlc/IRAO/ZUKAN/Z007_hyuu_1_en.bin
/dlc/IRAO/ZUKAN/Z008_bell_1_e.bin
/dlc/IRAO/ZUKAN/Z009_tyeren_1_e.bin
/dlc/IRAO/ZUKAN/Z100_defogirl_1_en.bin
/dlc/IRAO/ZUKAN/Z101_defoboy_1_en.bin
/dlc/IRAO/CGEAR/01 - Default.bin
/dlc/IRAO/CGEAR/02 - Meadow Munna.bin
/dlc/IRAO/CGEAR/03 - Aim for the Top.bin
/dlc/IRAO/CGEAR/04 - Twinkle Minccino.bin
/dlc/IRAO/CGEAR/05 - PokeSma Purrloin.bin
/dlc/IRAO/CGEAR/06 - Earful Audino.bin
/dlc/IRAO/CGEAR/07 - Happy Piplup.bin
/dlc/IRAO/CGEAR/08 - Poison Jab Croagunk.bin
/dlc/IRAO/CGEAR/09 - Venusaur!.bin
/dlc/IRAO/CGEAR/10 - Charizard!.bin
/dlc/IRAO/CGEAR/11 - Blastoise!.bin
/dlc/IRAO/CGEAR/12 - V for Victory!.bin
/dlc/IRAO/CGEAR/13 - Virtual Pokemon.bin
/dlc/IRAO/CGEAR/14 - Pokemon Cafe.bin
/dlc/IRAO/CGEAR/15 - Hero Reshiram.bin
/dlc/IRAO/CGEAR/16 - Hero Zekrom.bin
/dlc/IRAO/CGEAR/17 - 2011 Worlds C-Gear.bin
/dlc/IRAO/CGEAR/18 - Zoroark!.bin
/dlc/IRAO/CGEAR/19 - Spring Deerling.bin
/dlc/IRAO/CGEAR/20 - Summer Deerling.bin
/dlc/IRAO/CGEAR/21 - Autumn Deerling.bin
/dlc/IRAO/CGEAR/22 - Winter Deerling.bin
/dlc/IRAO/CGEAR/23 - CRUSTLE!.bin
/dlc/IRAO/CGEAR/24 - KLINK!.bin
/dlc/IRAO/CGEAR/25 - Ducklett Friends.bin
/dlc/IRAO/CGEAR/26 - Guidance Cobalion.bin
/dlc/IRAO/CGEAR/27 - Trial Terrakion.bin
/dlc/IRAO/CGEAR/28 - Rumination Virizion.bin
/dlc/IRAO/CGEAR/29 - Keldeo Ordinary Forme.bin
/dlc/IRAO/CGEAR/30 - Aria of the Night Sky.bin
/dlc/IRAO/CGEAR/31 - 2012 Worlds C-Gear.bin
/dlc/IRAO/CGEAR/32 - Red Genesect.bin
/dlc/IRAO/CGEAR2/01 - Default.bin
/dlc/IRAO/CGEAR2/02 - Meadow Munna.bin
/dlc/IRAO/CGEAR2/03 - Keldeo Resolute Forme.bin
/dlc/IRAO/CGEAR2/04 - 2012 Worlds C-Gear.bin
/dlc/IRAO/CGEAR2/05 - Pumpkin Pikachu.bin
/dlc/IRAO/CGEAR2/06 - V for Victory!.bin
/dlc/IRAO/CGEAR2/07 - Hero Reshiram.bin
/dlc/IRAO/CGEAR2/08 - Hero Zekrom.bin
/dlc/IRAO/CGEAR2/09 - Aria of the Night Sky.bin
/dlc/IRAO/CGEAR2/10 - Sleeping Eevee.bin
/dlc/IRAO/CGEAR2/11 - Venusaur!.bin
/dlc/IRAO/CGEAR2/12 - Charizard!.bin
/dlc/IRAO/CGEAR2/13 - Blastoise!.bin
/dlc/IRAO/CGEAR2/14 - Red Genesect.bin
/dlc/IRAO/CGEAR2/15 - Black Kyurem.bin
/dlc/IRAO/CGEAR2/16 - White Kyurem.bin
/dlc/IRAO/MUSICAL/01 - Charming Munna.bin
/dlc/IRAO/MUSICAL/02 - MELOETTAAA!!!.bin
/dlc/IRAO/ZUKAN/01 - Default (Pink).bin
/dlc/IRAO/ZUKAN/02 - Default (Red).bin
/dlc/IRAO/ZUKAN/03 - Unova Trio (Pink).bin
/dlc/IRAO/ZUKAN/04 - Unova Trio (Red).bin
/dlc/IRAO/ZUKAN/05 - Kanto Trio (Pink).bin
/dlc/IRAO/ZUKAN/06 - Kanto Trio (Red).bin
/dlc/IRAO/ZUKAN/07 - Hugh.bin
/dlc/IRAO/ZUKAN/08 - Bianca.bin
/dlc/IRAO/ZUKAN/09 - Cheren.bin

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.