# TODO some of these should probably be moved to level directory class (if it will exist) # (If that happens, correct compatibility docs in level.rb) class LevelFile # TODO: is :dir a string or a class? if it will be a class, it has to implement to_s, # which will return its "path". attr_accessor :dir, :nr, :type, :packed, :basename, :filename def initialize(dir = nil, nr = nil) @dir = dir @nr = nr @type = LEVEL_FILE_TYPE_UNKNOWN @packed = FALSE @basename = nil @filename = nil if nr determineFilename determineFiletype end end def basename=(val) @basename = val @filename = getLevelFilenameFromBasename(@dir,@basename) if @dir and @basename end # # Converts level basename into filename (prefixing it with dir). # # TODO private def LevelFile.getLevelFilenameFromBasename(dir, basename) return nil if !dir # TODO is this needed? (and if yes, is it needed in other functions too?) dir.to_s + "/" + basename end # # This tries to detect if the file 'basename' in directory 'dir' is a packed # level, and if it is, it returns its type. If it doesn't detect anything, it # returns LEVEL_FILE_TYPE_UNKNOWN. # # TODO maybe private def LevelFile.getFileTypeFromBasename(dir, basename) # ---------- try to determine file type from filename ---------- # check for typical filename of a Supaplex level package file return LEVEL_FILE_TYPE_SP if basename.length == 10 && basename[0,8].downcase == "levels.d" # ---------- try to determine file type from filesize ---------- filename = dir.to_s + "/" + basename begin return LEVEL_FILE_TYPE_SP if File.size(filename) == 170496 rescue Exception # nothing end LEVEL_FILE_TYPE_UNKNOWN end # TODO this (amongst other things) should probably belong to class of dir # # This searches for packed level files in 'dir'. # getFileTypeFromBasename() is used to determine if a file is a packed level. # def LevelFile.getPackedLevelBasename(dir, type) begin entries = Dir.entries(dir.to_s) rescue Exception error(ERR_WARN, "cannot read level directory '#{dir.to_s}'") return UNDEFINED_FILENAME end entries.each do |basename| entry_type = getFileTypeFromBasename(dir, entry_basename) if entry_type != LEVEL_FILE_TYPE_UNKNOWN # found valid level package return basename if type == LEVEL_FILE_TYPE_UNKNOWN || type == entry_type end end UNDEFINED_FILE end # # This returns a default filename of a native R'n'D level (placed in 'dir'). # For example, basename for level nr=3 is '003.level'. # # TODO this should maybe belong into class of dir # TODO maybe private def LevelFile.getDefaultLevelFilename(dir, nr) basename = (nr < 0 ? "template" : sprintf("%03d", nr)) + ".#{LEVELFILE_EXTENSION}" getLevelFilenameFromBasename(dir, basename) end # TODO these maybe should be private def formatSingleLevelFilename(type, format, *args) @type = type @packed = false self.basename = sprintf(format, *args) self.basename = @basename.upcase if !File.exists?(@filename) File.exists?(@filename) end def setPackedLevelFilename(type) @type = type @packed = true self.basename = getPackedLevelBasename(type) File.exists?(@filename) end # # This function is used in LevelFileInfo#determineFilename() to map level- # -type codes from levelinfo.conf to LEVEL_FILE_TYPE_* constants. # # TODO private def LevelFile.getFiletypeFromID(filetype_id) filetypeIdList = { "RND" => LEVEL_FILE_TYPE_RND, "BD" => LEVEL_FILE_TYPE_BD, "EM" => LEVEL_FILE_TYPE_EM, "SP" => LEVEL_FILE_TYPE_SP, "DX" => LEVEL_FILE_TYPE_DX, "SB" => LEVEL_FILE_TYPE_SB, "DC" => LEVEL_FILE_TYPE_DC, } filetypeIdList.default = LEVEL_FILE_TYPE_UNKNOWN filetypeIdList[filetype_id.upcase] end def determineFilename # special case: level number is negative => check for level template file if @nr < 0 formatSingleLevelFilename(LEVEL_FILE_TYPE_RND, "template.%s", LEVELFILE_EXTENSION) return # no fallback if template file not existing end # TODO get more info about leveldir_current (should @dir be used?) # special case: check for file name/pattern specified in "levelinfo.conf" if leveldir_current.level_filename filetype = getFiletypeFromID(leveldir_current.level_filetype) # TODO: try if it's possible to use level_filename to crash R'n'D (or to create buffer overflow), it appears to not be checked return if formatSingleLevelFilename(filetype, leveldir_current.level_filename, @nr) end # check for native Rocks'n'Diamonds level file return if formatSingleLevelFilename(LEVEL_FILE_TYPE_RND, "%03d.%s", @nr, LEVELFILE_EXTENSION) # check for Emerald Mine level file (V1) return if formatSingleLevelFilename(LEVEL_FILE_TYPE_EM, "a%c%c", 'a' + (@nr / 10) % 26, '0' + @nr % 10) # check for Emerald Mine level file (V2 to V5) return if formatSingleLevelFilename(LEVEL_FILE_TYPE_EM, "%d", @nr) # check for Emerald Mine level file (V6 / single mode) return if formatSingleLevelFilename(LEVEL_FILE_TYPE_EM, "%02ds", @nr) # check for Emerald Mine level file (V6 / teamwork mode) return if formatSingleLevelFilename(LEVEL_FILE_TYPE_EM, "%02dt", @nr) # check for various packed level file formats return if setPackedLevelFilename(LEVEL_FILE_TYPE_UNKNOWN) # no known level file found -- use default values (and fail later) formatSingleLevelFilename(LEVEL_FILE_TYPE_RND, "%03d.%s", @nr, LEVELFILE_EXTENSION) end def determineFiletype() @type = getFileTypeFromBasename(@basename) if @type == LEVEL_FILE_TYPE_UNKNOWN end end