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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,15 +1,11 @@
const { description } = require("../../package"); const { description } = require('../../package');
module.exports = { module.exports = {
title: "GCT Generator", title: 'GCT Generator',
description: description, description: description,
head: [ head: [
["meta", { name: "theme-color", content: "#3eaf7c" }], ['meta', { name: 'theme-color', content: '#3eaf7c' }],
["meta", { name: "apple-mobile-web-app-capable", content: "yes" }], ['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1' }],
[
"meta",
{ name: "apple-mobile-web-app-status-bar-style", content: "black" },
],
], ],
/** /**
@ -18,27 +14,27 @@ module.exports = {
* refhttps://v1.vuepress.vuejs.org/theme/default-theme-config.html * refhttps://v1.vuepress.vuejs.org/theme/default-theme-config.html
*/ */
themeConfig: { themeConfig: {
repo: "BitPatty/gctGenerator", repo: 'BitPatty/gctGenerator',
editLinks: true, editLinks: true,
docsDir: "docs", docsDir: 'docs',
editLinkText: "Edit this page on GitHub", editLinkText: 'Edit this page on GitHub',
lastUpdated: false, lastUpdated: false,
nav: [ nav: [
{ {
text: "Cookbook", text: 'Cookbook',
link: "/guide.html", link: '/guide.html',
}, },
{ {
text: "Changelog", text: 'Changelog',
link: "/changelog.html", link: '/changelog.html',
}, },
{ {
text: "Installing IOS58", text: 'Installing IOS58',
link: "/ios58.html", link: '/ios58.html',
}, },
{ {
text: "Sunshine Discord", text: 'Sunshine Discord',
link: "https://discord.gg/9dGJWEc", link: 'https://discord.gg/9dGJWEc',
}, },
], ],
}, },
@ -46,5 +42,5 @@ module.exports = {
/** /**
* Apply pluginsrefhttps://v1.vuepress.vuejs.org/zh/plugin/ * 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 Vue, // the version of Vue being used in the VuePress app
options, // the options for the root Vue instance options, // the options for the root Vue instance
router, // the router instance for the app router, // the router instance for the app
siteData // site metadata siteData, // site metadata
}) => { }) => {
// ...apply enhancements for the site. // ...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", "name": "gctgenerator",
"version": "0.0.1", "version": "3.0.1",
"description": "Super Mario Sunshine Practice File Generator", "description": "Super Mario Sunshine Practice File Generator",
"main": "index.js", "main": "index.js",
"authors": { "authors": {