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)
Instructions
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
end
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"
Script
##
# 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.
ERROR_PATH_NOT_FOUND = 3
# The system could not find the environment option that was entered.
ERROR_ENVVAR_NOT_FOUND = 203
module_function
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,
env_buffer,
env_buffer_length)
# 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()
end
# 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,
short_name_buffer,
short_name_buffer_length)
# 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()
end
# 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", '')
end
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)
end
end
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.
*hugs*
- Zeriab
本贴来自国际rpgmaker官方论坛作者:Zeriab处,因国际论坛即将永久关站,为了存档多年珍贵资料,署名转载到本论坛存档,由于官方帖子为英文原帖,需要中文翻译请点击论坛顶部切换语言为中文就可以将帖子翻译成中文浏览,方便大家随时查看,原文地址:
https://forums.rpgmakerweb.com/threads/enviroment.61961/