Skip to content
Permalink
Browse files

Merge pull request #1548 from TheCycoONE/iso-fs-fix

[RDY] The return of ISO support in CorsixTH
  • Loading branch information...
Alberth289346 committed Jun 12, 2019
2 parents 036e710 + 17167cc commit 54529618a235490c9ef89806719b77bc620cab0f
@@ -1249,11 +1249,12 @@ function App:checkInstallFolder()
-- Do a few more checks to make sure that commonly corrupted files are OK.
local corrupt = {}

-- Check for file corruption for local files.
-- No check is done if the game is loaded from an ISO
local function check_corrupt(path, correct_size)
local real_path = self.fs:getFilePath(path)
-- If the file exists but is smaller than usual it is probably corrupt
if real_path then
local real_size = lfs.attributes(real_path, "size")
if self.fs:fileExists(path) then
local real_size = self.fs:fileSize(path)
if real_size + 1024 < correct_size or real_size - 1024 > correct_size then
corrupt[#corrupt + 1] = path .. " (Size: " .. math.floor(real_size/1024) .. " kB / Correct: about " .. math.floor(correct_size/1024) .. " kB)"
end
@@ -1318,6 +1319,7 @@ function App:readDataFile(dir, filename)
if filename == nil then
dir, filename = "Data", dir
end

local data = assert(self.fs:readContents(dir .. pathsep .. filename))
if data:sub(1, 3) == "RNC" then
data = assert(rnc.decompress(data))
@@ -20,6 +20,7 @@ SOFTWARE. --]]

local lfs = require("lfs")
local TH = require("TH")
local iso_fs = TH.iso_fs()
local lfsext = TH.lfsExt()

--! A tree node representing a directory in the physical file-system.
@@ -70,10 +71,38 @@ function InstallDirTreeNode:createNewNode(path)
return InstallDirTreeNode(path)
end

--! Test if file name has an .iso extension
local function isIso(name)
if name == nil then
return false
end

local ext = 'iso'
return string.sub(name:lower(), -string.len(ext)) == ext
end

--! Test whether this file node is a directory or iso file.
--
--!return (bool) true if directory or iso, false otherwise
function InstallDirTreeNode:isValidFile(name)
-- Check parent criteria and that it's a directory.
if FileTreeNode.isValidFile(self, name) then
return DirTreeNode.isValidFile(self, name) or isIso(name)
end
return false
end


function InstallDirTreeNode:select()
-- Do nothing as an override. getHighlightColour solves this instead.
end

--! Check whether this node is a valid install selection.
--
-- Sets self.is_valid_directory to true if the selection is valid.
--
--!return (colour) A highlight colour if the node is a valid selection, or nil
-- otherwise.
function InstallDirTreeNode:getHighlightColour(canvas)
local highlight_colour = self.highlight_colour
if highlight_colour == nil then
@@ -97,6 +126,14 @@ function InstallDirTreeNode:getHighlightColour(canvas)
highlight_colour = canvas:mapRGB(0, 255, 0)
self.is_valid_directory = true
end
elseif isIso(self.path) then
local file = io.open(self.path, "rb")
if not file then return nil end
if iso_fs:setRoot(file) then
highlight_colour = canvas:mapRGB(0, 255, 0)
self.is_valid_directory = true
end
io.close(file)
end
self.highlight_colour = highlight_colour
end
@@ -81,11 +81,11 @@ function UINewGame:UINewGame(ui)
local avail_diff = {
{text = _S.new_game_window.medium, tooltip = _S.tooltip.new_game_window.medium, param = "full"},
}
if TheApp.fs:getFilePath("Levels", "Easy01.SAM") then
if TheApp.fs:fileExists("Levels", "Easy01.SAM") then
table.insert(avail_diff, 1, {text = _S.new_game_window.easy, tooltip = _S.tooltip.new_game_window.easy, param = "easy"})
self.difficulty = 2
end
if TheApp.fs:getFilePath("Levels", "Hard01.SAM") then
if TheApp.fs:fileExists("Levels", "Hard01.SAM") then
avail_diff[#avail_diff + 1] = {text = _S.new_game_window.hard, tooltip = _S.tooltip.new_game_window.hard, param = "hard"}
end
self.available_difficulties = avail_diff
@@ -21,21 +21,46 @@ SOFTWARE. --]]
local pathsep = package.config:sub(1, 1)
local part_pattern = "[^" .. pathsep .. "]+"

local ISO_FS = require("ISO_FS")
local ISO_FS = require("TH").iso_fs

--! Layer for abstracting away differences in file systems
--! Layer for abstracting away differences in file systems.
--
-- In the traditional case, the FileSystem is associated with a path on the
-- actual filesystem, from which all other operations are considered relative.
--
-- The FileSystem is also able to delegate operations to the ISO_FS library
-- in the engine for reading / listing files off an ISO image instead of a
-- file system.
class "FileSystem"

---@type FileSystem
local FileSystem = _G["FileSystem"]

function FileSystem:FileSystem()
-- A mapping of normalized file names in the current directory to their
-- actual path names.
self.files = nil

-- A mapping of normalized directory names in the current directory to
-- an object containing their actual physical path.
self.sub_dirs = nil

-- The actual filesystem path that is the basis/root of this FileSystem. Not
-- used when a provider is specified.
self.physical_path = nil

-- The ISO_FS provider if we are reading from an ISO instead of a
-- filesystem, otherwise nil.
self.provider = nil
end

--! Convert a file name to a canonical, case insensitive format.
-- The format is based on the limitations of the ISO filesystem.
local function normalise(str)
return str:upper():gsub("_", "-")
end

--! Populate the files and sub_dirs values for the current FileSystem path.
function FileSystem:_enumerate()
self.sub_dirs = {}
self.files = {}
@@ -49,6 +74,12 @@ function FileSystem:_enumerate()
end
end

--! Set the root physical path for this FileSystem.
-- If the path is an ISO then set the provider. If the path is a directory
-- then set the pysical_path and populate the files and sub_dirs.
--
--!param physical_path (string) a path on the filesystem to either a directory
-- or theme hospital ISO file.
function FileSystem:setRoot(physical_path)
if physical_path:match"%.[iI][sS][oO]$" or physical_path:match"%.[iI][sS][oO]9660$" then
self.provider = ISO_FS()
@@ -58,9 +89,8 @@ function FileSystem:setRoot(physical_path)
return nil, err
end
return self.provider:setRoot(file)
else
self.provider = nil
end

if physical_path:sub(-1) == pathsep then
-- Trim off the trailing separator (lfs doesn't like querying the mode of a
-- directory with a trailing slash on win32)
@@ -69,11 +99,20 @@ function FileSystem:setRoot(physical_path)
if lfs.attributes(physical_path, "mode") ~= "directory" then
return nil, "Specified path ('" .. physical_path .. "') is not a directory"
end

self.provider = nil
self.physical_path = physical_path
self:_enumerate()

return true
end

--! list the files in the given path.
--
--!param virtual_path (string) a path relative to the FileSystem root.
--!param ... (string) the virtual_path may be split into separate arguments
-- for each level in the filesystem.
--!return (object) a map of normalized names to actual names of files.
function FileSystem:listFiles(virtual_path, ...)
if ... then
virtual_path = table.concat({virtual_path, ...}, pathsep)
@@ -99,8 +138,29 @@ function FileSystem:listFiles(virtual_path, ...)
return self.files
end

--! Combine the given path segments into a single path string.
--!param virtual_path (string) a path relative to the FileSystem root.
--!param ... (string) the virtual_path may be split into separate arguments
-- for each level in the filesystem.
local function getFullPath(virtual_path, ...)
if ... then
virtual_path = table.concat({virtual_path, ...}, pathsep)
end
return virtual_path
end

--! Return the contents of the file at the given path.
--
--!param virtual_path (string) a path relative to the FileSystem root.
--!param ... (string) the virtual_path may be split into separate arguments
-- for each level in the filesystem.
function FileSystem:readContents(virtual_path, ...)
local file, err = self:getFilePath(virtual_path, ...)
virtual_path = getFullPath(virtual_path, ...)
if self.provider then
return self.provider:readContents(virtual_path)
end

local file, err = self:_getFilePath(virtual_path)
if not file then
return file, err
end
@@ -113,13 +173,55 @@ function FileSystem:readContents(virtual_path, ...)
return data
end

function FileSystem:getFilePath(virtual_path, ...)
if ... then
virtual_path = table.concat({virtual_path, ...}, pathsep)
--! Determines if the given path points to a real file.
--
--!param virtual_path (string) a path relative to the FileSystem root.
--!param ... (string) the virtual_path may be split into separate arguments
-- for each level in the filesystem.
function FileSystem:fileExists(virtual_path, ...)
virtual_path = getFullPath(virtual_path, ...)
if self.provider then
local found, err = self.provider:fileExists(virtual_path)
if found then
return true
else
return nil, err
end
end

local s, e = self:_getFilePath(virtual_path)
return (not not s), e
end

--! Get the size of the file at the given path.
--
--!param virtual_path (string) a path relative to the FileSystem root.
--!param ... (string) the virtual_path may be split into separate arguments
-- for each level in the filesystem.
--!return (numeric) Number of bytes in the given file. Nil if the file doesn't
-- exist.
function FileSystem:fileSize(virtual_path, ...)
virtual_path = getFullPath(virtual_path, ...)
if self.provider then
return self.provider:readContents(virtual_path)
elseif not self.sub_dirs then
return self.provider:fileSize(virtual_path)
end

local s, e = self:_getFilePath(virtual_path)
if not s then
return nil, e
end
return lfs.attributes(s, "size")
end

-- If the file exists and we are not using a provider then return a FileSystem
-- rooted in the directory that the directory the file is contained in.
-- Otherwise return nil and an error message.
function FileSystem:_getFilePath(virtual_path, ...)
virtual_path = getFullPath(virtual_path, ...)
if self.provider then
return nil, "This function is not supported for providers"
end
if not self.sub_dirs then
return nil, "Filesystem layer not initialised"
end
local is_file = false

0 comments on commit 5452961

Please sign in to comment.
You can’t perform that action at this time.