Skip to content

Commit

Permalink
0.7
Browse files Browse the repository at this point in the history
Finish up lovexedit to change file metadata like description
  • Loading branch information
ellraiser committed Aug 6, 2024
1 parent f82dd7e commit ad56f5c
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 26 deletions.
153 changes: 145 additions & 8 deletions libs/love-exedit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ require('libs.love-icon')

love.exedit = {

updateIcon = function(exe_file, image_file)
updateIcon = function(exe_file, image_file, debug_mode)

print('love.exedit > modiying exe file')

Expand Down Expand Up @@ -241,16 +241,113 @@ love.exedit = {
local lvl3_entry = lvl2_entry.SubDirectory.Entries[l3]
--print('love.exedit > DATA_ENTRY', l2, lvl3_entry.Name, tostring(#lvl3_entry.Data))
if lvl1_type == 'VERSION' then
print('love.exedit > VERSION_INFO')

-- @TODO read out version info and then rewrite as needed
-- first check the version info header
-- https://learn.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo
local vlength = love.exedit._readUInt(lvl3_entry.Data, 1, 2)
local vvallen = love.exedit._readUInt(lvl3_entry.Data, 3, 2)
local vtype = love.exedit._readUInt(lvl3_entry.Data, 4, 2)
local vkey = lvl3_entry.Data:sub(6, 6+29) -- VS_VERSION_INFO but WCHAR so *2 == 30 bytes
local vpad = love.exedit._readUInt(lvl3_entry.Data, 36, 2)
local fixedfileinfo = love.exedit._readDataType(lvl3_entry.Data, 38+vpad, 'FIXED_FILE_INFO')
local vvallen = love.exedit._readUInt(lvl3_entry.Data, 3, 2) -- 52 if we have VS_FIXEDFILEINFO otherwise 0
local vtype = love.exedit._readUInt(lvl3_entry.Data, 5, 2) -- 0, binary data, 1 text data
local vkey = lvl3_entry.Data:sub(7, 7+29) -- "VS_VERSION_INFO" but WCHAR so *2 == 30 bytes
local vpad = love.exedit._readUInt(lvl3_entry.Data, 37, 2) -- padding to add for VS_FIXEDFILEINFO
local fixedfileinfo = love.exedit._readDataType(lvl3_entry.Data, 39+vpad, 'FIXED_FILE_INFO')
local vpad2 = love.exedit._readUInt(lvl3_entry.Data, 39+vpad+vvallen, 2)
print('love.exedit > VERSION_INFO', vkey)

-- after the second padding is a list of 1 or more StringFileInfo or VarFileInfo objs
-- in love's case this is just 1 stringfileinfo then 1 varfileinfo
-- https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo
local sfi_start = 39+vpad+vvallen+2+vpad2
local stringfileinfo = {
wLength = love.exedit._readUInt(lvl3_entry.Data, sfi_start, 2),
wValueLength = love.exedit._readUInt(lvl3_entry.Data, sfi_start+2, 2),
wType = love.exedit._readUInt(lvl3_entry.Data, sfi_start+4, 2), -- will be 1, as love uses text data for the info
szKey = lvl3_entry.Data:sub(sfi_start+6, sfi_start+35), -- 'StringFileInfo'
padding = love.exedit._readUInt(lvl3_entry.Data, sfi_start+36, 2)
}
local vfi_start = sfi_start+stringfileinfo.wLength-2 -- -2 or +1 or +2?
local varfileinfo = {
wLength = love.exedit._readUInt(lvl3_entry.Data, vfi_start, 2),
wValueLength = love.exedit._readUInt(lvl3_entry.Data, vfi_start+2, 2),
wType = love.exedit._readUInt(lvl3_entry.Data, vfi_start+4, 2), -- will be 1, as love uses text data for the info
szKey = lvl3_entry.Data:sub(vfi_start+6, vfi_start+35), -- 'VarFileInfo'
padding = love.exedit._readUInt(lvl3_entry.Data, vfi_start+36, 2)
}
-- check things that should always be asserted
if stringfileinfo.wValueLength == 0 and varfileinfo.wValueLength == 0 then
print('love.exedit > StringFileInfo', stringfileinfo.szKey)
print('love.exedit > VarFileInfo', varfileinfo.szKey)

local stringtabledata = lvl3_entry.Data:sub(sfi_start, sfi_start+stringfileinfo.wLength-1)
if not debug_mode then love.filesystem.write('testing2', stringtabledata) end
local st_start = 37
--if not debug_mode then love.filesystem.write('testing2', stringtabledata) end
-- now we need to get the actual stringtable under stringfileinfo for the data
local stringtable = {
wLength = love.exedit._readUInt(stringtabledata, st_start, 2),
wValueLength = love.exedit._readUInt(stringtabledata, st_start+2, 2),
wType = love.exedit._readUInt(stringtabledata, st_start+4, 2),
-- windows docs list this as a WCHAR 8-digit hexadecimal number so 16 bytes
szKey = stringtabledata:sub(st_start+6, st_start+21),
padding = love.exedit._readUInt(stringtabledata, st_start+22, 2),
}
print('love.exedit > StringTable', stringtable.wLength, stringtable.wValueLength, stringtable.wType, stringtable.szKey, stringtable.padding)

if stringtable.wValueLength == 0 and stringtable.wType == 1 then
-- psych! we have to go another level deeper for the actual string :)
-- we also just have to loop through the data as we have no start/stop just the length of the stringtable
if debug_mode then
love.exedit._readStringTable(stringtabledata, st_start+24, stringtable.wLength-24, {})
else

-- we replace the StringTable with our own table containing the data
-- set by the user
local dname = love.exedit._writeWord(love.build.opts.name .. ' by ' .. love.build.opts.developer)
local dver = love.exedit._writeWord(love.build.opts.version)
local newdesc = stringtabledata:sub(1, 37+23)

-- @TODO
-- using FileVersion or ProductVersion keywords doesnt actually overwrite the version shown in the tooltip of the exe
-- not sure where that version comes from, must be set somewhere else
-- i think possibly the fixedfileinfo
--
newdesc = newdesc .. love.data.pack('string', '<i2', 38 + 4 + #dname) -- length of whole string obj
newdesc = newdesc .. love.data.pack('string', '<i2', #dname/2) -- length of actual data in WORD (string len/2)
newdesc = newdesc .. love.data.pack('string', '<i2', 1) -- type 1 (text)
newdesc = newdesc .. 'FileDescription' -- keyword
newdesc = newdesc .. love.data.pack('string', '<i2', 0) -- padding (0)
newdesc = newdesc .. '' .. dname .. ''
print('love.exedit > String', 'FileDescription', dname, #dname)
--
newdesc = newdesc .. love.data.pack('string', '<i2', 30 + 4 + #dver) -- length of whole string obj
newdesc = newdesc .. love.data.pack('string', '<i2', #dver/2) -- length of actual data in WORD (string len/2)
newdesc = newdesc .. love.data.pack('string', '<i2', 1) -- type 1 (text)
newdesc = newdesc .. 'FileVersion' -- keyword
newdesc = newdesc .. love.data.pack('string', '<i2', 0) -- padding (0)
newdesc = newdesc .. '' .. dver .. ''
print('love.exedit > String', 'FileVersion', dver, #dver)
--
newdesc = newdesc .. love.data.pack('string', '<i2', 36 + 4 + #dver) -- length of whole string obj
newdesc = newdesc .. love.data.pack('string', '<i2', #dver/2) -- length of actual data in WORD (string len/2)
newdesc = newdesc .. love.data.pack('string', '<i2', 1) -- type 1 (text)
newdesc = newdesc .. 'ProductVersion' -- keyword
newdesc = newdesc .. love.data.pack('string', '<i2', 0) -- padding (0)
newdesc = newdesc .. '' .. dver .. ''
print('love.exedit > String', 'ProductVersion', dver, #dver)

local padding = #stringtabledata - #newdesc
newdesc = newdesc .. string.rep('', padding)
print('love.exedit > Space remaining:', padding)

local prefix = new_data:sub(1, rsrc_data_index+lvl3_entry.Position-2)
local newdata = lvl3_entry.Data:sub(1, sfi_start-1) .. newdesc .. lvl3_entry.Data:sub(sfi_start+stringfileinfo.wLength-1, lvl3_entry.DataSize-1)
love.filesystem.write('testing', newdesc)
local suffix = new_data:sub(rsrc_data_index+lvl3_entry.Position-2+lvl3_entry.DataSize+1, #new_data)
new_data = prefix .. newdata .. suffix

end
end
end


end

Expand Down Expand Up @@ -404,11 +501,51 @@ love.exedit = {
return result
end,

-- reads the strings from a stringtable
-- mainly just used for debugging stuff to reverse engineer
_readStringTable = function(data, offset, total, results)
local str_start = offset
local sz_size = 30
if total == 442 then sz_size = 22 end
if total == 394 then sz_size = 22 end -- CompanyName
if total == 308 then sz_size = 28 end -- LegalCopyright
local str = {
wLength = love.exedit._readUInt(data, str_start, 2),
wValueLength = love.exedit._readUInt(data, str_start+2, 2),
wType = love.exedit._readUInt(data, str_start+4, 2),
szKey = data:sub(str_start+6, str_start+6+sz_size-1),
padding = love.exedit._readUInt(data, str_start+6+sz_size, 2),
}
local strv = data:sub(str_start+7+sz_size+str.padding, str_start+7+sz_size+str.padding+(str.wValueLength*2)-1)
local realKey = str.szKey:gsub('', '')
print('love.exedit > String', str.wLength, str.wValueLength, str.wType, str.szKey, realKey, #realKey, str.padding, strv, #strv)
results[str.szKey] = strv
total = total - str.wLength
if total > 0 and str.wLength > 0 then
love.exedit._readStringTable(data, offset+str.wLength, total, results)
else
return results
end

end,


-- reads the data from a given index as a UInt
_readUInt = function(data, index, size)
return love.data.unpack('<i' .. tostring(size), data:sub(index, index + (size-1)))
end,

-- turns a string into a windows WORD
_writeWord = function(str)
local word = ''
for i = 1, #str do
local c = str:sub(i,i)
word = word .. c .. ''
end
return word
end,


-- datatypes to use with readDataType
_DATA_TYPES = {
RESOURCE_DIRECTORY = {
Expand Down
7 changes: 2 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,8 @@ You can view the logs inside `output/version/build.log` after running the builde

## Todo
**.AppImages for Linux export**
Currently the `love-squashfs` lib handles decompressing squashfs binaries fine, however resquashing them has an issue I'm working on. At the moment the Linux export just uses the same AppImage directory format with a `AppRun` entrypoint and fused binary, which will work fine for most distros so no harm there (and also works a bit better for Steam distribution)
Currently the `love-squashfs` lib handles decompressing squashfs binaries fine, however resquashing them has an issue I'm working on. At the moment the Linux export just uses the same AppImage directory format with a `AppRun` entrypoint and fused binary, which will work fine for most distros so no harm there (it also works a bit better for Steam distribution, just set your installation path as the `AppRun` file)

Once the lib issues are fixed we'll be able to export as a proper `.AppImage` if needed, but I think we should still keep the `-linux.zip` output as an option for people who want it, similar to the additional 32bit option windows has.

**Windows.exe metadata**
The current `love-exedit` module is very basic and only lets you modify ICON resources at the moment. I've made some work on parsing the VERSION_INFO to change the name/description, but it still needs finishing off and writing the name/version changes before all the metadata stuff is finished.
Once the lib issues are fixed lovebuild could add a proper `.AppImage` if needed, but I think we should still keep the `-linux.zip` output as an option for people who want it, similar to the additional 32bit option windows has.

See [todo.md](todo.md) for stuff planned in future
17 changes: 4 additions & 13 deletions todo.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
# Before 1.0
[misc]
- would be nice to be able to have multiple configs in `build.lua`, say for steam vs non-steam builds
[love-exedit]
- add VERSION_INFO modifier for `love-exedit`


---


# Future Stuff
[misc]
- create github workflow action for people to use
- wildcards (*) for ignore/lib paths
- put module stuff (extract/compress etc) into threads to prevent hanging on large files
- put module stuff (extract/compress etc) into threads to prevent hanging on large files?
- would be nice to be able to have multiple configs in `build.lua`, say for steam vs non-steam builds

[linux]
- add `love-squashfs` :compress() for repackaging linux (chunk cap?)
- still keep the 'basic' output for linux, maybe -linux vs -AppImage ZIPs
+ still keep the 'basic' output for linux, maybe -linux vs -AppImage ZIPs

[love-exedit]
- add option to pass rsrc file
- file version doesnt seem to update correctly all the time?


---
Expand Down

0 comments on commit ad56f5c

Please sign in to comment.