From 6a25f344a0c7f78ea4ae6cd6d6b4dd578b6babbb Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 17:54:51 -0500
Subject: [PATCH 01/21] Create placeholder
---
server/placeholder | 1 +
1 file changed, 1 insertion(+)
create mode 100644 server/placeholder
diff --git a/server/placeholder b/server/placeholder
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/server/placeholder
@@ -0,0 +1 @@
+
From 5f69448a9c33e270bd19597244f18457d17a4b8a Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 17:55:08 -0500
Subject: [PATCH 02/21] Create toytags.json
---
server/toytags.json | 1 +
1 file changed, 1 insertion(+)
create mode 100644 server/toytags.json
diff --git a/server/toytags.json b/server/toytags.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/server/toytags.json
@@ -0,0 +1 @@
+[]
From be2d71fb75a17d4e88d87d40d8bd152cfeac411c Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 17:55:30 -0500
Subject: [PATCH 03/21] Update toytags.json
---
server/toytags.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/server/toytags.json b/server/toytags.json
index fe51488..f2cb873 100644
--- a/server/toytags.json
+++ b/server/toytags.json
@@ -1 +1,2 @@
+//This is where all toy tag data is stored.
[]
From 35dee190bde1edcded43b6fce9d9da1df7d6d538 Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 17:56:24 -0500
Subject: [PATCH 04/21] Update and rename index.html to server/index.html
---
index.html | 279 ----------------------------------
server/index.html | 374 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 374 insertions(+), 279 deletions(-)
delete mode 100644 index.html
create mode 100644 server/index.html
diff --git a/index.html b/index.html
deleted file mode 100644
index 12061d4..0000000
--- a/index.html
+++ /dev/null
@@ -1,279 +0,0 @@
-
-
-
-
-
-
-
LD ToyPad Emulator
-
- → Please don't spam click, or things may break and you'll have to restart the server! ←
- After changing a position, you have to wait for about 2 seconds, otherwise the game can't keep up.
- If there are too many characters, reload the page, add 7 characters and click "Remove all".
- Make sure not to exceed the item limit on each position when switching or placing objects (left/right: 3, middle: 1)!!!
-
-
-
-
-
-
-
Currently Placed
-
Click an entry to remove it from the ToyPad.
-
-
-
Remove all
-
-
-
-
-
-
\ No newline at end of file
diff --git a/server/index.html b/server/index.html
new file mode 100644
index 0000000..df40e66
--- /dev/null
+++ b/server/index.html
@@ -0,0 +1,374 @@
+
+
+
+
+
+
+
+
+
LD ToyPad Emulator
+
+ → Please don't spam click, or things may break and you'll have to restart the server! ←
+ After changing a position, you have to wait for about 2 seconds, otherwise the game can't keep up.
+ If there are too many characters, reload the page, add 7 characters and click "Remove all".
+ Make sure not to exceed the item limit on each position when switching or placing objects (left/right: 3,
+ middle: 1)!!!
+
+
+
+
+
+
Toy Box
+
Drag an entry to move it to the pad
+
+
+
+
+ Drag here to delete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 08aa1128a157f8a49a8f800b36b4198a305b2383 Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:02:32 -0500
Subject: [PATCH 05/21] Reworked Graphical Interface
> Added:
> > fs to read and write to toytags.json
> > Socket.io for communications between index.js and the html script.
> Pad Index data is now also stored locally allowing data to be retrieved even after a refresh.
> Tag data is now stored locally and called upon instead of creating new data with every action.
> > This includes vehicles with vehicle upgrades which are now saved as well
> Combined all tag movement into one function '/characterPlace' which checks for type before placing.
---
index.js | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 244 insertions(+), 23 deletions(-)
diff --git a/index.js b/index.js
index d11621e..60fdb7e 100644
--- a/index.js
+++ b/index.js
@@ -1,25 +1,35 @@
/*
-
Copyright © 2021 Berny23
This file is part of "ToyPad Emulator for Lego Dimensions" which is released under the "MIT" license.
See file "LICENSE" or go to "https://choosealicense.com/licenses/mit" for full license details.
-
*/
const ld = require ('node-ld')
+const fs = require('fs');
const path = require('path');
+const { DH_CHECK_P_NOT_PRIME } = require('constants');
+
const express = require('express');
const app = express();
+const http = require('http');
+const server = http.createServer(app);
+const { Server } = require("socket.io");
+const io = new Server(server);
+
+const toytagFileName = './server/json/toytags.json';
var tp = new ld.ToyPadEmu()
tp.registerDefaults()
+initalizeToyTagsJSON();
+
function createVehicle(id, upgrades, uid){
upgrades = upgrades || [0,0];
var token = new Buffer(180);
token.fill(0);
token.uid = uid;
+ console.log(upgrades);
token.writeUInt32LE(upgrades[0],0x23*4);
token.writeUInt16LE(id,0x24*4);
token.writeUInt32LE(upgrades[1],0x25*4);
@@ -35,49 +45,260 @@ function createCharacter(id, uid){
return token;
}
+function getNameFromID(id){
+ if(id < 1000)
+ dbfilename = './server/json/charactermap.json';
+ else
+ dbfilename = './server/json/tokenmap.json';
+ var name = "test";
+ const data = fs.readFileSync(dbfilename, 'utf8');
+ const databases = JSON.parse(data);
+ databases.forEach(db => {
+ if(id == db.id){
+ name = db.name
+ }
+ });
+
+ return name;
+}
+
+function getJSONFromUID(uid){
+ const data = fs.readFileSync(toytagFileName, 'utf8');
+ const databases = JSON.parse(data);
+ var entry;
+ databases.forEach(db => {
+ if(db.uid==uid)
+ entry = db;
+ });
+ return entry;
+}
+
+function updatePadIndex(uid, index){
+ console.log('Planning to set UID: ' + uid + ' to index ' + index);
+ const data = fs.readFileSync(toytagFileName, 'utf8');
+ const databases = JSON.parse(data);
+ databases.forEach(db => {
+ if(uid == db.uid){
+ db.index = index;
+ }
+ });
+ fs.writeFileSync(toytagFileName, JSON.stringify(databases, null, 4), function(){
+ console.log('Set UID: ' + uid + ' to index ' + index);
+ })
+}
+
+function getUIDFromIndex(index){
+ const data = fs.readFileSync(toytagFileName, 'utf8');
+ const databases = JSON.parse(data);
+ var uid;
+ databases.forEach(db => {
+ if(index == db.index){
+ uid = db.uid;
+ }
+ });
+ return uid;
+}
+
+function writeJSONData(uid, datatype, data){
+ console.log('Planning to set '+ datatype + ' of ' + uid + ' to ' + data);
+ const tags = fs.readFileSync(toytagFileName, 'utf8');
+ const databases = JSON.parse(tags);
+ databases.forEach(db => {
+ if(uid == db.uid){
+ db[datatype] = data;
+ return;
+ }
+ });
+ fs.writeFileSync(toytagFileName, JSON.stringify(databases, null, 4), function(){
+ console.log('Set '+ datatype + ' of ' + uid + ' to ' + data);
+ })
+}
+
+function initalizeToyTagsJSON(){
+ const data = fs.readFileSync(toytagFileName, 'utf8');
+ const databases = JSON.parse(data);
+ databases.forEach(db => {
+ db.index = "-1";
+ });
+ fs.writeFileSync(toytagFileName, JSON.stringify(databases, null, 4), function(){
+ console.log("Initalized toytags.JSON");
+ })
+}
+
+//When the game calls 'CMD_WRITE', writes the given data to the toytag in the top position.
+tp.hook(tp.CMD_WRITE, (req, res) => {
+ var ind = req.payload[0];
+ var page = req.payload[1];
+ var data = req.payload.slice(2);
+ var uid = getUIDFromIndex('2');
+ console.log('REQUEST (CMD_WRITE): index:', ind, 'page', page, 'data', data);
+
+ if(page == 24 || page == 36){
+ writeJSONData(uid,"id",data.readInt16LE(0));
+ var name = getNameFromID(data.readInt16LE(0));
+ writeJSONData(uid,"name",name);
+ writeJSONData(uid,"type","vehicle");
+ //writeVehicleData(uid, "uid", tp.randomUID())
+ }
+ else if(page == 23 || page == 35)
+ writeJSONData(uid, "vehicleUpgradesP23", data.readUInt32LE(0));
+ else if (page == 25 || page == 37){
+ writeJSONData(uid, "vehicleUpgradesP25", data.readUInt32LE(0));
+ io.emit("refreshTokens");
+ }
+
+ res.payload = new Buffer('00', 'hex');
+ var token = tp._tokens.find(t => t.index == ind);
+ if (token){
+ req.payload.copy(token.token, 4 * page, 2, 6);
+ }
+
+});
+
app.use(express.json());
+app.use(express.static(path.join(__dirname, 'server')))
+
app.get('/', (request, response) => {
- response.sendFile(path.join(__dirname, '/index.html'));
+ response.sendFile(path.join(__dirname, 'server/index.html'));
});
app.post('/character', (request, response) => {
- console.log('Placing character: ' + request.body.id);
+ console.log('Creating character: ' + request.body.id);
var uid = tp.randomUID();
var character = createCharacter(request.body.id, uid);
- tp.place(character, request.body.position, request.body.index, character.uid);
- console.log('Character placed: ' + request.body.id);
- response.send(uid);
+ var name = getNameFromID(request.body.id, "character");
+
+ console.log("name: " + name, " uid: " + character.uid, " id: " + character.id)
+
+ fs.readFile(toytagFileName, 'utf8', (err, data) => {
+ if(err){
+ console.log(err)
+ }
+ else{
+ const tags = JSON.parse(data.toString());
+
+ tags.push({
+ name: name,
+ id: character.id,
+ uid: character.uid,
+ index: "-1",
+ type: 'character',
+ vehicleUpgradesP23: 0,
+ vehicleUpgradesP25: 0
+ });
+
+ fs.writeFile(toytagFileName, JSON.stringify(tags, null, 4), 'utf8', (err) => {
+ if (err) {
+ console.log(`Error writing file: ${err}`);
+ } else {
+ console.log(`File is written successfully!`);
+ }
+ });
+ }
+ })
+
+ console.log('Character created: ' + request.body.id);
+ response.send();
});
+app.post('/characterPlace', (request, response) => {
+ console.log('Placing tag: ' + request.body.id);
+ var entry = getJSONFromUID(request.body.uid);
+
+ console.log(entry.type);
+
+ if(entry.type == "character"){
+ var character = createCharacter(request.body.id, request.body.uid);
+ tp.place(character, request.body.position, request.body.index, character.uid);
+ console.log('Character tag: ' + request.body.id);
+ updatePadIndex(character.uid, request.body.index);
+ response.send();
+ }
+ else{
+ var vehicle = createVehicle(request.body.id,[entry.vehicleUpgradesP23, entry.vehicleUpgradesP25],request.body.uid);
+ tp.place(vehicle, request.body.position, request.body.index, vehicle.uid);
+ console.log('Vehicle tag: ' + request.body.id);
+ updatePadIndex(vehicle.uid, request.body.index);
+ response.send();
+ }
+})
+
app.post('/vehicle', (request, response) => {
console.log('Placing vehicle: ' + request.body.id);
var uid = tp.randomUID();
var vehicle = createVehicle(request.body.id, [0xEFFFFFFF, 0xEFFFFFFF], uid);
- tp.place(vehicle, request.body.position, request.body.index, vehicle.uid);
- console.log('Vehicle placed: ' + request.body.id);
- response.send(uid);
-});
+ var name = getNameFromID(request.body.id, "vehicle");
-app.put('/character', (request, response) => {
- console.log('Changing character: "' + request.body.uid + '" to position ' + request.body.position + ', index ' + request.body.index);
- var character = createCharacter(request.body.id, request.body.uid);
- tp.place(character, request.body.position, request.body.index, character.uid);
- console.log('Character changed: "' + request.body.uid + '" to position ' + request.body.position + ', index ' + request.body.index);
-});
+ console.log("name: " + name, " uid: " + vehicle.uid, " id: " + vehicle.id)
+
+ fs.readFile(toytagFileName, 'utf8', (err, data) => {
+ if(err){
+ console.log(err)
+ }
+ else{
+ const tags = JSON.parse(data.toString());
+ var entry = {
+ name: name,
+ id: request.body.id,
+ uid: vehicle.uid,
+ index: "-1",
+ type: 'vehicle',
+ vehicleUpgradesP23: 0xEFFFFFFF,
+ vehicleUpgradesP25: 0xEFFFFFFF
+ }
-app.put('/vehicle', (request, response) => {
- console.log('Changing vehicle: "' + request.body.uid + '" to position ' + request.body.position + ', index ' + request.body.index);
- var vehicle = createVehicle(request.body.id, [0xEFFFFFFF, 0xEFFFFFFF], request.body.uid);
- tp.place(vehicle, request.body.position, request.body.index, vehicle.uid);
- console.log('Vehicle changed: "' + request.body.uid + '" to position ' + request.body.position + ', index ' + request.body.index);
+ console.log(entry)
+ tags.push(entry);
+
+ fs.writeFile(toytagFileName, JSON.stringify(tags, null, 4), 'utf8', (err) => {
+ if (err) {
+ console.log(`Error writing file: ${err}`);
+ } else {
+ console.log(`File is written successfully!`);
+ }
+ });
+ }
+ })
+ console.log('Vehicle placed: ' + request.body.id);
+ response.send(uid);
});
app.delete('/remove', (request, response) => {
console.log('Removing item: ' + request.body.index);
+ console.log('DEBUG: pad-from-token: ', tp._tokens.filter(v => v.index == request.body.index)[0].pad);
tp.remove(request.body.index);
console.log('Item removed: ' + request.body.index);
+ updatePadIndex(request.body.uid, "-1");
response.send(true);
});
-app.listen(80, () => console.log('Server running on port 80'));
\ No newline at end of file
+io.on('connection', (socket) => {
+ socket.on('deleteToken', (uid) => {
+ console.log('IO Recieved: Deleting entry '+ uid + ' from JSON');
+ const tags = fs.readFileSync(toytagFileName, 'utf8');
+ const databases = JSON.parse(tags);
+ var index = -1;
+ var i = 0;
+ databases.forEach(db => {
+ if(uid == db.uid){
+ index = i;
+ return;
+ }
+ i++;
+ });
+ console.log('Entry to delete: ', index)
+ if (index > -1) {
+ databases.splice(index, 1);
+ }
+ fs.writeFileSync(toytagFileName, JSON.stringify(databases, null, 4), function(){
+ if (index > -1)
+ console.log('Token not found');
+ else
+ console.log('Deleted ', uid, ' from JSON');
+ })
+ io.emit("refreshTokens");
+ });
+});
+
+server.listen(80, () => console.log('Server running on port 80'));
From 0c64bd05e91180633ddaa7394cf53c3cc24c5f30 Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:05:23 -0500
Subject: [PATCH 06/21] Updated demo image
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 7d99157..67ebc6d 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ Allows you to connect an emulated ToyPad to your PC or video-game console.
- Mobile-friendly web interface
## Demo
-![](https://i.imgur.com/Hg12EDL.jpg)
+![](https://imgur.com/a/f2A6Op8)
Video for Cemu emulator: https://www.youtube.com/watch?v=7CBa9u2ip-Y
From cab099bf9c993ca0044ade8be77b71a029f1bceb Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:06:03 -0500
Subject: [PATCH 07/21] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 67ebc6d..21b1b6e 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ Allows you to connect an emulated ToyPad to your PC or video-game console.
- Mobile-friendly web interface
## Demo
-![](https://imgur.com/a/f2A6Op8)
+![](https://i.imgur.com/iyWVObT.png)
Video for Cemu emulator: https://www.youtube.com/watch?v=7CBa9u2ip-Y
From 7e5d64d093a84f719d1968fc3e6e3c19c33a3196 Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:06:54 -0500
Subject: [PATCH 08/21] Rename server/toytags.json to server/json/toytags.json
---
server/{ => json}/toytags.json | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename server/{ => json}/toytags.json (100%)
diff --git a/server/toytags.json b/server/json/toytags.json
similarity index 100%
rename from server/toytags.json
rename to server/json/toytags.json
From a1ffe66ef188a61edd08cf61e70c12735a58783d Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:07:42 -0500
Subject: [PATCH 09/21] Add files via upload
---
server/json/charactermap.json | 88 ++++++++++++
server/json/tokenmap.json | 247 ++++++++++++++++++++++++++++++++++
server/json/upgrademap.json | 224 ++++++++++++++++++++++++++++++
3 files changed, 559 insertions(+)
create mode 100644 server/json/charactermap.json
create mode 100644 server/json/tokenmap.json
create mode 100644 server/json/upgrademap.json
diff --git a/server/json/charactermap.json b/server/json/charactermap.json
new file mode 100644
index 0000000..bf1d3bc
--- /dev/null
+++ b/server/json/charactermap.json
@@ -0,0 +1,88 @@
+[
+ { "id": 1, "name": "Batman", "world": "DC Comics" },
+ { "id": 2, "name": "Gandalf", "world": "Lord of the Rings" },
+ { "id": 3, "name": "Wyldstyle", "world": "The LEGO Movie" },
+ { "id": 4, "name": "Aquaman", "world": "DC Comics" },
+ { "id": 5, "name": "Bad Cop", "world": "The LEGO Movie" },
+ { "id": 6, "name": "Bane", "world": "DC Comics" },
+ { "id": 7, "name": "Bart", "world": "The Simpsons" },
+ { "id": 8, "name": "Benny", "world": "The LEGO Movie" },
+ { "id": 9, "name": "Chell", "world": "Portal 2" },
+ { "id": 10, "name": "Cole", "world": "Ninjago" },
+ { "id": 11, "name": "Cragger", "world": "Legends of Chima" },
+ { "id": 12, "name": "Cyborg", "world": "DC Comics" },
+ { "id": 13, "name": "Cyberman", "world": "Doctor Who" },
+ { "id": 14, "name": "Doc Brown", "world": "Back to the Future" },
+ { "id": 15, "name": "The Doctor", "world": "Doctor Who" },
+ { "id": 16, "name": "Emmet", "world": "The LEGO Movie" },
+ { "id": 17, "name": "Eris", "world": "Legends of Chima" },
+ { "id": 18, "name": "Gimli", "world": "Lord of the Rings" },
+ { "id": 19, "name": "Smeagol", "world": "Lord of the Rings" },
+ { "id": 20, "name": "Harley Quinn", "world": "DC Comics" },
+ { "id": 21, "name": "Homer", "world": "The Simpsons" },
+ { "id": 22, "name": "Jay", "world": "Ninjago" },
+ { "id": 23, "name": "Joker", "world": "DC Comics" },
+ { "id": 24, "name": "Kai", "world": "Ninjago" },
+ { "id": 25, "name": "ACU", "world": "Jurrasic Park" },
+ { "id": 26, "name": "Gamer Kid", "world": "Midway Arcade" },
+ { "id": 27, "name": "Krusty", "world": "The Simpsons" },
+ { "id": 28, "name": "Laval", "world": "Legends of Chima" },
+ { "id": 29, "name": "Legolas", "world": "Lord of the Rings" },
+ { "id": 30, "name": "Lloyd", "world": "Ninjago" },
+ { "id": 31, "name": "Marty Mcfly", "world": "Back to the Future" },
+ { "id": 32, "name": "Nya", "world": "Ninjago" },
+ { "id": 33, "name": "Owen", "world": "Jurrasic Park" },
+ { "id": 34, "name": "Peter Venkman", "world": "Ghostbusters" },
+ { "id": 35, "name": "Slimer", "world": "Ghostbusters" },
+ { "id": 36, "name": "Scooby Doo", "world": "Scooby-Doo" },
+ { "id": 37, "name": "SenseiWu", "world": "Ninjago" },
+ { "id": 38, "name": "Shaggy", "world": "Scooby-Doo" },
+ { "id": 39, "name": "Stay Puft", "world": "Ghostbusters" },
+ { "id": 40, "name": "Superman", "world": "DC Comics" },
+ { "id": 41, "name": "Unikitty", "world": "The LEGO Movie" },
+ { "id": 42, "name": "Wicked Witch", "world": "Wizard of OZ" },
+ { "id": 43, "name": "Superwoman", "world": "DC Comics" },
+ { "id": 44, "name": "Zane", "world": "Ninjago" },
+ { "id": 45, "name": "Green Arrow", "world": "DC Comics" },
+ { "id": 46, "name": "Supergirl", "world": "DC Comics" },
+ { "id": 47, "name": "Abby Yates", "world": "Ghostbuster 2016" },
+ { "id": 48, "name": "Finn", "world": "Adventure Time" },
+ { "id": 49, "name": "Ethan Hunt", "world": "Mission Impossible" },
+ { "id": 50, "name": "Lumpy Space Princess", "world": "Adventure Time" },
+ { "id": 51, "name": "Jake", "world": "Adventure Time" },
+ { "id": 52, "name": "Harry Potter", "world": "Harry Potter" },
+ { "id": 53, "name": "Voldemort", "world": "Harry Potter" },
+ { "id": 54, "name": "Michael Knight", "world":"Knight Rider"},
+ { "id": 55, "name": "B.A. Baracus", "world": "A-Team" },
+ { "id": 56, "name": "Newt", "world": "Fantastic Beasts" },
+ { "id": 57, "name": "Sonic", "world": "Sonic" },
+ { "id": 58, "name": "Future Update", "world": "N/A" },
+ { "id": 59, "name": "Gizmo", "world": "Gremlins" },
+ { "id": 60, "name": "Stripe", "world": "Gremlins" },
+ { "id": 61, "name": "E.T.", "world": "E.T." },
+ { "id": 62, "name": "Tina", "world": "Fantastic Beasts" },
+ { "id": 63, "name": "Marceline", "world": "Adventure Time" },
+ { "id": 64, "name": "Bat Girl", "world":"LEGO Batman Movie"},
+ { "id": 65, "name": "Robin", "world":"LEGO Batman Movie"},
+ { "id": 66, "name": "Sloth", "world":"Goonies" },
+ { "id": 67, "name": "Hermione Granger", "world":"Harry Potter" },
+ { "id": 68, "name": "Chase McCain", "world":"LEGO City" },
+ { "id": 69, "name": "Excalibur Batman","world":"lego Batman Movie"},
+ { "id": 70, "name": "Raven", "world":"Teen Titans Go"},
+ { "id": 71, "name": "Beast Boy", "world":"Teen Titans Go"},
+ { "id": 72, "name": "Beetle Juice", "world":"Beetle Juice"},
+ { "id": 73, "name": "Lord Vortech", "world":"Lego Dimensions"},
+ { "id": 74, "name": "Blossom", "world":"Power Puff Girls"},
+ { "id": 75, "name": "Bubbles", "world":"Power Puff Girls"},
+ { "id": 76, "name": "Buttercup", "world":"Power Puff Girls"},
+ { "id": 77, "name": "Star Fire", "world":"Teen Titans Go"},
+ { "id": 78, "name": "Test 15", "world":"15"},
+ { "id": 79, "name": "Test 16", "world":"16"},
+ { "id": 80, "name": "Test 17", "world":"17"},
+ { "id": 81, "name": "Test 16", "world":"18"},
+ { "id": 82, "name": "Test 17", "world":"19"},
+ { "id": 83, "name": "Test 18", "world":"20"},
+ { "id": 768, "name": "Unknown", "world":"Unknown"},
+ { "id": 769, "name": "Supergirl Red Lantern", "world":"Dc Comics"},
+ { "id": 770, "name": "Unknown", "world":"Unknown"}
+]
diff --git a/server/json/tokenmap.json b/server/json/tokenmap.json
new file mode 100644
index 0000000..7ea717c
--- /dev/null
+++ b/server/json/tokenmap.json
@@ -0,0 +1,247 @@
+[
+ { "id": 1000, "upgrademap": 0, "rebuild": 0, "name": "Police Car" },
+ { "id": 1001, "upgrademap": 0, "rebuild": 1, "name": "* Aerial Squad Car" },
+ { "id": 1002, "upgrademap": 0, "rebuild": 2, "name": "* Missile Striker" },
+ { "id": 1003, "upgrademap": 0, "rebuild": 0, "name": "Gravity Sprinter" },
+ { "id": 1004, "upgrademap": 0, "rebuild": 1, "name": "* Street Shredder" },
+ { "id": 1005, "upgrademap": 0, "rebuild": 2, "name": "* Sky Clobberer" },
+ { "id": 1006, "upgrademap": 0, "rebuild": 0, "name": "Batmobile" },
+ { "id": 1007, "upgrademap": 0, "rebuild": 1, "name": "* Batblaster" },
+ { "id": 1008, "upgrademap": 0, "rebuild": 2, "name": "* Sonic Batray" },
+ { "id": 1009, "upgrademap": 0, "rebuild": 0, "name": "Benny's Spaceship" },
+ { "id": 1010, "upgrademap": 0, "rebuild": 1, "name": "* Lasercraft" },
+ { "id": 1011, "upgrademap": 0, "rebuild": 2, "name": "* The Annihilator" },
+ { "id": 1012, "upgrademap": 0, "rebuild": 0, "name": "Delorean" },
+ { "id": 1013, "upgrademap": 0, "rebuild": 1, "name": "* Ultra Time Machine" },
+ { "id": 1014, "upgrademap": 0, "rebuild": 2, "name": "* Electric Time Machine" },
+ { "id": 1015, "upgrademap": 0, "rebuild": 0, "name": "Hoverboard" },
+ { "id": 1016, "upgrademap": 0, "rebuild": 1, "name": "* Cyclone Board" },
+ { "id": 1017, "upgrademap": 0, "rebuild": 2, "name": "* Ultimate Hoverjet" },
+ { "id": 1018, "upgrademap": 0, "rebuild": 0, "name": "Eagle interceptor" },
+ { "id": 1019, "upgrademap": 0, "rebuild": 1, "name": "* Eagle Skyblazer" },
+ { "id": 1020, "upgrademap": 0, "rebuild": 2, "name": "* Eagle Swoop Diver" },
+ { "id": 1021, "upgrademap": 0, "rebuild": 0, "name": "Cragger's Fireship" },
+ { "id": 1022, "upgrademap": 0, "rebuild": 1, "name": "* Croc Command Sub" },
+ { "id": 1023, "upgrademap": 0, "rebuild": 2, "name": "* Swamp Skimmer" },
+ { "id": 1024, "upgrademap": 0, "rebuild": 0, "name": "Cyber Guard" },
+ { "id": 1025, "upgrademap": 0, "rebuild": 1, "name": "* Cyber-Wrecker" },
+ { "id": 1026, "upgrademap": 0, "rebuild": 2, "name": "* Laser Robot Walker" },
+ { "id": 1027, "upgrademap": 0, "rebuild": 0, "name": "K9" },
+ { "id": 1028, "upgrademap": 0, "rebuild": 1, "name": "* K9 Ruff Rover" },
+ { "id": 1029, "upgrademap": 0, "rebuild": 2, "name": "* K9 Laser Cutter" },
+ { "id": 1030, "upgrademap": 0, "rebuild": 0, "name": "TARDIS" },
+ { "id": 1031, "upgrademap": 0, "rebuild": 1, "name": "* Laser-Pulse TARDIS" },
+ { "id": 1032, "upgrademap": 0, "rebuild": 2, "name": "* Energy-Burst TARDIS" },
+ { "id": 1033, "upgrademap": 0, "rebuild": 0, "name": "Emmet's Excavator" },
+ { "id": 1034, "upgrademap": 0, "rebuild": 1, "name": "* The Destroydozer" },
+ { "id": 1035, "upgrademap": 0, "rebuild": 2, "name": "* Destruct-o-Mech" },
+ { "id": 1036, "upgrademap": 0, "rebuild": 0, "name": "Winged Monkey" },
+ { "id": 1037, "upgrademap": 0, "rebuild": 1, "name": "* Battle Monkey" },
+ { "id": 1038, "upgrademap": 0, "rebuild": 2, "name": "* Commander Monkey" },
+ { "id": 1039, "upgrademap": 0, "rebuild": 0, "name": "Axe Chariot" },
+ { "id": 1040, "upgrademap": 0, "rebuild": 1, "name": "* Axe Hurler" },
+ { "id": 1041, "upgrademap": 0, "rebuild": 2, "name": "* Soaring Chariot" },
+ { "id": 1042, "upgrademap": 0, "rebuild": 0, "name": "Shelob the Great" },
+ { "id": 1043, "upgrademap": 0, "rebuild": 1, "name": "* 8-Legged Stalker" },
+ { "id": 1044, "upgrademap": 0, "rebuild": 2, "name": "* Poison Slinger" },
+ { "id": 1045, "upgrademap": 0, "rebuild": 0, "name": "Homer's Car" },
+ { "id": 1046, "upgrademap": 0, "rebuild": 1, "name": "* Homercraft" },
+ { "id": 1047, "upgrademap": 0, "rebuild": 2, "name": "* SubmaHomer" },
+ { "id": 1048, "upgrademap": 0, "rebuild": 0, "name": "Taunt-o-Vision" },
+ { "id": 1049, "upgrademap": 0, "rebuild": 1, "name": "* Blast Cam" },
+ { "id": 1050, "upgrademap": 0, "rebuild": 2, "name": "* The MechaHomer" },
+ { "id": 1051, "upgrademap": 0, "rebuild": 0, "name": "Velociraptor" },
+ { "id": 1052, "upgrademap": 0, "rebuild": 1, "name": "* Spike Attack Raptor" },
+ { "id": 1053, "upgrademap": 0, "rebuild": 2, "name": "* Venom Raptor" },
+ { "id": 1054, "upgrademap": 0, "rebuild": 0, "name": "Gyro Sphere" },
+ { "id": 1055, "upgrademap": 0, "rebuild": 1, "name": "* Sonic Beam Gyrosphere" },
+ { "id": 1056, "upgrademap": 0, "rebuild": 2, "name": "* Speed Boost Gyrosphere" },
+ { "id": 1057, "upgrademap": 0, "rebuild": 0, "name": "Clown Bike" },
+ { "id": 1058, "upgrademap": 0, "rebuild": 1, "name": "* Cannon Bike" },
+ { "id": 1059, "upgrademap": 0, "rebuild": 2, "name": "* Anti-Gravity Rocket Bike" },
+ { "id": 1060, "upgrademap": 0, "rebuild": 0, "name": "Mighty Lion Rider" },
+ { "id": 1061, "upgrademap": 0, "rebuild": 1, "name": "* Lion Blazer" },
+ { "id": 1062, "upgrademap": 0, "rebuild": 2, "name": "* Fire Lion" },
+ { "id": 1063, "upgrademap": 0, "rebuild": 0, "name": "Arrow Launcher" },
+ { "id": 1064, "upgrademap": 0, "rebuild": 1, "name": "* Seeking Shooter" },
+ { "id": 1065, "upgrademap": 0, "rebuild": 2, "name": "* Triple Ballista" },
+ { "id": 1066, "upgrademap": 0, "rebuild": 0, "name": "Mystery Machine" },
+ { "id": 1067, "upgrademap": 0, "rebuild": 1, "name": "* Mystery Tow" },
+ { "id": 1068, "upgrademap": 0, "rebuild": 2, "name": "* Mystery Monster" },
+ { "id": 1069, "upgrademap": 0, "rebuild": 0, "name": "Boulder Bomber" },
+ { "id": 1070, "upgrademap": 0, "rebuild": 1, "name": "* Boulder Blaster" },
+ { "id": 1071, "upgrademap": 0, "rebuild": 2, "name": "* Cyclone Jet" },
+ { "id": 1072, "upgrademap": 0, "rebuild": 0, "name": "Storm Fighter" },
+ { "id": 1073, "upgrademap": 0, "rebuild": 1, "name": "* Lightning Jet" },
+ { "id": 1074, "upgrademap": 0, "rebuild": 2, "name": "* Electro-Shooter" },
+ { "id": 1075, "upgrademap": 0, "rebuild": 0, "name": "Blade Bike" },
+ { "id": 1076, "upgrademap": 0, "rebuild": 1, "name": "* Flying Fire Bike" },
+ { "id": 1077, "upgrademap": 0, "rebuild": 2, "name": "* Blades of Fire" },
+ { "id": 1078, "upgrademap": 0, "rebuild": 0, "name": "Samurai Mech" },
+ { "id": 1079, "upgrademap": 0, "rebuild": 1, "name": "* Samurai Shooter" },
+ { "id": 1080, "upgrademap": 0, "rebuild": 2, "name": "* Soaring Samurai Mech" },
+ { "id": 1081, "upgrademap": 0, "rebuild": 0, "name": "Companion Cube" },
+ { "id": 1082, "upgrademap": 0, "rebuild": 1, "name": "* Laser Deflector" },
+ { "id": 1083, "upgrademap": 0, "rebuild": 2, "name": "* Gold Heart Emitter" },
+ { "id": 1084, "upgrademap": 0, "rebuild": 0, "name": "Sentry Turret" },
+ { "id": 1085, "upgrademap": 0, "rebuild": 1, "name": "* Turret Striker" },
+ { "id": 1086, "upgrademap": 0, "rebuild": 2, "name": "* Flying Turret Carrier" },
+ { "id": 1087, "upgrademap": 0, "rebuild": 0, "name": "Scooby Snack" },
+ { "id": 1088, "upgrademap": 0, "rebuild": 1, "name": "* Scooby Fire Snack" },
+ { "id": 1089, "upgrademap": 0, "rebuild": 2, "name": "* Scooby Ghost Snack" },
+ { "id": 1090, "upgrademap": 0, "rebuild": 0, "name": "Cloud Cukko Car" },
+ { "id": 1091, "upgrademap": 0, "rebuild": 1, "name": "* X-Stream Soaker" },
+ { "id": 1092, "upgrademap": 0, "rebuild": 2, "name": "* Rainbow Cannon" },
+ { "id": 1093, "upgrademap": 0, "rebuild": 0, "name": "Invisible Jet" },
+ { "id": 1094, "upgrademap": 0, "rebuild": 1, "name": "* Stealth Laser Shooter" },
+ { "id": 1095, "upgrademap": 0, "rebuild": 2, "name": "* Torpedo Bomber" },
+ { "id": 1096, "upgrademap": 0, "rebuild": 0, "name": "Ninja Copter" },
+ { "id": 1097, "upgrademap": 0, "rebuild": 1, "name": "* Glaciator" },
+ { "id": 1098, "upgrademap": 0, "rebuild": 2, "name": "* Freeze Fighter" },
+ { "id": 1099, "upgrademap": 0, "rebuild": 0, "name": "Traveling Time Train" },
+ { "id": 1100, "upgrademap": 0, "rebuild": 1, "name": "* (Traveling Time Train - rebuilt 1)" },
+ { "id": 1101, "upgrademap": 0, "rebuild": 2, "name": "* (Traveling Time Train - rebuilt 2)" },
+ { "id": 1102, "upgrademap": 0, "rebuild": 0, "name": "Aqua Watercraft" },
+ { "id": 1103, "upgrademap": 0, "rebuild": 1, "name": "* (Aqua Watercraft - rebuilt 1)" },
+ { "id": 1104, "upgrademap": 0, "rebuild": 2, "name": "* (Aqua Watercraft - rebuilt 2)" },
+ { "id": 1105, "upgrademap": 0, "rebuild": 0, "name": "Drill Driver" },
+ { "id": 1106, "upgrademap": 0, "rebuild": 1, "name": "* (Drill Driver - rebuilt 1)" },
+ { "id": 1107, "upgrademap": 0, "rebuild": 2, "name": "* (Drill Driver - rebuilt 2)" },
+ { "id": 1108, "upgrademap": 0, "rebuild": 0, "name": "Quinn-mobile" },
+ { "id": 1109, "upgrademap": 0, "rebuild": 1, "name": "* (Quinn-mobile - rebuilt 1)" },
+ { "id": 1110, "upgrademap": 0, "rebuild": 2, "name": "* (Quinn-mobile - rebuilt 2)" },
+ { "id": 1111, "upgrademap": 0, "rebuild": 0, "name": "The Jokers Chopper" },
+ { "id": 1112, "upgrademap": 0, "rebuild": 1, "name": "* (The Jokers Chopper - rebuilt 1)" },
+ { "id": 1113, "upgrademap": 0, "rebuild": 2, "name": "* (The Jokers Chopper - rebuilt 2)" },
+ { "id": 1114, "upgrademap": 0, "rebuild": 0, "name": "Hover Pod" },
+ { "id": 1115, "upgrademap": 0, "rebuild": 1, "name": "* (Hover Pod - rebuilt 1)" },
+ { "id": 1116, "upgrademap": 0, "rebuild": 2, "name": "* (Hover Pod - rebuilt 2)" },
+ { "id": 1117, "upgrademap": 0, "rebuild": 0, "name": "Dalek" },
+ { "id": 1118, "upgrademap": 0, "rebuild": 1, "name": "* (Dalek - rebuilt 1)" },
+ { "id": 1119, "upgrademap": 0, "rebuild": 2, "name": "* (Dalek - rebuilt 2)" },
+ { "id": 1120, "upgrademap": 0, "rebuild": 0, "name": "Ecto-1" },
+ { "id": 1121, "upgrademap": 0, "rebuild": 1, "name": "* (Ecto-1 - rebuilt 1)" },
+ { "id": 1122, "upgrademap": 0, "rebuild": 2, "name": "* (Ecto-1 - rebuilt 2)" },
+ { "id": 1123, "upgrademap": 0, "rebuild": 0, "name": "Ghost Trap" },
+ { "id": 1124, "upgrademap": 0, "rebuild": 1, "name": "* (Ghost Trap - rebuilt 1)" },
+ { "id": 1125, "upgrademap": 0, "rebuild": 2, "name": "* (Ghost Trap - rebuilt 2)" },
+ { "id": 1126, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1127, "upgrademap": 0, "rebuild": 1, "name": "unknown" },
+ { "id": 1128, "upgrademap": 0, "rebuild": 2, "name": "unknown" },
+ { "id": 1129, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1130, "upgrademap": 0, "rebuild": 1, "name": "unknown" },
+ { "id": 1131, "upgrademap": 0, "rebuild": 2, "name": "unknown" },
+ { "id": 1132, "upgrademap": 0, "rebuild": 0, "name": "Llyod's Golden Dragon" },
+ { "id": 1133, "upgrademap": 0, "rebuild": 1, "name": "* (Golden Dragon - rebuilt 1)" },
+ { "id": 1134, "upgrademap": 0, "rebuild": 2, "name": "* (Golden Dragon - rebuilt 2)" },
+ { "id": 1135, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1136, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1137, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1138, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1139, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1140, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1141, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1142, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1143, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1144, "upgrademap": 0, "rebuild": 0, "name": "Mega Flight Dragon" },
+ { "id": 1145, "upgrademap": 0, "rebuild": 1, "name": "* (Mega Flight Dragon - rebuilt 1)" },
+ { "id": 1146, "upgrademap": 0, "rebuild": 2, "name": "* (Mega Flight Dragon - rebuilt 2)" },
+ { "id": 1147, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1148, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1149, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1150, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1151, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1152, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1153, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1154, "upgrademap": 0, "rebuild": 0, "name": "unknown" },
+ { "id": 1155, "upgrademap": 0, "rebuild": 0, "name": "Flying White Dragon" },
+ { "id": 1156, "upgrademap": 0, "rebuild": 1, "name": "* Golden Fire Dragon" },
+ { "id": 1157, "upgrademap": 0, "rebuild": 2, "name": "* Ultra Destruction Dragon" },
+ { "id": 1158, "upgrademap": 0, "rebuild": 0, "name": "Arcade Machine" },
+ { "id": 1159, "upgrademap": 0, "rebuild": 1, "name": "* 8-bit Shooter" },
+ { "id": 1160, "upgrademap": 0, "rebuild": 2, "name": "* The Pixelator" },
+ { "id": 1161, "upgrademap": 0, "rebuild": 0, "name": "G-61555 Spy Hunter" },
+ { "id": 1162, "upgrademap": 0, "rebuild": 1, "name": "* The Interdiver" },
+ { "id": 1163, "upgrademap": 0, "rebuild": 2, "name": "* Aerial Spyhunter" },
+ { "id": 1164, "upgrademap": 0, "rebuild": 0, "name": "Slime Shooter" },
+ { "id": 1165, "upgrademap": 0, "rebuild": 1, "name": "* Slime Exploder" },
+ { "id": 1166, "upgrademap": 0, "rebuild": 2, "name": "* Slime Streamer" },
+ { "id": 1167, "upgrademap": 0, "rebuild": 0, "name": "Terror Dog" },
+ { "id": 1168, "upgrademap": 0, "rebuild": 1, "name": "* Terror Dog Destroyer" },
+ { "id": 1169, "upgrademap": 0, "rebuild": 2, "name": "* Soaring Terror Dog" },
+ { "id": 1170, "upgrademap": 0, "rebuild": 0, "name": "Ancient War Elephant" },
+ { "id": 1171, "upgrademap": 0, "rebuild": 1, "name": "* Cosmic Squid" },
+ { "id": 1172, "upgrademap": 0, "rebuild": 2, "name": "* Psychic Submarine" },
+ { "id": 1173, "upgrademap": 0, "rebuild": 0, "name": "BMO" },
+ { "id": 1174, "upgrademap": 0, "rebuild": 1, "name": "* DOGMO" },
+ { "id": 1175, "upgrademap": 0, "rebuild": 2, "name": "* SNAKEMO" },
+ { "id": 1176, "upgrademap": 0, "rebuild": 0, "name": "Jakemoblie" },
+ { "id": 1177, "upgrademap": 0, "rebuild": 1, "name": "* Snail Dude Jake" },
+ { "id": 1178, "upgrademap": 0, "rebuild": 2, "name": "* Hover Jake" },
+ { "id": 1179, "upgrademap": 0, "rebuild": 0, "name": "Lumpy Car" },
+ { "id": 1180, "upgrademap": 0, "rebuild": 1, "name": "* Lumpy Truck" },
+ { "id": 1181, "upgrademap": 0, "rebuild": 2, "name": "* Lumpy Land Whale" },
+ { "id": 1182, "upgrademap": 0, "rebuild": 0, "name": "Lunatic Amp" },
+ { "id": 1183, "upgrademap": 0, "rebuild": 1, "name": "* Lunatic Amp 2" },
+ { "id": 1184, "upgrademap": 0, "rebuild": 2, "name": "* Lunatic Amp 3" },
+ { "id": 1185, "upgrademap": 0, "rebuild": 0, "name": "B.A.s Van" },
+ { "id": 1186, "upgrademap": 0, "rebuild": 1, "name": "* Fool Smasher" },
+ { "id": 1187, "upgrademap": 0, "rebuild": 2, "name": "* The Pain Plane" },
+ { "id": 1188, "upgrademap": 0, "rebuild": 0, "name": "Phone Home" },
+ { "id": 1189, "upgrademap": 0, "rebuild": 1, "name": "* Phone Home 2" },
+ { "id": 1190, "upgrademap": 0, "rebuild": 2, "name": "* Phone Home 3" },
+ { "id": 1191, "upgrademap": 0, "rebuild": 0, "name": "Niffler" },
+ { "id": 1192, "upgrademap": 0, "rebuild": 1, "name": "* Niffler 2" },
+ { "id": 1193, "upgrademap": 0, "rebuild": 2, "name": "* Niffler 3" },
+ { "id": 1194, "upgrademap": 0, "rebuild": 0, "name": "Swooping Evil" },
+ { "id": 1195, "upgrademap": 0, "rebuild": 1, "name": "* Swooping Evil 2" },
+ { "id": 1196, "upgrademap": 0, "rebuild": 2, "name": "* Swooping Evil 3" },
+ { "id": 1197, "upgrademap": 0, "rebuild": 0, "name": "Ecto 1" },
+ { "id": 1198, "upgrademap": 0, "rebuild": 1, "name": "* Ectozer" },
+ { "id": 1199, "upgrademap": 0, "rebuild": 2, "name": "* Ecto1 Rebuild 2" },
+ { "id": 1200, "upgrademap": 0, "rebuild": 2, "name": "Flash 'n' Finish" },
+ { "id": 1201, "upgrademap": 0, "rebuild": 0, "name": "* Rampage Record Player" },
+ { "id": 1202, "upgrademap": 0, "rebuild": 1, "name": "* Stripe's Throne" },
+ { "id": 1203, "upgrademap": 0, "rebuild": 2, "name": "R.C. Racer" },
+ { "id": 1204, "upgrademap": 0, "rebuild": 0, "name": "* Gadet-o-matic" },
+ { "id": 1205, "upgrademap": 0, "rebuild": 1, "name": "* Scarlet Scorpion" },
+ { "id": 1206, "upgrademap": 0, "rebuild": 0, "name": "Hogwarts Express" },
+ { "id": 1207, "upgrademap": 0, "rebuild": 2, "name": "* Soaring Steam Plane" },
+ { "id": 1208, "upgrademap": 0, "rebuild": 1, "name": "* Steam Warrior" },
+ { "id": 1209, "upgrademap": 0, "rebuild": 2, "name": "Enchanted Car" },
+ { "id": 1210, "upgrademap": 0, "rebuild": 0, "name": "* Shark Sub" },
+ { "id": 1211, "upgrademap": 0, "rebuild": 1, "name": "* Monstrous Mouth" },
+ { "id": 1212, "upgrademap": 0, "rebuild": 2, "name": "IMF Scrambler" },
+ { "id": 1213, "upgrademap": 0, "rebuild": 0, "name": "* Shock Cycle" },
+ { "id": 1214, "upgrademap": 0, "rebuild": 1, "name": "* IMF Covert Jet" },
+ { "id": 1215, "upgrademap": 0, "rebuild": 2, "name": "IMF Sport Car" },
+ { "id": 1216, "upgrademap": 0, "rebuild": 0, "name": "* IMF Tank" },
+ { "id": 1217, "upgrademap": 0, "rebuild": 1, "name": "* IMF-Splorer" },
+ { "id": 1218, "upgrademap": 0, "rebuild": 0, "name": "Sonic Speedster" },
+ { "id": 1219, "upgrademap": 0, "rebuild": 1, "name": "* Sonic Speedster 2" },
+ { "id": 1220, "upgrademap": 0, "rebuild": 2, "name": "* Sonic Speedster 3" },
+ { "id": 1221, "upgrademap": 0, "rebuild": 0, "name": "The Tornado" },
+ { "id": 1222, "upgrademap": 0, "rebuild": 1, "name": "* The Tornado 1" },
+ { "id": 1223, "upgrademap": 0, "rebuild": 2, "name": "* The Tornado 2" },
+ { "id": 1224, "upgrademap": 0, "rebuild": 0, "name": "K.I.T.T" },
+ { "id": 1225, "upgrademap": 0, "rebuild": 1, "name": "* K.I.T.T. JET" },
+ { "id": 1226, "upgrademap": 0, "rebuild": 2, "name": "* GOLIATH ARMORED SEMI" },
+ { "id": 1227, "upgrademap": 0, "rebuild": 0, "name": "Police" },
+ { "id": 1228, "upgrademap": 0, "rebuild": 1, "name": "* Hovercraft" },
+ { "id": 1229, "upgrademap": 0, "rebuild": 2, "name": "* Plane" },
+ { "id": 1230, "upgrademap": 0, "rebuild": 0, "name": "BIONIC STEED" },
+ { "id": 1231, "upgrademap": 0, "rebuild": 1, "name": "* BAT RAPTOR" },
+ { "id": 1232, "upgrademap": 0, "rebuild": 2, "name": "* ULTRA BAT" },
+ { "id": 1233, "upgrademap": 0, "rebuild": 0, "name": "BAT WING" },
+ { "id": 1234, "upgrademap": 0, "rebuild": 1, "name": "* BLACK THUNDER" },
+ { "id": 1235, "upgrademap": 0, "rebuild": 2, "name": "* BAT TANK" },
+ { "id": 1236, "upgrademap": 0, "rebuild": 0, "name": "Skeleton Organ" },
+ { "id": 1237, "upgrademap": 0, "rebuild": 1, "name": "* Jukebox" },
+ { "id": 1238, "upgrademap": 0, "rebuild": 2, "name": "* Skele-Turkey" },
+ { "id": 1239, "upgrademap": 0, "rebuild": 0, "name": "Pirate Ship" },
+ { "id": 1240, "upgrademap": 0, "rebuild": 1, "name": "* Fanged Fortune" },
+ { "id": 1241, "upgrademap": 0, "rebuild": 2, "name": "* Inferno" },
+ { "id": 1242, "upgrademap": 0, "rebuild": 0, "name": "Buckbeak" },
+ { "id": 1243, "upgrademap": 0, "rebuild": 1, "name": "* Giant Owl" },
+ { "id": 1244, "upgrademap": 0, "rebuild": 2, "name": "* Fierce Falcon" }
+]
diff --git a/server/json/upgrademap.json b/server/json/upgrademap.json
new file mode 100644
index 0000000..1edc43a
--- /dev/null
+++ b/server/json/upgrademap.json
@@ -0,0 +1,224 @@
+{
+ "speed":["Model Boost A"]
+}
+
+"Speed":[{
+ "name":"Model Boost Ability",
+ "desc":"Inital model speed has a boost for a short time.",
+ "cost":"cost":15000
+},{
+ "name":"Boost Buff 1",
+ "desc":"Increased boost speed",
+ "cost":"15,000",
+},{
+ "name":"Movement Speed Buff 1",
+ "desc":"Increased model movement",
+ "cost":"15,000",
+},{
+ "name":"Jump Ability",
+ "desc":"Model can Jump ",
+ "cost":"20,000",
+},{
+ "name":"Boost Buff 2",
+ "desc":"Increased boost speed",
+ "cost":"20,000 Studs",
+},{
+ "name":"Movement Speed Buff 2",
+ "desc":"Increased model movement.",
+ "cost":"20,000 Studs",
+}]
+
+"Power":[{
+ "name":"Color Changing Bolts Ability",
+ "desc":"Gives the ability to fire tintable bolts",
+ "cost":"15,000",
+},{
+ "name":"Fire Speed Buff 1",
+ "desc":"Increased weapon fire rate",
+ "cost":"15,000 Studs",
+},{
+ "name":"Armor Buff",
+ "desc":"Model upgraded to have5 Armor Hearts ",
+ "cost":"15,000",
+},{
+ "name":"Instant Tow Bar",
+ "desc":"Tow Bars complete instantnly with vehicles that have them ",
+ "cost":"20,000",
+},{
+ "name":"Fire Speed Buff 2",
+ "desc":"Increased weapon fire rate",
+ "cost":"20,000 Studs",
+},{
+ "name":"Armor Buff",
+ "desc":"Model upgraded to have 6 armor hearts.",
+ "cost":"20,000 Studs",
+}]
+"Weapons":[{
+ "name":"Mine Weapon",
+ "desc":"Mines are deployed",
+ "cost":"15,000 Studs",
+},{
+ "name":"Explode Ability",
+ "desc":"Model explodes after a count down (toggle off/on)",
+ "cost":"15,000 Studs",
+}]
+"Extras":[{
+ "name":"Bolt Color Chooser",
+ "desc":"Allows you to tint bolt colors",
+ "cost":"15,000 Studs",
+},{
+ "name":"Enemy Hearts Ability",
+ "desc":"Enemy characters now drop hearts ",
+ "cost":"15,000",
+},{
+ "name":"Regenerate Armor Ability",
+ "desc":"Hearts on the model regenerate",
+ "cost":"15,000 Studs",
+}]
+"Colors":[{
+ "name":"Skin 1",
+ "desc":"Changes the appearance of the model",
+ "cost":"15,000 Studs",
+},{
+ "name":"Tire Color Chooser",
+ "desc":"Allows you to pick tire colors",
+ "cost":"15,000 Studs",
+},{
+ "name":"Flame Color Choser",
+ "desc":"Choose exhaust flame color.",
+ "cost":"15,000 Studs",
+}],
+"Weapons",
+{
+ "name":"Spinning Weapon",
+ "desc":"Spinning objects are now fired to destroy distant threats ",
+ "cost":"20,000",
+},
+"Weapons",
+{
+ "name":"Electric Shield",
+ "desc":"An electric field surrounds the model",
+ "cost":"20,000 Studs",
+},
+"Extras 1",
+{
+ "name":"",
+ "desc":"",
+ "cost":"15,000 Studs",
+},
+"Extras",
+{
+ "name":"Guardian Ability",
+ "desc":"Model will protect you by attacking enemies (needs an active weapon selected).",
+ "cost":"20,000 Studs",
+},
+"Extras",
+{
+ "name":"Character Studs Ability",
+ "desc":"Enemies now give off lots of studs when they are defeated.",
+ "cost":"20,000 Studs",
+},
+"Colors 1",
+{
+ "name":"",
+ "desc":" ",
+ "cost":"15,000 Studs",
+}
+"",
+"Colors 2",
+{
+ "name":"",
+ "desc":"",
+ "cost":"50,000",
+},
+"Colors 3",
+{
+ "name":"",
+ "desc":"",
+ "cost":"50,000",
+},
+"Speed 1",
+{
+ "name":"Instant Accelerator Switch Ability",
+ "desc":"Accelerator Switches complete instantly when driven on.",
+ "cost":"50,000 Studs",
+},
+"Speed 2",
+{
+ "name":"Boost Buff 3",
+ "desc":"Increased boost speed",
+ "cost":"50,000 Studs",
+},
+"Speed 3",
+{
+ "name":"Movement Speed Buff 3",
+ "desc":"Increased model movement.",
+ "cost":"50,000 Studs",
+},
+"Power 1",
+{
+ "name":"Instant Sonar Smash",
+ "desc":"Sonar smashes complete instantly rather than shaking.",
+ "cost":"50,000",
+},
+"Power 2",
+"",
+{
+ "name":"",
+ "desc":"",
+ "cost":"50,000",
+},
+"Power 3",
+{
+ "name":"",
+ "desc":"",
+ "cost":"50,000",
+},
+"Weapons 1",
+{
+ "name":"Swarm Weapon",
+ "desc":"Objects swarm around the model.",
+ "cost":"50,000 Studs",
+},
+"Weapons 3",
+{
+ "name":"Speaker Attack Ability",
+ "desc":"Music makes nearby enemies dance for a set time.",
+ "cost":"50,000 Studs",
+},
+"Extras 1",
+{
+ "name":"Engine Sound Chooser",
+ "desc":"Change the engine sounds.",
+ "cost":"50,000 Studs",
+},
+"Extras 2",
+{
+ "name":"Armor Hearts Multiplier",
+ "desc":"Heart multiplier for player's health.",
+ "cost":"50,000 Studs",
+},
+"Extras 3",
+{
+ "name":"Stud Magnet Ability",
+ "desc":"Studs are attracted to the vehicle",
+ "cost":"50,000 Studs",
+},
+"Colors 1",
+{
+ "name":"Skin 3",
+ "desc":"Changes the appearance of the model.",
+ "cost":"50,000 Studs",
+},
+"Colors 2",
+{
+ "name":"Model Color Chooser",
+ "desc":"Allows you to pick model color.",
+ "cost":"50,000 Studs",
+},
+"Colors 3",
+{
+ "name":"",
+ "desc":"",
+ "cost":"50,000",
+}
\ No newline at end of file
From ff59fc16570d9b448fec314cbe506ad9e9c43bab Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:08:41 -0500
Subject: [PATCH 10/21] Update README.md
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 21b1b6e..0ffb2c6 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,6 @@ Allows you to connect an emulated ToyPad to your PC or video-game console.
- Supports most if not all of the consoles the game is available for
- Confirmed working on Cemu and real Wii U
- Can be configured easily by following the instructions below
-- Mobile-friendly web interface
## Demo
![](https://i.imgur.com/iyWVObT.png)
From 9612d0aeb38f84ce43417ea100735dc23d8d1ea2 Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:09:38 -0500
Subject: [PATCH 11/21] Added socket.io
---
package.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 5293a53..8b07675 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
- "node-ld": "^1.1.6"
+ "node-ld": "^1.1.6",
+ "socket.io": "^4.2.0"
}
}
From eb1edf04d88aa7e57a8ba54ad03a04fb98c43f54 Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:53:29 -0500
Subject: [PATCH 12/21] Added comments
---
index.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 48 insertions(+), 6 deletions(-)
diff --git a/index.js b/index.js
index 60fdb7e..9086f4b 100644
--- a/index.js
+++ b/index.js
@@ -10,6 +10,7 @@ const fs = require('fs');
const path = require('path');
const { DH_CHECK_P_NOT_PRIME } = require('constants');
+//Setup Webserver
const express = require('express');
const app = express();
const http = require('http');
@@ -17,26 +18,36 @@ const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
+//File where tag info will be saved
const toytagFileName = './server/json/toytags.json';
var tp = new ld.ToyPadEmu()
tp.registerDefaults()
-initalizeToyTagsJSON();
-
+initalizeToyTagsJSON(); //Run in case there were any leftovers from a previous run.
+
+//Create a token JSON object from provided vehicle data
+/* Vehicle Data Explained:
+ * All data is transfered through a series of buffers. The data from these buffers needs to written to specific points (pages) in the token's
+ * buffer for it to be read properly.
+ *
+ * For vehicles:
+ * Page 24 is the ID of the vehicle
+ * Pages 23 & 25 are the upgrade data
+ */
function createVehicle(id, upgrades, uid){
upgrades = upgrades || [0,0];
var token = new Buffer(180);
token.fill(0);
token.uid = uid;
- console.log(upgrades);
token.writeUInt32LE(upgrades[0],0x23*4);
token.writeUInt16LE(id,0x24*4);
token.writeUInt32LE(upgrades[1],0x25*4);
- token.writeUInt16BE(1,0x26*4);
+ token.writeUInt16BE(1,0x26*4); //Page 26 is used for verification of somekind.
return token;
}
+//Create a token JSON object from provided character data
function createCharacter(id, uid){
var token = new Buffer(180)
token.fill(0); // Game really only cares about 0x26 being 0 and D4 returning an ID
@@ -45,6 +56,7 @@ function createCharacter(id, uid){
return token;
}
+//This finds a character or vehicles name from the ID provided.
function getNameFromID(id){
if(id < 1000)
dbfilename = './server/json/charactermap.json';
@@ -62,6 +74,7 @@ function getNameFromID(id){
return name;
}
+//This finds and returns an JSON entry from toytags.json with the matching uid.
function getJSONFromUID(uid){
const data = fs.readFileSync(toytagFileName, 'utf8');
const databases = JSON.parse(data);
@@ -73,6 +86,7 @@ function getJSONFromUID(uid){
return entry;
}
+//This updates the pad index of a tag in toytags.json, so that info can be accessed locally.
function updatePadIndex(uid, index){
console.log('Planning to set UID: ' + uid + ' to index ' + index);
const data = fs.readFileSync(toytagFileName, 'utf8');
@@ -87,6 +101,7 @@ function updatePadIndex(uid, index){
})
}
+//This searches toytags.json and returns to UID of the entry with the matching index.
function getUIDFromIndex(index){
const data = fs.readFileSync(toytagFileName, 'utf8');
const databases = JSON.parse(data);
@@ -99,6 +114,7 @@ function getUIDFromIndex(index){
return uid;
}
+//This updates the provided datatype, of the entry with the matching uid, with the provided data.
function writeJSONData(uid, datatype, data){
console.log('Planning to set '+ datatype + ' of ' + uid + ' to ' + data);
const tags = fs.readFileSync(toytagFileName, 'utf8');
@@ -114,6 +130,8 @@ function writeJSONData(uid, datatype, data){
})
}
+
+//This sets all saved index values to '-1' (meaning unplaced).
function initalizeToyTagsJSON(){
const data = fs.readFileSync(toytagFileName, 'utf8');
const databases = JSON.parse(data);
@@ -126,6 +144,21 @@ function initalizeToyTagsJSON(){
}
//When the game calls 'CMD_WRITE', writes the given data to the toytag in the top position.
+/* Writing Tags Explained:
+ * A write occurs in three seperate function calls, and will repreat until either the write is canceled in game,
+ * or all three calls successfully write data.
+ *
+ * To appease the game all data is passed through and copied to the token in the top pad. But during this we can intercept what is being written
+ * and save the data locally as well. This lets us call that data back when we want to use that tag again.
+ *
+ * payload[1] tells what page is being written, and everything after is the data.
+ * page 24 - ID
+ * page 23 - Vehicle Upgrade Pt 1
+ * page 26 - Vehicle Upgrades Pt 2
+ * **When writing the pages requested for the write are sometimes ofset by 12, not sure why.
+ *
+ * This data is copied to the JSON for future use.
+ */
tp.hook(tp.CMD_WRITE, (req, res) => {
var ind = req.payload[0];
var page = req.payload[1];
@@ -133,18 +166,18 @@ tp.hook(tp.CMD_WRITE, (req, res) => {
var uid = getUIDFromIndex('2');
console.log('REQUEST (CMD_WRITE): index:', ind, 'page', page, 'data', data);
+ //The ID is stored in page
if(page == 24 || page == 36){
writeJSONData(uid,"id",data.readInt16LE(0));
var name = getNameFromID(data.readInt16LE(0));
writeJSONData(uid,"name",name);
writeJSONData(uid,"type","vehicle");
- //writeVehicleData(uid, "uid", tp.randomUID())
}
else if(page == 23 || page == 35)
writeJSONData(uid, "vehicleUpgradesP23", data.readUInt32LE(0));
else if (page == 25 || page == 37){
writeJSONData(uid, "vehicleUpgradesP25", data.readUInt32LE(0));
- io.emit("refreshTokens");
+ io.emit("refreshTokens"); //Refreshes the html's tag gui.
}
res.payload = new Buffer('00', 'hex');
@@ -155,14 +188,18 @@ tp.hook(tp.CMD_WRITE, (req, res) => {
});
+
+
app.use(express.json());
app.use(express.static(path.join(__dirname, 'server')))
+//**Website requests**
app.get('/', (request, response) => {
response.sendFile(path.join(__dirname, 'server/index.html'));
});
+//Create a new Character and save that data to toytags.json
app.post('/character', (request, response) => {
console.log('Creating character: ' + request.body.id);
var uid = tp.randomUID();
@@ -202,6 +239,7 @@ app.post('/character', (request, response) => {
response.send();
});
+//This is called when a token is placed or move onto a position on the toypad.
app.post('/characterPlace', (request, response) => {
console.log('Placing tag: ' + request.body.id);
var entry = getJSONFromUID(request.body.uid);
@@ -224,6 +262,7 @@ app.post('/characterPlace', (request, response) => {
}
})
+//Create a new vehicle and save that data to toytags.json
app.post('/vehicle', (request, response) => {
console.log('Placing vehicle: ' + request.body.id);
var uid = tp.randomUID();
@@ -264,6 +303,7 @@ app.post('/vehicle', (request, response) => {
response.send(uid);
});
+//This is called when a tag is removed from the board or needs to be moved.
app.delete('/remove', (request, response) => {
console.log('Removing item: ' + request.body.index);
console.log('DEBUG: pad-from-token: ', tp._tokens.filter(v => v.index == request.body.index)[0].pad);
@@ -273,7 +313,9 @@ app.delete('/remove', (request, response) => {
response.send(true);
});
+//IO calls
io.on('connection', (socket) => {
+ //Removes a entry from toytags.json
socket.on('deleteToken', (uid) => {
console.log('IO Recieved: Deleting entry '+ uid + ' from JSON');
const tags = fs.readFileSync(toytagFileName, 'utf8');
From e8e0a9a93d830f3534ae15d1c613f18beec72eaa Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 18:53:52 -0500
Subject: [PATCH 13/21] Update index.js
---
index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/index.js b/index.js
index 9086f4b..2a143e4 100644
--- a/index.js
+++ b/index.js
@@ -1,5 +1,5 @@
/*
- Copyright © 2021 Berny23
+ Copyright © 2021 Berny23 & Cort1237
This file is part of "ToyPad Emulator for Lego Dimensions" which is released under the "MIT" license.
See file "LICENSE" or go to "https://choosealicense.com/licenses/mit" for full license details.
From 00a0063561670b4d8ebe01f58040ec04a49bbf20 Mon Sep 17 00:00:00 2001
From: cort1237 <59704055+cort1237@users.noreply.github.com>
Date: Mon, 20 Sep 2021 19:03:42 -0500
Subject: [PATCH 14/21] Changed warning
---
server/index.html | 27 +++++----------------------
1 file changed, 5 insertions(+), 22 deletions(-)
diff --git a/server/index.html b/server/index.html
index df40e66..2ee0c1a 100644
--- a/server/index.html
+++ b/server/index.html
@@ -1,6 +1,6 @@