prevent flickering & .prettier

This commit is contained in:
Matteias Collet 2020-06-28 17:35:18 +02:00
parent a1dd024714
commit c2cf83bcb3
17 changed files with 293 additions and 265 deletions

7
.prettierrc Normal file
View file

@ -0,0 +1,7 @@
{
"printWidth": 100,
"singleQuote": true,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "all"
}

View file

@ -1,33 +1,28 @@
<template>
<div :class="
disabled
? 'button-wrapper disabled'
: 'button-wrapper'
">
<div :class="disabled ? 'button-wrapper disabled' : 'button-wrapper'">
<button @click="onClick" :disabled="disabled">{{ label }}</button>
</div>
</template>
<script>
import CodeFormatter from "./scripts/codeFormatter";
import CodeFormatter from './scripts/codeFormatter';
export default {
props: {
disabled: { type: Boolean },
onClick: { type: Function },
label: { type: String }
}
label: { type: String },
},
};
</script>
<style scoped>
.button-wrapper {
position: relative;
display: block;
display: inline-block;
max-width: 400px;
min-width: 180px;
margin: 0 auto;
text-align: center;
width: 100%;
}
.button-wrapper.disabled button {
@ -51,6 +46,7 @@ button {
color: white;
font-weight: bold;
cursor: pointer;
text-align: center;
}
button:hover {

View file

@ -34,10 +34,10 @@ export default {
methods: {
toggle(code) {
code.selected = !code.selected;
this.onSelectionChanged(this.availableCodes.filter((c) => c.selected));
this.onSelectionChanged(this.availableCodes.filter(c => c.selected));
},
populate() {
this.availableCodes = this.codes.map((c) => ({ ...c, selected: false }));
this.availableCodes = this.codes.map(c => ({ ...c, selected: false }));
},
inspect(code) {
this.onInspect(code);
@ -64,10 +64,11 @@ ul li {
display: block;
min-width: 280px;
padding-right: 15px;
text-align: left;
}
ul li:nth-child(odd) {
background: #e2e2e2;
background: #e7e7e7;
}
ul li:hover {
@ -91,7 +92,7 @@ li {
}
li::before {
content: "";
content: '';
position: absolute;
border-color: #a6a6a6;
border-style: solid;
@ -105,11 +106,17 @@ li::before {
li:hover::before {
border-color: #fff;
background-color: #ffc0cb;
background-color: #1fa76e;
}
li.checked::before {
border-color: #fff;
background-color: #d85e55;
}
@media screen and (max-width: 400px) {
ul li {
min-width: 180px;
}
}
</style>

View file

@ -7,8 +7,8 @@
</template>
<script>
import ButtonComponent from "./ButtonComponent";
import CodeFormatter from "./scripts/codeFormatter";
import ButtonComponent from './ButtonComponent';
import CodeFormatter from './scripts/codeFormatter';
export default {
props: {
@ -26,22 +26,22 @@ export default {
if (this.stageLoaderCode)
c.push({
title: "Stage List Loader",
author: "Noki Doki",
date: "-",
version: "",
title: 'Stage List Loader',
author: 'Noki Doki',
date: '-',
version: '',
source: this.stageLoaderCode,
});
console.log(`Preparing download for ${this.format}`);
switch (this.format) {
case "gct":
case 'gct':
CodeFormatter.generateGCT(c, this.versionIdentifier);
break;
case "dolphin":
case 'dolphin':
CodeFormatter.generateDolphinINI(c, this.versionIdentifier);
break;
case "gcm":
case 'gcm':
CodeFormatter.generateCheatManagerTXT(c, this.versionIdentifier);
break;
}

View file

@ -9,10 +9,10 @@
<script>
// Components
import SelectComponent from "./SelectComponent";
import SelectComponent from './SelectComponent';
// Data
import downloadFormats from "../data/downloadFormats.json";
import downloadFormats from '../data/downloadFormats.json';
export default {
props: {
@ -21,7 +21,7 @@ export default {
},
data() {
return {
options: downloadFormats.map((v) => ({
options: downloadFormats.map(v => ({
value: v.target,
label: v.name,
})),

View file

@ -1,22 +1,16 @@
<template>
<div>
<div>
<p v-if="isLoading">Loading...</p>
</div>
<section class="config">
<div v-if="isLoading" class="loading-overlay">
<div class="spinner"></div>
</div>
<div>
<span>Game Version:</span>
<VersionSelect
:onChange="onVersionChanged"
:selectedValue="selectedVersion"
/>
<VersionSelect :onChange="onVersionChanged" :selectedValue="selectedVersion" />
</div>
<div>
<span>Download Format:</span>
<FormatSelect
:onChange="onFormatChanged"
:selectedValue="selectedFormat"
/>
<FormatSelect :onChange="onFormatChanged" :selectedValue="selectedFormat" />
</div>
<div>
<span>Use Stage Loader:</span>
@ -47,15 +41,9 @@
:onInspect="inspect"
/>
</div>
<div
class="prevent-shrink"
v-if="codes && codes.length > 0 && useStageLoader"
>
<div class="prevent-shrink" v-if="codes && codes.length > 0 && useStageLoader">
<h3>Stage Loader</h3>
<StageLoader
:fastCodes="stageLoaderCodes"
:onChange="onStageLoaderCodeChanged"
/>
<StageLoader :fastCodes="stageLoaderCodes" :onChange="onStageLoaderCodeChanged" />
</div>
<div v-if="codes && codes.length > 0" class="help">
@ -67,42 +55,31 @@
<h3>Super Mario Sunshine Cheatfile Generator</h3>
<div>
<p>
This is a cheatfile generator for Super Mario Sunshine speedrun
practice. If this is your first time using the generator we highly
recommend to check out the
This is a cheatfile generator for Super Mario Sunshine speedrun practice. If this is
your first time using the generator we highly recommend to check out the
<a href="/guide.html" target="_blank">guide</a> first. Visit the
<a href="/guide.html#troubleshooting" target="_blank"
>the troubleshooting section</a
>
<a href="/guide.html#troubleshooting" target="_blank">the troubleshooting section</a>
if you encounter any issues.
</p>
<div>
<h4>The SMS Speedrunning Community</h4>
<ul>
<li>
Discord:
<a href="https://discord.gg/9dGJWEc" target="_blank"
>https://discord.gg/9dGJWEc</a
<a href="https://discord.gg/9dGJWEc" target="_blank" rel="noopener">Discord</a>
</li>
<li>
<a href="https://speedrun.com/sms" target="_blank" rel="noopener"
>Speedrun.com Leaderboards</a
>
</li>
<li>
Speedrun.com:
<a href="https://speedrun.com/sms" target="_blank"
>https://speedrun.com/sms</a
<a href="https://twitter.com/SMSCommunity" target="_blank" rel="noopener"
>Twitter: @SMSCommunity</a
>
</li>
<li>
Twitter:
<a href="https://twitter.com/SMSCommunity" target="_blank"
>https://twitter.com/SMSCommunity</a
>
</li>
<li>
Twitch:
<a
href="https://www.twitch.tv/SunshineCommunity"
target="_blank"
>https://www.twitch.tv/SunshineCommunity</a
<a href="https://www.twitch.tv/SunshineCommunity" target="_blank" rel="noopener"
>Twitch: SunshineCommunity</a
>
</li>
</ul>
@ -110,16 +87,14 @@
<div>
<p>
GCT Generator &copy; 2017 - {{ new Date().getFullYear() }}
<a href="https://twitter.com/psychonauter" target="_blank"
<a href="https://twitter.com/psychonauter" target="_blank" rel="noopener"
>Psychonauter</a
>,
<a href="https://twitter.com/Qbe_Root" target="_blank"
>Noki Doki</a
>
<a href="https://twitter.com/Qbe_Root" target="_blank" rel="noopener">Noki Doki</a>
and
<a href="https://twitter.com/srlMilk" target="_blank">Milk</a>.
The source code is available on
<a href="https://github.com/BitPatty/gctGenerator" target="_blank"
<a href="https://twitter.com/srlMilk" target="_blank" rel="noopener">Milk</a>. The
source code is available on
<a href="https://github.com/BitPatty/gctGenerator" target="_blank" rel="noopener"
>Github</a
>.
</p>
@ -132,38 +107,38 @@
<script>
// Components
import VersionSelect from "./VersionSelect";
import FormatSelect from "./FormatSelect";
import SelectComponent from "./SelectComponent";
import StageLoader from "./StageLoader";
import CodeInfo from "./CodeInfo";
import CodeList from "./CodeList";
import DownloadButton from "./DownloadButton";
import VersionSelect from './VersionSelect';
import FormatSelect from './FormatSelect';
import SelectComponent from './SelectComponent';
import StageLoader from './StageLoader';
import CodeInfo from './CodeInfo';
import CodeList from './CodeList';
import DownloadButton from './DownloadButton';
// Data
import gameVersions from "../data/gameVersions.json";
import gameVersions from '../data/gameVersions.json';
// Util
import parseXml from "./scripts/parseXml";
import parseXml from './scripts/parseXml';
// Libs
import axios from "axios";
import axios from 'axios';
export default {
mounted() {
Promise.all(
gameVersions.map(async (v) => ({
gameVersions.map(async v => ({
identifier: v.identifier,
cheats: parseXml((await axios.get(`/codes/${v.identifier}.xml`)).data),
fastCodes: (await axios.get(`/codes/fast/${v.identifier}.json`)).data,
}))
})),
)
.then((codes) => {
localStorage.setItem("codes", JSON.stringify(codes));
.then(codes => {
localStorage.setItem('codes', JSON.stringify(codes));
this.isLoading = false;
})
.catch((err) => {
if (localStorage.getItem("codes") != null) this.isLoading = false;
.catch(err => {
if (localStorage.getItem('codes') != null) this.isLoading = false;
});
},
data() {
@ -174,12 +149,12 @@ export default {
selectedStageLoader: null,
inspectingCode: null,
selectedVersion: null,
selectedFormat: "gct",
selectedFormat: 'gct',
useStageLoader: false,
stageLoaderCodes: [],
useStageLoaderOptions: [
{ value: false, label: "No" },
{ value: true, label: "Yes" },
{ value: false, label: 'No' },
{ value: true, label: 'Yes' },
],
};
},
@ -187,18 +162,16 @@ export default {
onVersionChanged(e) {
this.selectedVersion = e;
this.selectedCheats = [];
const storedCodes = JSON.parse(localStorage.getItem("codes"));
this.codes = storedCodes.find((c) => c.identifier === e).cheats;
this.stageLoaderCodes = storedCodes.find(
(c) => c.identifier === e
).fastCodes;
const storedCodes = JSON.parse(localStorage.getItem('codes'));
this.codes = storedCodes.find(c => c.identifier === e).cheats;
this.stageLoaderCodes = storedCodes.find(c => c.identifier === e).fastCodes;
this.inspectingCode = null;
},
onFormatChanged(e) {
this.selectedFormat = e;
},
onStageLoaderChanged(e) {
this.useStageLoader = e === true || e === "true";
this.useStageLoader = e === true || e === 'true';
if (!this.useStageLoader) this.selectedStageLoader = null;
},
onCheatSelectionChanged(e) {
@ -232,11 +205,77 @@ section > div:not(:first-child) {
margin-left: 20px;
}
.config {
position: relative;
}
.config .loading-overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
text-align: center;
background: #ffffff44;
background-color: rgba(255, 255, 255, 0.7);
}
.config span {
display: block;
margin-bottom: 10px;
padding-left: 2px;
}
.help {
text-align: left;
}
.spinner {
display: inline-block;
}
.spinner:after {
content: ' ';
display: block;
width: 32px;
height: 32px;
border-radius: 50%;
border: 6px solid #fff;
border-color: #2eb9e2 transparent #2eb9e2 transparent;
animation: spinner 1.2s linear infinite;
}
@keyframes spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
.config .loading-overlay {
-webkit-backdrop-filter: blur(1px);
backdrop-filter: blur(1px);
}
}
@media screen and (max-width: 1100px) {
section {
flex-wrap: wrap;
display: block;
margin-left: 0px;
text-align: center;
}
section > div,
section > div:not(:first-child) {
margin-left: 0px;
width: 100%;
}
}
</style>
<style>

View file

@ -1,16 +1,13 @@
<template>
<div class="select-wrapper">
<select @change="onValueChanged" autocomplete="off">
<option v-if="placeholder != null" value="placeholder" selected disabled>
{{ placeholder }}
</option>
<option v-if="placeholder != null" value="placeholder" selected disabled>{{ placeholder }}</option>
<optgroup v-for="optGroup in optGroups" :label="optGroup.label">
<option
v-for="option in optGroup.options"
:value="option.value"
:selected="selectedValue && option.value === selectedValue"
>{{ option.label }}</option
>
>{{ option.label }}</option>
</optgroup>
</select>
</div>
@ -22,19 +19,19 @@ export default {
selectedValue: { type: String },
placeholder: { type: String },
optGroups: { type: Array },
onChange: { type: Function },
onChange: { type: Function }
},
computed: {},
data() {
return {
generation: 2,
generation: 2
};
},
methods: {
onValueChanged(e) {
this.onChange(e.target.value);
},
},
}
}
};
</script>
@ -72,6 +69,10 @@ select {
user-select: none;
}
select::-ms-expand {
display: none;
}
.select-wrapper:hover {
background-color: #47c38b;
}

View file

@ -1,15 +1,16 @@
<template>
<div class="select-wrapper">
<select @change="(e) => this.onChange(e.target.value)" autocomplete="off">
<option v-if="placeholder != null" selected disabled>{{
<option v-if="placeholder != null" selected disabled>
{{
placeholder
}}</option>
}}
</option>
<option
v-for="option in options"
:value="option.value"
:selected="selectedValue && option.value === selectedValue"
>{{ option.label }}</option
>
>{{ option.label }}</option>
</select>
</div>
</template>
@ -20,11 +21,11 @@ export default {
selectedValue: { type: String },
placeholder: { type: String },
options: { type: Array },
onChange: { type: Function },
onChange: { type: Function }
},
data() {
return {};
},
}
};
</script>
@ -62,6 +63,10 @@ select {
user-select: none;
}
select::-ms-expand {
display: none;
}
.select-wrapper:hover {
background-color: #47c38b;
}

View file

@ -35,25 +35,17 @@
<div class="config">
<span>Route:</span>
<ul class="level-select">
<draggable
v-model="selectedRoute"
handle=".route-drag"
ghost-class="ghost"
>
<draggable v-model="selectedRoute" handle=".route-drag" ghost-class="ghost">
<li v-for="(level, index) in selectedRoute">
<div class="route-drag">&#8801;</div>
<GroupSelectComponent
:selectedValue="level.value"
:optGroups="stageLoaderLevelOptions"
:onChange="(e) => onStageLoaderLevelChanged(index, e)"
:onChange="e => onStageLoaderLevelChanged(index, e)"
:key="index"
/>
<button
@click="onLevelDeleted(index)"
type="button"
class="route-remove"
>
<button @click="onLevelDeleted(index)" type="button" class="route-remove">
&#215;
</button>
</li>
@ -88,19 +80,19 @@
<script>
// Components
import SelectComponent from "./SelectComponent";
import ButtonComponent from "./ButtonComponent";
import GroupSelectComponent from "./GroupSelectComponent";
import SelectComponent from './SelectComponent';
import ButtonComponent from './ButtonComponent';
import GroupSelectComponent from './GroupSelectComponent';
// Data
import stageLoaderLevels from "../data/stageLoaderLevels.json";
import stageLoaderPresets from "../data/stageLoaderPresets.json";
import stageLoaderLevels from '../data/stageLoaderLevels.json';
import stageLoaderPresets from '../data/stageLoaderPresets.json';
// Util
import generateStageLoaderCode from "./scripts/generateStageLoadercode";
import generateStageLoaderCode from './scripts/generateStageLoadercode';
// Lib
import draggable from "vuedraggable";
import draggable from 'vuedraggable';
export default {
props: {
@ -116,30 +108,30 @@ export default {
stageLoaderLevelOptions: stageLoaderLevels,
stageLoaderPresetOptions: stageLoaderPresets,
removeDialogueOptions: [
{ value: "yes", label: "Always" },
{ value: "pv5", label: "Not in Pinna 5" },
{ value: "no", label: "Don't include" },
{ value: 'yes', label: 'Always' },
{ value: 'pv5', label: 'Not in Pinna 5' },
{ value: 'no', label: "Don't include" },
],
skippableFMVsOptions: [
{ value: "yes", label: "Always" },
{ value: "pp", label: "Not in Pinna" },
{ value: "no", label: "Don't include" },
{ value: 'yes', label: 'Always' },
{ value: 'pp', label: 'Not in Pinna' },
{ value: 'no', label: "Don't include" },
],
levelOrderOptions: [
{ value: "list", label: "As specified" },
{ value: "shuffle", label: "Random, no duplicates" },
{ value: "random", label: "Fully random" },
{ value: 'list', label: 'As specified' },
{ value: 'shuffle', label: 'Random, no duplicates' },
{ value: 'random', label: 'Fully random' },
],
postGameOptions: [
{ value: "0F00", label: "Return to title screen" },
{ value: "0109", label: "Load the flooded plaza" },
{ value: "3400", label: "Load post-Corona plaza" },
{ value: "3C00", label: "Load the Bowser fight" },
{ value: '0F00', label: 'Return to title screen' },
{ value: '0109', label: 'Load the flooded plaza' },
{ value: '3400', label: 'Load post-Corona plaza' },
{ value: '3C00', label: 'Load the Bowser fight' },
],
removeDialogSelection: "yes",
skippableFMVsSelection: "yes",
levelOrderSelection: "list",
postGameSelection: "0F00",
removeDialogSelection: 'yes',
skippableFMVsSelection: 'yes',
levelOrderSelection: 'list',
postGameSelection: '0F00',
generation: 0,
};
},
@ -182,18 +174,17 @@ export default {
if (
this.selectedRoute?.length > 0 &&
!confirm("Loading a preset will erase your current list. Continue?")
!confirm('Loading a preset will erase your current list. Continue?')
) {
return;
}
const preset = e.split(";")[0];
const ending = e.split(";")[1];
const preset = e.split(';')[0];
const ending = e.split(';')[1];
const newRoute = [];
for (let i = 0; i <= preset.length - 4; i += 4)
newRoute.push({ value: preset.substr(i, 4) });
for (let i = 0; i <= preset.length - 4; i += 4) newRoute.push({ value: preset.substr(i, 4) });
this.selectedRoute = newRoute;
@ -201,10 +192,7 @@ export default {
this.updateCode();
},
onClearList() {
if (
this.selectedRoute?.length > 0 &&
!confirm("Do you really want to clear the list?")
)
if (this.selectedRoute?.length > 0 && !confirm('Do you really want to clear the list?'))
return;
this.selectedRoute = [];
@ -222,16 +210,16 @@ export default {
return;
}
console.log("Generating new Stageloader-Code");
console.log('Generating new Stageloader-Code');
this.onChange(
generateStageLoaderCode(
this.fastCodes,
this.selectedRoute.map((v) => v.value),
this.selectedRoute.map(v => v.value),
this.levelOrderSelection,
this.postGameSelection,
this.skippableFMVsSelection,
this.removeDialogSelection
)
this.removeDialogSelection,
),
);
},
},

View file

@ -8,9 +8,9 @@
</template>
<script>
import SelectComponent from "./SelectComponent";
import SelectComponent from './SelectComponent';
import gameVersions from "../data/gameVersions.json";
import gameVersions from '../data/gameVersions.json';
export default {
props: {
@ -19,7 +19,7 @@ export default {
},
data() {
return {
options: gameVersions.map((v) => ({
options: gameVersions.map(v => ({
value: v.identifier,
label: v.name,
})),

View file

@ -1,8 +1,8 @@
export default class CodeFormatter {
static generateGCT(codes, version) {
let code = "00D0C0DE00D0C0DE";
codes.forEach((c) => (code += c.source));
code += "FF00000000000000";
let code = '00D0C0DE00D0C0DE';
codes.forEach(c => (code += c.source));
code += 'FF00000000000000';
let rawData = new Uint8Array(code.length / 2);
@ -14,14 +14,14 @@ export default class CodeFormatter {
}
static generateDolphinINI(codes, version) {
let data = "Paste the following on top of your games .ini file:\r\n[Gecko]";
let data = 'Paste the following on top of your games .ini file:\r\n[Gecko]';
codes.forEach((code) => {
codes.forEach(code => {
data += `\r\n$${code.title} (${code.author}) [${code.date}]\r\n`;
data += code.source
.match(/.{8}/g)
.join(" ")
.replace(/(.{17})./g, "$1\r\n");
.join(' ')
.replace(/(.{17})./g, '$1\r\n');
});
this.downloadFile(data, `${version}.txt`);
@ -30,12 +30,12 @@ export default class CodeFormatter {
static generateCheatManagerTXT(codes, version) {
let data = `${version}\r\nSuper Mario Sunshine`;
codes.forEach((code) => {
codes.forEach(code => {
data += `\r\n\r\n${code.title} (${code.author}) [${code.date}]\r\n`;
data += code.source
.match(/.{8}/g)
.join(" ")
.replace(/(.{17})./g, "$1\r\n");
.join(' ')
.replace(/(.{17})./g, '$1\r\n');
});
this.downloadFile(data, `${version}.txt`);
@ -43,13 +43,12 @@ export default class CodeFormatter {
static downloadFile(data, filename) {
var file = new Blob([data], {
type: "application/octet-stream",
type: 'application/octet-stream',
});
if (window.navigator.msSaveOrOpenBlob)
window.navigator.msSaveOrOpenBlob(file, filename);
if (window.navigator.msSaveOrOpenBlob) window.navigator.msSaveOrOpenBlob(file, filename);
else {
var a = document.createElement("a"),
var a = document.createElement('a'),
url = window.URL.createObjectURL(file);
a.href = url;
a.download = filename;

View file

@ -4,7 +4,7 @@ const generateStageLoaderCode = (
routeOrder,
routeEnding,
fmvSkips,
dialgueSkips
dialgueSkips,
) => {
const loadStageLength = {
list: 0x20,
@ -12,110 +12,101 @@ const generateStageLoaderCode = (
shuffle: 0x40,
}[routeOrder];
let codes = "";
let codes = '';
// Reset counter on file select
codes +=
"0" +
'0' +
(0x04000000 + (gameConfig.fileSelect & 0x01ffffff)).toString(16) +
(
0x48000001 +
((gameConfig.system + 0x52c - gameConfig.fileSelect) & 0x03fffffc)
).toString(16);
(0x48000001 + ((gameConfig.system + 0x52c - gameConfig.fileSelect) & 0x03fffffc)).toString(16);
// Load next stage on Shine get
codes +=
"0" +
'0' +
(0x04000000 + (gameConfig.shineGet & 0x01ffffff)).toString(16) +
(
0x48000001 +
((gameConfig.system + 0x53c - gameConfig.shineGet) & 0x03fffffc)
).toString(16);
(0x48000001 + ((gameConfig.system + 0x53c - gameConfig.shineGet) & 0x03fffffc)).toString(16);
// Reload stage on exit area
codes +=
"0" +
(0x04000000 + (gameConfig.system & 0x01ffffff)).toString(16) +
"48000511";
codes += '0' + (0x04000000 + (gameConfig.system & 0x01ffffff)).toString(16) + '48000511';
// Set next stage on game over
codes +=
"0" +
'0' +
(0x06000000 + ((gameConfig.system + 0xb4) & 0x01ffffff)).toString(16) +
"000000084800048948000044";
'000000084800048948000044';
// Reset timer on secret death
codes +=
(0xc2000000 + ((gameConfig.system + 0x208) & 0x01ffffff)).toString(16) +
"000000033C60817F38000001980300FF881C00006000000000000000";
'000000033C60817F38000001980300FF881C00006000000000000000';
// Reset coin count on loading main world
codes +=
(0xc2000000 + ((gameConfig.shineGet - 0x674) & 0x01ffffff)).toString(16) +
"00000005887D00002C030002418000142C0300074182000C2C03000A418000087C0400406000000000000000";
'00000005887D00002C030002418000142C0300074182000C2C03000A418000087C0400406000000000000000';
// Overwrite decideNextStage(void) with useful routines
codes +=
"0" +
'0' +
(0x06000000 + ((gameConfig.system + 0x510) & 0x01ffffff)).toString(16) +
("0000000" + (loadStageLength + 0x5c).toString(16)).slice(-8) +
"3C60817F38000001980300FFA00300023C60" +
('0000000' + (loadStageLength + 0x5c).toString(16)).slice(-8) +
'3C60817F38000001980300FFA00300023C60' +
gameConfig.gpAppHi +
"B003" +
'B003' +
gameConfig.gpAppLo +
"4E800020" + // reload current level
"3C60817F" +
'4E800020' + // reload current level
'3C60817F' +
(0x38800000 + ((selectedLevels.length * 2) & 0x0000ffff)).toString(16) +
"B08300004E800020" + // reset counter
"3C60817F38000001980300FFA00300002C00000038E0" +
'B08300004E800020' + // reset counter
'3C60817F38000001980300FFA00300002C00000038E0' +
routeEnding + // load next stage - the fun begins
(0x40810000 + (loadStageLength & 0x0000fffc)).toString(16) +
"7C8802A6600000007CC802A67C8803A6";
'7C8802A6600000007CC802A67C8803A6';
switch (routeOrder) {
case "list":
codes += "3400FFFEB00300007CE6022E";
case 'list':
codes += '3400FFFEB00300007CE6022E';
break;
case "random":
codes += "7C8C42E67CA403967CA501D67C8520505484003C7CE6222E";
case 'random':
codes += '7C8C42E67CA403967CA501D67C8520505484003C7CE6222E';
break;
case "shuffle":
case 'shuffle':
codes +=
"7C8C42E67CA403967CA501D67C8520505484003C3400FFFEB00300007CE6222E7CA6022E7CA6232E7CE6032E";
'7C8C42E67CA403967CA501D67C8520505484003C3400FFFEB00300007CE6222E7CA6022E7CA6232E7CE6032E';
}
codes +=
"B0E300023C60" +
'B0E300023C60' +
gameConfig.gpAppHi +
"B0E3" +
'B0E3' +
gameConfig.gpAppLo +
"806D" +
'806D' +
gameConfig.fmOffset +
"98E300DF4E800020" +
(routeOrder === "random" ? "" : "00000000");
'98E300DF4E800020' +
(routeOrder === 'random' ? '' : '00000000');
selectedLevels.reverse();
while (selectedLevels.length % 4) selectedLevels.push("0000");
while (selectedLevels.length % 4) selectedLevels.push('0000');
// Insert the list of levels into the loader
codes +=
(0xc2000000 + ((gameConfig.system + 0x55c) & 0x01ffffff)).toString(16) +
("0000000" + (selectedLevels.length / 4 + 1).toString(16)).slice(-8) +
('0000000' + (selectedLevels.length / 4 + 1).toString(16)).slice(-8) +
(0x48000001 + ((selectedLevels.length * 2 + 4) & 0x03fffffc)).toString(16) +
selectedLevels.join("") +
"00000000";
selectedLevels.join('') +
'00000000';
// Load next stage on setNextStage into main level
codes +=
"0" +
'0' +
(0x06000000 + ((gameConfig.system + 0x118c) & 0x01ffffff)).toString(16) +
"00000028B07D00143C80817F38000000B00400FFA0010038B01D00122C1C00094181000C4BFFF391B0E10038";
'00000028B07D00143C80817F38000000B00400FFA0010038B01D00122C1C00094181000C4BFFF391B0E10038';
// Setup timer
codes +=
(0xc2000000 + (gameConfig.proc & 0x01ffffff)).toString(16) +
"000000053CA0817F388000009085010C880500FF98050100988500FF38800001988501016000000000000000";
'000000053CA0817F388000009085010C880500FF98050100988500FF38800001988501016000000000000000';
codes = codes.toUpperCase();

View file

@ -1,21 +1,20 @@
const parseXml = (xmlString) => {
const parseXml = xmlString => {
const codeCollection = new DOMParser()
.parseFromString(xmlString, "text/xml")
.getElementsByTagName("code");
.parseFromString(xmlString, 'text/xml')
.getElementsByTagName('code');
const codes = [...codeCollection];
return codes.map((code) => ({
author: parseTextNode(code, "author"),
title: parseTextNode(code, "title"),
description: parseTextNode(code, "description"),
version: parseTextNode(code, "version"),
date: parseTextNode(code, "date"),
source: parseTextNode(code, "source").replace(/[\s\n\r\t]+/gm, ""),
return codes.map(code => ({
author: parseTextNode(code, 'author'),
title: parseTextNode(code, 'title'),
description: parseTextNode(code, 'description'),
version: parseTextNode(code, 'version'),
date: parseTextNode(code, 'date'),
source: parseTextNode(code, 'source').replace(/[\s\n\r\t]+/gm, ''),
}));
};
const parseTextNode = (node, identifier) =>
node.getElementsByTagName(identifier)[0].textContent;
const parseTextNode = (node, identifier) => node.getElementsByTagName(identifier)[0].textContent;
export default parseXml;

View file

@ -1,15 +1,11 @@
const { description } = require("../../package");
const { description } = require('../../package');
module.exports = {
title: "GCT Generator",
title: 'GCT Generator',
description: description,
head: [
["meta", { name: "theme-color", content: "#3eaf7c" }],
["meta", { name: "apple-mobile-web-app-capable", content: "yes" }],
[
"meta",
{ name: "apple-mobile-web-app-status-bar-style", content: "black" },
],
['meta', { name: 'theme-color', content: '#3eaf7c' }],
['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1' }],
],
/**
@ -18,27 +14,27 @@ module.exports = {
* refhttps://v1.vuepress.vuejs.org/theme/default-theme-config.html
*/
themeConfig: {
repo: "BitPatty/gctGenerator",
repo: 'BitPatty/gctGenerator',
editLinks: true,
docsDir: "docs",
editLinkText: "Edit this page on GitHub",
docsDir: 'docs',
editLinkText: 'Edit this page on GitHub',
lastUpdated: false,
nav: [
{
text: "Cookbook",
link: "/guide.html",
text: 'Cookbook',
link: '/guide.html',
},
{
text: "Changelog",
link: "/changelog.html",
text: 'Changelog',
link: '/changelog.html',
},
{
text: "Installing IOS58",
link: "/ios58.html",
text: 'Installing IOS58',
link: '/ios58.html',
},
{
text: "Sunshine Discord",
link: "https://discord.gg/9dGJWEc",
text: 'Sunshine Discord',
link: 'https://discord.gg/9dGJWEc',
},
],
},
@ -46,5 +42,5 @@ module.exports = {
/**
* Apply pluginsrefhttps://v1.vuepress.vuejs.org/zh/plugin/
*/
plugins: ["@vuepress/plugin-back-to-top", "@vuepress/plugin-medium-zoom"],
plugins: ['@vuepress/plugin-back-to-top', '@vuepress/plugin-medium-zoom'],
};

View file

@ -8,7 +8,7 @@ export default ({
Vue, // the version of Vue being used in the VuePress app
options, // the options for the root Vue instance
router, // the router instance for the app
siteData // site metadata
siteData, // site metadata
}) => {
// ...apply enhancements for the site.
}
};

View file

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 318 B

View file

@ -1,6 +1,6 @@
{
"name": "gctGenerator",
"version": "0.0.1",
"name": "gctgenerator",
"version": "3.0.1",
"description": "Super Mario Sunshine Practice File Generator",
"main": "index.js",
"authors": {