This commit is contained in:
SpreedBLood 2021-01-28 09:12:04 +01:00
commit 5488f25310
25 changed files with 2290 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules/
dist/
.idea
rare_dragonlamp

19
config.ini Normal file
View File

@ -0,0 +1,19 @@
output.folder.furniture=/home/user/WebstormProjects/sites/assets.nitro.se/game/dcr/endrit/furniture/
output.folder.figure=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/figure-new1/
output.folder.effect=/home/user/WebstormProjects/sites/assets.nitro.se/game/effect/
output.folder.pet=/home/user/WebstormProjects/sites/assets.nitro.se/game/pet/
furnidata.url=http://assets.nitro.se/game/gamedata/furnidata-entry.xml
figuremap.url=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/figuremap.xml
effectmap.url=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/effectmap.xml
external_vars.url=http://assets.nitro.se/game/gamedata/external_variables.txt
dynamic.download.url.furniture=/home/user/WebstormProjects/sites/assets.nitro.se/game/dcr/endrit/hof_furni/%className%.swf
dynamic.download.url.figure=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf
dynamic.download.url.effect=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf
dynamic.download.url.pet=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf
convert.furniture=0
convert.figure=1
convert.effect=1
convert.pet=1
figure.rotation.enabled=0
figure.skip.non-existing.asset.images=0
convert.threads=4

775
package-lock.json generated Normal file
View File

@ -0,0 +1,775 @@
{
"name": "nitroassetconverter",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@jvitela/mustache-wax": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@jvitela/mustache-wax/-/mustache-wax-1.0.3.tgz",
"integrity": "sha512-5M5p8d9YQwEDSa0oLoeCZ8ECiM2ZJLKxI/D0pDByiNBJw+4Tizjk/NMIjGx7IoJOGBnWcfHX3Pwd6m/MpMHoGA=="
},
"@types/node": {
"version": "14.14.22",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz",
"integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw=="
},
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"amdefine": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.1.tgz",
"integrity": "sha1-tcdcUyBS3M1qOcAGTHcsjVegbNI="
},
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"requires": {
"tweetnacl": "^0.14.3"
}
},
"bignumber.js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz",
"integrity": "sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg="
},
"bl": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz",
"integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=",
"requires": {
"readable-stream": "~1.0.26"
}
},
"bmp-js": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz",
"integrity": "sha1-ZBE+nHzxICs3btYHvzBibr5XsYo="
},
"buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
},
"buffer-equal": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
"integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs="
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"commander": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz",
"integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg="
},
"concat-frames": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/concat-frames/-/concat-frames-1.0.3.tgz",
"integrity": "sha1-z+moFvJce3WWPZn+8vSqBFj7+Zs=",
"requires": {
"pixel-stream": "^1.0.3"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"dom-walk": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"es6-promise": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
"integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM="
},
"exif-parser": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
"integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI="
},
"exif-reader": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/exif-reader/-/exif-reader-1.0.3.tgz",
"integrity": "sha512-tWMBj1+9jUSibgR/kv/GQ/fkR0biaN9GEZ5iPdf7jFeH//d2bSzgPoaWf1OfMv4MXFD4upwvpCCyeMvSyLWSfA=="
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"file-type": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"free-tex-packer-core": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/free-tex-packer-core/-/free-tex-packer-core-0.3.2.tgz",
"integrity": "sha512-CauShorQ77Pgm+u2YalXX6HyR0rnkuccS3lIKCZpnBeAjcUV6SXCwswUGS8cbrvS5/dIwM5UeqFu02X86PPMkg==",
"requires": {
"@jvitela/mustache-wax": "^1.0.1",
"jimp": "^0.2.28",
"maxrects-packer": "^2.5.0",
"mustache": "^2.3.0",
"tinify": "^1.5.0"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"global": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
"requires": {
"min-document": "^2.19.0",
"process": "^0.11.10"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"requires": {
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ip-regex": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz",
"integrity": "sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0="
},
"is-function": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
"integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"jimp": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz",
"integrity": "sha1-3VKak3GQ9ClXp5N9Gsw6d2KZbqI=",
"requires": {
"bignumber.js": "^2.1.0",
"bmp-js": "0.0.3",
"es6-promise": "^3.0.2",
"exif-parser": "^0.1.9",
"file-type": "^3.1.0",
"jpeg-js": "^0.2.0",
"load-bmfont": "^1.2.3",
"mime": "^1.3.4",
"mkdirp": "0.5.1",
"pixelmatch": "^4.0.0",
"pngjs": "^3.0.0",
"read-chunk": "^1.0.1",
"request": "^2.65.0",
"stream-to-buffer": "^0.1.0",
"tinycolor2": "^1.1.2",
"url-regex": "^3.0.0"
}
},
"jpeg-js": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz",
"integrity": "sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII="
},
"jpg-stream": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/jpg-stream/-/jpg-stream-1.1.2.tgz",
"integrity": "sha1-TboVnZ0ZNo3yExj2SM7pgKcr5Ac=",
"requires": {
"exif-reader": "^1.0.0",
"pixel-stream": "^1.0.3"
}
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
}
},
"load-bmfont": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz",
"integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==",
"requires": {
"buffer-equal": "0.0.1",
"mime": "^1.3.4",
"parse-bmfont-ascii": "^1.0.3",
"parse-bmfont-binary": "^1.0.5",
"parse-bmfont-xml": "^1.1.4",
"phin": "^2.9.1",
"xhr": "^2.0.1",
"xtend": "^4.0.0"
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"lzma-purejs": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/lzma-purejs/-/lzma-purejs-0.9.3.tgz",
"integrity": "sha1-yJF+iUsbTbXIZbkn34ZO3edZzN4=",
"requires": {
"amdefine": "~0.1.0",
"commander": "~2.0.0"
}
},
"maxrects-packer": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/maxrects-packer/-/maxrects-packer-2.7.2.tgz",
"integrity": "sha512-akd5IRLPqQeWlpJyRJyfYq86VB05zzbMIdyTgLxRk4z1H0A8g4oTJW31Yo6zO9piSRsFNYdzmgudW7J2g1gEhQ=="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.45.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
"integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w=="
},
"mime-types": {
"version": "2.1.28",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz",
"integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==",
"requires": {
"mime-db": "1.45.0"
}
},
"min-document": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
"requires": {
"dom-walk": "^0.1.0"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"mustache": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz",
"integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ=="
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-gzip": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz",
"integrity": "sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw=="
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"parse-bmfont-ascii": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
"integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU="
},
"parse-bmfont-binary": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz",
"integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY="
},
"parse-bmfont-xml": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz",
"integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==",
"requires": {
"xml-parse-from-string": "^1.0.0",
"xml2js": "^0.4.5"
}
},
"parse-headers": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz",
"integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA=="
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"phin": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
"integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA=="
},
"pixel-stream": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/pixel-stream/-/pixel-stream-1.0.3.tgz",
"integrity": "sha1-U+jFSyHVUIOTtTvLMrZKd1Xx+l4=",
"requires": {
"shallow-copy": "0.0.1"
}
},
"pixelmatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
"integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=",
"requires": {
"pngjs": "^3.0.0"
}
},
"png-stream": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/png-stream/-/png-stream-1.0.5.tgz",
"integrity": "sha1-W8cWh+qJWUJ+lQ5Sx8yknipvBMY=",
"requires": {
"bl": "^0.9.3",
"buffer-crc32": "^0.2.3",
"buffer-equal": "^0.0.1",
"pixel-stream": "^1.0.3"
}
},
"pngjs": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
},
"promise-nodeify": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/promise-nodeify/-/promise-nodeify-3.0.1.tgz",
"integrity": "sha512-ghsSuzZXJX8iO7WVec2z7GI+Xk/EyiD+JZK7AZKhUqYfpLa/Zs4ylUD+CwwnKlG6G3HnkUPMAi6PO7zeqGKssg=="
},
"proxying-agent": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/proxying-agent/-/proxying-agent-2.4.0.tgz",
"integrity": "sha512-b9vDqIcViJZVsWPpQlp9Py74u+Wqd0a+kMkkg7zX58mwNtrNbOChNlRTM7lUrlpiwNzyJCV8+5D8rnZYLDFh7Q=="
},
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"read-chunk": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz",
"integrity": "sha1-X2jKswfmY/GZk1J9m1icrORmEZQ="
},
"readable-stream": {
"version": "1.0.34",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"shallow-copy": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
"integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA="
},
"sshpk": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"stream-to": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/stream-to/-/stream-to-0.2.2.tgz",
"integrity": "sha1-hDBgmNhf25kLn6MAsbPM9V6O8B0="
},
"stream-to-array": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz",
"integrity": "sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=",
"requires": {
"any-promise": "^1.1.0"
}
},
"stream-to-buffer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz",
"integrity": "sha1-JnmdkDqyAlyb1VCsRxcbAPjdgKk=",
"requires": {
"stream-to": "~0.2.0"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"swf-extract": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/swf-extract/-/swf-extract-1.1.0.tgz",
"integrity": "sha1-DS6Q01lKFu9ly8hfuEEiRhdz9t0=",
"requires": {
"concat-frames": "^1.0.3",
"jpg-stream": "^1.1.1",
"lzma-purejs": "~0.9.3",
"png-stream": "^1.0.5",
"stream-to-array": "^2.3.0"
}
},
"tinify": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/tinify/-/tinify-1.5.0.tgz",
"integrity": "sha1-a6LqfognyEXSY947RvTbe61VWSA=",
"requires": {
"promise-nodeify": ">= 0.1",
"proxying-agent": ">= 2.1"
}
},
"tinycolor2": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
"integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"requires": {
"punycode": "^2.1.0"
}
},
"url-regex": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz",
"integrity": "sha1-260eDJ4p4QXdCx8J9oYvf9tIJyQ=",
"requires": {
"ip-regex": "^1.0.1"
}
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"xhr": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz",
"integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==",
"requires": {
"global": "~4.4.0",
"is-function": "^1.0.1",
"parse-headers": "^2.0.0",
"xtend": "^4.0.0"
}
},
"xml-parse-from-string": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
"integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig="
},
"xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
}
},
"xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
}
}
}

20
package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "nitroassetconverter",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@types/node": "^14.14.22",
"free-tex-packer-core": "^0.3.2",
"lodash": "^4.17.20",
"node-fetch": "^2.6.1",
"node-gzip": "^1.1.2",
"swf-extract": "^1.1.0",
"xml2js": "^0.4.23"
}
}

75
src/Main.ts Normal file
View File

@ -0,0 +1,75 @@
import Configuration from "./config/Configuration";
import FigureDownloader from "./downloaders/FigureDownloader";
import HabboAssetSWF from "./swf/HabboAssetSWF";
import SpriteSheetConverter from "./converters/util/SpriteSheetConverter";
import FigureConverter from "./converters/figure/FigureConverter";
import File from "./utils/File";
import FurnitureDownloader from "./downloaders/FurnitureDownloader";
import FurnitureConverter from "./converters/furniture/FurnitureConverter";
(async () => {
const config = new Configuration();
await config.init();
const outputFolderFigure = new File(config.getValue("output.folder.figure"));
if (!outputFolderFigure.isDirectory()) {
outputFolderFigure.mkdirs();
}
const outputFolderFurniture = new File(config.getValue("output.folder.furniture"));
if (!outputFolderFurniture.isDirectory()) {
outputFolderFurniture.mkdirs();
}
const spriteSheetConverter = new SpriteSheetConverter();
const figureConverter = new FigureConverter(config);
const furnitureConverter= new FurnitureConverter(config);
if (config.getBoolean("convert.figure")) {
const figureDownloader = new FigureDownloader(config);
await figureDownloader.download(async function (habboAssetSwf: HabboAssetSWF) {
console.log("Attempt parsing figure: " + habboAssetSwf.getDocumentClass());
try {
const spriteSheetType = await spriteSheetConverter.generateSpriteSheet(habboAssetSwf, outputFolderFigure.path, "figure");
if (spriteSheetType !== null)
await figureConverter.fromHabboAsset(habboAssetSwf, outputFolderFigure.path, "figure", spriteSheetType);
} catch (e) {
console.log("Figure error: " + habboAssetSwf.getDocumentClass());
}
});
}
let count = 0;
if (config.getBoolean("convert.furniture")) {
const furnitureDownloader = new FurnitureDownloader(config);
await furnitureDownloader.download(async function (habboAssetSwf: HabboAssetSWF, className: string) {
//console.log("Attempt parsing furniture: " + habboAssetSwf.getDocumentClass());
try {
const assetOuputFolder = new File(outputFolderFurniture.path + "/" + className);
if (!assetOuputFolder.isDirectory()) {
assetOuputFolder.mkdirs();
} else if (assetOuputFolder.list().length > 0) {
console.log("Furniture already exists or the directory is not empty!");
return;
}
const spriteSheetType = await spriteSheetConverter.generateSpriteSheet(habboAssetSwf, assetOuputFolder.path, "furniture");
if (spriteSheetType !== null) {
await furnitureConverter.fromHabboAsset(habboAssetSwf, assetOuputFolder.path, "furniture", spriteSheetType);
}
} catch (e) {
console.log("Furniture error: " + habboAssetSwf.getDocumentClass());
console.log(e);
}
});
console.log(`Parsed ${count} furnitures`)
}
console.log('finished!');
})()

View File

@ -0,0 +1,36 @@
const fs = require('fs/promises');
export default class Configuration {
private readonly _config: Map<string, string>;
constructor() {
this._config = new Map<string, string>();
}
async init() {
const content = await fs.readFile("/home/user/git/nitro-asset-converter-node/config.ini");
const config: string[] = content.toString("utf-8").split("\n");
for (const configEntry of config) {
const configEntrySplit = configEntry.split("=");
const configKey = configEntrySplit[0];
const configValue = configEntrySplit[1];
this._config.set(configKey, configValue);
}
}
public getBoolean(key: string): boolean {
return this._config.get(key) === "1";
}
public getValue(key: string, value: string = ""): string {
if (this._config.has(key)) {
// @ts-ignore
return this._config.get(key);
}
return value;
}
}

View File

@ -0,0 +1,62 @@
import HabboAssetSWF from "../../swf/HabboAssetSWF";
import {SpriteSheetType} from "../util/SpriteSheetTypes";
import DefineBinaryDataTag from "../../swf/tags/DefineBinaryDataTag";
import Configuration from "../../config/Configuration";
import FigureJsonMapper from "./FigureJsonMapper";
import {FigureJson} from "./FigureType";
import File from "../../utils/File";
const xml2js = require('xml2js');
const parser = new xml2js.Parser(/* options */);
const fs = require('fs').promises;
const {gzip} = require('node-gzip');
export default class FigureConverter {
private readonly _figureJsonMapper: FigureJsonMapper;
constructor(config: Configuration) {
this._figureJsonMapper = new FigureJsonMapper(config);
}
private getBinaryData(habboAssetSWF: HabboAssetSWF, type: string, documentNameTwice: boolean) {
let binaryName: string = habboAssetSWF.getFullClassName(type, documentNameTwice);
let tag = habboAssetSWF.getBinaryTagByName(binaryName);
if (tag === null) {
binaryName = habboAssetSWF.getFullClassNameSnake(type, documentNameTwice, true);
tag = habboAssetSWF.getBinaryTagByName(binaryName);
}
return tag;
}
private async convertXML2JSON(habboAssetSWF: HabboAssetSWF): Promise<FigureJson | null> {
const manifestXML: DefineBinaryDataTag | null = this.getBinaryData(habboAssetSWF, "manifest", false);
if (manifestXML !== null) {
const result = await parser.parseStringPromise(manifestXML.binaryData);
return this._figureJsonMapper.mapXML(habboAssetSWF, result);
}
return null;
}
public async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, spriteSheetType: SpriteSheetType) {
const manifestJson = await this.convertXML2JSON(habboAssetSWF);
if (manifestJson !== null) {
manifestJson.spritesheet = spriteSheetType;
const path = outputFolder + "/" + habboAssetSWF.getDocumentClass() + ".nitro";
const assetOuputFolder = new File(path);
if (assetOuputFolder.exists()) {
console.log("Furniture already exists or the directory is not empty!");
return;
}
const compressed = await gzip(JSON.stringify(manifestJson));
await fs.writeFile(path, compressed);
}
}
}

View File

@ -0,0 +1,85 @@
import Configuration from "../../config/Configuration";
import HabboAssetSWF from "../../swf/HabboAssetSWF";
import {FigureAsset, FigureAssets, FigureJson} from "./FigureType";
import FigureDownloader from "../../downloaders/FigureDownloader";
import SpriteSheetConverter from "../util/SpriteSheetConverter";
export default class FigureJsonMapper {
private static MUST_START_WITH: string = "h_";
private readonly _config: Configuration;
constructor(config: Configuration) {
this._config = config;
}
public mapXML(habboAssetSWF: HabboAssetSWF, manifestXML: any): FigureJson {
const name = habboAssetSWF.getDocumentClass();
return {
name: name,
type: FigureDownloader.types.get(name) as string,
assets: this.mapManifestXML(habboAssetSWF, manifestXML),
spritesheet: null as any
};
}
private mapManifestXML(habboAssetSWF: HabboAssetSWF, manifestXML: any): FigureAssets {
const assets: any = {};
const libraries = manifestXML.manifest.library;
for (const library of libraries) {
for (const assetObj of library.assets) {
for (const asset of assetObj.asset) {
const assetInfo = asset['$'];
const name = assetInfo.name;
if (name.startsWith(FigureJsonMapper.MUST_START_WITH)) {
let hasImage = false;
for (const imageTag of habboAssetSWF.imageTags()) {
if (imageTag.className.includes(name)) {
hasImage = true;
}
}
if (hasImage || !this._config.getBoolean("figure.skip.non-existing.asset.images")) {
const figureAsset: FigureAsset = {} as any;
figureAsset.name = name;
figureAsset.x = asset.param[0]['$'].value.split(',')[0];
figureAsset.y = asset.param[0]['$'].value.split(',')[1];
if (SpriteSheetConverter.imageSource.has(name)) {
figureAsset.source = SpriteSheetConverter.imageSource.get(name) as string;
}
assets[name] = figureAsset;
/*FigureJSON.Asset asset = new FigureJSON.Asset();
if (FFConverter.getConfig().getBoolean("figure.rotation.enabled")) {
String[] names = assetXML.getName().split("_");
if (this.isInteger(names[4])) {
String firstName = names[0] + "_" + names[1] + "_" + names[2] + "_" + names[3] + "_%ROTATION%_" + names[5];
Integer rotation = Integer.parseInt(names[4]);
if (rotation >= 0 && rotation < 8) {
if (assetRotations.containsKey(firstName)) {
assetRotations.get(firstName).add(rotation);
} else {
List<Integer> rotations = new ArrayList<Integer>();
rotations.add(rotation);
assetRotations.put(firstName, rotations);
}
}
}
}*/
} else {
console.log("Image " + name + " did not decompile for some reason");
}
}
}
}
}
return assets;
}
}

View File

@ -0,0 +1,20 @@
import {SpriteSheetType} from "../util/SpriteSheetTypes";
export interface FigureJson {
type: string,
name: string,
spritesheet: SpriteSheetType,
assets: FigureAssets
}
export interface FigureAssets {
[key: string]: FigureAsset
}
export interface FigureAsset {
name: string,
source: string,
x: number,
y: number,
flipH: boolean
}

View File

@ -0,0 +1,186 @@
import {
Action,
Direction,
Directions,
FurniAsset,
FurniAssets,
FurniJson,
Layer,
Visualization,
VisualizationLayers
} from "./FurniTypes";
import SpriteSheetConverter from "../util/SpriteSheetConverter";
export default class FurniJsonMapper {
private static readonly VISUALIZATION_DEFAULT_SIZE = 64;
private static readonly VISUALIZATION_ICON_SIZE = 1;
public mapXML(assetsXML: any, indexXML: any, logicXML: any, visualizationXML: any): FurniJson {
const furniJson: FurniJson = {} as any;
furniJson.assets = this.mapAssetsXML(assetsXML) as any;
this.mapIndexXML(indexXML, furniJson);
this.mapLogicXML(logicXML, furniJson);
this.mapVisualizationXML(visualizationXML, furniJson);
//console.log(furniJson);
return furniJson;
}
private mapAssetsXML(assetsXML: any): FurniAssets | null {
const assets: FurniAssets = {} as any;
for (const asset of assetsXML.assets.asset) {
const attributes = asset['$'];
if (!attributes.name.includes("_32_")) {
const furniAsset: FurniAsset = {} as any;
if (attributes.source !== undefined) {
furniAsset.source = attributes.source;
if (SpriteSheetConverter.imageSource.has(attributes.source)) {
furniAsset.source = SpriteSheetConverter.imageSource.get(attributes.source) as string;
}
}
if (SpriteSheetConverter.imageSource.has(attributes.name)) {
furniAsset.source = SpriteSheetConverter.imageSource.get(attributes.name) as string;
}
furniAsset.x = attributes.x;
furniAsset.y = attributes.y;
furniAsset.flipH = attributes.flipH === "1";
assets[attributes.name] = furniAsset;
}
}
return Object.keys(assets).length > 0 ? assets : null;
}
private mapIndexXML(indexXML: any, output: FurniJson) {
const attributes = indexXML.object['$'];
output.name = attributes.type;
output.logicType = attributes.logic;
output.visualizationType = attributes.visualization;
}
private mapLogicXML(logicXML: any, output: FurniJson) {
const objectData = logicXML.objectData;
const attributes = objectData['$'];
const model = objectData.model[0];
const dimensions = model.dimensions[0]['$'];
output.dimensions = {
x: parseInt(dimensions.x),
y: parseInt(dimensions.y),
z: parseFloat(dimensions.z)
}
const directions: Array<number> = [];
if (model.directions === undefined) {
directions.push(0);
} else {
for (const directionObj of model.directions) {
const direction = directionObj.direction;
for (const dir of direction) {
const dirAttributes = dir['$'];
directions.push(dirAttributes);
}
}
}
if (model.action !== undefined) {
const action: Action = {} as any;
}
output.directions = directions;
}
private mapVisualizationXML(visualizationXML: any, output: FurniJson) {
const visualizationsArray: Visualization[] = [];
const visualizationData = visualizationXML.visualizationData;
const attributes = visualizationData['$'];
const visualizations = visualizationData.graphics;
for (const visualizationArr of visualizations) {
for (const visualization of visualizationArr.visualization) {
const attributes = visualization['$'];
if (attributes.size == FurniJsonMapper.VISUALIZATION_DEFAULT_SIZE || attributes.size == FurniJsonMapper.VISUALIZATION_ICON_SIZE) {
const visualizationType: Visualization = {} as any;
visualizationType.angle = attributes.angle;
visualizationType.layerCount = attributes.layerCount;
visualizationType.size = attributes.size;
this.mapVisualizationLayersXML(visualization, visualizationType);
this.mapVisualizationDirectionXML(visualization, visualizationType);
visualizationsArray.push(visualizationType);
}
}
}
output.visualizations = visualizationsArray;
}
private mapVisualizationColorXML(visualization: any, visualizationType: Visualization) {
if (visualization.colors !== undefined && visualization.colors.length > 0) {
}
}
private mapVisualizationDirectionXML(visualization: any, visualizationType: Visualization) {
if (visualization.directions !== undefined && visualization.directions.length > 0) {
const directions: Directions = {} as any;
for (const directionParent of visualization.directions) {
for (const direction of directionParent.direction) {
const attributes = direction['$'];
const directionType: Direction = {} as any;
directionType.id = attributes.id;
if (direction.layers !== undefined && direction.layers.length > 0) {
directionType.layers = this.generateLayers(direction, visualizationType);
}
directions[directionType.id] = directionType;
}
}
if (Object.keys(directions).length > 0) visualizationType.directions = directions;
}
}
private mapVisualizationLayersXML(visualization: any, visualizationType: Visualization) {
if (visualization.layers !== undefined && visualization.layers.length > 0) {
for (const layerEntry of visualization.layers) {
const layer = layerEntry.layer;
visualizationType.layers = this.generateLayers(layer, visualizationType);
}
}
}
private generateLayers(visualization: any, visualizationType: Visualization): VisualizationLayers {
const visualizationLayers: VisualizationLayers = {} as any;
for (const layerEntry of visualization.layers) {
const layer = layerEntry.layer;
const layerAttributes = layer['$'];
const layerType: Layer = {
id: layerAttributes.id,
alpha: layerAttributes.alpha,
ink: layerAttributes.ink,
tag: layerAttributes.tag,
x: layerAttributes.x,
y: layerAttributes.y,
z: layerAttributes.z,
} as any;
if (layerAttributes.ignoreMouse !== undefined) layerType.ignoreMouse = layerAttributes.ignoreMouse === '1';
visualizationLayers[layerAttributes.id] = layerType;
}
return visualizationLayers;
}
}

View File

@ -0,0 +1,170 @@
import {SpriteSheetType} from "../util/SpriteSheetTypes";
export interface FurniJson {
type: string,
name: string,
visualizationType: string,
logicType: string,
maskType: string,
credits: string,
spritesheet: SpriteSheetType,
dimensions: Dimensions,
action: Action;
directions: number[],
assets: FurniAssets,
visualizations: Visualization[]
}
export interface Visualization {
layerCount: number,
angle: number,
size: number,
layers: VisualizationLayers,
directions: Directions,
colors: Colors,
animations: Animations,
postures: Postures;
gestures: Gestures
}
export interface Gestures {
[key: string]: Gesture
}
export interface Gesture {
id: string,
animationId: number
}
export interface Postures {
[key: string]: Posture
}
export interface Posture {
id: string,
animationId: number
}
export interface Offset {
direction: number,
x: number,
y: number
}
export interface Frame {
id: number,
x: number,
y: number,
randomX: number,
randomY: number,
offsets: Offset[]
}
export interface Frames {
[key: number]: Frame
}
export interface FrameSequence {
loopCount: number,
random: number,
frames: Frames
}
export interface FrameSequences {
[key: number]: FrameSequence
}
export interface AnimationLayer {
id: number,
loopCount: number,
frameRepeat: number,
random: number,
frameSequences: FrameSequences
}
export interface AnimationLayers {
[key: number]: AnimationLayer
}
export interface Animations {
[key: number]: Animation
}
export interface Animation {
id: number,
transitionTo: number,
transitionFrom: number,
immediateChangeFrom: string,
layers: AnimationLayers;
}
export interface ColorLayers {
[key: number]: ColorLayer
}
export interface ColorLayer {
id: number,
color: number
}
export interface Colors {
[key: number]: Color
}
export interface Color {
id: number;
layers: ColorLayers;
}
export interface Directions {
[key: number]: Direction
}
export interface Direction {
id: number;
layers: VisualizationLayers;
}
export interface VisualizationLayers {
[key: number]: Layer
}
export interface Layer {
id: number,
alpha: number,
x: number,
y: number,
z: number,
ink: string,
tag: string,
ignoreMouse: boolean
}
export interface Action {
link: string,
startState: number
}
export interface Dimensions {
x: number,
y: number,
z: number
}
export interface FurniAssets {
[key: string]: FurniAsset
}
export interface FurniAsset {
source: string,
x: number,
y: number,
flipH: boolean
}

View File

@ -0,0 +1,85 @@
import HabboAssetSWF from "../../swf/HabboAssetSWF";
import {SpriteSheetType} from "../util/SpriteSheetTypes";
import DefineBinaryDataTag from "../../swf/tags/DefineBinaryDataTag";
import Configuration from "../../config/Configuration";
import FurniJsonMapper from "./FurniJsonMapper";
import {FurniJson} from "./FurniTypes";
const xml2js = require('xml2js');
const parser = new xml2js.Parser(/* options */);
const fs = require('fs').promises;
export default class FurnitureConverter {
private readonly _furniJsonMapper: FurniJsonMapper;
constructor(config: Configuration) {
this._furniJsonMapper = new FurniJsonMapper();
}
private static getBinaryData(habboAssetSWF: HabboAssetSWF, type: string, documentNameTwice: boolean) {
let binaryName: string = habboAssetSWF.getFullClassName(type, documentNameTwice);
let tag = habboAssetSWF.getBinaryTagByName(binaryName);
if (tag === null) {
binaryName = habboAssetSWF.getFullClassNameSnake(type, documentNameTwice, true);
tag = habboAssetSWF.getBinaryTagByName(binaryName);
}
return tag;
}
private static async getAssetsXML(habboAssetSWF: HabboAssetSWF): Promise<any> {
const binaryData: DefineBinaryDataTag | null = FurnitureConverter.getBinaryData(habboAssetSWF, "assets", true);
if (binaryData !== null) {
return await parser.parseStringPromise(binaryData.binaryData);
}
return null;
}
private static async getLogicXML(habboAssetSWF: HabboAssetSWF): Promise<any> {
const binaryData: DefineBinaryDataTag | null = FurnitureConverter.getBinaryData(habboAssetSWF, "logic", true);
if (binaryData !== null) {
return await parser.parseStringPromise(binaryData.binaryData);
}
return null;
}
private static async getIndexXML(habboAssetSWF: HabboAssetSWF): Promise<any> {
const binaryData: DefineBinaryDataTag | null = FurnitureConverter.getBinaryData(habboAssetSWF, "index", true);
if (binaryData !== null) {
return await parser.parseStringPromise(binaryData.binaryData);
}
return null;
}
private static async getVisualizationXML(habboAssetSWF: HabboAssetSWF): Promise<any> {
const binaryData: DefineBinaryDataTag | null = FurnitureConverter.getBinaryData(habboAssetSWF, "visualization", true);
if (binaryData !== null) {
return await parser.parseStringPromise(binaryData.binaryData);
}
return null;
}
private async convertXML2JSON(habboAssetSWF: HabboAssetSWF): Promise<FurniJson | null> {
const assetXml = await FurnitureConverter.getAssetsXML(habboAssetSWF);
const logicXml = await FurnitureConverter.getLogicXML(habboAssetSWF);
const indexXml = await FurnitureConverter.getIndexXML(habboAssetSWF);
const visualizationXml = await FurnitureConverter.getVisualizationXML(habboAssetSWF);
return this._furniJsonMapper.mapXML(assetXml, indexXml, logicXml, visualizationXml);
}
public async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, spriteSheetType: SpriteSheetType) {
const furnitureJson = await this.convertXML2JSON(habboAssetSWF);
if (furnitureJson !== null) {
furnitureJson.spritesheet = spriteSheetType;
await fs.writeFile(outputFolder + "/" + habboAssetSWF.getDocumentClass() + ".json", JSON.stringify(furnitureJson));
}
}
}

View File

@ -0,0 +1,103 @@
import {SpriteSheetType} from "./SpriteSheetTypes";
const fs = require('fs').promises;
let {packAsync} = require("free-tex-packer-core");
import HabboAssetSWF from "../../swf/HabboAssetSWF";
import SymbolClassTag from "../../swf/tags/SymbolClassTag";
import ImageTag from "../../swf/tags/ImageTag";
export default class SpriteSheetConverter {
public static imageSource: Map<String, String> = new Map<String, String>();
public async generateSpriteSheet(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string): Promise<SpriteSheetType | null> {
const tagList: Array<SymbolClassTag> = habboAssetSWF.symbolTags();
const names: Array<string> = new Array<string>();
const tags: Array<number> = new Array<number>();
for (const tag of tagList) {
names.push(...tag.names);
tags.push(...tag.tags);
}
const images: Array<{ path: String, contents: Buffer }> = new Array<{ path: String, contents: Buffer }>();
const imageTags: Array<ImageTag> = habboAssetSWF.imageTags();
for (const imageTag of imageTags) {
if (tags.includes(imageTag.characterID)) {
for (let i = 0; i < tags.length; i++) {
if (tags[i] == imageTag.characterID) {
if (
(names[i].includes("_64_") && type === "furniture") ||
(names[i].includes("_icon_") && type === "furniture") ||
(names[i].includes("_h_") && (type === "figure" || type === "effect")) ||
(names[i].includes("_64_") && type === "pet")) {
if (names[i] !== imageTag.className) {
SpriteSheetConverter.imageSource.set(names[i].substring(habboAssetSWF.getDocumentClass().length + 1), imageTag.className.substring(habboAssetSWF.getDocumentClass().length + 1));
if ((imageTag.className.includes("_32_") && type === "furniture") ||
(imageTag.className.includes("_sh_") && (type === "figure" || type === "effect")) ||
(imageTag.className.includes("_32_") && type === "pet")) {
images.push({
path: imageTag.className,
contents: imageTag.imgData
});
}
}
}
}
}
}
if ((imageTag.className.includes("_64_") && type === "furniture") ||
(imageTag.className.includes("_icon_") && type === "furniture") ||
(imageTag.className.includes("_h_") && (type === "figure" || type === "effect")) ||
(imageTag.className.includes("_64_") && type === "pet")) {
images.push({
path: imageTag.className,
contents: imageTag.imgData
});
}
}
if (images.length === 0) {
return null;
}
return await this.packImages(habboAssetSWF.getDocumentClass(), outputFolder + "/", images);
}
async packImages(documentClass: string, outputFolder: string, images: Array<{ path: String, contents: Buffer }>): Promise<SpriteSheetType | null> {
let options = {
textureName: documentClass,
width: 1024,
height: 1024,
fixedSize: false,
allowRotation: true,
detectIdentical: true,
allowTrim: true,
exporter: "Pixi"
};
let spriteSheetType: SpriteSheetType | null = null;
let base64 = "";
try {
const files = await packAsync(images, options);
for (let item of files) {
if (item.name.endsWith(".json")) {
spriteSheetType = JSON.parse(item.buffer.toString('utf8'));
} else {
base64 = item.buffer.toString("base64");
//await fs.writeFile(outputFolder + item.name, item.buffer);
}
}
if (spriteSheetType === null) throw new Error("Failed to parse SpriteSheet. " + images[0].path);
} catch (error) {
console.log("Error: " + error);
}
if (spriteSheetType !== null) spriteSheetType.meta.imageb64 = base64;
return spriteSheetType;
}
}

View File

@ -0,0 +1,55 @@
export interface SpriteSheetType {
frames: SpriteSheetFrames,
meta: SpriteSheetMeta
}
export interface SpriteSheetFrames {
[key: string]: SpriteSheetFrame
}
export interface SpriteSheetFrame {
frame: SpriteSheetFrameDimensions,
rotated: boolean,
trimmed: boolean,
spriteSourceSize: SpriteSourceSize
sourceSize: SourceSize,
pivot: FramePivot
}
export interface FramePivot {
x: number,
y: number
}
export interface SourceSize {
w: number,
h: number
}
export interface SpriteSourceSize {
x: number,
y: number,
w: number,
h: number
}
export interface SpriteSheetFrameDimensions {
x: number,
y: number,
w: number,
h: number
}
export interface SpriteSheetMeta {
app: string,
version: string,
imageb64: string,
format: string,
size: SpriteSheetSize,
scale: number
}
export interface SpriteSheetSize {
w: number,
h: number
}

View File

@ -0,0 +1,69 @@
import Configuration from "../config/Configuration";
import HabboAssetSWF from "../swf/HabboAssetSWF";
import File from "../utils/File";
const fs = require("fs");
const fetch = require('node-fetch');
const xml2js = require('xml2js');
const parser = new xml2js.Parser(/* options */);
const util = require('util');
const readFile = util.promisify(fs.readFile);
export default class FigureDownloader {
private readonly _config: Configuration;
constructor(config: Configuration) {
this._config = config;
}
public static types: Map<String, String> = new Map<String, String>();
public async download(callback: (habboAssetSwf: HabboAssetSWF) => Promise<void>) {
const outputFolderFigure = this._config.getValue("output.folder.figure");
const figureMap = await this.parseFigureMap();
const map = figureMap.map;
for (const lib of map.lib) {
const info = lib['$'];
const className: string = info.id.split("\\*")[0];
if (className === "hh_human_fx") {
continue;
}
const assetOutputFolder = new File(outputFolderFigure + "/" + className);
if (assetOutputFolder.exists()) {
continue;
}
if (!FigureDownloader.types.has(className)) {
if (className !== "jacket_U_snowwar4_team1" &&
className !== "jacket_U_snowwar4_team2") { //TODO: Figure out why snowstorm assets aren't converting...
const url = this._config.getValue("dynamic.download.url.figure").replace("%className%", className);
if (!fs.existsSync(url)) {
console.log("SWF File does not exist: " + url);
return;
}
const buffer: Buffer = await readFile(url);
const habboAssetSWF = new HabboAssetSWF(buffer);
await habboAssetSWF.setupAsync();
FigureDownloader.types.set(className, lib.part[0]['$'].type);
await callback(habboAssetSWF);
}
}
}
}
async parseFigureMap() {
const figureMapPath = this._config.getValue("figuremap.url");
const figureFetch = await fetch(figureMapPath);
const figureMap = await figureFetch.text();
return await parser.parseStringPromise(figureMap);
}
}

View File

@ -0,0 +1,92 @@
import HabboAssetSWF from "../swf/HabboAssetSWF";
import Configuration from "../config/Configuration";
import {type} from "os";
import File from "../utils/File";
const fs = require("fs");
const fetch = require('node-fetch');
const xml2js = require('xml2js');
const parser = new xml2js.Parser(/* options */);
const util = require('util');
const readFile = util.promisify(fs.readFile);
export default class FurnitureDownloader {
private readonly _config: Configuration;
constructor(config: Configuration) {
this._config = config;
}
public async download(callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>) {
const outputFolderFurniture = new File(this._config.getValue("output.folder.furniture"));
const furniDataObj = await this.parseFurniData();
const furniData = furniDataObj.furnidata;
const roomitemtypes = furniData.roomitemtypes;
const wallitemtypes = furniData.wallitemtypes;
for (const roomItem of roomitemtypes) {
for (const furnitype of roomItem.furnitype) {
const attributes = furnitype['$'];
const className = attributes.classname.split("\*")[0];
const revision = furnitype.revision[0];
const assetOuputFolder = new File(outputFolderFurniture.path + "/" + className);
if (assetOuputFolder.isDirectory()) {
continue;
}
await this.extractFurniture(revision, className, callback);
}
}
for (const wallItem of wallitemtypes) {
for (const furnitype of wallItem.furnitype) {
const attributes = furnitype['$'];
const className = attributes.classname.split("\*")[0];
const revision = furnitype.revision[0];
const assetOuputFolder = new File(outputFolderFurniture + "/" + className);
if (assetOuputFolder.isDirectory()) {
continue;
}
await this.extractFurniture(revision, className, callback);
}
}
}
async extractFurniture(revision: string, className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>) {
if (className !== "rare_dragonlamp") return;
const url = this._config.getValue("dynamic.download.url.furniture").replace("%revision%", revision).replace("%className%", className);
const file = new File(url);
if (!file.exists()) {
console.log("SWF File does not exist: " + file.path);
return;
}
try {
const buffer: Buffer = await readFile(url);
const habboAssetSWF = new HabboAssetSWF(buffer);
await habboAssetSWF.setupAsync();
await callback(habboAssetSWF, className);
} catch (e) {
console.log("Error with furniture: " + url);
console.log(e);
}
}
async parseFurniData() {
const furniDataFetch = this._config.getValue("furnidata.url");
const furniFetch = await fetch(furniDataFetch);
const furniData = await furniFetch.text();
return await parser.parseStringPromise(furniData);
}
}

188
src/swf/HabboAssetSWF.ts Normal file
View File

@ -0,0 +1,188 @@
import SymbolClassTag from "./tags/SymbolClassTag";
import ImageTag from "./tags/ImageTag";
import ITag from "./tags/ITag";
import CustomIterator from "../utils/CustomIterator";
import CharacterTag from "./tags/CharacterTag";
import DefineBinaryDataTag from "./tags/DefineBinaryDataTag";
const {readFromBufferP, extractImages} = require('swf-extract');
export interface Tag {
code: number,
length: number,
rawData: Buffer
}
export interface SWFFrameSize {
x: number,
y: number,
width: number,
height: number
}
export interface SWFFileLength {
compressed: number,
uncompressed: number
}
export interface SWF {
tags: Array<Tag>,
version: number,
fileLength: SWFFileLength,
frameSize: SWFFrameSize,
frameRate: number,
frameCount: number
}
export default class HabboAssetSWF {
private swf: SWF | null;
private readonly _tags: Array<ITag>;
private _documentClass: string | null = null;
constructor(private readonly _buffer: Buffer) {
this.swf = null;
this._tags = new Array<ITag>();
}
public async setupAsync() {
this.swf = await readFromBufferP(this._buffer);
if (this.swf === null) throw new Error("SWF Can't be null!");
for (const tag of this.swf.tags) {
if (tag.code === 76) {
this._tags.push(new SymbolClassTag(tag));
}
if (tag.code === 87) {
this._tags.push(new DefineBinaryDataTag(tag));
}
}
const images = await Promise.all(extractImages(this.swf.tags));
for (const image of images) {
const imgObj: any = image;
this._tags.push(new ImageTag({
code: imgObj.code,
characterID: imgObj.characterId,
imgType: imgObj.imgType,
imgData: imgObj.imgData
}));
}
this.assignClassesToSymbols();
}
public imageTags(): Array<ImageTag> {
return this._tags.filter((tag: ITag) => tag instanceof ImageTag).map(x => x as ImageTag);
}
public symbolTags(): Array<SymbolClassTag> {
return this._tags.filter((tag: ITag) => tag instanceof SymbolClassTag).map(x => x as SymbolClassTag)
}
private binaryTags(): Array<DefineBinaryDataTag> {
return this._tags.filter((tag: ITag) => tag instanceof DefineBinaryDataTag).map(x => x as DefineBinaryDataTag);
}
public getBinaryTagByName(name: string): DefineBinaryDataTag | null {
const streamTag = this.binaryTags()
.filter(tag => tag.className === name)[0];
if (streamTag === undefined) return null;
return streamTag;
}
private assignClassesToSymbols() {
const classes: Map<number, string> = new Map();
let iterator: CustomIterator<ITag> = new CustomIterator(this._tags);
while (true) {
let t: ITag;
do {
if (!iterator.hasNext()) {
iterator = new CustomIterator(this._tags);
while (iterator.hasNext()) {
t = iterator.next();
if (t instanceof CharacterTag) {
const ct = t as CharacterTag;
if (classes.has(ct.characterId)) {
// @ts-ignore
ct.className = classes.get(ct.characterId);
}
}
}
return;
}
t = iterator.next();
} while (!(t instanceof SymbolClassTag));
const sct = t as SymbolClassTag;
for (let i = 0; i < sct.tags.length; ++i) {
if (!classes.has(sct.tags[i]) && !Array.from(classes.values()).includes(sct.names[i])) {
classes.set(sct.tags[i], sct.names[i]);
}
}
}
}
public getFullClassName(type: string, documentNameTwice: boolean): string {
return this.getFullClassNameSnake(type, documentNameTwice, false);
}
public getFullClassNameSnake(type: string, documentNameTwice: boolean, snakeCase: boolean): string {
if (this.swf === null) throw new Error("SWF Can't be null!");
let result: string = this.getDocumentClass() + "_";
if (documentNameTwice) {
if (snakeCase) {
//result += CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, this.swf.getDocumentClass()) + "_";
} else {
result += this.getDocumentClass() + "_";
}
}
return result + type;
}
public getDocumentClass(): string {
if (this._documentClass !== null) return this._documentClass;
let iterator: CustomIterator<ITag> = new CustomIterator(this._tags);
while (true) {
let t: ITag;
do {
if (!iterator.hasNext()) {
return "";
}
t = iterator.next();
} while (!(t instanceof SymbolClassTag));
const sc = t as SymbolClassTag;
for (let i = 0; i < sc.tags.length; ++i) {
if (sc.tags[i] == 0) {
this._documentClass = sc.names[i];
return this._documentClass;
}
}
}
}
public setDocumentClass(documentClass: string) {
this._documentClass = documentClass;
}
}

View File

@ -0,0 +1,20 @@
export default abstract class CharacterTag {
private _className: string = "";
private _characterId: number = -1;
get className(): string {
return this._className;
}
set className(value: string) {
this._className = value;
}
get characterId(): number {
return this._characterId;
}
set characterId(value: number) {
this._characterId = value;
}
}

View File

@ -0,0 +1,43 @@
import {Tag} from "../HabboAssetSWF";
import ITag from "./ITag";
import CharacterTag from "./CharacterTag";
const {SWFBuffer} = require('swf-extract/swf-buffer');
export default class DefineBinaryDataTag extends CharacterTag implements ITag {
private readonly _tag: number;
private readonly _reserved: number;
private readonly _binaryData: string;
constructor(tag: Tag) {
super();
const swfBuffer = new SWFBuffer(tag.rawData);
this._tag = swfBuffer.readUIntLE(16);
this._reserved = swfBuffer.readUIntLE(32);
const start = 6; //short 2 + int 4
const end = tag.rawData.length;
const binary = tag.rawData.slice(start, end);
this._binaryData = binary.toString("utf-8");
this.characterId = this._tag;
}
get code(): number {
return 87;
}
get tag(): number {
return this._tag;
}
get reserved(): number {
return this._reserved;
}
get binaryData(): string {
return this._binaryData;
}
}

3
src/swf/tags/ITag.ts Normal file
View File

@ -0,0 +1,3 @@
export default interface ITag {
code: number;
}

37
src/swf/tags/ImageTag.ts Normal file
View File

@ -0,0 +1,37 @@
import ITag from "./ITag";
import CharacterTag from "./CharacterTag";
export default class ImageTag extends CharacterTag implements ITag {
private readonly _code: number;
private readonly _characterID: number;
private readonly _imgType: string;
private readonly _imgData: Buffer;
constructor(image: { code: number, characterID: number, imgType: string, imgData: Buffer }) {
super();
this._code = image.code;
this._characterID = image.characterID;
this._imgType = image.imgType;
this._imgData = image.imgData;
this.characterId = this._characterID;
}
get code(): number {
return this._code;
};
get characterID(): number {
return this._characterID;
}
get imgType(): string {
return this._imgType;
}
get imgData(): Buffer {
return this._imgData;
}
}

View File

@ -0,0 +1,42 @@
import {Tag} from "../HabboAssetSWF";
import ITag from "./ITag";
const {SWFBuffer} = require('swf-extract/swf-buffer');
export default class SymbolClassTag implements ITag {
private readonly _tags: Array<number>;
private readonly _names: Array<string>;
constructor(tag: Tag) {
this._tags = [];
this._names = [];
this.init(tag);
}
init(tag: Tag) {
const swfBuffer = new SWFBuffer(tag.rawData);
const numSymbols = swfBuffer.readUIntLE(16);
for (let i = 0; i < numSymbols; i++) {
const tagId = swfBuffer.readUIntLE(16);
const tagName = swfBuffer.readString("utf-8");
this._tags.push(tagId);
this._names.push(tagName);
}
}
get tags(): Array<number> {
return this._tags;
}
get names(): Array<string> {
return this._names;
}
get code(): number {
return 76;
}
}

View File

@ -0,0 +1,54 @@
export default class CustomIterator<TType> {
private idx: number;
private readonly top: number;
private readonly keys: Array<any>;
private readonly arr: boolean;
private readonly collection: Array<TType>;
constructor(collection: Array<TType>) {
if (this.dontIterate(collection)) {
throw new Error('Oh you nasty man, I won\'t iterate over that (' + collection + ')!');
}
this.arr = this.isArray(collection);
this.idx = 0;
this.top = 0;
this.keys = [];
if (this.arr) {
this.top = collection.length;
} else {
for (const prop in collection) {
this.keys.push(prop);
}
}
this.collection = collection;
}
isArray(candidate: any) {
return candidate &&
typeof candidate === 'object' &&
typeof candidate.length === 'number' &&
typeof candidate.splice === 'function' &&
!(candidate.propertyIsEnumerable('length'));
}
dontIterate(collection: any) {
// put some checks chere for stuff that isn't iterable (yet)
return (!collection || typeof collection === 'number' || typeof collection === 'boolean');
}
public next(): TType {
if (!this.hasNext()) {
throw new Error('Oh you nasty man. I have no more elements.');
}
const elem = this.arr ? this.collection[this.idx] : this.collection[this.keys[this.idx]];
++this.idx;
return elem;
};
public hasNext() {
return this.arr ? this.idx <= this.top : this.idx <= this.keys.length;
};
}

33
src/utils/File.ts Normal file
View File

@ -0,0 +1,33 @@
const fs = require("fs");
export default class File {
private readonly _path: string;
constructor(path: string) {
this._path = path;
}
public exists(): boolean {
return fs.existsSync(this._path);
}
public mkdirs(): void {
return fs.mkdirSync(this._path);
}
public list(): string[] {
const test = fs.readdirSync(this._path);
console.log(test);
return test;
}
public isDirectory(): boolean {
return this.exists() && fs.lstatSync(this._path).isDirectory()
}
get path(): string {
return this._path;
}
}

14
tsconfig.json Normal file
View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es6"],
"allowJs": true,
"outDir": "dist",
"rootDir": "src",
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true
}
}