2024-08-28 06:46:22 +03:00
|
|
|
const fs = require('fs');
|
|
|
|
const path = require('path');
|
|
|
|
|
|
|
|
const systemNames = [
|
|
|
|
'uint32', 'uint64', 'int32', 'int64', 'sint32', 'sint64',
|
|
|
|
'fixed32', 'fixed64', 'sfixed32', 'sfixed64', 'float', 'double',
|
|
|
|
'bool', 'string', 'bytes'
|
|
|
|
];
|
|
|
|
|
|
|
|
const protoFilePath = "deobf.proto";
|
|
|
|
const outputPath1 = "./unimplemented";
|
|
|
|
const outputPath2 = "./proto";
|
|
|
|
const ntPath = '';
|
|
|
|
const version = `5.0`;
|
|
|
|
|
|
|
|
var cmdid_gen = [];
|
|
|
|
|
|
|
|
// Read the skip list
|
|
|
|
const skipFilePath = './skip2.txt';
|
|
|
|
let skipList;
|
|
|
|
try {
|
|
|
|
const skipFileContent = fs.readFileSync(skipFilePath, 'utf-8');
|
|
|
|
skipList = skipFileContent.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
|
|
|
} catch (err) {
|
|
|
|
console.error('Error reading skip file:', err);
|
|
|
|
skipList = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
function splitProtoFile(protoFilePath, nameTranslationPath = null, version = "0.0") {
|
|
|
|
|
|
|
|
let ntMap = {};
|
|
|
|
const lines = fs.readFileSync(protoFilePath, 'utf-8').split('\n');
|
|
|
|
|
|
|
|
if (nameTranslationPath) {
|
|
|
|
const ntLines = fs.readFileSync(nameTranslationPath, 'utf-8').split('\n');
|
|
|
|
for (let ntLine of ntLines) {
|
|
|
|
if (ntLine.trim().startsWith('#')) continue;
|
|
|
|
const [key, value] = ntLine.trim().split(' -> ');
|
|
|
|
ntMap[key] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let currentContent = [];
|
|
|
|
let currentName = null;
|
|
|
|
let cmdid = null;
|
|
|
|
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
|
|
const line = lines[i].trim();
|
|
|
|
|
2024-08-28 12:34:22 +03:00
|
|
|
if (line.startsWith('// CmdId: ')) {
|
2024-08-28 06:46:22 +03:00
|
|
|
cmdid = line;
|
2024-08-28 12:34:22 +03:00
|
|
|
// console.log(line)
|
2024-08-28 06:46:22 +03:00
|
|
|
} else if (line.startsWith("message") || line.startsWith("enum")) {
|
|
|
|
currentName = line.split(' ')[1];
|
|
|
|
currentContent.push(lines[i]);
|
|
|
|
} else if (line.startsWith("}")) {
|
|
|
|
|
|
|
|
currentContent.push(lines[i]);
|
|
|
|
|
|
|
|
// save
|
|
|
|
saveContent(currentName, currentContent, ntMap, cmdid, version);
|
|
|
|
|
|
|
|
if (cmdid) {
|
|
|
|
var d = new Object();
|
|
|
|
d["name"] = currentName;
|
2024-08-28 12:34:22 +03:00
|
|
|
d["id"] = parseInt(cmdid.replace(`// CmdId: `, ""));
|
2024-08-28 06:46:22 +03:00
|
|
|
cmdid_gen.push(d)
|
|
|
|
}
|
|
|
|
|
|
|
|
// clear
|
|
|
|
currentContent = [];
|
|
|
|
currentName = null;
|
|
|
|
cmdid = null;
|
|
|
|
|
|
|
|
} else if (currentName) {
|
|
|
|
currentContent.push(lines[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
save_json(cmdid_gen, `cmdid.json`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function saveContent(name, content, ntMap = {}, cmdid = "", version = "") {
|
|
|
|
if (name in ntMap && !/[a-z]/.test(name)) {
|
|
|
|
name = ntMap[name];
|
|
|
|
}
|
|
|
|
|
|
|
|
const filename = `${name}.proto`;
|
|
|
|
const nestedMessages = findNestedMessages(content);
|
|
|
|
|
2024-08-28 12:34:22 +03:00
|
|
|
/*
|
2024-08-28 06:46:22 +03:00
|
|
|
var isEnum = false
|
|
|
|
for (let i = 0; i < content.length; i++) {
|
|
|
|
let line = content[i];
|
|
|
|
if (isEnum && line.includes("_")) {
|
|
|
|
content[i] = renameConstant(line);
|
|
|
|
}
|
|
|
|
if (line.startsWith('enum')) {
|
|
|
|
isEnum = true;
|
|
|
|
}
|
|
|
|
}
|
2024-08-28 12:34:22 +03:00
|
|
|
*/
|
2024-08-28 06:46:22 +03:00
|
|
|
|
|
|
|
let contentStr = content.join('\n');
|
|
|
|
for (const [key, value] of Object.entries(ntMap)) {
|
|
|
|
if (/[a-z]/.test(key)) continue;
|
|
|
|
contentStr = contentStr.replace(new RegExp(key, 'g'), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
let output = `syntax = "proto3";\n\noption java_package = "emu.grasscutter.net.proto";\n// Version: ${version}\n`;
|
|
|
|
if (cmdid) {
|
|
|
|
//console.log(cmdid)
|
|
|
|
output += `${cmdid}\n`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nestedMessages.length > 0) {
|
|
|
|
const importStatements = nestedMessages.map(nm => `import "${nm}.proto";`).join('\n');
|
|
|
|
output += `\n${importStatements}\n\n`;
|
|
|
|
} else {
|
|
|
|
output += '\n';
|
|
|
|
}
|
|
|
|
output += contentStr;
|
|
|
|
|
|
|
|
// update file proto
|
|
|
|
const filePath2 = path.join(outputPath2, filename);
|
|
|
|
const filePath1 = path.join(outputPath1, filename);
|
|
|
|
if (fs.existsSync(filePath2)) {
|
|
|
|
|
|
|
|
// skip update if file skip or broke
|
|
|
|
if (skipList.includes(filename.replace(".proto", ""))) {
|
|
|
|
console.log('Skipping update file:', filePath2);
|
|
|
|
// TODO: fix it
|
|
|
|
} else {
|
|
|
|
console.log(`File exist add to implemented: ${filePath2}`);
|
|
|
|
fs.writeFileSync(filePath2, output);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
//console.log(`File does not exist add to unimplemented: ${filePath1}`);
|
|
|
|
fs.writeFileSync(filePath1, output);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkForNestedMessages(line) {
|
|
|
|
const nestedMessages = new Set();
|
|
|
|
if (line.startsWith('map<')) {
|
|
|
|
const mapsplit = line.replace('map<', '').replace('>', '').replace(',', '').split(' =')[0].split(' ').slice(0, -1);
|
|
|
|
for (const item of mapsplit) {
|
|
|
|
if (!systemNames.includes(item)) {
|
|
|
|
nestedMessages.add(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (line.startsWith('repeated')) {
|
|
|
|
const part1 = line.split(' ')[1];
|
|
|
|
if (!systemNames.includes(part1)) {
|
|
|
|
nestedMessages.add(part1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const part1 = line.split(' ')[0];
|
|
|
|
if (!systemNames.includes(part1)) {
|
|
|
|
nestedMessages.add(part1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nestedMessages;
|
|
|
|
}
|
|
|
|
|
|
|
|
function findNestedMessages(content) {
|
|
|
|
const nestedMessages = new Set();
|
|
|
|
const messageType = content[0].trim().split(' ')[0];
|
|
|
|
if (messageType === 'enum') return nestedMessages;
|
|
|
|
for (let line of content) {
|
|
|
|
line = line.trim();
|
|
|
|
if (line.startsWith('message') || line === '}' || line === '') continue;
|
|
|
|
const lineSplit = line.split(' ');
|
|
|
|
if (lineSplit[0] === 'oneof') continue;
|
|
|
|
const nested = checkForNestedMessages(line);
|
|
|
|
nested.forEach(nm => nestedMessages.add(nm));
|
|
|
|
}
|
|
|
|
return [...nestedMessages];
|
|
|
|
}
|
|
|
|
|
|
|
|
// save json
|
|
|
|
function save_json(raw, file) {
|
|
|
|
var j = JSON.stringify(raw, null, 4);
|
|
|
|
save(j, file);
|
|
|
|
}
|
|
|
|
|
|
|
|
function save(raw, file) {
|
|
|
|
fs.writeFile(file, raw, "utf8", function (err) {
|
|
|
|
if (err) {
|
|
|
|
console.log("An error occured while writing to File.");
|
|
|
|
return console.log(err);
|
|
|
|
}
|
|
|
|
console.log("File has been saved: " + file);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function renameConstant(declaration) {
|
|
|
|
// Split the declaration into the name and value parts
|
|
|
|
const [name, value] = declaration.split(" = ");
|
|
|
|
|
|
|
|
// Extract the relevant parts before and after the first underscore
|
|
|
|
const [prefix, relevantPart] = name.split("_", 2);
|
|
|
|
|
|
|
|
// Combine the prefix and the relevant part, and then convert to uppercase and replace camelCase with underscores
|
|
|
|
const newName = `${prefix}_${relevantPart}`.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
|
|
|
|
|
|
|
|
// Return the renamed declaration
|
|
|
|
return `${newName} = ${value}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
splitProtoFile(protoFilePath, ntPath, version);
|
|
|
|
|
|
|
|
console.log("Proto file split successfully!");
|