- Joined
- Jan 3, 2022
- Messages
- 364
I won't just feed you, I will teach you how to fish
Lines 2-42 are obviously the luahelper.lua file. $debug$ there is apparently substituted by the game itself, before the file gets loaded as Lua code. The last lines are garbage, not part of file.
Ok what do we see here?
This means:
(FourCC) 'dddd' = (100 << 24) + (100 << 16) + (100 << 8 ) + 100 = 1677721600 + 6553600 + 25600 + 100 = 1684300900
(not allowed) 'ddd' = (0 << 24 ) + (100 << 16) + (100 << 8) + 100 = 0 + 6553600 + 25600 + 100 = 6579300
(not allowed) 'dd' = (0 << 24) + (0 << 16) + (100 << 8) + 100 = 0 + 0 + 25600 + 100 = 25700
(understood by Jass) 'd' = (0 << 24) + (0 << 16) + (0 << 8) + 100 = 100
Ok, then this is what happens with their broken code:
To be honest, you shouldn't encounter a single-letter character codes while in Lua, the Jass2Lua transpiler will convert them to integers in the code.
PS: The thread FourCC breaks in V1.31 shows Blizzard's broken function without attribution and it's taken for granted that it works. No, it doesn't.
1. How to extract luahelper.lua
You need at least strings (Sysinternals or POSIX/GNU) to extract readable text from the .exe file. The following example shows GNU strings in shell (install bash and coreutils for the "Linux console" of your choice):
This searched all text strings for "__jarray" to find luahelper.lua. You can of course redirect the output (STDOUT) of strings to file and search with notepad. There're two matches: one at offset 37392449 (that's where the real luahelper.lua starts) and apparently another lua script at 37595752.
Bash:
user$ strings -8 -t d -d "Warcraft III.exe"|grep -C2 --color -i __jarray
#Output:
37392056 Af%X!Sdy
37392449 -- Jass2 array that returns an empty default value when the key does not exist
37392528 function __jarray(default)
37392555 return setmetatable({}, {
37392585 __index = function()
--
37595672 returns %s
37595688 function %s(
37595752 = __jarray(
37595784 ) then break end
37595808 while (true) do
2. luahelper.lua
...Then let's print the file starting at these positions (start at byte 37392400 | only readable text | max 50 lines):
Bash:
user$ tail --bytes +37392400 "Warcraft III.exe" | strings -n 1 | head -n 80
Lua:
-- Jass2 array that returns an empty default value when the key does not exist
function __jarray(default)
return setmetatable({}, {
__index = function()
return default
end
})
end
-- Math random functions should come from the game engine
function math.randomseed(seed)
return SetRandomSeed(seed // 1)
end
function math.random(m, n)
if m and n then
return GetRandomInt(m // 1, n // 1)
elseif m then
return GetRandomInt(1, m // 1)
else
return GetRandomReal(0.0, 1.0)
end
end
if DisplayTextToPlayer then
function print(...)
local sb = {}
for i = 1, select('#', ...) do
sb[i] = tostring(select(i, ...))
end
DisplayTextToPlayer(GetLocalPlayer(), 0, 0, table.concat(sb, ' '))
end
end
-- Helper function that enables the following syntax: ARCHMAGE = FourCC('Hamg')
function FourCC(id)
return 0x1000000 * string.byte(id:sub(1,1)) +
0x10000 * string.byte(id:sub(2,2)) +
0x100 * string.byte(id:sub(3,3)) +
string.byte(id:sub(4,4))
end
-- Debug build?
_DEBUG = $debug$
$debug$
luahelper.lua
Af%X!Sdy
$BQu
RaFC
xbgCRHI
internal error in the LUA transpiler
Hm looks like the jass2lua transpiler also has vJass support. Whatever, it's a Lua thread here.
Code:
user$ tail --bytes +37595700 "Warcraft III.exe" | strings | head -n 40
then
elseif
else
local
= {}
= __jarray(
= nil
if (
) then break end
while (true) do
%sreturn
end
TypeDefine('%s'
, '%s'
globals.
globals(function(_ENV)
end)
TriggerRegisterVariableEvent
null_node_ptr
program
library
interface
module
scope
textmacro
runtextmacro
return_
exitwhen
keyword
delegate
implement
literal_value
variable_decl
addr_of
logical_not
logical_and
logical_or
logical_less
logical_less_eq
logical_greater
- __jarray for internal use
- math.random/seed functions delegated to Jass' random functions to enable interoperation between two scripting languages, to have a common state.
- Inefficient replacement for print()
- Broken implementation of FourCC() - þou shalt not use þis
Blizzard's FourCC() function is broken
Why? Refer to Jeff Pang's JASS Manual:Hard to understand and harder to find still. What this means is that the four ASCII characters are the literal 4-byte representation of the uint32. However, 3/2/1-char long values are legal and perfectly understood by Jass. For example 'd' will turn into 100 (decimal). Because the byte value for ASCII 'd' is 100. This is the algorithm behind Blizzard's object type IDs. Extra: Due to standard A-Za-z0-9 limitations, these can represent <2^24 values, so they're probably serialized as 24-bit long shorts for network transfer.Integers can also be expressed as four character strings enclosed in single quotes, in which case the value is equal to the bit-string formed by the ASCII bytes (4 bytes = 32 bit value). These values are usually used to reference unit/upgrade/etc. identifiers which are enumerated in the *.slk files. For example, 'abcd', 'AhGn', 'EEEE'.
This means:
(FourCC) 'dddd' = (100 << 24) + (100 << 16) + (100 << 8 ) + 100 = 1677721600 + 6553600 + 25600 + 100 = 1684300900
(not allowed) 'ddd' = (0 << 24 ) + (100 << 16) + (100 << 8) + 100 = 0 + 6553600 + 25600 + 100 = 6579300
(not allowed) 'dd' = (0 << 24) + (0 << 16) + (100 << 8) + 100 = 0 + 0 + 25600 + 100 = 25700
(understood by Jass) 'd' = (0 << 24) + (0 << 16) + (0 << 8) + 100 = 100
Ok, then this is what happens with their broken code:
Lua:
> print(FourCC("d"))
1684300900
> print(FourCC("d"))
stdin:4: attempt to perform arithmetic on a nil value
stack traceback:
stdin:4: in function 'FourCC'
stdin:1: in main chunk
[C]: in ?
A Working FourCC() implementation. Use this:
Here's a proper, working substitute. Compatible with Lua 5.3+
Lua:
function unreforgedFourCC(str)
local n = 0
local len = #str
for i = len, 1, -1 do
n = n + (str:byte(i,i) << 8*(len-i)) -- shift by 0,8,16,24
end
return n
end
PS: The thread FourCC breaks in V1.31 shows Blizzard's broken function without attribution and it's taken for granted that it works. No, it doesn't.
Last edited: