Files
infinitefusion-e18/Data/Scripts/001_Technical/002_Files/001_FileTests.rb

485 lines
13 KiB
Ruby

#===============================================================================
# Reads files of certain format from a directory
#===============================================================================
class Dir
# Reads all files in a directory
def self.get(dir, filters = "*", full = true)
files = []
filters = [filters] if !filters.is_a?(Array)
self.chdir(dir) do
filters.each do |filter|
self.glob(filter) { |f| files.push(full ? (dir + "/" + f) : f) }
end
end
return files.sort
end
# Generates entire file/folder tree from a certain directory
def self.all(dir, filters = "*", full = true)
# sets variables for starting
files = []
subfolders = []
self.get(dir, filters, full).each do |file|
# engages in recursion to read the entire file tree
if self.safe?(file) # Is a directory
subfolders += self.all(file, filters, full)
else # Is a file
files += [file]
end
end
# returns all found files
return files + subfolders
end
# Checks for existing directory
def self.safe?(dir)
return FileTest.directory?(dir)
end
# Creates all the required directories for filename path
def self.create(path)
path.gsub!("\\", "/") # Windows compatibility
# get path tree
dirs = path.split("/")
full = ""
dirs.each do |dir|
full += dir + "/"
# creates directories
self.mkdir(full) if !self.safe?(full)
end
end
# Generates entire folder tree from a certain directory
def self.all_dirs(dir)
# sets variables for starting
dirs = []
self.get(dir, "*", true).each do |file|
# engages in recursion to read the entire folder tree
dirs += self.all_dirs(file) if self.safe?(file)
end
# returns all found directories
return dirs.length > 0 ? (dirs + [dir]) : [dir]
end
# Deletes all the files in a directory and all the sub directories (allows for non-empty dirs)
def self.delete_all(dir)
# delete all files in dir
self.all(dir).each { |f| File.delete(f) }
# delete all dirs in dir
self.all_dirs(dir).each { |f| Dir.delete(f) }
end
end
#===============================================================================
# Checking for files and directories
#===============================================================================
# Works around a problem with FileTest.directory if directory contains accent marks
# @deprecated This method is slated to be removed in v22.
def safeIsDirectory?(f)
Deprecation.warn_method("safeIsDirectory?(f)", "v22", "FileTest.directory?(f)")
return FileTest.directory?(f)
end
# @deprecated This method is slated to be removed in v22.
def safeExists?(f)
Deprecation.warn_method("safeExists?(f)", "v22", "FileTest.exist?(f)")
return FileTest.exist?(f)
end
# Similar to "Dir.glob", but designed to work around a problem with accessing
# files if a path contains accent marks.
# "dir" is the directory path, "wildcard" is the filename pattern to match.
def safeGlob(dir, wildcard)
ret = []
afterChdir = false
begin
Dir.chdir(dir) do
afterChdir = true
Dir.glob(wildcard) { |f| ret.push(dir + "/" + f) }
end
rescue Errno::ENOENT
raise if afterChdir
end
if block_given?
ret.each { |f| yield(f) }
end
return (block_given?) ? nil : ret
end
def pbResolveAudioSE(file)
return nil if !file
if RTP.exists?("Audio/SE/" + file, ["", ".wav", ".ogg", ".mp3", ".wma"])
return RTP.getPath("Audio/SE/" + file, ["", ".wav", ".ogg", ".mp3", ".wma"])
end
return nil
end
# Finds the real path for an image file. This includes paths in encrypted
# archives. Returns nil if the path can't be found.
def pbResolveBitmap(x)
return nil if !x
noext = x.gsub(/\.(bmp|png|gif|jpg|jpeg)$/, "")
filename = nil
# RTP.eachPathFor(x) { |path|
# filename = pbTryString(path) if !filename
# filename = pbTryString(path + ".gif") if !filename
# }
RTP.eachPathFor(noext) do |path|
filename = pbTryString(path + ".png") if !filename
filename = pbTryString(path + ".gif") if !filename
# filename = pbTryString(path + ".jpg") if !filename
# filename = pbTryString(path + ".jpeg") if !filename
# filename = pbTryString(path + ".bmp") if !filename
end
return filename
end
# Finds the real path for an image file. This includes paths in encrypted
# archives. Returns _x_ if the path can't be found.
def pbBitmapName(x)
ret = pbResolveBitmap(x)
return (ret) ? ret : x
end
def strsplit(str, re)
ret = []
tstr = str
while re =~ tstr
ret[ret.length] = $~.pre_match
tstr = $~.post_match
end
ret[ret.length] = tstr if ret.length
return ret
end
def canonicalize(c)
csplit = strsplit(c, /[\/\\]/)
pos = -1
ret = []
retstr = ""
csplit.each do |x|
if x == ".."
if pos >= 0
ret.delete_at(pos)
pos -= 1
end
elsif x != "."
ret.push(x)
pos += 1
end
end
ret.length.times do |i|
retstr += "/" if i > 0
retstr += ret[i]
end
return retstr
end
#===============================================================================
#
#===============================================================================
module RTP
@rtpPaths = nil
def self.exists?(filename, extensions = [])
return false if nil_or_empty?(filename)
eachPathFor(filename) do |path|
return true if FileTest.exist?(path)
extensions.each do |ext|
return true if FileTest.exist?(path + ext)
end
end
return false
end
def self.getImagePath(filename)
return self.getPath(filename, ["", ".png", ".gif"]) # ".jpg", ".jpeg", ".bmp"
end
def self.getAudioPath(filename)
return self.getPath(filename, ["", ".wav", ".ogg", ".mp3", ".midi", ".mid", ".wma"])
end
def self.getPath(filename, extensions = [])
return filename if nil_or_empty?(filename)
eachPathFor(filename) do |path|
return path if FileTest.exist?(path)
extensions.each do |ext|
file = path + ext
return file if FileTest.exist?(file)
end
end
return filename
end
# Gets the absolute RGSS paths for the given file name
def self.eachPathFor(filename)
return if !filename
if filename[/^[A-Za-z]\:[\/\\]/] || filename[/^[\/\\]/]
# filename is already absolute
yield filename
else
# relative path
RTP.eachPath do |path|
if path == "./"
yield filename
else
yield path + filename
end
end
end
end
# Gets all RGSS search paths.
# This function basically does nothing now, because
# the passage of time and introduction of MKXP make
# it useless, but leaving it for compatibility
# reasons
def self.eachPath
# XXX: Use "." instead of Dir.pwd because of problems retrieving files if
# the current directory contains an accent mark
yield ".".gsub(/[\/\\]/, "/").gsub(/[\/\\]$/, "") + "/"
end
def self.getSaveFileName(fileName)
File.join(getSaveFolder, fileName)
end
def self.getSaveFolder
# MKXP makes sure that this folder has been created
# once it starts. The location differs depending on
# the operating system:
# Windows: %APPDATA%
# Linux: $HOME/.local/share
# macOS (unsandboxed): $HOME/Library/Application Support
System.data_directory
end
end
#===============================================================================
#
#===============================================================================
module FileTest
IMAGE_EXTENSIONS = [".png", ".gif"] # ".jpg", ".jpeg", ".bmp",
AUDIO_EXTENSIONS = [".wav", ".ogg", ".mp3", ".midi", ".mid", ".wma"]
def self.audio_exist?(filename)
return RTP.exists?(filename, AUDIO_EXTENSIONS)
end
def self.image_exist?(filename)
return RTP.exists?(filename, IMAGE_EXTENSIONS)
end
end
#===============================================================================
#
#===============================================================================
# Used to determine whether a data file exists (rather than a graphics or
# audio file). Doesn't check RTP, but does check encrypted archives.
# NOTE: pbGetFileChar checks anything added in MKXP's RTP setting, and matching
# mount points added through System.mount.
def pbRgssExists?(filename)
return !pbGetFileChar(filename).nil? if FileTest.exist?("./Game.rgssad")
filename = canonicalize(filename)
return FileTest.exist?(filename)
end
# Opens an IO, even if the file is in an encrypted archive.
# Doesn't check RTP for the file.
# NOTE: load_data checks anything added in MKXP's RTP setting, and matching
# mount points added through System.mount.
def pbRgssOpen(file, mode = nil)
# File.open("debug.txt", "ab") { |fw| fw.write([file, mode, Time.now.to_f].inspect + "\r\n") }
if !FileTest.exist?("./Game.rgssad")
if block_given?
File.open(file, mode) { |f| yield f }
return nil
else
return File.open(file, mode)
end
end
file = canonicalize(file)
Marshal.neverload = true
str = load_data(file, true)
if block_given?
StringInput.open(str) { |f| yield f }
return nil
else
return StringInput.open(str)
end
end
# Gets at least the first byte of a file. Doesn't check RTP, but does check
# encrypted archives.
def pbGetFileChar(file)
canon_file = canonicalize(file)
if !FileTest.exist?("./Game.rgssad")
return nil if !FileTest.exist?(canon_file)
return nil if file.last == "/" # Is a directory
begin
File.open(canon_file, "rb") { |f| return f.read(1) } # read one byte
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, Errno::EISDIR
return nil
end
end
str = nil
begin
str = load_data(canon_file, true)
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, Errno::EISDIR, RGSSError, MKXPError
str = nil
end
return str
end
def pbTryString(x)
ret = pbGetFileChar(x)
return nil_or_empty?(ret) ? nil : x
end
# Gets the contents of a file. Doesn't check RTP, but does check
# encrypted archives.
# NOTE: load_data will check anything added in MKXP's RTP setting, and matching
# mount points added through System.mount.
def pbGetFileString(file)
file = canonicalize(file)
if !FileTest.exist?("./Game.rgssad")
return nil if !FileTest.exist?(file)
begin
File.open(file, "rb") { |f| return f.read } # read all data
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
return nil
end
end
str = nil
begin
str = load_data(file, true)
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, RGSSError, MKXPError
str = nil
end
return str
end
#===============================================================================
#
#===============================================================================
class StringInput
include Enumerable
attr_reader :lineno, :string
class << self
def new(str)
if block_given?
begin
f = super
yield f
ensure
f&.close
end
else
super
end
end
alias open new
end
def initialize(str)
@string = str
@pos = 0
@closed = false
@lineno = 0
end
def inspect
return "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@string[0, 30].inspect}>"
end
def close
raise IOError, "closed stream" if @closed
@pos = nil
@closed = true
end
def closed?; @closed; end
def pos
raise IOError, "closed stream" if @closed
[@pos, @string.size].min
end
alias tell pos
def rewind; seek(0); end
def pos=(value); seek(value); end
def seek(offset, whence = IO::SEEK_SET)
raise IOError, "closed stream" if @closed
case whence
when IO::SEEK_SET then @pos = offset
when IO::SEEK_CUR then @pos += offset
when IO::SEEK_END then @pos = @string.size - offset
else
raise ArgumentError, "unknown seek flag: #{whence}"
end
@pos = 0 if @pos < 0
@pos = [@pos, @string.size + 1].min
offset
end
def eof?
raise IOError, "closed stream" if @closed
@pos > @string.size
end
def each(&block)
raise IOError, "closed stream" if @closed
begin
@string.each(&block)
ensure
@pos = 0
end
end
def gets
raise IOError, "closed stream" if @closed
idx = @string.index("\n", @pos)
if idx
idx += 1 # "\n".size
line = @string[@pos...idx]
@pos = idx
@pos += 1 if @pos == @string.size
else
line = @string[@pos..-1]
@pos = @string.size + 1
end
@lineno += 1
line
end
def getc
raise IOError, "closed stream" if @closed
ch = @string[@pos]
@pos += 1
@pos += 1 if @pos == @string.size
ch
end
def read(len = nil)
raise IOError, "closed stream" if @closed
if !len
return nil if eof?
rest = @string[@pos...@string.size]
@pos = @string.size + 1
return rest
end
str = @string[@pos, len]
@pos += len
@pos += 1 if @pos == @string.size
str
end
alias read_all read
alias sysread read
end