Created
February 23, 2025 05:44
-
-
Save gabcasanova/c096ea3f20ed92f8bf267151f99c1b04 to your computer and use it in GitHub Desktop.
far 1a extractor inlua
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Lua 5.4 FAR Archive Extractor | |
-- Supports listing files, extracting individual files, and extracting all files with subfolder creation. | |
local function read_u32(file) | |
local bytes = file:read(4) | |
if not bytes or #bytes ~= 4 then return nil end | |
return string.unpack("<I4", bytes) | |
end | |
local function read_string(file, length) | |
return file:read(length) | |
end | |
local function create_directories(path) | |
local dir = "" | |
for part in string.gmatch(path, "[^\\]+") do | |
dir = dir .. part .. "\\" | |
if not os.rename(dir, dir) then | |
os.execute("mkdir " .. dir) | |
end | |
end | |
end | |
local function extract_file(file, offset, size, output_path) | |
file:seek("set", offset) | |
local data = file:read(size) | |
local output_file = io.open(output_path, "wb") | |
if not output_file then | |
error("Failed to create file: " .. output_path) | |
end | |
output_file:write(data) | |
output_file:close() | |
end | |
local function list_files(file_table) | |
print("Files in the archive:") | |
for i, entry in ipairs(file_table) do | |
print(string.format("%d: %s (Size: %d bytes)", i, entry.filename, entry.decompressed_size)) | |
end | |
end | |
local function extract_all_files(file, file_table, output_folder) | |
for _, entry in ipairs(file_table) do | |
local output_path = output_folder .. "\\" .. entry.filename | |
create_directories(output_path:match("^(.*\\)")) | |
extract_file(file, entry.data_offset, entry.compressed_size, output_path) | |
print("Extracted: " .. output_path) | |
end | |
end | |
local function main() | |
io.write("Enter the path to the .FAR file: ") | |
local far_path = io.read() | |
local far_file = io.open(far_path, "rb") | |
if not far_file then | |
print("Failed to open file: " .. far_path) | |
return | |
end | |
-- Read header | |
local magic = far_file:read(8) | |
if magic ~= "FAR!byAZ" then | |
print("Invalid FAR file.") | |
far_file:close() | |
return | |
end | |
local version = read_u32(far_file) | |
local file_table_offset = read_u32(far_file) | |
-- Read file table | |
far_file:seek("set", file_table_offset) | |
local num_files = read_u32(far_file) | |
local file_table = {} | |
for i = 1, num_files do | |
local decompressed_size = read_u32(far_file) | |
local compressed_size = read_u32(far_file) | |
local data_offset = read_u32(far_file) | |
local filename_length = read_u32(far_file) | |
local filename = read_string(far_file, filename_length) | |
table.insert(file_table, { | |
decompressed_size = decompressed_size, | |
compressed_size = compressed_size, | |
data_offset = data_offset, | |
filename = filename | |
}) | |
end | |
-- List files | |
list_files(file_table) | |
-- User options | |
io.write("Enter the number of the file to extract, or 'all' to extract all files: ") | |
local choice = io.read() | |
if choice == "all" then | |
io.write("Enter the output folder path: ") | |
local output_folder = io.read() | |
extract_all_files(far_file, file_table, output_folder) | |
else | |
local index = tonumber(choice) | |
if index and index >= 1 and index <= #file_table then | |
local entry = file_table[index] | |
io.write("Enter the output path (including filename): ") | |
local output_path = io.read() | |
extract_file(far_file, entry.data_offset, entry.compressed_size, output_path) | |
print("Extracted: " .. output_path) | |
else | |
print("Invalid selection.") | |
end | |
end | |
far_file:close() | |
end | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment