RE: What's up, RMers? - DerVVulfman - 08-06-2024
Keeping sharp, I decided... why not make a reversal of my Missing Resources script. Instead of checking your project to see if any files are missing... check the resources to see if it is even being USED!
Code: #==============================================================================
# ** Resource Sifter
#------------------------------------------------------------------------------
# by DerVVulfman
# version 0.1 (beta - incomplete early stage)
# 08-06-2024 (MM-DD-YYYY)
# RGSS / RPGMaker XP
#------------------------------------------------------------------------------
# Place below Scene_Debug and above Main, and it should search your game's
# project folder for "Audio" not used in your game. That is to say, it will
# find if you have any extra/unnecessary audio files.
#
# Output of resources unused will appear in your game's root folder.
#
# This is a beta as I am continuing development.
#==============================================================================
module Resource_Sifter
#--------------------------------------------------------------------------
# * Invariables
#--------------------------------------------------------------------------
GFX = ['@system_l.gameover_name', '@system_l.title_name',
'@system_l.battle_transition', '@system_l.windowskin_name',
'@system_l.title_bgm.name', '@system_l.battle_bgm.name',
'@system_l.battle_end_me.name', '@system_l.gameover_me.name']
SFX = ['@system_l.cursor_se.name', '@system_l.decision_se.name',
'@system_l.cancel_se.name', '@system_l.buzzer_se.name',
'@system_l.equip_se.name', '@system_l.shop_se.name',
'@system_l.save_se.name', '@system_l.load_se.name',
'@system_l.battle_start_se.name', '@system_l.escape_se.name',
'@system_l.actor_collapse_se.name', '@system_l.enemy_collapse_se.name']
#--------------------------------------------------------------------------
# * Main Processing
#--------------------------------------------------------------------------
def self.main
#
setup # Setup data
update # Update/Process tests
output # Output result(s)
#
end
#--------------------------------------------------------------------------
# * Setup
#--------------------------------------------------------------------------
def self.setup
#
# Load database data
@system_l = load_data("Data/System.rxdata")
@common_l = load_data("Data/CommonEvents.rxdata")
@map_l = load_data("Data/MapInfos.rxdata")
#
@root_bgm = []
@root_bgs = []
@root_me = []
@root_se = []
#
setup_files(@root_bgm, "Audio/BGM/*") # Setup test for BGM files
setup_files(@root_bgs, "Audio/BGS/*") # Setup test for BGS files
setup_files(@root_me, "Audio/ME/*") # Setup test for ME files
setup_files(@root_se, "Audio/SE/*") # Setup test for SE files
#
end
#--------------------------------------------------------------------------
# * Update and perform searches
#--------------------------------------------------------------------------
def self.update
#
search_system # Search system database
search_common # Search common events
search_map # Search map content
#
end
#--------------------------------------------------------------------------
# * Output Content
#--------------------------------------------------------------------------
def self.output
#
output_to_file(@root_bgm, "BGMs.txt") # Print BGM test results
output_to_file(@root_bgs, "BGSs.txt") # Print BGS test results
output_to_file(@root_me, "MEs.txt") # Print ME test results
output_to_file(@root_se, "SEs.txt") # Print SE test results
#
end
#--------------------------------------------------------------------------
# * Setup (when acquiring files from a game resource folder)
# pathway_list : pathway specific resource array
# path_test : the folder path for a resource
#--------------------------------------------------------------------------
def self.setup_files(pathway_list, path_test)
#
path_len = (path_test.length) -1 # Get pathway length (minus *)
#
for name in Dir[path_test] # Cycle through root folder
next if File.stat(name).directory? # If NOT a subfolder
name_len = name.length # Get filename's length
name_len -= path_len # Shorten length by path length
name_len -= 4 # Shorten length by ext length
name = name.slice(path_len, name_len) # Trim the audio name
pathway_list.push(name) # Push into the root path list
end
#
end
#--------------------------------------------------------------------------
# * Perform search of the System Database
#--------------------------------------------------------------------------
def self.search_system
#
# Testing BGM and ME
test_bgm(eval(GFX[4])) # Test Title Screen BGM
test_bgm(eval(GFX[5])) # Test Battle BGM
test_me(eval(GFX[6])) # Test Battle End ME
test_me(eval(GFX[7])) # Test Gameover ME
#
for item in SFX # Cycle through all System Sfx
test_se(eval(item)) # Test each Sound Effect
end
#
end
#--------------------------------------------------------------------------
# * Perform search of the Common Events Database
#--------------------------------------------------------------------------
def self.search_common
#
for event in @common_l # Cycle through all common events
next if event.nil? # Skip invalid event
list = event.list # Get list of commands
search_event_item(list) #`Search every item in the event
end
#
end
#--------------------------------------------------------------------------
# * Perform search through every project map
#--------------------------------------------------------------------------
def self.search_map
#
for map in @map_l # Cycle through all maps
#
id = map[0] # Get the map ID
text = "Data/Map%03d.rxdata" # Define the Map file structure
filename = sprintf(text, id) # Get the formatted map filename
map_data = load_data(filename) # Get the map data
#
search_map_params(map_data) # Search through map parameters
search_map_events(map_data) # Search through map events
#
end
#
end
#--------------------------------------------------------------------------
# * Map Database Search (when getting map param data)
# map_data : data acquired from a single map
#--------------------------------------------------------------------------
def self.search_map_params(map_data)
#
test_bgm(map_data.bgm.name) # Test the map's auto-run BGM
test_bgs(map_data.bgs.name) # Test the map's auto-run BGS
#
end
#--------------------------------------------------------------------------
# * Map Database Search (when getting map event data)
# map_data : data acquired from a single map
#--------------------------------------------------------------------------
def self.search_map_events(map_data)
#
events = {} # Create the Event hash array
for i in map_data.events.keys # Sort through map events
events[i] = map_data.events[i] # Fill testable Event hash array
end
#
for event in events # Sort through the Event array
for page in event[1].pages.reverse # Sort through Event Pages
list = page.list # Get the list of commands
search_event_item(list) # Execute resource item search
end
end
#
end
#--------------------------------------------------------------------------
# * Map Database Search (when sifting through each event command)
#--------------------------------------------------------------------------
def self.search_event_item(list)
#
for list_item in list # Cycle through all items in list
item_0 = list_item.parameters[0] # Get the item
case list_item.code # Branch by item's event code
#
when 132 ; test_bgm(item_0.name) # Change Battle BGM
when 133 ; test_me(item_0.name) # Change Battle End ME
when 209 ; self.search_mr(list_item) # Set Move Route
when 241 ; test_bgm(item_0.name) # Play BGM
when 245 ; test_bgs(item_0.name) # Play BGS
when 249 ; test_me(item_0.name) # Play ME
when 250 ; test_se(item_0.name) # Play SE
#
end
end
end
#--------------------------------------------------------------------------
# * Map Database Search (when sifting through the move route command)
#--------------------------------------------------------------------------
def self.search_mr(list_item)
#
move_l = list_item.parameters[1].list # Get the move route command list
for move_item in move_l # Cycle through
item_0 = move_item.parameters[0] # Get the item
case move_item.code # Branch by item's event code
when 44 ; test_se(item_0.name) # Play SE
end
end
#
end
#--------------------------------------------------------------------------
# * Test BGM Audio resource and remove from list
# filename : name of the resource to test
#--------------------------------------------------------------------------
def self.test_bgm(filename)
#
return if filename.nil? # Exit if no file
return if filename. == '' # Exit if empty filename
test_resource(@root_bgm, filename) # Perform resource test
#
end
#--------------------------------------------------------------------------
# * Test BGS Audio resource and remove from list
# filename : name of the resource to test
#--------------------------------------------------------------------------
def self.test_bgs(filename)
#
return if filename.nil? # Exit if no file
return if filename. == '' # Exit if empty filename
test_resource(@root_bgs, filename) # Perform resource test
#
end
#--------------------------------------------------------------------------
# * Test ME Audio resource and remove from list
# filename : name of the resource to test
#--------------------------------------------------------------------------
def self.test_me(filename)
#
return if filename.nil? # Exit if no file
return if filename. == '' # Exit if empty filename
test_resource(@root_me, filename) # Perform resource test
#
end
#--------------------------------------------------------------------------
# * Test SE Audio resource and remove from list
# filename : name of the resource to test
#--------------------------------------------------------------------------
def self.test_se(filename)
#
return if filename.nil? # Exit if no file
return if filename. == '' # Exit if empty filename
test_resource(@root_se, filename) # Perform resource test
#
end
#--------------------------------------------------------------------------
# * Test resource and remove from list
# pathway_list : pathway specific resource array
# filename : name of the resource to test
#--------------------------------------------------------------------------
def self.test_resource(pathway_list, filename)
#
unless pathway_list.include?(filename) # If file is not found
return pathway_list # Exit method with pathway list
end
#
jeff = pathway_list.index(filename) # Get index position
pathway_list.delete_at(jeff) # Delete at index position
return pathway_list # Exit with updated pathway list
#
end
#--------------------------------------------------------------------------
# * Output Content to a file
# pathway_list : pathway specific resource array
# filename : name of the output file
#--------------------------------------------------------------------------
def self.output_to_file(pathway_list, filename="Unnecessary")
#
return if pathway_list.nil? # Exit if no list
return if pathway_list == [] # Exit if empty list
#
f = File.new(filename, "w+") # Create and open the file
for resource_name in pathway_list # Cycle through all resources
f.puts resource_name # Print resource name to file
end
f.close # Close the file
#
end
end
Resource_Sifter.main
This is an extreme BETA edition, only focusing on the Audio folder. So indeed, I do plan to continue this... which is why it is not in the Scripts Database. its not done!
RE: What's up, RMers? - DerVVulfman - 08-08-2024
(08-05-2024, 07:49 PM)DerVVulfman Wrote: Indeed, this right here can let me open up any script from the database and read it as text IN the game. And I have included a method that 'compresses' any code into the necessary form which can be properly saved.
Yet another followup, and again... with the script database as days before. I now present THIS:
Code: module Script_list
#--------------------------------------------------------------------------
# * Main Processing
#--------------------------------------------------------------------------
def self.main
#
# Define hashes for scripts
@scripts_name = {} # Holds script names
@scripts_list = {} # Holds scripts as an array
#
# Access the script database
script_setup
#
end
#--------------------------------------------------------------------------
# * Search project script database
#--------------------------------------------------------------------------
def self.script_setup
#
path_test = "Data/Scripts.rxdata" # Define scripts database file
scripts_db = load_data(path_test) # Load file into memory
list_size = scripts_db.size # Acquire total number of scripts
#
# Cycle through them all
for i in 0...list_size # Cycle through all scripts
script_setup_hash(i+1, scripts_db[i]) # Put into hash array entries
end
#
p @scripts_name[i+1] # Show the last script's name
p @scripts_list[i+1] # Show the script decoded as array
p @scripts_list[i+1][3] # Show line 4 of the last script
#
end
#--------------------------------------------------------------------------
# * Put each script into its own hash entry
# key_id : key in hash for the script (starts from 1, not 0 as an index)
# script : script contents
#--------------------------------------------------------------------------
def self.script_setup_hash(key_id, script)
#
txt_id = script[0] # Unknown variable for script...
txt_name = script[1] # Script name in editor
txt_decode = tool_inflate(script[2]) # Script converted to text
#
txt_sep = "\r\n" # Define separator between lines
txt_array = txt_decode.split(txt_sep) # Split script into array
#
@scripts_name[key_id] = txt_name # Push the name of the script
@scripts_list[key_id] = txt_array # Push the usable script array
#
end
#--------------------------------------------------------------------------
# * ZLib Tool Inflate
# data_text : text to be inflated/expanded from encryption
#--------------------------------------------------------------------------
def self.tool_inflate(data_text)
#
zlib_tool = Zlib::Inflate.new() # Create the ZLib Inflate tool
new_text = zlib_tool.inflate(data_text) # Decrypt/expand with the tool
zlib_tool.finish # Show work done
zlib_tool.close # Close the tool
return new_text # Return the expanded text
#
end
#--------------------------------------------------------------------------
# * ZLib Tool Deflate
# data_text : text to be compressed/encrypted
# level : a defined compression level (see comments)
#--------------------------------------------------------------------------
def self.deflate(data_text, level = Zlib::BEST_COMPRESSION)
#
# Zlib::DEFAULT_COMPRESSION (-1)
# Zlib::NO_COMPRESSION 0
# Zlib::BEST_SPEED 1
# Zlib::BEST_COMPRESSION 9
#
zlib_tool = Zlib::Deflate.new(level) # Create the ZLib Inflate tool
new_text = zlib_tool.deflate(data_text, Zlib::FINISH) # Encrypt until done
zlib_tool.close # Close the tool
return new_text # Return the expanded text
#
end
end
# This executes at start
Script_list.main
This will store the 'name' of a script from the editor in one hash array, and the script itself in a similar matching hash array. Separate, but they both use the same matching key (numeric value). As defined in the script, the first script would be key #1, the second being Key #2... rather than by index positions that begin from 0 on up.
Ergo, key 7 would get both the name "Game_Picture" and separately the script for Game_Picture as an array. No clumsy illegal characters, and broken into their individual lines.
How can this be adapted for use to read VX or VXAce scripts? Just change the line that reads path_test = "Data/Scripts.rxdata" on line 21. I'm not fond of RPGMaker VX, but I do have it and did test this to confirm.
USEFULNESS?
Well, this COULD be used to generate a script Exporter of sorts, whether to output to a file or series thereof.
Or more comically... to make a FREAKIN SCRIPT TEXT EDITOR. But ya better be pretty dang good at making it possible to edit text that way.
RE: What's up, RMers? - kyonides - 08-09-2024
Unnecessary if you know that such data is being stored in $RGSS_SCRIPTS already.
RE: What's up, RMers? - DerVVulfman - 08-09-2024
(08-09-2024, 12:20 AM)kyonides Wrote: Unnecessary if you know that such data is being stored in $RGSS_SCRIPTS already. That is not really true.
Whether you load the scripts database with:
script_list = load_data("Data/Scripts.rxdata")
or
script_list = $RGSS_SCRIPTS
the result is the same...
A test result with the actual script content under ZLIB compression.
Generated with $RGSS_SCRIPTS (the same result with my code).
My above code acquires the script database, breaks the database down into a hash array so you can grab an individual script by a key value, expands and basically decrypts each script in the database itself into something legible, and breaks each script down into individual lines to make it even more useful and essentially legible.
The only thing argued is that I used the classic load_data command used already within the system rather than $RGSS_SCRIPTS which you have just suggested. Other than that, nothing else would have changed.
BUT... on an incidental note, the $RGSS_SCRIPTS variable appears nowhere in the default scripts, no where in the help manual, would surely not be in Ruby-Docs, and does not appear in any Google search. Considering the Game.Ini file has the line Scripts=Data\Scripts.rxdata, I assume it is here where $RGSS_SCRIPTS comes into play. Only one who would have been working on their own replacement engine would have known that.
RE: What's up, RMers? - kyonides - 08-09-2024
Nah, just print all of the global variables by calling:
Code: print global_variables.join("\n")
RE: What's up, RMers? - DerVVulfman - 08-09-2024
Oh, dang. Part of the Kernel module. Indeed, global_variables would be a way to find all the globals... as long as at the bottom of the script list. Heh. Put it at the top and it skips everything below it.
Well, I have made a breakthrough when it comes to sifting through scripts. I can now retrieve the custom cache methods from RPG::Cache from any custom script!
Indeed, if a script appeared ...
module RPG::Cache
def self.menus(filename, hue=0)
or
module RPG
module Cache
def self.cursors(filename)
... it can retrieve both the names menus and cursors. Which means it does recognize the RPG::Cache module, whether formatted in one line or broken up in twain.
This is stage 1 in being able to actively search scripts for resources. Stage 2 will require me to delve into each method (def) and get the pathway suggested by each method. Given my proclivity to have the path be 'variable', this could be ... entertaining.
RE: What's up, RMers? - DerVVulfman - 08-16-2024
I can now define the RPG::Cache.(method_name) to the actual subfolders... mainly assumed to be in the project's Graphics folder.
...defining the actual Folder Pathways to custom RPG::Cache methods!
The right image is a 'sample' RPG::Cache code that I am using to work out the mechanics of the sifter cache folder scripts. The first is a 'direct' assignment of a folder name, the second and third are versions that assumes the game developer (like YOU) can change the name in a script.
And the left image shows that I can create the relationship between the calling methods created within RPG::Cache and the actual folder paths. And in it, the 'path' word is merely a placeholder for what is meant to be the name of the subfolder... assuming certain conditions of course.
THIS part will be a FUN one to figure out. But once done, I very well may be able to acquire resources used by custom scripts for my resource sifter!
RE: What's up, RMers? - kyonides - 08-18-2024
Now working on the KGuildFame XP demo loosely based on the Guild scene available in the Generations of War games. I guess I should have picked the term Titles instead of Fame there, but I'm lazy so I'll be keeping the current name for good.
RE: What's up, RMers? - Remi-chan - 08-18-2024
t update last week due to a number of factors, most of which was recovering from a surgery alongside just a general lack of productivity in regards to such!
But that has changed, i am getting close to my full recovery and dismorning I implemented some things. For see, someone has been busy during my sojourn of silence.
Pjcr
To say my gambit to motivate him to send me files went well is putting it lightly. Ten files per fortnight at minimum now, he should be done with the vast majority of the games files before it becomes a concern.
I didn't even need over half of these yet! The white skeleton and ghosty gal aren't even until Disk 3, but there was a funny mishap and it just saves me time in the future.
So the winning strat was to make the resolution of the files small enough that they could be uploaded before something happened to stop them. We started at 25% size but decided 50% was fine after some experiments.
So here's a single screenshot of just one of these in-game.
Why don't I show the rest?
1. Lots of clutter
2. I have videos for that!
Svana and the Sylph
It's pretty rare for someone to so easily bypass Fyori's general stiff nature, but Svana broke through by being a philosophical rizzler, truly brazen!
Mothers Reunited
In this episode of "Mum powers are bullsh**!" we get to see a casual conversation between Svoli and her mother Svana.
Finally, these snakes have been hanging for some love, so lets get to our final video!
The High Courtesan
A redo of an older video, now with all the updated portraits.
For the record, I am still largely on break. I plan to return to gamedev full time in early to mid-september.
Until then, implementing some files just saves me a bit of work later and gives me some youtube content.
Speaking of my youtube, we recently hit 250 subscribers, at last. It's been slow-going but I'ma sticking with it. My twitter is also in the 2300s.
RE: What's up, RMers? - DerVVulfman - 08-23-2024
My endeavor to scour through the actual Script database in RPGMaker XP... WHILE running in RPGMaker XP... ran into an odd snag.
If someone wished to set up custom sub-folders within their project's Graphics folder, they would likely add their folder paths to the RPG::Cache module. And in doing so, they would undoubtedly create a new method* to retrieve the graphics from that folder. So my work to scour through the RPG::Cache module requires that my code find any viable methods. Viable methods would provide folder-paths such as:
Graphics\KyoQuestIcons or Graphics\RemiAnims.
If ya like the name of the subfolder, kyonides... go for it.
This way, I could find the self.kyo_q_icon method and acquire its matching Graphics\KyoQuestIcons sub-folder.
But, what if a method discovered was not one that retrieved a viable bitmap for use? What if it was an already existing method that someone wished to tamper with, or one that was added that performed a new or unique function? Therein was the problem... It would find a method, but had no folder-paths to use.
Until I decided to create an array to test against:
Quote:module Script_list
#--------------------------------------------------------------------------
# * Invariables
#--------------------------------------------------------------------------
SL_IGNORE = "Cache_list.main"
# Names of all default cache method names
DEFAULTS = ['animation', 'autotile', 'battleback', 'battler', 'character',
'fog', 'gameover', 'icon', 'panorama', 'picture', 'tileset',
'title', 'windowskin', 'tile']
# Methods within RPG::Cache that are not to be checked
FORBID = ["def self.clear", "def self.load_bitmap", "def self.test_1",
"def self.test_2" ]
# Search test phrases
PHRASE = [ "module RPG::Cache", "module RPG", "module Cache", "def self.",
"self.load_bitmap", "begin", "if ", "end" ]
PATH_BROKE = "INVALID"
#--------------------------------------------------------------------------
# * Main Processing
#--------------------------------------------------------------------------
def self.main
#
This code has a FORBID constant array, an array that listing methods that exist within the RPG::Cache module that are not to be examined. While the first two are part of the RPG::Cache's innate system, the other two are custom methods I wrote that allow for testing of cached graphics existence. So indeed, this can be expanded.
And now, it will skip any method that is within the RPG::Cache module that shouldn't be tested... and not give me any stupid errors.
But the next phase is going to be the headache phase...
In the event that the folder-path (ex: Graphics\Goofball) isn't fixed, but is instead something the game developer can configure, I have to search for the SOURCE of the path... the configuration page, section, variable.
For this, I have to determine what kind of search I am even doing? If the variable used to define the folder path begins with "$", that is a global variable and can be ANYWHERE in the entire run of the script database. If it has '::' within the name, it is likely a constant within some separate configuration module... and may be in pairs. And I have to test for both instance and local variable usage too.
Much to do.
|