├── 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 | 
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 | 
98 |
99 | Two beer donations (5.00€):
100 | 
101 |
102 | Donate a six-pack of beer. :) (10.00€):
103 | 
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 |
--------------------------------------------------------------------------------