├── Makefile ├── README.md ├── main ├── component.mk ├── fMSXgo_main.c ├── minIni │ ├── component.mk │ ├── minGlue.h │ ├── minIni.c │ └── minIni.h ├── odroid-go-common │ ├── component.mk │ ├── hourglass_empty_black_48dp.h │ ├── image_sd_card_alert.h │ ├── image_sd_card_unknown.h │ ├── image_splash.h │ ├── odroid_audio.c │ ├── odroid_audio.h │ ├── odroid_display.c │ ├── odroid_display.h │ ├── odroid_input.c │ ├── odroid_input.h │ ├── odroid_sdcard.c │ ├── odroid_sdcard.h │ ├── odroid_settings.c │ ├── odroid_settings.h │ ├── odroid_system.c │ └── odroid_system.h ├── odroidGo │ ├── Audio.c │ ├── LibOdroidGo.c │ ├── LibOdroidGo.h │ ├── Menu.c │ ├── Multiplayer.c │ ├── component.mk │ └── files.c ├── ugui │ ├── component.mk │ ├── ugui.c │ ├── ugui.h │ └── ugui_config.h ├── utils │ ├── component.mk │ ├── utils.c │ └── utils.h └── video │ └── AVideo.i ├── res ├── tile.png └── tile.raw └── sdkconfig /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := goMSX 7 | COMPONENT_SRCDIRS := ./ EMULib fMSX odroidGo odroid-go-common Z80 8 | 9 | include $(IDF_PATH)/make/project.mk 10 | .build-post: .build-impl 11 | python /home/Test/esp/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port COM3 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 /c/Users/Test/Documents/NetBeansProjects/goMSX/build/bootloader/bootloader.bin 0x10000 /c/Users/Test/Documents/NetBeansProjects/goMSX/build/goMSX.bin 0x8000 /c/Users/Test/Documents/NetBeansProjects/goMSX/build/partitions_singleapp.bin 12 | 13 | flash_COM6: 14 | python /home/Test/esp/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port COM6 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0x1000 /c/Users/Test/Documents/NetBeansProjects/fMSX-go/build/bootloader/bootloader.bin 0x10000 /c/Users/Test/Documents/NetBeansProjects/fMSX-go/build/goMSX.bin 0x8000 /c/Users/Test/Documents/NetBeansProjects/fMSX-go/build/partitions.bin 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fMSX-go 2 | fMSX Port to ODROID-GO 3 | 4 | to build it, please download fMSX from here: https://fms.komkon.org/fMSX/ 5 | 6 | But there is also a precompiled Firmware file here: https://github.com/Schuemi/fMSX-go/releases 7 | 8 | Please create these directories on your SD Card manually: 9 | ``` 10 | /roms/msx/bios 11 | /roms/msx/games 12 | /odroid/data/msx 13 | ``` 14 | and put the BIOS files in /roms/msx/bios. You need at least the files MSX2.ROM, MSX2EXT.ROM and DISK.ROM. 15 | 16 | You can have subfolders in /roms/msx/games. If you have much games, subfolders are recommend. You should not have more than 150 Games in one folder. 17 | 18 | In /odroid/data/msx you can save keymapping files if you wish. The default keymapping is: 19 | ``` 20 | [KEYMAPPING] 21 | UP = JST_UP 22 | RIGHT = JST_RIGHT 23 | DOWN = JST_DOWN 24 | LEFT = JST_LEFT 25 | SELECT = 1 26 | START = 2 27 | A = JST_FIREA 28 | B = JST_FIREB 29 | ``` 30 | The possible Key Mappings are: 31 | JST_UP, JST_RIGHT, JST_DOWN, JST_LEFT, JST_FIREA, JST_FIREB, KBD_SPACE, KBD_F1, KBD_F2, KBD_F3, KBD_F3, KBD_F4, KBD_F5, KBD_LEFT, KBD_UP, KBD_DOWN, KBD_SHIFT, KBD_CONTROL, KBD_GRAPH, KBD_BS, KBD_TAB, KBD_CAPSLOCK, KBD_SELECT, KBD_HOME, KBD_ENTER, KBD_INSERT, KBD_COUNTRY, KBD_STOP, KBD_NUMPAD0 - KBD_NUMPAD9, KBD_ESCAPE and a single digit or a single letter. 32 | 33 | You can use for every game a custom mapping file. Put the file in /odroid/data/msx. It schould have the name [GAME].ini. For example if your gamefiles name is GTA3.rom the keyfilename has to be GTA3.ini. 34 | 35 | Also you can have a default setting. Go into /odroid/data/msx, if you have ever started fMSX there should be a file called "config.ini". In this file you can add your defualt keymapping the same way you would add a key mapping for a game. 36 | 37 | 38 | 39 | Gamesaves are saved in the same directory as the game itself. It is calles [GAME].sav and is compatible to every other fMSX port. So you can continue playing on pc, playstation etc... 40 | 41 | # Virtual Keyboard: 42 | 43 | You can open a virtual keyboard by pressing an hold the "A" button ans then the "Menu" button. On the virtual keyboard you can use the "A" or the "B" button to press a key. If you are holding a button, this button will also be holded in the emulator. So if you go to the "shift" key, press and hold "A" and then while pressing "A" go to the "1" key and press "B" you will write a "!". Because on the real MSX if you would press shift + 1 you will also write a "!" 44 | 45 | 46 | 47 | # Multiplayer 48 | 49 | Multiplayer is really fun. I tested It a hole night with some friends and a couple of beers ;) 50 | 51 | To use multiplayer you need to have exactly the same BIOS files on both devices and the same game file in the same directory. The best way is to simply copy the SD from one device. 52 | 53 | One is the server, the other is the client. The server starts a game with "start multiplayer server" in the menu, the other one chooses "multiplayer client". The server selects a ROM or a floppy disk. Both devices will restart and they run now the same game. 54 | 55 | The "joystick" of the server runs in port 1, the client has port 2. 56 | 57 | In multiplayer mode there are a few limitations: 58 | 59 | - You cannot enter the menu. To start another game or not to play in pairs, please turn off the devices. 60 | - Only the server can call the virtual keyboard 61 | - ~~Only the server has sound.~~ 62 | - no save games 63 | 64 | Many games work very well, some crack the sound, some run too slowly. The problem is to run both games in exactly the same state. I've tried to get the best out of the hardware, maybe I'll find ways to optimize it, but I think it's going very well already. And it's a lot of fun. 65 | 66 | You can't have two Multiplayer games with 4 devices at the same place yet. They're gonna bother each other, because there are no "Multiplayer rooms" yet. 67 | 68 | When it cracks in a game: just turn off the sound. 69 | 70 | How does this work? 71 | 72 | - The server starts an access point with a hidden Siid 73 | - The client searches for this access point, if he finds it he will connect 74 | - The server tells the client what game they whant to play 75 | - after starting the game on both devices they send UDP packts with joysick and keyboard data to each other. Both devices have to know what the other is doing in every vblank. 76 | 77 | 78 | # Next: 79 | 80 | The next things I'm planning are: 81 | 82 | - Bugfix, bugfix, bugfix. No new functions the nex releases. 83 | 84 | # ODROID QWERTY 85 | ![QWERTY](https://raw.githubusercontent.com/Schuemi/fMSX-go/b665513d9a78db4154b9bda7300a44e97bdeb064/res/IMG_20181016_165658.jpg) 86 | 87 | You can use the Odroid QWERTY keyboard now to write some BASIC code ;) 88 | To press the F1-F5 keys, press Ctrl+1, Ctrl+2 ... 89 | To lock the shift key press Ctrl + Up. To unlock press up only. 90 | 91 | 92 | # Donations 93 | 94 | Donations are very welcome. I have long wondered if I should add a donate button here or not. But, what the hell, if one or the other beer should come out for my troubles here, I like to drink one to you! Thanks a lot! 95 | 96 | One beer donation (3.50€): 97 | ![3.50](https://www.paypalobjects.com/en_GB/i/btn/btn_donate_LG.gif) 98 | 99 | Two beer donations (5.00€): 100 | ![5.00](https://www.paypalobjects.com/en_GB/i/btn/btn_donate_LG.gif) 101 | 102 | Donate a six-pack of beer. :) (10.00€): 103 | ![10.00](https://www.paypalobjects.com/en_GB/i/btn/btn_donate_LG.gif) 104 | 105 | 106 | 107 | 108 | 109 | Hint: 110 | If you find any spelling or grammatical mistakes, please tell me. My english could be better. Thank you. 111 | 112 | -------------------------------------------------------------------------------- /main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_PRIV_INCLUDEDIRS := odroid-go-common nfMSX odroidGo nZ80 nEMULib utils minIni 2 | CPPFLAGS := -DBPS16 -DLSB_FIRST -DESP32 3 | CFLAGS := -Ofast -mlongcalls -Wno-error 4 | -------------------------------------------------------------------------------- /main/fMSXgo_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2018 Schuemi. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | /* 25 | * File: main.cpp 26 | * Author: Schuemi 27 | * 28 | * Created on 25. Juli 2018, 09:38 29 | */ 30 | 31 | 32 | // used online Font creator: http://dotmatrixtool.com 33 | // set to : 8px by 8px, row major, little endian. 34 | 35 | /// link the video function on top of the rest (more speed)) 36 | 37 | #include "../video/AVideo.i" 38 | 39 | 40 | #include 41 | #include 42 | #include "odroid_sdcard.h" 43 | #include "odroid_display.h" 44 | #include "odroid_system.h" 45 | #include "MSX.h" 46 | #include "EMULib.h" 47 | #include "Console.h" 48 | 49 | 50 | #include "esp_system.h" 51 | #include "odroid_input.h" 52 | #include "nvs_flash.h" 53 | 54 | #include "LibOdroidGo.h" 55 | #include "esp_event.h" 56 | 57 | #include "esp_event_loop.h" 58 | 59 | #include "odroid_settings.h" 60 | 61 | #include "utils.h" 62 | 63 | 64 | 65 | #ifndef PIXEL 66 | #define PIXEL(R,G,B) (pixel)(((31*(R)/255)<<11)|((63*(G)/255)<<5)|(31*(B)/255)) 67 | #endif 68 | const char* SD_BASE_PATH = "/sd"; 69 | 70 | 71 | //mkfw.exe "fMSX" tile.raw 0 16 2097152 fMSX goMSX.bin 72 | 73 | 74 | void app_main(void) { 75 | 76 | 77 | esp_err_t ret = nvs_flash_init(); 78 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 79 | ESP_ERROR_CHECK(nvs_flash_erase()); 80 | ret = nvs_flash_init(); 81 | } 82 | ESP_ERROR_CHECK(ret); 83 | 84 | 85 | 86 | 87 | uint8_t initOkay; 88 | uint8_t failure = 0; 89 | 90 | odroid_system_init(); 91 | #ifdef WITH_WLAN 92 | ODROID_WLAN_TYPE wlan = odroid_settings_WLAN_get(); 93 | if (wlan == ODROID_WLAN_AP) server_init(); 94 | if (wlan == ODROID_WLAN_STA) client_init(); 95 | 96 | #endif 97 | 98 | odroid_input_gamepad_init(); 99 | 100 | 101 | esp_err_t r = odroid_sdcard_open(SD_BASE_PATH); 102 | if (r != ESP_OK) 103 | { 104 | // odroid_display_show_sderr(ODROID_SD_ERR_NOCARD); 105 | failure = 1; 106 | printf("ODROID_SD_ERR_NOCARD\n"); 107 | } 108 | 109 | 110 | 111 | // Display 112 | ili9341_prepare(); 113 | ili9341_init(); 114 | 115 | /// keyboard 116 | 117 | 118 | UPeriod = 50; 119 | 120 | initOkay = InitMachine(); 121 | 122 | if (initOkay) { 123 | if (! dirExist(FMSX_ROOT_GAMESDIR)) mkdir(FMSX_ROOT_GAMESDIR, 666); 124 | if (! dirExist(FMSX_ROOT_GAMESDIR"/bios")) mkdir(FMSX_ROOT_GAMESDIR"/bios", 666); 125 | if (! dirExist(FMSX_ROOT_DATADIR)) mkdir(FMSX_ROOT_DATADIR, 666); 126 | } 127 | 128 | 129 | if (initOkay) { 130 | 131 | 132 | #ifdef WITH_WLAN 133 | if (wlan == ODROID_WLAN_AP){ server_wait_for_player(); } 134 | if (wlan == ODROID_WLAN_STA){ client_try_connect();} 135 | if (wlan == ODROID_WLAN_AP || wlan == ODROID_WLAN_STA) { 136 | InitChangeGame(getMPFileName()); 137 | } 138 | 139 | #endif 140 | Mode=(Mode&~MSX_VIDEO)|MSX_PAL; 141 | Mode=(Mode&~MSX_MODEL)|MSX_MSX2; 142 | Mode=(Mode&~MSX_JOYSTICKS)|MSX_JOY1|MSX_JOY2; 143 | 144 | RAMPages = 2; 145 | VRAMPages = 2; 146 | 147 | if (!StartMSX(Mode,RAMPages,VRAMPages)) { 148 | switch (failure) { 149 | case 1: 150 | odroidFmsxGUI_msgBox("Error","Cannot mount SDCard", 1); 151 | break; 152 | default: 153 | odroidFmsxGUI_msgBox("Error","Start fMSX failed.\nMissing bios files?", 1); 154 | } 155 | 156 | } 157 | 158 | odroidFmsxGUI_msgBox("End","The emulation was shutted down\nYou can turn off your device", 1); 159 | } 160 | 161 | } 162 | 163 | -------------------------------------------------------------------------------- /main/minIni/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_PRIV_INCLUDEDIRS := ../odroidGo 2 | CPPFLAGS := -BPS16 -DBPP16 -DLSB_FIRST -DESP32 3 | CFLAGS := -Ofast -mlongcalls -Wno-error 4 | -------------------------------------------------------------------------------- /main/minIni/minGlue.h: -------------------------------------------------------------------------------- 1 | /* Glue functions for the minIni library, based on the C/C++ stdio library 2 | * 3 | * Or better said: this file contains macros that maps the function interface 4 | * used by minIni to the standard C/C++ file I/O functions. 5 | * 6 | * By CompuPhase, 2008-2012 7 | * This "glue file" is in the public domain. It is distributed without 8 | * warranties or conditions of any kind, either express or implied. 9 | */ 10 | 11 | /* map required file I/O types and functions to the standard C library */ 12 | #include 13 | #include "LibOdroidGo.h" 14 | 15 | #define INI_FILETYPE FILE* 16 | #define ini_openread(filename,file) ((*(file) = _fopen((filename),"rb")) != NULL) 17 | #define ini_openwrite(filename,file) ((*(file) = _fopen((filename),"wb")) != NULL) 18 | #define ini_close(file) (_fclose(*(file)) == 0) 19 | #define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) 20 | #define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) 21 | #define ini_rename(source,dest) (rename((source), (dest)) == 0) 22 | #define ini_remove(filename) (remove(filename) == 0) 23 | 24 | #define INI_FILEPOS fpos_t 25 | #define ini_tell(file,pos) (fgetpos(*(file), (pos)) == 0) 26 | #define ini_seek(file,pos) (fsetpos(*(file), (pos)) == 0) 27 | 28 | /* for floating-point support, define additional types and functions */ 29 | #define INI_REAL float 30 | #define ini_ftoa(string,value) sprintf((string),"%f",(value)) 31 | #define ini_atof(string) (INI_REAL)strtod((string),NULL) 32 | -------------------------------------------------------------------------------- /main/minIni/minIni.c: -------------------------------------------------------------------------------- 1 | /* minIni - Multi-Platform INI file parser, suitable for embedded systems 2 | * 3 | * These routines are in part based on the article "Multiplatform .INI Files" 4 | * by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal. 5 | * 6 | * Copyright (c) CompuPhase, 2008-2012 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 9 | * use this file except in compliance with the License. You may obtain a copy 10 | * of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 17 | * License for the specific language governing permissions and limitations 18 | * under the License. 19 | * 20 | * Version: $Id: minIni.c 45 2012-05-14 11:53:09Z thiadmer.riemersma $ 21 | */ 22 | 23 | #if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined MININI_ANSI 24 | # if !defined UNICODE /* for Windows */ 25 | # define UNICODE 26 | # endif 27 | # if !defined _UNICODE /* for C library */ 28 | # define _UNICODE 29 | # endif 30 | #endif 31 | 32 | #define MININI_IMPLEMENTATION 33 | #include "minIni.h" 34 | #if defined NDEBUG 35 | #define assert(e) 36 | #else 37 | #include 38 | #endif 39 | 40 | #if !defined __T 41 | #include 42 | #include 43 | #include 44 | #define strnicmp strncasecmp 45 | #define TCHAR char 46 | #define __T(s) s 47 | #define _tcscat strcat 48 | #define _tcschr strchr 49 | #define _tcscmp strcmp 50 | #define _tcscpy strcpy 51 | #define _tcsicmp stricmp 52 | #define _tcslen strlen 53 | #define _tcsncmp strncmp 54 | #define _tcsnicmp strnicmp 55 | #define _tcsrchr strrchr 56 | #define _tcstol strtol 57 | #define _tcstod strtod 58 | #define _totupper toupper 59 | #define _stprintf sprintf 60 | #define _tfgets fgets 61 | #define _tfputs fputs 62 | #define _tfopen fopen 63 | #define _tremove remove 64 | #define _trename rename 65 | #endif 66 | 67 | #if defined __linux || defined __linux__ 68 | #define __LINUX__ 69 | #elif defined FREEBSD && !defined __FreeBSD__ 70 | #define __FreeBSD__ 71 | #elif defined(_MSC_VER) 72 | #pragma warning(disable: 4996) /* for Microsoft Visual C/C++ */ 73 | #endif 74 | #if !defined strnicmp && !defined PORTABLE_STRNICMP 75 | #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ 76 | #define strnicmp strncasecmp 77 | #endif 78 | #endif 79 | 80 | #if !defined INI_LINETERM 81 | #define INI_LINETERM __T("\n") 82 | #endif 83 | #if !defined INI_FILETYPE 84 | #error Missing definition for INI_FILETYPE. 85 | #endif 86 | 87 | #if !defined sizearray 88 | #define sizearray(a) (sizeof(a) / sizeof((a)[0])) 89 | #endif 90 | 91 | enum quote_option { 92 | QUOTE_NONE, 93 | QUOTE_ENQUOTE, 94 | QUOTE_DEQUOTE, 95 | }; 96 | 97 | #if defined PORTABLE_STRNICMP 98 | int strnicmp(const TCHAR *s1, const TCHAR *s2, size_t n) 99 | { 100 | register int c1, c2; 101 | 102 | while (n-- != 0 && (*s1 || *s2)) { 103 | c1 = *s1++; 104 | if ('a' <= c1 && c1 <= 'z') 105 | c1 += ('A' - 'a'); 106 | c2 = *s2++; 107 | if ('a' <= c2 && c2 <= 'z') 108 | c2 += ('A' - 'a'); 109 | if (c1 != c2) 110 | return c1 - c2; 111 | } /* while */ 112 | return 0; 113 | } 114 | #endif /* PORTABLE_STRNICMP */ 115 | 116 | static TCHAR *skipleading(const TCHAR *str) 117 | { 118 | assert(str != NULL); 119 | while (*str != '\0' && *str <= ' ') 120 | str++; 121 | return (TCHAR *)str; 122 | } 123 | 124 | static TCHAR *skiptrailing(const TCHAR *str, const TCHAR *base) 125 | { 126 | assert(str != NULL); 127 | assert(base != NULL); 128 | while (str > base && *(str-1) <= ' ') 129 | str--; 130 | return (TCHAR *)str; 131 | } 132 | 133 | static TCHAR *striptrailing(TCHAR *str) 134 | { 135 | TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str); 136 | assert(ptr != NULL); 137 | *ptr = '\0'; 138 | return str; 139 | } 140 | 141 | static TCHAR *save_strncpy(TCHAR *dest, const TCHAR *source, size_t maxlen, enum quote_option option) 142 | { 143 | size_t d, s; 144 | 145 | assert(maxlen>0); 146 | assert(dest <= source || dest >= source + maxlen); 147 | if (option == QUOTE_ENQUOTE && maxlen < 3) 148 | option = QUOTE_NONE; /* cannot store two quotes and a terminating zero in less than 3 characters */ 149 | 150 | switch (option) { 151 | case QUOTE_NONE: 152 | for (d = 0; d < maxlen - 1 && source[d] != '\0'; d++) 153 | dest[d] = source[d]; 154 | assert(d < maxlen); 155 | dest[d] = '\0'; 156 | break; 157 | case QUOTE_ENQUOTE: 158 | d = 0; 159 | dest[d++] = '"'; 160 | for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) { 161 | if (source[s] == '"') { 162 | if (d >= maxlen - 3) 163 | break; /* no space to store the escape character plus the one that follows it */ 164 | dest[d++] = '\\'; 165 | } /* if */ 166 | dest[d] = source[s]; 167 | } /* for */ 168 | dest[d++] = '"'; 169 | dest[d] = '\0'; 170 | break; 171 | case QUOTE_DEQUOTE: 172 | for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) { 173 | if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"') 174 | s++; 175 | dest[d] = source[s]; 176 | } /* for */ 177 | dest[d] = '\0'; 178 | break; 179 | default: 180 | assert(0); 181 | } /* switch */ 182 | 183 | return dest; 184 | } 185 | 186 | static TCHAR *cleanstring(TCHAR *string, enum quote_option *quotes) 187 | { 188 | int isstring; 189 | TCHAR *ep; 190 | 191 | assert(string != NULL); 192 | assert(quotes != NULL); 193 | 194 | /* Remove a trailing comment */ 195 | isstring = 0; 196 | for (ep = string; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) { 197 | if (*ep == '"') { 198 | if (*(ep + 1) == '"') 199 | ep++; /* skip "" (both quotes) */ 200 | else 201 | isstring = !isstring; /* single quote, toggle isstring */ 202 | } else if (*ep == '\\' && *(ep + 1) == '"') { 203 | ep++; /* skip \" (both quotes */ 204 | } /* if */ 205 | } /* for */ 206 | assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#')); 207 | *ep = '\0'; /* terminate at a comment */ 208 | striptrailing(string); 209 | /* Remove double quotes surrounding a value */ 210 | *quotes = QUOTE_NONE; 211 | if (*string == '"' && (ep = _tcschr(string, '\0')) != NULL && *(ep - 1) == '"') { 212 | string++; 213 | *--ep = '\0'; 214 | *quotes = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */ 215 | } /* if */ 216 | return string; 217 | } 218 | 219 | static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key, 220 | int idxSection, int idxKey, TCHAR *Buffer, int BufferSize) 221 | { 222 | TCHAR *sp, *ep; 223 | int len, idx; 224 | enum quote_option quotes; 225 | TCHAR LocalBuffer[INI_BUFFERSIZE]; 226 | 227 | assert(fp != NULL); 228 | /* Move through file 1 line at a time until a section is matched or EOF. If 229 | * parameter Section is NULL, only look at keys above the first section. If 230 | * idxSection is postive, copy the relevant section name. 231 | */ 232 | len = (Section != NULL) ? _tcslen(Section) : 0; 233 | if (len > 0 || idxSection >= 0) { 234 | idx = -1; 235 | do { 236 | if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp)) 237 | return 0; 238 | sp = skipleading(LocalBuffer); 239 | ep = _tcschr(sp, ']'); 240 | } while (*sp != '[' || ep == NULL || (((int)(ep-sp-1) != len || _tcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection)); 241 | if (idxSection >= 0) { 242 | if (idx == idxSection) { 243 | assert(ep != NULL); 244 | assert(*ep == ']'); 245 | *ep = '\0'; 246 | save_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE); 247 | return 1; 248 | } /* if */ 249 | return 0; /* no more section found */ 250 | } /* if */ 251 | } /* if */ 252 | 253 | /* Now that the section has been found, find the entry. 254 | * Stop searching upon leaving the section's area. 255 | */ 256 | assert(Key != NULL || idxKey >= 0); 257 | len = (Key != NULL) ? (int)_tcslen(Key) : 0; 258 | idx = -1; 259 | do { 260 | if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[') 261 | return 0; 262 | sp = skipleading(LocalBuffer); 263 | ep = _tcschr(sp, '='); /* Parse out the equal sign */ 264 | if (ep == NULL) 265 | ep = _tcschr(sp, ':'); 266 | } while (*sp == ';' || *sp == '#' || ep == NULL || (((int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey)); 267 | if (idxKey >= 0) { 268 | if (idx == idxKey) { 269 | assert(ep != NULL); 270 | assert(*ep == '=' || *ep == ':'); 271 | *ep = '\0'; 272 | striptrailing(sp); 273 | save_strncpy(Buffer, sp, BufferSize, QUOTE_NONE); 274 | return 1; 275 | } /* if */ 276 | return 0; /* no more key found (in this section) */ 277 | } /* if */ 278 | 279 | /* Copy up to BufferSize chars to buffer */ 280 | assert(ep != NULL); 281 | assert(*ep == '=' || *ep == ':'); 282 | sp = skipleading(ep + 1); 283 | sp = cleanstring(sp, "es); /* Remove a trailing comment */ 284 | save_strncpy(Buffer, sp, BufferSize, quotes); 285 | return 1; 286 | } 287 | 288 | /** ini_gets() 289 | * \param Section the name of the section to search for 290 | * \param Key the name of the entry to find the value of 291 | * \param DefValue default string in the event of a failed read 292 | * \param Buffer a pointer to the buffer to copy into 293 | * \param BufferSize the maximum number of characters to copy 294 | * \param Filename the name and full path of the .ini file to read from 295 | * 296 | * \return the number of characters copied into the supplied buffer 297 | */ 298 | int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue, 299 | TCHAR *Buffer, int BufferSize, const TCHAR *Filename) 300 | { 301 | INI_FILETYPE fp; 302 | int ok = 0; 303 | 304 | if (Buffer == NULL || BufferSize <= 0 || Key == NULL) 305 | return 0; 306 | if (ini_openread(Filename, &fp)) { 307 | ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize); 308 | (void)ini_close(&fp); 309 | } /* if */ 310 | if (!ok) 311 | save_strncpy(Buffer, DefValue, BufferSize, QUOTE_NONE); 312 | return _tcslen(Buffer); 313 | } 314 | 315 | /** ini_getl() 316 | * \param Section the name of the section to search for 317 | * \param Key the name of the entry to find the value of 318 | * \param DefValue the default value in the event of a failed read 319 | * \param Filename the name of the .ini file to read from 320 | * 321 | * \return the value located at Key 322 | */ 323 | long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename) 324 | { 325 | TCHAR LocalBuffer[64]; 326 | int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); 327 | return (len == 0) ? DefValue 328 | : ((len >= 2 && _totupper(LocalBuffer[1]) == 'X') ? _tcstol(LocalBuffer, NULL, 16) 329 | : _tcstol(LocalBuffer, NULL, 10)); 330 | } 331 | 332 | #if defined INI_REAL 333 | /** ini_getf() 334 | * \param Section the name of the section to search for 335 | * \param Key the name of the entry to find the value of 336 | * \param DefValue the default value in the event of a failed read 337 | * \param Filename the name of the .ini file to read from 338 | * 339 | * \return the value located at Key 340 | */ 341 | INI_REAL ini_getf(const TCHAR *Section, const TCHAR *Key, INI_REAL DefValue, const TCHAR *Filename) 342 | { 343 | TCHAR LocalBuffer[64]; 344 | int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); 345 | return (len == 0) ? DefValue : ini_atof(LocalBuffer); 346 | } 347 | #endif 348 | 349 | /** ini_getbool() 350 | * \param Section the name of the section to search for 351 | * \param Key the name of the entry to find the value of 352 | * \param DefValue default value in the event of a failed read; it should 353 | * zero (0) or one (1). 354 | * \param Buffer a pointer to the buffer to copy into 355 | * \param BufferSize the maximum number of characters to copy 356 | * \param Filename the name and full path of the .ini file to read from 357 | * 358 | * A true boolean is found if one of the following is matched: 359 | * - A string starting with 'y' or 'Y' 360 | * - A string starting with 't' or 'T' 361 | * - A string starting with '1' 362 | * 363 | * A false boolean is found if one of the following is matched: 364 | * - A string starting with 'n' or 'N' 365 | * - A string starting with 'f' or 'F' 366 | * - A string starting with '0' 367 | * 368 | * \return the true/false flag as interpreted at Key 369 | */ 370 | int ini_getbool(const TCHAR *Section, const TCHAR *Key, int DefValue, const TCHAR *Filename) 371 | { 372 | TCHAR LocalBuffer[2]; 373 | int ret; 374 | 375 | ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); 376 | LocalBuffer[0] = (TCHAR)toupper(LocalBuffer[0]); 377 | if (LocalBuffer[0] == 'Y' || LocalBuffer[0] == '1' || LocalBuffer[0] == 'T') 378 | ret = 1; 379 | else if (LocalBuffer[0] == 'N' || LocalBuffer[0] == '0' || LocalBuffer[0] == 'F') 380 | ret = 0; 381 | else 382 | ret = DefValue; 383 | 384 | return(ret); 385 | } 386 | 387 | /** ini_getsection() 388 | * \param idx the zero-based sequence number of the section to return 389 | * \param Buffer a pointer to the buffer to copy into 390 | * \param BufferSize the maximum number of characters to copy 391 | * \param Filename the name and full path of the .ini file to read from 392 | * 393 | * \return the number of characters copied into the supplied buffer 394 | */ 395 | int ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) 396 | { 397 | INI_FILETYPE fp; 398 | int ok = 0; 399 | 400 | if (Buffer == NULL || BufferSize <= 0 || idx < 0) 401 | return 0; 402 | if (ini_openread(Filename, &fp)) { 403 | ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize); 404 | (void)ini_close(&fp); 405 | } /* if */ 406 | if (!ok) 407 | *Buffer = '\0'; 408 | return _tcslen(Buffer); 409 | } 410 | 411 | /** ini_getkey() 412 | * \param Section the name of the section to browse through, or NULL to 413 | * browse through the keys outside any section 414 | * \param idx the zero-based sequence number of the key to return 415 | * \param Buffer a pointer to the buffer to copy into 416 | * \param BufferSize the maximum number of characters to copy 417 | * \param Filename the name and full path of the .ini file to read from 418 | * 419 | * \return the number of characters copied into the supplied buffer 420 | */ 421 | int ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) 422 | { 423 | INI_FILETYPE fp; 424 | int ok = 0; 425 | 426 | if (Buffer == NULL || BufferSize <= 0 || idx < 0) 427 | return 0; 428 | if (ini_openread(Filename, &fp)) { 429 | ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize); 430 | (void)ini_close(&fp); 431 | } /* if */ 432 | if (!ok) 433 | *Buffer = '\0'; 434 | return _tcslen(Buffer); 435 | } 436 | 437 | 438 | #if !defined INI_NOBROWSE 439 | /** ini_browse() 440 | * \param Callback a pointer to a function that will be called for every 441 | * setting in the INI file. 442 | * \param UserData arbitrary data, which the function passes on the the 443 | * \c Callback function 444 | * \param Filename the name and full path of the .ini file to read from 445 | * 446 | * \return 1 on success, 0 on failure (INI file not found) 447 | * 448 | * \note The \c Callback function must return 1 to continue 449 | * browsing through the INI file, or 0 to stop. Even when the 450 | * callback stops the browsing, this function will return 1 451 | * (for success). 452 | */ 453 | int ini_browse(INI_CALLBACK Callback, const void *UserData, const TCHAR *Filename) 454 | { 455 | TCHAR LocalBuffer[INI_BUFFERSIZE]; 456 | TCHAR *sp, *ep; 457 | int lenSec, lenKey; 458 | enum quote_option quotes; 459 | INI_FILETYPE fp; 460 | 461 | if (Callback == NULL) 462 | return 0; 463 | if (!ini_openread(Filename, &fp)) 464 | return 0; 465 | 466 | LocalBuffer[0] = '\0'; /* copy an empty section in the buffer */ 467 | lenSec = _tcslen(LocalBuffer) + 1; 468 | for ( ;; ) { 469 | if (!ini_read(LocalBuffer + lenSec, INI_BUFFERSIZE - lenSec, &fp)) 470 | break; 471 | sp = skipleading(LocalBuffer + lenSec); 472 | /* ignore empty strings and comments */ 473 | if (*sp == '\0' || *sp == ';' || *sp == '#') 474 | continue; 475 | /* see whether we reached a new section */ 476 | ep = _tcschr(sp, ']'); 477 | if (*sp == '[' && ep != NULL) { 478 | *ep = '\0'; 479 | save_strncpy(LocalBuffer, sp + 1, INI_BUFFERSIZE, QUOTE_NONE); 480 | lenSec = _tcslen(LocalBuffer) + 1; 481 | continue; 482 | } /* if */ 483 | /* not a new section, test for a key/value pair */ 484 | ep = _tcschr(sp, '='); /* test for the equal sign or colon */ 485 | if (ep == NULL) 486 | ep = _tcschr(sp, ':'); 487 | if (ep == NULL) 488 | continue; /* invalid line, ignore */ 489 | *ep++ = '\0'; /* split the key from the value */ 490 | striptrailing(sp); 491 | save_strncpy(LocalBuffer + lenSec, sp, INI_BUFFERSIZE - lenSec, QUOTE_NONE); 492 | lenKey = _tcslen(LocalBuffer + lenSec) + 1; 493 | /* clean up the value */ 494 | sp = skipleading(ep); 495 | sp = cleanstring(sp, "es); /* Remove a trailing comment */ 496 | save_strncpy(LocalBuffer + lenSec + lenKey, sp, INI_BUFFERSIZE - lenSec - lenKey, quotes); 497 | /* call the callback */ 498 | if (!Callback(LocalBuffer, LocalBuffer + lenSec, LocalBuffer + lenSec + lenKey, UserData)) 499 | break; 500 | } /* for */ 501 | 502 | (void)ini_close(&fp); 503 | return 1; 504 | } 505 | #endif /* INI_NOBROWSE */ 506 | 507 | #if ! defined INI_READONLY 508 | static void ini_tempname(TCHAR *dest, const TCHAR *source, int maxlength) 509 | { 510 | TCHAR *p; 511 | 512 | save_strncpy(dest, source, maxlength, QUOTE_NONE); 513 | p = _tcsrchr(dest, '\0'); 514 | assert(p != NULL); 515 | *(p - 1) = '~'; 516 | } 517 | 518 | static enum quote_option check_enquote(const TCHAR *Value) 519 | { 520 | const TCHAR *p; 521 | 522 | /* run through the value, if it has trailing spaces, or '"', ';' or '#' 523 | * characters, enquote it 524 | */ 525 | assert(Value != NULL); 526 | for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++) 527 | /* nothing */; 528 | return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE; 529 | } 530 | 531 | static void writesection(TCHAR *LocalBuffer, const TCHAR *Section, INI_FILETYPE *fp) 532 | { 533 | TCHAR *p; 534 | 535 | if (Section != NULL && _tcslen(Section) > 0) { 536 | LocalBuffer[0] = '['; 537 | save_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE); /* -1 for '[', -1 for ']', -2 for '\r\n' */ 538 | p = _tcsrchr(LocalBuffer, '\0'); 539 | assert(p != NULL); 540 | *p++ = ']'; 541 | _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ 542 | (void)ini_write(LocalBuffer, fp); 543 | } /* if */ 544 | } 545 | 546 | static void writekey(TCHAR *LocalBuffer, const TCHAR *Key, const TCHAR *Value, INI_FILETYPE *fp) 547 | { 548 | TCHAR *p; 549 | enum quote_option option = check_enquote(Value); 550 | save_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE); /* -1 for '=', -2 for '\r\n' */ 551 | p = _tcsrchr(LocalBuffer, '\0'); 552 | assert(p != NULL); 553 | *p++ = '='; 554 | save_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */ 555 | p = _tcsrchr(LocalBuffer, '\0'); 556 | assert(p != NULL); 557 | _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ 558 | (void)ini_write(LocalBuffer, fp); 559 | } 560 | 561 | static int cache_accum(const TCHAR *string, int *size, int max) 562 | { 563 | int len = _tcslen(string); 564 | if (*size + len >= max) 565 | return 0; 566 | *size += len; 567 | return 1; 568 | } 569 | 570 | static int cache_flush(TCHAR *buffer, int *size, 571 | INI_FILETYPE *rfp, INI_FILETYPE *wfp, INI_FILEPOS *mark) 572 | { 573 | int pos = 0; 574 | 575 | (void)ini_seek(rfp, mark); 576 | assert(buffer != NULL); 577 | buffer[0] = '\0'; 578 | assert(size != NULL); 579 | while (pos < *size) { 580 | (void)ini_read(buffer + pos, INI_BUFFERSIZE - pos, rfp); 581 | pos += _tcslen(buffer + pos); 582 | assert(pos <= *size); 583 | } /* while */ 584 | if (buffer[0] != '\0') 585 | (void)ini_write(buffer, wfp); 586 | (void)ini_tell(rfp, mark); /* update mark */ 587 | *size = 0; 588 | /* return whether the buffer ended with a line termination */ 589 | return (_tcscmp(buffer + pos - _tcslen(INI_LINETERM), INI_LINETERM) == 0); 590 | } 591 | 592 | static int close_rename(INI_FILETYPE *rfp, INI_FILETYPE *wfp, const TCHAR *filename, TCHAR *buffer) 593 | { 594 | (void)ini_close(rfp); 595 | (void)ini_close(wfp); 596 | (void)ini_remove(filename); 597 | (void)ini_tempname(buffer, filename, INI_BUFFERSIZE); 598 | (void)ini_rename(buffer, filename); 599 | return 1; 600 | } 601 | 602 | /** ini_puts() 603 | * \param Section the name of the section to write the string in 604 | * \param Key the name of the entry to write, or NULL to erase all keys in the section 605 | * \param Value a pointer to the buffer the string, or NULL to erase the key 606 | * \param Filename the name and full path of the .ini file to write to 607 | * 608 | * \return 1 if successful, otherwise 0 609 | */ 610 | int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename) 611 | { 612 | INI_FILETYPE rfp; 613 | INI_FILETYPE wfp; 614 | INI_FILEPOS mark; 615 | TCHAR *sp, *ep; 616 | TCHAR LocalBuffer[INI_BUFFERSIZE]; 617 | int len, match, flag, cachelen; 618 | 619 | assert(Filename != NULL); 620 | if (!ini_openread(Filename, &rfp)) { 621 | /* If the .ini file doesn't exist, make a new file */ 622 | if (Key != NULL && Value != NULL) { 623 | if (!ini_openwrite(Filename, &wfp)) 624 | return 0; 625 | writesection(LocalBuffer, Section, &wfp); 626 | writekey(LocalBuffer, Key, Value, &wfp); 627 | (void)ini_close(&wfp); 628 | } /* if */ 629 | return 1; 630 | } /* if */ 631 | 632 | /* If parameters Key and Value are valid (so this is not an "erase" request) 633 | * and the setting already exists and it already has the correct value, do 634 | * nothing. This early bail-out avoids rewriting the INI file for no reason. 635 | */ 636 | if (Key != NULL && Value != NULL) { 637 | (void)ini_tell(&rfp, &mark); 638 | match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer)); 639 | if (match && _tcscmp(LocalBuffer,Value) == 0) { 640 | (void)ini_close(&rfp); 641 | return 1; 642 | } /* if */ 643 | /* key not found, or different value -> proceed (but rewind the input file first) */ 644 | (void)ini_seek(&rfp, &mark); 645 | } /* if */ 646 | 647 | /* Get a temporary file name to copy to. Use the existing name, but with 648 | * the last character set to a '~'. 649 | */ 650 | ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); 651 | if (!ini_openwrite(LocalBuffer, &wfp)) { 652 | (void)ini_close(&rfp); 653 | return 0; 654 | } /* if */ 655 | (void)ini_tell(&rfp, &mark); 656 | cachelen = 0; 657 | 658 | /* Move through the file one line at a time until a section is 659 | * matched or until EOF. Copy to temp file as it is read. 660 | */ 661 | len = (Section != NULL) ? _tcslen(Section) : 0; 662 | if (len > 0) { 663 | do { 664 | if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { 665 | /* Failed to find section, so add one to the end */ 666 | flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); 667 | if (Key!=NULL && Value!=NULL) { 668 | if (!flag) 669 | (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ 670 | writesection(LocalBuffer, Section, &wfp); 671 | writekey(LocalBuffer, Key, Value, &wfp); 672 | } /* if */ 673 | return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ 674 | } /* if */ 675 | /* Copy the line from source to dest, but not if this is the section that 676 | * we are looking for and this section must be removed 677 | */ 678 | sp = skipleading(LocalBuffer); 679 | ep = _tcschr(sp, ']'); 680 | match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0); 681 | if (!match || Key != NULL) { 682 | if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { 683 | cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); 684 | (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); 685 | cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); 686 | } /* if */ 687 | } /* if */ 688 | } while (!match); 689 | } /* if */ 690 | cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); 691 | /* when deleting a section, the section head that was just found has not been 692 | * copied to the output file, but because this line was not "accumulated" in 693 | * the cache, the position in the input file was reset to the point just 694 | * before the section; this must now be skipped (again) 695 | */ 696 | if (Key == NULL) { 697 | (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); 698 | (void)ini_tell(&rfp, &mark); 699 | } /* if */ 700 | 701 | /* Now that the section has been found, find the entry. Stop searching 702 | * upon leaving the section's area. Copy the file as it is read 703 | * and create an entry if one is not found. 704 | */ 705 | len = (Key!=NULL) ? _tcslen(Key) : 0; 706 | for( ;; ) { 707 | if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { 708 | /* EOF without an entry so make one */ 709 | flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); 710 | if (Key!=NULL && Value!=NULL) { 711 | if (!flag) 712 | (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ 713 | writekey(LocalBuffer, Key, Value, &wfp); 714 | } /* if */ 715 | return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ 716 | } /* if */ 717 | sp = skipleading(LocalBuffer); 718 | ep = _tcschr(sp, '='); /* Parse out the equal sign */ 719 | if (ep == NULL) 720 | ep = _tcschr(sp, ':'); 721 | match = (ep != NULL && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0); 722 | if ((Key != NULL && match) || *sp == '[') 723 | break; /* found the key, or found a new section */ 724 | /* copy other keys in the section */ 725 | if (Key == NULL) { 726 | (void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */ 727 | } else { 728 | if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { 729 | cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); 730 | (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); 731 | cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); 732 | } /* if */ 733 | } /* if */ 734 | } /* for */ 735 | /* the key was found, or we just dropped on the next section (meaning that it 736 | * wasn't found); in both cases we need to write the key, but in the latter 737 | * case, we also need to write the line starting the new section after writing 738 | * the key 739 | */ 740 | flag = (*sp == '['); 741 | cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); 742 | if (Key != NULL && Value != NULL) 743 | writekey(LocalBuffer, Key, Value, &wfp); 744 | /* cache_flush() reset the "read pointer" to the start of the line with the 745 | * previous key or the new section; read it again (because writekey() destroyed 746 | * the buffer) 747 | */ 748 | (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); 749 | if (flag) { 750 | /* the new section heading needs to be copied to the output file */ 751 | cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); 752 | } else { 753 | /* forget the old key line */ 754 | (void)ini_tell(&rfp, &mark); 755 | } /* if */ 756 | /* Copy the rest of the INI file */ 757 | while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { 758 | if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { 759 | cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); 760 | (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); 761 | cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); 762 | } /* if */ 763 | } /* while */ 764 | cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); 765 | return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ 766 | } 767 | 768 | /* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */ 769 | #define ABS(v) ((v) < 0 ? -(v) : (v)) 770 | 771 | static void strreverse(TCHAR *str) 772 | { 773 | TCHAR t; 774 | int i, j; 775 | 776 | for (i = 0, j = _tcslen(str) - 1; i < j; i++, j--) { 777 | t = str[i]; 778 | str[i] = str[j]; 779 | str[j] = t; 780 | } /* for */ 781 | } 782 | 783 | static void long2str(long value, TCHAR *str) 784 | { 785 | int i = 0; 786 | long sign = value; 787 | int n; 788 | 789 | /* generate digits in reverse order */ 790 | do { 791 | n = (int)(value % 10); /* get next lowest digit */ 792 | str[i++] = (TCHAR)(ABS(n) + '0'); /* handle case of negative digit */ 793 | } while (value /= 10); /* delete the lowest digit */ 794 | if (sign < 0) 795 | str[i++] = '-'; 796 | str[i] = '\0'; 797 | 798 | strreverse(str); 799 | } 800 | 801 | /** ini_putl() 802 | * \param Section the name of the section to write the value in 803 | * \param Key the name of the entry to write 804 | * \param Value the value to write 805 | * \param Filename the name and full path of the .ini file to write to 806 | * 807 | * \return 1 if successful, otherwise 0 808 | */ 809 | int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename) 810 | { 811 | TCHAR LocalBuffer[32]; 812 | long2str(Value, LocalBuffer); 813 | return ini_puts(Section, Key, LocalBuffer, Filename); 814 | } 815 | 816 | #if defined INI_REAL 817 | /** ini_putf() 818 | * \param Section the name of the section to write the value in 819 | * \param Key the name of the entry to write 820 | * \param Value the value to write 821 | * \param Filename the name and full path of the .ini file to write to 822 | * 823 | * \return 1 if successful, otherwise 0 824 | */ 825 | int ini_putf(const TCHAR *Section, const TCHAR *Key, INI_REAL Value, const TCHAR *Filename) 826 | { 827 | TCHAR LocalBuffer[64]; 828 | ini_ftoa(LocalBuffer, Value); 829 | return ini_puts(Section, Key, LocalBuffer, Filename); 830 | } 831 | #endif /* INI_REAL */ 832 | #endif /* !INI_READONLY */ 833 | -------------------------------------------------------------------------------- /main/minIni/minIni.h: -------------------------------------------------------------------------------- 1 | /* minIni - Multi-Platform INI file parser, suitable for embedded systems 2 | * 3 | * Copyright (c) CompuPhase, 2008-2012 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | * use this file except in compliance with the License. You may obtain a copy 7 | * of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations 15 | * under the License. 16 | * 17 | * Version: $Id: minIni.h 44 2012-01-04 15:52:56Z thiadmer.riemersma@gmail.com $ 18 | */ 19 | #ifndef MININI_H 20 | #define MININI_H 21 | 22 | #include "minGlue.h" 23 | 24 | #if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined MININI_ANSI 25 | #include 26 | #define mTCHAR TCHAR 27 | #else 28 | /* force TCHAR to be "char", but only for minIni */ 29 | #define mTCHAR char 30 | #endif 31 | 32 | #if !defined INI_BUFFERSIZE 33 | #define INI_BUFFERSIZE 512 34 | #endif 35 | 36 | #if defined __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const mTCHAR *Filename); 41 | long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const mTCHAR *Filename); 42 | int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); 43 | int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); 44 | int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); 45 | 46 | #if defined INI_REAL 47 | INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const mTCHAR *Filename); 48 | #endif 49 | 50 | #if !defined INI_READONLY 51 | int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const mTCHAR *Filename); 52 | int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const mTCHAR *Filename); 53 | #if defined INI_REAL 54 | int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const mTCHAR *Filename); 55 | #endif 56 | #endif /* INI_READONLY */ 57 | 58 | #if !defined INI_NOBROWSE 59 | typedef int (*INI_CALLBACK)(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const void *UserData); 60 | int ini_browse(INI_CALLBACK Callback, const void *UserData, const mTCHAR *Filename); 61 | #endif /* INI_NOBROWSE */ 62 | 63 | #if defined __cplusplus 64 | } 65 | #endif 66 | 67 | 68 | #if defined __cplusplus 69 | 70 | #if defined __WXWINDOWS__ 71 | #include "wxMinIni.h" 72 | #else 73 | #include 74 | 75 | /* The C++ class in minIni.h was contributed by Steven Van Ingelgem. */ 76 | class minIni 77 | { 78 | public: 79 | minIni(const std::string& filename) : iniFilename(filename) 80 | { } 81 | 82 | bool getbool(const std::string& Section, const std::string& Key, bool DefValue=false) const 83 | { return ini_getbool(Section.c_str(), Key.c_str(), int(DefValue), iniFilename.c_str()) != 0; } 84 | 85 | long getl(const std::string& Section, const std::string& Key, long DefValue=0) const 86 | { return ini_getl(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } 87 | 88 | int geti(const std::string& Section, const std::string& Key, int DefValue=0) const 89 | { return static_cast(this->getl(Section, Key, long(DefValue))); } 90 | 91 | std::string gets(const std::string& Section, const std::string& Key, const std::string& DefValue="") const 92 | { 93 | char buffer[INI_BUFFERSIZE]; 94 | ini_gets(Section.c_str(), Key.c_str(), DefValue.c_str(), buffer, INI_BUFFERSIZE, iniFilename.c_str()); 95 | return buffer; 96 | } 97 | 98 | std::string getsection(int idx) const 99 | { 100 | char buffer[INI_BUFFERSIZE]; 101 | ini_getsection(idx, buffer, INI_BUFFERSIZE, iniFilename.c_str()); 102 | return buffer; 103 | } 104 | 105 | std::string getkey(const std::string& Section, int idx) const 106 | { 107 | char buffer[INI_BUFFERSIZE]; 108 | ini_getkey(Section.c_str(), idx, buffer, INI_BUFFERSIZE, iniFilename.c_str()); 109 | return buffer; 110 | } 111 | 112 | #if defined INI_REAL 113 | INI_REAL getf(const std::string& Section, const std::string& Key, INI_REAL DefValue=0) const 114 | { return ini_getf(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } 115 | #endif 116 | 117 | #if ! defined INI_READONLY 118 | bool put(const std::string& Section, const std::string& Key, long Value) const 119 | { return ini_putl(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } 120 | 121 | bool put(const std::string& Section, const std::string& Key, int Value) const 122 | { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } 123 | 124 | bool put(const std::string& Section, const std::string& Key, bool Value) const 125 | { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } 126 | 127 | bool put(const std::string& Section, const std::string& Key, const std::string& Value) const 128 | { return ini_puts(Section.c_str(), Key.c_str(), Value.c_str(), iniFilename.c_str()) != 0; } 129 | 130 | bool put(const std::string& Section, const std::string& Key, const char* Value) const 131 | { return ini_puts(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } 132 | 133 | #if defined INI_REAL 134 | bool put(const std::string& Section, const std::string& Key, INI_REAL Value) const 135 | { return ini_putf(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } 136 | #endif 137 | 138 | bool del(const std::string& Section, const std::string& Key) const 139 | { return ini_puts(Section.c_str(), Key.c_str(), 0, iniFilename.c_str()) != 0; } 140 | 141 | bool del(const std::string& Section) const 142 | { return ini_puts(Section.c_str(), 0, 0, iniFilename.c_str()) != 0; } 143 | #endif 144 | 145 | private: 146 | std::string iniFilename; 147 | }; 148 | 149 | #endif /* __WXWINDOWS__ */ 150 | #endif /* __cplusplus */ 151 | 152 | #endif /* MININI_H */ 153 | -------------------------------------------------------------------------------- /main/odroid-go-common/component.mk: -------------------------------------------------------------------------------- 1 | CFLAGS := -Ofast -mlongcalls -Wno-error 2 | -------------------------------------------------------------------------------- /main/odroid-go-common/hourglass_empty_black_48dp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* GIMP RGBA C-Source image dump (hourglass_empty_black_48dp.c) */ 3 | 4 | static const struct { 5 | unsigned int width; 6 | unsigned int height; 7 | unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ 8 | unsigned char pixel_data[48 * 48 * 2 + 1]; 9 | } image_hourglass_empty_black_48dp = { 10 | 48, 48, 2, 11 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 12 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 13 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 14 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 15 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 16 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 17 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 18 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 19 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 20 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 21 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 22 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 23 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 24 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 25 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 26 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 27 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 28 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 29 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 30 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 31 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 32 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 33 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 34 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 35 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 36 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 37 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 38 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 39 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 40 | "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377" 41 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 42 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 43 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 44 | "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377" 45 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 46 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 47 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 48 | "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377" 49 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 50 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 51 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 52 | "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377" 53 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 54 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 55 | "\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 56 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 57 | "\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 58 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 59 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0" 60 | "\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 61 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0" 62 | "\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 63 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 64 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0" 65 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 66 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0" 67 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 68 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 69 | "\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377" 70 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 71 | "\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377" 72 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 73 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 74 | "\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377" 75 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 76 | "\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377" 77 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 78 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 79 | "\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\357{\377\377\377\377\377" 80 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 81 | "\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377" 82 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 83 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 84 | "\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377" 85 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\357{\0\0" 86 | "\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 87 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 88 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 89 | "\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377" 90 | "\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377" 91 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 92 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 93 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0" 94 | "\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 95 | "\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377" 96 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 97 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 98 | "\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377" 99 | "\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377" 100 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 101 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 102 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 103 | "\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377" 104 | "\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377" 105 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 106 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 107 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 108 | "\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\357{\0\0\0\0\0\0\0\0\357" 109 | "{\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 110 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 111 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 112 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\357{\0\0" 113 | "\0\0\0\0\0\0\357{\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377" 114 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 115 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 116 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 117 | "\377\377\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\0\0" 118 | "\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 119 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 120 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 121 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 122 | "\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\0\0\0\0\357" 123 | "{\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 124 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 125 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 126 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 127 | "\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\0\0\0\0\357{\377\377" 128 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 129 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 130 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 131 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 132 | "\377\377\377\377\357{\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\357{\377\377\377\377" 133 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 134 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 135 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 136 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\357{\0\0" 137 | "\0\0\0\0\0\0\357{\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377" 138 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 139 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 140 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 141 | "\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377" 142 | "\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377" 143 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 144 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 145 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 146 | "\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\357{\0\0\0\0\0" 147 | "\0\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 148 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 149 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 150 | "\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377" 151 | "\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377" 152 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 153 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 154 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 155 | "\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377" 156 | "\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377" 157 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 158 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 159 | "\377\377\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0\357" 160 | "{\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 161 | "\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377" 162 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 163 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 164 | "\377\377\377\377\357{\0\0\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377" 165 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\357{\0\0" 166 | "\0\0\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 167 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 168 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0" 169 | "\0\0\0\0\357{\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 170 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\357{\0\0\0\0\0\0\0\0" 171 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 172 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 173 | "\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377" 174 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 175 | "\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377" 176 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 177 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 178 | "\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377" 179 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 180 | "\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377" 181 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 182 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 183 | "\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377\377\377" 184 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 185 | "\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377\377\377" 186 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 187 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 188 | "\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377" 189 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 190 | "\377\377\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377" 191 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 192 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 193 | "\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377" 194 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 195 | "\377\377\377\377\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377" 196 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 197 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 198 | "\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 199 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377" 200 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 201 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 202 | "\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 203 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377" 204 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 205 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 206 | "\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 207 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377" 208 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 209 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 210 | "\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 211 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377" 212 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 213 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 214 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 215 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 216 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 217 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 218 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 219 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 220 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 221 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 222 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 223 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 224 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 225 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 226 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 227 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 228 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 229 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 230 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 231 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 232 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 233 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 234 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 235 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 236 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 237 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 238 | "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" 239 | "\377\377\377\377\377\377\377\377", 240 | }; 241 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_audio.c: -------------------------------------------------------------------------------- 1 | #include "odroid_audio.h" 2 | 3 | 4 | #include "freertos/FreeRTOS.h" 5 | #include "esp_system.h" 6 | #include "driver/i2s.h" 7 | #include "driver/rtc_io.h" 8 | 9 | 10 | 11 | #define I2S_NUM (I2S_NUM_0) 12 | 13 | static int AudioSink = ODROID_AUDIO_SINK_SPEAKER; 14 | static float Volume = 1.0f; 15 | static odroid_volume_level volumeLevel = ODROID_VOLUME_LEVEL3; 16 | static int volumeLevels[] = {0, 20, 40, 80, 140}; 17 | static int volumeLevels_dac[] = {0, 25, 40, 80, 120}; 18 | 19 | static int audio_sample_rate; 20 | 21 | 22 | odroid_volume_level odroid_audio_volume_get() 23 | { 24 | return volumeLevel; 25 | } 26 | 27 | void odroid_audio_volume_set(odroid_volume_level value) 28 | { 29 | if (value >= ODROID_VOLUME_LEVEL_COUNT) 30 | { 31 | printf("odroid_audio_volume_set: value out of range (%d)\n", value); 32 | abort(); 33 | } 34 | 35 | volumeLevel = value; 36 | if(AudioSink == ODROID_AUDIO_SINK_SPEAKER) 37 | Volume = (float)volumeLevels[value] * 0.001f; 38 | else 39 | Volume = (float)volumeLevels_dac[value] * 0.001f; 40 | } 41 | 42 | void odroid_audio_volume_change() 43 | { 44 | int level = (volumeLevel + 1) % ODROID_VOLUME_LEVEL_COUNT; 45 | odroid_audio_volume_set(level); 46 | 47 | 48 | odroid_settings_Volume_set(level); 49 | 50 | } 51 | 52 | void odroid_audio_init(ODROID_AUDIO_SINK sink, int sample_rate) 53 | { 54 | printf("%s: sink=%d, sample_rate=%d\n", __func__, sink, sample_rate); 55 | 56 | AudioSink = sink; 57 | audio_sample_rate = sample_rate; 58 | 59 | // NOTE: buffer needs to be adjusted per AUDIO_SAMPLE_RATE 60 | if(AudioSink == ODROID_AUDIO_SINK_SPEAKER) 61 | { 62 | i2s_config_t i2s_config = { 63 | //.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX 64 | .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, 65 | .sample_rate = audio_sample_rate, 66 | .bits_per_sample = 16, 67 | .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels 68 | .communication_format = I2S_COMM_FORMAT_I2S_MSB, 69 | //.communication_format = I2S_COMM_FORMAT_PCM, 70 | .dma_buf_count = 6, 71 | //.dma_buf_len = 1472 / 2, // (368samples * 2ch * 2(short)) = 1472 72 | .dma_buf_len = 512, // (416samples * 2ch * 2(short)) = 1664 73 | .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, //Interrupt level 1 74 | .use_apll = 0 //1 75 | }; 76 | 77 | i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); 78 | 79 | i2s_set_pin(I2S_NUM, NULL); 80 | i2s_set_dac_mode(/*I2S_DAC_CHANNEL_LEFT_EN*/ I2S_DAC_CHANNEL_BOTH_EN); 81 | } 82 | else if (AudioSink == ODROID_AUDIO_SINK_DAC) 83 | { 84 | i2s_config_t i2s_config = { 85 | .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX 86 | .sample_rate = audio_sample_rate, 87 | .bits_per_sample = 16, 88 | .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels 89 | .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, 90 | .dma_buf_count = 6, 91 | //.dma_buf_len = 1472 / 2, // (368samples * 2ch * 2(short)) = 1472 92 | .dma_buf_len = 512, // (416samples * 2ch * 2(short)) = 1664 93 | .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, //Interrupt level 1 94 | .use_apll = 1 95 | }; 96 | 97 | i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); 98 | 99 | i2s_pin_config_t pin_config = { 100 | .bck_io_num = 4, 101 | .ws_io_num = 12, 102 | .data_out_num = 15, 103 | .data_in_num = -1 //Not used 104 | }; 105 | i2s_set_pin(I2S_NUM, &pin_config); 106 | 107 | 108 | // Disable internal amp 109 | esp_err_t err; 110 | 111 | err = gpio_set_direction(GPIO_NUM_25, GPIO_MODE_OUTPUT); 112 | if (err != ESP_OK) 113 | { 114 | abort(); 115 | } 116 | 117 | err = gpio_set_direction(GPIO_NUM_26, GPIO_MODE_DISABLE); 118 | if (err != ESP_OK) 119 | { 120 | abort(); 121 | } 122 | 123 | err = gpio_set_level(GPIO_NUM_25, 0); 124 | if (err != ESP_OK) 125 | { 126 | abort(); 127 | } 128 | } 129 | else 130 | { 131 | abort(); 132 | } 133 | 134 | odroid_volume_level level = odroid_settings_Volume_get(); 135 | odroid_audio_volume_set(level); 136 | } 137 | 138 | void odroid_audio_terminate() 139 | { 140 | i2s_zero_dma_buffer(I2S_NUM); 141 | i2s_stop(I2S_NUM); 142 | 143 | i2s_start(I2S_NUM); 144 | 145 | 146 | esp_err_t err = rtc_gpio_init(GPIO_NUM_25); 147 | err = rtc_gpio_init(GPIO_NUM_26); 148 | if (err != ESP_OK) 149 | { 150 | abort(); 151 | } 152 | 153 | err = rtc_gpio_set_direction(GPIO_NUM_25, RTC_GPIO_MODE_OUTPUT_ONLY); 154 | err = rtc_gpio_set_direction(GPIO_NUM_26, RTC_GPIO_MODE_OUTPUT_ONLY); 155 | if (err != ESP_OK) 156 | { 157 | abort(); 158 | } 159 | 160 | err = rtc_gpio_set_level(GPIO_NUM_25, 0); 161 | err = rtc_gpio_set_level(GPIO_NUM_26, 0); 162 | if (err != ESP_OK) 163 | { 164 | abort(); 165 | } 166 | } 167 | 168 | void odroid_audio_submit(short* stereoAudioBuffer, int frameCount) 169 | { 170 | short currentAudioSampleCount = frameCount * 2; 171 | 172 | if (AudioSink == ODROID_AUDIO_SINK_SPEAKER) 173 | { 174 | // Convert for built in DAC 175 | for (short i = 0; i < currentAudioSampleCount; i += 2) 176 | { 177 | int32_t dac0; 178 | int32_t dac1; 179 | 180 | if (Volume == 0.0f) 181 | { 182 | // Disable amplifier 183 | dac0 = 0; 184 | dac1 = 0; 185 | } 186 | else 187 | { 188 | // Down mix stero to mono 189 | int32_t sample = stereoAudioBuffer[i]; 190 | sample += stereoAudioBuffer[i + 1]; 191 | sample >>= 1; 192 | 193 | // Normalize 194 | const float sn = (float)sample / 0x8000; 195 | 196 | // Scale 197 | const int magnitude = 127 + 127; 198 | const float range = magnitude * sn * Volume; 199 | 200 | // Convert to differential output 201 | if (range > 127) 202 | { 203 | dac1 = (range - 127); 204 | dac0 = 127; 205 | } 206 | else if (range < -127) 207 | { 208 | dac1 = (range + 127); 209 | dac0 = -127; 210 | } 211 | else 212 | { 213 | dac1 = 0; 214 | dac0 = range; 215 | } 216 | 217 | dac0 += 0x80; 218 | dac1 = 0x80 - dac1; 219 | 220 | dac0 <<= 8; 221 | dac1 <<= 8; 222 | } 223 | 224 | stereoAudioBuffer[i] = (int16_t)dac1; 225 | stereoAudioBuffer[i + 1] = (int16_t)dac0; 226 | } 227 | 228 | int len = currentAudioSampleCount * sizeof(int16_t); 229 | int count = i2s_write_bytes(I2S_NUM, (const char *)stereoAudioBuffer, len, portMAX_DELAY); 230 | if (count != len) 231 | { 232 | printf("i2s_write_bytes: count (%d) != len (%d)\n", count, len); 233 | abort(); 234 | } 235 | } 236 | else if (AudioSink == ODROID_AUDIO_SINK_DAC) 237 | { 238 | int len = currentAudioSampleCount * sizeof(int16_t); 239 | 240 | for (short i = 0; i < currentAudioSampleCount; ++i) 241 | { 242 | int sample = stereoAudioBuffer[i] * Volume; 243 | 244 | if (sample > 32767) 245 | sample = 32767; 246 | else if (sample < -32768) 247 | sample = -32767; 248 | 249 | stereoAudioBuffer[i] = (short)sample; 250 | } 251 | 252 | int count = i2s_write_bytes(I2S_NUM, (const char *)stereoAudioBuffer, len, portMAX_DELAY); 253 | if (count != len) 254 | { 255 | printf("i2s_write_bytes: count (%d) != len (%d)\n", count, len); 256 | abort(); 257 | } 258 | } 259 | else 260 | { 261 | abort(); 262 | } 263 | } 264 | 265 | int odroid_audio_sample_rate_get() 266 | { 267 | return audio_sample_rate; 268 | } 269 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_audio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "odroid_settings.h" 4 | 5 | #define ODROID_VOLUME_LEVEL_COUNT (5) 6 | 7 | typedef enum 8 | { 9 | ODROID_VOLUME_LEVEL0 = 0, 10 | ODROID_VOLUME_LEVEL1 = 1, 11 | ODROID_VOLUME_LEVEL2 = 2, 12 | ODROID_VOLUME_LEVEL3 = 3, 13 | ODROID_VOLUME_LEVEL4 = 4, 14 | 15 | _ODROID_VOLUME_FILLER = 0xffffffff 16 | } odroid_volume_level; 17 | 18 | 19 | odroid_volume_level odroid_audio_volume_get(); 20 | void odroid_audio_volume_set(odroid_volume_level value); 21 | void odroid_audio_volume_change(); 22 | void odroid_audio_init(ODROID_AUDIO_SINK sink, int sample_rate); 23 | void odroid_audio_terminate(); 24 | void odroid_audio_submit(short* stereoAudioBuffer, int frameCount); 25 | int odroid_audio_sample_rate_get(); 26 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_display.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum ODROID_SD_ERR { 6 | ODROID_SD_ERR_BADFILE = 0x01, 7 | ODROID_SD_ERR_NOCARD = 0x02 8 | }; 9 | 10 | #ifndef STOP_DISPLAY_FUNCTION 11 | #define STOP_DISPLAY_FUNCTION odroid_display_stop_msx_display 12 | #endif 13 | 14 | #ifndef RESUME_DISPLAY_FUNCTION 15 | #define RESUME_DISPLAY_FUNCTION odroid_display_resume_msx_display 16 | #endif 17 | 18 | 19 | void ili9341_write_frame_gb(uint16_t* buffer, int scale); 20 | void ili9341_init(); 21 | void ili9341_poweroff(); 22 | void ili9341_prepare(); 23 | void send_reset_drawing(int left, int top, int width, int height); 24 | void send_continue_wait(); 25 | void send_continue_line(uint16_t *line, int width, int lineCount); 26 | 27 | void ili9341_write_frame_sms(uint8_t* buffer, uint16_t color[], uint8_t isGameGear, uint8_t scale); 28 | void ili9341_write_frame_nes(uint8_t* buffer, uint16_t* myPalette, uint8_t scale); 29 | void ili9341_write_frame_msx(short left, short top, short width, short height, uint8_t* buffer, uint16_t bgColor, uint16_t* palette); 30 | 31 | void backlight_percentage_set(int value); 32 | //void ili9341_write_frame(uint16_t* buffer); 33 | void ili9341_write_frame_rectangle(short left, short top, short width, short height, uint16_t* buffer); 34 | void ili9341_clear(uint16_t color); 35 | void ili9341_write_frame_rectangleLE(short left, short top, short width, short height, uint16_t* buffer); 36 | 37 | void display_tasktonotify_set(int value); 38 | 39 | int is_backlight_initialized(); 40 | void odroid_display_show_splash(); 41 | void odroid_display_drain_spi(); 42 | void odroid_display_lock_gb_display(); 43 | void odroid_display_unlock_gb_display(); 44 | void odroid_display_show_sderr(int errNum); 45 | void odroid_display_show_hourglass(); 46 | void odroid_display_lock_nes_display(); 47 | void odroid_display_unlock_nes_display(); 48 | void odroid_display_lock_sms_display(); 49 | void odroid_display_unlock_sms_display(); 50 | void odroid_display_lock_msx_display(); 51 | void odroid_display_unlock_msx_display(); 52 | void odroid_display_stop_msx_display(); 53 | void odroid_display_resume_msx_display(); -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_input.c: -------------------------------------------------------------------------------- 1 | #include "odroid_input.h" 2 | #include "odroid_settings.h" 3 | #include "odroid_system.h" 4 | //#include "odroid_display.h" 5 | #include "driver/ledc.h" 6 | 7 | #include "driver/i2c.h" 8 | #include "driver/gpio.h" 9 | #include 10 | #include "esp_adc_cal.h" 11 | #include "freertos/FreeRTOS.h" 12 | 13 | #include 14 | 15 | //extern void backlight_percentage_set(int value); 16 | 17 | static volatile bool input_task_is_running = false; 18 | static volatile odroid_gamepad_state gamepad_state; 19 | static odroid_gamepad_state previous_gamepad_state; 20 | static uint8_t debounce[ODROID_INPUT_MAX]; 21 | static volatile bool input_gamepad_initialized = false; 22 | static SemaphoreHandle_t xSemaphore; 23 | 24 | static esp_adc_cal_characteristics_t characteristics; 25 | static bool input_battery_initialized = false; 26 | static float adc_value = 0.0f; 27 | static float forced_adc_value = 0.0f; 28 | static bool battery_monitor_enabled = true; 29 | 30 | #define BACKLIGHT_LEVEL_COUNT (4) 31 | static int BacklightLevels[BACKLIGHT_LEVEL_COUNT] = {10, 33, 66, 100}; 32 | static int BacklightLevel = BACKLIGHT_LEVEL_COUNT - 1; 33 | 34 | int is_backlight_initialized(); 35 | 36 | odroid_gamepad_state odroid_input_read_raw() 37 | { 38 | odroid_gamepad_state state = {0}; 39 | 40 | int joyX = adc1_get_raw(ODROID_GAMEPAD_IO_X); 41 | int joyY = adc1_get_raw(ODROID_GAMEPAD_IO_Y); 42 | 43 | if (joyX > 2048 + 1024) 44 | { 45 | state.values[ODROID_INPUT_LEFT] = 1; 46 | state.values[ODROID_INPUT_RIGHT] = 0; 47 | } 48 | else if (joyX > 1024) 49 | { 50 | state.values[ODROID_INPUT_LEFT] = 0; 51 | state.values[ODROID_INPUT_RIGHT] = 1; 52 | } 53 | else 54 | { 55 | state.values[ODROID_INPUT_LEFT] = 0; 56 | state.values[ODROID_INPUT_RIGHT] = 0; 57 | } 58 | 59 | if (joyY > 2048 + 1024) 60 | { 61 | state.values[ODROID_INPUT_UP] = 1; 62 | state.values[ODROID_INPUT_DOWN] = 0; 63 | } 64 | else if (joyY > 1024) 65 | { 66 | state.values[ODROID_INPUT_UP] = 0; 67 | state.values[ODROID_INPUT_DOWN] = 1; 68 | } 69 | else 70 | { 71 | state.values[ODROID_INPUT_UP] = 0; 72 | state.values[ODROID_INPUT_DOWN] = 0; 73 | } 74 | 75 | state.values[ODROID_INPUT_SELECT] = !(gpio_get_level(ODROID_GAMEPAD_IO_SELECT)); 76 | state.values[ODROID_INPUT_START] = !(gpio_get_level(ODROID_GAMEPAD_IO_START)); 77 | 78 | state.values[ODROID_INPUT_A] = !(gpio_get_level(ODROID_GAMEPAD_IO_A)); 79 | state.values[ODROID_INPUT_B] = !(gpio_get_level(ODROID_GAMEPAD_IO_B)); 80 | 81 | state.values[ODROID_INPUT_MENU] = !(gpio_get_level(ODROID_GAMEPAD_IO_MENU)); 82 | state.values[ODROID_INPUT_VOLUME] = !(gpio_get_level(ODROID_GAMEPAD_IO_VOLUME)); 83 | 84 | return state; 85 | } 86 | 87 | static void odroid_input_task(void *arg) 88 | { 89 | input_task_is_running = true; 90 | 91 | // Initialize state 92 | for(int i = 0; i < ODROID_INPUT_MAX; ++i) 93 | { 94 | debounce[i] = 0xff; 95 | } 96 | 97 | BacklightLevel = odroid_settings_Backlight_get(); 98 | bool changed = false; 99 | 100 | while(input_task_is_running) 101 | { 102 | // Shift current values 103 | for(int i = 0; i < ODROID_INPUT_MAX; ++i) 104 | { 105 | debounce[i] <<= 1; 106 | } 107 | 108 | 109 | // Read hardware 110 | #if 1 111 | 112 | odroid_gamepad_state state = odroid_input_read_raw(); 113 | 114 | #else 115 | odroid_gamepad_state state = {0}; 116 | 117 | int joyX = adc1_get_raw(ODROID_GAMEPAD_IO_X); 118 | int joyY = adc1_get_raw(ODROID_GAMEPAD_IO_Y); 119 | 120 | if (joyX > 2048 + 1024) 121 | { 122 | state.values[ODROID_INPUT_LEFT] = 1; 123 | state.values[ODROID_INPUT_RIGHT] = 0; 124 | } 125 | else if (joyX > 1024) 126 | { 127 | state.values[ODROID_INPUT_LEFT] = 0; 128 | state.values[ODROID_INPUT_RIGHT] = 1; 129 | } 130 | else 131 | { 132 | state.values[ODROID_INPUT_LEFT] = 0; 133 | state.values[ODROID_INPUT_RIGHT] = 0; 134 | } 135 | 136 | if (joyY > 2048 + 1024) 137 | { 138 | state.values[ODROID_INPUT_UP] = 1; 139 | state.values[ODROID_INPUT_DOWN] = 0; 140 | } 141 | else if (joyY > 1024) 142 | { 143 | state.values[ODROID_INPUT_UP] = 0; 144 | state.values[ODROID_INPUT_DOWN] = 1; 145 | } 146 | else 147 | { 148 | state.values[ODROID_INPUT_UP] = 0; 149 | state.values[ODROID_INPUT_DOWN] = 0; 150 | } 151 | 152 | state.values[ODROID_INPUT_SELECT] = !(gpio_get_level(ODROID_GAMEPAD_IO_SELECT)); 153 | state.values[ODROID_INPUT_START] = !(gpio_get_level(ODROID_GAMEPAD_IO_START)); 154 | 155 | state.values[ODROID_INPUT_A] = !(gpio_get_level(ODROID_GAMEPAD_IO_A)); 156 | state.values[ODROID_INPUT_B] = !(gpio_get_level(ODROID_GAMEPAD_IO_B)); 157 | 158 | state.values[ODROID_INPUT_MENU] = !(gpio_get_level(ODROID_GAMEPAD_IO_MENU)); 159 | state.values[ODROID_INPUT_VOLUME] = !(gpio_get_level(ODROID_GAMEPAD_IO_VOLUME)); 160 | #endif 161 | 162 | // Debounce 163 | xSemaphoreTake(xSemaphore, portMAX_DELAY); 164 | 165 | for(int i = 0; i < ODROID_INPUT_MAX; ++i) 166 | { 167 | debounce[i] |= state.values[i] ? 1 : 0; 168 | uint8_t val = debounce[i] & 0x03; //0x0f; 169 | switch (val) { 170 | case 0x00: 171 | gamepad_state.values[i] = 0; 172 | break; 173 | 174 | case 0x03: //0x0f: 175 | gamepad_state.values[i] = 1; 176 | break; 177 | 178 | default: 179 | // ignore 180 | break; 181 | } 182 | 183 | //gamepad_state.values[i] = (debounce[i] == 0xff); 184 | //printf("odroid_input_task: %d=%d (raw=%d)\n", i, gamepad_state.values[i], state.values[i]); 185 | } 186 | 187 | if (gamepad_state.values[ODROID_INPUT_START]) 188 | { 189 | 190 | if (gamepad_state.values[ODROID_INPUT_DOWN] && !previous_gamepad_state.values[ODROID_INPUT_DOWN]) 191 | { 192 | --BacklightLevel; 193 | if (BacklightLevel < 0) BacklightLevel = 0; 194 | 195 | changed = true; 196 | odroid_settings_Backlight_set(BacklightLevel); 197 | } 198 | else if (gamepad_state.values[ODROID_INPUT_UP] && !previous_gamepad_state.values[ODROID_INPUT_UP]) 199 | { 200 | ++BacklightLevel; 201 | if (BacklightLevel >= BACKLIGHT_LEVEL_COUNT) BacklightLevel = BACKLIGHT_LEVEL_COUNT - 1; 202 | 203 | changed = true; 204 | odroid_settings_Backlight_set(BacklightLevel); 205 | } 206 | 207 | 208 | } 209 | 210 | if (is_backlight_initialized()) 211 | { 212 | const int DUTY_MAX = 0x1fff; 213 | int duty = DUTY_MAX * (BacklightLevels[BacklightLevel] * 0.01f); 214 | 215 | uint32_t currentDuty = ledc_get_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); 216 | if (currentDuty != duty) 217 | { 218 | changed = true; 219 | } 220 | 221 | if (changed) 222 | { 223 | //backlight_percentage_set(BacklightLevels[BacklightLevel]); 224 | 225 | 226 | ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty, 1); 227 | ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE /*LEDC_FADE_NO_WAIT*/); 228 | 229 | //ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty); 230 | 231 | changed = false; 232 | } 233 | } 234 | 235 | previous_gamepad_state = gamepad_state; 236 | 237 | xSemaphoreGive(xSemaphore); 238 | 239 | 240 | // delay 241 | vTaskDelay(10 / portTICK_PERIOD_MS); 242 | } 243 | 244 | input_gamepad_initialized = false; 245 | 246 | vSemaphoreDelete(xSemaphore); 247 | 248 | // Remove the task from scheduler 249 | vTaskDelete(NULL); 250 | 251 | // Never return 252 | while (1) { vTaskDelay(1);} 253 | } 254 | 255 | void odroid_input_gamepad_init() 256 | { 257 | //printf("portTICK_PERIOD_MS = %d\n", portTICK_PERIOD_MS); 258 | 259 | xSemaphore = xSemaphoreCreateMutex(); 260 | 261 | if(xSemaphore == NULL) 262 | { 263 | printf("xSemaphoreCreateMutex failed.\n"); 264 | abort(); 265 | } 266 | 267 | gpio_set_direction(ODROID_GAMEPAD_IO_SELECT, GPIO_MODE_INPUT); 268 | gpio_set_pull_mode(ODROID_GAMEPAD_IO_SELECT, GPIO_PULLUP_ONLY); 269 | 270 | gpio_set_direction(ODROID_GAMEPAD_IO_START, GPIO_MODE_INPUT); 271 | 272 | gpio_set_direction(ODROID_GAMEPAD_IO_A, GPIO_MODE_INPUT); 273 | gpio_set_pull_mode(ODROID_GAMEPAD_IO_A, GPIO_PULLUP_ONLY); 274 | 275 | gpio_set_direction(ODROID_GAMEPAD_IO_B, GPIO_MODE_INPUT); 276 | gpio_set_pull_mode(ODROID_GAMEPAD_IO_B, GPIO_PULLUP_ONLY); 277 | 278 | adc1_config_width(ADC_WIDTH_12Bit); 279 | adc1_config_channel_atten(ODROID_GAMEPAD_IO_X, ADC_ATTEN_11db); 280 | adc1_config_channel_atten(ODROID_GAMEPAD_IO_Y, ADC_ATTEN_11db); 281 | 282 | gpio_set_direction(ODROID_GAMEPAD_IO_MENU, GPIO_MODE_INPUT); 283 | gpio_set_pull_mode(ODROID_GAMEPAD_IO_MENU, GPIO_PULLUP_ONLY); 284 | 285 | gpio_set_direction(ODROID_GAMEPAD_IO_VOLUME, GPIO_MODE_INPUT); 286 | 287 | input_gamepad_initialized = true; 288 | 289 | // Start background polling 290 | xTaskCreatePinnedToCore(&odroid_input_task, "odroid_input_task", 1024 * 2, NULL, 5, NULL, 1); 291 | 292 | printf("odroid_input_gamepad_init done.\n"); 293 | 294 | } 295 | 296 | void odroid_input_gamepad_terminate() 297 | { 298 | if (!input_gamepad_initialized) abort(); 299 | 300 | input_task_is_running = false; 301 | } 302 | 303 | void odroid_input_gamepad_read(odroid_gamepad_state* out_state) 304 | { 305 | if (!input_gamepad_initialized) abort(); 306 | 307 | xSemaphoreTake(xSemaphore, portMAX_DELAY); 308 | 309 | *out_state = gamepad_state; 310 | 311 | xSemaphoreGive(xSemaphore); 312 | } 313 | 314 | 315 | static void odroid_battery_monitor_task() 316 | { 317 | bool led_state = false; 318 | 319 | while(true) 320 | { 321 | if (battery_monitor_enabled) 322 | { 323 | odroid_battery_state battery; 324 | odroid_input_battery_level_read(&battery); 325 | 326 | if (battery.percentage < 2) 327 | { 328 | led_state = !led_state; 329 | odroid_system_led_set(led_state); 330 | } 331 | else if(led_state) 332 | { 333 | led_state = 0; 334 | odroid_system_led_set(led_state); 335 | } 336 | } 337 | 338 | vTaskDelay(500 / portTICK_PERIOD_MS); 339 | } 340 | } 341 | 342 | 343 | static void print_char_val_type(esp_adc_cal_value_t val_type) 344 | { 345 | if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { 346 | printf("ADC: Characterized using Two Point Value\n"); 347 | } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { 348 | printf("ADC: Characterized using eFuse Vref\n"); 349 | } else { 350 | printf("ADC: Characterized using Default Vref\n"); 351 | } 352 | } 353 | 354 | #define DEFAULT_VREF 1100 355 | void odroid_input_battery_level_init() 356 | { 357 | adc1_config_width(ADC_WIDTH_12Bit); 358 | adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_11db); 359 | 360 | //int vref_value = odroid_settings_VRef_get(); 361 | //esp_adc_cal_get_characteristics(vref_value, ADC_ATTEN_11db, ADC_WIDTH_12Bit, &characteristics); 362 | 363 | //Characterize ADC 364 | //adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t)); 365 | esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_11db, ADC_WIDTH_BIT_12, DEFAULT_VREF, &characteristics); 366 | print_char_val_type(val_type); 367 | 368 | input_battery_initialized = true; 369 | xTaskCreatePinnedToCore(&odroid_battery_monitor_task, "battery_monitor", 1024, NULL, 5, NULL, 1); 370 | } 371 | 372 | void odroid_input_battery_level_read(odroid_battery_state* out_state) 373 | { 374 | if (!input_battery_initialized) 375 | { 376 | printf("odroid_input_battery_level_read: not initilized.\n"); 377 | abort(); 378 | } 379 | 380 | 381 | const int sampleCount = 4; 382 | 383 | float adcSample = 0.0f; 384 | for (int i = 0; i < sampleCount; ++i) 385 | { 386 | //adcSample += adc1_to_voltage(ADC1_CHANNEL_0, &characteristics) * 0.001f; 387 | adcSample += esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC1_CHANNEL_0), &characteristics) * 0.001f; 388 | } 389 | adcSample /= sampleCount; 390 | 391 | 392 | if (adc_value == 0.0f) 393 | { 394 | adc_value = adcSample; 395 | } 396 | else 397 | { 398 | adc_value += adcSample; 399 | adc_value /= 2.0f; 400 | } 401 | 402 | 403 | // Vo = (Vs * R2) / (R1 + R2) 404 | // Vs = Vo / R2 * (R1 + R2) 405 | const float R1 = 10000; 406 | const float R2 = 10000; 407 | const float Vo = adc_value; 408 | const float Vs = (forced_adc_value > 0.0f) ? (forced_adc_value) : (Vo / R2 * (R1 + R2)); 409 | 410 | const float FullVoltage = 4.2f; 411 | const float EmptyVoltage = 3.5f; 412 | 413 | out_state->millivolts = (int)(Vs * 1000); 414 | out_state->percentage = (int)((Vs - EmptyVoltage) / (FullVoltage - EmptyVoltage) * 100.0f); 415 | } 416 | 417 | void odroid_input_battery_level_force_voltage(float volts) 418 | { 419 | forced_adc_value = volts; 420 | } 421 | 422 | void odroid_input_battery_monitor_enabled_set(int value) 423 | { 424 | battery_monitor_enabled = value; 425 | } 426 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | #define ODROID_GAMEPAD_IO_X ADC1_CHANNEL_6 7 | #define ODROID_GAMEPAD_IO_Y ADC1_CHANNEL_7 8 | #define ODROID_GAMEPAD_IO_SELECT GPIO_NUM_27 9 | #define ODROID_GAMEPAD_IO_START GPIO_NUM_39 10 | #define ODROID_GAMEPAD_IO_A GPIO_NUM_32 11 | #define ODROID_GAMEPAD_IO_B GPIO_NUM_33 12 | #define ODROID_GAMEPAD_IO_MENU GPIO_NUM_13 13 | #define ODROID_GAMEPAD_IO_VOLUME GPIO_NUM_0 14 | 15 | 16 | enum 17 | { 18 | ODROID_INPUT_UP = 0, 19 | ODROID_INPUT_RIGHT, 20 | ODROID_INPUT_DOWN, 21 | ODROID_INPUT_LEFT, 22 | ODROID_INPUT_SELECT, 23 | ODROID_INPUT_START, 24 | ODROID_INPUT_A, 25 | ODROID_INPUT_B, 26 | ODROID_INPUT_MENU, 27 | ODROID_INPUT_VOLUME, 28 | 29 | ODROID_INPUT_MAX 30 | }; 31 | 32 | typedef struct 33 | { 34 | uint8_t values[ODROID_INPUT_MAX]; 35 | } odroid_gamepad_state; 36 | 37 | typedef struct 38 | { 39 | int millivolts; 40 | int percentage; 41 | } odroid_battery_state; 42 | 43 | void odroid_input_gamepad_init(); 44 | void odroid_input_gamepad_terminate(); 45 | void odroid_input_gamepad_read(odroid_gamepad_state* out_state); 46 | odroid_gamepad_state odroid_input_read_raw(); 47 | 48 | void odroid_input_battery_level_init(); 49 | void odroid_input_battery_level_read(odroid_battery_state* out_state); 50 | void odroid_input_battery_level_force_voltage(float volts); 51 | void odroid_input_battery_monitor_enabled_set(int value); 52 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_sdcard.c: -------------------------------------------------------------------------------- 1 | #include "odroid_sdcard.h" 2 | 3 | //#include "esp_err.h" 4 | #include "esp_log.h" 5 | #include "esp_vfs_fat.h" 6 | #include "driver/sdmmc_host.h" 7 | #include "driver/sdspi_host.h" 8 | #include "sdmmc_cmd.h" 9 | #include "esp_heap_caps.h" 10 | #include "esp_spiffs.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | 19 | #define SD_PIN_NUM_MISO 19 20 | #define SD_PIN_NUM_MOSI 23 21 | #define SD_PIN_NUM_CLK 18 22 | #define SD_PIN_NUM_CS 22 23 | 24 | 25 | static bool isOpen = false; 26 | 27 | 28 | esp_err_t odroid_sdcard_open(const char* base_path) 29 | { 30 | esp_err_t ret; 31 | 32 | if (isOpen) 33 | { 34 | printf("odroid_sdcard_open: alread open.\n"); 35 | ret = ESP_FAIL; 36 | } 37 | else 38 | { 39 | sdmmc_host_t host = SDSPI_HOST_DEFAULT(); 40 | host.slot = HSPI_HOST; // HSPI_HOST; 41 | //host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; //10000000; 42 | host.max_freq_khz = SDMMC_FREQ_DEFAULT; 43 | 44 | sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT(); 45 | slot_config.gpio_miso = (gpio_num_t)SD_PIN_NUM_MISO; 46 | slot_config.gpio_mosi = (gpio_num_t)SD_PIN_NUM_MOSI; 47 | slot_config.gpio_sck = (gpio_num_t)SD_PIN_NUM_CLK; 48 | slot_config.gpio_cs = (gpio_num_t)SD_PIN_NUM_CS; 49 | //slot_config.dma_channel = 2; 50 | 51 | // Options for mounting the filesystem. 52 | // If format_if_mount_failed is set to true, SD card will be partitioned and 53 | // formatted in case when mounting fails. 54 | esp_vfs_fat_sdmmc_mount_config_t mount_config; 55 | memset(&mount_config, 0, sizeof(mount_config)); 56 | 57 | mount_config.format_if_mount_failed = false; 58 | mount_config.max_files = 5; 59 | 60 | 61 | // Use settings defined above to initialize SD card and mount FAT filesystem. 62 | // Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function. 63 | // Please check its source code and implement error recovery when developing 64 | // production applications. 65 | sdmmc_card_t* card; 66 | ret = esp_vfs_fat_sdmmc_mount(base_path, &host, &slot_config, &mount_config, &card); 67 | 68 | if (ret == ESP_OK) 69 | { 70 | isOpen = true; 71 | } 72 | else 73 | { 74 | printf("odroid_sdcard_open: esp_vfs_fat_sdmmc_mount failed (%d)\n", ret); 75 | } 76 | } 77 | 78 | return ret; 79 | } 80 | 81 | 82 | esp_err_t odroid_sdcard_close() 83 | { 84 | esp_err_t ret; 85 | 86 | if (!isOpen) 87 | { 88 | printf("odroid_sdcard_close: not open.\n"); 89 | ret = ESP_FAIL; 90 | } 91 | else 92 | { 93 | ret = esp_vfs_fat_sdmmc_unmount(); 94 | 95 | if (ret != ESP_OK) 96 | { 97 | printf("odroid_sdcard_close: esp_vfs_fat_sdmmc_unmount failed (%d)\n", ret); 98 | } 99 | 100 | isOpen = false; 101 | } 102 | 103 | return ret; 104 | } 105 | 106 | 107 | size_t odroid_sdcard_get_filesize(const char* path) 108 | { 109 | size_t ret = 0; 110 | 111 | if (!isOpen) 112 | { 113 | printf("odroid_sdcard_get_filesize: not open.\n"); 114 | } 115 | else 116 | { 117 | FILE* f = fopen(path, "rb"); 118 | if (f == NULL) 119 | { 120 | printf("odroid_sdcard_get_filesize: fopen failed.\n"); 121 | } 122 | else 123 | { 124 | // get the file size 125 | fseek(f, 0, SEEK_END); 126 | ret = ftell(f); 127 | fseek(f, 0, SEEK_SET); 128 | } 129 | } 130 | 131 | return ret; 132 | } 133 | 134 | size_t odroid_sdcard_copy_file_to_memory(const char* path, void* ptr) 135 | { 136 | size_t ret = 0; 137 | 138 | if (!isOpen) 139 | { 140 | printf("odroid_sdcard_copy_file_to_memory: not open.\n"); 141 | } 142 | else 143 | { 144 | if (!ptr) 145 | { 146 | printf("odroid_sdcard_copy_file_to_memory: ptr is null.\n"); 147 | } 148 | else 149 | { 150 | FILE* f = fopen(path, "rb"); 151 | if (f == NULL) 152 | { 153 | printf("odroid_sdcard_copy_file_to_memory: fopen failed.\n"); 154 | } 155 | else 156 | { 157 | // copy 158 | const size_t BLOCK_SIZE = 512; 159 | while(true) 160 | { 161 | __asm__("memw"); 162 | size_t count = fread((uint8_t*)ptr + ret, 1, BLOCK_SIZE, f); 163 | __asm__("memw"); 164 | 165 | ret += count; 166 | 167 | if (count < BLOCK_SIZE) break; 168 | } 169 | } 170 | } 171 | } 172 | 173 | return ret; 174 | } 175 | 176 | char* odroid_sdcard_create_savefile_path(const char* base_path, const char* fileName) 177 | { 178 | char* result = NULL; 179 | 180 | if (!base_path) abort(); 181 | if (!fileName) abort(); 182 | 183 | //printf("%s: base_path='%s', fileName='%s'\n", __func__, base_path, fileName); 184 | 185 | // Determine folder 186 | char* extension = fileName + strlen(fileName); // place at NULL terminator 187 | while (extension != fileName) 188 | { 189 | if (*extension == '.') 190 | { 191 | ++extension; 192 | break; 193 | } 194 | --extension; 195 | } 196 | 197 | if (extension == fileName) 198 | { 199 | printf("%s: File extention not found.\n", __func__); 200 | abort(); 201 | } 202 | 203 | //printf("%s: extension='%s'\n", __func__, extension); 204 | 205 | const char* DATA_PATH = "/odroid/data/"; 206 | const char* SAVE_EXTENSION = ".sav"; 207 | 208 | size_t savePathLength = strlen(base_path) + strlen(DATA_PATH) + strlen(extension) + 1 + strlen(fileName) + strlen(SAVE_EXTENSION) + 1; 209 | char* savePath = malloc(savePathLength); 210 | if (savePath) 211 | { 212 | strcpy(savePath, base_path); 213 | strcat(savePath, DATA_PATH); 214 | strcat(savePath, extension); 215 | strcat(savePath, "/"); 216 | strcat(savePath, fileName); 217 | strcat(savePath, SAVE_EXTENSION); 218 | 219 | printf("%s: savefile_path='%s'\n", __func__, savePath); 220 | 221 | result = savePath; 222 | } 223 | 224 | return result; 225 | } 226 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_sdcard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_err.h" 4 | 5 | 6 | esp_err_t odroid_sdcard_open(const char* base_path); 7 | esp_err_t odroid_sdcard_close(); 8 | size_t odroid_sdcard_get_filesize(const char* path); 9 | size_t odroid_sdcard_copy_file_to_memory(const char* path, void* ptr); 10 | char* odroid_sdcard_create_savefile_path(const char* base_path, const char* fileName); 11 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_settings.c: -------------------------------------------------------------------------------- 1 | #include "odroid_settings.h" 2 | 3 | #include "nvs_flash.h" 4 | #include "esp_heap_caps.h" 5 | 6 | #include "string.h" 7 | 8 | #include "odroid_audio.h" 9 | 10 | #include "odroid_display.h" 11 | 12 | 13 | static const char* NvsNamespace = "Odroid"; 14 | 15 | static const char* NvsKey_RomFilePath = "RomFilePath"; 16 | static const char* NvsKey_Volume = "Volume"; 17 | static const char* NvsKey_VRef = "VRef"; 18 | static const char* NvsKey_AppSlot = "AppSlot"; 19 | static const char* NvsKey_DataSlot = "DataSlot"; 20 | static const char* NvsKey_Backlight = "Backlight"; 21 | static const char* NvsKey_StartAction = "StartAction"; 22 | static const char* NvsKey_ScaleDisabled = "ScaleDisabled"; 23 | static const char* NvsKey_AudioSink = "AudioSink"; 24 | static const char* NvsKey_WLANtype = "WLANtype"; 25 | 26 | char* odroid_util_GetFileName(const char* path) 27 | { 28 | int length = strlen(path); 29 | int fileNameStart = length; 30 | 31 | if (fileNameStart < 1) abort(); 32 | 33 | while (fileNameStart > 0) 34 | { 35 | if (path[fileNameStart] == '/') 36 | { 37 | ++fileNameStart; 38 | break; 39 | } 40 | 41 | --fileNameStart; 42 | } 43 | 44 | int size = length - fileNameStart + 1; 45 | 46 | char* result = malloc(size); 47 | if (!result) abort(); 48 | 49 | result[size - 1] = 0; 50 | for (int i = 0; i < size - 1; ++i) 51 | { 52 | result[i] = path[fileNameStart + i]; 53 | } 54 | 55 | //printf("GetFileName: result='%s'\n", result); 56 | 57 | return result; 58 | } 59 | 60 | char* odroid_util_GetFileExtenstion(const char* path) 61 | { 62 | // Note: includes '.' 63 | int length = strlen(path); 64 | int extensionStart = length; 65 | 66 | if (extensionStart < 1) abort(); 67 | 68 | while (extensionStart > 0) 69 | { 70 | if (path[extensionStart] == '.') 71 | { 72 | break; 73 | } 74 | 75 | --extensionStart; 76 | } 77 | 78 | int size = length - extensionStart + 1; 79 | 80 | char* result = malloc(size); 81 | if (!result) abort(); 82 | 83 | result[size - 1] = 0; 84 | for (int i = 0; i < size - 1; ++i) 85 | { 86 | result[i] = path[extensionStart + i]; 87 | } 88 | 89 | //printf("GetFileExtenstion: result='%s'\n", result); 90 | 91 | return result; 92 | } 93 | 94 | char* odroid_util_GetFileNameWithoutExtension(const char* path) 95 | { 96 | char* fileName = odroid_util_GetFileName(path); 97 | 98 | int length = strlen(fileName); 99 | int extensionStart = length; 100 | 101 | if (extensionStart < 1) abort(); 102 | 103 | while (extensionStart > 0) 104 | { 105 | if (fileName[extensionStart] == '.') 106 | { 107 | break; 108 | } 109 | 110 | --extensionStart; 111 | } 112 | 113 | int size = extensionStart + 1; 114 | 115 | char* result = malloc(size); 116 | if (!result) abort(); 117 | 118 | result[size - 1] = 0; 119 | for (int i = 0; i < size - 1; ++i) 120 | { 121 | result[i] = fileName[i]; 122 | } 123 | 124 | free(fileName); 125 | 126 | //printf("GetFileNameWithoutExtension: result='%s'\n", result); 127 | 128 | return result; 129 | } 130 | 131 | 132 | int32_t odroid_settings_VRef_get() 133 | { 134 | STOP_DISPLAY_FUNCTION(); 135 | int32_t result = 1100; 136 | 137 | // Open 138 | nvs_handle my_handle; 139 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 140 | if (err != ESP_OK) abort(); 141 | 142 | 143 | // Read 144 | err = nvs_get_i32(my_handle, NvsKey_VRef, &result); 145 | if (err == ESP_OK) 146 | { 147 | printf("odroid_settings_VRefGet: value=%d\n", result); 148 | } 149 | 150 | 151 | // Close 152 | nvs_close(my_handle); 153 | RESUME_DISPLAY_FUNCTION(); 154 | return result; 155 | } 156 | void odroid_settings_VRef_set(int32_t value) 157 | { 158 | STOP_DISPLAY_FUNCTION(); 159 | nvs_handle my_handle; 160 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 161 | if (err != ESP_OK) abort(); 162 | 163 | // Write key 164 | err = nvs_set_i32(my_handle, NvsKey_VRef, value); 165 | if (err != ESP_OK) abort(); 166 | 167 | // Close 168 | nvs_close(my_handle); 169 | RESUME_DISPLAY_FUNCTION(); 170 | } 171 | 172 | 173 | int32_t odroid_settings_Volume_get() 174 | { 175 | STOP_DISPLAY_FUNCTION(); 176 | int result = ODROID_VOLUME_LEVEL3; 177 | 178 | // Open 179 | nvs_handle my_handle; 180 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 181 | if (err != ESP_OK) abort(); 182 | 183 | // Read 184 | err = nvs_get_i32(my_handle, NvsKey_Volume, &result); 185 | if (err == ESP_OK) 186 | { 187 | printf("odroid_settings_Volume_get: value=%d\n", result); 188 | } 189 | 190 | // Close 191 | nvs_close(my_handle); 192 | RESUME_DISPLAY_FUNCTION(); 193 | return result; 194 | } 195 | void odroid_settings_Volume_set(int32_t value) 196 | { 197 | STOP_DISPLAY_FUNCTION(); 198 | // Open 199 | nvs_handle my_handle; 200 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 201 | if (err != ESP_OK) abort(); 202 | // Read 203 | err = nvs_set_i32(my_handle, NvsKey_Volume, value); 204 | if (err != ESP_OK) abort(); 205 | // Close 206 | nvs_close(my_handle); 207 | RESUME_DISPLAY_FUNCTION(); 208 | } 209 | 210 | 211 | char* odroid_settings_RomFilePath_get() 212 | { 213 | STOP_DISPLAY_FUNCTION(); 214 | char* result = NULL; 215 | 216 | // Open 217 | nvs_handle my_handle; 218 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 219 | if (err != ESP_OK) abort(); 220 | 221 | 222 | // Read 223 | size_t required_size; 224 | err = nvs_get_str(my_handle, NvsKey_RomFilePath, NULL, &required_size); 225 | if (err == ESP_OK) 226 | { 227 | char* value = malloc(required_size); 228 | if (!value) abort(); 229 | 230 | err = nvs_get_str(my_handle, NvsKey_RomFilePath, value, &required_size); 231 | if (err != ESP_OK) abort(); 232 | 233 | result = value; 234 | 235 | printf("odroid_settings_RomFilePathGet: value='%s'\n", value); 236 | } 237 | 238 | 239 | // Close 240 | nvs_close(my_handle); 241 | RESUME_DISPLAY_FUNCTION(); 242 | return result; 243 | } 244 | void odroid_settings_RomFilePath_set(char* value) 245 | { 246 | STOP_DISPLAY_FUNCTION(); 247 | nvs_handle my_handle; 248 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 249 | if (err != ESP_OK) abort(); 250 | 251 | // Write key 252 | err = nvs_set_str(my_handle, NvsKey_RomFilePath, value); 253 | if (err != ESP_OK) abort(); 254 | 255 | // Close 256 | nvs_close(my_handle); 257 | RESUME_DISPLAY_FUNCTION(); 258 | } 259 | 260 | 261 | int32_t odroid_settings_AppSlot_get() 262 | { 263 | STOP_DISPLAY_FUNCTION(); 264 | int32_t result = -1; 265 | 266 | // Open 267 | nvs_handle my_handle; 268 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 269 | if (err != ESP_OK) abort(); 270 | 271 | 272 | // Read 273 | err = nvs_get_i32(my_handle, NvsKey_AppSlot, &result); 274 | if (err == ESP_OK) 275 | { 276 | printf("odroid_settings_AppSlot_get: value=%d\n", result); 277 | } 278 | 279 | 280 | // Close 281 | nvs_close(my_handle); 282 | RESUME_DISPLAY_FUNCTION(); 283 | return result; 284 | 285 | } 286 | void odroid_settings_AppSlot_set(int32_t value) 287 | { 288 | STOP_DISPLAY_FUNCTION(); 289 | nvs_handle my_handle; 290 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 291 | if (err != ESP_OK) abort(); 292 | 293 | // Write key 294 | err = nvs_set_i32(my_handle, NvsKey_AppSlot, value); 295 | if (err != ESP_OK) abort(); 296 | 297 | // Close 298 | nvs_close(my_handle); 299 | RESUME_DISPLAY_FUNCTION(); 300 | } 301 | 302 | 303 | int32_t odroid_settings_DataSlot_get() 304 | { 305 | STOP_DISPLAY_FUNCTION(); 306 | int32_t result = -1; 307 | 308 | // Open 309 | nvs_handle my_handle; 310 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 311 | if (err != ESP_OK) abort(); 312 | 313 | 314 | // Read 315 | err = nvs_get_i32(my_handle, NvsKey_DataSlot, &result); 316 | if (err == ESP_OK) 317 | { 318 | printf("odroid_settings_DataSlot_get: value=%d\n", result); 319 | } 320 | 321 | 322 | // Close 323 | nvs_close(my_handle); 324 | 325 | return result; 326 | RESUME_DISPLAY_FUNCTION(); 327 | } 328 | void odroid_settings_DataSlot_set(int32_t value) 329 | { 330 | STOP_DISPLAY_FUNCTION(); 331 | nvs_handle my_handle; 332 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 333 | if (err != ESP_OK) abort(); 334 | 335 | // Write key 336 | err = nvs_set_i32(my_handle, NvsKey_DataSlot, value); 337 | if (err != ESP_OK) abort(); 338 | 339 | // Close 340 | nvs_close(my_handle); 341 | RESUME_DISPLAY_FUNCTION(); 342 | } 343 | 344 | 345 | int32_t odroid_settings_Backlight_get() 346 | { 347 | STOP_DISPLAY_FUNCTION(); 348 | // TODO: Move to header 349 | int result = 2; 350 | 351 | // Open 352 | nvs_handle my_handle; 353 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 354 | if (err != ESP_OK) abort(); 355 | 356 | // Read 357 | err = nvs_get_i32(my_handle, NvsKey_Backlight, &result); 358 | if (err == ESP_OK) 359 | { 360 | printf("odroid_settings_Backlight_get: value=%d\n", result); 361 | } 362 | 363 | // Close 364 | nvs_close(my_handle); 365 | RESUME_DISPLAY_FUNCTION(); 366 | return result; 367 | } 368 | void odroid_settings_Backlight_set(int32_t value) 369 | { 370 | STOP_DISPLAY_FUNCTION(); 371 | // Open 372 | nvs_handle my_handle; 373 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 374 | if (err != ESP_OK) abort(); 375 | 376 | // Read 377 | err = nvs_set_i32(my_handle, NvsKey_Backlight, value); 378 | if (err != ESP_OK) abort(); 379 | 380 | // Close 381 | nvs_close(my_handle); 382 | RESUME_DISPLAY_FUNCTION(); 383 | } 384 | 385 | 386 | ODROID_START_ACTION odroid_settings_StartAction_get() 387 | { 388 | STOP_DISPLAY_FUNCTION(); 389 | int result = 0; 390 | 391 | // Open 392 | nvs_handle my_handle; 393 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 394 | if (err != ESP_OK) abort(); 395 | 396 | // Read 397 | err = nvs_get_i32(my_handle, NvsKey_StartAction, &result); 398 | if (err == ESP_OK) 399 | { 400 | printf("%s: value=%d\n", __func__, result); 401 | } 402 | 403 | // Close 404 | nvs_close(my_handle); 405 | RESUME_DISPLAY_FUNCTION(); 406 | return result; 407 | } 408 | void odroid_settings_StartAction_set(ODROID_START_ACTION value) 409 | { 410 | STOP_DISPLAY_FUNCTION(); 411 | // Open 412 | nvs_handle my_handle; 413 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 414 | if (err != ESP_OK) abort(); 415 | 416 | // Write 417 | err = nvs_set_i32(my_handle, NvsKey_StartAction, value); 418 | if (err != ESP_OK) abort(); 419 | 420 | // Close 421 | nvs_close(my_handle); 422 | RESUME_DISPLAY_FUNCTION(); 423 | } 424 | 425 | 426 | uint8_t odroid_settings_ScaleDisabled_get(ODROID_SCALE_DISABLE system) 427 | { 428 | STOP_DISPLAY_FUNCTION(); 429 | int result = 0; 430 | 431 | // Open 432 | nvs_handle my_handle; 433 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 434 | if (err != ESP_OK) abort(); 435 | 436 | // Read 437 | err = nvs_get_i32(my_handle, NvsKey_ScaleDisabled, &result); 438 | if (err == ESP_OK) 439 | { 440 | printf("%s: result=%d\n", __func__, result); 441 | } 442 | 443 | // Close 444 | nvs_close(my_handle); 445 | RESUME_DISPLAY_FUNCTION(); 446 | return (result & system) ? 1 : 0; 447 | } 448 | void odroid_settings_ScaleDisabled_set(ODROID_SCALE_DISABLE system, uint8_t value) 449 | { 450 | printf("%s: system=%#010x, value=%d\n", __func__, system, value); 451 | STOP_DISPLAY_FUNCTION(); 452 | // Open 453 | nvs_handle my_handle; 454 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 455 | if (err != ESP_OK) abort(); 456 | 457 | // Read 458 | int result = 0; 459 | err = nvs_get_i32(my_handle, NvsKey_ScaleDisabled, &result); 460 | if (err == ESP_OK) 461 | { 462 | printf("%s: result=%d\n", __func__, result); 463 | } 464 | 465 | // set system flag 466 | //result = 1; 467 | result &= ~system; 468 | //printf("%s: result=%d\n", __func__, result); 469 | result |= (system & (value ? 0xffffffff : 0)); 470 | printf("%s: set result=%d\n", __func__, result); 471 | 472 | // Write 473 | err = nvs_set_i32(my_handle, NvsKey_ScaleDisabled, result); 474 | if (err != ESP_OK) abort(); 475 | 476 | // Close 477 | nvs_close(my_handle); 478 | RESUME_DISPLAY_FUNCTION(); 479 | } 480 | 481 | 482 | ODROID_AUDIO_SINK odroid_settings_AudioSink_get() 483 | { 484 | STOP_DISPLAY_FUNCTION(); 485 | int result = ODROID_AUDIO_SINK_SPEAKER; 486 | 487 | // Open 488 | nvs_handle my_handle; 489 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 490 | if (err != ESP_OK) abort(); 491 | 492 | // Read 493 | err = nvs_get_i32(my_handle, NvsKey_AudioSink, &result); 494 | if (err == ESP_OK) 495 | { 496 | printf("%s: value=%d\n", __func__, result); 497 | } 498 | 499 | // Close 500 | nvs_close(my_handle); 501 | RESUME_DISPLAY_FUNCTION(); 502 | return (ODROID_AUDIO_SINK)result; 503 | } 504 | void odroid_settings_AudioSink_set(ODROID_AUDIO_SINK value) 505 | { 506 | STOP_DISPLAY_FUNCTION(); 507 | // Open 508 | nvs_handle my_handle; 509 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 510 | if (err != ESP_OK) abort(); 511 | 512 | // Write 513 | err = nvs_set_i32(my_handle, NvsKey_AudioSink, (int)value); 514 | if (err != ESP_OK) abort(); 515 | 516 | // Close 517 | nvs_close(my_handle); 518 | RESUME_DISPLAY_FUNCTION(); 519 | } 520 | 521 | 522 | void odroid_settings_WLAN_set(ODROID_WLAN_TYPE value) 523 | { 524 | STOP_DISPLAY_FUNCTION(); 525 | // Open 526 | nvs_handle my_handle; 527 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 528 | if (err != ESP_OK) abort(); 529 | 530 | // Write 531 | err = nvs_set_i32(my_handle, NvsKey_WLANtype, (int)value); 532 | if (err != ESP_OK) abort(); 533 | 534 | // Close 535 | nvs_close(my_handle); 536 | RESUME_DISPLAY_FUNCTION(); 537 | } 538 | 539 | ODROID_WLAN_TYPE odroid_settings_WLAN_get() 540 | { 541 | STOP_DISPLAY_FUNCTION(); 542 | int result = ODROID_WLAN_NONE; 543 | 544 | // Open 545 | nvs_handle my_handle; 546 | esp_err_t err = nvs_open(NvsNamespace, NVS_READWRITE, &my_handle); 547 | if (err != ESP_OK) abort(); 548 | 549 | // Read 550 | err = nvs_get_i32(my_handle, NvsKey_WLANtype, &result); 551 | if (err == ESP_OK) 552 | { 553 | printf("%s: value=%d\n", __func__, result); 554 | } 555 | 556 | // Close 557 | nvs_close(my_handle); 558 | RESUME_DISPLAY_FUNCTION(); 559 | return (ODROID_WLAN_TYPE)result; 560 | } 561 | 562 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | char* odroid_util_GetFileName(const char* path); 6 | char* odroid_util_GetFileExtenstion(const char* path); 7 | char* odroid_util_GetFileNameWithoutExtension(const char* path); 8 | 9 | 10 | typedef enum 11 | { 12 | ODROID_START_ACTION_NORMAL = 0, 13 | ODROID_START_ACTION_RESTART 14 | } ODROID_START_ACTION; 15 | 16 | typedef enum 17 | { 18 | ODROID_SCALE_DISABLE_NES = (1 << 0), 19 | ODROID_SCALE_DISABLE_GB = (1 << 1), 20 | ODROID_SCALE_DISABLE_SMS = (1 << 2) 21 | } ODROID_SCALE_DISABLE; 22 | 23 | typedef enum 24 | { 25 | ODROID_AUDIO_SINK_NONE = 99, 26 | ODROID_AUDIO_SINK_SPEAKER = 0, 27 | ODROID_AUDIO_SINK_DAC 28 | } ODROID_AUDIO_SINK; 29 | 30 | typedef enum 31 | { 32 | ODROID_WLAN_NONE = 99, 33 | ODROID_WLAN_AP = 0, 34 | ODROID_WLAN_STA 35 | } ODROID_WLAN_TYPE; 36 | 37 | 38 | int32_t odroid_settings_VRef_get(); 39 | void odroid_settings_VRef_set(int32_t value); 40 | 41 | int32_t odroid_settings_Volume_get(); 42 | void odroid_settings_Volume_set(int32_t value); 43 | 44 | char* odroid_settings_RomFilePath_get(); 45 | void odroid_settings_RomFilePath_set(char* value); 46 | 47 | int32_t odroid_settings_AppSlot_get(); 48 | void odroid_settings_AppSlot_set(int32_t value); 49 | 50 | int32_t odroid_settings_DataSlot_get(); 51 | void odroid_settings_DataSlot_set(int32_t value); 52 | 53 | int32_t odroid_settings_Backlight_get(); 54 | void odroid_settings_Backlight_set(int32_t value); 55 | 56 | ODROID_START_ACTION odroid_settings_StartAction_get(); 57 | void odroid_settings_StartAction_set(ODROID_START_ACTION value); 58 | 59 | uint8_t odroid_settings_ScaleDisabled_get(ODROID_SCALE_DISABLE system); 60 | void odroid_settings_ScaleDisabled_set(ODROID_SCALE_DISABLE system, uint8_t value); 61 | 62 | ODROID_AUDIO_SINK odroid_settings_AudioSink_get(); 63 | void odroid_settings_AudioSink_set(ODROID_AUDIO_SINK value); 64 | 65 | ODROID_WLAN_TYPE odroid_settings_WLAN_get(); 66 | void odroid_settings_WLAN_set(ODROID_WLAN_TYPE value); 67 | 68 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_system.c: -------------------------------------------------------------------------------- 1 | #include "odroid_system.h" 2 | 3 | #include "freertos/FreeRTOS.h" 4 | #include "esp_system.h" 5 | #include "esp_event.h" 6 | #include "driver/rtc_io.h" 7 | #include "esp_partition.h" 8 | #include "esp_ota_ops.h" 9 | 10 | #include "odroid_input.h" 11 | 12 | static bool system_initialized = false; 13 | 14 | void odroid_system_application_set(int slot) 15 | { 16 | const esp_partition_t* partition = esp_partition_find_first( 17 | ESP_PARTITION_TYPE_APP, 18 | ESP_PARTITION_SUBTYPE_APP_OTA_MIN + slot, 19 | NULL); 20 | if (partition != NULL) 21 | { 22 | esp_err_t err = esp_ota_set_boot_partition(partition); 23 | if (err != ESP_OK) 24 | { 25 | printf("odroid_system_application_set: esp_ota_set_boot_partition failed.\n"); 26 | abort(); 27 | } 28 | } 29 | } 30 | 31 | void odroid_system_sleep() 32 | { 33 | printf("odroid_system_sleep: Entered.\n"); 34 | 35 | // Wait for button release 36 | odroid_gamepad_state joystick; 37 | odroid_input_gamepad_read(&joystick); 38 | 39 | while (joystick.values[ODROID_INPUT_MENU]) 40 | { 41 | vTaskDelay(1); 42 | odroid_input_gamepad_read(&joystick); 43 | } 44 | 45 | //odroid_input_gamepad_terminate(); 46 | 47 | 48 | // Configure button to wake 49 | printf("odroid_system_sleep: Configuring deep sleep.\n"); 50 | #if 1 51 | esp_err_t err = esp_sleep_enable_ext0_wakeup(ODROID_GAMEPAD_IO_MENU, 0); 52 | #else 53 | const int ext_wakeup_pin_1 = ODROID_GAMEPAD_IO_MENU; 54 | const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1; 55 | 56 | esp_err_t err = esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_1_mask, ESP_EXT1_WAKEUP_ALL_LOW); 57 | #endif 58 | if (err != ESP_OK) 59 | { 60 | printf("odroid_system_sleep: esp_sleep_enable_ext0_wakeup failed.\n"); 61 | abort(); 62 | } 63 | 64 | err = rtc_gpio_pullup_en(ODROID_GAMEPAD_IO_MENU); 65 | if (err != ESP_OK) 66 | { 67 | printf("odroid_system_sleep: rtc_gpio_pullup_en failed.\n"); 68 | abort(); 69 | } 70 | 71 | 72 | // Isolate GPIO12 pin from external circuits. This is needed for modules 73 | // which have an external pull-up resistor on GPIO12 (such as ESP32-WROVER) 74 | // to minimize current consumption. 75 | rtc_gpio_isolate(GPIO_NUM_12); 76 | #if 1 77 | rtc_gpio_isolate(GPIO_NUM_34); 78 | rtc_gpio_isolate(GPIO_NUM_35); 79 | rtc_gpio_isolate(GPIO_NUM_0); 80 | rtc_gpio_isolate(GPIO_NUM_39); 81 | //rtc_gpio_isolate(GPIO_NUM_14); 82 | #endif 83 | 84 | // Sleep 85 | //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); 86 | 87 | vTaskDelay(100); 88 | esp_deep_sleep_start(); 89 | } 90 | 91 | void odroid_system_init() 92 | { 93 | rtc_gpio_deinit(ODROID_GAMEPAD_IO_MENU); 94 | //rtc_gpio_deinit(GPIO_NUM_14); 95 | 96 | // blue led 97 | gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT); 98 | gpio_set_level(GPIO_NUM_2, 0); 99 | 100 | system_initialized = true; 101 | } 102 | 103 | void odroid_system_led_set(int value) 104 | { 105 | if (!system_initialized) 106 | { 107 | printf("odroid_system_init not called before use.\n"); 108 | abort(); 109 | } 110 | 111 | gpio_set_level(GPIO_NUM_2, value); 112 | } 113 | -------------------------------------------------------------------------------- /main/odroid-go-common/odroid_system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void odroid_system_application_set(int slot); 4 | void odroid_system_sleep(); 5 | void odroid_system_init(); 6 | void odroid_system_led_set(int value); 7 | -------------------------------------------------------------------------------- /main/odroidGo/Audio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2018 Schuemi. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | 26 | #include "MSX.h" 27 | #include "EMULib.h" 28 | #include 29 | #include "odroid_audio.h" 30 | #include "LibOdroidGo.h" 31 | #include "freertos/FreeRTOS.h" 32 | #include "freertos/queue.h" 33 | #include "freertos/task.h" 34 | 35 | #include "minIni.h" 36 | 37 | #include "Sound.h" 38 | 39 | #include 40 | 41 | 42 | 43 | 44 | //#define NO_SOUND 45 | 46 | 47 | sample *playData; 48 | unsigned int playLength; 49 | 50 | uint16_t toPlayLength = 0; 51 | QueueHandle_t audioQueue; 52 | ODROID_AUDIO_SINK sink = ODROID_AUDIO_SINK_NONE; 53 | 54 | int volLevel; 55 | char stop = 0; 56 | void audioTask(void* arg) 57 | { 58 | // sound 59 | uint16_t* param; 60 | 61 | while(!stop) 62 | { 63 | xQueuePeek(audioQueue, ¶m, portMAX_DELAY); 64 | #ifndef NO_SOUND 65 | if (! stop) { 66 | RenderAndPlayAudio(toPlayLength); 67 | odroid_audio_submit(playData, playLength/2); 68 | } 69 | #endif 70 | xQueueReceive(audioQueue, ¶m, portMAX_DELAY); 71 | } 72 | 73 | printf("audioTask: exiting.\n"); 74 | odroid_audio_terminate(); 75 | 76 | vTaskDelete(NULL); 77 | 78 | 79 | } 80 | 81 | 82 | unsigned int InitAudio(unsigned int Rate,unsigned int Latency) { 83 | 84 | stop = 0; 85 | char buf[3]; 86 | 87 | if (sink == ODROID_AUDIO_SINK_NONE) ini_gets("FMSX", "DAC", "0", buf, 3, FMSX_CONFIG_FILE); 88 | 89 | if (atoi(buf)) sink = ODROID_AUDIO_SINK_DAC; else sink = ODROID_AUDIO_SINK_SPEAKER; 90 | 91 | 92 | audioQueue = xQueueCreate(1, sizeof(uint16_t*)); 93 | 94 | odroid_audio_init(sink, Rate); 95 | volLevel = ini_getl("FMSX", "VOLUME", ODROID_VOLUME_LEVEL1, FMSX_CONFIG_FILE); 96 | 97 | if (volLevel >= ODROID_VOLUME_LEVEL_COUNT || volLevel < ODROID_VOLUME_LEVEL0) volLevel = ODROID_VOLUME_LEVEL1; 98 | odroid_audio_volume_set(volLevel); 99 | 100 | xTaskCreatePinnedToCore(&audioTask, "audioTask", 2048, NULL, 5, NULL, 1); 101 | 102 | return Rate; 103 | } 104 | void TrashAudio(void){ 105 | 106 | } 107 | 108 | void audio_volume_set_change() { 109 | ets_delay_us(100000); // have to wait a little (other transactions have to finish?) 110 | volLevel = (volLevel + 1) % ODROID_VOLUME_LEVEL_COUNT; 111 | odroid_audio_volume_set(volLevel); 112 | ini_putl("FMSX", "VOLUME", volLevel, FMSX_CONFIG_FILE); 113 | 114 | } 115 | 116 | void pause_audio() { 117 | void* tempPtr = (void*)0x1234; 118 | stop=1; 119 | xQueueSend(audioQueue, &tempPtr, portMAX_DELAY); // to wait until sound was send 120 | 121 | TrashAudio(); 122 | } 123 | 124 | void restart_audio() { 125 | InitAudio(AUDIO_SAMPLE_RATE, 0); 126 | } 127 | 128 | /** TrashAudio() *********************************************/ 129 | /** Free resources allocated by InitAudio(). **/ 130 | /*************************************************************/ 131 | 132 | /** PlayAllSound() *******************************************/ 133 | /** Render and play given number of microseconds of sound. **/ 134 | /************************************ TO BE WRITTEN BY USER **/ 135 | void PlayAllSound(int uSec) { 136 | #ifdef NO_SOUND 137 | return; 138 | #endif 139 | //#ifdef WITH_WLAN 140 | // if (getMultiplayState() == MULTIPLAYER_CONNECTED_CLIENT) return; 141 | //#endif 142 | void* tempPtr = (void*)0x1234; 143 | toPlayLength = 2*uSec*AUDIO_SAMPLE_RATE/1000000; 144 | xQueueSend(audioQueue, &tempPtr, portMAX_DELAY); 145 | 146 | } 147 | 148 | unsigned int WriteAudio(sample *Data,unsigned int Length) 149 | { 150 | playData = Data; 151 | playLength = Length; 152 | return Length; 153 | } 154 | unsigned int GetFreeAudio(void) 155 | { 156 | return 1024; 157 | 158 | } 159 | -------------------------------------------------------------------------------- /main/odroidGo/LibOdroidGo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2018 Schuemi. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include "LibOdroidGo.h" 26 | 27 | 28 | #include "FDIDisk.h" 29 | #include 30 | #include 31 | #include "MSX.h" 32 | #include "EMULib.h" 33 | #include 34 | #include 35 | #include 36 | #include "odroid_display.h" 37 | #include "odroid_input.h" 38 | #include "odroid_qwerty.h" 39 | #include 40 | #include "sound.h" 41 | #include "odroid_audio.h" 42 | #include "ff.h" 43 | 44 | #include "minIni.h" 45 | 46 | #include "freertos/FreeRTOS.h" 47 | #include "freertos/queue.h" 48 | #include "freertos/task.h" 49 | 50 | #include "utils.h" 51 | 52 | 53 | int keyMapping[ODROID_INPUT_MAX]; 54 | char pushedKeys[ODROID_INPUT_MAX]; 55 | char* lastGame; 56 | int pushedVirtKeyboardKey = -1; 57 | int holdVirtKeyboardKey = -1; 58 | int holdVirtKeyboardSelectKey = -1; 59 | uint8_t inMenue = 0; 60 | uint8_t vKeyboardShow = 0; 61 | /// for the Menu 62 | unsigned int lastKey = ODROID_INPUT_MENU; 63 | unsigned int pressCounter = 0; 64 | /////////// 65 | bool odroidQwertyFound = false; 66 | odroid_qwerty_state odroidQwertyPushedKeys; 67 | char holdShift = 0; 68 | 69 | void setDefaultKeymapping() { 70 | keyMapping[ODROID_INPUT_UP] = JST_UP; 71 | keyMapping[ODROID_INPUT_RIGHT] = JST_RIGHT; 72 | keyMapping[ODROID_INPUT_DOWN] = JST_DOWN; 73 | keyMapping[ODROID_INPUT_LEFT] = JST_LEFT; 74 | keyMapping[ODROID_INPUT_SELECT] = '2' << 8; 75 | keyMapping[ODROID_INPUT_START] = '1' << 8; 76 | keyMapping[ODROID_INPUT_A] = JST_FIREA; 77 | keyMapping[ODROID_INPUT_B] = JST_FIREB; 78 | keyMapping[ODROID_INPUT_MENU] = '|' << 8; 79 | keyMapping[ODROID_INPUT_VOLUME] = '~' << 8; 80 | 81 | } 82 | 83 | void SetKeyMapping(int Key, char* mappingString) { 84 | keyMapping[Key] = -1; 85 | 86 | if (!strcmp(mappingString, "JST_UP")) keyMapping[Key] = JST_UP; 87 | if (!strcmp(mappingString, "JST_RIGHT")) keyMapping[Key] = JST_RIGHT; 88 | if (!strcmp(mappingString, "JST_DOWN")) keyMapping[Key] = JST_DOWN; 89 | if (!strcmp(mappingString, "JST_LEFT")) keyMapping[Key] = JST_LEFT; 90 | if (!strcmp(mappingString, "JST_FIREA")) keyMapping[Key] = JST_FIREA; 91 | if (!strcmp(mappingString, "JST_FIREB")) keyMapping[Key] = JST_FIREB; 92 | 93 | if (!strcmp(mappingString, "KBD_SPACE")) keyMapping[Key] = KBD_SPACE << 8; 94 | 95 | if (!strcmp(mappingString, "KBD_F1")) keyMapping[Key] = KBD_F1 << 8; 96 | if (!strcmp(mappingString, "KBD_F2")) keyMapping[Key] = KBD_F2 << 8; 97 | if (!strcmp(mappingString, "KBD_F3")) keyMapping[Key] = KBD_F3 << 8; 98 | if (!strcmp(mappingString, "KBD_F4")) keyMapping[Key] = KBD_F4 << 8; 99 | if (!strcmp(mappingString, "KBD_F5")) keyMapping[Key] = KBD_F5 << 8; 100 | 101 | if (!strcmp(mappingString, "KBD_LEFT")) keyMapping[Key] = KBD_LEFT << 8; 102 | if (!strcmp(mappingString, "KBD_UP")) keyMapping[Key] = KBD_UP << 8; 103 | if (!strcmp(mappingString, "KBD_DOWN")) keyMapping[Key] = KBD_DOWN << 8; 104 | if (!strcmp(mappingString, "KBD_RIGHT")) keyMapping[Key] = KBD_RIGHT << 8; 105 | if (!strcmp(mappingString, "KBD_SHIFT")) keyMapping[Key] = KBD_SHIFT << 8; 106 | if (!strcmp(mappingString, "KBD_CONTROL")) keyMapping[Key] = KBD_CONTROL << 8; 107 | if (!strcmp(mappingString, "KBD_GRAPH")) keyMapping[Key] = KBD_GRAPH << 8; 108 | if (!strcmp(mappingString, "KBD_BS")) keyMapping[Key] = KBD_BS << 8; 109 | if (!strcmp(mappingString, "KBD_TAB")) keyMapping[Key] = KBD_TAB << 8; 110 | if (!strcmp(mappingString, "KBD_CAPSLOCK")) keyMapping[Key] = KBD_CAPSLOCK << 8; 111 | if (!strcmp(mappingString, "KBD_SELECT")) keyMapping[Key] = KBD_SELECT << 8; 112 | if (!strcmp(mappingString, "KBD_HOME")) keyMapping[Key] = KBD_HOME << 8; 113 | if (!strcmp(mappingString, "KBD_ENTER")) keyMapping[Key] = KBD_ENTER << 8; 114 | if (!strcmp(mappingString, "KBD_INSERT")) keyMapping[Key] = KBD_INSERT << 8; 115 | if (!strcmp(mappingString, "KBD_COUNTRY")) keyMapping[Key] = KBD_COUNTRY << 8; 116 | if (!strcmp(mappingString, "KBD_STOP")) keyMapping[Key] = KBD_STOP << 8; 117 | 118 | if (!strcmp(mappingString, "KBD_NUMPAD0")) keyMapping[Key] = KBD_NUMPAD0 << 8; 119 | if (!strcmp(mappingString, "KBD_NUMPAD1")) keyMapping[Key] = KBD_NUMPAD1 << 8; 120 | if (!strcmp(mappingString, "KBD_NUMPAD2")) keyMapping[Key] = KBD_NUMPAD2 << 8; 121 | if (!strcmp(mappingString, "KBD_NUMPAD3")) keyMapping[Key] = KBD_NUMPAD3 << 8; 122 | if (!strcmp(mappingString, "KBD_NUMPAD4")) keyMapping[Key] = KBD_NUMPAD4 << 8; 123 | if (!strcmp(mappingString, "KBD_NUMPAD5")) keyMapping[Key] = KBD_NUMPAD5 << 8; 124 | if (!strcmp(mappingString, "KBD_NUMPAD6")) keyMapping[Key] = KBD_NUMPAD6 << 8; 125 | if (!strcmp(mappingString, "KBD_NUMPAD7")) keyMapping[Key] = KBD_NUMPAD7 << 8; 126 | if (!strcmp(mappingString, "KBD_NUMPAD8")) keyMapping[Key] = KBD_NUMPAD8 << 8; 127 | if (!strcmp(mappingString, "KBD_NUMPAD9")) keyMapping[Key] = KBD_NUMPAD9 << 8; 128 | 129 | if (!strcmp(mappingString, "KBD_ESCAPE")) keyMapping[Key] = KBD_ESCAPE << 8; 130 | 131 | 132 | 133 | if (keyMapping[Key] == -1) { 134 | keyMapping[Key] = mappingString[0] << 8; 135 | } 136 | } 137 | char LoadKeyMapping(char* KeyFile) { 138 | 139 | char buffer[16]; 140 | int res; 141 | 142 | res = ini_gets("KEYMAPPING", "UP", "", buffer, 16, KeyFile); 143 | if (res) SetKeyMapping(ODROID_INPUT_UP, buffer); 144 | 145 | res = ini_gets("KEYMAPPING", "RIGHT", "", buffer, 16, KeyFile); 146 | if (res) SetKeyMapping(ODROID_INPUT_RIGHT, buffer); 147 | 148 | res = ini_gets("KEYMAPPING", "DOWN", "", buffer, 16, KeyFile); 149 | if (res) SetKeyMapping(ODROID_INPUT_DOWN, buffer); 150 | 151 | res = ini_gets("KEYMAPPING", "LEFT", "", buffer, 16, KeyFile); 152 | if (res) SetKeyMapping(ODROID_INPUT_LEFT, buffer); 153 | 154 | res = ini_gets("KEYMAPPING", "SELECT", "", buffer, 16, KeyFile); 155 | if (res) SetKeyMapping(ODROID_INPUT_SELECT, buffer); 156 | 157 | res = ini_gets("KEYMAPPING", "START", "", buffer, 16, KeyFile); 158 | if (res) SetKeyMapping(ODROID_INPUT_START, buffer); 159 | 160 | res = ini_gets("KEYMAPPING", "A", "", buffer, 16, KeyFile); 161 | if (res) SetKeyMapping(ODROID_INPUT_A, buffer); 162 | 163 | res = ini_gets("KEYMAPPING", "B", "", buffer, 16, KeyFile); 164 | if (res) SetKeyMapping(ODROID_INPUT_B, buffer); 165 | 166 | 167 | 168 | 169 | 170 | } 171 | 172 | 173 | void loadKeyMappingFromGame(const char* gameFileName) { 174 | const char* filename = getFileName(gameFileName); 175 | char* keyBoardFile = malloc(256); 176 | char* keyBoardFileFullPath = malloc(612); 177 | strncpy(keyBoardFile, filename, 256); 178 | keyBoardFile = cutExtension(keyBoardFile); 179 | snprintf(keyBoardFileFullPath, 612, "/sd/odroid/data/msx/%s.ini", keyBoardFile); 180 | LoadKeyMapping(keyBoardFileFullPath); 181 | free(keyBoardFile); 182 | free(keyBoardFileFullPath); 183 | } 184 | /** InitMachine() ********************************************/ 185 | /** Allocate resources needed by the machine-dependent code.**/ 186 | /************************************ TO BE WRITTEN BY USER **/ 187 | int InitChangeGame(const char* name) { 188 | printf("Should load: %s\n", name); 189 | loadKeyMappingFromGame(name); 190 | if (hasExt(name, ".rom\0.mx1\0.mx2\0\0")) { ROMName[0] = name; printf("is rom\n");} 191 | if (hasExt(name, ".dsk\0\0")){ DSKName[0] = name; printf("is disc\n");ROMName[0] = "";} 192 | if (hasExt(name, ".cas\0\0")){ CasName = name; printf("is cas\n");ROMName[0] = "";} 193 | 194 | odroidFmsxGUI_setLastLoadedFile(name); 195 | } 196 | int InitMachine(void){ 197 | 198 | 199 | 200 | lastGame = malloc(1024); 201 | initFiles(); 202 | 203 | setDefaultKeymapping(); 204 | LoadKeyMapping("/sd/odroid/data/msx/config.ini"); 205 | 206 | InitVideo(); 207 | odroidFmsxGUI_initMenu(); 208 | 209 | 210 | int res = ini_gets("FMSX", "LASTGAME", "", lastGame, 1024, FMSX_CONFIG_FILE); 211 | if (res) { 212 | InitChangeGame(lastGame); 213 | } 214 | 215 | InitSound(AUDIO_SAMPLE_RATE, 0); 216 | 217 | odroidQwertyFound = odroid_qwerty_init(); 218 | printf("odroid_qwerty_found: %d\n",odroidQwertyFound); 219 | memset(odroidQwertyPushedKeys.values, ODROID_QWERTY_NONE, ODROID_QWERTY_MAX_KEYS); 220 | 221 | return 1; 222 | } 223 | 224 | 225 | 226 | 227 | /** Joystick() ***********************************************/ 228 | /** Query positions of two joystick connected to ports 0/1. **/ 229 | /** Returns 0.0.B2.A2.R2.L2.D2.U2.0.0.B1.A1.R1.L1.D1.U1. **/ 230 | /************************************ TO BE WRITTEN BY USER **/ 231 | void checkKey(int key, odroid_gamepad_state out_state) { 232 | if (keyMapping[key] > 0xFF) { 233 | // its a keyboard key 234 | if (out_state.values[ODROID_INPUT_START] && ! pushedKeys[ODROID_INPUT_START]){ KBD_SET(keyMapping[ODROID_INPUT_START] >> 8); pushedKeys[ODROID_INPUT_START] = 1;} 235 | if (! out_state.values[ODROID_INPUT_START] && pushedKeys[ODROID_INPUT_START]){ KBD_RES(keyMapping[ODROID_INPUT_START] >> 8); pushedKeys[ODROID_INPUT_START] = 0;} 236 | } 237 | } 238 | void keybmoveCursor(odroid_gamepad_state out_state) { 239 | 240 | if (out_state.values[ODROID_INPUT_UP]) moveCursor(0,-2); 241 | if (out_state.values[ODROID_INPUT_DOWN])moveCursor(0,2); 242 | if (out_state.values[ODROID_INPUT_LEFT]) moveCursor(-2,0); 243 | if (out_state.values[ODROID_INPUT_RIGHT]) moveCursor(2,0); 244 | 245 | 246 | if (out_state.values[ODROID_INPUT_A] && pushedVirtKeyboardKey == -1){ 247 | int key = mousePress(); 248 | if (key != -1) 249 | { 250 | KBD_SET(key); 251 | pushedVirtKeyboardKey = key; 252 | } 253 | 254 | } 255 | if (! out_state.values[ODROID_INPUT_A] && pushedVirtKeyboardKey != -1) { 256 | KBD_RES(pushedVirtKeyboardKey); 257 | pushedVirtKeyboardKey = -1; 258 | } 259 | if (out_state.values[ODROID_INPUT_B] && holdVirtKeyboardKey == -1){ 260 | int key = mousePress(); 261 | if (key != -1) 262 | { 263 | KBD_SET(key); 264 | holdVirtKeyboardKey = key; 265 | } 266 | } 267 | if (! out_state.values[ODROID_INPUT_B] && holdVirtKeyboardKey != -1){ 268 | KBD_RES(holdVirtKeyboardKey); 269 | holdVirtKeyboardKey = -1; 270 | } 271 | if (out_state.values[ODROID_INPUT_SELECT] && holdVirtKeyboardSelectKey == -1){ 272 | doFlipScreen(); 273 | holdVirtKeyboardSelectKey = 1; 274 | } 275 | if (! out_state.values[ODROID_INPUT_SELECT]) holdVirtKeyboardSelectKey = -1; 276 | 277 | } 278 | // standard ctrl buttons: 279 | /* 280 | ctrl + h = delete 281 | * ctrl + b cursor up 282 | * ctrl + j cursor down 283 | * ctrl + r insert 284 | */ 285 | 286 | uint8_t toMSXKey(uint8_t key, bool pressedCtrl) { 287 | if (key == ODROID_QWERTY_TILDE) return KBD_STOP; 288 | if (key == ODROID_QWERTY_ESC) return KBD_HOME; 289 | if (key == ODROID_QWERTY_CTRL) return KBD_CONTROL; 290 | if (key == ODROID_QWERTY_ALT) return KBD_GRAPH; 291 | if (key == ODROID_QWERTY_SHIFT) return KBD_SHIFT; 292 | if (key == ODROID_QWERTY_BS) return KBD_BS; 293 | if (key == ODROID_QWERTY_ENTER) return KBD_ENTER; 294 | if (key == ODROID_QWERTY_1 && pressedCtrl) return KBD_F1; 295 | if (key == ODROID_QWERTY_2 && pressedCtrl) return KBD_F2; 296 | if (key == ODROID_QWERTY_3 && pressedCtrl) return KBD_F3; 297 | if (key == ODROID_QWERTY_4 && pressedCtrl) return KBD_F4; 298 | if (key == ODROID_QWERTY_5 && pressedCtrl) return KBD_F5; 299 | 300 | char asc = odroid_qwerty_key_to_ascii(key, true); 301 | if (asc >= 0x20 && asc < 0x61) return asc; 302 | 303 | return 0x00; 304 | 305 | } 306 | unsigned int Joystick(void) { 307 | 308 | 309 | unsigned int returnState = 0; 310 | odroid_gamepad_state out_state; 311 | odroid_input_gamepad_read(&out_state); 312 | #ifdef WITH_WLAN 313 | if (getMultiplayState() != MULTIPLAYER_CONNECTED_CLIENT) { 314 | #endif 315 | bool pressed = false; 316 | for (int i = 0; i < ODROID_INPUT_MAX; i++) if (out_state.values[i]) pressed = true; 317 | if (! vKeyboardShow) { 318 | if (out_state.values[ODROID_INPUT_A] && out_state.values[ODROID_INPUT_MENU]){ 319 | vKeyboardShow = 2; 320 | showVirtualKeyboard(); 321 | #ifdef WITH_WLAN 322 | exchangeJoystickState(&returnState); 323 | #endif 324 | return returnState; 325 | } 326 | } else if (vKeyboardShow == 1) { 327 | if (out_state.values[ODROID_INPUT_MENU]){ 328 | vKeyboardShow = 3; 329 | hideVirtualKeyboard(); 330 | clearScreen(); 331 | } 332 | } else if(vKeyboardShow == 2 && !out_state.values[ODROID_INPUT_MENU]) { 333 | vKeyboardShow = 1; 334 | } else if(vKeyboardShow == 3 && !out_state.values[ODROID_INPUT_MENU]) { 335 | vKeyboardShow = 0; 336 | 337 | } 338 | if (vKeyboardShow) { 339 | if (vKeyboardShow == 1) keybmoveCursor(out_state); 340 | #ifdef WITH_WLAN 341 | exchangeJoystickState(&returnState); 342 | #endif 343 | return returnState; 344 | } 345 | if (inMenue && !pressed) inMenue = 0; 346 | #ifdef WITH_WLAN 347 | } 348 | #endif 349 | /* joystick mapping */ 350 | for (int i = 0; i < ODROID_INPUT_MAX; i++){ 351 | if (i == ODROID_INPUT_MENU || i == ODROID_INPUT_VOLUME) continue; 352 | if (keyMapping[i] <= 0xFF){ 353 | // it is a joystick 354 | if (out_state.values[i]) returnState |= keyMapping[i]; 355 | } 356 | if (keyMapping[i] > 0xFF){ 357 | // it is a keyboard key 358 | if (out_state.values[i] && ! pushedKeys[i]){KBD_SET(keyMapping[i] >> 8); pushedKeys[i] = 1;} 359 | if (! out_state.values[i] && pushedKeys[i]){KBD_RES(keyMapping[i] >> 8); pushedKeys[i] = 0;} 360 | } 361 | } 362 | /* odroid qwerty keyboard */ 363 | if (odroidQwertyFound) { 364 | odroid_qwerty_state keys; 365 | bool newKey = odroid_qwerty_read(&keys); 366 | if (newKey) { 367 | for (int i = 0; i < ODROID_QWERTY_MAX_KEYS; i++) { 368 | 369 | /// hold shift 370 | if (odroid_qwerty_is_key_pressed(&keys, ODROID_QWERTY_SHIFT) && odroid_qwerty_is_key_pressed(&keys, ODROID_QWERTY_CTRL)) {holdShift = 2;odroid_qwerty_led_Aa(true);} 371 | else if (holdShift == 2 && ! odroid_qwerty_is_key_pressed(&keys, ODROID_QWERTY_SHIFT)) holdShift = 1; 372 | else if (holdShift == 1 && odroid_qwerty_is_key_pressed(&keys, ODROID_QWERTY_SHIFT)) {holdShift = 0;odroid_qwerty_led_Aa(false);} 373 | ///////////// 374 | if (odroidQwertyPushedKeys.values[i] != keys.values[i]) { 375 | if (keys.values[i] == ODROID_QWERTY_NONE ) { 376 | uint8_t key = toMSXKey(odroidQwertyPushedKeys.values[i], odroid_qwerty_is_key_pressed(&odroidQwertyPushedKeys, ODROID_QWERTY_CTRL)); 377 | KBD_RES(key); 378 | } else { 379 | uint8_t key = toMSXKey(keys.values[i], odroid_qwerty_is_key_pressed(&keys, ODROID_QWERTY_CTRL)); 380 | KBD_SET(key); 381 | } 382 | } 383 | if (holdShift) KBD_SET(KBD_SHIFT); 384 | } 385 | memcpy(odroidQwertyPushedKeys.values, keys.values, ODROID_QWERTY_MAX_KEYS); 386 | } 387 | } 388 | 389 | if (!inMenue){ 390 | if (out_state.values[ODROID_INPUT_VOLUME]) { 391 | if (! pushedKeys[ODROID_INPUT_VOLUME]) { 392 | audio_volume_set_change(); 393 | pushedKeys[ODROID_INPUT_VOLUME] = 1; 394 | } 395 | } else { pushedKeys[ODROID_INPUT_VOLUME] = 0; } 396 | #ifdef WITH_WLAN 397 | if (getMultiplayState() == MULTIPLAYER_NOT_CONNECTED) { 398 | #endif 399 | if (out_state.values[ODROID_INPUT_MENU]) { 400 | // go into menu 401 | pressCounter = 0; 402 | lastKey = ODROID_INPUT_MENU; //the last pressed key for the menu 403 | inMenue = 1; 404 | pause_audio(); 405 | clearOverlay(); 406 | 407 | 408 | if (vKeyboardShow) hideVirtualKeyboard(); 409 | odroidFmsxGUI_showMenu(); 410 | if (vKeyboardShow) showVirtualKeyboard(); 411 | 412 | 413 | 414 | restart_audio(); 415 | 416 | clearScreen(); 417 | } 418 | #ifdef WITH_WLAN 419 | } 420 | #endif 421 | 422 | } 423 | #ifdef WITH_WLAN 424 | if (isConnectionLost()) { 425 | pause_audio(); 426 | clearOverlay(); 427 | odroidFmsxGUI_msgBox("Connection", " \nThe connection has been lost\nPress a key to restart\n ", 1); 428 | esp_restart(); 429 | } 430 | exchangeJoystickState(&returnState); 431 | #endif 432 | 433 | return returnState; 434 | } 435 | 436 | 437 | 438 | /** TrashMachine() *******************************************/ 439 | /** Deallocate all resources taken by InitMachine(). **/ 440 | /************************************ TO BE WRITTEN BY USER **/ 441 | void TrashMachine(void){ 442 | 443 | TrashVideo(); 444 | free(lastGame); 445 | 446 | } 447 | 448 | 449 | /** GetJoystick() ********************************************/ 450 | /** Get the state of joypad buttons (1="pressed"). Refer to **/ 451 | /** the BTN_* #defines for the button mappings. Notice that **/ 452 | /** on Windows this function calls ProcessEvents() thus **/ 453 | /** automatically handling all Windows messages. **/ 454 | /*************************************************************/ 455 | unsigned int GetJoystick(void) { 456 | return 0; 457 | } 458 | /** Keyboard() ***********************************************/ 459 | /** This function is periodically called to poll keyboard. **/ 460 | /************************************ TO BE WRITTEN BY USER **/ 461 | void Keyboard(void){ 462 | /* Everything is done in Joystick() */ 463 | 464 | 465 | } 466 | 467 | /** Mouse() **************************************************/ 468 | /** Query coordinates of a mouse connected to port N. **/ 469 | /** Returns F2.F1.Y.Y.Y.Y.Y.Y.Y.Y.X.X.X.X.X.X.X.X. **/ 470 | /************************************ TO BE WRITTEN BY USER **/ 471 | unsigned int Mouse(byte N) {return 0;} 472 | 473 | /** DiskPresent()/DiskRead()/DiskWrite() *********************/ 474 | /*** These three functions are called to check for floppyd **/ 475 | /*** disk presence in the "drive", and to read/write given **/ 476 | /*** sector to the disk. **/ 477 | /************************************ TO BE WRITTEN BY USER **/ 478 | //byte DiskPresent(byte ID) {return 0;} 479 | //byte DiskRead(byte ID,byte *Buf,int N) {return 0;} 480 | //byte DiskWrite(byte ID,const byte *Buf,int N) {return 0;} 481 | 482 | 483 | 484 | 485 | 486 | -------------------------------------------------------------------------------- /main/odroidGo/Menu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2018 Schuemi. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | 26 | #include 27 | #include 28 | #include 29 | #include "LibOdroidGo.h" 30 | #include "ugui.h" 31 | #include "odroid_input.h" 32 | #include "freertos/FreeRTOS.h" 33 | #include "freertos/task.h" 34 | #include 35 | #include 36 | #include 37 | #include "MSX.h" 38 | #include "EMULib.h" 39 | #include "esp_system.h" 40 | #include "odroid_settings.h" 41 | #include "minini.h" 42 | 43 | #define MAX_OBJECTS 6 44 | 45 | 46 | #define MENU_TEXTBOX_ID 1 47 | #define FILES_TEXTBOX_ID 2 48 | #define MSG_TEXTBOX_ID 3 49 | 50 | #define FILES_MAX_ROWS 20 51 | #define FILES_MAX_LENGTH 31 52 | #define FILES_HEADER_LENGTH 27 53 | #define FILES_MAX_LENGTH_NAME 256 54 | 55 | 56 | 57 | 58 | typedef unsigned short pixelp; 59 | 60 | 61 | pixelp* pixelBuffer; 62 | 63 | UG_GUI gui; 64 | UG_WINDOW window; 65 | UG_TEXTBOX menuTextBox; 66 | UG_OBJECT obj_buff_wnd_1[MAX_OBJECTS]; 67 | 68 | UG_WINDOW fileWindow; 69 | UG_TEXTBOX fileTextBox; 70 | UG_OBJECT obj_buff_wnd_file[MAX_OBJECTS]; 71 | 72 | UG_WINDOW msgWindow; 73 | UG_TEXTBOX msgTextBox; 74 | UG_OBJECT obj_buff_wnd_msg[MAX_OBJECTS]; 75 | 76 | 77 | int m_width = WIDTH_OVERLAY; 78 | int m_height = HEIGHT_OVERLAY; 79 | int currentSelectedItem = 0; 80 | char* menuTxt; 81 | bool keyPressed; 82 | int lastPressedKey; 83 | int lastSelectedItem = 0; 84 | int holdDownCounter = 0; 85 | char* selectedFile; 86 | char* lastOpenedFileFullPath; 87 | 88 | int position = 0; 89 | int selPosition = 0; 90 | #ifdef WITH_WLAN 91 | #define MENU_ITEMS 15 92 | #else 93 | #define MENU_ITEMS 13 94 | #endif 95 | static const struct { 96 | const char* menuItem[MENU_ITEMS]; 97 | 98 | } menuItems = { 99 | "Load file\n", 100 | "Save state\n", 101 | "\n", 102 | "Eject cartridge\n", 103 | "Eject disks\n", 104 | "\n", 105 | "fMSX Menu\n", 106 | "Reset\n", 107 | "\n", 108 | "Audio output\n", 109 | "\n", 110 | #ifdef WITH_WLAN 111 | "Start multiplayer server\n", 112 | "Start multiplayer client\n", 113 | #endif 114 | "\n", 115 | "About\n", 116 | 117 | 118 | }; 119 | 120 | void pixelSet(UG_S16 ul_x,UG_S16 ul_y, UG_COLOR ul_color){ 121 | if (ul_x > m_width || ul_y > m_height) return; 122 | pixelBuffer[ul_x + (ul_y*m_width)] = ul_color; 123 | } 124 | 125 | void window_callback( UG_MESSAGE* msg ) { 126 | 127 | } 128 | void odroidFmsxGUI_initMenu() { 129 | 130 | 131 | pixelBuffer = (pixelp*)heap_caps_malloc(m_width*m_height*sizeof(pixelp), MALLOC_CAP_SPIRAM); 132 | memset(pixelBuffer, 0, m_width*m_height*sizeof(pixelp)); 133 | 134 | selectedFile = (char*)heap_caps_malloc(FILES_MAX_LENGTH_NAME+1, MALLOC_CAP_SPIRAM); 135 | lastOpenedFileFullPath = (char*)malloc(1024); 136 | lastOpenedFileFullPath[0] = 0; 137 | 138 | menuTxt = (char*)heap_caps_malloc(1000, MALLOC_CAP_SPIRAM); 139 | 140 | UG_Init(&gui,pixelSet,m_width, m_height); 141 | 142 | UG_WindowCreate ( &window , obj_buff_wnd_1 , MAX_OBJECTS, window_callback); 143 | UG_WindowResize(&window, 10, 10, 310, 220); 144 | UG_WindowSetTitleTextFont ( &window , &FONT_8X8 ) ; 145 | UG_WindowSetTitleText ( &window , "ODROID-GO fMSX" ) ; 146 | UG_WindowSetBackColor( &window , C_GRAY ); 147 | UG_TextboxCreate(&window, &menuTextBox, MENU_TEXTBOX_ID, 10, 10, 290, 160); 148 | UG_TextboxSetAlignment(&window, MENU_TEXTBOX_ID, ALIGN_TOP_LEFT); 149 | UG_TextboxSetForeColor(&window, MENU_TEXTBOX_ID, C_BLACK); 150 | UG_TextboxSetFont ( &window , MENU_TEXTBOX_ID, &FONT_8X8 ) ; 151 | 152 | 153 | UG_TextboxShow(&window, MENU_TEXTBOX_ID); 154 | UG_WindowShow(&window); 155 | 156 | 157 | 158 | 159 | } 160 | 161 | int odroidFmsxGUI_getKey() { 162 | odroid_gamepad_state out_state; 163 | odroid_input_gamepad_read(&out_state); 164 | keyPressed = false; 165 | for (int i = 0; i < ODROID_INPUT_MAX; i++) if (out_state.values[i]) keyPressed = true; 166 | if (keyPressed && lastPressedKey != -1){ 167 | holdDownCounter++; 168 | if (holdDownCounter > 200) return lastPressedKey; 169 | return -1; 170 | } 171 | holdDownCounter = 0; 172 | 173 | if (!keyPressed) lastPressedKey = -1; else { 174 | if (out_state.values[ODROID_INPUT_UP]) lastPressedKey = ODROID_INPUT_UP; 175 | if (out_state.values[ODROID_INPUT_DOWN]) lastPressedKey = ODROID_INPUT_DOWN; 176 | if (out_state.values[ODROID_INPUT_LEFT]) lastPressedKey = ODROID_INPUT_LEFT; 177 | if (out_state.values[ODROID_INPUT_RIGHT]) lastPressedKey = ODROID_INPUT_RIGHT; 178 | if (out_state.values[ODROID_INPUT_A]) lastPressedKey = ODROID_INPUT_A; 179 | if (out_state.values[ODROID_INPUT_B]) lastPressedKey = ODROID_INPUT_B; 180 | if (out_state.values[ODROID_INPUT_MENU]) lastPressedKey = ODROID_INPUT_MENU; 181 | } 182 | return lastPressedKey; 183 | } 184 | int odroidFmsxGUI_getKey_block() { 185 | int key; 186 | do{ 187 | vTaskDelay(1 / portTICK_PERIOD_MS); 188 | key = odroidFmsxGUI_getKey(); 189 | }while (key == -1); 190 | return key; 191 | } 192 | 193 | 194 | 195 | 196 | 197 | const char* odroidFmsxGUI_chooseFile(const char *Ext) { 198 | 199 | DIR *D; 200 | struct dirent *DP; 201 | struct stat ST; 202 | 203 | 204 | char* Buf; 205 | int BufSize = 256; 206 | char* txtFiles; 207 | char* shownFiles[FILES_MAX_ROWS]; 208 | 209 | 210 | txtFiles = heap_caps_malloc(FILES_MAX_ROWS*(FILES_MAX_LENGTH + 5) + 1, MALLOC_CAP_SPIRAM); 211 | for (int i = 0; i < FILES_MAX_ROWS; i++) { 212 | shownFiles[i] = heap_caps_malloc(FILES_MAX_LENGTH_NAME+1, MALLOC_CAP_SPIRAM); 213 | } 214 | 215 | Buf = (char*)heap_caps_malloc(256, MALLOC_CAP_SPIRAM); 216 | char* txtFilesPosition; 217 | 218 | 219 | 220 | int i, r, s, fileCount; 221 | 222 | 223 | bool isDir[FILES_MAX_ROWS]; 224 | bool switchedPage = true; 225 | 226 | UG_WindowCreate ( &fileWindow , obj_buff_wnd_file , MAX_OBJECTS, window_callback); 227 | UG_WindowResize(&fileWindow, 20, 20, 300, 210); 228 | UG_WindowSetTitleTextFont ( &fileWindow , &FONT_8X8 ) ; 229 | 230 | if(!getcwd(Buf,BufSize-2)) strncpy(Buf,"Choose File", 256); 231 | txtFilesPosition = Buf; 232 | if (strlen(txtFilesPosition) > FILES_HEADER_LENGTH) txtFilesPosition += strlen(txtFilesPosition) - FILES_HEADER_LENGTH; 233 | 234 | 235 | 236 | UG_WindowSetTitleText ( &fileWindow , txtFilesPosition ) ; 237 | UG_WindowSetBackColor( &fileWindow , C_GRAY ); 238 | UG_TextboxCreate(&fileWindow, &fileTextBox, FILES_TEXTBOX_ID, 1, 1, 274, 80); 239 | 240 | UG_TextboxSetAlignment(&fileWindow, FILES_TEXTBOX_ID, ALIGN_TOP_LEFT); 241 | UG_TextboxSetForeColor(&fileWindow, FILES_TEXTBOX_ID, C_BLACK); 242 | UG_TextboxSetFont ( &fileWindow , FILES_TEXTBOX_ID, &FONT_8X8 ) ; 243 | UG_TextboxShow(&fileWindow, FILES_TEXTBOX_ID); 244 | UG_WindowShow(&fileWindow); 245 | 246 | int keyNumPressed; 247 | 248 | if((D=_opendir("."))){ 249 | 250 | 251 | fileCount = 0; 252 | // count how many files we have here 253 | for (_rewinddir(D); (DP=_readdir(D));){ 254 | if (DP->d_type==DT_DIR || hasExt(DP->d_name, Ext)) {fileCount++;} 255 | } 256 | 257 | do { 258 | 259 | txtFilesPosition = txtFiles; 260 | 261 | // read a new Page 262 | if (switchedPage){ 263 | // first, go to the position 264 | _rewinddir(D); 265 | for (s=0; s < position && (DP=_readdir(D)); s++){ 266 | if (DP->d_type!=DT_DIR && !hasExt(DP->d_name, Ext)) {s--; continue;} 267 | } 268 | // than read the next FILES_MAX_ROWS files 269 | for(i=0;(i < FILES_MAX_ROWS && (DP=_readdir(D)));i++) { 270 | isDir[i] = false; 271 | if (DP->d_type==DT_DIR) isDir[i] = true; 272 | if (isDir[i] == false && !hasExt(DP->d_name, Ext)) {i--; continue;} 273 | strncpy(shownFiles[i],DP->d_name,FILES_MAX_LENGTH_NAME); 274 | } 275 | switchedPage = false; 276 | if (i == 0) { 277 | // nothing on this page, go to the first page, if we are not already 278 | if (position >= FILES_MAX_ROWS) { 279 | position = 0; 280 | selPosition = 0; 281 | switchedPage = true; 282 | continue; 283 | } 284 | 285 | } 286 | } 287 | 288 | /// Draw the TextBox 289 | r = i; 290 | for(i=0;i= position + i) { 327 | // go to the first page 328 | selPosition = 0; position = 0; switchedPage = true; 329 | } 330 | 331 | if (selPosition >= FILES_MAX_ROWS + position){ 332 | // go to next page 333 | position += FILES_MAX_ROWS; selPosition = position; switchedPage = true; 334 | } 335 | 336 | if (selPosition < position){ 337 | // go to previous page 338 | position -= FILES_MAX_ROWS; 339 | if (position < 0) position = 0; 340 | selPosition = position + FILES_MAX_ROWS - 1; 341 | switchedPage = true; 342 | } 343 | if (selPosition - position >= 0 && selPosition - position < FILES_MAX_ROWS && keyNumPressed == ODROID_INPUT_A && isDir[selPosition - position]) { 344 | free(txtFiles); 345 | free(Buf); 346 | UG_TextboxDelete(&fileWindow, FILES_TEXTBOX_ID); 347 | UG_WindowDelete(&fileWindow); 348 | chdir(shownFiles[selPosition - position]); 349 | selPosition = 0; 350 | for (int i = 0; i < FILES_MAX_ROWS; i++) free(shownFiles[i]); 351 | closedir(D); 352 | return odroidFmsxGUI_chooseFile(Ext); 353 | } 354 | if(keyNumPressed == ODROID_INPUT_B) { 355 | free(txtFiles); 356 | free(Buf); 357 | UG_TextboxDelete(&fileWindow, FILES_TEXTBOX_ID); 358 | UG_WindowDelete(&fileWindow); 359 | chdir(".."); 360 | selPosition = 0; 361 | for (int i = 0; i < FILES_MAX_ROWS; i++) free(shownFiles[i]); 362 | closedir(D); 363 | return odroidFmsxGUI_chooseFile(Ext); 364 | 365 | } 366 | 367 | 368 | } while(keyNumPressed != ODROID_INPUT_A && keyNumPressed != ODROID_INPUT_MENU); 369 | 370 | 371 | closedir(D); 372 | } 373 | if(keyNumPressed == ODROID_INPUT_A) { 374 | strncpy(selectedFile, shownFiles[selPosition - position], FILES_MAX_LENGTH_NAME); 375 | } 376 | 377 | free(txtFiles); 378 | free(Buf); 379 | for (int i = 0; i < FILES_MAX_ROWS; i++) free(shownFiles[i]); 380 | UG_TextboxDelete(&fileWindow, FILES_TEXTBOX_ID); 381 | UG_WindowDelete(&fileWindow); 382 | 383 | if(keyNumPressed == ODROID_INPUT_A) { 384 | return selectedFile; 385 | } 386 | return NULL; 387 | } 388 | void odroidFmsxGUI_selectMenuItem(int item) { 389 | char* menuPos = menuTxt; 390 | for (int i = 0; i < MENU_ITEMS; i++) { 391 | if (item == i) *menuPos = 16; else *menuPos = 0x20; 392 | menuPos++;*menuPos = 0x20;menuPos++; 393 | int len = strlen(menuItems.menuItem[i]); 394 | memcpy(menuPos, menuItems.menuItem[i], len); 395 | menuPos += len; 396 | } 397 | *menuPos = 0; 398 | UG_TextboxSetText( &window , MENU_TEXTBOX_ID, menuTxt); 399 | UG_TextboxSetAlignment(&window , MENU_TEXTBOX_ID,ALIGN_H_LEFT|ALIGN_V_CENTER); 400 | } 401 | 402 | void odroidFmsxGUI_setLastLoadedFile(const char* file) { 403 | if (file != NULL) 404 | strncpy(lastOpenedFileFullPath, file, 1024); 405 | else 406 | lastOpenedFileFullPath[0] = 0; 407 | } 408 | 409 | // msg Box: max 33 letters in one row! 410 | 411 | void odroidFmsxGUI_msgBox(const char* title, const char* msg, char waitKey) { 412 | int rows = 1; 413 | const char* p = msg; 414 | while(*p++ != 0){ 415 | if (*p == '\n') rows++; 416 | } 417 | 418 | // if there is a old instance 419 | UG_TextboxDelete(&msgWindow, MSG_TEXTBOX_ID); 420 | UG_WindowDelete(&msgWindow); 421 | 422 | 423 | UG_WindowCreate ( &msgWindow , obj_buff_wnd_file , MAX_OBJECTS, window_callback); 424 | UG_WindowResize(&msgWindow, 20, 20, 300, 54 + (rows*8)); 425 | 426 | 427 | 428 | UG_WindowSetTitleTextFont ( &msgWindow , &FONT_8X8 ) ; 429 | UG_WindowSetTitleText ( &msgWindow , title ) ; 430 | UG_WindowSetBackColor( &msgWindow , C_GRAY ); 431 | 432 | 433 | UG_TextboxCreate(&msgWindow, &msgTextBox, MSG_TEXTBOX_ID, 6, 6, 274, 12+rows*8); 434 | 435 | UG_TextboxSetAlignment(&msgWindow, MSG_TEXTBOX_ID, ALIGN_TOP_LEFT); 436 | UG_TextboxSetForeColor(&msgWindow, MSG_TEXTBOX_ID, C_BLACK); 437 | UG_TextboxSetFont ( &msgWindow , MSG_TEXTBOX_ID, &FONT_8X8 ) ; 438 | UG_TextboxShow(&msgWindow, MSG_TEXTBOX_ID); 439 | UG_WindowShow(&msgWindow); 440 | 441 | UG_TextboxSetText( &msgWindow , MSG_TEXTBOX_ID, msg); 442 | 443 | 444 | UG_Update(); 445 | DrawuGui(pixelBuffer, 0); 446 | 447 | if (waitKey && odroidFmsxGUI_getKey_block()){} 448 | 449 | 450 | 451 | } 452 | char saveState(const char* fileName) { 453 | char res = 1; 454 | if (! fileName) { 455 | char* stateFileName = (char*)heap_caps_malloc(1024, MALLOC_CAP_SPIRAM); 456 | char* stateFileNameF = (char*)heap_caps_malloc(1024, MALLOC_CAP_SPIRAM); 457 | strncpy(stateFileName, lastOpenedFileFullPath, 1024); 458 | cutExtension(stateFileName); 459 | snprintf(stateFileNameF, 1024, "%s.sta", stateFileName); 460 | if (!SaveSTA(stateFileNameF)){ 461 | res = 0; 462 | } 463 | free(stateFileName); 464 | free(stateFileNameF); 465 | } else { 466 | SaveSTA(fileName); 467 | } 468 | return res; 469 | } 470 | MENU_ACTION odroidFmsxGUI_showMenu() { 471 | 472 | char stopMenu = 0; 473 | odroidFmsxGUI_selectMenuItem(currentSelectedItem); 474 | MENU_ACTION ret = MENU_ACTION_NONE; 475 | 476 | 477 | 478 | // wait until the menu button is not pressed anymore 479 | int keyPressed; 480 | do { 481 | keyPressed = odroidFmsxGUI_getKey(); 482 | }while (keyPressed == ODROID_INPUT_MENU); 483 | 484 | 485 | /// now listen for another button 486 | 487 | do { 488 | int c = 0; 489 | char buf[3]; 490 | UG_WindowShow(&window); // force a window update 491 | UG_Update(); 492 | DrawuGui(pixelBuffer, 0); 493 | 494 | keyPressed = odroidFmsxGUI_getKey_block(); 495 | if (keyPressed == ODROID_INPUT_DOWN) c = 1; 496 | if (keyPressed == ODROID_INPUT_UP) c = -1; 497 | 498 | currentSelectedItem += c; 499 | 500 | if (currentSelectedItem < 0)currentSelectedItem = MENU_ITEMS - 1; 501 | if (currentSelectedItem >= MENU_ITEMS)currentSelectedItem = 0; 502 | 503 | if (menuItems.menuItem[currentSelectedItem][0] == '\n') currentSelectedItem += c; 504 | 505 | 506 | if (lastSelectedItem != currentSelectedItem) { 507 | lastSelectedItem = currentSelectedItem; 508 | odroidFmsxGUI_selectMenuItem(currentSelectedItem); 509 | } 510 | 511 | if (keyPressed == ODROID_INPUT_A) { 512 | const char* lastSelectedFile = NULL; 513 | switch(currentSelectedItem){ 514 | ///////////// Load File /////////////////// 515 | case 0: 516 | lastSelectedFile = odroidFmsxGUI_chooseFile(".rom\0.mx1\0.mx2\0.dsk\0.cas\0\0"); 517 | odroidFmsxGUI_msgBox("Please wait...", "Please wait while loading", 0); 518 | if (lastSelectedFile != NULL) { 519 | if (LoadFile(lastSelectedFile)){ 520 | getFullPath(lastOpenedFileFullPath, lastSelectedFile, 1024); 521 | ini_puts("FMSX", "LASTGAME", lastOpenedFileFullPath, FMSX_CONFIG_FILE); 522 | loadKeyMappingFromGame(lastSelectedFile); 523 | } else lastOpenedFileFullPath[0] = 0; 524 | 525 | stopMenu = true; 526 | } 527 | break; 528 | 529 | //////////////// Save State //////////////////// 530 | case 1: 531 | if (lastOpenedFileFullPath[0] != 0) { 532 | odroidFmsxGUI_msgBox("Please wait...", "Please wait while saving", 0); 533 | if (!saveState(NULL)) { 534 | odroidFmsxGUI_msgBox("Error", "Could not save state", 1); 535 | } 536 | } 537 | stopMenu = true; 538 | break; 539 | 540 | //////////////// Eject Cardridge //////////////////// 541 | case 3: 542 | for(int J=0;J 29 | #include "freertos/FreeRTOS.h" 30 | #include "freertos/task.h" 31 | #include "freertos/event_groups.h" 32 | #include "esp_system.h" 33 | #include "esp_wifi.h" 34 | #include "esp_event_loop.h" 35 | #include "esp_log.h" 36 | 37 | #include "nvs_flash.h" 38 | 39 | #include "lwip/err.h" 40 | #include "lwip/sys.h" 41 | 42 | #include "MSX.h" 43 | 44 | #include "odroid_settings.h" 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | 52 | 53 | #define EXAMPLE_ESP_WIFI_MODE_AP 1 //CONFIG_ESP_WIFI_MODE_AP //TRUE:AP FALSE:STA 54 | #define MP_ESP_WIFI_SSID "fMSXMultiplayerV0.01" 55 | #define MP_ESP_WIFI_PASS "" 56 | #define MP_MAX_STA_CONN 1 57 | 58 | /* FreeRTOS event group to signal when we are connected*/ 59 | static EventGroupHandle_t wifi_event_group; 60 | 61 | /*server and client socket*/ 62 | int LSocket,SSocket; 63 | struct sockaddr_in Addr; 64 | socklen_t AddrLength; 65 | fd_set FDs; 66 | struct timeval TV; 67 | static struct sockaddr_in PeerAddr; 68 | 69 | 70 | unsigned char currentLocalTickNumber = 0; 71 | unsigned char currentRemoteTickNumber = 0; 72 | char currentLocalJoy = 0; 73 | char lastLocalJoy = 0; 74 | 75 | 76 | char currentremoteJoy = 0; 77 | char lastremoteJoy = 0; 78 | char stopNet = 0; 79 | char gotRemoteData = 0; 80 | char tcpSend = 0; 81 | bool connectionLost = false; 82 | 83 | char playFileName[1024]; 84 | void copyFile(const char* fileName, const char* destination); 85 | char sendFile(const char* fileName); 86 | char recievFile(const char* fileName, char toMemory, char*memory); 87 | void sendDataBlob(const char* data, uint16_t size); 88 | uint16_t recievDataBlob(char* data, uint16_t maxSize); 89 | int NETSend(const char *Out,int N); 90 | int NETRecv(char *In,int N); 91 | int gotAck = -1; 92 | 93 | 94 | /* The event group allows multiple bits for each event, 95 | but we only care about one event - are we connected 96 | to the AP with an IP? */ 97 | const int WIFI_CONNECTED_BIT = BIT0; 98 | enum MP_SERVER_STATE{ 99 | MP_NO_CONNECTION = 0, 100 | MP_CLIENT_IS_CONNECTING, 101 | MP_CLIENT_DISCONNECTED, 102 | STA_GOT_IP 103 | } ; 104 | 105 | 106 | enum MP_SERVER_STATE server_state; 107 | MULTIPLAYER_STATE mpState = MULTIPLAYER_NOT_CONNECTED; 108 | 109 | 110 | void sendTask(void* arg) 111 | { 112 | 113 | char sendBuf[20]; 114 | 115 | memset(sendBuf, 0, 20); 116 | while(!stopNet) 117 | { 118 | if (tcpSend && gotAck != currentLocalTickNumber) { 119 | sendBuf[0] = currentRemoteTickNumber; 120 | sendBuf[1] = currentLocalTickNumber; 121 | sendBuf[2] = lastLocalJoy; 122 | sendBuf[3] = currentLocalJoy; 123 | if (mpState == MULTIPLAYER_CONNECTED_SERVER) memcpy(sendBuf + 4, KeyState, 16); 124 | NETSend(sendBuf,20); 125 | } 126 | 127 | vTaskDelay(7 / portTICK_PERIOD_MS); 128 | 129 | 130 | } 131 | 132 | 133 | 134 | vTaskDelete(NULL); 135 | 136 | 137 | } 138 | 139 | void recievTask(void* arg) 140 | { 141 | 142 | 143 | char recievBuf[20]; 144 | 145 | while(!stopNet) 146 | { 147 | recievBuf[0] = 0; 148 | if(NETRecv(recievBuf,20)==20) { 149 | if (! gotRemoteData) { 150 | currentremoteJoy = recievBuf[3]; 151 | lastremoteJoy = recievBuf[2]; 152 | if (currentRemoteTickNumber != recievBuf[1]){gotRemoteData = 1; } 153 | currentRemoteTickNumber = recievBuf[1]; 154 | if (currentLocalTickNumber == recievBuf[0]) gotAck = recievBuf[0]; 155 | } 156 | if (mpState == MULTIPLAYER_CONNECTED_CLIENT) memcpy(KeyState, recievBuf+4, 16); 157 | 158 | 159 | 160 | } else { 161 | if (recievBuf[0] != 0xff) NETRecv(recievBuf,1); 162 | } 163 | 164 | 165 | } 166 | 167 | 168 | 169 | vTaskDelete(NULL); 170 | 171 | 172 | } 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | static esp_err_t event_handler(void *ctx, system_event_t *event) 186 | { 187 | printf("Got event: %d\n", event->event_id); 188 | switch(event->event_id) { 189 | case SYSTEM_EVENT_STA_START: 190 | esp_wifi_connect(); 191 | break; 192 | case SYSTEM_EVENT_STA_GOT_IP: 193 | printf("got ip:%s\n", 194 | ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); 195 | xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); 196 | server_state = STA_GOT_IP; 197 | break; 198 | case SYSTEM_EVENT_AP_STACONNECTED: 199 | printf("Player is connecting "MACSTR"AID=%d\n", MAC2STR(event->event_info.sta_connected.mac), event->event_info.sta_connected.aid); 200 | server_state = MP_CLIENT_IS_CONNECTING; 201 | break; 202 | case SYSTEM_EVENT_AP_STADISCONNECTED: 203 | printf("Player is disconnected "MACSTR"AID=%d\n", MAC2STR(event->event_info.sta_connected.mac), event->event_info.sta_connected.aid); 204 | server_state = MP_CLIENT_DISCONNECTED; 205 | break; 206 | case SYSTEM_EVENT_STA_DISCONNECTED: 207 | esp_wifi_connect(); 208 | xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT); 209 | break; 210 | default: 211 | break; 212 | } 213 | return ESP_OK; 214 | } 215 | 216 | void wifi_init_softap() 217 | { 218 | wifi_event_group = xEventGroupCreate(); 219 | 220 | tcpip_adapter_init(); 221 | ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); 222 | 223 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 224 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 225 | 226 | wifi_config_t wifi_config = { 227 | .ap = { 228 | .ssid = MP_ESP_WIFI_SSID, 229 | .ssid_len = strlen(MP_ESP_WIFI_SSID), 230 | .password = MP_ESP_WIFI_PASS, 231 | .max_connection = MP_MAX_STA_CONN, 232 | .authmode = WIFI_AUTH_WPA_WPA2_PSK, 233 | .ssid_hidden = 1 234 | }, 235 | }; 236 | if (strlen(MP_ESP_WIFI_PASS) == 0) { 237 | wifi_config.ap.authmode = WIFI_AUTH_OPEN; 238 | } 239 | 240 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); 241 | //ESP_ERROR_CHECK( esp_wifi_set_protocol(WIFI_IF_AP, WIFI_PROTOCOL_LR) ); 242 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config)); 243 | ESP_ERROR_CHECK(esp_wifi_start()); 244 | 245 | printf("wifi_init_softap finished.SSID:%s password:%s", 246 | MP_ESP_WIFI_SSID, MP_ESP_WIFI_PASS); 247 | } 248 | 249 | void wifi_init_sta() 250 | { 251 | wifi_event_group = xEventGroupCreate(); 252 | 253 | tcpip_adapter_init(); 254 | ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) ); 255 | 256 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 257 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 258 | 259 | wifi_config_t wifi_config = { 260 | .sta = { 261 | .ssid = MP_ESP_WIFI_SSID, 262 | .password = MP_ESP_WIFI_PASS 263 | }, 264 | }; 265 | 266 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 267 | //ESP_ERROR_CHECK( esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR) ); 268 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); 269 | ESP_ERROR_CHECK(esp_wifi_start() ); 270 | 271 | printf ("wifi_init_sta finished.\n"); 272 | printf ("connect to ap SSID:%s password:%s\n", 273 | MP_ESP_WIFI_SSID, MP_ESP_WIFI_PASS); 274 | } 275 | 276 | MULTIPLAYER_STATE getMultiplayState(){ 277 | return mpState; 278 | } 279 | 280 | 281 | void server_init() 282 | { 283 | mpState = MULTIPLAYER_INIT; 284 | server_state = MP_NO_CONNECTION; 285 | wifi_init_softap(); 286 | 287 | 288 | currentRemoteTickNumber = 99; 289 | 290 | } 291 | void client_init() { 292 | mpState = MULTIPLAYER_INIT; 293 | server_state = MP_NO_CONNECTION; 294 | wifi_init_sta(); 295 | 296 | 297 | currentRemoteTickNumber = 98; 298 | } 299 | int waitKeyOrStatusChange() 300 | { 301 | int key; 302 | int currStatus = server_state; 303 | do{ 304 | vTaskDelay(1 / portTICK_PERIOD_MS); 305 | key = odroidFmsxGUI_getKey(); 306 | }while (key == -1 && currStatus == server_state); 307 | return key; 308 | } 309 | const char* getMPFileName() { 310 | return playFileName; 311 | } 312 | void client_try_connect() 313 | { 314 | 315 | odroidFmsxGUI_msgBox("Multiplayer", "Try to connect to server...\n\nPress a key to stop trying", 0); 316 | odroid_settings_WLAN_set(ODROID_WLAN_NONE); 317 | int key; 318 | playFileName[0] = 0; 319 | if (server_state == MP_NO_CONNECTION) key = waitKeyOrStatusChange(); 320 | 321 | if (server_state == STA_GOT_IP) { 322 | odroidFmsxGUI_msgBox("Multiplayer", "We are connecting...", 0); 323 | printf("NET-CLIENT: Connecting to Server...\n"); 324 | ip4_addr_t ipv4addr; 325 | IP4_ADDR(&ipv4addr, 192, 168 , 4, 1); 326 | memset(&Addr,0,sizeof(Addr)); 327 | Addr.sin_addr.s_addr = ipv4addr.addr; 328 | Addr.sin_family = AF_INET; 329 | Addr.sin_port = htons(1234); 330 | 331 | /* Create a socket */ 332 | 333 | memcpy(&PeerAddr,&Addr,sizeof(PeerAddr)); 334 | if((SSocket=socket(AF_INET,SOCK_DGRAM,0))<0) return; 335 | 336 | 337 | 338 | printf("NET-CLIENT: Created socket...\n"); 339 | 340 | mpState = MULTIPLAYER_CONNECTED_CLIENT; 341 | char buffer[6]; 342 | memcpy(buffer, "Hello", 6); 343 | NETSend(buffer, 6); 344 | 345 | printf("wait rom...\n"); 346 | 347 | int g = recievDataBlob(playFileName, 1024); 348 | printf("filename: %s\n", playFileName); 349 | 350 | /*struct timeval to; 351 | to.tv_sec = 1; 352 | to.tv_usec = 0; 353 | setsockopt(SSocket,SOL_SOCKET,SO_RCVTIMEO,&to,sizeof(to));*/ 354 | 355 | 356 | xTaskCreatePinnedToCore(&sendTask, "sendTask", 2048, NULL, 2, NULL, 1); 357 | xTaskCreatePinnedToCore(&recievTask, "recievTask", 2048, NULL, 2, NULL, 1); 358 | 359 | 360 | } 361 | 362 | 363 | } 364 | 365 | 366 | void server_wait_for_player() 367 | { 368 | server_state = MP_NO_CONNECTION; 369 | odroidFmsxGUI_msgBox("Multiplayer", "Waiting for player...\n\nPress a key to stop waiting", 0); 370 | odroid_settings_WLAN_set(ODROID_WLAN_NONE); 371 | playFileName[0] = 0; 372 | 373 | if (waitKeyOrStatusChange() == -1 && server_state == MP_CLIENT_IS_CONNECTING) { 374 | odroidFmsxGUI_msgBox("Multiplayer", "Player is connecting...", 0); 375 | 376 | memset(&Addr,0,sizeof(Addr)); 377 | Addr.sin_addr.s_addr = htonl(INADDR_ANY); 378 | Addr.sin_family = AF_INET; 379 | Addr.sin_port = htons(1234); 380 | memcpy(&PeerAddr,&Addr,sizeof(PeerAddr)); 381 | if((SSocket=socket(AF_INET,SOCK_DGRAM,0))<0) return; 382 | if(bind(SSocket,(struct sockaddr *)&Addr,sizeof(Addr))<0) 383 | { close(SSocket);return; } 384 | 385 | 386 | 387 | mpState = MULTIPLAYER_CONNECTED_SERVER; 388 | 389 | char buffer[1024]; 390 | printf("wait message...\n"); 391 | int s = NETRecv(buffer+s, 6); 392 | printf("!!!!!!!%d got message: %s\n", s, buffer); 393 | 394 | 395 | /* 396 | to.tv_sec = 0; 397 | to.tv_usec = 10000; 398 | setsockopt(SSocket,SOL_SOCKET,SO_SNDTIMEO,&to,sizeof(to)); */ 399 | /*struct timeval to; 400 | to.tv_sec = 1; 401 | to.tv_usec = 0; 402 | setsockopt(SSocket,SOL_SOCKET,SO_RCVTIMEO,&to,sizeof(to));*/ 403 | 404 | char* rom = odroid_settings_RomFilePath_get(); 405 | sendDataBlob(rom, strlen(rom) + 1); 406 | memcpy(playFileName, rom, strlen(rom) + 1); 407 | free(rom); 408 | 409 | 410 | xTaskCreatePinnedToCore(&sendTask, "sendTask", 2048, NULL, 5, NULL, 1); 411 | xTaskCreatePinnedToCore(&recievTask, "recievTask", 2048, NULL, 5, NULL, 1); 412 | 413 | 414 | 415 | } 416 | 417 | 418 | } 419 | 420 | 421 | /** NETSend() ************************************************/ 422 | /** Send N bytes. Returns number of bytes sent or 0. **/ 423 | /*************************************************************/ 424 | int NETSend(const char *Out,int N) 425 | { 426 | int J,I; 427 | 428 | if (mpState != MULTIPLAYER_CONNECTED_CLIENT && mpState != MULTIPLAYER_CONNECTED_SERVER) return 0; 429 | 430 | /* Send data */ 431 | for(I=J=N;(J>=0)&&I;) 432 | { 433 | 434 | J = sendto(SSocket,Out,I,0,(struct sockaddr *)&PeerAddr,sizeof(PeerAddr)); 435 | 436 | if(J>0) { Out+=J;I-=J; } 437 | } 438 | 439 | /* Return number of bytes sent */ 440 | return(N-I); 441 | } 442 | 443 | /** NETRecv() ************************************************/ 444 | /** Receive N bytes. Returns number of bytes received or 0. **/ 445 | /*************************************************************/ 446 | int NETRecv(char *In,int N) 447 | { 448 | int J,I; 449 | socklen_t AddrLen = sizeof(PeerAddr); 450 | /* Have to have a socket */ 451 | if (mpState != MULTIPLAYER_CONNECTED_CLIENT && mpState != MULTIPLAYER_CONNECTED_SERVER) return 0; 452 | 453 | 454 | 455 | /* Receive data */ 456 | for(I=J=N;(J>=0)&&I;) 457 | { 458 | 459 | J = recvfrom(SSocket,In,I,0,(struct sockaddr *)&PeerAddr,&AddrLen); 460 | 461 | if(J>0) { In+=J;I-=J; } 462 | } 463 | 464 | 465 | /* Return number of bytes received */ 466 | return(N-I); 467 | } 468 | uint16_t crc16(const unsigned char* data_p, int length){ 469 | uint8_t x; 470 | uint16_t crc = 0xFFFF; 471 | 472 | while (length--){ 473 | x = crc >> 8 ^ *data_p++; 474 | x ^= x>>4; 475 | crc = (crc << 8) ^ ((unsigned short)(x << 12)) ^ ((unsigned short)(x <<5)) ^ ((unsigned short)x); 476 | } 477 | return crc; 478 | } 479 | #define FILEBUFFER_SIZE 1024 480 | void copyFile(const char* fileName, const char* destination) { 481 | 482 | FILE* source = _fopen(fileName, "rb"); 483 | FILE* dest = _fopen(destination, "wb"); 484 | if (! source || ! dest) return; 485 | char* buffer; 486 | buffer = (char*)malloc(FILEBUFFER_SIZE); 487 | size_t s; 488 | do { 489 | 490 | s = _fread(buffer,1,FILEBUFFER_SIZE,source); 491 | _fwrite(buffer, 1, s, dest); 492 | } while (s == FILEBUFFER_SIZE); 493 | 494 | _fclose(source); 495 | _fclose(dest); 496 | free(buffer); 497 | 498 | return; 499 | 500 | } 501 | 502 | /////// sendFile and recievFile are for future use, not working yet 503 | 504 | char sendFile(const char* fileName) { 505 | char* buffer; 506 | buffer = (char*)malloc(FILEBUFFER_SIZE); 507 | FILE* f = _fopen(fileName, "rb"); 508 | if (! f) {printf("could not open file! %s\n", fileName);return 0;} 509 | _fseek(f,0,SEEK_END); 510 | size_t size=_ftell(f); 511 | _rewind(f); 512 | 513 | NETSend((char*)&size, 4); 514 | 515 | uint16_t parnum = 0; 516 | uint16_t parts = (size / FILEBUFFER_SIZE) + 1; 517 | while (parnum < parts) { 518 | _fseek(f,0,parnum*FILEBUFFER_SIZE); 519 | size_t s = _fread(buffer + 2,1,FILEBUFFER_SIZE-4,f); 520 | memcpy(buffer, &parnum, 2); 521 | 522 | uint16_t crc = crc16(buffer, s+2); 523 | memcpy(buffer + s + 2, (char*)&crc , 2); 524 | sendDataBlob(buffer, s + 4); 525 | NETRecv((char*)&parnum, 2); 526 | 527 | } 528 | _fclose(f); 529 | 530 | free(buffer); 531 | 532 | return 1; 533 | } 534 | 535 | 536 | char recievFile(const char* fileName, char toMemory, char*memory) { 537 | char* buffer; 538 | char* memPosition; 539 | buffer = (char*)malloc(FILEBUFFER_SIZE); 540 | FILE* f = NULL; 541 | if (! toMemory) { 542 | f = _fopen(fileName, "wb"); 543 | if (f==NULL) return 0; 544 | } 545 | size_t size = 0; 546 | size_t recievedSize = 0; 547 | NETRecv((char*)&size, 4); 548 | uint16_t gettingPartnum = 0; 549 | uint16_t partnum = 0; 550 | uint16_t parts = (size / FILEBUFFER_SIZE) + 1; 551 | if (toMemory) memPosition = memory = malloc(size); 552 | 553 | while(gettingPartnum < parts) { 554 | uint16_t r = recievDataBlob(buffer, FILEBUFFER_SIZE); 555 | memcpy((char*)&partnum, buffer, 2); 556 | if (r < 4) { NETSend((char*)&gettingPartnum, 2); continue;} 557 | if (partnum != gettingPartnum) { NETSend((char*)&gettingPartnum, 2); continue;} // what part should this be? 558 | 559 | uint16_t crc = crc16(buffer, r-2); 560 | uint16_t recrc; 561 | memcpy((char*)&recrc, buffer + (r - 2), 2); 562 | if (crc != recrc) { NETSend((char*)&gettingPartnum, 2); continue;} 563 | 564 | gettingPartnum++; 565 | NETSend((char*)&gettingPartnum, 2); 566 | 567 | recievedSize += r; 568 | if (toMemory){ 569 | memcpy(memPosition, buffer + 2, r - 4); 570 | memPosition += (r-4); 571 | } 572 | else 573 | _fwrite(buffer + 2, 1, r - 4, f); 574 | 575 | 576 | } 577 | 578 | if (! toMemory)_fclose(f); 579 | 580 | free(buffer); 581 | 582 | return 1; 583 | } 584 | 585 | void sendDataBlob(const char* data, uint16_t size) { 586 | NETSend((char*)&size, 2); 587 | NETSend(data, size); 588 | 589 | } 590 | uint16_t recievDataBlob(char* data, uint16_t maxSize) { 591 | 592 | uint16_t datalength; 593 | NETRecv((char*)&datalength, 2); 594 | //printf("Got datalength: %d\n", datalength); 595 | uint16_t tooMuch = 0; 596 | 597 | if (datalength > maxSize) {tooMuch = datalength - maxSize; datalength = maxSize;} 598 | uint16_t r = NETRecv(data, datalength); 599 | //printf("got data; %d\n", r ); 600 | 601 | return r; 602 | } 603 | 604 | inline void copyKeyState(byte* a, byte* b) { 605 | memcpy(a,b,16); 606 | } 607 | bool isConnectionLost() { 608 | return connectionLost; 609 | } 610 | void exchangeJoystickState(uint16_t* state) 611 | { 612 | 613 | if (mpState == MULTIPLAYER_NOT_CONNECTED) return; 614 | 615 | tcpSend = 1; 616 | 617 | static int running = 0; 618 | if (running < 30) { 619 | running++; 620 | while (! gotRemoteData){vTaskDelay(1);} 621 | } else { 622 | int max = 0; 623 | while (! gotRemoteData && ++max < 400){vTaskDelay(1);} 624 | if (currentLocalTickNumber && max == 400) { connectionLost = true; } 625 | } 626 | 627 | 628 | gotRemoteData = 0; 629 | 630 | 631 | char rJoy = 0; 632 | 633 | if (currentRemoteTickNumber > currentLocalTickNumber || (currentRemoteTickNumber == 0 && currentLocalTickNumber == 255)) 634 | rJoy = lastremoteJoy; 635 | else rJoy = currentremoteJoy; 636 | 637 | lastLocalJoy = currentLocalJoy; 638 | currentLocalJoy = *state & 0xff; 639 | if (mpState == MULTIPLAYER_CONNECTED_CLIENT) *state = (lastLocalJoy << 8) + rJoy; 640 | if (mpState == MULTIPLAYER_CONNECTED_SERVER) *state = (rJoy << 8) + lastLocalJoy; 641 | 642 | currentLocalTickNumber++; 643 | 644 | 645 | return; 646 | } 647 | 648 | #endif 649 | -------------------------------------------------------------------------------- /main/odroidGo/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_PRIV_INCLUDEDIRS := ../utils ../sxmlc ../ ../odroid-go-common ../nfMSX ../nZ80 ../nEMULib ./ ../minIni ../ugui 2 | CPPFLAGS := -DBPS16 -DLSB_FIRST -DESP32 3 | CFLAGS := -Ofast -mlongcalls -Wno-error 4 | -------------------------------------------------------------------------------- /main/odroidGo/files.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2018 Schuemi. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | 26 | #include "LibOdroidGo.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "odroid_display.h" 34 | #include 35 | 36 | char* buffer; 37 | char* fullCurrentDir; 38 | 39 | struct dirent dirInfo; 40 | struct dirent firstDirEntry; 41 | bool haveFirstDirEntry = false; 42 | 43 | bool hasExt(const char *file, const char *Ext) { 44 | 45 | const char* p = strstr(file,"."); 46 | if (! p) return false; 47 | int lenFile = strlen(file); 48 | const char* cExt = Ext; 49 | 50 | while(cExt[0] != 0) { 51 | int lenExtension = strlen(cExt); 52 | if (lenFile < lenExtension) continue; 53 | p = file; 54 | p += lenFile; 55 | p -= lenExtension; 56 | if (!strcasecmp(p, cExt)) return true; 57 | cExt += strlen(cExt) + 2; 58 | 59 | } 60 | 61 | return false; 62 | } 63 | 64 | char* cutExtension(char* file) { 65 | register int i = strlen(file)-1; 66 | while(i>=0) { 67 | if (file[i] == '.') { 68 | file[i] = 0; 69 | return file; 70 | } 71 | i--; 72 | } 73 | return file; 74 | } 75 | const char* getFileName(const char* file) { 76 | register int i = strlen(file)-1; 77 | while(i>=0) { 78 | if (file[i] == '/') { 79 | return &file[i+1]; 80 | } 81 | i--; 82 | } 83 | return file; 84 | } 85 | 86 | 87 | char* getPath(char* file) { 88 | register int i = strlen(file)-1; 89 | while(i>=0) { 90 | if (file[i] == '/') { 91 | file[i] = 0; 92 | return file; 93 | } 94 | i--; 95 | } 96 | return file; 97 | } 98 | 99 | 100 | 101 | int initFiles(){ 102 | fullCurrentDir = malloc(612); 103 | buffer = malloc(1024); 104 | 105 | if (!fullCurrentDir){ printf("malloc fullCurrentDir failed!\n"); return 0; } 106 | 107 | strncpy(fullCurrentDir, FMSX_ROOT_GAMESDIR, 612); 108 | 109 | } 110 | 111 | int chdir(const char *path) 112 | { 113 | if (path == 0) return -1; 114 | 115 | if (!strcmp(path, "..")){ 116 | if (strlen(fullCurrentDir) > strlen(FMSX_ROOT_GAMESDIR)) getPath(fullCurrentDir); 117 | return 0; 118 | } 119 | if (path[0] == '/'){ 120 | return -1; 121 | } 122 | 123 | int len = strlen(fullCurrentDir); 124 | if (len > 610) return -1; 125 | fullCurrentDir[len] = '/'; 126 | fullCurrentDir[len+1] = 0; 127 | 128 | strncpy((fullCurrentDir + strlen(fullCurrentDir)), path, 610 - strlen(fullCurrentDir)); 129 | 130 | return 0; 131 | } 132 | 133 | char *getcwd(char *buf, size_t size) 134 | { 135 | strncpy(buf, fullCurrentDir, size); 136 | return buf; 137 | } 138 | char* getFullPath(char* buffer, const char* fileName, int bufferLength){ 139 | if (fileName[0] != '/'){ 140 | getcwd(buffer, bufferLength); 141 | int len = strlen(buffer); 142 | *(buffer + strlen(buffer)) = '/'; 143 | strncpy((buffer + len + 1), fileName, bufferLength - (len + 1)); 144 | 145 | } else { 146 | strncpy(buffer, fileName, 1024); 147 | } 148 | return buffer; 149 | } 150 | 151 | DIR* _opendir(const char* name) 152 | { 153 | STOP_DISPLAY_FUNCTION(); 154 | DIR* d; 155 | if (!strcmp(name, ".")) { 156 | d = opendir(getcwd(buffer, 1024)); 157 | } else d = opendir(name); 158 | 159 | RESUME_DISPLAY_FUNCTION(); 160 | return d; 161 | 162 | } 163 | size_t _fread(_PTR __restrict p, size_t _size, size_t _n, FILE *__restrict f) { 164 | STOP_DISPLAY_FUNCTION(); 165 | size_t s = fread(p, _size, _n, f); 166 | RESUME_DISPLAY_FUNCTION(); 167 | return s; 168 | } 169 | size_t _fwrite(const _PTR __restrict p , size_t _size, size_t _n, FILE * f) { 170 | STOP_DISPLAY_FUNCTION(); 171 | size_t s = fwrite(p, _size, _n, f); 172 | RESUME_DISPLAY_FUNCTION(); 173 | return s; 174 | } 175 | 176 | int _closedir(DIR* pdir) { 177 | int res; 178 | STOP_DISPLAY_FUNCTION(); 179 | res = closedir(pdir); 180 | RESUME_DISPLAY_FUNCTION(); 181 | return res; 182 | } 183 | void _rewinddir(DIR* pdir) { 184 | STOP_DISPLAY_FUNCTION(); 185 | rewinddir(pdir); 186 | RESUME_DISPLAY_FUNCTION(); 187 | } 188 | void _rewind(FILE* f) { 189 | STOP_DISPLAY_FUNCTION(); 190 | rewind(f); 191 | RESUME_DISPLAY_FUNCTION(); 192 | } 193 | 194 | struct dirent* _readdir(DIR* pdir) 195 | { 196 | STOP_DISPLAY_FUNCTION(); 197 | if (telldir(pdir) == 0) { 198 | // the first dir I should send is the ".." dir to go one dir up. 199 | 200 | dirInfo.d_ino = 0; 201 | dirInfo.d_type = DT_DIR; 202 | strncpy(dirInfo.d_name, "..", 3); 203 | 204 | firstDirEntry = *(readdir(pdir)); 205 | haveFirstDirEntry = true; 206 | RESUME_DISPLAY_FUNCTION(); 207 | return &dirInfo; 208 | 209 | } 210 | if (haveFirstDirEntry){ 211 | haveFirstDirEntry = false; 212 | RESUME_DISPLAY_FUNCTION(); 213 | return &firstDirEntry; 214 | 215 | } 216 | struct dirent* r = readdir(pdir); 217 | RESUME_DISPLAY_FUNCTION(); 218 | return r; 219 | 220 | } 221 | int _stat( const char *__restrict __path, struct stat *__restrict __sbuf ){ 222 | 223 | STOP_DISPLAY_FUNCTION(); 224 | if (!strcmp(__path, "..")) { 225 | RESUME_DISPLAY_FUNCTION(); 226 | __sbuf->st_mode = 0x41FF; 227 | return 0; 228 | } 229 | getFullPath(buffer, __path, 1024); 230 | int res = stat(buffer, __sbuf); 231 | RESUME_DISPLAY_FUNCTION(); 232 | return res; 233 | } 234 | int _fscanf(FILE *__restrict f, const char *__restrict c, ...) { 235 | STOP_DISPLAY_FUNCTION(); 236 | va_list args; 237 | va_start(args, c); 238 | int res = vfscanf(f,c, args); 239 | va_end(args); 240 | RESUME_DISPLAY_FUNCTION(); 241 | return res; 242 | 243 | } 244 | int _fprintf(FILE *__restrict f, const char *__restrict c, ...){ 245 | STOP_DISPLAY_FUNCTION(); 246 | va_list args; 247 | va_start(args, c); 248 | int res = vfprintf(f,c, args); 249 | va_end(args); 250 | RESUME_DISPLAY_FUNCTION(); 251 | return res; 252 | } 253 | int _fgetc(FILE * f) 254 | { 255 | STOP_DISPLAY_FUNCTION(); 256 | int res = fgetc(f); 257 | RESUME_DISPLAY_FUNCTION(); 258 | return res; 259 | } 260 | 261 | int _fputc(int i, FILE * f) 262 | { 263 | STOP_DISPLAY_FUNCTION(); 264 | int res = fputc(i,f); 265 | RESUME_DISPLAY_FUNCTION(); 266 | return res; 267 | } 268 | int _fputs(const char *__restrict c, FILE *__restrict f) 269 | { 270 | STOP_DISPLAY_FUNCTION(); 271 | int res = fputs(c,f); 272 | RESUME_DISPLAY_FUNCTION(); 273 | return res; 274 | } 275 | 276 | char * _fgets(char *__restrict c, int i, FILE *__restrict f) 277 | { 278 | STOP_DISPLAY_FUNCTION(); 279 | char * res = fgets(c, i, f); 280 | RESUME_DISPLAY_FUNCTION(); 281 | return res; 282 | } 283 | 284 | 285 | int _fseek(FILE * f, long a, int b) { 286 | STOP_DISPLAY_FUNCTION(); 287 | int ret = fseek(f, a, b); 288 | RESUME_DISPLAY_FUNCTION(); 289 | return ret; 290 | } 291 | long _ftell( FILE * f) { 292 | STOP_DISPLAY_FUNCTION(); 293 | long r = ftell(f); 294 | RESUME_DISPLAY_FUNCTION(); 295 | return r; 296 | } 297 | 298 | FILE* _fopen(const char *__restrict _name, const char *__restrict _type) { 299 | // are we in multiplayer modus? then don't send a state, not supportet yet 300 | #ifdef WITH_WLAN 301 | if (getMultiplayState() != MULTIPLAYER_NOT_CONNECTED){ 302 | if (strstr(_name, ".sta")) return NULL; 303 | } 304 | #endif 305 | ////////////////////////////////// 306 | // is this a bios file? 307 | if (!strcmp(_name, "CMOS.ROM") || !strcmp(_name, "KANJI.ROM") || !strcmp(_name, "RS232.ROM") || !strcmp(_name, "MSXDOS2.ROM") || !strcmp(_name, "PAINTER.ROM") || !strcmp(_name, "FMPAC.ROM") || !strcmp(_name, "MSX.ROM") || !strcmp(_name, "MSX2.ROM") || !strcmp(_name, "MSX2EXT.ROM") || !strcmp(_name, "DISK.ROM") || !strcmp(_name, "MSX2P.ROM") || !strcmp(_name, "MSX2PEXT.ROM")) { 308 | //it's a rom file, open from rom file path 309 | snprintf(buffer, 1024, "/sd/roms/msx/bios/%s", _name); 310 | return fopen(buffer, _type); 311 | } 312 | 313 | if (_name[0] != '/') { 314 | getFullPath(buffer, _name, 1024); 315 | } else { 316 | strncpy(buffer, _name, 1024); 317 | } 318 | //printf("fopen: %s\n", buffer); 319 | STOP_DISPLAY_FUNCTION(); 320 | FILE* f = fopen(buffer, _type); 321 | RESUME_DISPLAY_FUNCTION(); 322 | return f; 323 | } 324 | int _fclose(FILE* file) { 325 | STOP_DISPLAY_FUNCTION(); 326 | fflush(file); 327 | int res = fclose(file); 328 | RESUME_DISPLAY_FUNCTION(); 329 | return res; 330 | } 331 | long _telldir(DIR* pdir){ 332 | STOP_DISPLAY_FUNCTION(); 333 | long r = telldir(pdir); 334 | if (r > 0) r++; 335 | RESUME_DISPLAY_FUNCTION(); 336 | return r; 337 | } 338 | void _seekdir(DIR* pdir, long loc){ 339 | STOP_DISPLAY_FUNCTION(); 340 | if (loc > 0) loc--; 341 | seekdir(pdir, loc); 342 | RESUME_DISPLAY_FUNCTION(); 343 | } 344 | -------------------------------------------------------------------------------- /main/ugui/component.mk: -------------------------------------------------------------------------------- 1 | CFLAGS := -Ofast -mlongcalls -Wno-error 2 | -------------------------------------------------------------------------------- /main/ugui/ugui_config.h: -------------------------------------------------------------------------------- 1 | #ifndef __UGUI_CONFIG_H 2 | #define __UGUI_CONFIG_H 3 | #include 4 | 5 | /* -------------------------------------------------------------------------------- */ 6 | /* -- CONFIG SECTION -- */ 7 | /* -------------------------------------------------------------------------------- */ 8 | 9 | //#define USE_MULTITASKING 10 | 11 | /* Enable color mode */ 12 | //#define USE_COLOR_RGB888 // RGB = 0xFF,0xFF,0xFF 13 | #define USE_COLOR_RGB565 // RGB = 0bRRRRRGGGGGGBBBBB 14 | 15 | /* Enable needed fonts here */ 16 | //#define USE_FONT_4X6 17 | //#define USE_FONT_5X8 18 | //#define USE_FONT_5X12 19 | //#define USE_FONT_6X8 20 | //#define USE_FONT_6X10 21 | //#define USE_FONT_7X12 22 | #define USE_FONT_8X8 23 | //#define USE_FONT_8X12_CYRILLIC 24 | //#define USE_FONT_8X12 25 | //#define USE_FONT_8X12 26 | //#define USE_FONT_8X14 27 | //#define USE_FONT_10X16 28 | //#define USE_FONT_12X16 29 | //#define USE_FONT_12X20 30 | //#define USE_FONT_16X26 31 | //#define USE_FONT_22X36 32 | //#define USE_FONT_24X40 33 | //#define USE_FONT_32X53 34 | 35 | /* Specify platform-dependent integer types here */ 36 | 37 | #define __UG_FONT_DATA const 38 | typedef uint8_t UG_U8; 39 | typedef int8_t UG_S8; 40 | typedef uint16_t UG_U16; 41 | typedef int16_t UG_S16; 42 | typedef uint32_t UG_U32; 43 | typedef int32_t UG_S32; 44 | 45 | 46 | /* Example for dsPIC33 47 | typedef unsigned char UG_U8; 48 | typedef signed char UG_S8; 49 | typedef unsigned int UG_U16; 50 | typedef signed int UG_S16; 51 | typedef unsigned long int UG_U32; 52 | typedef signed long int UG_S32; 53 | */ 54 | 55 | /* -------------------------------------------------------------------------------- */ 56 | /* -------------------------------------------------------------------------------- */ 57 | 58 | 59 | /* Feature enablers */ 60 | #define USE_PRERENDER_EVENT 61 | #define USE_POSTRENDER_EVENT 62 | 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /main/utils/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_PRIV_INCLUDEDIRS := ./ 2 | CPPFLAGS := -DBPS16 -DLSB_FIRST -DESP32 3 | CFLAGS := -Ofast -mlongcalls -Wno-error 4 | -------------------------------------------------------------------------------- /main/utils/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2018 Schuemi. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | 26 | int getRomType(const char* sha); 27 | int fileExist(const char* fileName); 28 | int dirExist(const char* dirName); 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /res/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schuemi/fMSX-go/9627e054846f168a1fa0921f888514b6e05342d2/res/tile.png -------------------------------------------------------------------------------- /res/tile.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schuemi/fMSX-go/9627e054846f168a1fa0921f888514b6e05342d2/res/tile.raw -------------------------------------------------------------------------------- /sdkconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # Espressif IoT Development Framework Configuration 4 | # 5 | 6 | # 7 | # SDK tool configuration 8 | # 9 | CONFIG_TOOLPREFIX="xtensa-esp32-elf-" 10 | CONFIG_PYTHON="python" 11 | CONFIG_MAKE_WARN_UNDEFINED_VARIABLES=y 12 | 13 | # 14 | # Bootloader config 15 | # 16 | CONFIG_LOG_BOOTLOADER_LEVEL_NONE=y 17 | CONFIG_LOG_BOOTLOADER_LEVEL_ERROR= 18 | CONFIG_LOG_BOOTLOADER_LEVEL_WARN= 19 | CONFIG_LOG_BOOTLOADER_LEVEL_INFO= 20 | CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG= 21 | CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE= 22 | CONFIG_LOG_BOOTLOADER_LEVEL=0 23 | CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y 24 | CONFIG_BOOTLOADER_FACTORY_RESET= 25 | CONFIG_BOOTLOADER_APP_TEST= 26 | 27 | # 28 | # Security features 29 | # 30 | CONFIG_SECURE_BOOT_ENABLED= 31 | CONFIG_FLASH_ENCRYPTION_ENABLED= 32 | 33 | # 34 | # Serial flasher config 35 | # 36 | CONFIG_ESPTOOLPY_PORT="COM3" 37 | CONFIG_ESPTOOLPY_BAUD_115200B= 38 | CONFIG_ESPTOOLPY_BAUD_230400B= 39 | CONFIG_ESPTOOLPY_BAUD_921600B=y 40 | CONFIG_ESPTOOLPY_BAUD_2MB= 41 | CONFIG_ESPTOOLPY_BAUD_OTHER= 42 | CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 43 | CONFIG_ESPTOOLPY_BAUD=921600 44 | CONFIG_ESPTOOLPY_COMPRESSED=y 45 | CONFIG_FLASHMODE_QIO= 46 | CONFIG_FLASHMODE_QOUT= 47 | CONFIG_FLASHMODE_DIO=y 48 | CONFIG_FLASHMODE_DOUT= 49 | CONFIG_ESPTOOLPY_FLASHMODE="dio" 50 | CONFIG_ESPTOOLPY_FLASHFREQ_80M=y 51 | CONFIG_ESPTOOLPY_FLASHFREQ_40M= 52 | CONFIG_ESPTOOLPY_FLASHFREQ_26M= 53 | CONFIG_ESPTOOLPY_FLASHFREQ_20M= 54 | CONFIG_ESPTOOLPY_FLASHFREQ="80m" 55 | CONFIG_ESPTOOLPY_FLASHSIZE_1MB= 56 | CONFIG_ESPTOOLPY_FLASHSIZE_2MB= 57 | CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y 58 | CONFIG_ESPTOOLPY_FLASHSIZE_8MB= 59 | CONFIG_ESPTOOLPY_FLASHSIZE_16MB= 60 | CONFIG_ESPTOOLPY_FLASHSIZE="4MB" 61 | CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y 62 | CONFIG_ESPTOOLPY_BEFORE_RESET=y 63 | CONFIG_ESPTOOLPY_BEFORE_NORESET= 64 | CONFIG_ESPTOOLPY_BEFORE="default_reset" 65 | CONFIG_ESPTOOLPY_AFTER_RESET=y 66 | CONFIG_ESPTOOLPY_AFTER_NORESET= 67 | CONFIG_ESPTOOLPY_AFTER="hard_reset" 68 | CONFIG_MONITOR_BAUD_9600B= 69 | CONFIG_MONITOR_BAUD_57600B= 70 | CONFIG_MONITOR_BAUD_115200B=y 71 | CONFIG_MONITOR_BAUD_230400B= 72 | CONFIG_MONITOR_BAUD_921600B= 73 | CONFIG_MONITOR_BAUD_2MB= 74 | CONFIG_MONITOR_BAUD_OTHER= 75 | CONFIG_MONITOR_BAUD_OTHER_VAL=115200 76 | CONFIG_MONITOR_BAUD=115200 77 | 78 | # 79 | # Partition Table 80 | # 81 | CONFIG_PARTITION_TABLE_SINGLE_APP= 82 | CONFIG_PARTITION_TABLE_TWO_OTA= 83 | CONFIG_PARTITION_TABLE_CUSTOM=y 84 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 85 | CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" 86 | CONFIG_PARTITION_TABLE_OFFSET=0x8000 87 | CONFIG_PARTITION_TABLE_MD5= 88 | 89 | # 90 | # Compiler options 91 | # 92 | CONFIG_OPTIMIZATION_LEVEL_DEBUG= 93 | CONFIG_OPTIMIZATION_LEVEL_RELEASE=y 94 | CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED= 95 | CONFIG_OPTIMIZATION_ASSERTIONS_SILENT= 96 | CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED=y 97 | CONFIG_CXX_EXCEPTIONS= 98 | CONFIG_STACK_CHECK_NONE=y 99 | CONFIG_STACK_CHECK_NORM= 100 | CONFIG_STACK_CHECK_STRONG= 101 | CONFIG_STACK_CHECK_ALL= 102 | CONFIG_STACK_CHECK= 103 | CONFIG_WARN_WRITE_STRINGS= 104 | 105 | # 106 | # Component config 107 | # 108 | 109 | # 110 | # Application Level Tracing 111 | # 112 | CONFIG_ESP32_APPTRACE_DEST_TRAX= 113 | CONFIG_ESP32_APPTRACE_DEST_NONE=y 114 | CONFIG_ESP32_APPTRACE_ENABLE= 115 | CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y 116 | CONFIG_AWS_IOT_SDK= 117 | 118 | # 119 | # Bluetooth 120 | # 121 | CONFIG_BT_ENABLED= 122 | CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 123 | CONFIG_BT_RESERVE_DRAM=0 124 | 125 | # 126 | # Driver configurations 127 | # 128 | 129 | # 130 | # ADC configuration 131 | # 132 | CONFIG_ADC_FORCE_XPD_FSM= 133 | CONFIG_ADC2_DISABLE_DAC=y 134 | 135 | # 136 | # SPI master configuration 137 | # 138 | CONFIG_SPI_MASTER_IN_IRAM= 139 | CONFIG_SPI_MASTER_ISR_IN_IRAM=y 140 | 141 | # 142 | # ESP32-specific 143 | # 144 | CONFIG_ESP32_DEFAULT_CPU_FREQ_80= 145 | CONFIG_ESP32_DEFAULT_CPU_FREQ_160= 146 | CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y 147 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 148 | CONFIG_SPIRAM_SUPPORT=y 149 | 150 | # 151 | # SPI RAM config 152 | # 153 | CONFIG_SPIRAM_BOOT_INIT=y 154 | CONFIG_SPIRAM_IGNORE_NOTFOUND= 155 | CONFIG_SPIRAM_USE_MEMMAP= 156 | CONFIG_SPIRAM_USE_CAPS_ALLOC= 157 | CONFIG_SPIRAM_USE_MALLOC=y 158 | CONFIG_SPIRAM_TYPE_ESPPSRAM32=y 159 | CONFIG_SPIRAM_SIZE=4194304 160 | CONFIG_SPIRAM_SPEED_40M= 161 | CONFIG_SPIRAM_SPEED_80M=y 162 | CONFIG_SPIRAM_MEMTEST= 163 | CONFIG_SPIRAM_CACHE_WORKAROUND=y 164 | CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 165 | CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST=y 166 | CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 167 | CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY= 168 | CONFIG_MEMMAP_TRACEMEM= 169 | CONFIG_MEMMAP_TRACEMEM_TWOBANKS= 170 | CONFIG_ESP32_TRAX= 171 | CONFIG_TRACEMEM_RESERVE_DRAM=0x0 172 | CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH= 173 | CONFIG_ESP32_ENABLE_COREDUMP_TO_UART= 174 | CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y 175 | CONFIG_ESP32_ENABLE_COREDUMP= 176 | CONFIG_TWO_UNIVERSAL_MAC_ADDRESS= 177 | CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y 178 | CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 179 | CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 180 | CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 181 | CONFIG_MAIN_TASK_STACK_SIZE=4096 182 | CONFIG_IPC_TASK_STACK_SIZE=1024 183 | CONFIG_TIMER_TASK_STACK_SIZE=4096 184 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y 185 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF= 186 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR= 187 | CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF= 188 | CONFIG_NEWLIB_STDIN_LINE_ENDING_LF= 189 | CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y 190 | CONFIG_NEWLIB_NANO_FORMAT= 191 | CONFIG_CONSOLE_UART_DEFAULT=y 192 | CONFIG_CONSOLE_UART_CUSTOM= 193 | CONFIG_CONSOLE_UART_NONE= 194 | CONFIG_CONSOLE_UART_NUM=0 195 | CONFIG_CONSOLE_UART_BAUDRATE=115200 196 | CONFIG_ULP_COPROC_ENABLED=y 197 | CONFIG_ULP_COPROC_RESERVE_MEM=512 198 | CONFIG_ESP32_PANIC_PRINT_HALT= 199 | CONFIG_ESP32_PANIC_PRINT_REBOOT=y 200 | CONFIG_ESP32_PANIC_SILENT_REBOOT= 201 | CONFIG_ESP32_PANIC_GDBSTUB= 202 | CONFIG_ESP32_DEBUG_OCDAWARE=y 203 | CONFIG_ESP32_DEBUG_STUBS_ENABLE=y 204 | CONFIG_INT_WDT= 205 | CONFIG_TASK_WDT= 206 | CONFIG_BROWNOUT_DET=y 207 | CONFIG_BROWNOUT_DET_LVL_SEL_0=y 208 | CONFIG_BROWNOUT_DET_LVL_SEL_1= 209 | CONFIG_BROWNOUT_DET_LVL_SEL_2= 210 | CONFIG_BROWNOUT_DET_LVL_SEL_3= 211 | CONFIG_BROWNOUT_DET_LVL_SEL_4= 212 | CONFIG_BROWNOUT_DET_LVL_SEL_5= 213 | CONFIG_BROWNOUT_DET_LVL_SEL_6= 214 | CONFIG_BROWNOUT_DET_LVL_SEL_7= 215 | CONFIG_BROWNOUT_DET_LVL=0 216 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y 217 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC= 218 | CONFIG_ESP32_TIME_SYSCALL_USE_FRC1= 219 | CONFIG_ESP32_TIME_SYSCALL_USE_NONE= 220 | CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y 221 | CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL= 222 | CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 223 | CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 224 | CONFIG_ESP32_XTAL_FREQ_40= 225 | CONFIG_ESP32_XTAL_FREQ_26= 226 | CONFIG_ESP32_XTAL_FREQ_AUTO=y 227 | CONFIG_ESP32_XTAL_FREQ=0 228 | CONFIG_DISABLE_BASIC_ROM_CONSOLE= 229 | CONFIG_NO_BLOBS= 230 | CONFIG_ESP_TIMER_PROFILING= 231 | CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS= 232 | CONFIG_ESP_ERR_TO_NAME_LOOKUP=y 233 | 234 | # 235 | # Wi-Fi 236 | # 237 | CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=4 238 | CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=10 239 | CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y 240 | CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 241 | CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16 242 | CONFIG_ESP32_WIFI_CSI_ENABLED= 243 | CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y 244 | CONFIG_ESP32_WIFI_TX_BA_WIN=6 245 | CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y 246 | CONFIG_ESP32_WIFI_RX_BA_WIN=6 247 | CONFIG_ESP32_WIFI_NVS_ENABLED=y 248 | CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y 249 | CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1= 250 | 251 | # 252 | # PHY 253 | # 254 | CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y 255 | CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION= 256 | CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 257 | CONFIG_ESP32_PHY_MAX_TX_POWER=20 258 | 259 | # 260 | # Power Management 261 | # 262 | CONFIG_PM_ENABLE= 263 | 264 | # 265 | # ADC-Calibration 266 | # 267 | CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y 268 | CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y 269 | CONFIG_ADC_CAL_LUT_ENABLE=y 270 | 271 | # 272 | # ESP HTTP client 273 | # 274 | CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y 275 | 276 | # 277 | # Ethernet 278 | # 279 | CONFIG_DMA_RX_BUF_NUM=10 280 | CONFIG_DMA_TX_BUF_NUM=10 281 | CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE=y 282 | CONFIG_EMAC_TASK_PRIORITY=20 283 | 284 | # 285 | # FAT Filesystem support 286 | # 287 | CONFIG_FATFS_CODEPAGE_DYNAMIC= 288 | CONFIG_FATFS_CODEPAGE_437= 289 | CONFIG_FATFS_CODEPAGE_720= 290 | CONFIG_FATFS_CODEPAGE_737= 291 | CONFIG_FATFS_CODEPAGE_771= 292 | CONFIG_FATFS_CODEPAGE_775= 293 | CONFIG_FATFS_CODEPAGE_850=y 294 | CONFIG_FATFS_CODEPAGE_852= 295 | CONFIG_FATFS_CODEPAGE_855= 296 | CONFIG_FATFS_CODEPAGE_857= 297 | CONFIG_FATFS_CODEPAGE_860= 298 | CONFIG_FATFS_CODEPAGE_861= 299 | CONFIG_FATFS_CODEPAGE_862= 300 | CONFIG_FATFS_CODEPAGE_863= 301 | CONFIG_FATFS_CODEPAGE_864= 302 | CONFIG_FATFS_CODEPAGE_865= 303 | CONFIG_FATFS_CODEPAGE_866= 304 | CONFIG_FATFS_CODEPAGE_869= 305 | CONFIG_FATFS_CODEPAGE_932= 306 | CONFIG_FATFS_CODEPAGE_936= 307 | CONFIG_FATFS_CODEPAGE_949= 308 | CONFIG_FATFS_CODEPAGE_950= 309 | CONFIG_FATFS_CODEPAGE=850 310 | CONFIG_FATFS_LFN_NONE= 311 | CONFIG_FATFS_LFN_HEAP= 312 | CONFIG_FATFS_LFN_STACK=y 313 | CONFIG_FATFS_MAX_LFN=255 314 | CONFIG_FATFS_API_ENCODING_ANSI_OEM=y 315 | CONFIG_FATFS_API_ENCODING_UTF_16= 316 | CONFIG_FATFS_API_ENCODING_UTF_8= 317 | CONFIG_FATFS_FS_LOCK=0 318 | CONFIG_FATFS_TIMEOUT_MS=10000 319 | CONFIG_FATFS_PER_FILE_CACHE=y 320 | 321 | # 322 | # FreeRTOS 323 | # 324 | CONFIG_FREERTOS_UNICORE= 325 | CONFIG_FREERTOS_CORETIMER_0=y 326 | CONFIG_FREERTOS_CORETIMER_1= 327 | CONFIG_FREERTOS_HZ=1000 328 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION= 329 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE=y 330 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL= 331 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY= 332 | CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK= 333 | CONFIG_FREERTOS_INTERRUPT_BACKTRACE= 334 | CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 335 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT= 336 | CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE= 337 | CONFIG_FREERTOS_ASSERT_DISABLE=y 338 | CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024 339 | CONFIG_FREERTOS_ISR_STACKSIZE=1536 340 | CONFIG_FREERTOS_LEGACY_HOOKS= 341 | CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 342 | CONFIG_SUPPORT_STATIC_ALLOCATION=y 343 | CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK= 344 | CONFIG_TIMER_TASK_PRIORITY=1 345 | CONFIG_TIMER_TASK_STACK_DEPTH=2048 346 | CONFIG_TIMER_QUEUE_LENGTH=10 347 | CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 348 | CONFIG_FREERTOS_USE_TRACE_FACILITY= 349 | CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS= 350 | CONFIG_FREERTOS_DEBUG_INTERNALS= 351 | 352 | # 353 | # Heap memory debugging 354 | # 355 | CONFIG_HEAP_POISONING_DISABLED=y 356 | CONFIG_HEAP_POISONING_LIGHT= 357 | CONFIG_HEAP_POISONING_COMPREHENSIVE= 358 | CONFIG_HEAP_TRACING= 359 | 360 | # 361 | # libsodium 362 | # 363 | CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y 364 | 365 | # 366 | # Log output 367 | # 368 | CONFIG_LOG_DEFAULT_LEVEL_NONE= 369 | CONFIG_LOG_DEFAULT_LEVEL_ERROR=y 370 | CONFIG_LOG_DEFAULT_LEVEL_WARN= 371 | CONFIG_LOG_DEFAULT_LEVEL_INFO= 372 | CONFIG_LOG_DEFAULT_LEVEL_DEBUG= 373 | CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= 374 | CONFIG_LOG_DEFAULT_LEVEL=1 375 | CONFIG_LOG_COLORS= 376 | 377 | # 378 | # LWIP 379 | # 380 | CONFIG_L2_TO_L3_COPY= 381 | CONFIG_LWIP_IRAM_OPTIMIZATION= 382 | CONFIG_LWIP_MAX_SOCKETS=10 383 | CONFIG_USE_ONLY_LWIP_SELECT= 384 | CONFIG_LWIP_SO_REUSE=y 385 | CONFIG_LWIP_SO_REUSE_RXTOALL=y 386 | CONFIG_LWIP_SO_RCVBUF=y 387 | CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 388 | CONFIG_LWIP_IP_FRAG= 389 | CONFIG_LWIP_IP_REASSEMBLY= 390 | CONFIG_LWIP_STATS= 391 | CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y 392 | CONFIG_TCPIP_RECVMBOX_SIZE=32 393 | CONFIG_LWIP_DHCP_DOES_ARP_CHECK= 394 | 395 | # 396 | # DHCP server 397 | # 398 | CONFIG_LWIP_DHCPS_LEASE_UNIT=60 399 | CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 400 | CONFIG_LWIP_AUTOIP= 401 | CONFIG_LWIP_NETIF_LOOPBACK=y 402 | CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 403 | 404 | # 405 | # TCP 406 | # 407 | CONFIG_LWIP_MAX_ACTIVE_TCP=16 408 | CONFIG_LWIP_MAX_LISTENING_TCP=16 409 | CONFIG_TCP_MAXRTX=12 410 | CONFIG_TCP_SYNMAXRTX=6 411 | CONFIG_TCP_MSS=1436 412 | CONFIG_TCP_MSL=60000 413 | CONFIG_TCP_SND_BUF_DEFAULT=5744 414 | CONFIG_TCP_WND_DEFAULT=5744 415 | CONFIG_TCP_RECVMBOX_SIZE=6 416 | CONFIG_TCP_QUEUE_OOSEQ=y 417 | CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES= 418 | CONFIG_TCP_OVERSIZE_MSS=y 419 | CONFIG_TCP_OVERSIZE_QUARTER_MSS= 420 | CONFIG_TCP_OVERSIZE_DISABLE= 421 | 422 | # 423 | # UDP 424 | # 425 | CONFIG_LWIP_MAX_UDP_PCBS=16 426 | CONFIG_UDP_RECVMBOX_SIZE=6 427 | CONFIG_TCPIP_TASK_STACK_SIZE=2560 428 | CONFIG_PPP_SUPPORT=y 429 | CONFIG_PPP_PAP_SUPPORT=y 430 | CONFIG_PPP_CHAP_SUPPORT=y 431 | CONFIG_PPP_MSCHAP_SUPPORT=y 432 | CONFIG_PPP_MPPE_SUPPORT=y 433 | CONFIG_PPP_DEBUG_ON= 434 | 435 | # 436 | # ICMP 437 | # 438 | CONFIG_LWIP_MULTICAST_PING= 439 | CONFIG_LWIP_BROADCAST_PING= 440 | 441 | # 442 | # LWIP RAW API 443 | # 444 | CONFIG_LWIP_MAX_RAW_PCBS=16 445 | 446 | # 447 | # mbedTLS 448 | # 449 | CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 450 | CONFIG_MBEDTLS_DEBUG= 451 | CONFIG_MBEDTLS_HARDWARE_AES=y 452 | CONFIG_MBEDTLS_HARDWARE_MPI= 453 | CONFIG_MBEDTLS_HARDWARE_SHA= 454 | CONFIG_MBEDTLS_HAVE_TIME=y 455 | CONFIG_MBEDTLS_HAVE_TIME_DATE= 456 | CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y 457 | CONFIG_MBEDTLS_TLS_SERVER_ONLY= 458 | CONFIG_MBEDTLS_TLS_CLIENT_ONLY= 459 | CONFIG_MBEDTLS_TLS_DISABLED= 460 | CONFIG_MBEDTLS_TLS_SERVER=y 461 | CONFIG_MBEDTLS_TLS_CLIENT=y 462 | CONFIG_MBEDTLS_TLS_ENABLED=y 463 | 464 | # 465 | # TLS Key Exchange Methods 466 | # 467 | CONFIG_MBEDTLS_PSK_MODES= 468 | CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y 469 | CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y 470 | CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y 471 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y 472 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y 473 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y 474 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y 475 | CONFIG_MBEDTLS_SSL_RENEGOTIATION=y 476 | CONFIG_MBEDTLS_SSL_PROTO_SSL3= 477 | CONFIG_MBEDTLS_SSL_PROTO_TLS1=y 478 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y 479 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y 480 | CONFIG_MBEDTLS_SSL_PROTO_DTLS= 481 | CONFIG_MBEDTLS_SSL_ALPN=y 482 | CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y 483 | 484 | # 485 | # Symmetric Ciphers 486 | # 487 | CONFIG_MBEDTLS_AES_C=y 488 | CONFIG_MBEDTLS_CAMELLIA_C= 489 | CONFIG_MBEDTLS_DES_C= 490 | CONFIG_MBEDTLS_RC4_DISABLED=y 491 | CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT= 492 | CONFIG_MBEDTLS_RC4_ENABLED= 493 | CONFIG_MBEDTLS_BLOWFISH_C= 494 | CONFIG_MBEDTLS_XTEA_C= 495 | CONFIG_MBEDTLS_CCM_C=y 496 | CONFIG_MBEDTLS_GCM_C=y 497 | CONFIG_MBEDTLS_RIPEMD160_C= 498 | 499 | # 500 | # Certificates 501 | # 502 | CONFIG_MBEDTLS_PEM_PARSE_C=y 503 | CONFIG_MBEDTLS_PEM_WRITE_C=y 504 | CONFIG_MBEDTLS_X509_CRL_PARSE_C=y 505 | CONFIG_MBEDTLS_X509_CSR_PARSE_C=y 506 | CONFIG_MBEDTLS_ECP_C=y 507 | CONFIG_MBEDTLS_ECDH_C=y 508 | CONFIG_MBEDTLS_ECDSA_C=y 509 | CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y 510 | CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y 511 | CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y 512 | CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y 513 | CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y 514 | CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y 515 | CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y 516 | CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y 517 | CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y 518 | CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y 519 | CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y 520 | CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y 521 | CONFIG_MBEDTLS_ECP_NIST_OPTIM=y 522 | 523 | # 524 | # OpenSSL 525 | # 526 | CONFIG_OPENSSL_DEBUG= 527 | CONFIG_OPENSSL_ASSERT_DO_NOTHING=y 528 | CONFIG_OPENSSL_ASSERT_EXIT= 529 | 530 | # 531 | # PThreads 532 | # 533 | CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 534 | CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=2048 535 | 536 | # 537 | # SPI Flash driver 538 | # 539 | CONFIG_SPI_FLASH_VERIFY_WRITE= 540 | CONFIG_SPI_FLASH_ENABLE_COUNTERS= 541 | CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y 542 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y 543 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS= 544 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED= 545 | 546 | # 547 | # SPIFFS Configuration 548 | # 549 | CONFIG_SPIFFS_MAX_PARTITIONS=3 550 | 551 | # 552 | # SPIFFS Cache Configuration 553 | # 554 | CONFIG_SPIFFS_CACHE=y 555 | CONFIG_SPIFFS_CACHE_WR=y 556 | CONFIG_SPIFFS_CACHE_STATS= 557 | CONFIG_SPIFFS_PAGE_CHECK=y 558 | CONFIG_SPIFFS_GC_MAX_RUNS=10 559 | CONFIG_SPIFFS_GC_STATS= 560 | CONFIG_SPIFFS_PAGE_SIZE=256 561 | CONFIG_SPIFFS_OBJ_NAME_LEN=32 562 | CONFIG_SPIFFS_USE_MAGIC=y 563 | CONFIG_SPIFFS_USE_MAGIC_LENGTH=y 564 | CONFIG_SPIFFS_META_LENGTH=4 565 | CONFIG_SPIFFS_USE_MTIME=y 566 | 567 | # 568 | # Debug Configuration 569 | # 570 | CONFIG_SPIFFS_DBG= 571 | CONFIG_SPIFFS_API_DBG= 572 | CONFIG_SPIFFS_GC_DBG= 573 | CONFIG_SPIFFS_CACHE_DBG= 574 | CONFIG_SPIFFS_CHECK_DBG= 575 | CONFIG_SPIFFS_TEST_VISUALISATION= 576 | 577 | # 578 | # tcpip adapter 579 | # 580 | CONFIG_IP_LOST_TIMER_INTERVAL=120 581 | 582 | # 583 | # Virtual file system 584 | # 585 | CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y 586 | 587 | # 588 | # Wear Levelling 589 | # 590 | CONFIG_WL_SECTOR_SIZE_512= 591 | CONFIG_WL_SECTOR_SIZE_4096=y 592 | CONFIG_WL_SECTOR_SIZE=4096 593 | --------------------------------------------------------------------------------