| Cheat Engine The Official Site of Cheat Engine FAQ Search Memberlist UsergroupsRegister | Profile Log in to check your private messages Log in |
|
Fixing Lua String Compare Goto page Previous1, 2, 3Next | Cheat Engine Forum Index -> Cheat Engine Lua Scripting | View previous topic :: View next topic | Author | Message |
---|
Csimbi I post too much Reputation: 95Joined: 14 Jul 2007 Posts: 3147
| Posted: Sun Jun 09, 2024 1:56 pm Post subject: | | | As it happens, I also need to compare strings in LUA. To be a bit more specific, I need to check whether a string contains a string. These strings are wide strings (UTF-16).This is what my code looks like for now: Code: | local iEntryLen = readInteger('[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+10') if iEntryLen > 10 and iEntryLen <25 then -- Check if this entry is of interest. local sEntryName = readString('[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+14', 12, true) if sEntryName~=nil and <contains>(sEntryName,"Rep.") then
|
The aim is to replace that <contains>(sEntryName,"Rep.") with a legit test in a way that comparison returns true only if "Rep." is found in sEntryName (it will be near the beginning). I suppose it would be faster if sEntryName was not read into a local variable in the first place, so a <contains>(<pointer to sEntryName>,"Rep.") might better. I will take either one though, speed is not a requirement Thank you! |
| Back to top | | | AylinCE Grandmaster Cheater Supreme Reputation: 33Joined: 16 Feb 2017 Posts: 1396
| Posted: Mon Jun 10, 2024 8:00 am Post subject: | | | Just an example to get you started; It makes it easier to read and compare special characters (non-letters and numbers!)! Code: | function specialcrt(str) fields = "" for wrd in str:gmatch(".") do if string.find(wrd,"%p") or string.find(wrd,"%c") then fields = fields..(wrd:gsub(".", "%%"..wrd)) else fields = fields..wrd end end return fields endfunction finds(s1,s2) s2 = specialcrt(s2) --print(s2) if string.find(s1,s2) then return true else return false end end sEntryName = "Compare Rep. rep1-" search1 = "Rep." search2 = "rep1-" if sEntryName~="" and finds(sEntryName,search1)==true then print("true") else print("false") end |
_________________
Hi Hitler Different Trainer forms for you! https://forum.cheatengine.org/viewtopic.php?t=619279 Enthusiastic people: Always one step ahead Do not underestimate me Master: You were a beginner in the past
|
| Back to top | | | ');//--> | |
| | Csimbi I post too much Reputation: 95Joined: 14 Jul 2007 Posts: 3147
| Posted: Mon Jun 10, 2024 2:57 pm Post subject: | | | Erm, thanks? I understand the bottom part, but that function specialcrt(str) on top looks like chinese to me AylinCE wrote: |
Code: | function specialcrt(str) fields = "" for wrd in str:gmatch(".") do if string.find(wrd,"%p") or string.find(wrd,"%c") then fields = fields..(wrd:gsub(".", "%%"..wrd)) else fields = fields..wrd end end return fields end
|
|
Any chance you could explain how it works? Will this work with UTF16? Thank you! |
| Back to top | | | AylinCE Grandmaster Cheater Supreme Reputation: 33Joined: 16 Feb 2017 Posts: 1396
| Posted: Mon Jun 10, 2024 6:30 pm Post subject: | | | This is just a function that adds a wildcard "%" character to the beginning of characters that are not letters or numbers, so that the code does not ignore them. In some cases, lua code (find, match, gsub, etc.) may see punctuation marks as wildcard characters that it constantly uses and gives other meanings, and skips reading it. In String.find "Rep." The string is sometimes not noticeable to the code because of the dot, but "Rep%." When you type with , the code will recognize that line and see the "period" as part of the string, not as a wildcard. I added this function for this reason. If you want a function for UTF16 characters, "Convert" and "search word" are exemplified below in the same function, which will both correct the entire text and scan and find the word in it. You can test the sample and give your opinion. ( All UTF16 characters list; https://www.fileformat.info/info/charset/UTF-16/list.htm ) --Unicode to text -- Code: | function utf8Char (decimal) if decimal < 128 then return string.char(decimal) elseif decimal < 2048 then local byte2 = (128 + (decimal % 64)) local byte1 = (192 + math.floor(decimal / 64)) return string.char(byte1, byte2) elseif decimal < 65536 then local byte3 = (128 + (decimal % 64)) decimal = math.floor(decimal / 64) local byte2 = (128 + (decimal % 64)) local byte1 = (224 + math.floor(decimal / 64)) return string.char(byte1, byte2, byte3) elseif decimal < 1114112 then local byte4 = (128 + (decimal % 64)) decimal = math.floor(decimal / 64) local byte3 = (128 + (decimal % 64)) decimal = math.floor(decimal / 64) local byte2 = (128 + (decimal % 64)) local byte1 = (240 + math.floor(decimal / 64)) return string.char(byte1, byte2, byte3, byte4) else return nil end endfunction specialcrt(str) fnt = {} local i = 0 local res = "" for wrd in str:gmatch("u....") or str:gmatch("u.....") do aa1 = wrd:gsub("u","") i=tonumber(i) + 1 fnt[i] = aa1 end for l,k in pairs(fnt) do aa2 = tonumber(k,16) print(aa2) str = str:gsub("u"..k, utf8Char(tonumber(aa2))) res = str end return res end rst="Citau00e7u00e3o: Vocu00ea u00e9 a minha razu00e3o de viver. u1d4ac" res = specialcrt(rst) print(rst) print(res) if string.find(res,"razão") then print("true") else print("false") end res = tonumber("00e9",16) print(utf8Char(res)) |
_________________
Hi Hitler Different Trainer forms for you! https://forum.cheatengine.org/viewtopic.php?t=619279 Enthusiastic people: Always one step ahead Do not underestimate me Master: You were a beginner in the past
Last edited by AylinCE on Mon Jun 10, 2024 10:18 pm; edited 1 time in total |
| Back to top | | | ');//--> | |
| | ParkourPenguin I post too much Reputation: 144Joined: 06 Jul 2014 Posts: 4430
| Posted: Mon Jun 10, 2024 8:22 pm Post subject: | | | AylinCE wrote: | Code: | <single line of 6538 characters> |
| f*cking hell. Please edit that post and just leave an ellipsis after a bit. i.e. `[[]]):gsub("u0100","Ā"):gsub("u0101","ā"):gsub("u0102","Ă"):gsub("u0103","ă")...`If you must use gsub like that, replace it with a table lookup: Code: | local t = { u0100 = "Ā", u0101 = "ā", u0102 = "Ă", u0103 = "ă", u0104 = "Ą", u0105 = "ą", --... }function foo(s) return s:gsub('(u%d%d%d%d)', t) end | There are other functions in CE's Lua API. e.g. `wideStringToByteTable` _________________I don't know where I'm going, but I'll figure it out when I get there. |
| Back to top | | | AylinCE Grandmaster Cheater Supreme Reputation: 33Joined: 16 Feb 2017 Posts: 1396
| Posted: Mon Jun 10, 2024 10:23 pm Post subject: | | | Replaced with a different format! @Csimbi, I got scolded by @Parkour, because of you! _________________
Hi Hitler Different Trainer forms for you! https://forum.cheatengine.org/viewtopic.php?t=619279 Enthusiastic people: Always one step ahead Do not underestimate me Master: You were a beginner in the past
|
| Back to top | | | ');//--> | |
| | Csimbi I post too much Reputation: 95Joined: 14 Jul 2007 Posts: 3147
| Posted: Wed Jun 12, 2024 7:41 am Post subject: | | | ParkourPenguin wrote: | f*cking hell. |
Have not heard that one before from you ParkourPenguin wrote: | There are other functions in CE's Lua API. e.g. `wideStringToByteTable` |
Thanks for that!
Code: | wideStringToByteTable(string): {} - Converts a string to a widestring and converts that to a bytetable
|
Is the idea to convert the ASCII string to UTF16 and that to a bytetable, which I can directly compare to the UTF16 string? If so, how does one do that comparison? AylinCE wrote: | @Csimbi, I got scolded by @Parkour, because of you! |
Sorry |
| Back to top | | | AylinCE Grandmaster Cheater Supreme Reputation: 33Joined: 16 Feb 2017 Posts: 1396
| Posted: Wed Jun 12, 2024 10:55 am Post subject: | | | Csimbi wrote: | Is the idea to convert the ASCII string to UTF16 and that to a bytetable, which I can directly compare to the UTF16 string? If so, how does one do that comparison? |
Is it possible to send here a text, sentence or paragraph you come across? (ASCII or UTF16) An example of Wide String format; Code: | function byteTableToAobString(t) for k,v in ipairs(t) do t[k] = ('%02X'):format(v) end return table.concat(t, ' ') endfunction formatWideStringToAobs1(text1) newvalue = text1 --newvalue = tonumber(newvalue) if not newvalue then return end newvalue = wideStringToByteTable(newvalue) newvalue = byteTableToAobString(newvalue) res1=('%s '):format(newvalue, newvalue) return res1 end function utf8Char(decimal) if decimal < 128 then return string.char(decimal) elseif decimal < 2048 then local byte2 = (128 + (decimal % 64)) local byte1 = (192 + math.floor(decimal / 64)) return string.char(byte1, byte2) elseif decimal < 65536 then local byte3 = (128 + (decimal % 64)) decimal = math.floor(decimal / 64) local byte2 = (128 + (decimal % 64)) local byte1 = (224 + math.floor(decimal / 64)) return string.char(byte1, byte2, byte3) elseif decimal < 1114112 then local byte4 = (128 + (decimal % 64)) decimal = math.floor(decimal / 64) local byte3 = (128 + (decimal % 64)) decimal = math.floor(decimal / 64) local byte2 = (128 + (decimal % 64)) local byte1 = (240 + math.floor(decimal / 64)) return string.char(byte1, byte2, byte3, byte4) else return nil end end text = "razão" print("Text:\n"..text) fmtaobs = formatWideStringToAobs1(text) print("\nFormat WideString aobs:\n"..fmtaobs) ---------------------------------------------- fmttext = "" fmtaobs2 = fmtaobs:gsub("00","") for wrd in fmtaobs2:gmatch("%x%x") do --print(wrd) chrdec = tonumber(wrd,16) print("\nFormat byte:\n"..wrd.." to "..chrdec) fmttext = fmttext..utf8Char(tonumber(chrdec)) end print("\nFormat aobs to text:\n"..fmttext) |
result: Text: razão Format WideString aobs: 72 00 61 00 7A 00 E3 00 6F 00 Format byte: 72 to 114 Format byte: 61 to 97 Format byte: 7A to 122 Format byte: E3 to 227 Format byte: 6F to 111 Format aobs to text: razão _________________
Hi Hitler Different Trainer forms for you! https://forum.cheatengine.org/viewtopic.php?t=619279 Enthusiastic people: Always one step ahead Do not underestimate me Master: You were a beginner in the past
|
| Back to top | | | ');//--> | |
| | Csimbi I post too much Reputation: 95Joined: 14 Jul 2007 Posts: 3147
| Posted: Wed Jun 12, 2024 11:17 am Post subject: | | | AylinCE wrote: | Is it possible to send here a text, sentence or paragraph you come across? |
Certainly. I have a tuple array that I iterate through. Each tuple is a pair of UTF-16 strings and a 4-byte value. A string may, or may not contain "Reputation.xxx" - for example: "Reputation.Pirates". So, my aim is to iterate through the array and check each string as fast as possible, to see if it contains "Reputation." and if it's found, then add a pointer to a new memrec. I have the code working - except the string comparison for UTF-16 strings. Here's the full code. Problem is, it checks only the beginning of the string only using a qword so it won't find entries where "Repu" is not at the beginning and there might be false positives, too. Code: | [ENABLE] {$lua} if syntaxcheck then return endfor i=memrec.Count-1,0,-1 do memrec.Child[i].delete() end local pStatsAddress=readPointer("pStats") local count if pStatsAddress~=nil and pStatsAddress~=0 then print(string.format("pStats: %X", pStatsAddress)) count = readInteger('[[pStats]+18]+40') if count~=nil then print(string.format("Count: %d", count)) end end if count==nil then ShowMessage('Invalid pointer; refresh pointer and try again!') elseif count <=0 then ShowMessage('No items found.') elseif count >1000 then ShowMessage('Too many items; wrong pointer? Refresh pointer and try again!') else AddressList.List.beginUpdate() for i=0, count do local iEntryLen = readInteger('[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+10') if iEntryLen~=nil and iEntryLen > 11 and iEntryLen <35 and readQword('[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+14')==0x75007000650052 then local mr = AddressList.createMemoryRecord() mr.description = 'Stats['..(i)..']' mr.Type = vtString mr.String.Size = 64 mr.String.Unicode = true mr.Address = '[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+14' mr.Color = 0x0000FF mr.appendToEntry(memrec) local mrchild = AddressList.createMemoryRecord() mrchild.description = 'iRelationshipValue' mrchild.Type = vtDword --vtSingle mrchild.ShowAsSigned = true mrchild.Address = '[[[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+30]+18]+20]+10' mrchild.Color = 0xFF0000 mrchild.appendToEntry(mr) end end AddressList.List.endUpdate() end {$asm} [DISABLE] {$lua} if syntaxcheck then return end AddressList.List.beginUpdate() for i=memrec.Count-1,0,-1 do memrec.Child[i].delete() end AddressList.List.endUpdate() {$asm} |
This script is part of my BattleTech table posted on fearless, if you'd like to try it. You'll need the game though and some work to setup the required mods. |
| Back to top | | | AylinCE Grandmaster Cheater Supreme Reputation: 33Joined: 16 Feb 2017 Posts: 1396
| Posted: Wed Jun 12, 2024 12:37 pm Post subject: | | | In your case "75007000650052" returns "upeR" and shows "Repu" in reverse. Here's a different method for reading: Is it possible to get iEntryLen as just the address? So you can print it as aob and compare it. The code below allows you to print the address to the aob output in the length you specify. So it's easier to test it with just the aob (hex) output rather than any string. Code: | function getByteString(address, bytecount) local bytes = readBytes(address, bytecount, true) if bytes then local result = "" for i = 1, #bytes do if #result > 0 then result = result .. "" end result = result .. string.format("%02X", bytes[i]) end return result end end --for i=0, count do --local iEntryLen = readInteger('[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+10') local iEntryLen1 = '[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+10' --if iEntryLen~=nil and iEntryLen > 11 and iEntryLen <35 and readQword('[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+14')==0x75007000650052 then iEntryLen2 = getByteString(iEntryLen1, 7) -- 75+00+70+00+65+00+52 = 7 byte --75706552 = upeR --52657075 = Repu if iEntryLen2~=nil and iEntryLen2=="75007000650052" then |
I try the code like this.I am looking for "E8 03 00 00" in "arrayofbyte" in CE via Chrome process. I get the address of the result; I format it 7 bytes wide;
Code: | iEntryLen2 = getByteString(aa2, 7) print(iEntryLen2) |
The result it gave me was; E8030000010000 _________________
Hi Hitler Different Trainer forms for you! https://forum.cheatengine.org/viewtopic.php?t=619279 Enthusiastic people: Always one step ahead Do not underestimate me Master: You were a beginner in the past
|
| Back to top | | | ');//--> | |
| | ParkourPenguin I post too much Reputation: 144Joined: 06 Jul 2014 Posts: 4430
| Posted: Wed Jun 12, 2024 2:59 pm Post subject: | | | Csimbi wrote: | Code: | wideStringToByteTable(string): {} - Converts a string to a widestring and converts that to a bytetable |
Is the idea to convert the ASCII string to UTF16 and that to a bytetable, which I can directly compare to the UTF16 string? | This goes through a long list of conversions
- Lua string- an array of 8-bit values (including 0). Entirely encoding-agnostic.
- C string- almost the same as a Lua string, but it terminates early at 0 bytes. e.g. `print'12\x0034'` only prints "12"
- Pascal String- aliased to some other type (AnsiString?). Probably the same as a C string, just with its own memory. I think CE assumes this is a UTF-8 encoding
- WideString- basically UTF-16
- raw bytes- the way the widestring is stored in memory as bytes
As for comparing them, comparing the Lua strings should be fine:
Code: | local s1 = readString(addr, 256, true) -- read wide string print(s1 == 'whatever') |
_________________I don't know where I'm going, but I'll figure it out when I get there. |
| Back to top | | | Csimbi I post too much Reputation: 95Joined: 14 Jul 2007 Posts: 3147
| Posted: Thu Jun 13, 2024 12:27 pm Post subject: | | | AylinCE wrote: | Here's a different method for reading: Is it possible to get iEntryLen as just the address? So you can print it as aob and compare it.
|
I am sure how that would that help me. All strings have different lengths. I already check for min. length (can't possibly contain the substring) and max. length (I have not found any string that's longer and contains the substring). Please clarify.Note: iEntryLen here means the number of printable character, not the size of the memory the UTF16 array occupies. ParkourPenguin wrote: |
Code: | local s1 = readString(addr, 256, true) -- read wide string print(s1 == 'whatever') |
|
Do I need to read the widestring into s1 the first place? (is there a direct widescreen comparison?) Code: | local s1 = readString(addr, iEntryLen*3, true) -- Min 2 bytes, max 4, so 3 on average :D print(s1 == 'Reputation.') |
Is the string 'Reputation.' automatically treated as a widestring in the comparison? Looks like a UTF8/ascii string. I know in a C++ compiler the "low precision operand" is converted up to the match the "high precision operand". I.e. in a float vs. double comparison the float is converted to a double automatically before the comparison (and then two doubles are compared). Does LUA work the same way? Might be a lame question, sorry. Edit Oh, hang on, this won't when the string is not at the beginning of the other string. I need to find a string within a string. That's what the gsub magic is there for from AylinCE, I suspect. I felt I had it, now I feel I lost it. |
| Back to top | | | ParkourPenguin I post too much Reputation: 144Joined: 06 Jul 2014 Posts: 4430
| Posted: Thu Jun 13, 2024 1:31 pm Post subject: | | | Csimbi wrote: | Do I need to read the widestring into s1 the first place? (is there a direct widescreen comparison?) Code: | local s1 = readString(addr, iEntryLen*3, true) -- Min 2 bytes, max 4, so 3 on average :D print(s1 == 'Reputation.') |
Is the string 'Reputation.' automatically treated as a widestring in the comparison? Looks like a UTF8/ascii string. | In CE, all Lua strings are pretty much always treated as UTF-8. If the game is using a widestring, it's perfectly fine to convert that to UTF-8 and compare the strings normally.
Code: | local widechars = { 0x43, 0x61, 0x73, 0x68, 0x3A, 0x20, 0x20AC, 0x35, 0 } local lpStr = createMemoryStream() lpStr.Size = #widechars * 2 for _,w in ipairs(widechars) do lpStr.writeWord(w) end print'Bytes:' print(readBytesLocal(lpStr.Memory, lpStr.Size, false)) print'Ansi string:' print(readStringLocal(lpStr.Memory, lpStr.Size)) print'Wide string:' local widestring = readStringLocal(lpStr.Memory, lpStr.Size, true) -- this converts from UTF-16 (game string) to UTF-8 (CE Lua string) print(widestring) print"widestring == 'Cash: 5'" print(tostring(widestring == 'Cash: 5')) -- it's ok to compare like this lpStr.destroy() |
The second parameter to `readstring` is the maximum number of bytes read. Unless the string isn't zero-terminated, you can let this be larger than the actual string length and it'll be fine. However, if it's less than the string length, it'll only get part of the string. If you don't know how long the string is, set it to something slightly larger than what you expect the string length to be. _________________I don't know where I'm going, but I'll figure it out when I get there. |
| Back to top | | | AylinCE Grandmaster Cheater Supreme Reputation: 33Joined: 16 Feb 2017 Posts: 1396
| Posted: Thu Jun 13, 2024 10:38 pm Post subject: | | | Check the additions I put in lines and try this code. Specifically, follow the results of "print(resultAobs1)" and see if it has the aob outputs you want. If not, give "resultAobs1" a correct address containing "75007000650052". Code: | [ENABLE] {$lua} if syntaxcheck then return endfor i=memrec.Count-1,0,-1 do memrec.Child[i].delete() end local pStatsAddress=readPointer("pStats") local count -------------------------------------------------- function getByteString(address, bytecount) local bytes = readBytes(address, bytecount, true) if bytes then local result = "" for i = 1, #bytes do if #result > 0 then result = result .. "" end result = result .. string.format("%02X", bytes[i]) end return result end end --------------------------------------------------- if pStatsAddress~=nil and pStatsAddress~=0 then print(string.format("pStats: %X", pStatsAddress)) count = readInteger('[[pStats]+18]+40') if count~=nil then print(string.format("Count: %d", count)) end end if count==nil then ShowMessage('Invalid pointer; refresh pointer and try again!') elseif count <=0 then ShowMessage('No items found.') elseif count >1000 then ShowMessage('Too many items; wrong pointer? Refresh pointer and try again!') else AddressList.List.beginUpdate() for i=0, count do local iEntryLen = readInteger('[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+10') ------------------------------------------------------------------------------------- local addr1 = getAddress('[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+14') local resultAobs1 = getByteString(addr1, 10) -- getByteString(addr1, 80) --> It is possible to query the word you are looking for in the longer aobs signature. Just increase the byte. if resultAobs1~=nil then print(resultAobs1) end -- Check out what's in the results and see if it's what you're looking for! ------------------------------------------------------------------------------------- if iEntryLen~=nil and iEntryLen > 11 and iEntryLen <35 then ------------------------------------------------------------------------------------ if string.find(resultAobs1,"75007000650052") or string.find(resultAobs1,"52006500700075") then -- 75007000650052 = upeR .. 52006500700075 = Repu ------------------------------------------------------------------------------------ local mr = AddressList.createMemoryRecord() mr.description = 'Stats['..(i)..']' mr.Type = vtString mr.String.Size = 64 mr.String.Unicode = true mr.Address = '[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+28]+14' mr.Color = 0x0000FF mr.appendToEntry(memrec) local mrchild = AddressList.createMemoryRecord() mrchild.description = 'iRelationshipValue' mrchild.Type = vtDword --vtSingle mrchild.ShowAsSigned = true mrchild.Address = '[[[[[[pStats]+18]+18]+18*'..string.format('%x',i)..'+30]+18]+20]+10' mrchild.Color = 0xFF0000 mrchild.appendToEntry(mr) end end end AddressList.List.endUpdate() end {$asm} [DISABLE] {$lua} if syntaxcheck then return end AddressList.List.beginUpdate() for i=memrec.Count-1,0,-1 do memrec.Child[i].delete() end AddressList.List.endUpdate() {$asm} |
_________________
Hi Hitler Different Trainer forms for you! https://forum.cheatengine.org/viewtopic.php?t=619279 Enthusiastic people: Always one step ahead Do not underestimate me Master: You were a beginner in the past
|
| Back to top | | | ');//--> | |
| | Csimbi I post too much Reputation: 95Joined: 14 Jul 2007 Posts: 3147
| Posted: Fri Jun 14, 2024 12:20 pm Post subject: | | | ParkourPenguin wrote: |
Code: | local widestring = readStringLocal(lpStr.Memory, lpStr.Size, true) -- this converts from UTF-16 (game string) to UTF-8 (CE Lua string) |
|
This was a key information. I did not know the UTF-16 becomes UTF-8 internally. Thank you! AylinCE wrote: | Check the additions I put in lines and try this code.
|
I just copy-pasted it 'as is'. It dumped a bunch of hex codes and built the structure as expected. Here is a sample:
Code: | Count: 541 4D006900730073006900 460075006E0064007300 460075006E0064007300 5400610073006B004400 54007200610076006500 54007200610076006500 55007000670072006100 4D006500630068005400 4D006500640054006500 45007800700065007200 45007800700065007200 44006900660066006900 53006800690070005400 4D006500630068004200 42006100720072006100 |
I got 55 entries in the table. I have no idea how many there should be. Thank you! |
| Back to top | | | |
| Cheat Engine Forum Index -> Cheat Engine Lua Scripting | All times are GMT - 6 Hours Goto page Previous1, 2, 3Next | Page 2 of 3 | | You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
| |