Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by CmP

  1. As others mentioned above, this is not something that needs a fix from user side. The regions that were identified by GG as "C++ alloc" or "C++ heap" on Android 10 and earlier versions are (in some, but not all, cases) identified as "Other" on Android 11 and higher versions. So the solution is do the following:
  2. The idea is to read enough bytes at once and then trim the string to first occurrence of quote character instead of reading one byte at a time until quote character is encountered. Generally, first approach should be better in terms of performance, because each read from memory takes significantly more time than several Lua operations, even if just one byte needs to be read. I thought to add 12 to get position of first byte of the answer that is the next one after last quote character in searched string. I don't immediately see why it doesn't work as expected, but it seems that there is a mistake somewhere and it's fixed by your correction to use "+ 11" instead of "+ 12".
  3. You should also consider refactoring the code to make it simpler and cleaner. For example, your "MENU1" function can be refactored to something like the following: function MENU1() gg.setRanges(gg.REGION_ANONYMOUS) gg.clearResults() gg.searchNumber(':","answer":"', gg.TYPE_BYTE) local count = gg.getResultsCount() local results = gg.getResults(count) local answers = {} for i = 1, count, 12 do local address = results[i].address + 12 local maxLength = 1024 local answer = readBytesToString(address, maxLength) answer = answer:match('[^"]*') -- match part up to first quote character table.insert(answers, answer) end for number, answer in ipairs(answers) do print(string.format("Answer #%d:", number), answer) end gg.alert("Answer See [ON]") end Let me know, if the code above works for you.
  4. To output a string Lua implementation in GG treats it as UTF-8 (by themselves Lua strings are just byte arrays), so reading bytes from memory to Lua string and passing the string to a function that outputs it ("print", "gg.alert", "gg.toast", etc) should be enough to achieve desired result in your case. Here is an example of function for reading bytes from memory and constructing string from them: function readBytesToString(address, length) local values = {} for i = 0, length - 1 do values[i] = {address = address + i, flags = gg.TYPE_BYTE} end values = gg.getValues(values) local bytes = {} for i, v in ipairs(values) do bytes[i] = v.value & 0xFF end return string.char(table.unpack(bytes)) end Call it with address to read from as first argument and amount of bytes to read as second argument, for example: local startAddress = 0x12340000 local bytesToRead = 20 local result = readBytesToString(startAddress, bytesToRead) print(result)
  5. CmP

    Edit only one final value

    You can use second parameter of "getResults" function to get (only) the last result: local count = gg.getResultsCount() local result = gg.getResults(1, count - 1) result[1].value = 123 gg.setValues(result)
  6. Preserving selection state only of some menu items can be done by adding a table for specifying menu items whose selection state needs to be preserved and updating "selection" table after the call to "multiChoice" function according to the added table and user's selection of items. Here is an example of how it can be implemented based on your code: function updateSelection(selection, keysToUpdate, newSelection) newSelection = newSelection or {} for i = 1, #selection do if keysToUpdate[i] then -- if item's selection state needs to be updated selection[i] = newSelection[i] or false -- update item's selection state and ensure that new value is not nil else selection[i] = false -- reset item's selection state to unselected end end end local options = {'Skip Game', 'Infinite Ammo', 'Exit'} local selection = {false, false, false} local remember = {[2] = true} function start() local choices = gg.multiChoice(options, selection) updateSelection(selection, remember, choices) if choices == nil then return end if choices[3] then print('Exited') os.exit() end if choices[1] then gg.toast('GAME SKIPPED') end if choices[2] then gg.toast('ON') else gg.toast('OFF') end end while true do if gg.isVisible() then gg.setVisible(false) start() end gg.sleep(100) end
  7. I can confirm the behavior of fuzzy search that is shown in the video. Some values of floating-point types (Float and Double), despite not changing, pass both "Increased" and "Decreased" conditions (but not "Changed", it correctly filters most of them out). It's not absolutely clear whether such behavior is intentional or not, but from user's side it's fairly easy to account for it. First time after making target value change in the game, search for values that have "changed". After that "increased" and "decreased" modes can be used as usual to find values that have increased or decreased correspondingly. Search tab toolbar contains "Remove" item (). Tapping on it causes dialog to be displayed with it's first option being "Remove all". Choosing the option clears results list allowing to start new fuzzy search.
  8. Relevant explanation from StackOverflow answer: https://stackoverflow.com/a/6389082
  9. It's because of fundamentals of how Lua is implemented. The implementations (official one and LuaJ) are single-threaded, therefore only one operation can be performed at a time. Nothing can be done about it without changing the implementation.
  10. How does that achieve the goal though? Original "searchNumber" function needs to be called anyway to actually perform the search. When it's called, it will only return after the search is finished (successfully or by cancellation). So how would a coroutine do anything when Lua executes native (i.e. not Lua one) function?
  11. Can you explain how would coroutines make that possible? How would you pass execution to a coroutine after calling "searchNumber", when it is doing it's job? Coroutines don't make it possible to execute code in parallel.
  12. Check the code carefully. Your manually retyped code has difference in second and third lines of loop body. Second line of loop's body from my code: heightValuesCheck[(i - 1) * 3 + 2] = {address = v.address - 0x100, flags = gg.TYPE_FLOAT} And how you retyped it: heightValuesCheck[(i - 1) * 3 + 1] = {address = v.address - 0x100, flags = gg.TYPE_FLOAT} It should be "(i - 1) * 3 + 2" there for the code to work as intended. Third line of loop's body in your retyped code needs to be fixed accordingly. This code is a minor optimization for adding elements to the end of array part of the table for the case of adding 3 elements per loop iteration. It achieves same result as more familiar "[#heightValuesCheck + 1]". The expression "(i - 1) * 3" is for calculating "base" index for three elements to be added to the table during iteration "i". At the first iteration, when i = 1, the expression evaluates to 0. At the second iteration (i = 2) the expression evaluates to 3. At the third to 6, and so on. To calculate index for the first element to be added during iteration, 1 is added to the result of the expression, giving index of 1 at the first iteration, 4 at the second iteration, 7 at the third iteration, and so on. Likewise, index of the second element to be added during iteration is calculated by adding 2 to the result of the expression and index of the third element - by adding 3.
  13. First and the most important thing that can be done to optimize the code is to call "setValues" once after loop(s) instead of calling it each time for the whole table after setting "value" field of one sub-table. It is extremely redundant to call "setValues" N times for table of N values (sub-tables). It only needs to be called once, after desired values of "value" field of all sub-tables (for which the value needs to be edited) have been set. One other thing that can be done is to have one table for all values instead of three tables, since all values need to be edited the same way. This won't make the code noticeably more or less efficient, but it will allow to replace fragments of code that are repeated three times (once for each table) with one instance of them for combined table. The code with the changes suggested above can look like the following: function theGiant() if giants == on then gg.setRanges(gg.REGION_ANONYMOUS | gg.REGION_OTHER) gg.clearResults() gg.searchNumber('h 85 EB 91 3F 6F 12 03 3C', gg.TYPE_BYTE) -- corrected the type here gg.refineNumber('h 85', gg.TYPE_BYTE) local results = gg.getResults(50) gg.clearResults() local heightValuesCheck = {} for i, v in ipairs(results) do heightValuesCheck[(i - 1) * 3 + 1] = {address = v.address - 0x104, flags = gg.TYPE_FLOAT} heightValuesCheck[(i - 1) * 3 + 2] = {address = v.address - 0x100, flags = gg.TYPE_FLOAT} heightValuesCheck[(i - 1) * 3 + 3] = {address = v.address - 0xFC, flags = gg.TYPE_FLOAT} end heightValuesCheck = gg.getValues(heightValuesCheck) -- filtering the tables, so that only tables with value '1' are left to avoid possible crashing during editing. heightValues = {} for i, v in ipairs(heightValuesCheck) do if v.value == 1 then heightValues[#heightValues + 1] = v end end -- editing, user input = ON for i, v in ipairs(heightValues) do v.value = '2.125478' end gg.setValues(heightValues) -- calling the function once, after desired values are set for all table elements else -- editing, user input = OFF for i, v in ipairs(heightValues) do v.value = '1' end gg.setValues(heightValues) -- calling the function once likewise end end
  14. CmP


    "Aarch64 = arm64" statement is correct. AArch64 is the name of 64-bit ARM architecture. Both names refer to the same thing.
  15. Try changing the option for GG to work with SELinux as mentioned here: Android 11 My phone is rooted but game gaurdian not work properly I'm downloading latest version it's not fix (#axy2r12m)
  16. CmP

    String match help

    Use "-" quantifier instead of "*": string.match(text, "str.-str") From Lua reference manual (https://www.lua.org/manual/5.3/manual.html#6.4.1) :
  17. Interesting encounter, it seems to be a bug in GG's Lua implementation, because running the code with official implementation (whether of version 5.2 or 5.3) results in expected output. The issue occurs in line 3: decimals = tostring(seconds):match("%.(.*)"); At the first invocation of the function, "decimals" global variable contains value of nil before the assignment, the expression at the right side of the assignment evaluates to "05" and value of "decimals" global variable is set to "05". At the second and all next invocations of the function, "decimal" global variable contains numeric value 50 (at the second invocation) or 500 (from third invocation onwards) before the assignment, the expression at the right side of the assignment evaluates to "05", but value of "decimals" global variable is set to 5 (number type). Such behavior may be related with variable containing value of number type before the assignment, it seems that "05" is mistakenly coerced to number. As a workaround, to not encounter the bug, you can just set value of "decimals" global variable to nil at the start of the function: decimals = nil Even better idea is to make "decimals" local, since there is no need to access it outside the function. Add definition of the variable at the start of the function: local decimals = nil Interestingly, the bug doesn't seem to occur with local variable, so you can also safely initialize the variable with default value of number type: local decimals = 0
  18. There are logical operators "and" and "or" that can be used to combine conditions. Here is how you can check if number is in certain range: if num >= 500 and num <= 1000 then -- ...
  19. As mentioned in this post with clarification about XOR mode: It means that when you edit value at address Av and specify distance of D, address of the key that will be used Ak will be calculated by subtracting D from Av: Ak = Av - D So if you need to use key at address that is greater than address of the value, use negative value of distance. -8 in your case. Example of edit string that you can use for values "cash1" and "cash2" from your screenshot: 100X-8 Edit: negative values of distance in XOR mode can be used for editing values, but not for searching.
  20. Main question to this algorithm description is whether addresses of target results after applying the offset (-9) to them will be divisible by 4, because according to your code, you want to edit value of float type. Float values are required to be aligned to 4 byte boundary, i.e. result of division of address of a float value by 4 is required to be integer. For example, 0x11223344 is a valid address for float value, but 0x11223345 isn't. Correspondingly, to avoid attempts of modifying float values at invalid addresses, there needs to be a check of address validity in implementation of the algorithm. Accounting for the above, the algorithm can be implemented, for example, like the following: local count = gg.getResultsCount() local results = gg.getResults(count) if count == 6 or count == 7 then local targetValues = {} local startIndex = (count == 6) and 1 or 2 for i = startIndex, 6, 2 do local targetAddress = results[i].address - 9 if targetAddress % 4 == 0 then -- check whether address is valid for a float value table.insert(targetValues, {address = targetAddress, flags = gg.TYPE_FLOAT, value = "0"}) end end gg.setValues(targetValues) --gg.addListItems(targetValues) -- add values to saved list, if needed end
  21. "Edit selected" interface feature edits only selected values of results list (or saved list). Your search results only include space for first 4 characters of a string in utf-16le encoding to be used in place of "coin", that's why only first 4 characters are edited. To be able to as well edit 6 bytes (for 3 characters in utf-16le encoding) that directly follow "coin", they need to be present in search results list. And values of those 6 bytes most likely aren't constant, so search modes like "h" or "Q" can't be used to reliably find them in all cases. Luckily, there is other search mode that can be used to find 6 bytes, that directly follow "coin", regardless of their values - group search with ranges (relevant examples are present in documentation for searchNumber GG API function). So there is a need to create group search to find sequences of bytes that match the following pattern: 04 00 00 00 63 00 6f 00 69 00 6e 00 ?? ?? ?? ?? ?? ?? where "??" means any possible value of byte, from 0x0 to 0xFF. Group searches, apart from list of values, have the following parameters: - group search type (unordered or ordered); - group size in bytes; - default data type of values. For this case group search needs to: - be ordered; - have group size equal to amount of bytes in searched sequence (18); - have default data type set to "Byte" (or to specify type of each value in list of values as "B" in which case default type doesn't matter). After figuring out parameters of desired group search, search string for the group search should be constructed according to the format (that is described in GG help: https://gameguardian.net/help/help.html#help_group_search_). To use hexadecimal values of bytes for convenience, "h" suffix needs to be appended to them. To specify any value, as mentioned in GG help about group search, "0~~0" can be used. Search string to find desired sequence of bytes can then be next: 04h;00h;00h;00h;63h;00h;6fh;00h;69h;00h;6eh;00h;0~~0;0~~0;0~~0;0~~0;0~~0;0~~0::18 After entering this search string in GG search window and choosing "Byte" type, it should look like the following: When search with above parameters completes, results list will be populated with values that match the pattern mentioned above. The only thing left is to edit found sequences of bytes. String to edit group(s) of results follows the format of list of values in group search string: value1;value2;...;valueN. After applying desired edits to list of values from search string: - first byte is changed from "04h" to "07h"; - 5-th to 18-th bytes are changed to bytes that encode "diamond" in utf-16le encoding; the string becomes: 07h;00h;00h;00h;64h;00h;69h;00h;61h;00h;6dh;00h;6fh;00h;6eh;00h;64h;00h It can then be used in "Value:" field of "Edit selected" feature to edit all found sequences of bytes in search results. Lastly, if it is desired, this method of searching and editing particular sequences of bytes can be automated with a simple script: gg.clearResults() -- ensures that new search will be started gg.searchNumber("04h;00h;00h;00h;63h;00h;6fh;00h;69h;00h;6eh;00h;0~~0;0~~0;0~~0;0~~0;0~~0;0~~0::18", gg.TYPE_BYTE) gg.getResults(gg.getResultsCount()) -- loads all found results gg.editAll("07h;00h;00h;00h;64h;00h;69h;00h;61h;00h;6dh;00h;6fh;00h;6eh;00h;64h;00h", gg.TYPE_BYTE)
  22. "os.clock" function can be used to measure elapsed time: local start = os.clock() -- code to measure execution time of local execTime = os.clock() - start print("Execution took " .. exexTime .. " seconds")
  23. To add to discussion of this suggestion: Lua strings by themselves are encoding-agnostic, so desired conversion is actually from bytes that represent string in one encoding to bytes that represent the same string in other encoding (utf-8 in this case, it seems). Input and output bytes of a hypothetical API function for such conversion can be in the form of strings or tables with values of bytes, but strings seem to be more reasonable option, because internally they are represented as array of bytes (as opposed to table with 8-byte integers). It's also worth to mention that GG API already has bytes function that converts bytes from utf-8 encoding to specified one, so it may be not a bad idea to extend the function by adding new parameter for source encoding (how to interpret string that is passed to the function) instead of creating a new function. As for what can be done currently, without such function: Since there is "utf8" library with functionality to construct a string from list of character codepoints, to be able to convert bytes from, for example, UTF-16LE encoding to UTF-8, it is enough to implement parsing of bytes that represent string in UTF-16LE encoding to list of character codepoints. The list then just need to be passed to "utf8.char" function and it will return desired string in UTF-8 encoding.
  24. There is "value" field of sub-tables of table returned by "getResults" function for that. In your case you can do something like this: gg.clearResults() gg.searchNumber("3;-12;30;1000000~3000000::33", gg.TYPE_DWORD) gg.refineNumber("1000000~3000000", gg.TYPE_DWORD) local result = gg.getResults(1) local value = result[1].value local newSearch = "3;3~8;1;2;5;" .. value .. "::33" gg.clearResults() gg.searchNumber(newSearch, gg.TYPE_DWORD) gg.refineNumber("3~8", gg.TYPE_DWORD) gg.getResults(1000) -- just load results without saving returned table gg.editAll("1000", gg.TYPE_DWORD) gg.clearResults()
  25. CmP

    increment index number

    This understanding is based on fundamental misconception that GG automatically updates anything in tables that have been returned with "getResults", "getValues" or other GG API functions. GG not only doesn't do that, but also wouldn't be able to do that without significant performance impact (just imagine having to read values from process memory for all elements of all tables that have been returned by GG API functions every N milliseconds). So changes in process memory "automatically" don't affect anything in script anyhow. To get updated data from process memory, corresponding GG API function ("getValues") needs to be called, but even it doesn't change anything in table that is passed to it as argument, instead it returns new table that has new values in "value" field of it's sub-tables.
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.