@@ -75,8 +75,81 @@ function GetAsyncCount()
7575 return 0
7676end
7777
78- -- Search Handles
79- function NewFileSearch () end
78+ -- [PoEW Patch] Basic file search for headless mode
79+ -- Supports exact paths and simple glob patterns (e.g. "*.part*")
80+ do
81+ local function matchGlob (filename , pattern )
82+ local luaPat = pattern :gsub (" %." , " %%." ):gsub (" %*" , " .*" )
83+ return filename :match (" ^" .. luaPat .. " $" ) ~= nil
84+ end
85+
86+ local IS_WINDOWS = package.config :sub (1 ,1 ) == " \\ "
87+
88+ local function listDir (dir )
89+ local files = {}
90+ local cmd
91+ if IS_WINDOWS then
92+ cmd = ' dir /b "' .. dir :gsub (" /" , " \\ " ) .. ' " 2>nul'
93+ else
94+ cmd = ' ls -1 "' .. dir .. ' " 2>/dev/null'
95+ end
96+ local handle = io.popen (cmd )
97+ if handle then
98+ for line in handle :lines () do
99+ line = line :gsub (" %s+$" , " " ) -- trim trailing \r on Windows
100+ if line ~= " " then
101+ files [# files + 1 ] = line
102+ end
103+ end
104+ handle :close ()
105+ end
106+ return files
107+ end
108+
109+ local fileSearchClass = {}
110+ fileSearchClass .__index = fileSearchClass
111+
112+ function fileSearchClass :GetFileName ()
113+ return self .files [self .index ]
114+ end
115+
116+ function fileSearchClass :GetFileModifiedTime ()
117+ -- Return a fixed timestamp; the .bin cache check will just re-extract
118+ return 0
119+ end
120+
121+ function fileSearchClass :NextFile ()
122+ self .index = self .index + 1
123+ return self .index <= # self .files
124+ end
125+
126+ function NewFileSearch (pattern )
127+ if not pattern or pattern == " " then return nil end
128+ local dir = pattern :gsub (" [/\\ ][^/\\ ]*$" , " " )
129+ local filePattern = pattern :gsub (" .*[/\\ ]" , " " )
130+
131+ if not filePattern :find (" [%*%?]" ) then
132+ -- Exact path — just check existence
133+ local f = io.open (pattern , " r" )
134+ if not f then return nil end
135+ f :close ()
136+ local obj = setmetatable ({ files = { filePattern }, index = 1 , dir = dir }, fileSearchClass )
137+ return obj
138+ end
139+
140+ -- Glob pattern — list directory and filter
141+ local entries = listDir (dir )
142+ local matched = {}
143+ for _ , name in ipairs (entries ) do
144+ if matchGlob (name , filePattern ) then
145+ matched [# matched + 1 ] = name
146+ end
147+ end
148+ table.sort (matched )
149+ if # matched == 0 then return nil end
150+ return setmetatable ({ files = matched , index = 1 , dir = dir }, fileSearchClass )
151+ end
152+ end
80153
81154-- General Functions
82155function SetWindowTitle (title ) end
@@ -88,32 +161,83 @@ function ShowCursor(doShow) end
88161function IsKeyDown (keyName ) end
89162function Copy (text ) end
90163function Paste () end
91- function Deflate (data )
92- -- TODO: Might need this
93- return " "
94- end
95- function Inflate (data )
96- -- TODO: And this
97- return " "
164+ -- [PoEW Patch] Inflate/Deflate via LuaJIT FFI + system zlib
165+ do
166+ local ok , ffi = pcall (require , " ffi" )
167+ if ok and ffi then
168+ ffi .cdef [[
169+ unsigned long compressBound (unsigned long sourceLen );
170+ int compress2 (uint8_t * dest , unsigned long * destLen ,
171+ const uint8_t * source , unsigned long sourceLen , int level );
172+ int uncompress (uint8_t * dest , unsigned long * destLen ,
173+ const uint8_t * source , unsigned long sourceLen );
174+ ]]
175+ local zlib
176+ -- Try loading zlib from common locations (Windows, Linux, macOS)
177+ local libs = { " zlib1" , " z" , " libz.so.1" , " libz.1.dylib" }
178+ for _ , name in ipairs (libs ) do
179+ local lok , lib = pcall (ffi .load , name )
180+ if lok then zlib = lib ; break end
181+ end
182+ if zlib then
183+ function Deflate (data )
184+ if not data or # data == 0 then return " " end
185+ local srcLen = # data
186+ local bound = zlib .compressBound (srcLen )
187+ local buf = ffi .new (" uint8_t[?]" , bound )
188+ local destLen = ffi .new (" unsigned long[1]" , bound )
189+ local ret = zlib .compress2 (buf , destLen , data , srcLen , 9 )
190+ if ret ~= 0 then return " " end
191+ return ffi .string (buf , destLen [0 ])
192+ end
193+ function Inflate (data )
194+ if not data or # data == 0 then return " " end
195+ local srcLen = # data
196+ -- Try progressively larger buffers (data can expand 10-50x)
197+ for mult = 10 , 100 , 10 do
198+ local destSize = srcLen * mult
199+ local buf = ffi .new (" uint8_t[?]" , destSize )
200+ local destLen = ffi .new (" unsigned long[1]" , destSize )
201+ local ret = zlib .uncompress (buf , destLen , data , srcLen )
202+ if ret == 0 then
203+ return ffi .string (buf , destLen [0 ])
204+ end
205+ -- ret == -5 means buffer too small, try larger
206+ if ret ~= - 5 then return " " end
207+ end
208+ return " "
209+ end
210+ print (" zlib loaded via FFI — Inflate/Deflate available" )
211+ else
212+ print (" WARNING: zlib not found — timeless jewel data unavailable" )
213+ function Deflate (data ) return " " end
214+ function Inflate (data ) return " " end
215+ end
216+ else
217+ function Deflate (data ) return " " end
218+ function Inflate (data ) return " " end
219+ end
98220end
99221function GetTime ()
100222 return 0
101223end
224+ -- [PoEW Patch] Return actual paths for data file resolution
102225function GetScriptPath ()
103- return " "
226+ return _G . POB_SCRIPT_DIR or " . "
104227end
105228function GetRuntimePath ()
106- return " "
229+ local base = _G .POB_SCRIPT_DIR or " ."
230+ return base .. " /../runtime"
107231end
108232function GetUserPath ()
109- return " "
233+ return _G .POB_SCRIPT_DIR or " ."
234+ end
235+ function GetWorkDir ()
236+ return _G .POB_SCRIPT_DIR or " "
110237end
111238function MakeDir (path ) end
112239function RemoveDir (path ) end
113240function SetWorkDir (path ) end
114- function GetWorkDir ()
115- return " "
116- end
117241function LaunchSubScript (scriptText , funcList , subList , ...) end
118242function AbortSubScript (ssID ) end
119243function IsSubScriptRunning (ssID ) end
@@ -170,16 +294,56 @@ function GetCloudProvider(fullPath)
170294end
171295
172296
297+
298+ -- [PoEW Patch] Determine script directory for robust path resolution
299+ -- Uses debug.getinfo instead of io.popen('pwd') for cross-platform support
300+ local function _poew_get_script_dir ()
301+ local info = debug and debug.getinfo and debug.getinfo (1 , ' S' )
302+ local src = info and info .source or ' '
303+ if type (src ) == ' string' and src :sub (1 ,1 ) == ' @' then
304+ local path = src :sub (2 )
305+ local dir = (path :gsub (' [^/\\ ]+$' , ' ' )):gsub (' [/\\ ]$' , ' ' )
306+ if dir ~= ' ' then return dir end
307+ end
308+ -- Fallback: probe for known files
309+ local probes = { ' .' , ' src' }
310+ for _ , d in ipairs (probes ) do
311+ local fh = io.open (d .. ' /HeadlessWrapper.lua' , ' r' )
312+ if fh then fh :close (); return d end
313+ end
314+ return ' .'
315+ end
316+ _G .POB_SCRIPT_DIR = _poew_get_script_dir ()
317+
173318local l_require = require
174319function require (name )
175320 -- Hack to stop it looking for lcurl, which we don't really need
176321 if name == " lcurl.safe" then
177322 return
178323 end
324+ -- [PoEW Patch] UTF-8 fallback for headless mode (no native .so on macOS)
325+ if name == " lua-utf8" then
326+ local ok , mod = pcall (l_require , name )
327+ if ok and type (mod ) == ' table' then return mod end
328+ local dir = _G .POB_SCRIPT_DIR or ' .'
329+ local fok , fmod = pcall (dofile , dir .. ' /lua-utf8.lua' )
330+ if fok and type (fmod ) == ' table' then return fmod end
331+ return {}
332+ end
179333 return l_require (name )
180334end
181335
182336
337+ -- [PoEW Patch] Add runtime Lua libraries to package.path
338+ local base = _G .POB_SCRIPT_DIR or " ."
339+ local runtimeLua = base .. " /../runtime/lua"
340+ local testPath = runtimeLua .. " /xml.lua"
341+ local fh = io.open (testPath , " r" )
342+ if fh then
343+ fh :close ()
344+ package.path = runtimeLua .. " /?.lua;" .. runtimeLua .. " /?/init.lua;" .. package.path
345+ end
346+
183347dofile (" Launch.lua" )
184348
185349-- Prevents loading of ModCache
@@ -217,3 +381,18 @@ function loadBuildFromJSON(getItemsJSON, getPassiveSkillsJSON)
217381 -- You now have a build without a correct main skill selected, or any configuration options set
218382 -- Good luck!
219383end
384+
385+ -- [PoEW Patch] CLI flag detection
386+ local function _poew_has_flag (flag )
387+ if type (arg ) ~= ' table' then return false end
388+ for i = 1 , # arg do if arg [i ] == flag then return true end end
389+ return false
390+ end
391+
392+ -- [PoEW Patch] JSON-RPC API server activation (env-gated)
393+ -- Set POB_API_STDIO=1 or pass --stdio to start the API server
394+ if os.getenv (' POB_API_STDIO' ) == ' 1' or _poew_has_flag (' --stdio' ) then
395+ local srvPath = (_G .POB_SCRIPT_DIR or ' .' ) .. ' /API/Server.lua'
396+ dofile (srvPath )
397+ return
398+ end
0 commit comments