Skip to content

Instantly share code, notes, and snippets.

@Disquse
Last active October 29, 2022 21:50
Show Gist options
  • Save Disquse/014882c6463a66af7e184d79136aeaed to your computer and use it in GitHub Desktop.
Save Disquse/014882c6463a66af7e184d79136aeaed to your computer and use it in GitHub Desktop.
Fix typo
const fs = require("fs");
const path = require("path");
// !!! PUT *unpacked* .#pm files in this folder.
const targetPath = `${__dirname}\\Files`;
// !!! Path to RAGE-StringsDatabase repo.
const stringsPath = `${__dirname}\\RAGE-StringsDatabase`;
async function* getFiles(dir, target = "") {
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
for (const dirent of entries) {
const res = path.resolve(dir, dirent.name);
if (dirent.isDirectory()) {
yield* getFiles(res);
} else {
if (!res.endsWith(target)) {
continue;
}
yield res;
}
}
}
function getJoaat(key) {
const length = key.length;
let hash, i;
for (hash = i = 0; i < length; i++){
hash += key.charCodeAt(i);
hash += (hash << 10);
hash ^= (hash >>> 6);
}
hash += (hash << 3);
hash ^= (hash >>> 11);
hash += (hash << 15);
return hash >>> 0;
}
const hashToName = new Map();
(async () => {
for await (const filePath of getFiles(targetPath, ".full")) {
const fileName = filePath.split("\\").pop().replace(".ypm.full", "");
if (!fileName.startsWith("0x")) {
continue;
}
const fileHash = parseInt(fileName, 16);
const fileContent = fs.readFileSync(filePath, { encoding: "binary" });
const nameOffsetBytes = [];
for (let i = 0x3B; i >= 0x38; i--) {
nameOffsetBytes.push(fileContent[i].charCodeAt(0));
}
const nameOffset = (nameOffsetBytes[3] | nameOffsetBytes[2] << 8 | nameOffsetBytes[1] << 16 | nameOffsetBytes[0] << 24) - 0x50000000;
const fileNameBytes = [];
let charOffset = 0;
while (true) {
const charCursor = nameOffset + charOffset;
const charCode = fileContent[charCursor].charCodeAt(0);
if (!charCode) {
break;
}
fileNameBytes.push(fileContent[charCursor]);
charOffset++;
}
const assetName = fileNameBytes.join("").trim();
if (getJoaat(assetName) !== fileHash) {
console.warn("joaat missmatch for " + fileName);
continue;
}
if (!assetName) {
console.warn("invalid name of " + fileName);
continue;
}
hashToName.set(fileHash, assetName);
}
const addedFiles = {};
for await (const filePath of getFiles(stringsPath, ".hashes")) {
const hashesList = fs.readFileSync(filePath, { encoding: "utf8" }).split("\n");
for (const hashLine of hashesList) {
const actualHash = parseInt(hashLine.trim(), 16);
if (hashToName.has(actualHash)) {
if (!addedFiles[filePath]) {
addedFiles[filePath] = [];
}
addedFiles[filePath].push(hashToName.get(actualHash));
}
}
}
for (const addedFile in addedFiles) {
const addedStrings = addedFiles[addedFile];
const textPath = addedFile.replace(".hashes", ".txt");
const fileContent = fs.readFileSync(textPath, { encoding: "utf8" }).split("\n").filter(line => line.trim());
for (const addedString of addedStrings) {
if (fileContent.includes(addedString)) {
continue;
}
if (addedString) {
fileContent.push(addedString);
}
}
fs.writeFileSync(textPath, fileContent.sort().join("\n") + "\n");
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment