diff --git a/src/Formats.c b/src/Formats.c index cb20b6ec1..6a5449e55 100644 --- a/src/Formats.c +++ b/src/Formats.c @@ -64,7 +64,9 @@ void Map_LoadFrom(const cc_string* path) { if (res) { Logger_SysWarn2(res, "opening", path); return; } importer = Map_FindImporter(path); - if ((res = importer(&stream))) { + if (!importer) { + Logger_SysWarn2(ERR_NOT_SUPPORTED, "decoding", path); + } else if ((res = importer(&stream))) { World_Reset(); Logger_SysWarn2(res, "decoding", path); } diff --git a/src/Game.c b/src/Game.c index 6c3b0ff0b..63acda6d7 100644 --- a/src/Game.c +++ b/src/Game.c @@ -523,7 +523,7 @@ void Game_TakeScreenshot(void) { String_Format3(&filename, "-%p2-%p2-%p2.png", &now.hour, &now.minute, &now.second); #ifdef CC_BUILD_WEB - Platform_ConvertString(str, &filename); + Platform_EncodeString(str, &filename); EM_ASM_({ var name = UTF8ToString($0); var canvas = Module['canvas']; diff --git a/src/Http.c b/src/Http.c index 134ddc19e..8b5def8f8 100644 --- a/src/Http.c +++ b/src/Http.c @@ -380,7 +380,7 @@ static void Http_DownloadAsync(struct HttpRequest* req) { String_InitArray(url, urlBuffer); Http_BeginRequest(req, &url); - Platform_ConvertString(urlStr, &url); + Platform_EncodeString(urlStr, &url); /* TODO: SET requestHeaders!!! */ emscripten_fetch(&attr, urlStr); @@ -605,7 +605,7 @@ static cc_result Http_BackendDo(struct HttpRequest* req, cc_string* url) { _curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req->meta); Http_SetCurlOpts(req); - Platform_ConvertString(urlStr, url); + Platform_EncodeString(urlStr, url); _curl_easy_setopt(curl, CURLOPT_URL, urlStr); if (req->requestType == REQUEST_TYPE_HEAD) { diff --git a/src/Menus.c b/src/Menus.c index 978cf297a..f202de2d8 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -175,11 +175,11 @@ struct ListScreen; static struct ListScreen { Screen_Body struct ButtonWidget btns[LIST_SCREEN_ITEMS]; - struct ButtonWidget left, right, done; + struct ButtonWidget left, right, done, upload; struct FontDesc font; float wheelAcc; int currentIndex; - Widget_LeftClick EntryClick, DoneClick; + Widget_LeftClick EntryClick, DoneClick, UploadClick; void (*LoadEntries)(struct ListScreen* s); void (*UpdateEntry)(struct ListScreen* s, struct ButtonWidget* btn, const cc_string* text); const char* titleText; @@ -187,14 +187,14 @@ static struct ListScreen { struct StringsBuffer entries; } ListScreen; -static struct Widget* list_widgets[9] = { +static struct Widget* list_widgets[10] = { (struct Widget*)&ListScreen.btns[0], (struct Widget*)&ListScreen.btns[1], (struct Widget*)&ListScreen.btns[2], (struct Widget*)&ListScreen.btns[3], (struct Widget*)&ListScreen.btns[4], (struct Widget*)&ListScreen.left, (struct Widget*)&ListScreen.right, (struct Widget*)&ListScreen.title, - (struct Widget*)&ListScreen.done + (struct Widget*)&ListScreen.done, NULL }; -#define LIST_MAX_VERTICES (8 * BUTTONWIDGET_MAX + TEXTWIDGET_MAX) +#define LIST_MAX_VERTICES (9 * BUTTONWIDGET_MAX + TEXTWIDGET_MAX) #define LISTSCREEN_EMPTY "-----" static void ListScreen_Layout(void* screen) { @@ -204,7 +204,13 @@ static void ListScreen_Layout(void* screen) { Widget_SetLocation(&s->btns[i], ANCHOR_CENTRE, ANCHOR_CENTRE, 0, (i - 2) * 50); } - Menu_LayoutBack(&s->done); + + if (s->UploadClick) { + Widget_SetLocation(&s->done, ANCHOR_CENTRE_MIN, ANCHOR_MAX, -150, 25); + Widget_SetLocation(&s->upload, ANCHOR_CENTRE_MAX, ANCHOR_MAX, -150, 25); + } else { + Menu_LayoutBack(&s->done); + } Widget_SetLocation(&s->left, ANCHOR_CENTRE, ANCHOR_CENTRE, -220, 0); Widget_SetLocation(&s->right, ANCHOR_CENTRE, ANCHOR_CENTRE, 220, 0); @@ -355,11 +361,20 @@ static void ListScreen_Init(void* screen) { for (i = 0; i < LIST_SCREEN_ITEMS; i++) { ButtonWidget_Init(&s->btns[i], 300, s->EntryClick); } + if (Game_ClassicMode) s->UploadClick = NULL; + + if (s->UploadClick) { + ButtonWidget_Init(&s->done, 140, s->DoneClick); + ButtonWidget_Init(&s->upload, 140, s->UploadClick); + s->widgets[9] = (struct Widget*)&s->upload; + } else { + Menu_InitBack(&s->done, s->DoneClick); + s->widgets[9] = NULL; + } ButtonWidget_Init(&s->left, 40, ListScreen_MoveBackwards); ButtonWidget_Init(&s->right, 40, ListScreen_MoveForwards); TextWidget_Init(&s->title); - Menu_InitBack(&s->done, s->DoneClick); s->LoadEntries(s); } @@ -391,6 +406,9 @@ static void ListScreen_ContextRecreated(void* screen) { ButtonWidget_SetConst(&s->right, ">", &s->font); ButtonWidget_SetConst(&s->done, "Done", &s->font); ListScreen_UpdatePage(s); + + if (!s->UploadClick) return; + ButtonWidget_SetConst(&s->upload, "Upload", &s->font); } static const struct ScreenVTABLE ListScreen_VTABLE = { @@ -1261,13 +1279,13 @@ static void DownloadMap(const cc_string* path) { char strFile[NATIVE_STR_LEN]; cc_string file; cc_result res; - Platform_ConvertString(strPath, path); + Platform_EncodeString(strPath, path); /* maps/aaa.schematic -> aaa.cw */ file = *path; Utils_UNSAFE_GetFilename(&file); file.length = String_LastIndexOf(&file, '.'); String_AppendConst(&file, ".cw"); - Platform_ConvertString(strFile, &file); + Platform_EncodeString(strFile, &file); res = EM_ASM_({ try { @@ -1508,19 +1526,50 @@ static void TexturePackScreen_FilterFiles(const cc_string* path, void* obj) { cc_string relPath = *path; if (!String_CaselessEnds(path, &zip)) return; +#ifdef CC_BUILD_WEB + /* Web client texture pack dir starts with /, so need to get rid of that */ + if (relPath.buffer[0] == '/') { relPath.buffer++; relPath.length--; } +#endif + Utils_UNSAFE_TrimFirstDirectory(&relPath); StringsBuffer_Add((struct StringsBuffer*)obj, &relPath); } static void TexturePackScreen_LoadEntries(struct ListScreen* s) { - static const cc_string path = String_FromConst("texpacks"); + static const cc_string path = String_FromConst(TEXPACKS_DIR); Directory_Enum(&path, &s->entries, TexturePackScreen_FilterFiles); ListScreen_Sort(s); } +#ifdef CC_BUILD_WEB +#include +static void TexturePackScreen_UploadCallback(const cc_string* path) { + char str[NATIVE_STR_LEN]; + Platform_EncodeString(str, path); + + /* Move from temp into texpacks folder */ + /* TODO: This is pretty awful and should be rewritten */ + EM_ASM_({ + var name = UTF8ToString($0);; + var data = FS.readFile(name); + FS.writeFile('/texpacks/' + name.substring(1), data); + }, str); + TexturePackScreen_Show(); + TexturePack_SetDefault(path); + TexturePack_ExtractCurrent(true); +} + +static void TexturePackScreen_UploadFunc(void* s, void* w) { + Window_OpenFileDialog(".zip", TexturePackScreen_UploadCallback); +} +#else +#define TexturePackScreen_UploadFunc NULL +#endif + void TexturePackScreen_Show(void) { struct ListScreen* s = &ListScreen; s->titleText = "Select a texture pack"; + s->UploadClick = TexturePackScreen_UploadFunc; s->LoadEntries = TexturePackScreen_LoadEntries; s->EntryClick = TexturePackScreen_EntryClick; s->DoneClick = Menu_SwitchPause; @@ -1568,6 +1617,7 @@ static void FontListScreen_LoadEntries(struct ListScreen* s) { void FontListScreen_Show(void) { struct ListScreen* s = &ListScreen; s->titleText = "Select a font"; + s->UploadClick = NULL; s->LoadEntries = FontListScreen_LoadEntries; s->EntryClick = FontListScreen_EntryClick; s->DoneClick = Menu_SwitchGui; @@ -1640,6 +1690,7 @@ static void HotkeyListScreen_LoadEntries(struct ListScreen* s) { void HotkeyListScreen_Show(void) { struct ListScreen* s = &ListScreen; s->titleText = "Modify hotkeys"; + s->UploadClick = NULL; s->LoadEntries = HotkeyListScreen_LoadEntries; s->EntryClick = HotkeyListScreen_EntryClick; s->DoneClick = Menu_SwitchPause; @@ -1679,9 +1730,19 @@ static void LoadLevelScreen_LoadEntries(struct ListScreen* s) { ListScreen_Sort(s); } +#ifdef CC_BUILD_WEB +static void LoadLevelScreen_UploadCallback(const cc_string* path) { Map_LoadFrom(path); } +static void LoadLevelScreen_UploadFunc(void* s, void* w) { + Window_OpenFileDialog(".cw", LoadLevelScreen_UploadCallback); +} +#else +#define LoadLevelScreen_UploadFunc NULL +#endif + void LoadLevelScreen_Show(void) { struct ListScreen* s = &ListScreen; s->titleText = "Select a level"; + s->UploadClick = LoadLevelScreen_UploadFunc; s->LoadEntries = LoadLevelScreen_LoadEntries; s->EntryClick = LoadLevelScreen_EntryClick; s->DoneClick = Menu_SwitchPause; diff --git a/src/Platform.c b/src/Platform.c index 1d9393d78..a07f037dd 100644 --- a/src/Platform.c +++ b/src/Platform.c @@ -19,12 +19,6 @@ #define _UNICODE #endif -#ifdef UNICODE -#define Platform_DecodeString(dst, src, len) String_AppendUtf16(dst, (cc_unichar*)(src), (len) * 2) -#else -#define Platform_DecodeString(dst, src, len) String_DecodeCP1252(dst, (cc_uint8*)(src), len) -#endif - #include #include #include @@ -59,9 +53,7 @@ const cc_result ReturnCode_SocketWouldBlock = WSAEWOULDBLOCK; #include #include -#define Platform_DecodeString(dst, src, len) String_AppendUtf8(dst, (cc_uint8*)(src), len) #define Socket__Error() errno - static char* defaultDirectory; const cc_result ReturnCode_FileShareViolation = 1000000000; /* TODO: not used apparently */ const cc_result ReturnCode_FileNotFound = ENOENT; @@ -332,7 +324,7 @@ int Directory_Exists(const cc_string* path) { TCHAR str[NATIVE_STR_LEN]; DWORD attribs; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); attribs = GetFileAttributes(str); return attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY); } @@ -341,7 +333,7 @@ cc_result Directory_Create(const cc_string* path) { TCHAR str[NATIVE_STR_LEN]; BOOL success; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); success = CreateDirectory(str, NULL); return success ? 0 : GetLastError(); } @@ -350,7 +342,7 @@ int File_Exists(const cc_string* path) { TCHAR str[NATIVE_STR_LEN]; DWORD attribs; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); attribs = GetFileAttributes(str); return attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY); } @@ -367,7 +359,7 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall /* Need to append \* to search for files in directory */ String_InitArray(path, pathBuffer); String_Format1(&path, "%s\\*", dirPath); - Platform_ConvertString(str, &path); + Platform_EncodeString(str, &path); find = FindFirstFile(str, &entry); if (find == INVALID_HANDLE_VALUE) return GetLastError(); @@ -401,7 +393,7 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall static cc_result File_Do(cc_file* file, const cc_string* path, DWORD access, DWORD createMode) { TCHAR str[NATIVE_STR_LEN]; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); *file = CreateFile(str, access, FILE_SHARE_READ, NULL, createMode, 0, NULL); return *file != INVALID_HANDLE_VALUE ? 0 : GetLastError(); } @@ -449,13 +441,13 @@ cc_result File_Length(cc_file file, cc_uint32* len) { int Directory_Exists(const cc_string* path) { char str[NATIVE_STR_LEN]; struct stat sb; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); return stat(str, &sb) == 0 && S_ISDIR(sb.st_mode); } cc_result Directory_Create(const cc_string* path) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); /* read/write/search permissions for owner and group, and with read/search permissions for others. */ /* TODO: Is the default mode in all cases */ return mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1 ? errno : 0; @@ -464,7 +456,7 @@ cc_result Directory_Create(const cc_string* path) { int File_Exists(const cc_string* path) { char str[NATIVE_STR_LEN]; struct stat sb; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); return stat(str, &sb) == 0 && S_ISREG(sb.st_mode); } @@ -476,7 +468,7 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall char* src; int len, res; - Platform_ConvertString(str, dirPath); + Platform_EncodeString(str, dirPath); dirPtr = opendir(str); if (!dirPtr) return errno; @@ -514,7 +506,7 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall static cc_result File_Do(cc_file* file, const cc_string* path, int mode) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); *file = open(str, mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); return *file == -1 ? errno : 0; } @@ -1049,14 +1041,14 @@ cc_result Process_StartGame(const cc_string* args) { String_InitArray(argv, argvBuffer); String_Format1(&argv, "ClassiCube.exe %s", args); - Platform_ConvertString(raw, &argv); + Platform_EncodeString(raw, &argv); return Process_RawStart(path, raw); } void Process_Exit(cc_result code) { ExitProcess(code); } void Process_StartOpen(const cc_string* args) { TCHAR str[NATIVE_STR_LEN]; - Platform_ConvertString(str, args); + Platform_EncodeString(str, args); ShellExecute(NULL, NULL, str, NULL, NULL, SW_SHOWNORMAL); } #elif defined CC_BUILD_WEB @@ -1065,7 +1057,7 @@ void Process_Exit(cc_result code) { exit(code); } void Process_StartOpen(const cc_string* args) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, args); + Platform_EncodeString(str, args); EM_ASM_({ window.open(UTF8ToString($0)); }, str); } #elif defined CC_BUILD_ANDROID @@ -1108,7 +1100,7 @@ cc_result Process_StartGame(const cc_string* args) { if (res) return res; path[len] = '\0'; - Platform_ConvertString(raw, args); + Platform_EncodeString(raw, args); argv[0] = path; argv[1] = raw; /* need to null-terminate multiple arguments */ @@ -1134,7 +1126,7 @@ void Process_StartOpen(const cc_string* args) { CFURLRef urlCF; int len; - len = Platform_ConvertString(str, args); + len = Platform_EncodeString(str, args); urlCF = CFURLCreateWithBytes(kCFAllocatorDefault, str, len, kCFStringEncodingUTF8, NULL); LSOpenCFURLRef(urlCF, NULL); CFRelease(urlCF); @@ -1143,7 +1135,7 @@ void Process_StartOpen(const cc_string* args) { void Process_StartOpen(const cc_string* args) { char str[NATIVE_STR_LEN]; char* cmd[3]; - Platform_ConvertString(str, args); + Platform_EncodeString(str, args); cmd[0] = "open"; cmd[1] = str; cmd[2] = NULL; Process_RawStart("open", cmd); @@ -1152,7 +1144,7 @@ void Process_StartOpen(const cc_string* args) { void Process_StartOpen(const cc_string* args) { char str[NATIVE_STR_LEN]; char* cmd[3]; - Platform_ConvertString(str, args); + Platform_EncodeString(str, args); /* TODO: Can xdg-open be used on original Solaris, or is it just an OpenIndiana thing */ cmd[0] = "xdg-open"; cmd[1] = str; cmd[2] = NULL; @@ -1431,7 +1423,7 @@ const cc_string DynamicLib_Ext = String_FromConst(".dll"); void* DynamicLib_Load2(const cc_string* path) { TCHAR str[NATIVE_STR_LEN]; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); return LoadLibrary(str); } @@ -1455,7 +1447,7 @@ const cc_string DynamicLib_Ext = String_FromConst(".dylib"); void* DynamicLib_Load2(const cc_string* path) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); return NSAddImage(str, NSADDIMAGE_OPTION_WITH_SEARCHING | NSADDIMAGE_OPTION_RETURN_ON_ERROR); } @@ -1497,7 +1489,7 @@ const cc_string DynamicLib_Ext = String_FromConst(".so"); void* DynamicLib_Load2(const cc_string* path) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, path); + Platform_EncodeString(str, path); return dlopen(str, RTLD_NOW); } @@ -1539,7 +1531,7 @@ cc_bool DynamicLib_GetAll(void* lib, const struct DynamicLibSym* syms, int count *--------------------------------------------------------Platform---------------------------------------------------------* *#########################################################################################################################*/ #if defined CC_BUILD_WIN -int Platform_ConvertString(void* data, const cc_string* src) { +int Platform_EncodeString(void* data, const cc_string* src) { TCHAR* dst = (TCHAR*)data; int i; if (src->length > FILENAME_SIZE) Logger_Abort("String too long to expand"); @@ -1551,6 +1543,14 @@ int Platform_ConvertString(void* data, const cc_string* src) { return src->length * 2; } +void Platform_DecodeString(cc_string* dst, const void* data, int len) { +#ifdef UNICODE + String_AppendUtf16(dst, (const cc_unichar*)data, len * 2); +#else + String_DecodeCP1252(dst, (const cc_uint8*)data, len); +#endif +} + static void Platform_InitStopwatch(void) { LARGE_INTEGER freq; sw_highRes = QueryPerformanceFrequency(&freq); @@ -1639,7 +1639,7 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return Platform_DescribeErrorExt(res, dst, NULL); } #elif defined CC_BUILD_POSIX -int Platform_ConvertString(void* data, const cc_string* src) { +int Platform_EncodeString(void* data, const cc_string* src) { cc_uint8* dst = (cc_uint8*)data; cc_uint8* cur; int i, len = 0; @@ -1653,6 +1653,10 @@ int Platform_ConvertString(void* data, const cc_string* src) { return len; } +void Platform_DecodeString(cc_string* dst, const void* data, int len) { + String_AppendUtf8(dst, (const cc_uint8*)data, len); +} + static void Platform_InitPosix(void) { signal(SIGCHLD, SIG_IGN); /* So writing to closed socket doesn't raise SIGPIPE */ diff --git a/src/Platform.h b/src/Platform.h index 02b1f8a73..14f0f27ee 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -33,7 +33,9 @@ extern const cc_result ReturnCode_SocketWouldBlock; /* Encodes a string in platform specific format. (e.g. unicode on windows, UTF8 on linux) */ /* NOTE: Only useful for platform specific function calls - do NOT try to interpret the data. */ /* Returns the number of bytes written, excluding trailing NULL terminator. */ -CC_API int Platform_ConvertString(void* data, const cc_string* src); +int Platform_EncodeString(void* data, const cc_string* src); +/* Attempts to append all characters from the platform specific encoded data to the given string. */ +void Platform_DecodeString(cc_string* dst, const void* data, int len); /* Initialises the platform specific state. */ void Platform_Init(void); diff --git a/src/TexturePack.c b/src/TexturePack.c index c7309d5c2..d9a19e1f6 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -338,13 +338,7 @@ static void ExtractFromFile(const cc_string* filename) { cc_result res; String_InitArray(path, pathBuffer); -#ifdef CC_BUILD_WEB - /* texpacks/default.zip must be read from memory */ - /* instead of the default filesystem */ - String_Format1(&path, "/texpacks/%s", filename); -#else - String_Format1(&path, "texpacks/%s", filename); -#endif + String_Format1(&path, TEXPACKS_DIR "/%s", filename); res = Stream_OpenFile(&stream, &path); if (res) { Logger_SysWarn2(res, "opening", &path); return; } diff --git a/src/TexturePack.h b/src/TexturePack.h index 24f5646f6..240c4843e 100644 --- a/src/TexturePack.h +++ b/src/TexturePack.h @@ -49,7 +49,14 @@ CC_VAR extern struct _Atlas1DData { /* Textures for each 1D atlas. Only Atlas1D_Count of these are valid. */ GfxResourceID TexIds[ATLAS1D_MAX_ATLASES]; } Atlas1D; + extern cc_string TexturePack_Url; +#ifdef CC_BUILD_WEB +/* texpacks must be read from memory instead of the normal filesystem */ +#define TEXPACKS_DIR "/texpacks" +#else +#define TEXPACKS_DIR "texpacks" +#endif #define Atlas2D_TileX(texLoc) ((texLoc) & ATLAS2D_MASK) /* texLoc % ATLAS2D_TILES_PER_ROW */ #define Atlas2D_TileY(texLoc) ((texLoc) >> ATLAS2D_SHIFT) /* texLoc / ATLAS2D_TILES_PER_ROW */ diff --git a/src/Window.c b/src/Window.c index b25005af6..2968e4a8c 100644 --- a/src/Window.c +++ b/src/Window.c @@ -158,7 +158,7 @@ void Window_Create(int width, int height) { void Window_SetTitle(const cc_string* title) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, title); + Platform_EncodeString(str, title); SDL_SetWindowTitle(win_handle, str); } @@ -173,7 +173,7 @@ void Clipboard_GetText(cc_string* value) { void Clipboard_SetText(const cc_string* value) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, value); + Platform_EncodeString(str, value); SDL_SetClipboardText(str); } @@ -724,7 +724,7 @@ void Window_Create(int width, int height) { void Window_SetTitle(const cc_string* title) { TCHAR str[NATIVE_STR_LEN]; - Platform_ConvertString(str, title); + Platform_EncodeString(str, title); SetWindowText(win_handle, str); } @@ -1261,7 +1261,7 @@ void Window_Create(int width, int height) { void Window_SetTitle(const cc_string* title) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, title); + Platform_EncodeString(str, title); XStoreName(win_display, win_handle, str); } @@ -1562,7 +1562,7 @@ void Window_ProcessEvents(void) { if (e.xselectionrequest.selection == xa_clipboard && e.xselectionrequest.target == xa_utf8_string && clipboard_copy_text.length) { reply.xselection.property = Window_GetSelectionProperty(&e); char str[800]; - int len = Platform_ConvertString(str, &clipboard_copy_text); + int len = Platform_EncodeString(str, &clipboard_copy_text); XChangeProperty(win_display, reply.xselection.requestor, reply.xselection.property, xa_utf8_string, 8, PropModeReplace, (unsigned char*)str, len); @@ -2085,7 +2085,7 @@ void Clipboard_SetText(const cc_string* value) { if (err) Logger_Abort2(err, "Clearing Pasteboard"); PasteboardSynchronize(pbRef); - len = Platform_ConvertString(str, value); + len = Platform_EncodeString(str, value); cfData = CFDataCreate(NULL, str, len); if (!cfData) Logger_Abort("CFDataCreate() returned null pointer"); @@ -2482,7 +2482,7 @@ void Window_SetTitle(const cc_string* title) { int len; /* TODO: This leaks memory, old title isn't released */ - len = Platform_ConvertString(str, title); + len = Platform_EncodeString(str, title); titleCF = CFStringCreateWithBytes(kCFAllocatorDefault, str, len, kCFStringEncodingUTF8, false); SetWindowTitleWithCFString(win_handle, titleCF); } @@ -2861,7 +2861,7 @@ void Window_SetTitle(const cc_string* title) { int len; /* TODO: This leaks memory, old title isn't released */ - len = Platform_ConvertString(str, title); + len = Platform_EncodeString(str, title); titleCF = CFStringCreateWithBytes(kCFAllocatorDefault, str, len, kCFStringEncodingUTF8, false); objc_msgSend(winHandle, sel_registerName("setTitle:"), titleCF); } @@ -3501,7 +3501,7 @@ void Window_Create(int width, int height) { void Window_SetTitle(const cc_string* title) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, title); + Platform_EncodeString(str, title); EM_ASM_({ document.title = UTF8ToString($0); }, str); } @@ -3510,13 +3510,10 @@ static void* clipboard_obj; EMSCRIPTEN_KEEPALIVE void Window_GotClipboardText(char* src) { cc_string str; char strBuffer[512]; - int len; if (!clipboard_func) return; String_InitArray(str, strBuffer); - len = String_CalcLen(src, 2048); - String_AppendUtf8(&str, (const cc_uint8*)src, len); - + Platform_DecodeString(&str, src, String_CalcLen(src, 2048)); clipboard_func(&str, clipboard_obj); clipboard_func = NULL; } @@ -3524,7 +3521,7 @@ EMSCRIPTEN_KEEPALIVE void Window_GotClipboardText(char* src) { void Clipboard_GetText(cc_string* value) { } void Clipboard_SetText(const cc_string* value) { char str[NATIVE_STR_LEN]; - Platform_ConvertString(str, value); + Platform_EncodeString(str, value); /* For IE11, use window.clipboardData to set the clipboard */ /* For other browsers, instead use the window.copy events */ @@ -3648,25 +3645,69 @@ static void ShowDialogCore(const char* title, const char* msg) { EM_ASM_({ alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1)); }, title, msg); } +static OpenFileDialogCallback uploadCallback; +EMSCRIPTEN_KEEPALIVE void Window_OnFileUploaded(const char* src) { + cc_string file; char buffer[FILENAME_SIZE]; + String_InitArray(file, buffer); + + Platform_DecodeString(&file, src, String_Length(src)); + uploadCallback(&file); + uploadCallback = NULL; +} + +cc_result Window_OpenFileDialog(const char* filter, OpenFileDialogCallback callback) { + uploadCallback = callback; + EM_ASM_({ + var elem = window.cc_uploadElem; + if (!elem) { + elem = document.createElement('input'); + elem.setAttribute('type', 'file'); + elem.setAttribute('style', 'display: none'); + elem.accept = UTF8ToString($0); + + elem.addEventListener('change', + function(ev) { + var files = ev.target.files; + for (var i = 0; i < files.length; i++) { + var reader = new FileReader(); + var name = files[i].name; + + reader.onload = function(e) { + var data = new Uint8Array(e.target.result); + FS.createDataFile('/', name, data, true, true, true); + ccall('Window_OnFileUploaded', 'void', ['string'], ['/' + name]); + FS.unlink('/' + name); + }; + reader.readAsArrayBuffer(files[i]); + } + window.cc_container.removeChild(window.cc_uploadElem); + window.cc_uploadElem = null; + }, false); + window.cc_uploadElem = elem; + window.cc_container.appendChild(elem); + } + elem.click(); + }, filter); + return ERR_NOT_SUPPORTED; +} + void Window_AllocFramebuffer(struct Bitmap* bmp) { } void Window_DrawFramebuffer(Rect2D r) { } void Window_FreeFramebuffer(struct Bitmap* bmp) { } EMSCRIPTEN_KEEPALIVE void Window_OnTextChanged(const char* src) { cc_string str; char buffer[800]; - int len; - String_InitArray(str, buffer); - len = String_CalcLen(src, 800); - String_AppendUtf8(&str, (const cc_uint8*)src, len); + + Platform_DecodeString(&str, src, String_CalcLen(src, 3200)); Event_RaiseString(&InputEvents.TextChanged, &str); } void Window_OpenKeyboard(const cc_string* text, int type) { char str[NATIVE_STR_LEN]; - keyboardOpen = true; - if (!Input_TouchMode) return; - Platform_ConvertString(str, text); + keyboardOpen = true; + if (!Input_TouchMode) return; + Platform_EncodeString(str, text); Platform_LogConst("OPEN SESAME"); EM_ASM_({ @@ -3701,7 +3742,7 @@ void Window_OpenKeyboard(const cc_string* text, int type) { void Window_SetKeyboardText(const cc_string* text) { char str[NATIVE_STR_LEN]; if (!Input_TouchMode) return; - Platform_ConvertString(str, text); + Platform_EncodeString(str, text); EM_ASM_({ if (!window.cc_inputElem) return; diff --git a/src/Window.h b/src/Window.h index da12a2c4f..92d7f904a 100644 --- a/src/Window.h +++ b/src/Window.h @@ -120,6 +120,10 @@ void Cursor_SetVisible(cc_bool visible); /* Shows a dialog box window. */ CC_API void Window_ShowDialog(const char* title, const char* msg); +typedef void (*OpenFileDialogCallback)(const cc_string* path); +/* Shows an 'load file' dialog window. */ +cc_result Window_OpenFileDialog(const char* filter, OpenFileDialogCallback callback); + /* Allocates a framebuffer that can be drawn/transferred to the window. */ /* NOTE: Do NOT free bmp->Scan0, use Window_FreeFramebuffer. */ /* NOTE: This MUST be called whenever the window is resized. */