Discussion in 'RGSS Scripts (RMXP)' started by Zeriab, May 17, 2016.

  1. Zeriab

    Zeriab Huggins! Veteran

    Likes Received:
    First Language:
    Primarily Uses:

    Retrieve file path from environment variables
    by Zeriab

    This here is a utility script aimed a developers who want to place the save files in the %APPDATA%\\GameName path. (I.e. the roaming folder, conforming to Microsoft best practices)


    A typical code structure follows this

    path = ENV['APPDATA']
    # possible some code trying to massaging path
    path += "\\Game?Name\\" # Subfolder with your game name.
    filename = path + "Save#{file_index + 1}.rxdata"

    This approach does not work for all UTF-8 code points. I am sure many of you have seen errors caused by players with Windows usernames containing non-latin letters.
    My script attempts to solve this by trying to retrieve the short name form of the path, also called the 8.3 filename format. A special interesting property of short name paths when using the Unicode Win32API call is that the second byte in their UTF-16 encoding will always be null. My approach avoids working with UTF-8, which simply does not play nice with Ruby 1.8

    Copy my script to a new section somewhere above where the ENV['APPDATA'] is called and wherever that call is we now use the following method call:

    path = EnvUtil.get_path_from_env("APPDATA") # Only change
    if path.nil? # Fallback, if you don't throw errors. Might work for more cases
    path = ENV['APPDATA']
    # possible some code trying to massaging path
    path += "\\Game?Name\\" # Subfolder with your game name. Yes, the ? is there to help you remember to change this
    filename = path + "Save#{file_index + 1}.rxdata"


    # Copyright (c) 2016 Zeriab
    # This software is provided 'as-is', without any express or implied
    # warranty. In no event will the authors be held liable for any damages
    # arising from the use of this software.
    # Permission is granted to anyone to use this software for any purpose,
    # including commercial applications, and to alter it and redistribute it
    # freely, subject to the following restrictions:
    # 1. The origin of this software must not be misrepresented; you must not
    # claim that you wrote the original software. If you use this software
    # in a product, an acknowledgement in the product documentation would be
    # appreciated but is not required.
    # 2. Altered source versions must be plainly marked as such, and must not be
    # misrepresented as being the original software.
    # 3. This notice may not be removed or altered from any source distribution.
    # usage:
    # path = EnvUtil.get_path_from_env("APPDATA")
    module EnvUtil
    THROW_ERRORS = true

    # Win32API bindings
    GetEnvironmentVariable = Win32API.new('Kernel32', 'GetEnvironmentVariableW', 'ppi', 'i')
    GetShortPathName = Win32API.new('Kernel32', 'GetShortPathNameW', 'ppi', 'i')
    GetLastError = Win32API.new('Kernel32', 'GetLastError', '', 'i')

    # Windows error codes

    # The system cannot find the path specified.
    # The system could not find the environment option that was entered.

    def get_shortpath_from_env(name)
    # Change to wide-chars with null-terminator
    array = name.split('')
    env_name = array.join("\0") + "\0\0\0"

    # Fetch path in wide-chars
    env_buffer = "\0" * 1024
    env_buffer_length = env_buffer.length - 1
    env_length = GetEnvironmentVariable.call(env_name,

    # If the function fails for any other reason, the return value is zero
    if env_length <= 0
    return nil unless THROW_ERRORS
    raise :env_error, GetLastError.call()

    # Try to retrieve the short path form of the specified path
    short_name_buffer = "\0" * 1024
    # Ensure the string will be null-terminated
    short_name_buffer_length = short_name_buffer.length - 1
    short_name_length = GetShortPathName.call(env_buffer,

    # If the function fails for any other reason, the return value is zero
    if short_name_length <= 0
    return nil unless THROW_ERRORS
    raise :shortname_error, GetLastError.call()

    # A short name confirming to the specification will only have left side of
    # the wide characters file. Following this we can convert the string into
    # UTF-8 by removing all null-terminators
    return short_name_buffer.gsub("\0", '')

    def get_path_from_env(env_name)
    # Sanity check
    path = ENV[env_name]
    return nil if path.nil?

    # Some paths work without being converted to short path form
    return path if FileTest.exists?(path)

    # Try to fetch the short path from as a fallback
    return get_shortpath_from_env(env_name)

    Pastebin: http://pastebin.com/6UzVPJVa

    The script have been verified to solve the issue in one instance: http://steamcommunity.com/app/327410/discussions/0/357286532027527002/
    I am interested in hearing about other cases, as there are many different path structures possible.

     - Zeriab
    Last edited by a moderator: May 17, 2016

Share This Page