Initial commit
This commit is contained in:
158
src/components/Seed.vue
Normal file
158
src/components/Seed.vue
Normal 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>
|
||||
62
src/components/SpritePicker.vue
Normal file
62
src/components/SpritePicker.vue
Normal 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>
|
||||
Reference in New Issue
Block a user