Initial commit

This commit is contained in:
2025-02-27 22:14:02 -06:00
commit 77f69becf9
18 changed files with 3611 additions and 0 deletions

158
src/components/Seed.vue Normal file
View File

@@ -0,0 +1,158 @@
<script>
import { defineComponent } from "vue";
import SpritePicker from "@/components/SpritePicker.vue";
import { Base64 } from "js-base64";
import * as bps from "bps";
import CRC32 from "crc-32";
import localforage from "localforage";
import axios from "axios";
export default defineComponent({
components: {
SpritePicker,
},
data() {
return {
rom_checksum: "3322EFFC",
baserom: null,
baserom_error: null,
sprite: null,
patch: null,
error: null,
};
},
props: {
id: "",
},
async mounted() {
document.title = `ALttPRandomizer: ${this.id}`;
const file = await localforage.getItem("baserom");
if (file) {
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
document.getElementById("rom-input").files = dataTransfer.files;
this.uploadBaseRom(file);
}
const response = await axios.get(`/seed/${this.id}`);
if (response && response.data && response.data["patch.bps"]) {
const seedData = response.data;
const patchArray = Base64.toUint8Array(seedData["patch.bps"]);
try {
const { instructions, _ } = bps.parse(patchArray);
const sourceChecksum = instructions.sourceChecksum.toString(16).toUpperCase();
if (sourceChecksum == this.rom_checksum) {
this.patch = instructions;
} else {
this.error = "Patch does not specify correct source checksum.";
}
} catch (error) {
console.log(error);
this.error = "Error parsing patch.";
}
} else {
console.log(response.data);
this.error = "Error loading seed.";
}
},
computed: {
permalink() {
return `/seed/${this.id}`;
},
},
methods: {
uploadBaseRom(file) {
if (!file) {
this.baserom_error = null;
this.baserom = null;
return;
}
const reader = new FileReader();
reader.onload = function() {
const buffer = new Uint8Array(reader.result);
const crc = (CRC32.buf(buffer, 0) >>> 0).toString(16).toUpperCase();
if (crc != this.rom_checksum) {
this.baserom_error = `Expected CRC ${this.rom_checksum}, but got ${crc}`;
this.baserom = null;
return;
}
this.baserom_error = null;
this.baserom = buffer;
localforage.setItem("baserom", file);
}.bind(this);
reader.readAsArrayBuffer(file);
},
spriteUpdate(sprite) {
this.sprite = sprite;
},
async patchRom() {
var rom = bps.apply(this.patch, this.baserom);
if (this.sprite) {
this.sprite.apply(rom);
}
// Fix Checksum
const sum = rom.reduce(function(sum, mbyte, i) {
if (i >= 0x7fdc && i < 0x7fe0) {
return sum;
}
return sum + mbyte;
});
const checksum = (sum + 0x01FE) & 0xFFFF;
const inverse = checksum ^ 0xFFFF;
rom[0x7FDC] = inverse & 0xFF;
rom[0x7FDD] = inverse >> 8;
rom[0x7FDE] = checksum & 0xFF;
rom[0x7FDF] = checksum >> 8;
const blob = new Blob([rom], { type: 'octet/stream' });
const link = document.getElementById('downloader');
link.href = URL.createObjectURL(blob);
link.download = `GK_${this.id}.sfc`;
link.click();
}
}
});
</script>
<template>
<div class="card content-div mt-3 mb-3">
<div class="card-header">
Permalink: <a :href="permalink">{{ permalink }}</a>
</div>
<div v-if="error" class="card-header">
{{ error }}
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<div class="mb-2">
<label for="rom-input" class="form-label">
The Legend of Zelda: A Link to the Past (JP 1.0) Rom:
</label>
<input id="rom-input" class="form-control" type="file" accept=".sfc,.smc" @change="uploadBaseRom($event.target.files[0])" />
<div v-if="baserom_error" class="invalid">
{{ baserom_error }}
</div>
</div>
</li>
<li class="list-group-item">
<div class="mb-2">
<SpritePicker @spriteUpdate="spriteUpdate" />
</div>
</li>
<li class="list-group-item">
<button type="submit" class="btn btn-primary submit-btn" :disabled="!baserom || !patch" @click="patchRom">
Download Seed!
</button>
<a id="downloader" style="display: none;" />
</li>
</ul>
</div>
</template>

View File

@@ -0,0 +1,62 @@
<script>
import { defineComponent } from 'vue';
import ZSPR from "@/ZSPR.js";
import localforage from "localforage";
export default defineComponent({
data() {
return {
sprite: null,
sprite_error: null,
};
},
async mounted() {
},
computed: {
},
methods: {
uploadSprite(file) {
console.log(file);
if (!file) {
this.sprite_error = null;
this.sprite = null;
return;
}
const reader = new FileReader();
reader.onload = function() {
const buffer = new Uint8Array(reader.result);
const zspr = new ZSPR(buffer);
if (!zspr.valid) {
this.sprite_error = "Invalid sprite file";
this.sprite = null;
return;
}
this.sprite_error = null;
this.sprite = zspr;
localforage.setItem("sprite", file);
this.$emit("spriteUpdate", this.sprite);
}.bind(this);
reader.readAsArrayBuffer(file);
},
}
});
</script>
<template>
<div>
<label for="sprite-input" class="form-label">
Custom Sprite:
<template v-if="sprite">
{{ sprite.spriteName }} by {{ sprite.spriteAuthor }}
</template>
</label>
<input id="sprite-input" class="form-control" type="file" accept=".zspr" @change="uploadSprite($event.target.files[0])" />
<div v-if="sprite_error" class="invalid">
{{ sprite_error }}
</div>
</div>
</template>