07-07-2020, 09:47 PM 
(This post was last modified: 12-13-2023, 06:13 PM by DerVVulfman.)
	
	
	Kichi's Kached Audio
Version: 1.2
![[Image: Eb6tpGA.png]](https://i.imgur.com/Eb6tpGA.png)
Version: 1.2
![[Image: Eb6tpGA.png]](https://i.imgur.com/Eb6tpGA.png)
Introduction
This system is designed with the purpose of protecting your game's music and audio files, giving them a minor encryption using the same data structure as RPGMaker XP's innate system, storing them within the game's Data folder, and allowing them to be compressed/encrypted within the .rgsaad file.
Features
- Gives a level of protection towards your audio files
 - Places them within your Data Folder and permits rxdata compression
 
Drawbacks
- Audio files take slightly longer to load based on filesize
 - Minor increase in cached file size by approximately 1.18 percent
 - Audio saved in the Data folder cannot be played in the Editor, game only*
 - During playback, original file is temporarily 'restored' in the root folder... but PROTECTED from copying anyway.
 
* Caching audio files makes valid .rxdata copies within your Data folder, and does not erase the originals. Original audio files within the Audio folder may still be played.
Script
 Kached Audio
  			Code:
#==============================================================================
# ** Kichi's Kached Audio
#------------------------------------------------------------------------------
#    version 1.2
#    by DerVVulfman
#    08-21-2023 (MM-DD-YYYY)
#    RGSS / RPGMaker XP
#==============================================================================
#
#  INTRODUCTION:
#
#  This system is designed with the purpose of protecting your game's music and
#  audio files, giving them a minor encryption using the same data structure as
#  RPGMaker XP's innate system, storing them within the game's Data folder, and
#  allowing them to be compressed/encrypted within the .rgsaad file.
#
#  It is nearly plug and play,  with only a couple minor issues that are noted.
#
#  Positives:
#  * Gives a level of protection towards your audio files
#  * Places them within your Data Folder and permits rxdata compression
#
#  Negatives:
#  * Audio files take slightly longer to load based on filesize
#  * Minor increase in cached file size by approximately 1.18 percent
#  * Audio saved in the Data folder cannot be played in the Editor, game only
#
#
#------------------------------------------------------------------------------
#
#  INSTALLATION:
#
#  Place this script below Scene_Debug and above Main. Use the described script
#  calls to cache, monitor and/or un-cache your files.
#
#
#------------------------------------------------------------------------------
#
#  FILES CREATED:
#
#  This system  will create a series of subfolders  within your project's Data
#  folder, these subfolders being BGM, BGS, ME and SE. It is within these that
#  your stored 'cached' audio files will be recorded in .rxdata format.
#
#  As all audio files kept here will have the same extension,  the system will
#  also create the 'AudioCache.rxdata' file,  this too within the Data folder.
#
#  The AudioCache file keeps track  of all cached audio files,  and their file
#  extensions.  This is vital for both playback of all cached audio files,  as
#  well as un-cached file restoration  if the original audio files from within
#  the project's Audio folder had been accidentally erased.
#
#  All of the  cached audio files will begin  with '~$' to denote and identify
#  them as temporary 'owner locked' files.  Initially for Microsoft Word, this
#  prefix will identify these filenames for temporary usage. This was found to
#  be necessary with users who have their PCs connected to cloud services.
#
#
#------------------------------------------------------------------------------
#
#  USAGE:
#
#  When first run, this system generates a new file which it saves within your
#  Data folder.  This file is the 'AudioCache.rxdata' data file, and its pur-
#  pose is to  help track your cached audio files,  and stores the file exten-
#  sions of your audio for proper playback.  It must not be deleted, otherwise
#  the system will not function.
#
#  The default methods  of play_bgm, play_bgs, play_me and play_se  have been
#  altered to allow the playback of audio from within the Data folder. At the
#  same time, a method within the sprite class that generate battle animation
#  sound effects have too been altered.   These alterations allow  the system
#  determine  if the audio files  have been cached,  or are still  within the
#  default audio folder.   This is quite handy as you will not be caching any
#  audio from the RTP itself, only that from your own game project.
#
#  There isn't a built-in function that will automatically  cache all of your
#  audio files.  If you do not have access to a custom editor which may cache
#  or un-cache an audio file,  I will describe  the actual script calls which
#  will need to use this system.  Even if you have an editor, please read it.
#
#  If you are actively done with your project and are about to compress your
#  game into the .rgsaad format, you will need to toggle the RGSAAD variable
#  in the below 'AudioCache' module.  The script itself does not know if the
#  project is or isn't compressed, and needs this switch activated if true.
#
#
#------------------------------------------------------------------------------
#
#  CLOUD SERVICES:
#
#  The .rxdata audio files are stored with the '~$' prefix,  identifying these
#  files as temporary and to be ignored by cloud services.  This was necessary
#  for the system to work as it temporarily extracts the audio files for play-
#  back. However, this means that a variety of cloud storage services will not
#  copy and save your .rxdata encrypted audio files.  In this instance, you'll
#  need to manually upload them into your cloud.
#
#
#------------------------------------------------------------------------------
#
#  SCRIPT CALLS:
#
#  * Is it cached?
#    -------------
#    Syntax:   result = $game_system.is_cached?(file, path)
#    Params:   file - the filename of the audio file (no extension included)
#              path - the folder path ( "BGM\", "BGS\", "ME\" or "SE\ ")
#
#    Function: This script call  will let you know if the defined audio file
#              has been cached and can be found within the Data folder under
#              its specifically defined path.
#
#
#                          *     *     *     *     *
#
#
#  * Cache the audio file
#    --------------------
#    Syntax:   $game_system.cache_audio(file, path)
#    Params:   file - the filename of the audio file (no extension included)
#              path - the folder path ( "BGM\", "BGS\", "ME\" or "SE\ ")
#
#    Function: This script call is used to convert its innate format into an
#              rxdata format  which can be cached.  It stores  the resulting
#              file within the Data folder and its defined path, and records
#              the file and its original extension  in the AudioCache  file.
#              Once this  is  performed,  checking  the audio file  with the
#              is_cached? script call (above) will return a 'true' result.
#
#              This does not delete the original file from the Audio folder.
#
#
#                          *     *     *     *     *
#
#
#  * Read/Restore from a Cache
#    -------------------------
#    Syntax:   $game_system.audio_cache(file, path)
#    Params:   file - the filename of the audio file (no extension included)
#              path - the folder path ( "BGM\", "BGS\", "ME\" or "SE\ ")
#              dest - (optional) a folder in which to save the audio copy
#
#    Function: This script call reads the cached audio file  from the proper
#              Data folder,  and converts it  into its original music format
#              so it may be properly played within the system. It is assumed
#              that the restored file  is to be deleted after playback,  and
#              this call does not delete the file from the data folder.
#
#              This brings out a copy of the audio file from the Data folder
#              and saves it to the described destination folder,  or it will
#              be saved in the project's root folder if none specified.
#
#
#                          *     *     *     *     *
#
#
#  * Remove from the Cache
#    ---------------------
#    Syntax:   $game_system.remove_cache(file, path)
#    Params:   file - the filename of the audio file (no extension included)
#              path - the folder path ( "BGM\", "BGS\", "ME\" or "SE\ ")
#
#    Function: This script call is intended to restore the cached audio file
#              back to the specified Audio folder, and eliminate it from the
#              specified Data folder.  Along with that,  it will also remove
#              the file  from the AudioCache list.  Doing so will generate a
#              'false' return when the 'is_cached?' script call is used.
#
#              If an audio folder  (BGM, ME, etc) is empty,  the folder will
#              be erased. A nice little bit of cleanup duty, eh?
#
#              Removing a cached file  will overwrite  any audio file within
#              the project's actual Audio folder it it exists.
#
#              When this command  is used  to restore  the last cached audio
#              file, the AudioCache.rxdata file will have been emptied. When
#              that happens, the AudioCache.rxdata file will also be erased.
#              You will need to restart  or use the Kichi Editor to recreate
#              the AudioCache file for the file extention tracking service.
#
#
#------------------------------------------------------------------------------
#
#  KNOWN CONFLICTS:
#
#  This script will obviously conflict with any other script that will modify
#  the bgm_play, bgs_play, me_play and/or se_play methods within Game System,
#  and will conflict with any script that alters the animation_process_timing
#  method within the RPG::Sprite class that handles battle system effects.
#
#
#------------------------------------------------------------------------------
#
#  COMPATIBILITY:
#
#  Fairly compatible for RPGMaker XP systems other than the conflicts just
#  described above.
#
#
#------------------------------------------------------------------------------
#
#  CREDITS AND THANKS:
#
#  Thanks goes to Carlos_Davilla.   Were not for his assistance,  I would not
#  have been aware of issues pertaining cloud storage systems, nor the use of
#  established temporary file naming conventions to fix the issues.
#
#
#==============================================================================
#
#  TERMS AND CONDITIONS:
#
#  Free to use, even in commercial projects.  Just note that I need some form
#  of due credit, not just myself but Carlos_Davilla who assisted.
#
#
#==============================================================================
module AudioCache
  
  RGSAAD  = true  # Switch to identify that that the project is encrypted
  
end
#==============================================================================
# ** RPG::Sprite
#------------------------------------------------------------------------------
#  This is a class within the RPGXP module itself that loads each of RPGXP's
#  graphic formats, creates a Bitmap object, and retains it.
#==============================================================================
class RPG::Sprite < ::Sprite
  #--------------------------------------------------------------------------
  # * Process Timing for animation flash and effects
  #     timing : timing
  #     hit    : flag determining battle action effectiveness
  #--------------------------------------------------------------------------
  def animation_process_timing(timing, hit)
    if (timing.condition == 0) or
       (timing.condition == 1 and hit == true) or
       (timing.condition == 2 and hit == false)
      if timing.se.name != ""
        se = timing.se
        # If we have a cached file, use cached system
        if $game_system.is_cached?("~$" + se.name,"SE/")
          name = "~$" + se.name
          $game_system.audio_cache(name,"SE/")
          # Playback audio (not tied now to the Audio/SE folder)
          Audio.se_play(name, se.volume, se.pitch)
          # Acquire cached extension
          ext = $game_system.get_cache_ext(name, "SE/")
          # Delete temp file generated by audio_cache
          File.delete(name + ext)
        # Otherwise, default playback system
        else
          Audio.se_play("Audio/SE/" + se.name, se.volume, se.pitch)
        end          
      end
      case timing.flash_scope
      when 1
        self.flash(timing.flash_color, timing.flash_duration * 2)
      when 2
        if self.viewport != nil
          self.viewport.flash(timing.flash_color, timing.flash_duration * 2)
        end
      when 3
        self.flash(nil, timing.flash_duration * 2)
      end
    end
  end
end
#==============================================================================
# ** Game_System
#------------------------------------------------------------------------------
#  This class handles data surrounding the system. Background music, etc.
#  is managed here as well. Refer to "$game_system" for the instance of
#  this class.
#==============================================================================
class Game_System
  #--------------------------------------------------------------------------
  # * Alias Listings
  #--------------------------------------------------------------------------
  alias dvv_game_system_cache_audio_initialize initialize
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    # Perform the original call
    dvv_game_system_cache_audio_initialize
    # Set up the audio cache data file
    initialize_audio_cache  
  end
  #--------------------------------------------------------------------------
  # * Play Background Sound
  #     bgs : background sound to be played
  #--------------------------------------------------------------------------
  def bgm_play(bgm)
    @playing_bgm = bgm
    if bgm != nil and bgm.name != ""
      # If we have a cached file, use cached system
      if is_cached?("~$" + bgm.name,"BGM/")
        name = "~$" + bgm.name
        audio_cache(name,"BGM/")
        # Playback audio (not tied now to the Audio/BGM folder)
        Audio.bgm_play(name, bgm.volume, bgm.pitch)
        # Acquire cached extension
        ext = get_cache_ext(name, "BGM/")
        # Delete temp file generated by audio_cache
        File.delete(name + ext)
      # Otherwise, default playback system
      else
        Audio.bgm_play("Audio/BGM/" + bgm.name, bgm.volume, bgm.pitch)
      end
    else
      Audio.bgm_stop
    end
    Graphics.frame_reset
  end
  #--------------------------------------------------------------------------
  # * Play Background Sound
  #     bgs : background sound to be played
  #--------------------------------------------------------------------------
  def bgs_play(bgs)
    @playing_bgs = bgs
    if bgs != nil and bgs.name != ""
      # If we have a cached file, use cached system
      if is_cached?("~$" + bgs.name,"BGS/")
        name = "~$" + bgs.name
        audio_cache(name,"BGS/")
        # Playback audio (not tied now to the Audio/BGS folder)
        Audio.bgs_play(name, bgs.volume, bgs.pitch)
        # Acquire cached extension
        ext = get_cache_ext(name, "BGS/")
        # Delete temp file generated by audio_cache
        File.delete(name + ext)
      # Otherwise, default playback system
      else
        Audio.bgs_play("Audio/BGS/" + bgs.name, bgs.volume, bgs.pitch)
      end
    else
      Audio.bgs_stop
    end
    Graphics.frame_reset
  end
  #--------------------------------------------------------------------------
  # * Play Music Effect
  #     me : music effect to be played
  #--------------------------------------------------------------------------
  def me_play(me)
    if me != nil and me.name != ""
      # If we have a cached file, use cached system
      if is_cached?("~$" + me.name,"ME/")
        name = "~$" + me.name
        audio_cache(name,"ME/")
        # Playback audio (not tied now to the Audio/ME folder)
        Audio.me_play(name, me.volume, me.pitch)
        # Acquire cached extension
        ext = get_cache_ext(name, "ME/")
        # Delete temp file generated by audio_cache
        File.delete(name + ext)
      # Otherwise, default playback system
      else
        Audio.me_play("Audio/ME/" + me.name, me.volume, me.pitch)
      end
    else
      Audio.me_stop
    end
    Graphics.frame_reset
  end
  #--------------------------------------------------------------------------
  # * Play Sound Effect
  #     se : sound effect to be played
  #--------------------------------------------------------------------------
  def se_play(se)
    if se != nil and se.name != ""
      # If we have a cached file, use cached system
      if is_cached?("~$" + se.name,"SE/")
        name = "~$" + se.name
        audio_cache(name,"SE/")
        # Playback audio (not tied now to the Audio/SE folder)
        Audio.se_play(name, se.volume, se.pitch)
        # Acquire cached extension
        ext = get_cache_ext(name, "SE/")
        # Delete temp file generated by audio_cache
        File.delete(name + ext)
      # Otherwise, default playback system
      else
        Audio.se_play("Audio/SE/" + se.name, se.volume, se.pitch)
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Cache Audio List Initialization
  #--------------------------------------------------------------------------
  def initialize_audio_cache
    # Establish Paths
    cache_path  = "Data/AudioCache.rxdata"
    # Generate / load cached audio data
    @cache_audiolist    = {}
    # If a cached/encrypted game
    if AudioCache::RGSAAD
      # We load current data and exit, no saving done
      @cache_audiolist  = load_data(cache_path)
      return
    end
    # Either load current or save new data file
    if File.exists?(cache_path)
      @cache_audiolist  = load_data(cache_path)
    else
      save_data(@cache_audiolist, cache_path)
    end
  end
  #--------------------------------------------------------------------------
  # * Get audio list
  #--------------------------------------------------------------------------
  def cache_audiolist
    @cache_audiolist
  end
  #--------------------------------------------------------------------------
  # * Is file cached?
  #     file : audio file being tested
  #     path : the initial folder in the Data folder for testing
  #--------------------------------------------------------------------------
  def is_cached?(file, path)
    # Establish Paths
    data_path   = "Data/"   + path
    # Determine true if it exists within the cached audio list
    key         = data_path + file
    return true if @cache_audiolist.has_key?(key)
    return false
  end
  #--------------------------------------------------------------------------
  # * Cache Audio
  #     file : audio file to be cached
  #     path : the initial folder from the Audio folder, now the Data folder
  #--------------------------------------------------------------------------
  def cache_audio(file, path)
    # Establish Paths
    cache_path  = "Data/AudioCache.rxdata"
    audio_path  = "Audio/"  + path
    data_path   = "Data/"   + path
    # Create specified data folder unless it exists
    Dir.mkdir(data_path) unless File.exists?(data_path)  
    # Acquire file extension
    ext = get_file_ext(audio_path + file)
    # Load File from specified audio folder
    afile       = File.open(audio_path + file + ext, "rb")
    audio_data  = afile.readlines
    afile.close    
    #
    file = "~$" + file
    # Save File in specified data folder
    save_data(audio_data, data_path + file + ".rxdata")
    # Record audio extension in cached audio list
    key         = data_path + file
    unless @cache_audiolist.has_key?(key)
      @cache_audiolist[key] = ext
    end
    # Save cached audio data
    save_data(@cache_audiolist, cache_path)
  end
  #--------------------------------------------------------------------------
  # * Get Audio from Cache
  #     file : audio file that is cached
  #     path : the folder within the Data folder, mirroring the Audio folder
  #     dest : custom audio path in which to redirect
  #--------------------------------------------------------------------------
  def audio_cache(file, path, dest="")
    # Establish Paths
    data_path   = "Data/"   + path
    # Acquire extension from cached audio list
    ext          = get_cache_ext(file, path)
    # Load File from specified data folder
    audio_data  = load_data(data_path + file + ".rxdata")
    # Perform file manipulation if possible
    begin    
      # Save File into folder for playback
      afile       = File.open(dest+file + ext, "wb")
      afile.print audio_data
      afile.close
    rescue
      # Cannot open as yet
    end    
  end
  #--------------------------------------------------------------------------
  # * Remove from Cache
  #     file : audio file that is returned to the Audio folder (if needed)
  #     path : the folder within the Data folder, mirroring the Audio folder
  #--------------------------------------------------------------------------
  def remove_cache(file, path)
    # Establish Paths and renamed rxdata file
    cache_path      = "Data/AudioCache.rxdata"
    audio_path      = "Audio/"  + path
    data_path       = "Data/"   + path
    rx_file         = "~$" + file
    # Acquire extension from cached audio list
    ext             = get_cache_ext(rx_file, path)
    # Load rx audio file from the specified data folder
    audio_data      = load_data(data_path + rx_file + ".rxdata")
    # Create the return pathway for the audio file (trims last character)
    return_path     = audio_path[ 0, (audio_path.length)-1 ]
    # Create the audio folder and subfolders if necessary
    Dir.mkdir("Audio") unless File.exists?("Audio")
    Dir.mkdir(return_path) unless File.exists?(return_path)  
    # Perform file manipulation if possible
    begin
      # Save File into folder for playback
      afile = File.open(audio_path + file + ext, "wb")
      afile.print audio_data
      afile.close        
    rescue
      # Restoration/copying permission must be denied
      p "File could not be restored within the " + return_path + " folder."
      return
    end
    # Delete the rxdata file from specified data folder
    File.delete(data_path + rx_file + ".rxdata")
    # Acquire the key and erase from the cached audio list
    key         = data_path + rx_file    
    @cache_audiolist.delete(key) if @cache_audiolist.has_key?(key)
    # Remove empty cached audio folders
    remove_cache_pathway
    # Eliminate AudioCache file and exit if empty
    num_keys = @cache_audiolist.keys.size
    return File.delete(cache_path) if num_keys == 0
    # Save the updated cached audio list
    save_data(@cache_audiolist, cache_path)
  end
  #--------------------------------------------------------------------------
  # * Remove empty cached audio folders
  #--------------------------------------------------------------------------
  def remove_cache_pathway
    begin
      Dir.rmdir("Data/BGM")
    rescue
    end
    begin
      Dir.rmdir("Data/BGS")
    rescue
    end
    begin
      Dir.rmdir("Data/ME")
    rescue
    end
    begin
      Dir.rmdir("Data/SE")
    rescue
    end
  end
  #--------------------------------------------------------------------------
  # * Get the extension of an Audio folder file
  #     filename : filename
  #--------------------------------------------------------------------------
  def get_file_ext(filename)
    if FileTest.exist?(filename + ".wav")
      return ".wav"
    elsif FileTest.exist?(filename + ".mp3")
      return ".mp3"
    elsif FileTest.exist?(filename + ".ogg")
      return ".ogg"
    elsif FileTest.exist?(filename + ".mid")
      return ".mid"
    elsif FileTest.exist?(filename + ".wma")
      return ".wma"
    end
  end
  #--------------------------------------------------------------------------
  # * Get the extension of an Data folder cached file
  #     file : audio file that is in the Data Folder
  #     path : the folder within the Data folder to test
  #--------------------------------------------------------------------------
  def get_cache_ext(file, path)
    data_path   = "Data/"   + path
    key         = data_path + file
    ext         = @cache_audiolist[key]
    return ext
  end
endInstructions
Pretty much plug-and-play in its functions, but it does require you to use script calls to actively 'cache' audio which self-stores within the Data folder. And when you are done, use the "RGSAAD" value to let the script know it is ready for an encrypted/compressed game.
Detailed instructions are inside the script itself.
But if you need an editor for the system, a simple and easy-to-use one is available below:
 Kached Audio Editor
  			Code:
#==============================================================================
# ** Kichi's Kached Audio Editor
#    An editor for 'Kichi's Kached Audio' by DerVVulfman
#------------------------------------------------------------------------------
#    version 1.1
#    by DerVVulfman
#    08-21-2023 (MM-DD-YYYY)
#    RGSS / RPGMaker XP
#    Requires:  Kichi's Kached Audio to function
#
#==============================================================================
#
#  INTRODUCTION:
#
#  This is a very simple editor utilizing the arrow keys to select your chosen
#  audio file which cna be cached or uncached by hitting the Enter/Select key.
#  When a file is cached,  it is turned into an .rxdata file and sent into the
#  appropriate subfolder  within your game's  data folder.   And when an audio
#  file has been un-cached, it is removed from the data folder. All changes to
#  the files are updated within the 'Cache Audio' system's  'AudioCache'  data
#  file.  And no files are ever deleted  from the project's root audio folder.
#  However,  un-caching a file  will restore it to its spefified Audio folder,
#  and may overwrite an existing audio file of the same name.
#
#------------------------------------------------------------------------------
#
#  INSTALLATION:
#
#  Place this script either above or below 'Cached Audio' to function. To acti-
#  vate, toggle the KACHED_AUDIO_EDITOR value to true. A false setting disables
#  this script.
#
#  The BUFFER_SIZE value  is to ensure that projects  with large quantities of
#  does not bog down the file loading system  which could result in 'script is
#  hanging' errors to occur.
#
#
#==============================================================================
  # Just set this to 'true' and it's turned on, or to 'false' to disable it.
  # The rest of the instructions are in the menu.
  #
    KACHED_AUDIO_EDITOR = false
  # This sets how many songs/files are read before a necessary Graphics frame
  # update.  This to prevent 'script hanging' issues.  Larger values slows
  # down the system, but with better running security on editor load.
  #
    BUFFER_SIZE  = 100  
#==============================================================================
# *** The actual editor, once made fully active
#==============================================================================  
if KACHED_AUDIO_EDITOR == true
  #============================================================================
  # ** Game_AudioCacheEditor
  #----------------------------------------------------------------------------
  #  It is the MP3 player.
  #============================================================================
  class Game_AudioCacheEditor
    #------------------------------------------------------------------------
    # * Object Initialization
    #------------------------------------------------------------------------
    def initialize
      # Initialization of data
      @page         = 1   # Setting page to BGMs at start
      @list_bgm     = []  # List of BGM audio
      @list_bgs     = []  # List of BGS audio
      @list_me      = []  # List of ME audio
      @list_se      = []  # List of SE audio
      # Generate Data Lists for each audio type
      @list_bgm     = list_process("BGM/")
      @list_bgs     = list_process("BGS/")
      @list_me      = list_process("ME/")
      @list_se      = list_process("SE/")
      # Create array for List Window
      @list_window  = []
      # Generate List Windows
      @list_window[1]   = Window_ACList.new(@list_bgm)
      @list_window[2]   = Window_ACList.new(@list_bgs)
      @list_window[3]   = Window_ACList.new(@list_me)
      @list_window[4]   = Window_ACList.new(@list_se)
      # Define windows active/inactive
      for i in 2..4
        @list_window[i].visible = false
        @list_window[i].active  = false
      end
      @list_window[1].visible   = true
      @list_window[1].active    = true
      ## Compilation of data window
      @data_window = Window_ACData.new
      @help_window = Window_ACHelp.new
    end
    #------------------------------------------------------------------------
    # * Process List
    #------------------------------------------------------------------------
    def list_process(type)
      # Initialize values based on folder type
      audio_sort    = "Audio/" + type + "*"   # Used to acquire folder filenames
      audio_pattern = /Audio\/#{type}(.*?)/   # Formerly /Audio\/BGM\/(.*?)/
      data_type     = "Data/" + type          # To acquire Data subfolder
      d_size        = data_type.size          # To slice point in name
      # Create arrays for data acquisition
      inituncached  = []
      initcached    = []
      temp          = []
      result        = []
      # Reset the buffer
      buffer        = 0
      # Generate uncached data list
      for name in Dir[audio_sort]
        # Handle buffer
        buffer += 1
        if buffer > BUFFER_SIZE
          buffer = 0
          Graphics.update
        end          
        # Acquire name from Directory
        tempname = name.sub(audio_pattern) { $1 }
        next unless init_extensions?(tempname)
        inituncached.push(tempname)
      end
      # Generate cached data
      for key in $game_system.cache_audiolist
        # Handle buffer
        buffer += 1
        if buffer > BUFFER_SIZE
          buffer = 0
          Graphics.update
        end  
        tempname =  key[0]
        next unless tempname[0,d_size] == data_type
        tempname = key[0]
        tempname = tempname[d_size,(tempname.size)-d_size]
        tempname = tempname + key[1]
        audio_filename = tempname.delete "~$"
        initcached.push(audio_filename)
      end
      # Eliminate cached files from uncached list
      for name in inituncached
        temp.push(name) unless initcached.include?(name)
      end
      # Push names as arrays with binary tags  (0 = uncached, 1 = cached)
      for name in initcached
        result.push( [name,1] )
      end
      for name in temp
       result.push( [name,0] )
      end
      # Sort list and return
      result.sort!
      return result
    end
    #------------------------------------------------------------------------
    # * Test for file extension
    #------------------------------------------------------------------------
    def init_extensions?(name)
      effective = false
      effective = true if name[(name.size)-4,4] == ".mid"
      effective = true if name[(name.size)-4,4] == ".mp3"
      effective = true if name[(name.size)-4,4] == ".ogg"
      effective = true if name[(name.size)-4,4] == ".wav"
      effective = true if name[(name.size)-4,4] == ".wma"
      return effective
    end
    #------------------------------------------------------------------------
    # * Frame Update
    #------------------------------------------------------------------------
    def update
      @oldpage = @page if @oldpage.nil?
      # Update windows
      @list_window[@page].update
      # If in active list
      update_list if @list_window[@page].active
      # Acquire list index
      @idx = @list_window[@page].index
      # Update Data window when scrolling
      if @name != @list_window[@page].item(@idx)[0]
        @name = @list_window[@page].item(@idx)[0]
        @state = @list_window[@page].item(@idx)[1]
        @data_window.refresh(@page, @name, @state)
      end
    end
    #------------------------------------------------------------------------
    # * Frame Update : when list window is active
    #------------------------------------------------------------------------
    def update_list
      # Handle page change
      update_list_paging
      return if update_list_decision?
    end
    #------------------------------------------------------------------------
    # * Frame Update : when list window is active - changing pages
    #------------------------------------------------------------------------
    def update_list_paging
      if Input.repeat?(Input::LEFT)
        @page -= 1
        @page = 4 if @page == 0
      end
      if Input.repeat?(Input::RIGHT)
        @page += 1
        @page = 1 if @page == 5
      end
      if @oldpage != @page
        @list_window[@oldpage].visible = false
        @list_window[@oldpage].active  = false
        @list_window[@page].visible   = true
        @list_window[@page].active    = true
        @oldpage = @page
        @data_window.refresh(@page, @name, @state)
      end
    end
    #------------------------------------------------------------------------
    # * Frame Update : when list window is active - hitting Enter/Select
    #------------------------------------------------------------------------
    def update_list_decision?
      # Exit unless hitting Enter/Select
      return false unless Input.trigger?(Input::C)
      # Play Decision SE
      $game_system.se_play($data_system.decision_se)
      # Choose Path for Cache System
      case @page
      when 1 ; path = "BGM/"
      when 2 ; path = "BGS/"
      when 3 ; path = "ME/"
      when 4 ; path = "SE/"
      end
      # If State is already cached
      if @state == 1
        # Get filename without extension, uncache, and change state
        file = @name[0,(@name.size)-4]
        $game_system.remove_cache(file, path)
        @state = 0
      # Otherwise, state is not cached
      else
        # Get filename without extension, cache, and change state
        file = @name[0,(@name.size)-4]
        $game_system.cache_audio(file, path)
        @state = 1
      end
      # Change State in the current list and update data window
      @list_window[@page].item(@idx)[1] = @state
      @data_window.refresh(@page, @name, @state)
      return true
    end
  end
  #============================================================================
  # ** Window_ACList
  #----------------------------------------------------------------------------
  #  It is the window which lists all of the audio files within your project's
  #  Audio folder or Data cached folder.
  #============================================================================
  
  class Window_ACList < Window_Selectable
    #------------------------------------------------------------------------
    # * Object Initialization
    #------------------------------------------------------------------------
    def initialize(list)
      super(64, 64, 256, 352)
      @list = list
      @item_max = @list.size
      @index = 0
      temp_size = @list.size
      temp_size = 1 if temp_size == 0
      self.contents = Bitmap.new(width - 32, temp_size * 32)
      self.opacity = 192
      for i in 0...@list.size
        text = @list[i]
        text = "" if text.nil?
        self.contents.draw_text(0, i * 32, 256, 32, @list[i][0])
      end
    end
    #------------------------------------------------------------------------
    # * Get Item
    #------------------------------------------------------------------------
    def item(index)
      value = @list[index]
      value = ["",0] if value.nil?
      return value
    end
  end
  #============================================================================
  # ** Window_ACData
  #----------------------------------------------------------------------------
  #  It is the window which depicts the cached state of a highlighted audio
  #  file, and the folder in which it belongs
  #============================================================================
  
  class Window_ACData < Window_Selectable
    #------------------------------------------------------------------------
    # * Object Initialization
    #------------------------------------------------------------------------
    def initialize
      super(320, 64, 256, 128)
      self.contents = Bitmap.new(width - 32, height - 32)
      self.opacity = 192
    end
    #------------------------------------------------------------------------
    # * Refresh
    #------------------------------------------------------------------------
    def refresh(page, audio, state)
      if audio == ""
        audio = "> No file present <"
        state = 2
      else
        audio = audio[0,(audio.size)-4]
      end
      # Initialization of contents
      self.contents.fill_rect(0, 0, 224, 168, Color.new(0, 0, 0, 0))
      # Drawing of Page
      self.contents.font.color = Color.new(255, 255, 255)
      self.contents.font.color = Color.new(192, 192, 192) if state == 2
      case page
      when 1; self.contents.draw_text(0, 0, 224, 24, "[BGM]")
      when 2; self.contents.draw_text(0, 0, 224, 24, "[BGS]")
      when 3; self.contents.draw_text(0, 0, 224, 24, "[ME]")
      when 4; self.contents.draw_text(0, 0, 224, 24, "[SE]")
      end
      # Drawing of audio name
      self.contents.font.color = Color.new(192, 255, 255)
      self.contents.font.color = Color.new(192, 192, 192) if state == 2
      self.contents.draw_text(0, 32, 224, 24, audio)
      # Exit if no valid filename
      return if state == 2
      # Drawing of audio cached state
      if state == 0
        self.contents.font.color = Color.new(255, 255, 255)
        self.contents.draw_text(0, 64, 224, 24, "Uncached")
      else
        self.contents.font.color = Color.new(255, 255, 64)
        self.contents.draw_text(0, 64, 224, 24, "Cached")
      end
    end
  end
  #============================================================================
  # ** Window_ACHelp
  #----------------------------------------------------------------------------
  #  It is the window which depicts basic instructions for the editor.
  #============================================================================
  
  class Window_ACHelp < Window_Base
    #------------------------------------------------------------------------
    # * Object Initialization
    #------------------------------------------------------------------------
    def initialize
      super(320, 192, 256, 224)
      self.contents = Bitmap.new(width - 32, height - 32)
      self.opacity = 192
      refresh
    end
    #------------------------------------------------------------------------
    # * Refresh
    #------------------------------------------------------------------------
    def refresh
      self.contents.font.size = 20
      self.contents.font.bold = true
      self.contents.font.color = Color.new(255, 255, 255)
      self.contents.draw_text(0, 0, 224, 24, "The [Up] & [Down] arrows")
      self.contents.draw_text(0, 22, 224, 24, "highlights the audio file.")
      self.contents.font.color = Color.new(224, 224, 224)
      self.contents.draw_text(0, 55, 224, 24, "The [Left] & [Right] arrows")
      self.contents.draw_text(0, 77, 224, 24, "changes what type of audio")
      self.contents.draw_text(0, 99, 224, 24, "you are viewing.")
      self.contents.font.color = Color.new(192,192,192)
      self.contents.draw_text(0, 132, 224, 24, "Pressing [Enter] will change")
      self.contents.draw_text(0, 154, 224, 24, "the cached state of the file.")
    end
  end
  #============================================================================
  # ** Scene_Title
  #----------------------------------------------------------------------------
  #  This class performs title screen processing.
  #============================================================================
  
  class Scene_Title
    #-------------------------------------------------------------------------
    # * Main Processing
    #------------------------------------------------------------------------
    def main
      # Bring up required game system data
      $data_system    = load_data("Data/System.rxdata")
      $game_system    = Game_System.new
      # Drawing up title graphics
      @sprite         = Sprite.new
      @sprite.bitmap  = RPG::Cache.title($data_system.title_name)
      @gac            = Game_AudioCacheEditor.new
      # Execute transition
      Graphics.transition
      # Main loop
      loop do
        # Update game screen
        Graphics.update
        # Update input information
        Input.update
        # Frame update
        update
        # Abort loop if screen is changed
        break if $scene != self
      end
    end
    #------------------------------------------------------------------------
    # * Frame Update
    #------------------------------------------------------------------------
    def update
      @gac.update
    end
  end
endDemos
Demo 1 - Version able to be examined
Demo 2 - Version Encrypted into .rgsaad
Both are Box.com links. Look to top of window for DOWNLOAD button in page
Compatibility
This script will obviously conflict with any other script that will modify the bgm_play, bgs_play, me_play and/or se_play methods within Game System, and will conflict with any script that alters the animation_process_timing method within the RPG::Sprite class that handles battle system effects.
Other than that, it is compatible for RPGMaker XP systems.
Terms of Use
Free to use, even in commercial projects. Just note that I need some form of due credit, not just myself but Carlos_Davilla who assisted.
![[Image: Exclusive1.png]](https://www.save-point.org/images/exclusive/Exclusive1.png)
Up is down, left is right and sideways is straight ahead. - Cord "Circle of Iron", 1978 (written by Bruce Lee and James Coburn... really...)
	  Above are clickable links

![[Image: QrnbKlx.jpg]](https://i.imgur.com/QrnbKlx.jpg)
![[Image: sGz1ErF.png]](https://i.imgur.com/sGz1ErF.png)
![[Image: liM4ikn.png]](https://i.imgur.com/liM4ikn.png)
![[Image: fdzKgZA.png]](https://i.imgur.com/fdzKgZA.png)
![[Image: sj0H81z.png]](https://i.imgur.com/sj0H81z.png)
![[Image: QL7oRau.png]](https://i.imgur.com/QL7oRau.png)
![[Image: uSqjY09.png]](https://i.imgur.com/uSqjY09.png)
![[Image: GAA3qE9.png]](https://i.imgur.com/GAA3qE9.png)
![[Image: 2Hmnx1G.png]](https://i.imgur.com/2Hmnx1G.png)
![[Image: BwtNdKw.png%5B]](https://i.imgur.com/BwtNdKw.png%5B)