mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
Add support for writing to file. Basics work now (having memstick on a content path)
This commit is contained in:
parent
dba0a6ba12
commit
2f31cb12fb
@ -101,15 +101,42 @@ FILE *OpenCFile(const Path &path, const char *mode) {
|
||||
case PathType::NATIVE:
|
||||
break;
|
||||
case PathType::CONTENT_URI:
|
||||
// We're gonna need some error codes..
|
||||
if (!strcmp(mode, "r") || !strcmp(mode, "rb")) {
|
||||
INFO_LOG(COMMON, "Opening content file for read: '%s'", path.c_str());
|
||||
// Read, let's support this - easy one.
|
||||
int descriptor = Android_OpenContentUriFd(path.ToString(), Android_OpenContentUriMode::READ);
|
||||
if (descriptor == -1) {
|
||||
// Set last error message?
|
||||
// We're gonna need some error codes..
|
||||
return nullptr;
|
||||
}
|
||||
return fdopen(descriptor, "rb");
|
||||
} else if (!strcmp(mode, "w") || !strcmp(mode, "wb")) {
|
||||
// Need to be able to create the file here if it doesn't exist.
|
||||
// Not exactly sure which abstractions are best, let's start simple.
|
||||
if (!File::Exists(path)) {
|
||||
INFO_LOG(COMMON, "Opening content file '%s' for write. Doesn't exist, creating empty and reopening.", path.c_str());
|
||||
std::string name = path.GetFilename();
|
||||
if (path.CanNavigateUp()) {
|
||||
Path parent = path.NavigateUp();
|
||||
if (!Android_CreateFile(parent.ToString(), name)) {
|
||||
WARN_LOG(COMMON, "Failed to create file '%s' in '%s'", name.c_str(), parent.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
INFO_LOG(COMMON, "Failed to navigate up to create file");
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
INFO_LOG(COMMON, "Opening file by fd for write");
|
||||
}
|
||||
|
||||
// Read, let's support this - easy one.
|
||||
int descriptor = Android_OpenContentUriFd(path.ToString(), Android_OpenContentUriMode::READ_WRITE_TRUNCATE);
|
||||
if (descriptor == -1) {
|
||||
INFO_LOG(COMMON, "Opening '%s' for write failed", path.ToString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
return fdopen(descriptor, "wb");
|
||||
} else {
|
||||
ERROR_LOG(COMMON, "OpenCFile(%s): Mode not yet supported: %s", path.c_str(), mode);
|
||||
return nullptr;
|
||||
@ -365,6 +392,7 @@ bool CreateDir(const Path &path) {
|
||||
AndroidContentURI uri(path.ToString());
|
||||
std::string newDirName = uri.GetLastPart();
|
||||
if (uri.NavigateUp()) {
|
||||
INFO_LOG(COMMON, "Calling Android_CreateDirectory(%s, %s)", uri.ToString().c_str(), newDirName.c_str());
|
||||
return Android_CreateDirectory(uri.ToString(), newDirName);
|
||||
} else {
|
||||
// Bad path - can't create this directory.
|
||||
@ -437,14 +465,18 @@ bool CreateFullPath(const Path &path) {
|
||||
|
||||
Path curPath = root;
|
||||
|
||||
INFO_LOG(COMMON, "About to create folder tree '%s', rooted at '%s'", path.c_str(), root.c_str());
|
||||
|
||||
for (auto &part : parts) {
|
||||
curPath /= part;
|
||||
INFO_LOG(COMMON, "Creating %s", curPath.c_str());
|
||||
|
||||
if (!File::Exists(curPath)) {
|
||||
INFO_LOG(COMMON, "Creating folder '%s', doesn't already exist", curPath.c_str());
|
||||
File::CreateDir(curPath);
|
||||
}
|
||||
}
|
||||
INFO_LOG(COMMON, "Done");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deletes a directory filename, returns true on success
|
||||
|
@ -268,8 +268,8 @@ Path Path::GetRootVolume() const {
|
||||
|
||||
if (type_ == PathType::CONTENT_URI) {
|
||||
AndroidContentURI uri(path_);
|
||||
std::string rootPath = uri.RootPath();
|
||||
return Path(rootPath);
|
||||
AndroidContentURI rootPath = uri.WithRootFilePath("");
|
||||
return Path(rootPath.ToString());
|
||||
}
|
||||
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
|
@ -552,12 +552,12 @@ void Download::Do() {
|
||||
}
|
||||
|
||||
if (resultCode == 200) {
|
||||
INFO_LOG(IO, "Completed downloading %s to %s", url_.c_str(), outfile_.empty() ? "memory" : outfile_.ToVisualString().c_str());
|
||||
INFO_LOG(IO, "Completed downloading %s to %s", url_.c_str(), outfile_.empty() ? "memory" : outfile_.c_str());
|
||||
if (!outfile_.empty() && !buffer_.FlushToFile(outfile_)) {
|
||||
ERROR_LOG(IO, "Failed writing download to %s", outfile_.ToVisualString().c_str());
|
||||
ERROR_LOG(IO, "Failed writing download to '%s'", outfile_.c_str());
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(IO, "Error downloading %s to %s: %i", url_.c_str(), outfile_.ToVisualString().c_str(), resultCode);
|
||||
ERROR_LOG(IO, "Error downloading '%s' to '%s': %i", url_.c_str(), outfile_.c_str(), resultCode);
|
||||
}
|
||||
resultCode_ = resultCode;
|
||||
}
|
||||
|
@ -131,8 +131,10 @@ bool GameManager::Uninstall(std::string name) {
|
||||
void GameManager::Update() {
|
||||
if (curDownload_.get() && curDownload_->Done()) {
|
||||
INFO_LOG(HLE, "Download completed! Status = %d", curDownload_->ResultCode());
|
||||
Path fileName = Path(curDownload_->outfile());
|
||||
Path fileName = curDownload_->outfile();
|
||||
if (curDownload_->ResultCode() == 200) {
|
||||
// TODO: This fails. Wonder if there's a race condition?
|
||||
|
||||
if (!File::Exists(fileName)) {
|
||||
ERROR_LOG(HLE, "Downloaded file '%s' does not exist :(", fileName.c_str());
|
||||
curDownload_.reset();
|
||||
|
@ -63,7 +63,10 @@ public:
|
||||
|
||||
AndroidContentURI WithRootFilePath(const std::string &filePath) {
|
||||
AndroidContentURI uri = *this;
|
||||
uri.file = uri.root + "/" + filePath;
|
||||
uri.file = uri.root;
|
||||
if (!filePath.empty()) {
|
||||
uri.file += "/" + filePath;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@ struct JNIEnv {};
|
||||
#include "Common/System/System.h"
|
||||
#include "Common/Thread/ThreadUtil.h"
|
||||
#include "Common/File/Path.h"
|
||||
#include "Common/File/DirListing.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/File/VFS/AssetReader.h"
|
||||
#include "Common/Input/InputState.h"
|
||||
@ -269,7 +270,7 @@ int Android_OpenContentUriFd(const std::string &filename, Android_OpenContentUri
|
||||
|
||||
bool Android_CreateDirectory(const std::string &rootTreeUri, const std::string &dirName) {
|
||||
if (!nativeActivity) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
auto env = getEnv();
|
||||
jstring paramRoot = env->NewStringUTF(rootTreeUri.c_str());
|
||||
@ -279,7 +280,7 @@ bool Android_CreateDirectory(const std::string &rootTreeUri, const std::string &
|
||||
|
||||
bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) {
|
||||
if (!nativeActivity) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
auto env = getEnv();
|
||||
jstring paramRoot = env->NewStringUTF(parentTreeUri.c_str());
|
||||
@ -289,20 +290,46 @@ bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fil
|
||||
|
||||
bool Android_RemoveFile(const std::string &fileUri) {
|
||||
if (!nativeActivity) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
auto env = getEnv();
|
||||
jstring paramFileName = env->NewStringUTF(fileUri.c_str());
|
||||
return env->CallBooleanMethod(nativeActivity, contentUriRemoveFile, paramFileName);
|
||||
}
|
||||
|
||||
bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info) {
|
||||
static bool ParseFileInfo(const std::string &line, File::FileInfo *fileInfo) {
|
||||
INFO_LOG(FILESYS, "!! %s", line.c_str());
|
||||
std::vector<std::string> parts;
|
||||
SplitString(line, '|', parts);
|
||||
if (parts.size() != 5) {
|
||||
ERROR_LOG(FILESYS, "Bad format: %s", line.c_str());
|
||||
return false;
|
||||
}
|
||||
fileInfo->name = std::string(parts[2]);
|
||||
fileInfo->isDirectory = parts[0][0] == 'D';
|
||||
fileInfo->exists = true;
|
||||
sscanf(parts[1].c_str(), "%ld", &fileInfo->size);
|
||||
fileInfo->fullName = Path(parts[3]);
|
||||
fileInfo->isWritable = false; // TODO: We don't yet request write access
|
||||
sscanf(parts[4].c_str(), "%ld", &fileInfo->lastModified);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *fileInfo) {
|
||||
if (!nativeActivity) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
auto env = getEnv();
|
||||
jstring paramFileUri = env->NewStringUTF(fileUri.c_str());
|
||||
return env->CallObjectMethod(nativeActivity, contentUriGetFileInfo, paramFileUri);
|
||||
|
||||
jstring str = (jstring)env->CallObjectMethod(nativeActivity, contentUriGetFileInfo, paramFileUri);
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
const char *charArray = env->GetStringUTFChars(str, 0);
|
||||
bool retval = ParseFileInfo(std::string(charArray), fileInfo);
|
||||
env->DeleteLocalRef(str);
|
||||
return retval && fileInfo->exists;
|
||||
}
|
||||
|
||||
std::vector<File::FileInfo> Android_ListContentUri(const std::string &path) {
|
||||
@ -321,22 +348,11 @@ std::vector<File::FileInfo> Android_ListContentUri(const std::string &path) {
|
||||
const char *charArray = env->GetStringUTFChars(str, 0);
|
||||
if (charArray) { // paranoia
|
||||
std::string file = charArray;
|
||||
INFO_LOG(FILESYS, "!! %s", file.c_str());
|
||||
std::vector<std::string> parts;
|
||||
SplitString(file, '|', parts);
|
||||
if (parts.size() != 5) {
|
||||
continue;
|
||||
}
|
||||
File::FileInfo info;
|
||||
info.name = parts[2];
|
||||
info.isDirectory = parts[0][0] == 'D';
|
||||
info.exists = true;
|
||||
sscanf(parts[1].c_str(), "%ld", &info.size);
|
||||
info.fullName = Path(parts[3]);
|
||||
info.isWritable = false; // We don't yet request write access
|
||||
sscanf(parts[4].c_str(), "%ld", &info.lastModified);
|
||||
if (ParseFileInfo(file, &info)) {
|
||||
items.push_back(info);
|
||||
}
|
||||
}
|
||||
env->ReleaseStringUTFChars(str, charArray);
|
||||
env->DeleteLocalRef(str);
|
||||
}
|
||||
@ -345,6 +361,9 @@ std::vector<File::FileInfo> Android_ListContentUri(const std::string &path) {
|
||||
}
|
||||
|
||||
int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) {
|
||||
if (!nativeActivity) {
|
||||
return false;
|
||||
}
|
||||
auto env = getEnv();
|
||||
|
||||
jstring param = env->NewStringUTF(uri.c_str());
|
||||
@ -352,6 +371,9 @@ int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) {
|
||||
}
|
||||
|
||||
int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) {
|
||||
if (!nativeActivity) {
|
||||
return false;
|
||||
}
|
||||
auto env = getEnv();
|
||||
|
||||
jstring param = env->NewStringUTF(filePath.c_str());
|
||||
|
@ -1317,7 +1317,7 @@ public abstract class NativeActivity extends Activity {
|
||||
} else if (command.equals("browse_folder")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); // Only allow local folders.
|
||||
|
@ -172,6 +172,7 @@ public class PpssppActivity extends NativeActivity {
|
||||
DocumentFile createdDir = documentFile.createDirectory(dirName);
|
||||
return createdDir != null;
|
||||
} else {
|
||||
Log.e(TAG, "contentUriCreateDirectory: fromTreeUri returned null");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -185,9 +186,10 @@ public class PpssppActivity extends NativeActivity {
|
||||
Uri uri = Uri.parse(rootTreeUri);
|
||||
DocumentFile documentFile = DocumentFile.fromTreeUri(this, uri);
|
||||
if (documentFile != null) {
|
||||
DocumentFile createdFile = documentFile.createFile("application/arbitrary", fileName);
|
||||
DocumentFile createdFile = documentFile.createFile("application/octet-stream", fileName);
|
||||
return createdFile != null;
|
||||
} else {
|
||||
Log.e(TAG, "contentUriCreateFile: fromTreeUri returned null");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -216,11 +218,15 @@ public class PpssppActivity extends NativeActivity {
|
||||
Uri uri = Uri.parse(fileName);
|
||||
DocumentFile documentFile = DocumentFile.fromSingleUri(this, uri);
|
||||
if (documentFile != null) {
|
||||
if (documentFile.exists()) {
|
||||
String str = fileInfoToString(documentFile);
|
||||
return str;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "contentUriGetFileInfo exception: " + e.toString());
|
||||
return null;
|
||||
|
Loading…
Reference in New Issue
Block a user