TIC-80-guile/src/ext/file_dialog.c

491 lines
11 KiB
C

// MIT License
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "file_dialog.h"
#include <SDL.h>
#if defined(__WINDOWS__)
#include <windows.h>
#include <commdlg.h>
#include <stdio.h>
FILE* _wfopen(const wchar_t *, const wchar_t *);
wchar_t* wcsrchr(const wchar_t *, wchar_t);
wchar_t* wcscpy(wchar_t *, const wchar_t *);
#define UTF8ToString(S) (wchar_t *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
#define StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(wchar_t))
void file_dialog_load(file_dialog_load_callback callback, void* data)
{
OPENFILENAMEW ofn;
SDL_zero(ofn);
wchar_t filename[MAX_PATH];
memset(filename, 0, sizeof(filename));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFile = filename;
ofn.nMaxFile = sizeof(filename);
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
if(GetOpenFileNameW(&ofn))
{
FILE* source = _wfopen(filename, L"rb");
if (source)
{
fseek(source, 0, SEEK_END);
s32 size = ftell(source);
fseek(source, 0, SEEK_SET);
u8* buffer = malloc(size);
if(buffer)
fread(buffer, size, 1, source);
fclose(source);
if(buffer)
{
const wchar_t* basename = wcsrchr(filename, L'\\');
const wchar_t* name = basename ? basename + 1 : filename;
callback(StringToUTF8(name), buffer, size, data, 0);
free(buffer);
return;
}
}
}
callback(NULL, NULL, 0, data, 0);
}
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
{
OPENFILENAMEW ofn;
SDL_zero(ofn);
wchar_t filename[MAX_PATH];
memset(filename, 0, sizeof(filename));
wcscpy(filename, UTF8ToString(name));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFile = filename;
ofn.nMaxFile = sizeof(filename);
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
if(GetSaveFileNameW(&ofn))
{
FILE* file = _wfopen(filename, L"wb");
if(file)
{
fwrite(buffer, 1, size, file);
fclose(file);
#if !defined(__WINDOWS__)
chmod(filename, mode);
#endif
callback(true, data);
return;
}
}
callback(false, data);
}
#include <shlobj.h>
const char* folder_dialog(void* data)
{
BROWSEINFOW bi = { 0 };
bi.lpszTitle = L"Browse for folder...";
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lParam = (LPARAM) NULL;
LPITEMIDLIST pidl = SHBrowseForFolderW ( &bi );
if ( pidl != 0 )
{
wchar_t path[MAX_PATH];
SHGetPathFromIDListW (pidl, path);
LPMALLOC imalloc = NULL;
if ( SUCCEEDED( SHGetMalloc ( &imalloc )) )
{
imalloc->lpVtbl->Free (imalloc, pidl );
imalloc->lpVtbl->Release (imalloc);
}
{
static char result[MAX_PATH];
strcpy(result, StringToUTF8(path));
return result;
}
}
return NULL;
}
#elif defined(__EMSCRIPTEN__)
#include <emscripten.h>
void file_dialog_load(file_dialog_load_callback callback, void* data)
{
EM_ASM_
({
Module.showAddPopup(function(filename, rom)
{
if(filename == null || rom == null)
{
Runtime.dynCall('viiiii', $0, [0, 0, 0, $1, 0]);
}
else
{
var filePtr = Module._malloc(filename.length + 1);
stringToUTF8(filename, filePtr, filename.length + 1);
var dataPtr = Module._malloc(rom.length);
writeArrayToMemory(rom, dataPtr);
Runtime.dynCall('viiiii', $0, [filePtr, dataPtr, rom.length, $1, 0]);
Module._free(filePtr);
Module._free(dataPtr);
}
});
}, callback, data);
}
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
{
EM_ASM_
({
var name = Pointer_stringify($0);
var blob = new Blob([HEAPU8.subarray($1, $1 + $2)], {type: "application/octet-stream"});
Module.saveAs(blob, name);
}, name, buffer, size);
callback(true, data);
}
#elif defined(__LINUX__)
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <sys/stat.h>
static void WaitForCleanup(void)
{
while (gtk_events_pending())
gtk_main_iteration();
}
void file_dialog_load(file_dialog_load_callback callback, void* data)
{
bool done = false;
if(gtk_init_check(NULL,NULL))
{
GtkWidget *dialog = gtk_file_chooser_dialog_new("Open File", NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT,
NULL);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT )
{
char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
FILE* source = fopen(filename, "rb");
if (source)
{
fseek(source, 0, SEEK_END);
s32 size = ftell(source);
fseek(source, 0, SEEK_SET);
u8* buffer = malloc(size);
if(buffer && fread(buffer, size, 1, source));
fclose(source);
if(buffer)
{
const char* basename = strrchr(filename, '/');
mode_t mode = 0;
{
struct stat s;
if(stat(filename, &s) == 0)
mode = s.st_mode;
}
callback(basename ? basename + 1 : filename, buffer, size, data, mode);
free(buffer);
done = true;
}
}
}
WaitForCleanup();
gtk_widget_destroy(dialog);
WaitForCleanup();
}
if(!done)
callback(NULL, NULL, 0, data, 0);
}
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
{
bool done = false;
if(gtk_init_check(NULL,NULL))
{
GtkWidget *dialog = gtk_file_chooser_dialog_new("Save File", NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Save", GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), name);
if(gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT)
{
char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
FILE* file = fopen(filename, "wb");
if(file)
{
fwrite(buffer, 1, size, file);
fclose(file);
chmod(filename, mode);
callback(true, data);
done = true;
}
}
WaitForCleanup();
gtk_widget_destroy(dialog);
WaitForCleanup();
}
if(!done)
callback(false, data);
}
#elif defined(__MACOSX__)
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
bool file_dialog_load_path(char* buffer);
bool file_dialog_save_path(const char* name, char* buffer);
void file_dialog_load(file_dialog_load_callback callback, void* data)
{
bool done = false;
char filename[FILENAME_MAX];
if(file_dialog_load_path(filename))
{
FILE* source = fopen(filename, "rb");
if (source)
{
fseek(source, 0, SEEK_END);
s32 size = ftell(source);
fseek(source, 0, SEEK_SET);
u8* buffer = malloc(size);
if(buffer && fread(buffer, size, 1, source)){}
fclose(source);
if(buffer)
{
const char* basename = strrchr(filename, '/');
mode_t mode = 0;
{
struct stat s;
if(stat(filename, &s) == 0)
mode = s.st_mode;
}
callback(basename ? basename + 1 : filename, buffer, size, data, mode);
free(buffer);
done = true;
}
}
}
if(!done)
callback(NULL, NULL, 0, data, 0);
}
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
{
bool done = false;
char filename[FILENAME_MAX];
if(file_dialog_save_path(name, filename))
{
FILE* file = fopen(filename, "wb");
if(file)
{
fwrite(buffer, 1, size, file);
fclose(file);
chmod(filename, mode);
callback(true, data);
done = true;
}
}
if(!done)
callback(false, data);
}
#elif defined(__ANDROID__)
#include <jni.h>
#include <sys/stat.h>
void file_dialog_load(file_dialog_load_callback callback, void* data)
{
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
jobject activity = (jobject)SDL_AndroidGetActivity();
jclass clazz = (*env)->GetObjectClass(env, activity);
jmethodID method_id = (*env)->GetMethodID(env, clazz, "loadFile", "()Ljava/lang/String;");
jstring jPath = (jstring) (*env)->CallObjectMethod(env, activity, method_id);
const char *filename = (*env)->GetStringUTFChars(env, jPath, NULL);
(*env)->DeleteLocalRef(env, activity);
(*env)->DeleteLocalRef(env, clazz);
bool done = false;
if(filename && strlen(filename))
{
FILE* source = fopen(filename, "rb");
if (source)
{
fseek(source, 0, SEEK_END);
s32 size = ftell(source);
fseek(source, 0, SEEK_SET);
u8* buffer = malloc(size);
if(buffer && fread(buffer, size, 1, source)){}
fclose(source);
if(buffer)
{
const char* basename = strrchr(filename, '/');
mode_t mode = 0;
{
struct stat s;
if(stat(filename, &s) == 0)
mode = s.st_mode;
}
callback(basename ? basename + 1 : filename, buffer, size, data, mode);
free(buffer);
done = true;
}
}
}
if(!done)
callback(NULL, NULL, 0, data, 0);
(*env)->ReleaseStringUTFChars(env, jPath, filename);
}
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
{
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
jobject activity = (jobject)SDL_AndroidGetActivity();
jclass clazz = (*env)->GetObjectClass(env, activity);
jstring jName = (*env)->NewStringUTF(env, name);
jmethodID method_id = (*env)->GetMethodID(env, clazz, "saveFile", "(Ljava/lang/String;)Ljava/lang/String;");
jstring jPath = (jstring) (*env)->CallObjectMethod(env, activity, method_id, jName);
const char *filename = (*env)->GetStringUTFChars(env, jPath, NULL);
(*env)->DeleteLocalRef(env, jName);
(*env)->DeleteLocalRef(env, activity);
(*env)->DeleteLocalRef(env, clazz);
bool done = false;
if(filename && strlen(filename))
{
FILE* file = fopen(filename, "wb");
if(file)
{
fwrite(buffer, 1, size, file);
fclose(file);
chmod(filename, mode);
callback(true, data);
done = true;
}
}
if(!done)
callback(false, data);
(*env)->ReleaseStringUTFChars(env, jPath, filename);
}
#else
void file_dialog_load(file_dialog_load_callback callback, void* data) {}
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode) {}
#endif