Skip to content

Instantly share code, notes, and snippets.

@dixyes
Last active September 5, 2020 14:21
Show Gist options
  • Save dixyes/c27c622aad2a722fb02520f3edb29f09 to your computer and use it in GitHub Desktop.
Save dixyes/c27c622aad2a722fb02520f3edb29f09 to your computer and use it in GitHub Desktop.
my mc mod updater
AbyssalCraft-1.12.2-1.10.2.jar:AbyssalCraft*
astralsorcery-1.12.2-1.10.26.jar:astralsorcery*
BuildingGadgets-2.8.4.jar:BuildingGadgets*
gregtech-1.12.2-1.10.0.546.jar:gregtech*
industrialcraft-2-2.8.220-ex112.jar:industrialcraft*
Jade-0.1.0.jar:Jade*
jei_1.12.2-4.16.1.301.jar:jei_1.12.2*
just-enough-harvestcraft-1.12.2-1.7.2.jar:just-enough-harvestcraft-*
levelup2-1.5.6.jar:levelup2*
LittleMaidAppendPack-1.0.3.jar:LittleMaidAppendPack*
LittleMaidAvatar-1.0.4.jar:LittleMaidAvatar*
LittleMaidReengaged_FirisPatch-9.2.5.fp.049.jar:LittleMaidReengaged_FirisPatch*
LMLibrary-1.0.9.jar:LMLibrary*
MMLib-1.5.0.jar:MMLib*
phosphor-forge-mc1.12.2-0.2.7-universal.jar:phosphor*
SlashBlade-mc1.12-r33.jar:SlashBlade*
TLS-2.0.4-1.12.2.jar:TLS*
TofuCraftReload-0.1.0.8.jar:TofuCraftReload*
TravelersBackpack-1.12.2-1.0.33.jar:TravelersBackpack*
twilightforest-1.12.2-3.11.1020-universal[1].jar:twilightforest*
XaerosWorldMap_1.10.1_Forge_1.12.jar:XaerosWorldMap_*
Xaeros_Minimap_20.20.1_Forge_1.12.jar:Xaeros_Minimap_*
-- my mod updater using pure lua and ffi with win32apis
-- MIT license
-- copyright 2020 dixyes
local ffi = require("ffi")
local newPath = "new"
local confPath = "config.list"
local defModsPath = "%APPDATA%\\.minecraft\\mods\\1.12.2"
local logf = io.open("log","a")
ffi.cdef[[
uint32_t __stdcall FreeConsole(void);
uint32_t GetLastError();
int MultiByteToWideChar(unsigned int,uint32_t,const char *,int,wchar_t *,int);
int WideCharToMultiByte(unsigned int,uint32_t,const wchar_t *,int,char *,int,const char *,uint32_t *);
uint32_t SetProcessDpiAwareness(uint32_t);
uint32_t ExpandEnvironmentStringsW(const wchar_t *, wchar_t *,uint32_t);
void* __stdcall ILCreateFromPathW(const wchar_t*);
void* LoadLibraryExW(wchar_t*, void*, int);
void* GetProcAddress(void*, const char*);
typedef uint64_t __stdcall (* WNDPROC)(void*, uint32_t, uint64_t, uint64_t);
typedef struct tagWNDCLASSEXW {
uint32_t cbSize;
uint32_t style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
void* hInstance;
void* hIcon;
void* hCursor;
void* hbrBackground;
wchar_t* lpszMenuName;
wchar_t* lpszClassName;
void* hIconSm;
} WNDCLASSEXW;
typedef struct tagPOINT
{
int32_t x;
int32_t y;
} POINT;
typedef struct tagMSG {
void* hwnd;
uint32_t message;
void* wParam;
void* lParam;
uint32_t time;
POINT pt;
} MSG;
uint32_t __stdcall GetMessageW(MSG*,void*,uint32_t,uint32_t);
uint64_t __stdcall DefWindowProcW(void*, uint32_t, uint64_t, uint64_t);
void* GetModuleHandleW(wchar_t*);
void* __stdcall CreateWindowExW(
uint32_t,wchar_t*,wchar_t*,uint32_t,
int32_t,int32_t,int32_t,int32_t,
void*,void*,void*,void*
);
uint32_t __stdcall ShowWindow(void*,int);
uint32_t __stdcall UpdateWindow(void*);
uint32_t RegisterClassExW(const WNDCLASSEXW *);
void* GetStockObject(int);
void* LoadCursorW(void*, wchar_t*);
void* __stdcall LoadAcceleratorsW(void*,wchar_t*);
int32_t __stdcall TranslateAcceleratorW(void*, void*, MSG*);
uint32_t __stdcall TranslateMessage(MSG*);
uint32_t __stdcall DispatchMessageW(MSG*);
void __stdcall PostQuitMessage(int);
void* SendMessageW(void*,uint32_t,uint64_t,uint64_t);
typedef struct tagRECT
{
int32_t left;
int32_t top;
int32_t right;
int32_t bottom;
} RECT;
typedef struct tagPAINTSTRUCT {
void* hdc;
uint32_t fErase;
RECT rcPaint;
uint32_t fRestore;
uint32_t fIncUpdate;
char rgbReserved[32];
} PAINTSTRUCT;
void* BeginPaint(void*, PAINTSTRUCT*);
void* EndPaint(void*, PAINTSTRUCT*);
uint32_t TextOutW(void*,int,int,const wchar_t*,int);
uint32_t DrawTextExW(void*,const wchar_t*,int32_t,RECT*,uint32_t,void*);
uint32_t GetDpiForWindow(void*);
uint32_t GetDpiForSystem();
uint32_t SystemParametersInfoW(uint32_t, uint32_t, void*, uint32_t);
uint32_t SetWindowPos(void*,void*,int,int,int,int,uint32_t);
uint32_t GetWindowRect(void*,RECT*);
uint32_t GetClientRect(void*,RECT*);
typedef struct tagMINMAXINFO {
POINT ptReserved;
POINT ptMaxSize;
POINT ptMaxPosition;
POINT ptMinTrackSize;
POINT ptMaxTrackSize;
} MINMAXINFO;
typedef struct tagTEXTMETRICW
{
int32_t tmHeight;
int32_t tmAscent;
int32_t tmDescent;
int32_t tmInternalLeading;
int32_t tmExternalLeading;
int32_t tmAveCharWidth;
int32_t tmMaxCharWidth;
int32_t tmWeight;
int32_t tmOverhang;
int32_t tmDigitizedAspectX;
int32_t tmDigitizedAspectY;
wchar_t tmFirstChar;
wchar_t tmLastChar;
wchar_t tmDefaultChar;
wchar_t tmBreakChar;
int8_t tmItalic;
int8_t tmUnderlined;
int8_t tmStruckOut;
int8_t tmPitchAndFamily;
int8_t tmCharSet;
} TEXTMETRICW;
typedef struct tagLOGFONTW
{
int32_t lfHeight;
int32_t lfWidth;
int32_t lfEscapement;
int32_t lfOrientation;
int32_t lfWeight;
int8_t lfItalic;
int8_t lfUnderline;
int8_t lfStrikeOut;
int8_t lfCharSet;
int8_t lfOutPrecision;
int8_t lfClipPrecision;
int8_t lfQuality;
int8_t lfPitchAndFamily;
wchar_t lfFaceName[32/* LF_FACESIZE */];
} LOGFONTW;
typedef int (__stdcall * FONTENUMPROCW)(const LOGFONTW *, const TEXTMETRICW *, uint32_t, void*);
void* CreateFontIndirectW(const LOGFONTW *);
int EnumFontFamiliesW(void*,const wchar_t*,FONTENUMPROCW,void*);
void* SelectObject(void*, void*);
uint32_t SetConsoleOutputCP(uint32_t wCodePageID);
uint32_t GetConsoleOutputCP(void);
void* RtlCopyMemory(void*, void*, size_t);
void* GetDlgItem(void*, int);
int GetDlgCtrlID(void*);
typedef struct _browseinfoW {
void *hwndOwner;
void *pidlRoot;
wchar_t * pszDisplayName;
wchar_t * lpszTitle;
uint32_t ulFlags;
void * lpfn;
uint32_t lParam;
int iImage;
} BROWSEINFOW;
uint64_t SHBrowseForFolderW(BROWSEINFOW*);
uint32_t SHGetPathFromIDListW(const uint64_t,wchar_t *);
int __stdcall MessageBoxExW(void*, const wchar_t*,const wchar_t*,uint32_t,uint16_t);
int __stdcall lstrcpynW(wchar_t* ,const wchar_t*,int);
int GetWindowTextW(void*,wchar_t*,int);
uint32_t SetWindowTextW(void*, const wchar_t*);
uint32_t EnableWindow(void*,uint32_t);
uint32_t CopyFileW(wchar_t*,wchar_t*,uint32_t);
uint32_t DeleteFileW(wchar_t*);
void* CreateFileW(wchar_t*, uint32_t, uint32_t,void*,uint32_t,uint32_t,void*);\
typedef struct _fakeFFD {
uint32_t dwFileAttributes;
wchar_t field1[20];
wchar_t field2[260];
wchar_t field3[14];
} fakeFFD;
void* FindNextFileW(void* , fakeFFD*);
void* FindFirstFileExW(const wchar_t *,uint32_t,fakeFFD*,uint32_t,void*,uint32_t);
uint32_t CloseHandle(void*);
uint32_t GetFileAttributesW(const wchar_t*);
]]
local targetPathWCS = ffi.new("wchar_t[4096]")
local function printf(fmt, ...)
print(string.format(fmt, ...))
logf:write(string.format(fmt, ...),"\n")
end
local function d(func)
local ok, excOrRet = xpcall(func, debug.traceback)
if not ok then
printf(excOrRet)
return
end
return excOrRet
end
local Shell32 = ffi.load("Shell32.dll", true)
local User32 = ffi.load("User32.dll", true)
--ffi.load("Gdi32.dll", true)
local function _mb2wc(s)
local szbuf = s:len()
local buf = ffi.new('wchar_t[?]', szbuf+1)
local ret = ffi.C.MultiByteToWideChar(65001--[[ utf8]], 0, s, -1, buf, szbuf)
if -1 == ret then
return nil
end
return ret, buf
end
local function mb2wc(s)
local ret, buf = _mb2wc(s)
return buf
end
local function _wc2mb(s, cp)
local dummybuf = ffi.new('char[?]', 1024)
local l = ffi.C.WideCharToMultiByte(cp, 0, s, -1, dummybuf, 0, nil, nil)
local buf = ffi.new('char[?]', l+1)
if ffi.C.WideCharToMultiByte(cp, 0, s, -1, buf, l, nil, nil) == -1 then
return nil
end
return ffi.string(buf)
end
local function wc2mb(s)
return _wc2mb(s, 65001)
end
local function pathJoin(a,b)
if string.sub(a, #a, #a) == "\\" then
return a .. b
end
return a .. "\\" .. b
end
local function rm(filename)
--print("do rm", filename)
local ret = ffi.C.DeleteFileW(mb2wc(filename))
--local ret = 1
if ret == 0 then
local le = ffi.C.GetLastError()
--print(le)
return string.format("DeleteFileW: %d", le)
end
end
local function cp(src, dst)
--print("do cp", src, dst)
local ret = ffi.C.CopyFileW(mb2wc(src), mb2wc(dst), 1)
--local ok = 1
if 0 == ret then
local le = ffi.C.GetLastError()
--print(le)
if le == 5 --[[ERROR_ACCESS_DENIED]] then
return "eacces"
elseif le == 80 --[[ERROR_FILE_EXISTS]] then
return "eexist"
end
return string.format("CopyFileW: %d", le)
end
end
local function checkDirWritable(path)
local function touch(path)
--print("touch", path)
local handle = ffi.C.CreateFileW(
mb2wc(path),
0xc0000000,--[[GENERIC_READ | GENERIC_WRITE]]
3,--[[FILE_SHARE_READ|FILE_SHARE_WRITE]]
nil,
1,--[[CREATE_NEW]]
0x2, --[[FILE_ATTRIBUTE_HIDDEN]]
nil
)
local nhandle = ffi.cast("uint64_t", handle)
--print(nhandle, 18446744073709551615ULL == nhandle)
--os.exit()
if 0xffffffffffffffffULL == nhandle then
local le = ffi.C.GetLastError()
--print("touch", path,le)
if le == 5 --[[ERROR_ACCESS_DENIED]] then
return "eacces"
elseif le == 80 --[[ERROR_FILE_EXISTS]] then
return "eexist"
end
return string.format("CreateFileW: %d", le)
else
--print("touch", path, "ok")
ffi.C.CloseHandle(handle)
return nil
end
end
--print("testtouch is", touch(path.."\\CAN_I_CREATE_FILE_HERE"))
--print("testtouch is", touch(path.."\\新建文本文档.txt"))
local testPath = pathJoin(path,"CAN_I_CREATE_FILE_HERE")
--print("testtouch is", pathJoin(path,"CAN_I_CREATE_FILE_HERE"))
local err = touch(testPath)
--print("touch", err)
if nil == err then
err = rm(testPath)
if err then
--print("flag delete failed")
return false
end
elseif "eexist" == err then
--print("flag alreadey exist")
err = rm(testPath)
if err then
--print("flag delete failed with exist")
return false
end
err = touch(testPath)
if err then
--print("create flag failed")
return false
end
rm(testPath)
else
--print("create flag failed")
return false
end
return true
end
local arraylen = {
__index = function(self, k)
if k == "len" then
local l = 0
for _,__ in pairs(self) do
l = l + 1
end
return l
end
return rawget(k)
end
}
local files2Rm = {}
setmetatable(files2Rm,arraylen)
local files2Cp = {}
setmetatable(files2Cp,arraylen)
local hint = "baddir"
local readableHint = ""
local confList = {}
local targetPath = nil
local function checkTarget(target)
targetPath = nil
files2Rm = {}
setmetatable(files2Rm,arraylen)
files2Cp = {}
setmetatable(files2Cp,arraylen)
local fileAttr = ffi.C.GetFileAttributesW(mb2wc(target))
--printf("target %s is 0x%08x", target, fileAttr)
if fileAttr == 0xffffffff--[[INVILID]] or bit.band(0x00000010--[[FILE_ATTRIBUTE_DIRECTORY]], fileAttr)~=0x00000010 then
--printf("target %s is not dir", target)
hint = "baddir"
return false
end
if not checkDirWritable(target) then
hint = "baddir"
return false
end
local ffd = ffi.new("fakeFFD",{
--dwFileAttributes = 0x80 --[[FILE_ATTRIBUTE_NORMAL]] - not usable
})
--print(ffi.sizeof(ffd))
for newFn, oldPatterns in pairs(confList) do
--local fd = ffi.new('void*')
local skip = false
local removes = {}
--print()
--printf("oldpatterns is %s", oldPatterns)
for oldPattern in string.gmatch(oldPatterns, "[^,]+") do
local fullOp = pathJoin(target, oldPattern)
--printf("serch pattern is %s", oldPattern)
--printf("?? is %s", fullOp)
local firstFile = ffi.C.FindFirstFileExW(
mb2wc(fullOp), --path
1, -- basic info
ffd, -- find file data
0, --FindExSearchNameMatch
nil,
1 -- FIND_FIRST_EX_CASE_SENSITIVE
)
if 0xffffffffffffffffULL --[[INVALID_HANDLE_VALUE]] == ffi.cast("uint64_t", firstFile) then
--printf("FindFirstFileExW failed: %d", ffi.C.GetLastError())
break
end
repeat
local fn = wc2mb(ffd.field2)
if fn:len()>0 then
--printf("determining %s should be delete", fn)
local attr = ffd.dwFileAttributes --ffi.C.GetFileAttributesW(mb2wc(pathJoin(target, fn)))
if fileAttr ~= 0xffffffff--[[INVILID]] and
tonumber(bit.band(0x00000050--[[FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE]], attr)) == 0 then
--printf("skip file %s with attr 0x%08x", fn, attr)
if fn ~= oldMatch and fn == newFn then
-- alreadey have
--print("already exist")
skip = true
break
end
--printf("toremove %s", fn)
table.insert(removes, fn)
end
end
until skip or nil == ffi.C.FindNextFileW(firstFile, ffd)
end
if not skip then
--print("add", newFn)
files2Cp[newFn] = true
for _,v in pairs(removes) do
--print("rm", v)
files2Rm[v] = true
end
end
end
if files2Cp.len == 0 and files2Rm.len == 0 then
--print("nothing to do")
hint = "nothing"
return true
end
hint = "ok"
targetPath = target
return true
end
local function doChange(target)
local rmSucc = 0
for fn, ok in pairs(files2Rm) do
if ok then
--print("rm", ok, path, fn)
local err = rm(pathJoin(target, fn))
if err then
printf("rm %s failed: %s", pathJoin(target, fn), err)
else
rmSucc = rmSucc + 1
end
end
end
local cpSucc = 0
for fn, ok in pairs(files2Cp) do
if ok then
--print("cp", ok, newPath, fn, pathJointarget, fn)
local err = cp(pathJoin(newPath, fn), pathJoin(target, fn))
if err then
printf("cp %s to %s failed: %s", pathJoin(newPath, fn), pathJoin(target, fn), err)
else
cpSucc = cpSucc + 1
end
end
end
return cpSucc, rmSucc
end
local function parseConf(path)
local data, err= io.open(path,"r")
if not data then
return string.format("can't open config file %s", path)
end
for line in data:lines() do
for newFn, oldMatch in string.gmatch(line, "(.+):(.+)") do
confList[newFn] = oldMatch
break
end
end
return nil
end
local function fuckdpi()
d(function()
local Shcore = ffi.load("Shcore.dll", true)
if nil == Shcore then
printf("fuckdpi: not supp0rt: no dll")
return
end
if nil ~= Shcore.SetProcessDpiAwareness then
local hr = Shcore.SetProcessDpiAwareness(2)
--printf("fuckdpi: SetProcessDpiAwareness ret 0x%08x", hr)
end
end)
end
local function registerWinClass(attributes)
local wincls = {
lpfnWndProc = attributes.winProc,
lpszClassName = mb2wc(attributes.className),
cbSize = ffi.sizeof("WNDCLASSEXW"),
style = 3, --[[CS_HREDRAW | CS_VREDRAW]]
hCursor = ffi.C.LoadCursorW(nil, ffi.cast("wchar_t*", 32512)--[[IDC_ARROW]]),
hbrBackground = ffi.cast("void *", 5),
--ffi.C.GetStockObject(0--[[WHITE_BRUSH]]),
}
local wcex = ffi.new("WNDCLASSEXW", wincls)
--print("pre RegisterClassExW")
if 0 == ffi.C.RegisterClassExW(wcex) then
local err = ffi.C.GetLastError()
return string.format("RegisterWinClassExW: %d", err)
end
--print("post RegisterClassExW")
return nil
end
local g_dpi = 96
local metrics = {}
function initMetrics()
metrics.fontHeight = 18/96*g_dpi
metrics.selTextRight = 30 + 120/96*g_dpi
metrics.selTextBottom = 10 + 23/96*g_dpi
metrics.pathBrowseWinLeft = 642/96*g_dpi
metrics.bottomButtonTop = 457/96*g_dpi
metrics.stateTextTop = 407/96*g_dpi
metrics.cancelButtonLeft = 659/96*g_dpi
metrics.contentBoxVMargin = 10
metrics.contentBoxHMargin = 30
end
local function makeWinProc(win)
local defRoutines = {
[0x0002 --[[WM_DESTROY]]] = function()
ffi.C.PostQuitMessage(0)
end,
}
local routines = win.routines
setmetatable(routines, {__index = defRoutines})
local cb = function(hwnd, message, wparam, lparam)
local ok, excOrRet = xpcall(function()
--print(win.specialCbs)
--if win.specialCbs and win.specialCbs.preRoutine then
-- win.specialCbs.preRoutine(hwnd, message, wparam, lparam)
--end
if routines[message] then
local ret = routines[message](win, hwnd, message, wparam, lparam)
if nil ~= ret then
return ret
end
end
--if win.specialCbs and win.specialCbs.postRoutine then
-- win.specialCbs.postRoutine(hwnd, message, wparam, lparam)
--end
end, debug.traceback)
if not ok then
printf("on message process:")
printf(excOrRet)
return ffi.C.DefWindowProcW(hwnd, message, wparam, lparam)
end
return excOrRet or ffi.C.DefWindowProcW(hwnd, message, wparam, lparam)
end
return ffi.cast("WNDPROC", cb)
end
local function createWin(win, options)
local wincls = win.winClass
if nil == wincls then
wincls = {}
end
if win.routines then
local err = registerWinClass(setmetatable(wincls, {__index = {
winProc = makeWinProc(win),
className = win.className
}}))
if nil ~= err then
return "registerWinClass: " .. err, nil
end
end
--print(1)
local defOptions = {
exStyle = 0,
--title = "Hello, Windows",
style = 0xcf0000, --[[WS_OVERLAPPEDWINDOW]]
x = 0x80000000, --[[CW_USEDEFAULT]]
y = 0x80000000, --[[CW_USEDEFAULT]]
w = 0x80000000, --[[CW_USEDEFAULT]]
h = 0x80000000, --[[CW_USEDEFAULT]]
parent = nil,
menu = nil,
inst = nil,
lparam = nil
}
if nil == options then
options = {}
end
setmetatable(options, {__index = defOptions})
local title = type(options.title) == "string" and mb2wc(options.title) or options.title
--print("title is", title)
--print("pre CreateWindowExW", ffi.C.CreateWindowExW, User32.CreateWindowExW)
--print(string.format(
--[[
CreateWindowExW(%p)(
exStyle=%08x, className=L"%s", title=L"%s", style=%08x,
x=%d, y=%d, w=%d, h=%d,
parent=%p, menu=%p, inst=%p, lparam=%p
)]]
-- ,ffi.C.CreateWindowExW,
-- options.exStyle,
-- win.className,
-- options.title,
-- options.style,
-- options.x,
-- options.y,
-- options.w,
-- options.h,
-- options.parent,
-- options.menu,
-- options.inst,
-- options.lparam))
local hwnd = ffi.C.CreateWindowExW(
options.exStyle,
mb2wc(win.className),
title,
options.style,
options.x,
options.y,
options.w,
options.h,
options.parent,
options.menu,
options.inst,
options.lparam)
--print("post CreateWindowExW")
if nil == hwnd then
local err = ffi.C.GetLastError()
return string.format("failed CreateWindowExW: %d", err), nil
end
return nil, hwnd
end
local usableUIFont = nil
local function selUIFont(hdc)
if usableUIFont then
return usableUIFont
end
local prefer = {
"Microsoft YaHei UI",
"Microsoft JhengHei UI",
"思源黑体",
"思源黑體",
"Modern",
0
}
local all = {}
local function enumproc (lf, tm, ft, lp)
d(function()
--print("font:",wc2mb(lf.lfFaceName))
table.insert(all, wc2mb(lf.lfFaceName))
end)
return 1
end
local cb = ffi.cast("FONTENUMPROCW", enumproc)
if 1 ~= ffi.C.EnumFontFamiliesW(hdc, nil, cb, nil) then
print("selUIFont: my cb failed")
end
--print(1)
local found = #prefer + 1
local foundName = "Modern"
for _, v in pairs(all) do
--print(v)
local newFound = #prefer + 1
for i, a in pairs(prefer) do
--print(a,v)
if a == v then
newFound = i
--print(newFound)
break
end
end
if newFound < found then
found = newFound
foundName = v
--print("use", v)
end
if newFound == 0 then
break
end
end
--print(found)
usableUIFont = foundName
return usableUIFont
end
local fonts = {}
local function makeFont(name, numSize)
local size = math.floor(numSize + 0.5)
--printf("makeFont %s at %d", name, size)
if fonts[name] and fonts[name][size] then
--print("use already exist font")
return nil, fonts[name][size]
end
-- fuck ffi type converison
local fontWCS = ffi.new("wchar_t[32]")
ffi.C.MultiByteToWideChar(65001--[[ utf8]], 0, name, -1, fontWCS, 32)
local logfont = ffi.new("LOGFONTW",{
lfHeight = size,
--lfCharSet = 1,--[[DEFAULT_CHARSET]]
--lfOutPrecision = 2 ,--[[OUT_CHARACTER_PRECIS]]
lfFaceName = fontWCS,
})
local font = ffi.C.CreateFontIndirectW(logfont)
if nil == font then
return string.format("CreateFontIndirectW: %d",ffi.C.GetLastError()), nil
end
if nil == fonts[name] then
fonts[name] = {}
end
fonts[name][size] = font
return nil, font
end
local drawTextBuf = ffi.new("wchar_t[4096]")
local function drawText(hdc, text, inRect, size, attr)
local fontName = selUIFont(hdc)
--print(fontName)
--print("pre makeFont")
local err, font = makeFont(fontName, size)
--print("fontobj:", font)
if nil == font then
printf("makeFont failed: %s", err)
else
ffi.C.SelectObject(hdc, font)
end
--print(1)
ffi.fill(drawTextBuf, ffi.sizeof("wchar_t[4096]"))
--print(2)
local l = ffi.C.MultiByteToWideChar(65001--[[ utf8]], 0, text, -1, drawTextBuf, 4096)
--print(3)
local rect = ffi.new("RECT", inRect)
ffi.C.DrawTextExW(hdc, drawTextBuf, l-1, rect, attr or 0x25--[[DT_SINGLELINE | DT_CENTER | DT_VCENTER]], nil)
end
local defChildWin = {
recvParentRect = function(self, rect)
self.parentH = rect.bottom - rect.top
self.parentW = rect.right - rect.left
end,
fontUsed = nil,
postPaint = function(self, hwnd)
if usableUIFont then
--print("changing font for text")
local err, font = makeFont(usableUIFont, metrics.fontHeight)
if nil == err then
if self.fontUsed == font then
--print("not nessesery updat font")
return
end
--print('SendMessageW', hwnd, 0x0030--[[WM_SETFONT]], font, 0)
--d(function()
ffi.C.SendMessageW(hwnd, 0x0030--[[WM_SETFONT]], ffi.cast("uint64_t", font), 0)
--end)
--print('post SendMessageW')
self.fontNotSet = false
end
end
end,
}
local selTextWin = {
className = "Static",
style = 0x50000001, --[[WS_CHILD | WS_VISIBLE | SS_CENTER]]
getX = function(self)
metrics.selTextRight = metrics.contentBoxHMargin + self:getW()
--print("here metrics.selTextRight", metrics.selTextRight)
return metrics.contentBoxHMargin
end,
getW = function(self)
return 120/96*g_dpi
end,
getY = function(self)
metrics.selTextBottom = metrics.contentBoxVMargin + 23/96*g_dpi
return metrics.contentBoxVMargin
end,
getH = function(self)
return 23/96*g_dpi
end,
}
setmetatable(selTextWin, {__index = defChildWin})
local stateTextWin = {
className = "Static",
style = 0x50000001, --[[WS_CHILD | WS_VISIBLE | SS_CENTER]]
getX = function(self)
return metrics.contentBoxHMargin
end,
getW = function(self)
return self.parentW - 2 * metrics.contentBoxHMargin
end,
getY = function(self)
local bottom = metrics.bottomButtonTop - 10
metrics.stateTextTop = bottom - 40/96*g_dpi
return bottom - 40/96*g_dpi
end,
getH = function(self)
return metrics.bottomButtonTop - 10 - self:getY()
end,
}
setmetatable(stateTextWin, {__index = defChildWin})
local pathEditWin = {
className = "EDIT",
--exStyle = 0x200,--[[WS_EX_CLIENTEDGE]]
style = 0x50810000, --[[WS_CHILD | WS_VISIBLE | WS_TABSTOP]]
getX = function(self)
--print(metrics.selTextRight)
return metrics.selTextRight + 10
end,
getW = function(self)
return metrics.pathBrowseWinLeft - self:getX() - 10
end,
getY = function(self)
return metrics.contentBoxVMargin
end,
getH = function(self)
return 23/96*g_dpi
end,
}
setmetatable(pathEditWin, {__index = defChildWin})
local pathBrowseWin = {
className = "BUTTON",
title = "浏览...",
style = 0x50818f00, --[[WS_CHILD | WS_VISIBLE | WS_BORDER | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_FLAT | BS_TEXT | WS_TABSTOP]]
getX = function(self)
local left = self.parentW - metrics.contentBoxHMargin - self:getW()
metrics.pathBrowseWinLeft = left
--print("calced is",(metrics.pathBrowseWinLeft))
return left
end,
getW = function(self)
return 50/96*g_dpi
end,
getY = function(self)
return metrics.contentBoxVMargin
end,
getH = function(self)
return 23/96*g_dpi
end,
}
setmetatable(pathBrowseWin, {__index = defChildWin})
local cancelButtonWin = {
className = "BUTTON",
style = 0x50818f00, --[[WS_CHILD | WS_VISIBLE | WS_BORDER | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_FLAT | BS_TEXT|WS_TABSTOP]]
getX = function(self)
local left = self.parentW - metrics.contentBoxHMargin - self:getW()
metrics.cancelButtonLeft = left
--print("here cancelButtonLeft", metrics.cancelButtonLeft)
return left
end,
getW = function(self)
return 50/96*g_dpi
end,
getY = function(self)
local top = self.parentH - metrics.contentBoxVMargin - self:getH()
metrics.bottomButtonTop = top
--print("here bottomButtonTop", top)
return top
end,
getH = function(self)
return 23/96*g_dpi
end,
}
setmetatable(cancelButtonWin, {__index = defChildWin})
local okButtonWin = {
className = "BUTTON",
style = 0x50818f00, --[[WS_CHILD | WS_VISIBLE | WS_BORDER | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_FLAT | BS_TEXT|WS_TABSTOP]]
getX = function(self)
return metrics.cancelButtonLeft - 10 - self:getW()
end,
getW = function(self)
return 50/96*g_dpi
end,
getY = function(self)
return metrics.bottomButtonTop
end,
getH = function(self)
return 23/96*g_dpi
end,
}
setmetatable(okButtonWin, {__index = defChildWin})
local leftFrameWin = {
className = "LISTBOX",
--className = "leftFrameWin",
style = 0x50a0400a, --[[WS_CHILD |WS_VISIBLE | WS_VSCROLL | WS_BORDER | LBS_SORT | LBS_NOSEL | LBS_MULTIPLESEL]]
--exStyle = 0x00004000, --[[WS_EX_LEFTSCROLLBAR]]
getX = function(self)
return metrics.contentBoxHMargin
end,
getW = function(self)
return (self.parentW - (3*metrics.contentBoxHMargin))/2
end,
getY = function(self)
return metrics.selTextBottom + 10
end,
getH = function(self)
return metrics.stateTextTop - 10 - self:getY()
end,
}
setmetatable(leftFrameWin, {__index = defChildWin})
local rightFrameWin = {
className = "LISTBOX",
--className = "leftFrameWin",
style = 0x50a0400a, --[[WS_CHILD |WS_VISIBLE | WS_VSCROLL | WS_BORDER | LBS_SORT | LBS_NOSEL | LBS_MULTIPLESEL]]
--exStyle = 0x00004000, --[[WS_EX_LEFTSCROLLBAR]]
getX = function(self)
return self.parentW - self:getW() - metrics.contentBoxHMargin
end,
getW = function(self)
return (self.parentW - (3*metrics.contentBoxHMargin))/2
end,
getY = function(self)
return metrics.selTextBottom + 10
end,
getH = function(self)
return metrics.stateTextTop - 10 - self:getY()
end,
}
setmetatable(rightFrameWin, {__index = defChildWin})
local function lbWinUpdate(self, hint, targetDict)
--printf("%s:update",self.name)
if not self.hwnd then
return
end
ffi.C.SendMessageW(self.hwnd, 0x0184--[[LB_RESETCONTENT]], 0, 0)
for k,_ in pairs(targetDict) do
local ret = ffi.C.SendMessageW(self.hwnd, 0x0180--[[LB_ADDSTRING]], 0, ffi.cast("uint64_t", mb2wc(hint .. k)))
local idx = tonumber(ffi.cast("int64_t", ret))
ffi.C.SendMessageW(self.hwnd, 0x019a--[[LB_SETITEMDATA]], idx, idx)
--print("add",k, "as", idx)
if idx < 0 then
printf("SendMessageW failed: %d", ffi.C.GetLastError())
return
end
end
end
local mianWin = {
updateChildren = function (self, selfHwnd)
for _, child in pairs(self.children) do
if child.update then
child:update(selfHwnd)
end
end
end,
children = {
{
name = "selTextWin",
win = selTextWin,
title = "选择jar所在的目录",
},
{
name = "pathBrowseWin",
win = pathBrowseWin,
title = "浏览...",
onClick = function(self, parent, parentHwnd)
--print("im clicked", parent, parentHwnd)
local targetPathIL = Shell32.ILCreateFromPathW(targetPathWCS)
local function initFolderCb(hwnd, message, wparam, lparam)
if message == 1 --[[BFFM_INITIALIZED]] then
--print("sending msg", hWnd)
ffi.C.SendMessageW(hwnd, 1126 --[[BFFM_SETSELECTION]], 0, ffi.cast("uint64_t", targetPathIL))
return 1
end
return 0
end
local bi = ffi.new("BROWSEINFOW")
bi.lpfn=targetPathIL and ffi.cast("WNDPROC", initFolderCb)
bi.lpszTitle = mb2wc("选一下mod目录,jar在的那个目录\n例如.minecraft\\mods\\1.12.2")
bi.ulFlags= 624 --[[BIF_VALIDATE | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON]]
local ret = tonumber(Shell32.SHBrowseForFolderW(bi))
--print(ret,Shell32.SHGetPathFromIDListW,targetPathWCS)
if 0 == ret then
--print("canceled BrowseForFolder")
return
end
if 0 == Shell32.SHGetPathFromIDListW(ret, targetPathWCS) then
printf("failed SHGetPathFromIDListW: %d", ffi.C.GetLastError())
return
end
--print("selected",wc2mb(targetPathWCS))
parent.children.pathEditWin:updateTitle()
checkTarget(wc2mb(targetPathWCS))
parent:updateChildren(parentHwnd)
end,
},
{
name = "pathEditWin",
win = pathEditWin,
getTitle = function(self) return targetPathWCS end,
updateTitle = function(self)
if not self.hwnd then
return
end
ffi.C.SendMessageW(self.hwnd, 0x000c--[[WM_SETTEXT]], 0, ffi.cast("uint64_t", targetPathWCS))
--self.hwnd
end,
onChange = function(self, parent, parentHwnd)
if not self.hwnd then
return
end
if 0 == ffi.C.GetWindowTextW(self.hwnd, targetPathWCS, 4096) then
printf("0 == GetWindowTextW, GetLastError() = %d", ffi.C.GetLastError())
end
--print("updating targetPathWCS", wc2mb(targetPathWCS))
checkTarget(wc2mb(targetPathWCS))
parent:updateChildren(parentHwnd)
end,
},
{
name = "cancelButtonWin",
win = cancelButtonWin,
title = "算了",
onClick = function(self, parent, parentHwnd)
ffi.C.PostQuitMessage(0)
end
},
{
name = "okButtonWin",
win = okButtonWin,
title = "行吧",
onClick = function(self, parent, parentHwnd)
if hint ~= "ok" then
printf("wtf?")
ffi.C.MessageBoxExW(parentHwnd, mb2wc("我觉得不行"), mb2wc("???"), 0, 65001)
return
end
local ret = ffi.C.MessageBoxExW(
parentHwnd,
mb2wc(string.format("确认要这么做吗\r\n更新到 %s\r\n", targetPath) .. readableHint),
mb2wc("确认一下"),
0x2101--[[MB_OKCANCEL|MB_TASKMODAL|MB_DEFBUTTON2]],
65001
)
if 1 --[[ID_OK]] == ret then
local ca = files2Cp.len
local ra = files2Rm.len
local c, r = doChange(targetPath)
if c == ca and r == ra then
ffi.C.MessageBoxExW(parentHwnd, mb2wc("成了"), mb2wc("OJBK"), 0, 65001)
else
ffi.C.MessageBoxExW(
parentHwnd,
mb2wc(string.format("%d 个要复制的文件中 %d 个复制失败了\r\n%d 个要删除的文件中 %d 个删除失败了",ca,ca-c,ra,ra-r)),
mb2wc("不太行"),
0,
65001
)
end
checkTarget(targetPath)
parent:updateChildren(parentHwnd)
end
end,
update = function(self, parentHwnd)
if hint == "ok" then
--print("enabling")
ffi.C.SetWindowTextW(self.hwnd, mb2wc("行吧"))
ffi.C.EnableWindow(self.hwnd, 1)
return
end
ffi.C.SetWindowTextW(self.hwnd, mb2wc("不行"))
ffi.C.EnableWindow(self.hwnd, 0)
end
},
{
name = "stateTextWin",
win = stateTextWin,
title = "ceafbabeda",
update = function(self, parentHwnd)
readableHint = "??? You should never see this"
if "nothing" == hint then
readableHint = "“今日无事可做”"
elseif "ok" == hint then
local numCp = files2Cp.len
local numRm = files2Rm.len
readableHint = (0 ~= numCp and string.format("新增 %s 个文件", numCp) or "没有新增文件") ..
(0 ~= numRm and string.format(",删除 %s 个文件", numRm) or ",没有删除文件")
elseif "baddir" == hint then
readableHint = "选择的目录不对"
end
ffi.C.SetWindowTextW(self.hwnd, mb2wc(readableHint))
end,
},
{
name = "leftFrameWin",
win = leftFrameWin,
update = function(self, parentHwnd)
lbWinUpdate(self, "添加 ", files2Cp)
end
},
{
name = "rightFrameWin",
win = rightFrameWin,
update = function(self, parentHwnd)
lbWinUpdate(self, "删除 ", files2Rm)
end
},
},
recvRect = function(self, hwnd)
if nil == self.clientRect then
self.clientRect = ffi.new("RECT")
end
--print("rect", rect)
if 0 == ffi.C.GetClientRect(hwnd, self.clientRect) then
printf("failed GetClientRect: %d", ffi.GetLastError())
self.clientRect.left = 0
self.clientRect.right = 1424
self.clientRect.top = 0
self.clientRect.bottom = 750
end
--print(rect.left, rect.right, rect.top, rect.bottom)
end,
updateChildrenRect = function(self)
for k,v in pairs(self.children) do
if v.hwnd then
--print("updateChildrenRect", k)
v.win:recvParentRect(self.clientRect)
if 0 == ffi.C.SetWindowPos(
v.hwnd,
nil,
v.win:getX(),
v.win:getY(),
v.win:getW(),
v.win:getH(),
0x0014 --[[SWP_NOZORDER | SWP_NOACTIVATE]]) then
printf("updateChildrenRect: failed SetWindowPos: %d", ffi.C.GetLastError())
end
end
end
end,
createChildren = function(self, hwnd)
for k,v in pairs(self.children) do
--print(string.format("createChildren %s, %p, %08x, %08x", v.name, v.win, v.win.style, v.win.exStyle or 0))
v.win:recvParentRect(self.clientRect)
local err, childhwnd = createWin(v.win, {
parent = hwnd,
exStyle = v.win.exStyle,
style = v.win.style,
title = v.title or (v.getTitle and v.getTitle()),
menu = ffi.cast("void*", k),
x = v.win:getX(),
y = v.win:getY(),
w = v.win:getW(),
h = v.win:getH(),
})
if nil ~= err then
printf("createWin failed err: %s", err)
end
v.hwnd = childhwnd
--if bit.band(v.win.style ) == nil then
local did = ffi.C.GetDlgCtrlID(childhwnd)
if k ~= did then
printf("did not match: k=%d, did=%d, err=0x%08x", k, did, ffi.C.GetLastError())
else
v.did = did
end
--end
if v.update then
v:update(hwnd)
end
end
end,
className = "myWin",
routines = {
[0x0001 --[[WM_CREATE]]] = function(win, hwnd, message, wparam, lparam)
--print("mianWin: WM_CREATE")
--print(win, win.recvSize)
win:recvRect(hwnd)
win:createChildren(hwnd)
--print("oncreate:", err, child)
end,
[0x0005 --[[WM_SIZE]]] = function(win,hwnd,msg,wp,lp)
--print(hwnd,msg,wp,lp)
win:recvRect(hwnd)
win:updateChildrenRect()
end,
[0x0024 --[[WM_GETMINMAXINFO]]] = function(win,hwnd,msg,wp,lp)
--print("WM_GETMINMAXINFO",hwnd,msg,wp,lp)
local mmi = ffi.cast("MINMAXINFO*", lp)
if nil == win.defMinX then
win.defMinX = mmi.ptMinTrackSize.x
win.defMinY = mmi.ptMinTrackSize.y
end
if g_dpi < 300 then
mmi.ptMinTrackSize.x = (g_dpi * 7.87401575--[[20cm in inch]])
mmi.ptMinTrackSize.y = (g_dpi * 5.51181102--[[14cm in inch]])
end
end,
[0x0111 --[[WM_COMMAND]]] = function(win, hwnd, message, wparam, lparam)
--printf("WM_COMMAND:\n\twp:0x%016x\n\tlp:0x%016x", tonumber(ffi.cast("uint64_t", wparam)), tonumber(ffi.cast("uint64_t", lparam)))
--print(ffi.cast("void*", lparam), win.children[1].hwnd)
local notify = tonumber(bit.rshift(bit.band(wparam, 0xffff0000), 16))
local did = tonumber(bit.band(wparam, 0xffff))
--printf("notify=0x%04x, did=0x%04x", notify, did)
if notify == 0 --[[BN_CLICKED]] then
if win.children[did].onClick then
win.children[did]:onClick(win, hwnd)
end
elseif notify == 0x0300 --[[EN_CHANGE]] then
if win.children[did].onChange then
--printf("calling win.children.%s:onChange",win.children[did].name)
win.children[did]:onChange(win, hwnd)
end
end
end,
--[0x004E --[[WM_NOTIFY]]] = function(win, hwnd, message, wparam, lparam)
-- print("WM_NOTIFY", wparam, lparam)
--end,
[0x02E0 --[[WM_DPICHANGED]]] = function (win, hwnd, message, wparam, lparam)
g_dpi = tonumber(bit.rshift(bit.band(wparam,0xffff0000), 16))
--print(g_dpi)
-- do things here
local prcNewWindow = ffi.cast("RECT*", lparam)
ffi.C.SetWindowPos(
hwnd,
nil,
prcNewWindow.left,
prcNewWindow.top,
prcNewWindow.right - prcNewWindow.left,
prcNewWindow.bottom - prcNewWindow.top,
0x14 --[[SWP_NOZORDER | SWP_NOACTIVATE]]
)
win:recvRect(hwnd)
--for k,v in pairs(win.texts) do
--v:setMetrics(win)
--end
win:updateChildrenRect()
metrics.fontHeight = 18/96*g_dpi
end,
[0x000F --[[WM_PAINT]]] = function(win, hwnd, message, wparam, lparam)
--print("WM_PAINT", wparam)
win:recvRect(hwnd)
local ps = ffi.new("PAINTSTRUCT")
local hdc = ffi.C.BeginPaint(hwnd, ps)
--print("hdc:", hdc)
if nil == hdc then
printf("failed BeginPaint: %d", ffi.C.GetLastError())
return
else
selUIFont(hdc)
--print(-1)
--print(metrics.fontHeight)
--for k,v in pairs(win.texts) do
--print(v.size)
--drawText(hdc, v.text or v:getText(), v:getRect(win), v.size or v.getSize())
--v:setMetrics(win,metrics)
--end
ffi.C.EndPaint(hwnd, ps)
end
for _, child in pairs(win.children) do
--print(child.win.postPaint)
if child.win.postPaint then
--printf("calling postPaint for %s",child.name)
child.win:postPaint(child.hwnd)
end
end
end
}
}
setmetatable(mianWin.children, {__index=function(mytable, index)
for _,v in pairs(mytable) do
if v.name == index then
return v
end
end
end})
local function fuckAA()
ffi.C.SystemParametersInfoW(
75, --[[SPI_SETFONTSMOOTHING]]
1,
nil,
3 --[[SPIF_UPDATEINIFILE | SPIF_SENDCHANGE)]]
)
ffi.C.SystemParametersInfoW(
8203,--[[SPI_SETFONTSMOOTHINGTYPE]]
0,
ffi.cast("void*", 1600),
3 --[[SPIF_UPDATEINIFILE | SPIF_SENDCHANGE]]
)
end
local function mian()
ffi.C.ExpandEnvironmentStringsW(mb2wc("%APPDATA%\\.minecraft\\mods\\1.12.2"), targetPathWCS, 4096)
local targetPath = wc2mb(targetPathWCS)
local err = parseConf(confPath)
if err then
printf("parseConf: %s",err)
return 1
end
local ret = checkTarget(targetPath)
--local ret = checkTarget("..\\mods")
--print(ret, hint)
--os.exit(0)
fuckdpi()
--fuckAA()
g_dpi = ffi.C.GetDpiForSystem()
initMetrics()
--print("pre create main win")
local err, hwnd = createWin(mianWin, {
title = "来更新一波mod吧",
w = g_dpi * 7.87401575,--[[20cm in inch]]
h = g_dpi * 5.51181102,--[[14cm in inch]]
})
if nil ~= err then
printf("failed createWindow: %s", err)
return 1
end
--print("post create main win")
ffi.C.FreeConsole()
ffi.C.ShowWindow(hwnd, 9--[[SW_RESTORE]])
ffi.C.UpdateWindow(hwnd)
--local accel = ffi.C.LoadAcceleratorsW(nil, className)
local msg = ffi.new("MSG")
while 0 ~= ffi.C.GetMessageW(msg, nil, 0, 0) do
--if 0 == ffi.C.TranslateAcceleratorW(msg.hwnd, accel, msg) then
ffi.C.TranslateMessage(msg)
ffi.C.DispatchMessageW(msg)
--end
end
printf("done")
end
function entry()
--ffi.C.MessageBoxW(nil, mb2wc("你好测试测试"), mb2wc("Test"), 0)
--print(Shell32.ILCreateFromPathW(mb2wc("C:\\")))
--os.exit(0)
local origcp = ffi.C.GetConsoleOutputCP()
ffi.C.SetConsoleOutputCP(65001)
local ret, exc = xpcall(function ()
mian(arg)
end, debug.traceback)
if nil~=exc then
printf(exc)
end
ffi.C.SetConsoleOutputCP(origcp)
os.exit(ret)
end
entry()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment