Skip to content

Commit

Permalink
Merge pull request #95 from NMSCD/dev
Browse files Browse the repository at this point in the history
display timestamp
  • Loading branch information
Lenni009 authored Aug 8, 2024
2 parents 3c64a93 + e23cd6f commit 0f38702
Show file tree
Hide file tree
Showing 13 changed files with 565 additions and 380 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/fetchMapping.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
type Mapping = { Key: string; Value: string };
import { Mapping } from "../../src/types/mapping.ts";

interface MappingObject {
libMBIN_version: string;
Mapping: Mapping[];
Mapping: Mapping;
}

const mappingUrl =
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ on:
pull_request:
types: [opened, synchronize]

concurrency:
group: "testbuild"
cancel-in-progress: true

jobs:
Build:
runs-on: ubuntu-latest
Expand All @@ -22,4 +18,4 @@ jobs:
uses: actions/checkout@v4

- name: Test Build
uses: Lenni009/test-build-vite-action@main
uses: Lenni009/test-build-vite-action@main
678 changes: 408 additions & 270 deletions package-lock.json

Large diffs are not rendered by default.

19 changes: 10 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@
},
"dependencies": {
"@picocss/pico": "^2.0.6",
"pinia": "^2.1.6",
"sass": "^1.71.1",
"vue": "^3.4.21"
"pinia": "^2.2.0",
"sass": "^1.77.8",
"vue": "^3.4.35"
},
"devDependencies": {
"@types/node": "^20.11.25",
"@vitejs/plugin-vue": "^5.0.3",
"prettier": "^3.0.0",
"typescript": "^5.4.2",
"vite": "^5.1.5",
"vue-tsc": "^2.0.6"
"@types/node": "^22.1.0",
"@vitejs/plugin-vue": "^5.1.2",
"@vueuse/core": "^10.11.1",
"prettier": "^3.3.3",
"typescript": "^5.5.4",
"vite": "^5.3.4",
"vue-tsc": "^2.0.29"
}
}
92 changes: 40 additions & 52 deletions public/hglz4.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,66 +41,54 @@ function readFile(file) {
const Buffer = require('buffer').Buffer;
const LZ4 = require('lz4');

// TODO: Make this work
async function compressSave() {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (!file) {
alert('Please select a file.');
return;
}

const data = await readFile(file);
const compressedData = compress(data);
downloadFile(compressedData, file.name + '.compressed');
}

// Uploaded file -> json file
async function decompressSave(file, mapping) {
const fileData = await readFile(file);

let decodedText = '';
const size = fileData.byteLength;
let tell = 0;
return new Promise((resolve, reject) => {
let decodedText = '';
const size = fileData.byteLength;
let tell = 0;

do {
// Slice the uploaded data for the next block
const data = fileData.slice(tell);
// Read No Man's Sky's magic number block
const magicNumber = new Uint32Array(data, 0, 4)[0];
// Get info on this block
const compressedSize = new Uint32Array(data, 4, 4)[0];
const uncompressedSize = new Uint32Array(data, 8, 4)[0];
do {
// Slice the uploaded data for the next block
const data = fileData.slice(tell);
// Read No Man's Sky's magic number block
const magicNumber = new Uint32Array(data, 0, 4)[0];
// Get info on this block
const compressedSize = new Uint32Array(data, 4, 4)[0];
const uncompressedSize = new Uint32Array(data, 8, 4)[0];

if (magicNumber !== 0xfeeda1e5) {
alert('Invalid NMS Block, bad file');
return;
}
if (magicNumber !== 0xfeeda1e5) {
reject('Invalid NMS Block, bad file');
return;
}

// Read the current compressed block
const compressedBlock = new Buffer.from(data.slice(16));
// Read the current compressed block
const compressedBlock = new Buffer.from(data.slice(16));

// Set the uncompressed block up
let uncompressedBlock = Buffer.alloc(uncompressedSize);
// Decode the current compressed block
LZ4.decodeBlock(compressedBlock, uncompressedBlock, 0, compressedSize);
// Trim the uncompressed block, for reasons
uncompressedBlock = uncompressedBlock.subarray(0, uncompressedSize);
// Convert uncompressed block to text and append
const decodedBlockText = uncompressedBlock.toString();
decodedText += decodedBlockText;
// Set the uncompressed block up
let uncompressedBlock = Buffer.alloc(uncompressedSize);
// Decode the current compressed block
LZ4.decodeBlock(compressedBlock, uncompressedBlock, 0, compressedSize);
// Trim the uncompressed block, for reasons
uncompressedBlock = uncompressedBlock.subarray(0, uncompressedSize);
// Convert uncompressed block to text and append
const decodedBlockText = uncompressedBlock.toString();
decodedText += decodedBlockText;

// Advance the file reader by the number of bytes we have read so far
tell += 4 + 4 + 4 + 4 + compressedSize;
} while (tell < size);
// Advance the file reader by the number of bytes we have read so far
tell += 4 + 4 + 4 + 4 + compressedSize;
} while (tell < size);

// Clean up decoded text
let parsedJson;
try {
parsedJson = JSON.parse(decodedText);
} catch {
parsedJson = JSON.parse(decodedText.slice(0, -1));
}
const mappedJson = mapKeys(parsedJson, mapping);
return mappedJson;
// Clean up decoded text
let parsedJson;
try {
parsedJson = JSON.parse(decodedText);
} catch {
parsedJson = JSON.parse(decodedText.slice(0, -1));
}
const mappedJson = mapKeys(parsedJson, mapping);
resolve(mappedJson);
});
}
45 changes: 27 additions & 18 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
<script setup lang="ts">
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { Mapping } from './assets/mapping.json';
import { formatTime } from './helpers/time';
import { downloadFile } from './helpers/download';
import { reverseMapKeys } from './helpers/obfuscate';
const fileInput = ref<HTMLInputElement | null>(null);
const saveData = ref(null);
const saveData = ref<object>();
const isDecompressing = ref(false);
const filename = ref('');
async function decodeSave() {
const file = fileInput.value?.files?.[0];
if (!file) return;
isDecompressing.value = true;
filename.value = file.name;
const decompressedSave = await decompressSave(file, Mapping);
if (typeof decompressedSave === 'string') return;
saveData.value = decompressedSave;
isDecompressing.value = false;
console.log('success!');
downloadFile(JSON.stringify(decompressedSave, null, 2), `${file.name}.json`);
}
// chatGPT's version of the file downloader
function downloadFile(data: string, fileName: string) {
const blob = new File([data], fileName, { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
a.click();
URL.revokeObjectURL(url);
const playTime = computed(() => formatTime(saveData.value?.CommonStateData.TotalPlayTime));
function downloadSave() {
if (!saveData.value) return;
const obfuscatedJson = reverseMapKeys(saveData.value, Mapping);
const stringifiedJson = JSON.stringify(obfuscatedJson);
downloadFile(stringifiedJson, filename.value);
}
</script>

Expand All @@ -34,11 +37,17 @@ function downloadFile(data: string, fileName: string) {
accept=".hg"
ref="fileInput"
type="file"
@change="decodeSave"
/>
<button
:disabled="isDecompressing"
@click="decodeSave"
>
Decompress
</button>
<div :aria-busy="isDecompressing"></div>

<!-- <pre>{{ saveData }}</pre> -->

<template v-if="saveData">
<div>
<span>Playtime:</span> <span>{{ playTime }}</span>
</div>

<button @click="downloadSave">Download</button>
</template>
</template>
10 changes: 10 additions & 0 deletions src/helpers/download.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// chatGPT's version of the file downloader
export function downloadFile(data: string, fileName: string) {
const blob = new File([data], fileName, { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
a.click();
URL.revokeObjectURL(url);
}
21 changes: 21 additions & 0 deletions src/helpers/obfuscate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Mapping } from '../types/mapping';

// obfuscate keys for compression
export function reverseMapKeys(json: object, mapping: Mapping) {
if (Array.isArray(json)) {
return json.map((item) => reverseMapKeys(item, mapping));
} else if (typeof json === 'object' && json !== null) {
const newJson = {};
for (const key in json) {
const mappedKey = mapping.find((m) => m.Value === key)?.Key;
if (mappedKey) {
newJson[mappedKey] = reverseMapKeys(json[key], mapping);
} else {
newJson[key] = reverseMapKeys(json[key], mapping);
}
}
return newJson;
} else {
return json;
}
}
17 changes: 17 additions & 0 deletions src/helpers/time.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const formatTimePart = (timePart: number) => timePart.toString().padStart(2, '0');

/**
*
* @param timestamp takes playtime in seconds from TotalPlayTime
*/
export function formatTime(timestamp: number) {
const secondsInMinutes = 60;
const secondsInHours = secondsInMinutes ** 2;
const hours = Math.floor(timestamp / secondsInHours);
const hoursInSeconds = hours * secondsInHours;
const secondsWithoutHours = timestamp - hoursInSeconds;
const minutes = Math.floor(secondsWithoutHours / secondsInMinutes);
const minutesInSeconds = minutes * secondsInMinutes;
const seconds = secondsWithoutHours - minutesInSeconds;
return `${formatTimePart(hours)}:${formatTimePart(minutes)}:${formatTimePart(seconds)}`;
}
5 changes: 5 additions & 0 deletions src/lz4.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare global {
async function decompressSave(file: File, mapping: { Key: string; Value: string }[]): Promise<object | string>;
}

export {};
4 changes: 4 additions & 0 deletions src/types/mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type Mapping = {
Key: string;
Value: string;
}[];
Loading

0 comments on commit 0f38702

Please sign in to comment.