#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:= # Resource Tester by Blizzard # Version: 1.1 # Type: Time-and-Space saving Utility # Date: 16.8.2007 # Date v1.0b: 17.8.2007 # Date v1.01b: 17.9.2007 # Date v1.02b: 27.3.2008 # Date v1.05b: 3.4.2008 # Date v1.1: 23.3.2019 #:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:= # # This work is licensed under BSD License 2.0: # # #---------------------------------------------------------------------------- # # # # Copyright (c) Boris "Blizzard" Mikić # # All rights reserved. # # # # Redistribution and use in source and binary forms, with or without # # modification, are permitted provided that the following conditions are met: # # # # 1. Redistributions of source code must retain the above copyright notice, # # this list of conditions and the following disclaimer. # # # # 2. Redistributions in binary form must reproduce the above copyright # # notice, this list of conditions and the following disclaimer in the # # documentation and/or other materials provided with the distribution. # # # # 3. Neither the name of the copyright holder nor the names of its # # contributors may be used to endorse or promote products derived from # # this software without specific prior written permission. # # # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # # POSSIBILITY OF SUCH DAMAGE. # # # #---------------------------------------------------------------------------- # # You may use this script for both non-commercial and commercial products # without limitations as long as you fulfill the conditions presented by the # above license. The "complete" way to give credit is to include the license # somewhere in your product (e.g. in the credits screen), but a "simple" way # is also acceptable. The "simple" way to give credit is as follows: # # Resource Tester licensed under BSD License 2.0 # Copyright (c) Boris "Blizzard" Mikić # # Alternatively, if your font doesn't support diacritic characters, you may # use this variant: # # Resource Tester licensed under BSD License 2.0 # Copyright (c) Boris "Blizzard" Mikic # # In general other similar variants are allowed as long as it is clear who # the creator is (e.g. "Resource Tester created by Blizzard" is # acceptable). But if possible, prefer to use one of the two variants listed # above. # # If you fail to give credit and/or claim that this work was created by you, # this may result in legal action and/or payment of damages even though this # work is free of charge to use normally. # #:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=# # Compatibility: # # Compatible with anything, even the SDK 2.x (because it gets overriden). # Will not search for script-required resources. # # # Features: # # - finds all resources used in your game # - incredible speed: searching trough a game with over 300 maps is a matter # of just seconds # - optional feature to list all actors, skills, weapons, armors, items, # enemies, troops, states, animations, tilesets and common events that are # never being used anywhere in the game and only log all the used data # # new in 1.0b: # - fixed the typing mistake that would cause adding the skill with ID 1 # into the logged skills instead of the real skill when the script was # processing a "Force Action" event command # - rewritten conditions using classic syntax to avoid RGSS conditioning bug # - now beta # # new in 1.01b: # - fixed glitch where changing a Battle End ME would be logged as SE # - improved coding # # new in 1.02b: # - fixed bug caused by a typing mistake # # new in 1.05b: # - fixed other potential bugs # # new in 1.1: # - added new license # - added usage and crediting instructions # # # Explanation: # # This script will search through your database and your maps for resources # that are being used. It will create a list of the resources used in your # game. # # # How to use: # # Put this script over all other scripts in the editor. Set the player's # starting position and starting party. Set everything up as if you were # about to set up the game to be playable and run your game once. Several # text files will be created during that time. Now you can remove the script # from the editor and open the text files. They will include lists of # resources that your game uses, but the file names will not have extensions. # Note that resources that are "hard-coded" into your scripts, cannot be # located by this script. # # # Configuration: # # Set up following constants to suit your needs: # # DEEP_TESTING - When this value is false, the script will just iterate # through your database and maps and find out which resources # are being accessed. When this values is true, the script # will find out which data isn't used first and then create a # list of resources used by data that is being used. It will # also create a file of which data isn't used. (i.e. a skill # that no one uses in the entire game) # # # How the script works: # # - when DEEP_TESTING is false # # 1. The script will open all your maps and gather a list of all events. It # will iterate through all the events' codes and find resources that are # being accessed (i.e. playing a sound, changing windowskin, etc.). # 2. It will do the same with all common events and battle events. # 3. It will open all actor, skill, weapon, armor, item, enemy, animations, # tilesets and system data. # 4. It will iterate through all of them and find all resources ever being # accessed. # # - when DEEP_TESTING is true # # 1. Your system data will be logged and the player's starting map ID will # be accesssed. This map will be logged as "pending". # -> logs audio FILES, graphic FILES from system and all actors # 2. Pending maps are being accessed, their properties are being logged: # -> logs new tilesets, troops, event pages and audio FILES # Maps are logged as completed. # 3. Pending troops will be checked: # -> logs new enemies and battle events # Troops are logged as completed. # 4. Pending enemies will be checked: # -> logs new skills, weapons, armors items, animations and # battler FILES # Enemies are logged as completed. # 5. Pending actors will be checked: # -> logs classes, weapons, armors, character FILES and battler FILES # Actors are logged as completed. # 6. Pending classes will be checked: # -> logs new skills # Classes are logged as completed. # 7. Pending skills will be checked: # -> logs new event pages, states, animations, icon FILES and # audio FILES # Skills are logged as completed. # 8. Pending items will be checked: # -> logs new event pages, states, animations, icon FILES and # audio FILES # Items are logged as completed. # 9. Pending events are getting tested on following commands: # - call common event -> logs new event pages # - change items -> logs new items # - change weapons -> logs new weapons # - change armors -> logs new armors # - change party members -> logs new actors and classes # - change windowskin -> logs new windowskin FILES # - change battle audio -> logs new audio FILES # - transfer player -> logs new maps # - change map settings -> logs new panorama/fog/battleback FILES # - show animation -> logs new animations # - move event -> logs new character FILES # - execute transition -> logs new transition FILES # - show picture -> logs new picture FILES # - play any audio file -> logs new audio FILES # - battle processing -> logs new troops # - shop processing -> logs new weapons, armors and items # - name processing -> logs new actors and classes # - change actor state -> logs new actors, classes and states # - change actor skill -> logs new actors, classes and skills # - change actor class -> logs new actors and classes # - change actor graphic -> logs new actors, classes and character FILES # - change enemy state -> logs new stantes # - enemy transform -> logs new enemies # - show battle animation -> logs new animations # - force action -> logs new skills # Also logs new character FILES if the page is a normal event page. # Common Events are logged as completed. # 10. Returns to step 2 if there are any pending maps, troops, enemies, # actors, classes, skills, items, or event pages. # 11. Pending weapons will be checked: # -> logs new states, animations and icon FILES # 12. Pending armors will be checked: # -> logs new states and icon FILES # 13. Pending states and zero-hp states will be checked: # -> logs new states and animations # 14. Pending animations will be checked: # -> logs new animation FILES and audio FILES # 15. Pending tilesets will be checked: # -> logs new tileset FILES, autotile FILES, panorama FILES, fog FILES # and battleback FILES # 16. Saves a list of resources and saves a list of unused actors, classes, # skills, weapons, armors, items, enemies, troops, states, animations, # tilesets, common events and maps. # # # Final Notes: # # - If you use a system where you have more than one start location for the # game (i.e. a separated mode with a different storyline) and want to turn # DEEP_TESTING on during the testing, then just put in the starting map a # dummy event with teleport commands to the starting maps of the other game # modes. # # - If you are using DEEP_TESTING, be careful, since teleport commands that # use a variable to determine the new map ID can't be tested. Do as you # would with with different game modes: Create dummy events. # # - Auto-start and parallel process common events will be automatically logged # from the beginning. # # - The best moment to use this script is just before you are going to compile # and encrypt the game data. If you want to use it for resource cleaning, # turn off DEEP_TESTING. # # # If you find any bugs, please report them here: # http://forum.chaos-project.com #:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:= DEEP_TESTING = true #============================================================================== # Main #============================================================================== begin time = Time.now _actors = load_data('Data/Actors.rxdata') _classes = load_data('Data/Classes.rxdata') _skills = load_data('Data/Skills.rxdata') _items = load_data('Data/Items.rxdata') _weapons = load_data('Data/Weapons.rxdata') _armors = load_data('Data/Armors.rxdata') _enemies = load_data('Data/Enemies.rxdata') _states = load_data('Data/States.rxdata') _animations = load_data('Data/Animations.rxdata') _tilesets = load_data('Data/Tilesets.rxdata') _pages = load_data('Data/CommonEvents.rxdata') _troops = load_data('Data/Troops.rxdata') _maps = load_data('Data/MapInfos.rxdata') _system = load_data('Data/System.rxdata') bgm, bgs, se, me, animations, autotiles, battlebacks, battlers, characters, fogs, gameovers, icons, panoramas, pictures, tilesets, titles, transitions, windowskins = [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [] windowskins.push(_system.windowskin_name) titles.push(_system.title_name) gameovers.push(_system.gameover_name) transitions.push(_system.battle_transition) bgm.push(_system.title_bgm.name) me.push(_system.battle_end_me.name, _system.gameover_me.name) se.push(_system.cursor_se.name, _system.decision_se.name, _system.cancel_se.name, _system.buzzer_se.name, _system.equip_se.name, _system.shop_se.name, _system.save_se.name, _system.load_se.name, _system.battle_start_se.name, _system.escape_se.name, _system.actor_collapse_se.name, _system.enemy_collapse_se.name) if DEEP_TESTING p_maps, p_troops, p_enemies, p_actors, p_classes, p_skills, p_items, p_pages, p_weapons, p_armors, p_states, p_animations, p_tilesets, c_maps, c_troops, c_enemies, c_actors, c_classes, c_skills, c_items, c_pages = [_system.start_map_id], [], [], _system.party_members.clone, [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [] (_pages - [nil]).each {|page| if [1, 2].include?(page.trigger) p_pages.push(page) c_pages.push(page.id) end} p_maps.delete(0) raise 'Error! There is no start map defined!' if p_maps.size == 0 loop do Graphics.update p_maps.clone.each {|id| map = load_data(sprintf("Data/Map%03d.rxdata", id)) map.events.values.each {|i| p_pages += i.pages} p_tilesets.push(map.tileset_id) map.encounter_list.each {|i| p_troops.push(i) unless (c_troops | p_troops).include?(i)} bgm.push(map.bgm.name) if map.autoplay_bgm bgs.push(map.bgs.name) if map.autoplay_bgs c_maps.push(p_maps.shift)} p_troops.clone.each {|id| p_pages += _troops[id].pages _troops[id].members.each {|member| unless (c_enemies | p_enemies).include?(member.enemy_id) p_enemies.push(member.enemy_id) end} c_troops.push(p_troops.shift)} p_enemies.clone.each {|id| _enemies[id].actions.each {|i| unless (c_skills | p_skills).include?(i.skill_id) p_skills.push(i.skill_id) end} if _enemies[id].treasure_prob > 0 if _enemies[id].item_id > 0 unless (c_items | p_items).include?(_enemies[id].item_id) p_items.push(_enemies[id].item_id) end elsif _enemies[id].weapon_id > 0 unless p_weapons.include?(_enemies[id].weapon_id) p_weapons.push(_enemies[id].weapon_id) end elsif _enemies[id].armor_id > 0 unless p_armors.include?(_enemies[id].armor_id) p_armors.push(_enemies[id].armor_id) end end end [_enemies[id].animation1_id, _enemies[id].animation2_id].each {|i| p_animations.push(i) unless i == 0 || p_animations.include?(i)} battlers.push(_enemies[id].battler_name) c_enemies.push(p_enemies.shift)} p_actors.clone.each {|id| actor = _actors[id] unless _actors[id].weapon_id == 0 || p_weapons.include?(_actors[id].weapon_id) p_weapons.push(_actors[id].weapon_id) end [_actors[id].armor1_id, _actors[id].armor2_id, _actors[id].armor3_id, _actors[id].armor4_id].each {|i| p_armors.push(i) unless i == 0 || p_armors.include?(i)} unless (c_classes | p_classes).include?(_actors[id].class_id) p_classes.push(_actors[id].class_id) end characters.push(_actors[id].character_name) battlers.push(_actors[id].battler_name) c_actors.push(p_actors.shift)} p_classes.clone.each {|id| _classes[id].learnings.each {|i| unless (c_skills | p_skills).include?(i.skill_id) p_skills.push(i.skill_id) end} c_classes.push(p_classes.shift)} p_skills.clone.each {|id| unless _skills[id].common_event_id == 0 || c_pages.include?(_skills[id].common_event_id) p_pages.push(_pages[_skills[id].common_event_id]) c_pages.push(_skills[id].common_event_id) end _skills[id].plus_state_set.each {|i| p_states.push(i) unless p_states.include?(i)} [_skills[id].animation1_id, _skills[id].animation2_id].each {|i| p_animations.push(i) unless i == 0 || p_animations.include?(i)} icons.push(_skills[id].icon_name) se.push(_skills[id].menu_se.name) c_skills.push(p_skills.shift)} p_items.clone.each {|id| unless _items[id].common_event_id == 0 || c_pages.include?(_items[id].common_event_id) p_pages.push(_pages[_items[id].common_event_id]) c_pages.push(_items[id].common_event_id) end _items[id].plus_state_set.each {|i| p_states.push(i) unless p_states.include?(i)} [_items[id].animation1_id, _items[id].animation2_id].each {|i| p_animations.push(i) unless i == 0 || p_animations.include?(i)} icons.push(_items[id].icon_name) se.push(_items[id].menu_se.name) c_items.push(p_items.shift)} p_pages.clone.each {|page| characters.push(page.graphic.character_name) if page.is_a?(RPG::Event::Page) page.list.each {|command| case command.code when 117 p_pages.push(_pages[command.parameters[0]]) c_pages.push(command.parameters[0]) when 126 if command.parameters[1] == 0 && command.parameters[2] == 0 && !(c_items | p_items).include?(command.parameters[0]) p_items.push(command.parameters[0]) end when 127 if command.parameters[1] == 0 && command.parameters[2] == 0 && !p_weapons.include?(command.parameters[0]) p_weapons.push(command.parameters[0]) end when 128 if command.parameters[1] == 0 && command.parameters[2] == 0 && !p_armors.include?(command.parameters[0]) p_armors.push(command.parameters[0]) end when 129 if command.parameters[1] == 0 && !(c_actors | p_actors).include?(command.parameters[0]) p_actors.push(command.parameters[0]) end when 131 then windowskins.push(command.parameters[0]) when 132 then bgm.push(command.parameters[0].name) when 133 then me.push(command.parameters[0].name) when 201 if command.parameters[0] == 0 && !(c_maps | p_maps).include?(command.parameters[1]) p_maps.push(command.parameters[1]) end when 204 case command.parameters[0] when 0 then panoramas.push(command.parameters[1]) when 1 then fogs.push(command.parameters[1]) when 2 then battlebacks.push(command.parameters[1]) end when 207 unless p_animations.include?(command.parameters[1]) p_animations.push(command.parameters[1]) end when 209 command.parameters[1].list.each {|move| characters.push(move.parameters[0]) if move.code == 41} when 222 then transitions.push(command.parameters[0]) when 231 then pictures.push(command.parameters[1]) when 241 then bgm.push(command.parameters[0].name) when 245 then bgs.push(command.parameters[0].name) when 249 then me.push(command.parameters[0].name) when 250 then se.push(command.parameters[0].name) when 301 unless c_troops.include?(command.parameters[0]) p_troops.push(command.parameters[0]) end when 302 goods = [command.parameters] index = page.list.index(command) loop do index += 1 if page.list[index].code == 605 goods.push(page.list[index].parameters) else break end end goods.each {|good| case good[0] when 0 unless (c_items | p_items).include?(good[1]) p_items.push(good[1]) end when 1 p_weapons.push(good[1]) unless p_weapons.include?(good[1]) when 2 p_armors.push(good[1]) unless p_armors.include?(good[1]) end} when 303 unless (c_actors | p_actors).include?(command.parameters[0]) p_actors.push(command.parameters[0]) end when 313 if command.parameters[1] == 0 && !p_states.include?(command.parameters[2]) p_states.push(command.parameters[2]) end when 318 unless (c_actors | p_actors).include?(command.parameters[0]) p_actors.push(command.parameters[0]) end if command.parameters[1] == 0 && !(c_skills | p_skills).include?(command.parameters[2]) p_skills.push(command.parameters[2]) end when 321 unless (c_actors | p_actors).include?(command.parameters[0]) p_actors.push(command.parameters[0]) end unless (c_classes | p_classes).include?(command.parameters[1]) p_classes.push(command.parameters[1]) end when 322 unless (c_actors | p_actors).include?(command.parameters[0]) p_actors.push(command.parameters[0]) end characters.push(command.parameters[1]) battlers.push(command.parameters[3]) when 333 if command.parameters[1] == 0 && !p_states.include?(command.parameters[2]) p_states.push(command.parameters[2]) end when 336 unless (c_enemies | p_enemies).include?(command.parameters[1]) p_enemies.push(command.parameters[1]) end when 337 unless p_animations.include?(command.parameters[2]) p_animations.push(command.parameters[2]) end when 339 if command.parameters[2] != 0 && !(c_skills | p_skills).include?(command.parameters[3]) p_skills.push(command.parameters[3]) end end} p_pages.shift} all = [p_maps, p_troops, p_enemies, p_actors, p_classes, p_skills, p_items, p_pages] break if all.all? {|ary| ary.size == 0} end p_weapons.each {|id| _weapons[id].plus_state_set.each {|i| p_states.push(i) unless p_states.include?(i)} [_weapons[id].animation1_id, _weapons[id].animation2_id].each {|i| p_animations.push(i) unless i == 0 || p_animations.include?(i)} icons.push(_weapons[id].icon_name)} p_armors.each {|id| unless _armors[id].auto_state_id == 0 || p_states.include?(_armors[id].auto_state_id) p_states.push(_armors[id].auto_state_id) end icons.push(_armors[id].icon_name)} (_states - [nil]).each {|s| p_states.push(s.id) if s.zero_hp && !p_states.include?(s.id)} size = 0 while size != p_states.size size = p_states.size p_states.clone.each {|id| _states[id].plus_state_set.each {|i| p_states.push(i) unless p_states.include?(i)}} end p_states.each {|id| unless _states[id].animation_id == 0 || p_animations.include?(_states[id].animation_id) p_animations.push(_states[id].animation_id) end} p_animations.each {|id| animations.push(_animations[id].animation_name) _animations[id].timings.each {|timing| se.push(timing.se.name)}} p_tilesets.each {|id| tilesets.push(_tilesets[id].tileset_name) fogs.push(_tilesets[id].fog_name) panoramas.push(_tilesets[id].panorama_name) battlebacks.push(_tilesets[id].battleback_name) (0..6).each {|i| autotiles.push(_tilesets[id].autotile_names[i])}} all = [[_maps, _maps.keys - c_maps], [_troops, (1..._troops.size).to_a - c_troops], [_enemies, (1..._enemies.size).to_a - c_enemies], [_actors, (1..._actors.size).to_a - c_actors], [_classes, (1..._classes.size).to_a - c_classes], [_skills, (1..._skills.size).to_a - c_skills], [_items, (1..._items.size).to_a - c_items], [_pages, (1..._pages.size).to_a - c_pages], [_weapons, (1..._weapons.size).to_a - p_weapons], [_armors, (1..._armors.size).to_a - p_armors], [_states, (1..._states.size).to_a - p_states], [_animations, (1..._animations.size).to_a - p_animations], [_tilesets, (1..._tilesets.size).to_a - p_tilesets]] datas = ['Maps', 'Troops', 'Enemies', 'Actors', 'Classes', 'Skills', 'Items', 'Common Events', 'Weapons', 'Armors', 'States', 'Animations', 'Tilesets'] file = File.open('Unused.txt', 'wb') datas.each_index {|i| file.write("\r\n --::>> Unused #{datas[i]} <<::--\r\n\r\n") (all[i][1] | all[i][1]).each {|one| file.write("#{sprintf('%03d', one)} : #{all[i][0][one].name}\r\n")} file.write("\r\n")} else (_actors - [nil]).each {|actor| characters.push(actor.character_name) battlers.push(actor.battler_name)} (_skills + _items - [nil]).each {|object| icons.push(object.icon_name) se.push(object.menu_se.name)} (_weapons + _armors - [nil]).each {|object| icons.push(object.icon_name)} (_enemies - [nil]).each {|enemy| battlers.push(enemy.battler_name)} (_animations - [nil]).each {|animation| animations.push(animation.animation_name) animation.timings.each {|timing| se.push(timing.se.name)}} (_tilesets - [nil]).each {|tileset| tilesets.push(tileset.tileset_name) fogs.push(tileset.fog_name) panoramas.push(tileset.panorama_name) battlebacks.push(tileset.battleback_name) (0..6).each {|i| autotiles.push(tileset.autotile_names[i])}} (_troops - [nil]).each {|troop| _pages += troop.pages} _keys = _maps.keys _keys.each_index {|i| Graphics.update if i % 20 == 0 events = load_data(sprintf("Data/Map%03d.rxdata", _keys[i])).events events.values.each {|event| _pages += event.pages}} _pages -= [nil] _pages.each_index {|i| Graphics.update if i % 100 == 0 if _pages[i].is_a?(RPG::Event::Page) characters.push(_pages[i].graphic.character_name) end _pages[i].list.each {|command| case command.code when 131 then windowskins.push(command.parameters[0]) when 132 then bgm.push(command.parameters[0].name) when 133 then se.push(command.parameters[0].name) when 204 case command.parameters[0] when 0 then panoramas.push(command.parameters[1]) when 1 then fogs.push(command.parameters[1]) when 2 then battlebacks.push(command.parameters[1]) end when 209 command.parameters[1].list.each {|move| characters.push(move.parameters[0]) if move.code == 41} when 222 then transitions.push(command.parameters[0]) when 231 then pictures.push(command.parameters[1]) when 241 then bgm.push(command.parameters[0].name) when 245 then bgs.push(command.parameters[0].name) when 249 then me.push(command.parameters[0].name) when 250 then se.push(command.parameters[0].name) when 322 characters.push(command.parameters[1]) battlers.push(command.parameters[3]) end}} end folders = ['Audio/BGM', 'Audio/BGS', 'Audio/ME', 'Audio/SE', 'Animations', 'Autotiles', 'Battlebacks', 'Battlers', 'Characters', 'Fogs', 'Gameovers', 'Icons', 'Panoramas', 'Pictures', 'Tilesets', 'Titles', 'Transitions', 'Windowskins'] all = [bgm, bgs, me, se, animations, autotiles, battlebacks, battlers, characters, fogs, gameovers, icons, panoramas, pictures, tilesets, titles, transitions, windowskins] all.each {|ary| ary.delete(''); ary.sort!} file = File.open('Tested Resources.txt', 'wb') folders.each_index {|i| file.write("\r\n ---::{ #{folders[i]} }::---\r\n\r\n") (all[i] | all[i]).each {|one| file.write("#{folders[i]}/#{one}\r\n")} file.write("\r\n")} file.close p "Resource Test successful! Time needed: #{Time.now-time} seconds" rescue Errno::ENOENT filename = $!.message.sub('No such file or directory - ', '') print("File -- #{filename} -- is missing.") end exit