Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by CmP

  1. 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.
  2. Relevant explanation from StackOverflow answer: https://stackoverflow.com/a/6389082
  3. 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.
  4. 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?
  5. 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.
  6. 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.
  7. 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
  8. CmP


    "Aarch64 = arm64" statement is correct. AArch64 is the name of 64-bit ARM architecture. Both names refer to the same thing.
  9. 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)
  10. 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) :
  11. 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
  12. 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 -- ...
  13. 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.
  14. 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
  15. "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)
  16. "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")
  17. 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.
  18. 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()
  19. 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.
  20. There is one other detail regarding the comparison to "0" (string). Behavior of comparison operators for this case is described in the manual as follows: For our case it means that the following condition _x[i].value ~= '0' will always evaluate to "true", because "value" field has value of number type. Correspondingly, correct comparison to check for value being not equal to zero is: _x[i].value ~= 0
  21. This code is equivalent to your previous version. You don't make a copy of tables with this line: local xValue, yValue = x, y Instead you create new variables that refer to the same tables, so it doesn't make any difference. Lua reference manual explains this pretty clearly:
  22. No, that's because all values of the table are explicitly set to value of first element when the following code is executed: for i = 1, #x do x[i].value = x[1].value end If, for example, "x" has 3 elements, the loop is then equivalent to: x[1].value = x[1].value -- value of first element is set to itself x[2].value = x[1].value -- value of second element is set to value of first element x[3].value = x[1].value -- value of third element is set to value of first element And then, when next loop is executed: for i = 1, #x do x[i].value = x[2].value end "x[2].value" no longer has it's original value, because new value (of "x[1].value") has been assigned to it during execution of previous loop. This is why only first loop works as intended and all subsequent ones don't. And this is why copy of values needs to be created before the first loop (to have access to original values after table values have been overwritten).
  23. The issue is in the code that needs to be repeated. When the code is executed for the first time, "value" fields of all sub-tables of the table ("x", for example) are set to "x[1].value", so that when the code is repeated and should set all values to second value, "x[2].value" is already modified to the value of "x[1].value", that's why each time the code is repeated, it sets all values to first value. To actually set all values of a table to value of different table element each time, make sure that there is a copy of values that doesn't get modified and in the code that gets repeated just use values from the copy to set "value" field of sub-tables. To make copy of values, construct a table in which indexes from original table (1, 2, 3, ...) are mapped to values of "value" field of corresponding sub-tables of the original table (x[1].value, x[2].value, x[3].value, ...), for example, like this: local xValues = {} for i, v in ipairs(x) do xValues[i] = v.value end Then use values of constructed table in inner loop of the code that gets repeated (slightly modified version of the loop from @TisNquyen's post above): for loop = 1, #x do for i = 1, #x do x[i].value = xValues[loop] y[i].value = yValues[loop] end gg.setValues(x) gg.setValues(y) gg.sleep(500) end
  24. --gg.clearResults() -- to ensure that new search will be started gg.searchNumber("1000000~1700000", gg.TYPE_DWORD) local results = gg.getResults(gg.getResultsCount()) for i, v in ipairs(results) do if v.value % 10 ~= 1 then results[i] = nil end end gg.loadResults(results)
  25. Fuzzy search approach can be used, but the right function to do fuzzy refine search is "searchFuzzy", not "startFuzzy". Here is an example code to search for a group of values with one or more unknown values, refine for changed values and restore group that includes first changed value: local searchedGroup = "123;45;0~~0;6789" -- "0~~0" is for any value local groupSize = 25 local searchString = searchedGroup .. "::" .. groupSize gg.setRanges(gg.REGION_ANONYMOUS) gg.clearResults() gg.searchNumber(searchString, gg.TYPE_DWORD) if gg.getResultsCount() == 0 then print("No groups have been found") os.exit() end gg.sleep(1000) -- wait for the value to change (if needed) gg.searchFuzzy("0", gg.SIGN_FUZZY_NOT_EQUAL) -- refine for changed values if gg.getResultsCount() == 0 then print("No values have changed") os.exit() end local changedValue = gg.getResults(1) local valueAddress = changedValue[1].address gg.clearResults() -- Search for the group that includes changed value within value's address ± group size gg.searchNumber(searchString, gg.TYPE_DWORD, false, gg.SIGN_EQUAL, valueAddress - groupSize, valueAddress + groupSize)
  • 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.