2020-06-28 06:33:20 +09:00
|
|
|
<template>
|
2021-06-11 07:48:15 +09:00
|
|
|
<div>
|
|
|
|
<ButtonComponent
|
|
|
|
label="Download"
|
|
|
|
:onClick="onClick"
|
|
|
|
:disabled="(!codes || codes.length === 0) && !stageLoaderCode"
|
2022-01-17 03:17:07 +09:00
|
|
|
/>
|
2021-06-11 07:48:15 +09:00
|
|
|
</div>
|
2020-06-28 06:33:20 +09:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2020-06-30 05:30:29 +09:00
|
|
|
// Data
|
|
|
|
import gameVersions from '../data/gameVersions.json';
|
|
|
|
|
2020-07-10 12:21:13 +09:00
|
|
|
// Util
|
2022-04-23 23:29:38 +09:00
|
|
|
import { translate, translateCode } from '../i18n/localeHelper';
|
2020-07-10 12:21:13 +09:00
|
|
|
|
2022-04-08 12:48:37 +09:00
|
|
|
// customizable code
|
|
|
|
import codegens from './codes/codegen.js';
|
|
|
|
|
2020-06-28 06:33:20 +09:00
|
|
|
export default {
|
|
|
|
props: {
|
|
|
|
codes: { type: Array },
|
2020-06-28 10:45:44 +09:00
|
|
|
stageLoaderCode: { type: String },
|
2020-06-28 06:33:20 +09:00
|
|
|
format: { type: String },
|
2020-06-28 10:45:44 +09:00
|
|
|
versionIdentifier: { type: String },
|
2020-06-28 06:33:20 +09:00
|
|
|
},
|
2021-06-11 07:48:15 +09:00
|
|
|
data() {
|
2022-01-17 03:17:07 +09:00
|
|
|
return {};
|
2021-06-11 07:48:15 +09:00
|
|
|
},
|
2020-06-28 06:33:20 +09:00
|
|
|
methods: {
|
|
|
|
onClick() {
|
2020-06-28 10:45:44 +09:00
|
|
|
if (!(this.codes || this.codes.length === 0) && !this.stageLoaderCode) {
|
2020-06-28 06:33:20 +09:00
|
|
|
return;
|
|
|
|
}
|
2020-06-28 10:45:44 +09:00
|
|
|
const c = [...(this.codes ?? [])];
|
|
|
|
|
|
|
|
if (this.stageLoaderCode)
|
|
|
|
c.push({
|
2020-06-29 00:35:18 +09:00
|
|
|
title: 'Stage List Loader',
|
|
|
|
author: 'Noki Doki',
|
|
|
|
date: '-',
|
|
|
|
version: '',
|
2020-06-28 10:45:44 +09:00
|
|
|
source: this.stageLoaderCode,
|
|
|
|
});
|
2020-06-28 06:33:20 +09:00
|
|
|
|
2020-07-01 13:14:05 +09:00
|
|
|
try {
|
2020-07-01 13:19:15 +09:00
|
|
|
window._paq.push([
|
2020-07-01 13:14:05 +09:00
|
|
|
'trackEvent',
|
|
|
|
'GCT Generator',
|
|
|
|
'Code Download',
|
|
|
|
JSON.stringify({
|
|
|
|
gameVersion: this.versionIdentifier,
|
|
|
|
format: this.format,
|
2020-07-01 14:26:48 +09:00
|
|
|
codes: c.map((code) => ({
|
2020-07-01 13:14:05 +09:00
|
|
|
title: code.title,
|
|
|
|
version: code.version,
|
|
|
|
})),
|
|
|
|
}),
|
|
|
|
]);
|
|
|
|
} catch {}
|
|
|
|
|
2020-07-01 14:26:48 +09:00
|
|
|
const fileName = gameVersions.find((v) => v.identifier === this.versionIdentifier).version;
|
2020-06-30 05:30:29 +09:00
|
|
|
|
2022-04-08 12:48:37 +09:00
|
|
|
// apply customizable codes
|
|
|
|
for (const code of c) {
|
|
|
|
const codegen = codegens[code.id];
|
|
|
|
if (codegen) {
|
|
|
|
code.source = codegen(this.versionIdentifier);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate file
|
2022-04-23 23:29:38 +09:00
|
|
|
const codeSize = c.reduce((a, e) => a+e.source.length, 0)/2 + 16; // 8(00D0)+8(F000)
|
2022-10-15 19:31:18 +09:00
|
|
|
// console.log(codeSize, c);
|
2020-06-28 06:33:20 +09:00
|
|
|
switch (this.format) {
|
2020-06-29 00:35:18 +09:00
|
|
|
case 'gct':
|
2022-04-23 23:29:38 +09:00
|
|
|
this.alertGCTCodeSize(codeSize);
|
2020-07-03 10:53:48 +09:00
|
|
|
this.generateGCT(c, fileName);
|
2020-06-28 06:33:20 +09:00
|
|
|
break;
|
2020-06-29 00:35:18 +09:00
|
|
|
case 'dolphin':
|
2022-04-23 23:29:38 +09:00
|
|
|
this.alertDolphinCodeSize(codeSize);
|
2020-07-03 10:53:48 +09:00
|
|
|
this.generateDolphinINI(c, fileName);
|
2020-06-28 06:33:20 +09:00
|
|
|
break;
|
2020-06-29 00:35:18 +09:00
|
|
|
case 'gcm':
|
2022-04-23 23:29:38 +09:00
|
|
|
this.alertDolphinCodeSize(codeSize);
|
2020-07-03 10:53:48 +09:00
|
|
|
this.generateCheatManagerTXT(c, fileName);
|
2020-06-28 06:33:20 +09:00
|
|
|
break;
|
2022-11-16 17:20:30 +09:00
|
|
|
case 'gci+gct':
|
|
|
|
this.generateGCI(c, fileName) &&
|
|
|
|
this.generateGCT(this.getGCILoader(), fileName);
|
|
|
|
break;
|
|
|
|
case 'gci+dolphin':
|
|
|
|
this.generateGCI(c, fileName) &&
|
|
|
|
this.generateDolphinINI(this.getGCILoader(), fileName);
|
|
|
|
break;
|
|
|
|
case 'gci+gcm':
|
|
|
|
this.generateGCI(c, fileName) &&
|
|
|
|
this.generateCheatManagerTXT(this.getGCILoader(), fileName);
|
|
|
|
break;
|
2022-11-11 21:58:24 +09:00
|
|
|
case 'gci':
|
|
|
|
this.generateGCI(c, fileName);
|
|
|
|
break;
|
2020-06-28 06:33:20 +09:00
|
|
|
}
|
2020-06-28 10:45:44 +09:00
|
|
|
},
|
2022-04-23 23:29:38 +09:00
|
|
|
alertGCTCodeSize(size) {
|
|
|
|
if (size > 5000) {
|
|
|
|
alert(translate('generatorconfig.alert.gct', this.$lang).replaceAll('{size}', size));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
alertDolphinCodeSize(size) {
|
|
|
|
if (size > 3272) { // 0x3000-0x2338
|
|
|
|
// excluding header+footer
|
|
|
|
alert(translate('generatorconfig.alert.dolphin', this.$lang).replaceAll('{size}', size-16));
|
|
|
|
}
|
|
|
|
},
|
2022-11-16 17:20:30 +09:00
|
|
|
getGCILoader() {
|
|
|
|
const {codes} = gameVersions.find((v) => v.identifier === this.versionIdentifier);
|
|
|
|
const code = codes.find(code => code.id === 'GCILoader');
|
|
|
|
return [code];
|
|
|
|
},
|
2020-07-03 10:53:48 +09:00
|
|
|
generateGCT(codes, version) {
|
|
|
|
let code = '00D0C0DE00D0C0DE';
|
|
|
|
codes.forEach((c) => (code += c.source));
|
2021-01-15 08:39:49 +09:00
|
|
|
code += 'F000000000000000';
|
2020-07-03 10:53:48 +09:00
|
|
|
|
|
|
|
let rawData = new Uint8Array(code.length / 2);
|
|
|
|
|
|
|
|
for (let x = 0; x < rawData.length; x++) {
|
|
|
|
rawData[x] = parseInt(code.substr(x * 2, 2), 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.downloadFile(rawData, `${version}.gct`);
|
|
|
|
},
|
|
|
|
generateDolphinINI(codes, version) {
|
|
|
|
let data = 'Paste the following on top of your games .ini file:\r\n[Gecko]';
|
|
|
|
|
|
|
|
codes.forEach((code) => {
|
2020-07-10 12:21:13 +09:00
|
|
|
const codeTitle =
|
|
|
|
typeof code.title === 'string' ? code.title : translateCode(code, this.$lang).title;
|
|
|
|
|
|
|
|
data += `\r\n$${codeTitle} (${code.date}) [${code.author}]\r\n`;
|
2020-07-03 10:53:48 +09:00
|
|
|
data += code.source
|
|
|
|
.match(/.{8}/g)
|
|
|
|
.join(' ')
|
|
|
|
.replace(/(.{17})./g, '$1\r\n');
|
|
|
|
});
|
|
|
|
|
|
|
|
this.downloadFile(data, `${version}.txt`);
|
|
|
|
},
|
|
|
|
generateCheatManagerTXT(codes, version) {
|
|
|
|
let data = `${version}\r\nSuper Mario Sunshine`;
|
|
|
|
|
|
|
|
codes.forEach((code) => {
|
2020-07-10 12:21:13 +09:00
|
|
|
const codeTitle =
|
|
|
|
typeof code.title === 'string' ? code.title : translateCode(code, this.$lang).title;
|
|
|
|
|
|
|
|
data += `\r\n\r\n${codeTitle} (${code.date}) [${code.author}]\r\n`;
|
2020-07-03 10:53:48 +09:00
|
|
|
data += code.source
|
|
|
|
.match(/.{8}/g)
|
|
|
|
.join(' ')
|
|
|
|
.replace(/(.{17})./g, '$1\r\n');
|
|
|
|
});
|
|
|
|
|
|
|
|
this.downloadFile(data, `${version}.txt`);
|
|
|
|
},
|
2022-11-11 21:58:24 +09:00
|
|
|
generateGCI(codes, version) {
|
|
|
|
if (!['GMSJ01'].includes(version)) {
|
|
|
|
alert('GCI format is not yet supported for versions other than GMSJ01');
|
2022-11-16 17:20:30 +09:00
|
|
|
return false; // error
|
2022-11-11 21:58:24 +09:00
|
|
|
}
|
|
|
|
let code = '00D0C0DE00D0C0DE';
|
|
|
|
codes.forEach((c) => (code += c.source));
|
2022-11-16 17:20:30 +09:00
|
|
|
code += 'C0000000000000023C60818081E3A7FC7DE478504E800020'; // return
|
2022-11-11 21:58:24 +09:00
|
|
|
|
2022-11-16 17:20:30 +09:00
|
|
|
const blockCount = 2; // TODO
|
2022-11-11 21:58:24 +09:00
|
|
|
const gciSize = 0x40+0x2000*blockCount;
|
2022-11-16 17:20:30 +09:00
|
|
|
const padSize = 0x40; // TODO
|
2022-11-11 21:58:24 +09:00
|
|
|
let rawData = new Uint8Array(gciSize);
|
|
|
|
|
|
|
|
for (let iD=padSize, iC=0; iD<rawData.length; iD++, iC+=2) {
|
|
|
|
rawData[iD] = parseInt(code.slice(iC, iC+2), 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
// game id
|
|
|
|
[...new TextEncoder().encode(version), 0xff, 0x00].forEach((e, i) => rawData[i] = e);
|
|
|
|
// file name
|
|
|
|
[...new TextEncoder().encode('gct')].forEach((e, i) => rawData[0x8+i] = e);
|
|
|
|
// block count
|
|
|
|
rawData[0x39] = blockCount;
|
|
|
|
// ff*6
|
|
|
|
for (let i=0x3A; i<0x40; i++) rawData[i] = 0xff;
|
|
|
|
|
|
|
|
this.downloadFile(rawData, `01-${version.slice(0, 4)}-gct.gci`);
|
2022-11-16 17:20:30 +09:00
|
|
|
return true; // good
|
2022-11-11 21:58:24 +09:00
|
|
|
},
|
2020-07-03 10:53:48 +09:00
|
|
|
downloadFile(data, filename) {
|
|
|
|
var file = new Blob([data], {
|
|
|
|
type: 'application/octet-stream',
|
|
|
|
});
|
|
|
|
|
|
|
|
if (window.navigator.msSaveOrOpenBlob) window.navigator.msSaveOrOpenBlob(file, filename);
|
|
|
|
else {
|
|
|
|
var a = document.createElement('a'),
|
|
|
|
url = window.URL.createObjectURL(file);
|
|
|
|
a.href = url;
|
|
|
|
a.download = filename;
|
|
|
|
a.click();
|
|
|
|
setTimeout(function () {
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
}, 500);
|
|
|
|
}
|
|
|
|
},
|
2020-06-28 10:45:44 +09:00
|
|
|
},
|
2020-06-28 06:33:20 +09:00
|
|
|
};
|
|
|
|
</script>
|