From 388dfb7f0f4e69bdf6eea527b8c2a93573fb8de8 Mon Sep 17 00:00:00 2001 From: peshomir <80340328+peshomir@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:31:24 +0300 Subject: [PATCH] Add automatic localizer function matching - Fix for v1.99.6.4 --- build.js | 23 ++++++++++++++++++----- patches.js | 18 +++++++++--------- src/fx_core.js | 4 ++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/build.js b/build.js index 7312656..64feeb5 100644 --- a/build.js +++ b/build.js @@ -58,7 +58,11 @@ const matchDictionaryExpression = expression => { // and this matches "a=b+1;", the returned value will be the object: { var1: "a", var2: "b" } const replaceRawCode = (/** @type {string} */ raw, /** @type {string} */ result, nameMappings) => { const { expression, groups } = generateRegularExpression(raw, false, nameMappings); - let replacementString = result.replaceAll("$", "$$").replace(/\w+/g, match => { + let localizerCount = 0; + let replacementString = result.replaceAll("$", "$$").replaceAll("__L()", "__L)").replaceAll("__L(", "__L,") + .replace(/\w+/g, match => { + // these would get stored as "___localizer1", "___localizer2", ... + if (match === "__L") match = "___localizer" + (++localizerCount); return groups.hasOwnProperty(match) ? "$" + groups[match] : match; }); //console.log(replacementString); @@ -77,11 +81,20 @@ const matchRawCode = (/** @type {string} */ raw, nameMappings) => { const generateRegularExpression = (/** @type {string} */ code, /** @type {boolean} */ isForDictionary, nameMappings) => { const groups = {}; let groupNumberCounter = 1; - let raw = escapeRegExp(code).replace(isForDictionary ? /(?:@@)*(@?)(\w+)/g : /()(\w+)/g, (_match, modifier, word) => { + let localizerCount = 0; + let raw = escapeRegExp(code).replaceAll("__L\\(\\)", "___localizer\\)") + // when there is a parameter, add a comma to separate it from the added number + .replaceAll("__L\\(", "___localizer,"); + raw = raw.replace(isForDictionary ? /(?:@@)*(@?)(\w+)/g : /()(\w+)/g, (_match, modifier, word) => { // if a substitution string for the "word" is specified in the nameMappings, use it if (nameMappings && nameMappings.hasOwnProperty(word)) return nameMappings[word]; // if the "word" is a number or is one of these specific words, ingore it if (/^\d/.test(word) || ["return", "this", "var", "function", "Math"].includes(word)) return word; + // for easy localizer function matching + else if (word === "___localizer") { + groups[word + (++localizerCount)] = groupNumberCounter++; + return "\\b(L\\(\\d+)"; // would match "L(123", "L(50" and etc. when using "__L(" + } else if (groups.hasOwnProperty(word)) return "\\" + groups[word]; // regex numeric reference to the group else { groups[word] = groupNumberCounter++; @@ -94,8 +107,8 @@ const generateRegularExpression = (/** @type {string} */ code, /** @type {boolea [ /,this\.(?\w+)=this\.\w+<7\|\|9===this\.\w+,/g, - /=function\((\w+),(\w+),\w+\){\1===(?\w+)\.(?\w+)\?\w+\(175," "\+\w+\(34,\[(?\w+)\.(?\w+)\[\2\]\]\),1001,\2,\w+\(/g, - /\w+\.\w+\((\w+)\)\?\w+\.\w+\(\1\)\?(\w+)=(\w+)\(29,\[\2\]\):\(\w+=\w+\.\w+\(\1\),\2=\3\(30,\[\w+\.\w+\.\w+\((?\w+)\.(?\w+)\[(\w+)\],\w+\.\w+\.\w+\(0,10\),150\),(\w+\.\w+\.\w+\()\4\.(?\w+)\[\6\]\),\7\4\.(?\w+)\[\6\]\),\2\]\),\w+=!0\):\2=/g, + /=function\((\w+),(\w+),\w+\){\1===(?\w+)\.(?\w+)\?\w+\(175," "\+\w+\(\d+,\[(?\w+)\.(?\w+)\[\2\]\]\),1001,\2,\w+\(/g, + /\w+\.\w+\((\w+)\)\?\w+\.\w+\(\1\)\?(\w+)=(\w+)\(\d+,\[\2\]\):\(\w+=\w+\.\w+\(\1\),\2=\3\(\d+,\[\w+\.\w+\.\w+\((?\w+)\.(?\w+)\[(\w+)\],\w+\.\w+\.\w+\(0,10\),150\),(\w+\.\w+\.\w+\()\4\.(?\w+)\[\6\]\),\7\4\.(?\w+)\[\6\]\),\2\]\),\w+=!0\):\2=/g, /function \w+\(\)\{if\(2===(?\w+)\.(?\w+)\)return 1;\w+\.\w+\(\),\1\.\2=2,\1\.\w+=\1.\w+\}/g, /(function \w+\((\w+),(?\w+),(?\w+),(?\w+),(?\w+)\){)(\6\.fillText\((?\w+)\.(?\w+)\[\2\],\4,\5\)),(\2<(?\w+)\.(?\w+)&&2!==\8\.(?\w+)\[[^}]+)}/g, /\w+\.font=(?\w+\.\w+\.\w+)\(1,\.39\*this\.\w+\),/g @@ -103,7 +116,7 @@ const generateRegularExpression = (/** @type {string} */ code, /** @type {boolea const rawCodeSegments = [ "this.@gIsSingleplayer?this.@gLobbyMaxJoin=@SingleplayerMenu.@getSingleplayerPlayerCount():this.gLobbyMaxJoin=this.@gMaxPlayers,this.@gBots=this.gLobbyMaxJoin-this.@gHumans,this.sg=0,", - "[0]=@L(80),@strs[1]=@game.@gIsSingleplayer?@L(81):@L(82),", + "[0]=__L(),@strs[1]=@game.@gIsSingleplayer?__L():__L(),", "?(this.gB=Math.floor(.0536*aK.fw),g5=aK.g5-4*@uiSizes.@gap-this.gB):", `for(a0L=new Array(@game.@gMaxPlayers),a0A.font=a07,@i=game.gMaxPlayers-1;0<=i;i--)a0L[i]=i+1+".",@playerData.@playerNames[i]=aY.qW.tm(playerData.@rawPlayerNames[i],a07,a0W),a0K[i]=Math.floor(a0A.measureText(playerData.playerNames[i]).width);`, ] diff --git a/patches.js b/patches.js index 7a87af5..b6d8a38 100644 --- a/patches.js +++ b/patches.js @@ -13,8 +13,8 @@ export default ({ replace, replaceOne, replaceRawCode, dictionary, matchOne, mat replaceOne(/(\(22,"logo",8,")[^"]+"\)/g, "$1" + assets.smallLogo + "\")"); // Add FX Client version info to the game version window - replaceRawCode(`ar.oa(4,1,new s8(L(254),gameVersion+"
"+ah.aC5+"",`, - `ar.oa(4,1,new s8(L(254),gameVersion+"
"+ah.aC5+"" + replaceRawCode(`ar.oa(4,1,new s8(__L(),gameVersion+"
"+ah.aC5+"",`, + `ar.oa(4,1,new s8(__L(),gameVersion+"
"+ah.aC5+"" + "

" + "FX Client v" + fx_version + " " + fx_update + "
FX Client Discord server" + "
Github repository
",`); @@ -28,8 +28,8 @@ export default ({ replace, replaceOne, replaceRawCode, dictionary, matchOne, mat // TODO: test this; it might cause issues with new boat mechanics? { // Add Troop Density and Maximum Troops in side panel - const { valuesArray } = replaceRawCode(`,labels[5]=L(86),labels[6]=L(87),labels[7]=L(88),(valuesArray=new Array(labels.length))[0]=game.io?`, - `,labels[5]=L(86),labels[6]=L(87),labels[7]=L(88), + const { valuesArray } = replaceRawCode(`,labels[5]=__L(),labels[6]=__L(),labels[7]=__L(),(valuesArray=new Array(labels.length))[0]=game.io?`, + `,labels[5]=__L(),labels[6]=__L(),labels[7]=__L(), labels.push("Max Troops", "Density"), // add labels (valuesArray=new Array(labels.length))[0]=game.io?`); replaceOne(new RegExp(/(:(?\w+)<7\?\w+\.\w+\.\w+\(valuesArray\[\2\]\)):(\w+\.\w+\(valuesArray\[7\]\))}/ @@ -42,18 +42,18 @@ export default ({ replace, replaceOne, replaceRawCode, dictionary, matchOne, mat } // Increment win counter on wins - replaceRawCode(`=function(sE){o.ha(sE,2),b.h9<100?xD(0,L(21,[a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,L(28,[a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0)`, + replaceRawCode(`=function(sE){o.ha(sE,2),b.h9<100?xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0)`, `=function(sE){ if (${playerId} === sE && !${gIsSingleplayer}) wins_counter++, window.localStorage.setItem("fx_winCount", wins_counter), xD(0,"Your Win Count is now " + wins_counter,3,sE,ad.gN,ad.kl,-1,!0); - o.ha(sE,2),b.h9<100?xD(0,L(21,[a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,L(28,[a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0)`); + o.ha(sE,2),b.h9<100?xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0)`); { // Add settings button and win count // add settings button - replaceRawCode(`,new nQ("☰
"+L(193),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1)]`, - `,new nQ("☰
"+L(193),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1), + replaceRawCode(`,new nQ("☰
"+__L(),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1)]`, + `,new nQ("☰
"+__L(),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1), new nQ("FX Client settings", function() { WindowManager.openWindow("settings"); }, "rgba(0, 0, 20, 0.5")]`) // set settings button position replaceRawCode(`aZ.g5.vO(aD3[3].button,x+a0S+gap,a3X+h+gap,a0S,h);`, @@ -142,7 +142,7 @@ canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6 { // Player list and leaderboard filter tabs // Draw player list button const uiOffset = dictionary.uiSizes + "." + dictionary.gap; - const { groups: { drawFunction, topBarHeight } } = replaceOne(/(=1;function (?\w+)\(\){[^}]+?(?\w+)\.fillRect\(0,(?\w+),\w+,1\),(?:\3\.fillRect\([^()]+\),)+\3\.font=\w+,(\w+\.\w+)\.textBaseline\(\3,1\),\5\.textAlign\(\3,1\),\3\.fillText\(\w+\(79\),Math\.floor\()(\w+)\/2\),(Math\.floor\(\w+\+\w+\/2\)\));/g, + const { groups: { drawFunction, topBarHeight } } = replaceOne(/(=1;function (?\w+)\(\){[^}]+?(?\w+)\.fillRect\(0,(?\w+),\w+,1\),(?:\3\.fillRect\([^()]+\),)+\3\.font=\w+,(\w+\.\w+)\.textBaseline\(\3,1\),\5\.textAlign\(\3,1\),\3\.fillText\(\w+\(\d+\),Math\.floor\()(\w+)\/2\),(Math\.floor\(\w+\+\w+\/2\)\));/g, "$1($6 + $ - 22) / 2), $7; playerList.drawButton($, 12, 12, $ - 22);"); const buttonBoundsCheck = `utils.isPointInRectangle($, $, ${uiOffset} + 12, ${uiOffset} + 12, ${topBarHeight} - 22, ${topBarHeight} - 22)` // Handle player list button and leaderboard tabs mouseDown diff --git a/src/fx_core.js b/src/fx_core.js index e3bf34f..6f38a36 100644 --- a/src/fx_core.js +++ b/src/fx_core.js @@ -1,5 +1,5 @@ -const fx_version = '0.6.5.4'; // FX Client Version -const fx_update = 'Aug 24'; // FX Client Last Updated +const fx_version = '0.6.5.5'; // FX Client Version +const fx_update = 'Aug 26'; // FX Client Last Updated if (localStorage.getItem("fx_winCount") == undefined || localStorage.getItem("fx_winCount") == null) { var wins_counter = 0;