blob: feffb5ddb2379c2bde8bfcbf62e5db212715108e [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/system.h"
#include <windows.h>
#include <codecvt>
#include <cstring>
#include <locale>
#include "starboard/common/log.h"
#include "starboard/common/string.h"
#include "starboard/configuration_constants.h"
#include "starboard/directory.h"
#include "starboard/shared/uwp/application_uwp.h"
#include "starboard/shared/win32/directory_internal.h"
#include "starboard/shared/win32/wchar_utils.h"
using starboard::shared::win32::platformStringToString;
using starboard::shared::win32::wchar_tToUTF8;
namespace {
std::string GetBinaryName() {
const size_t kMaxModuleNameSize = kSbFileMaxName;
std::vector<wchar_t> buffer(kMaxModuleNameSize);
DWORD result = GetModuleFileName(NULL, buffer.data(), buffer.size());
std::string full_binary_path;
if (result == 0) {
full_binary_path = "unknown";
} else {
full_binary_path = wchar_tToUTF8(buffer.data(), result).c_str();
}
std::string::size_type index = full_binary_path.rfind(kSbFileSepChar);
if (index == std::string::npos) {
return full_binary_path;
}
return full_binary_path.substr(index + 1);
}
// Places up to |path_size| - 1 characters of the path to the current
// executable in |out_path|, ensuring it is NULL-terminated. Returns success
// status. The result being greater than |path_size| - 1 characters is a
// failure. |out_path| may be written to in unsuccessful cases.
bool GetExecutablePath(char* out_path, int path_size) {
if (!out_path || (path_size <= 0)) {
return false;
}
auto folder =
Windows::ApplicationModel::Package::Current->InstalledLocation->Path;
if (folder->Length() >= static_cast<unsigned int>(path_size) ||
folder->Length() < 1) {
return false;
}
auto app_name = Windows::ApplicationModel::Package::Current->Id->Name;
if (app_name->Length() >= static_cast<unsigned int>(path_size) ||
app_name->Length() < 1) {
return false;
}
std::string utf8_string =
(platformStringToString(folder) + "\\" + GetBinaryName());
if (utf8_string.length() > static_cast<unsigned int>(path_size)) {
return false;
}
return starboard::strlcpy(out_path, utf8_string.c_str(), path_size);
}
// Places up to |path_size| - 1 characters of the path to the directory
// containing the current executable in |out_path|, ensuring it is
// NULL-terminated. Returns success status. The result being greater than
// |path_size| - 1 characters is a failure. |out_path| may be written to in
// unsuccessful cases.
bool GetExecutableDirectory(char* out_path, int path_size) {
if (!out_path || (path_size <= 0)) {
return false;
}
auto folder =
Windows::ApplicationModel::Package::Current->InstalledLocation->Path;
if (folder->Length() >= static_cast<unsigned int>(path_size) ||
folder->Length() < 1) {
return false;
}
std::string utf8_string =
starboard::shared::win32::wchar_tToUTF8(folder->Data(), folder->Length());
return starboard::strlcpy(out_path, utf8_string.c_str(), path_size);
}
// Places up to |path_size| - 1 characters of the path to the content directory
// in |out_path|, ensuring it is NULL-terminated. Returns success
// status. The result being greater than |path_size| - 1 characters is a
// failure. |out_path| may be written to in unsuccessful cases.
bool GetContentPath(char* out_path, int path_size) {
if (!out_path || (path_size <= 0)) {
return false;
}
std::vector<char> file_path(kSbFileMaxPath);
file_path[0] = '\0';
if (!GetExecutableDirectory(file_path.data(), path_size)) {
return false;
}
if (starboard::strlcat(file_path.data(), "\\content\\data", kSbFileMaxPath) >=
path_size) {
return false;
}
return starboard::strlcpy(out_path, file_path.data(), path_size);
}
// Places up to |path_size| - 1 characters of the path to the temporary
// directory in |out_path|, ensuring it is NULL-terminated, and creates the
// directory if necessary. Returns success status. The result being greater than
// |path_size| - 1 characters is a failure. |out_path| may be written to in
// unsuccessful cases.
bool CreateAndGetTempPath(char* out_path, int path_size) {
if (!out_path || (path_size <= 0)) {
return false;
}
std::vector<wchar_t> w_file_path(kSbFileMaxPath);
w_file_path[0] = L'\0';
DWORD characters_written = GetTempPathW(kSbFileMaxPath, w_file_path.data());
if (static_cast<int>(characters_written) >= (path_size + 1) ||
characters_written < 1) {
return false;
}
// Remove the last slash, to match other Starboard implementations.
w_file_path[characters_written - 1] = L'\0';
std::string utf8_string = wchar_tToUTF8(w_file_path.data());
if (starboard::strlcpy(out_path, utf8_string.c_str(), path_size) >=
path_size) {
return false;
}
mkdir(out_path, 0700);
size_t length = strlen(out_path);
if (length < 1 || length > path_size) {
return false;
}
return true;
}
// Places up to |path_size| - 1 characters of the path to the directory in
// |out_path|, ensuring it is NULL-terminated, and creates the directory if
// necessary. Returns success status. The result being greater than |path_size|
// - 1 characters is a failure. |out_path| may be written to in unsuccessful
// cases.
bool CreateAndGetApplicationDataFolder(Windows::Storage::StorageFolder ^ folder,
char* out_path,
int path_size) {
if (!out_path || (path_size <= 0)) {
return false;
}
// The caller expects that the output string will only be written if when
// true is returned. Therefore we have to buffer the string in case there is a
// false condition, such as small memory input size to hold the output
// parameter.
std::vector<char> out_path_copy(kSbFileMaxPath, 0);
std::string local_path = platformStringToString(folder->Path);
if (starboard::strlcpy(out_path_copy.data(), local_path.c_str(),
out_path_copy.size()) >= out_path_copy.size()) {
return false;
}
size_t length = strlen(out_path_copy.data());
if (length < 1 || length > out_path_copy.size()) {
return false;
}
// The copy is verified, transfer it over to out_path.
size_t len = std::min<size_t>(path_size, kSbFileMaxPath);
starboard::strlcpy(out_path, out_path_copy.data(), len);
return true;
}
} // namespace
// Note: This function is only minimally implemented to allow tests to run.
bool SbSystemGetPath(SbSystemPathId path_id, char* out_path, int path_size) {
if (!out_path || (path_size <= 0)) {
return false;
}
switch (path_id) {
case kSbSystemPathContentDirectory:
return GetContentPath(out_path, path_size);
case kSbSystemPathDebugOutputDirectory:
// We send debug output to the cache directory instead of the temp
// directory, because the temp directory is removed as soon as the
// application exits, while also a file can not be opened or copied while
// it is still open for writing. Using the cache directory makes it
// possible to retrieve the file contents after the application exits.
return SbSystemGetPath(kSbSystemPathCacheDirectory, out_path, path_size);
case kSbSystemPathExecutableFile:
return GetExecutablePath(out_path, path_size);
case kSbSystemPathStorageDirectory:
return CreateAndGetApplicationDataFolder(
Windows::Storage::ApplicationData::Current->LocalFolder, out_path,
path_size);
case kSbSystemPathTempDirectory:
// Note: This temp directory is removed as soon as the application exits.
return CreateAndGetTempPath(out_path, path_size);
case kSbSystemPathCacheDirectory:
return CreateAndGetApplicationDataFolder(
Windows::Storage::ApplicationData::Current->LocalCacheFolder,
out_path, path_size);
// TODO: implement all the other cases.
default:
SB_NOTIMPLEMENTED();
return false;
}
}