Initial import: ESP32 ZiFi32 firmware with SMB2/3 VFS bridge

Based on https://github.com/tslabs/zx-evo/tree/master/pentevo/esp32,
extended with an SMB2/3 server that exposes the ZX Evolution's real SD
card as a network share (browse/download/upload/delete), plus assorted
stability and protocol-correctness fixes in vfs_bridge.cpp and the
vendored libsmb2 - see firmware/README.md for the detailed changelog
and current status.

Also adds:
- firmware/: ready-to-flash compiled binaries + flashing instructions
- hardware/: adapter board connection diagram, PCB photo, and Gerber
  files
- src/third_party/libsmb2: vendored from sahlberg/libsmb2 (LGPL-2.1,
  see its own COPYING/LICENCE files), de-submoduled to a plain
  snapshot with local patches applied directly

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 16:00:05 +03:00
co-authored by Claude Sonnet 5
commit 6d835bc44c
377 changed files with 189430 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS
"${CMAKE_CURRENT_LIST_DIR}/main"
"${CMAKE_CURRENT_LIST_DIR}/components/libsmb2_esp"
)
project(esp-spi)
set(app_elf "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.elf")
set(app_disasm "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.s")
add_custom_command(
OUTPUT "${app_disasm}"
COMMAND "${CMAKE_OBJDUMP}" -S "${app_elf}" > "${app_disasm}"
DEPENDS "${app_elf}"
VERBATIM
)
add_custom_target(disasm ALL DEPENDS "${app_disasm}")
+58
View File
@@ -0,0 +1,58 @@
# WEATHER_GET по SPI
Команда `ESP_CMD_WEATHER_GET = 0x2D`. Код `0x24` исходного ZiFi занят в этом
проекте командой HTTPS_STREAM_START; UART-кадр ответа `0xA4` здесь не используется.
Сервисы: api.zippopotam.us и api.open-meteo.com, прямое HTTP-соединение.
1. Подключить Wi-Fi обычными командами проекта.
2. Создать объект DATA или DATAF через MAKE_OBJECT и записать через WRITE_OBJECT
две строки с завершающими нулями: код страны ISO из двух латинских букв и индекс.
Например, `IT\0` + `00144\0` — 9 байт. Индекс: от 1 до 31 байта.
Допускается нулевое дополнение объекта для выравнивания DMA.
3. Записать handle входного объекта в ESP_REG_OBJ_HANDLE и выполнить `0x2D`.
Дождаться завершения BUSY; тайм-аут на стороне хоста рекомендуется 90 секунд.
4. При READY: ESP_REG_OBJ_HANDLE содержит новый объект DATA,
ESP_REG_DATA_OFFSET=0, ESP_REG_DATA_SIZE=90. Прочитать через READ_OBJECT.
5. Удалить входной и выходной объекты через DELETE_OBJECT, когда они больше не нужны.
Входной объект команда не изменяет и не удаляет; его handle следует сохранить.
При ошибке новый объект не создаётся, входной handle сохраняется, DATA_SIZE=0.
Статусы: INV_HANDLE, INV_PARAM, INV_STR_LEN, AP_NOT_CONNECTED,
OUT_OF_MEMORY/OUT_OF_HANDLES; `ESP_ERR_WEATHER=0xB5` означает ошибку HTTP или
разбора JSON (подробность в логе `weather`). Без Wi-Fi команда даёт INV_COMMAND.
Координаты кешируются до изменения пары страна/индекс или перезагрузки.
Прогноз запрашивается при каждом вызове. Для обрывов, 429 и 5xx — максимум три
попытки, паузы 1 и 2 секунды; новые запросы начинаются только в первые 30 секунд.
Ответы ограничены 4095 байтами, усечённые ответы не принимаются.
Параметры из zifi.ini передаёт хост: прошивка сама этот файл не читает.
Прокси из оригинального ZiFi не перенесён: в данном SPI API нет его настройки.
## Запись версии 1 (90 байт)
Все 16-битные числа little-endian. Температуры — знаковые байты в °C.
| Смещение | Размер | Поле |
|---|---|---|
| 0 | 1 | 1: данные получены |
| 1 | 1 | Версия: 1 |
| 2 | 24 | Название места, CP866, с нулём |
| 26 | 1 | Текущая температура |
| 27 | 1 | Код WMO |
| 28 | 1 | 1: день |
| 29 | 2 | Ветер, км/ч ×10 |
| 31 | 2 | Осадки за час, мм ×10 |
| 33 | 2 | Давление, мм рт. ст. |
| 35 | 2 | Восход: час, минута |
| 37 | 2 | Закат: час, минута |
| 39 | 2 | Местное время данных: час, минута |
| 41 | 1 | Число дневных записей, до 6 |
| 42 | 48 | До 6 записей по 8 байт |
Дневная запись: число, месяц, день недели (0=понедельник), код WMO,
tmin, tmax, осадки мм ×10 (u16). Первый день — сегодня.
Парсер `main/weather_parse.cpp` и `main/zifi/weather_parse.hpp` перенесён из
локального ZiFi-ESP32-S3-Zero; формат совместим с коммитом
[616bee6](https://github.com/andrewinsidelazarev/ZiFi-ESP32-S3-Zero/commit/616bee6333d34dfd4dfd83ffb4bb8a44c2c974f9).
Проверка парсера: `python tests/test_weather_parse.py` (нужен MSVC).
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
HDR="${1:-$SCRIPT_DIR/../src/esp_spi_defs.h}"
if [ ! -f "$HDR" ]; then
echo "bump-api: file not found: $HDR" >&2
exit 1
fi
old="$(sed -nE 's/^[[:space:]]*#define[[:space:]]+API_VER[[:space:]]+([0-9]+).*/\1/p' "$HDR" | head -n1)"
if [ -z "$old" ]; then
echo "bump-api: API_VER not found in $HDR" >&2
exit 1
fi
if [ "$old" -ge 255 ]; then
echo "bump-api: API_VER is 1 byte and already at maximum 255" >&2
exit 1
fi
new=$((old + 1))
sed -i -E "s/^([[:space:]]*#define[[:space:]]+API_VER[[:space:]]+)[0-9]+/\1${new}/" "$HDR"
echo "bump-api: API ${old} -> ${new}"
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
HDR="${1:-$SCRIPT_DIR/../src/esp_spi_defs.h}"
if [ ! -f "$HDR" ]; then
echo "bump-feat: file not found: $HDR" >&2
exit 1
fi
old="$(sed -nE 's/^[[:space:]]*#define[[:space:]]+FEAT_VER[[:space:]]+([0-9]+).*/\1/p' "$HDR" | head -n1)"
if [ -z "$old" ]; then
echo "bump-feat: FEAT_VER not found in $HDR" >&2
exit 1
fi
if [ "$old" -ge 65535 ]; then
echo "bump-feat: FEAT_VER is 2 bytes and already at maximum 65535" >&2
exit 1
fi
new=$((old + 1))
sed -i -E "s/^([[:space:]]*#define[[:space:]]+FEAT_VER[[:space:]]+)[0-9]+/\1${new}/" "$HDR"
echo "bump-feat: FEAT ${old} -> ${new}"
+26
View File
@@ -0,0 +1,26 @@
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
HDR="${1:-$SCRIPT_DIR/../src/esp_spi_defs.h}"
if [ ! -f "$HDR" ]; then
echo "bump: file not found: $HDR" >&2
exit 1
fi
set -- $(sed -nE 's/^[[:space:]]*#define[[:space:]]+PROD_VER([0-2])[[:space:]]+([0-9]+).*/\1 \2/p' "$HDR" | sort | awk '{print $2}')
if [ "$#" -ne 3 ]; then
echo "bump: PROD_VER0..2 not found in $HDR" >&2
exit 1
fi
major="$1"
minor="$2"
old="$3"
new=$((old + 1))
sed -i -E "s/^([[:space:]]*#define[[:space:]]+PROD_VER2[[:space:]]+)[0-9]+/\1${new}/" "$HDR"
echo "bump: ver.${major}.${minor}.${old} -> ver.${major}.${minor}.${new}"
+103
View File
@@ -0,0 +1,103 @@
# Modern ESP-IDF (idf_component_register) wrapper around the vendored
# libsmb2 checkout at E:\esp32\src\third_party\libsmb2 (upstream
# sahlberg/libsmb2, LGPL-2.1). Vendored outside components/ deliberately:
# ESP-IDF auto-scans every immediate subdirectory of components/ as a
# candidate component, and libsmb2's own CMakeLists.txt predates
# idf_component_register (calls add_definitions() at the top level, which
# ESP-IDF 6.1's two-pass component requirement scan rejects with
# "add_definitions command is not scriptable") — so it must never be
# auto-discovered directly. Source list and compile definitions below are
# copied from the upstream lib/CMakeLists.txt ESP_PLATFORM branch, minus
# aes_apple.c/krb5-wrapper.c (excluded there too for this platform).
set(LIBSMB2_DIR "${CMAKE_CURRENT_LIST_DIR}/../../third_party/libsmb2")
set(LIBSMB2_LIB_DIR "${LIBSMB2_DIR}/lib")
set(LIBSMB2_SOURCES
aes.c
aes_reference.c
aes128ccm.c
alloc.c
asn1-ber.c
compat.c
libsmb2-dcerpc.c
libsmb2-dcerpc-srvsvc.c
errors.c
hmac.c
hmac-md5.c
init.c
libsmb2.c
md4c.c
md5.c
ntlmssp.c
pdu.c
sha1.c
sha224-256.c
sha384-512.c
smb2-cmd-close.c
smb2-cmd-create.c
smb2-cmd-echo.c
smb2-cmd-error.c
smb2-cmd-flush.c
smb2-cmd-ioctl.c
smb2-cmd-lock.c
smb2-cmd-logoff.c
smb2-cmd-negotiate.c
smb2-cmd-notify-change.c
smb2-cmd-oplock-break.c
smb2-cmd-query-directory.c
smb2-cmd-query-info.c
smb2-cmd-read.c
smb2-cmd-session-setup.c
smb2-cmd-set-info.c
smb2-cmd-tree-connect.c
smb2-cmd-tree-disconnect.c
smb2-cmd-write.c
smb2-data-file-info.c
smb2-data-filesystem-info.c
smb2-data-security-descriptor.c
smb2-data-reparse-point.c
smb2-share-enum.c
smb3-seal.c
smb2-signing.c
socket.c
spnego-wrapper.c
sync.c
timestamps.c
unicode.c
usha.c
)
list(TRANSFORM LIBSMB2_SOURCES PREPEND "${LIBSMB2_LIB_DIR}/")
idf_component_register(
SRCS ${LIBSMB2_SOURCES}
INCLUDE_DIRS
"${LIBSMB2_DIR}/include"
"${LIBSMB2_DIR}/include/smb2"
"${LIBSMB2_DIR}/include/esp"
PRIV_INCLUDE_DIRS
"${LIBSMB2_LIB_DIR}"
REQUIRES
lwip
mbedtls
sock_utils
)
target_compile_definitions(${COMPONENT_LIB} PRIVATE
HAVE_CONFIG_H
NEED_READV
NEED_WRITEV
NEED_GETLOGIN_R
NEED_RANDOM
NEED_SRANDOM
"_U_=__attribute__((unused))"
)
# Vendored upstream code, built as-is: relax -Werror for warning classes that
# are harmless in practice (e.g. a tautological unsigned comparison in
# libsmb2.c) rather than patching third-party sources to satisfy this
# project's stricter default warning flags.
target_compile_options(${COMPONENT_LIB} PRIVATE
-Wno-error=type-limits
)
@@ -0,0 +1,8 @@
## IDF Component Manager Manifest File
dependencies:
idf:
version: '>=4.1.0'
# gethostname() is not provided by ESP-IDF's libc; libsmb2's server accept
# path (smb2_serve_port) calls it directly. sock_utils supplies a minimal
# implementation.
espressif/sock_utils: "*"
+83
View File
@@ -0,0 +1,83 @@
dependencies:
espressif/esp_new_jpeg:
component_hash: e1e7e6991193c41ed6c9fc386702aa7821433319e7aa9c3147cd3236310cd69c
dependencies: []
source:
registry_url: https://components.espressif.com/
type: service
targets:
- esp32
- esp32s2
- esp32s3
- esp32s31
- esp32p4
- esp32c2
- esp32c3
- esp32c5
- esp32c6
- esp32c61
- esp32h4
version: 1.0.2
espressif/sock_utils:
component_hash: b2e5cafc60dfefd05720ababb9fca0dfe2830fb9d71f97e5336de36a84c591c9
dependencies:
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 0.2.2
espressif/usb:
component_hash: 6c2cff118141dbbca181d0d27d71801fdddfc78f7ac03ae43f6e5a8cbd89cb99
dependencies:
- name: idf
require: private
version: '>=5.5.3'
source:
registry_url: https://components.espressif.com
type: service
targets:
- esp32s2
- esp32s3
- esp32s31
- esp32p4
- esp32h4
- linux
version: 1.5.0
espressif/usb_host_hid:
component_hash: a6c5366f9b6d80b0d5f56d085d62941464512e0968b96c5877e76a80fcc23f13
dependencies:
- name: idf
require: private
version: '>=5.0'
- name: espressif/usb
registry_url: https://components.espressif.com
require: public
rules:
- if: idf_version >=6.0
- if: target not in ["linux"]
version: ^1.0.0
source:
registry_url: https://components.espressif.com/
type: service
targets:
- esp32s2
- esp32s3
- esp32p4
- esp32h4
- esp32s31
- linux
version: 1.2.0
idf:
source:
type: idf
version: 6.1.0
direct_dependencies:
- espressif/esp_new_jpeg
- espressif/sock_utils
- espressif/usb_host_hid
- idf
manifest_hash: a4d460bb515c6dffc30079d44d3c29fccc46d49abbe9d3c15eabf6486c7456c0
target: esp32s3
version: 3.0.0
+12
View File
@@ -0,0 +1,12 @@
file(GLOB SOURCES "*.c" "*.cpp")
idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS ".")
idf_build_set_property(COMPILE_OPTIONS "-ggdb;-g3" APPEND)
idf_build_set_property(COMPILE_OPTIONS "-Wno-missing-field-initializers" APPEND)
idf_build_set_property(COMPILE_OPTIONS "-Wno-implicit-fallthrough" APPEND)
# target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=format)
# target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error)
# target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-but-set-variable)
# target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function)
+314
View File
@@ -0,0 +1,314 @@
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_tls.h"
#include "esp_crt_bundle.h"
#include "main.h"
#include "at_uart.h"
#include "at_cip.h"
#include "wifi.h"
static const char *TAG = "at_cip";
static int cip_sock = -1;
static int cip_type = CIP_TYPE_NONE;
static char cip_host[64];
static int cip_port = 0;
static esp_tls_t *cip_tls = NULL;
void at_cip_init()
{
cip_sock = -1;
cip_type = CIP_TYPE_NONE;
cip_host[0] = 0;
cip_port = 0;
cip_tls = NULL;
}
bool at_cip_is_connected()
{
return cip_sock >= 0 || cip_tls != NULL;
}
void at_cip_status_line(char *buf, size_t buflen)
{
if (!at_cip_is_connected())
{
snprintf(buf, buflen, "STATUS:0");
return;
}
const char *type_str = cip_type == CIP_TYPE_UDP ? "UDP" : cip_type == CIP_TYPE_SSL ? "SSL" : "TCP";
snprintf(buf, buflen, "STATUS:3\r\n+CIPSTATUS:0,\"%s\",\"%s\",%d,0,0", type_str, cip_host, cip_port);
}
// Resolves and connects a raw socket via the plain BSD gethostbyname()/connect()
// path. Shared by plain TCP and (as of the SSL support below) also by the TLS
// path: esp_tls's own built-in connect step goes through getaddrinfo() instead,
// which crashes (LoadProhibited, NULL FreeRTOS queue handle deep inside lwIP's
// tcpip_task message-passing - confirmed via backtrace against esp-spi.elf,
// root cause not fully understood) the first time it's exercised from this
// task. gethostbyname() uses a different, older lwIP DNS code path that has
// always worked fine here, so SSL connects through it too and only hands
// esp_tls an already-open socket, skipping esp_tls's DNS/connect step entirely.
static int at_cip_connect_raw_tcp(int sock_type, const char *host, int port, bool set_rcv_timeout)
{
// gethostbyname() (and, for the SSL caller, esp_tls's own getaddrinfo()
// fallback below) both post through tcpip_task's mailbox - if this is the
// very first network command since boot (no AT+CWJAP/AT+CWLAP yet), that
// mailbox doesn't exist yet and the post crashes with a NULL-queue-handle
// LoadProhibited (EXCVADDR=0x54, confirmed via console backtrace: reproduced
// with a plain AT+CIPSTART="TCP" to a literal IP right after a fresh boot,
// fixed by ensuring the WiFi stack is up first, same lazy-init used by
// AT+CWLAP - safe to call again if already initialized).
if (!net_mtx)
initialize_wifi();
struct hostent *server = gethostbyname(host);
if (!server)
{
ESP_LOGW(TAG, "DNS lookup failed for '%s'", host);
return -1;
}
int sock = socket(AF_INET, sock_type, 0);
if (sock < 0)
{
ESP_LOGW(TAG, "socket() failed");
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
memcpy(&addr.sin_addr.s_addr, server->h_addr, server->h_length);
// A short receive timeout lets at_cip_rx_task poll periodically instead of
// blocking forever, so it notices at_cip_close() promptly. Skipped here for
// the SSL caller: applying it before the TLS handshake made mbedtls's own
// blocking reads see spurious timeouts mid-handshake (a partial/timed-out
// read on a socket mbedtls expects to block on desyncs its record parser -
// confirmed by this exact change turning consistent MBEDTLS_ERR_SSL_INVALID_RECORD
// handshake failures into a clean handshake). The SSL path sets this itself
// after the handshake completes, once the socket moves to steady-state reads.
if (set_rcv_timeout)
{
struct timeval tv = { 0, 200 * 1000 };
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}
if (sock_type == SOCK_DGRAM)
return sock; // UDP: no connect() handshake to wait on
// Bound the connect() itself with an explicit timeout via a non-blocking
// connect + select(): an unreachable-but-not-immediately-rejected address
// (e.g. a black-holed IP) can otherwise leave the OS/lwip TCP SYN retry
// logic blocking this call for a minute or more, freezing the whole AT
// command interface for that entire time.
int flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
int rc = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
if (rc < 0 && errno != EINPROGRESS)
{
ESP_LOGW(TAG, "connect() to %s:%d failed immediately", host, port);
close(sock);
return -1;
}
if (rc < 0) // EINPROGRESS - wait for it to complete
{
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(sock, &wfds);
struct timeval conn_tv = { 10, 0 }; // 10s connect timeout
int sel = select(sock + 1, NULL, &wfds, NULL, &conn_tv);
if (sel <= 0)
{
ESP_LOGW(TAG, "connect() to %s:%d timed out", host, port);
close(sock);
return -1;
}
int so_err = 0;
socklen_t so_len = sizeof(so_err);
getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_err, &so_len);
if (so_err != 0)
{
ESP_LOGW(TAG, "connect() to %s:%d failed: errno %d", host, port, so_err);
close(sock);
return -1;
}
}
fcntl(sock, F_SETFL, flags); // back to blocking for the (rare) case send() would need it
return sock;
}
static int at_cip_start_ssl(const char *host, int port)
{
int sock = at_cip_connect_raw_tcp(SOCK_STREAM, host, port, false);
if (sock < 0)
return -1;
// Kept local until the handshake fully succeeds below, deliberately not
// written to the global cip_tls yet: at_cip_is_connected() (checked by
// at_cip_rx_task's poll loop, running concurrently in its own task) goes by
// cip_tls != NULL, and that task calling esp_tls_conn_read() on a context
// that's mid-handshake raced with esp_tls_conn_new_sync() below over the
// same mbedtls ssl_context - confirmed via mbedtls debug log showing
// interleaved flush-output calls and a corrupted ssl->out_left, manifesting
// as MBEDTLS_ERR_SSL_INTERNAL_ERROR then MBEDTLS_ERR_SSL_INVALID_RECORD.
// The plain-TCP path never had this problem because it keeps its socket in
// a local variable the same way, only publishing to cip_sock at the very end.
esp_tls_t *tls = esp_tls_init();
if (!tls)
{
ESP_LOGW(TAG, "esp_tls_init failed");
close(sock);
return -1;
}
esp_tls_set_conn_sockfd(tls, sock);
esp_tls_set_conn_state(tls, ESP_TLS_CONNECTING); // skip esp_tls's own DNS/connect step - see at_cip_connect_raw_tcp()
esp_tls_cfg_t cfg = {};
cfg.crt_bundle_attach = esp_crt_bundle_attach;
cfg.timeout_ms = 10000; // matches the connect timeout already spent above
int rc = esp_tls_conn_new_sync(host, strlen(host), port, &cfg, tls);
if (rc != 1)
{
ESP_LOGW(TAG, "TLS handshake with %s:%d failed", host, port);
esp_tls_conn_destroy(tls); // also closes sock, it now owns the fd
return -1;
}
// Now that the handshake is done, throttle reads the same way the plain-TCP
// path does, so at_cip_rx_task's poll loop notices at_cip_close() promptly.
struct timeval tv = { 0, 200 * 1000 };
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
cip_tls = tls; // now safe to publish: handshake is done, at_cip_rx_task may use it
cip_type = CIP_TYPE_SSL;
strlcpy(cip_host, host, sizeof(cip_host));
cip_port = port;
ESP_LOGI(TAG, "connected SSL %s:%d", host, port);
return 0;
}
int at_cip_start(const char *type, const char *host, int port)
{
if (at_cip_is_connected())
return -2; // already connected
if (!strcasecmp(type, "SSL"))
return at_cip_start_ssl(host, port);
int sock_type = 0;
if (!strcasecmp(type, "TCP"))
sock_type = SOCK_STREAM;
else if (!strcasecmp(type, "UDP"))
sock_type = SOCK_DGRAM;
else
{
ESP_LOGW(TAG, "unknown type '%s'", type);
return -1;
}
int sock = at_cip_connect_raw_tcp(sock_type, host, port, true);
if (sock < 0)
return -1;
cip_sock = sock;
cip_type = (sock_type == SOCK_DGRAM) ? CIP_TYPE_UDP : CIP_TYPE_TCP;
strlcpy(cip_host, host, sizeof(cip_host));
cip_port = port;
ESP_LOGI(TAG, "connected %s %s:%d", cip_type == CIP_TYPE_UDP ? "UDP" : "TCP", host, port);
return 0;
}
int at_cip_send(const uint8_t *data, size_t len)
{
if (cip_tls) return (int)esp_tls_conn_write(cip_tls, data, len);
if (cip_sock < 0) return -1;
return send(cip_sock, data, len, 0);
}
void at_cip_close()
{
if (!at_cip_is_connected()) return;
if (cip_tls)
{
esp_tls_conn_destroy(cip_tls);
cip_tls = NULL;
}
else
{
close(cip_sock);
cip_sock = -1;
}
cip_type = CIP_TYPE_NONE;
cip_host[0] = 0;
cip_port = 0;
}
void at_cip_rx_task(void *arg)
{
uint8_t buf[1024];
while (1)
{
if (!at_cip_is_connected())
{
vTaskDelay(pdMS_TO_TICKS(100));
continue;
}
esp_tls_t *tls = cip_tls; // snapshot; at_cip_close() may change these concurrently
int sock = cip_sock;
int n = tls ? (int)esp_tls_conn_read(tls, buf, sizeof(buf)) : recv(sock, buf, sizeof(buf), 0);
if (n > 0)
{
char hdr[24];
snprintf(hdr, sizeof(hdr), "\r\n+IPD,%d:", n);
at_send_str(hdr);
at_send_raw(buf, n);
}
else if (n == 0)
{
// Peer closed (TCP/SSL only - UDP recv() doesn't return 0 on a connected socket).
at_cip_close();
at_send_str("\r\nCLOSED\r\n");
}
// n < 0: either the SO_RCVTIMEO timeout expired (EAGAIN/EWOULDBLOCK, expected -
// esp_tls_conn_read() surfaces the same underlying-socket timeout as a generic
// negative return too, not distinguished from a real error here, matching how
// the plain-TCP path already treats every negative read as "retry") or the
// connection was closed out from under us by at_cip_close() (also fine,
// at_cip_is_connected() will be false at the top next time).
}
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
enum
{
CIP_TYPE_NONE = 0,
CIP_TYPE_TCP,
CIP_TYPE_UDP,
CIP_TYPE_SSL,
};
void at_cip_init();
int at_cip_start(const char *type, const char *host, int port); // 0 ok, <0 on error, -2 if already connected
int at_cip_send(const uint8_t *data, size_t len); // returns bytes sent, or <0
void at_cip_close();
bool at_cip_is_connected();
void at_cip_status_line(char *buf, size_t buflen);
void at_cip_rx_task(void *arg);
+572
View File
@@ -0,0 +1,572 @@
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "main.h"
#include "esp_spi_defs.h"
#include "wifi.h"
#include "at_cip.h"
#include "at_uart.h"
#define AT_CIPSEND_MAX_LEN 1460
static const char *TAG = "at_uart";
static bool at_echo = true;
enum
{
AT_MODE_LINE,
AT_MODE_CIPSEND_DATA,
};
static int at_mode = AT_MODE_LINE;
static uint8_t at_cipsend_buf[AT_CIPSEND_MAX_LEN];
static size_t at_cipsend_len = 0;
static size_t at_cipsend_have = 0;
void at_uart_init()
{
uart_config_t cfg = {};
cfg.baud_rate = AT_UART_BAUD;
cfg.data_bits = UART_DATA_8_BITS;
cfg.parity = UART_PARITY_DISABLE;
cfg.stop_bits = UART_STOP_BITS_1;
cfg.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
cfg.source_clk = UART_SCLK_DEFAULT;
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, AT_UART_RX_BUF_SIZE, AT_UART_TX_BUF_SIZE, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &cfg));
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, AT_UART_TX_PIN, AT_UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_LOGI(TAG, "AT UART ready: TX=%d RX=%d %d 8N1", AT_UART_TX_PIN, AT_UART_RX_PIN, AT_UART_BAUD);
// Real ESP8266 AT firmware emits this on the AT UART itself after boot/AT+RST;
// AT client libraries commonly block waiting for it before sending further commands.
at_send_raw("\r\nready\r\n", 9);
}
void at_send_raw(const void *data, size_t len)
{
uart_write_bytes(UART_NUM_1, (const char*)data, len);
}
void at_send_str(const char *s)
{
at_send_raw(s, strlen(s));
}
static void at_reply_ok()
{
at_send_str("\r\nOK\r\n");
}
static void at_reply_error()
{
at_send_str("\r\nERROR\r\n");
}
static void at_cmd_gmr()
{
// The ZX Evolution client's single-shot display capture after AT+GMR is
// timing-sensitive on real hardware (verified: even small changes in send
// timing shift what fraction of this reply the client's display happens to
// catch, sometimes none of it, without affecting correctness - GMR's actual
// reply content and the OK are always sent and processed correctly
// regardless). A short delay between the echoed command line and the reply
// gives the client's display time to get ready before the content arrives.
vTaskDelay(pdMS_TO_TICKS(500));
// No leading CRLF and no blank line before OK: verified byte-for-byte
// against a real ESP8266 AT module's actual AT+GMR reply on real hardware.
char buf[192];
at_send_str(CP_STRING);
at_send_str("\r\n");
at_send_str(API_STRING);
at_send_str("\r\n");
snprintf(buf, sizeof(buf), "SDK version:%s", IDF_VER);
at_send_str(buf);
at_send_str("\r\n");
at_send_str("OK");
at_send_str("\r\n");
}
// Splits comma-separated args in place, honoring double-quoted tokens
// (e.g. `"ssid","pass"` or `"TCP",host,port`). Returns the number of tokens
// found (up to max_args); each out[i] points into 'args' (NUL-terminated).
static int at_parse_args(char *args, char *out[], int max_args)
{
int count = 0;
char *p = args;
while (*p && count < max_args)
{
while (*p == ' ') p++;
if (!*p) break;
if (*p == '"')
{
p++;
out[count++] = p;
while (*p && *p != '"') p++;
if (*p == '"') *p++ = 0;
}
else
{
out[count++] = p;
while (*p && *p != ',') p++;
}
while (*p == ' ') p++;
if (*p == ',') { *p = 0; p++; }
else if (*p) *p = 0;
}
return count;
}
static void at_cmd_cwmode(char *args, bool is_query)
{
if (is_query)
{
char buf[24];
snprintf(buf, sizeof(buf), "\r\n+CWMODE:1\r\n");
at_send_str(buf);
at_reply_ok();
return;
}
if (!args || strcmp(args, "1"))
{
at_reply_error(); // only STA mode (1) is supported - AP/APSTA out of scope
return;
}
at_reply_ok();
}
static void at_cmd_cwjap(char *args)
{
char *tok[2];
if (!args || at_parse_args(args, tok, 2) != 2)
{
at_reply_error();
return;
}
bool connected = wifi_connect_locked(tok[0], tok[1], CONNECT_TIMEOUT_MS);
if (!connected)
{
at_reply_error();
return;
}
// The ZX Evolution client (zifi_orig.asm) sends every AT command via zifi_send,
// which does a throwaway read (zifi_check_receive_command) right after writing
// the command - it patiently polls (screen-frame-gated, ~5s worst case) until
// ANY byte shows up, then slurps and discards it. For AT+CWJAP specifically,
// a SEPARATE loop right after re-polls for "OK\r" with no timeout at all. Real
// ESP8266 hardware takes several seconds to actually join, so that throwaway
// read naturally times out empty before the real reply lands, leaving it for
// the real wait loop to catch. Our join is near-instant (verified: the discard
// read grabs our whole reply within milliseconds, then the real wait loop hangs
// forever with nothing left to find) - so replying quickly here deterministically
// breaks this client. Deliberately hold the reply past that ~5s throwaway window.
vTaskDelay(pdMS_TO_TICKS(6500));
at_reply_ok();
}
static void at_cmd_cwqap()
{
wifi_disconnect_now_locked();
at_reply_ok();
}
static void at_cmd_cwlap()
{
if (!net_mtx)
initialize_wifi(); // lazily creates net_mtx as its first action
if (xSemaphoreTake(net_mtx, portMAX_DELAY) != pdTRUE)
{
at_reply_error();
return;
}
esp_err_t err = (esp_err_t)wf_scan(300);
if (err != ESP_OK)
{
xSemaphoreGive(net_mtx);
at_reply_error();
return;
}
uint16_t num = wf_get_ap_num();
char line[160];
for (uint16_t i = 0; i < num; i++)
{
uint8_t auth, chan;
int8_t rssi;
uint8_t *ssid;
wf_get_ap(i, auth, rssi, chan, ssid);
snprintf(line, sizeof(line), "\r\n+CWLAP:(%u,\"%s\",%d,%u)", auth, ssid, rssi, chan);
at_send_str(line);
}
xSemaphoreGive(net_mtx);
at_reply_ok();
}
static void at_cmd_cipsntpcfg(char *args, bool is_query)
{
bool enable;
int timezone;
char s1[64], s2[64], s3[64];
if (is_query)
{
sntp_get_config(enable, timezone, s1, s2, s3, sizeof(s1));
char buf[220];
if (s3[0])
snprintf(buf, sizeof(buf), "\r\n+CIPSNTPCFG:%d,%d,\"%s\",\"%s\",\"%s\"\r\n", enable ? 1 : 0, timezone, s1, s2, s3);
else if (s2[0])
snprintf(buf, sizeof(buf), "\r\n+CIPSNTPCFG:%d,%d,\"%s\",\"%s\"\r\n", enable ? 1 : 0, timezone, s1, s2);
else
snprintf(buf, sizeof(buf), "\r\n+CIPSNTPCFG:%d,%d,\"%s\"\r\n", enable ? 1 : 0, timezone, s1);
at_send_str(buf);
at_reply_ok();
return;
}
char *tok[5];
int n = args ? at_parse_args(args, tok, 5) : 0;
if (n < 2)
{
at_reply_error();
return;
}
char *endp = NULL;
long en = strtol(tok[0], &endp, 10);
if (!endp || *endp || (en != 0 && en != 1))
{
at_reply_error();
return;
}
endp = NULL;
long tz = strtol(tok[1], &endp, 10);
if (!endp || *endp || tz < -12 || tz > 14)
{
at_reply_error();
return;
}
sntp_apply_config(en != 0, (int)tz, n >= 3 ? tok[2] : NULL, n >= 4 ? tok[3] : NULL, n >= 5 ? tok[4] : NULL);
at_reply_ok();
}
static void at_cmd_cipsntptime()
{
char timestr[40];
sntp_format_time(timestr, sizeof(timestr));
char buf[64];
snprintf(buf, sizeof(buf), "\r\n+CIPSNTPTIME:%s\r\n", timestr);
at_send_str(buf);
at_reply_ok();
}
static void at_cmd_cifsr()
{
uint8_t ip[4], mask[4], gate[4], mac[6] = {0};
get_ip(ip, mask, gate);
esp_wifi_get_mac(WIFI_IF_STA, mac);
char buf[96];
snprintf(buf, sizeof(buf), "\r\n+CIFSR:STAIP,\"%u.%u.%u.%u\"\r\n+CIFSR:STAMAC,\"%02x:%02x:%02x:%02x:%02x:%02x\"\r\n",
ip[0], ip[1], ip[2], ip[3], mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
at_send_str(buf);
at_reply_ok();
}
static void at_cmd_cipstatus()
{
char buf[128];
at_cip_status_line(buf, sizeof(buf));
at_send_str("\r\n");
at_send_str(buf);
at_send_str("\r\n");
at_reply_ok();
}
static void at_cmd_cipstart(char *args)
{
char *tok[3];
if (!args || at_parse_args(args, tok, 3) != 3)
{
at_reply_error();
return;
}
int port = atoi(tok[2]);
int rc = at_cip_start(tok[0], tok[1], port);
if (rc == -2)
{
at_send_str("\r\nALREADY CONNECTED\r\n");
at_reply_error();
return;
}
if (rc != 0)
{
at_reply_error();
return;
}
at_send_str("\r\nCONNECT\r\n");
at_reply_ok();
}
static void at_cmd_cipsend(char *args)
{
if (!args)
{
at_reply_error();
return;
}
int len = atoi(args);
if (len <= 0 || len > AT_CIPSEND_MAX_LEN)
{
at_reply_error();
return;
}
if (!at_cip_is_connected())
{
at_reply_error();
return;
}
at_cipsend_len = (size_t)len;
at_cipsend_have = 0;
at_mode = AT_MODE_CIPSEND_DATA;
at_send_str("\r\nOK\r\n>");
}
static void at_cmd_cipclose()
{
at_cip_close();
at_send_str("\r\nCLOSED\r\n");
at_reply_ok();
}
// Dispatches one complete, NUL-terminated line (trailing \r/\n already stripped by caller).
static void at_dispatch_line(char *line, size_t len)
{
if (at_echo)
{
at_send_str(line);
at_send_str("\r\n");
}
if (len == 0) return; // silently ignore stray blank lines, matches real AT modules
if (!strcasecmp(line, "AT"))
{
at_reply_ok();
return;
}
if (!strncasecmp(line, "ATE", 3) && len == 4 && (line[3] == '0' || line[3] == '1'))
{
at_echo = (line[3] == '1');
at_reply_ok();
return;
}
if (strncasecmp(line, "AT+", 3) != 0)
{
at_reply_error();
return;
}
char *cmd = line + 3;
char *eq = strchr(cmd, '=');
char *args = NULL;
bool is_query = false;
if (eq)
{
*eq = 0;
args = eq + 1;
}
else
{
size_t clen = strlen(cmd);
if (clen && cmd[clen - 1] == '?')
{
cmd[clen - 1] = 0;
is_query = true;
}
}
if (!strcasecmp(cmd, "RST"))
{
at_reply_ok();
ESP_LOGE(TAG, "software restart requested by AT+RST on UART1");
vTaskDelay(pdMS_TO_TICKS(50));
esp_restart();
}
else if (!strcasecmp(cmd, "GMR"))
at_cmd_gmr();
else if (!strcasecmp(cmd, "CWMODE") || !strcasecmp(cmd, "CWMODE_CUR") || !strcasecmp(cmd, "CWMODE_DEF"))
at_cmd_cwmode(args, is_query);
else if (!strcasecmp(cmd, "CWJAP") || !strcasecmp(cmd, "CWJAP_CUR") || !strcasecmp(cmd, "CWJAP_DEF"))
at_cmd_cwjap(args);
else if (!strcasecmp(cmd, "CWQAP"))
at_cmd_cwqap();
else if (!strcasecmp(cmd, "CWLAP"))
at_cmd_cwlap();
else if (!strcasecmp(cmd, "CWAUTOCONN"))
at_reply_ok(); // autoconnect-on-boot persistence is out of scope by design; accept and no-op
else if (!strcasecmp(cmd, "CIPMUX"))
{
// Only single-connection mode (0) is supported - CIPMUX multi-connection is out of scope.
if (args && !strcmp(args, "0"))
at_reply_ok();
else
at_reply_error();
}
else if (!strcasecmp(cmd, "CIFSR"))
at_cmd_cifsr();
else if (!strcasecmp(cmd, "CIPSNTPCFG"))
at_cmd_cipsntpcfg(args, is_query);
else if (!strcasecmp(cmd, "CIPSNTPTIME"))
at_cmd_cipsntptime();
else if (!strcasecmp(cmd, "CIPSTATUS"))
at_cmd_cipstatus();
else if (!strcasecmp(cmd, "CIPSTART"))
at_cmd_cipstart(args);
else if (!strcasecmp(cmd, "CIPSEND"))
at_cmd_cipsend(args);
else if (!strcasecmp(cmd, "CIPCLOSE"))
at_cmd_cipclose();
else
{
ESP_LOGW(TAG, "unknown command '%s'", cmd);
at_reply_error();
}
}
static void at_process_cipsend_byte(uint8_t b)
{
at_cipsend_buf[at_cipsend_have++] = b;
if (at_cipsend_have < at_cipsend_len)
return;
int sent = at_cip_send(at_cipsend_buf, at_cipsend_have);
at_mode = AT_MODE_LINE;
if (sent < 0 || (size_t)sent != at_cipsend_have)
at_send_str("\r\nSEND FAIL\r\n");
else
at_send_str("\r\nSEND OK\r\n");
}
void at_task(void *arg)
{
char linebuf[AT_LINE_MAX_LEN];
size_t linelen = 0;
bool just_saw_cr = false;
uint8_t rxbuf[64];
while (1)
{
int n = uart_read_bytes(UART_NUM_1, rxbuf, sizeof(rxbuf), pdMS_TO_TICKS(20));
for (int i = 0; i < n; i++)
{
uint8_t b = rxbuf[i];
// Swallow the LF half of a CRLF pair unconditionally, *before* looking at
// at_mode - a command dispatched on '\r' (e.g. AT+CIPSEND=<len>) can switch
// straight into AT_MODE_CIPSEND_DATA, and the following '\n' must not be
// mistaken for the first byte of that payload.
if (just_saw_cr)
{
just_saw_cr = false;
if (b == '\n') continue;
}
if (at_mode == AT_MODE_CIPSEND_DATA)
{
at_process_cipsend_byte(b);
continue;
}
char c = (char)b;
if (c == '\r')
{
linebuf[linelen] = 0;
at_dispatch_line(linebuf, linelen);
linelen = 0;
just_saw_cr = true;
}
else if (c == '\n')
{
linebuf[linelen] = 0;
at_dispatch_line(linebuf, linelen);
linelen = 0;
}
else if (linelen < AT_LINE_MAX_LEN - 1)
{
linebuf[linelen++] = c;
}
else
{
linelen = 0;
at_reply_error();
}
}
}
}
TaskHandle_t g_at_task_handle = NULL;
TaskHandle_t g_at_cip_rx_task_handle = NULL;
void at_uart_suspend()
{
if (g_at_task_handle) vTaskSuspend(g_at_task_handle);
if (g_at_cip_rx_task_handle) vTaskSuspend(g_at_cip_rx_task_handle);
}
void at_uart_resume()
{
if (g_at_task_handle) vTaskResume(g_at_task_handle);
if (g_at_cip_rx_task_handle) vTaskResume(g_at_cip_rx_task_handle);
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <stddef.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define AT_UART_TX_PIN 18
#define AT_UART_RX_PIN 8
#define AT_UART_BAUD 115200
#define AT_UART_RX_BUF_SIZE 2048
#define AT_UART_TX_BUF_SIZE 2048
#define AT_LINE_MAX_LEN 512
void at_uart_init();
void at_task(void *arg);
void at_send_raw(const void *data, size_t len);
void at_send_str(const char *s);
// Populated by main.cpp's xTaskCreatePinnedToCoreWithCaps() calls for
// at_task/at_cip_rx_task - used by at_uart_suspend()/at_uart_resume() below.
extern TaskHandle_t g_at_task_handle;
extern TaskHandle_t g_at_cip_rx_task_handle;
// Pause/resume the whole AT-command modem interface (both the UART command
// parser and the CIP data-mode RX relay) - used while the SMB2/3 server is
// active, since the Z80 side is single-tasking and can't be driving both
// the classic ZiFi AT interface and the smbvfs.wmf plugin at once anyway.
void at_uart_suspend();
void at_uart_resume();
+10835
View File
File diff suppressed because it is too large Load Diff
+696
View File
@@ -0,0 +1,696 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct CmdApp CmdApp;
typedef struct CmdFsManager CmdFsManager;
typedef struct CmdPanel CmdPanel;
void cmd_console_register_system_commands();
void cmd_set_status(CmdApp *app, const char *fmt, ...);
void cmd_show_error(CmdApp *app, const char *title, esp_err_t err);
#ifdef __cplusplus
}
#endif
// --------------- cmd_types.h ---------------
#ifdef __cplusplus
extern "C" {
#endif
#define CMD_TEXT_COLS 80
#define CMD_TEXT_ROWS 30
#define CMD_FILE_NAME_MAX 256
#define CMD_PATH_MAX 256
#define CMD_PANEL_COUNT 2
#ifndef CMD_ERR_BASE
#define CMD_ERR_BASE 0x7000
#endif
typedef esp_err_t CmdErr;
typedef enum
{
CMD_DEVICE_SD = 0,
CMD_DEVICE_FAT,
CMD_DEVICE_TSF,
CMD_DEVICE_MAX,
CMD_DEVICE_INVALID = -1
} cmd_device_id_t;
typedef enum
{
CMD_ENTRY_UNKNOWN = 0,
CMD_ENTRY_FILE,
CMD_ENTRY_DIR,
CMD_ENTRY_PARENT
} cmd_entry_type_t;
typedef enum
{
CMD_ENTRY_FLAG_NONE = 0,
CMD_ENTRY_FLAG_SELECTED = 1u << 0,
CMD_ENTRY_FLAG_ERROR = 1u << 1
} cmd_entry_flags_t;
typedef struct
{
char name[CMD_FILE_NAME_MAX];
cmd_entry_type_t type;
uint64_t size;
uint64_t mtime;
uint32_t flags;
} cmd_file_entry_t;
typedef struct
{
int x;
int y;
} cmd_point_t;
typedef struct
{
int w;
int h;
} cmd_size_t;
typedef struct
{
int x;
int y;
int w;
int h;
} cmd_rect_t;
typedef enum
{
CMD_KEY_NONE = 0,
CMD_KEY_CHAR,
CMD_KEY_ESC,
CMD_KEY_ENTER,
CMD_KEY_BACKSPACE,
CMD_KEY_TAB,
CMD_KEY_UP,
CMD_KEY_DOWN,
CMD_KEY_LEFT,
CMD_KEY_RIGHT,
CMD_KEY_HOME,
CMD_KEY_END,
CMD_KEY_PAGE_UP,
CMD_KEY_PAGE_DOWN,
CMD_KEY_INSERT,
CMD_KEY_DELETE,
CMD_KEY_F1,
CMD_KEY_F2,
CMD_KEY_F3,
CMD_KEY_F4,
CMD_KEY_F5,
CMD_KEY_F6,
CMD_KEY_F7,
CMD_KEY_F8,
CMD_KEY_F9,
CMD_KEY_F10,
CMD_KEY_F11,
CMD_KEY_F12,
CMD_KEY_UNKNOWN
} cmd_key_t;
typedef enum
{
CMD_EVENT_NONE = 0,
CMD_EVENT_KEY,
CMD_EVENT_QUIT
} cmd_event_type_t;
typedef struct
{
cmd_event_type_t type;
cmd_key_t key;
uint32_t ch;
uint32_t mods;
} cmd_event_t;
typedef enum
{
CMD_KEY_MOD_NONE = 0,
CMD_KEY_MOD_SHIFT = 1u << 0,
CMD_KEY_MOD_CTRL = 1u << 1,
CMD_KEY_MOD_ALT = 1u << 2
} cmd_key_mod_t;
typedef enum
{
CMD_OK = ESP_OK,
CMD_ERR_INVALID_DEVICE = CMD_ERR_BASE,
CMD_ERR_NOT_MOUNTED,
CMD_ERR_PATH_TOO_LONG,
CMD_ERR_ENTRY_TOO_LONG,
CMD_ERR_DIR_FULL,
CMD_ERR_INPUT_UNAVAILABLE,
CMD_ERR_CANCELLED
} cmd_err_t;
const char *cmd_device_name(cmd_device_id_t id);
const char *cmd_entry_type_name(cmd_entry_type_t type);
#ifdef __cplusplus
}
#endif
// --------------- cmd_display.h ---------------
#ifdef __cplusplus
extern "C" {
#endif
#define CMD_DISPLAY_DEFAULT_COLS CMD_TEXT_COLS
#define CMD_DISPLAY_DEFAULT_ROWS CMD_TEXT_ROWS
#define CMD_DISPLAY_MAX_COLS 160
#define CMD_DISPLAY_MAX_ROWS 64
#define CMD_DISPLAY_COLS CMD_DISPLAY_MAX_COLS
#define CMD_DISPLAY_ROWS CMD_DISPLAY_MAX_ROWS
#define CMD_DISPLAY_CELL_COUNT (CMD_DISPLAY_COLS * CMD_DISPLAY_ROWS)
#define CMD_DISPLAY_MUX_MAX_BACKENDS 4
#define CMD_DISPLAY_BACKEND_FLAG_FORCE_PARTIAL 1u
typedef uint16_t cmd_display_attr_t;
typedef enum
{
CMD_DISPLAY_COLOR_BLACK = 0,
CMD_DISPLAY_COLOR_BLUE,
CMD_DISPLAY_COLOR_GREEN,
CMD_DISPLAY_COLOR_CYAN,
CMD_DISPLAY_COLOR_RED,
CMD_DISPLAY_COLOR_MAGENTA,
CMD_DISPLAY_COLOR_YELLOW,
CMD_DISPLAY_COLOR_WHITE,
CMD_DISPLAY_COLOR_BRIGHT_BLACK,
CMD_DISPLAY_COLOR_BRIGHT_BLUE,
CMD_DISPLAY_COLOR_BRIGHT_GREEN,
CMD_DISPLAY_COLOR_BRIGHT_CYAN,
CMD_DISPLAY_COLOR_BRIGHT_RED,
CMD_DISPLAY_COLOR_BRIGHT_MAGENTA,
CMD_DISPLAY_COLOR_BRIGHT_YELLOW,
CMD_DISPLAY_COLOR_BRIGHT_WHITE
} cmd_display_color_t;
typedef enum
{
CMD_DISPLAY_ATTR_FLAG_NONE = 0,
CMD_DISPLAY_ATTR_FLAG_BOLD = 1u << 0,
CMD_DISPLAY_ATTR_FLAG_INVERSE = 1u << 1,
CMD_DISPLAY_ATTR_FLAG_UNDERLINE = 1u << 2,
CMD_DISPLAY_ATTR_FLAG_SELECTED = 1u << 3
} cmd_display_attr_flag_t;
typedef enum
{
CMD_DISPLAY_MODE_CONSOLE = 0,
CMD_DISPLAY_MODE_FT812,
CMD_DISPLAY_MODE_MIRROR
} CmdDisplayMode;
#define CMD_DISPLAY_ATTR_FG_MASK 0x000f
#define CMD_DISPLAY_ATTR_BG_MASK 0x00f0
#define CMD_DISPLAY_ATTR_FLAGS_MASK 0xff00
#define CMD_DISPLAY_ATTR_BG_SHIFT 4
#define CMD_DISPLAY_ATTR_FLAGS_SHIFT 8
#define CMD_DISPLAY_ATTR_DEFAULT ((cmd_display_attr_t)(CMD_DISPLAY_COLOR_WHITE | (CMD_DISPLAY_COLOR_BLACK << CMD_DISPLAY_ATTR_BG_SHIFT)))
#define CMD_DISPLAY_BOX_TL ((char)0xC9)
#define CMD_DISPLAY_BOX_TR ((char)0xBB)
#define CMD_DISPLAY_BOX_BL ((char)0xC8)
#define CMD_DISPLAY_BOX_BR ((char)0xBC)
#define CMD_DISPLAY_BOX_H ((char)0xCD)
#define CMD_DISPLAY_BOX_V ((char)0xBA)
typedef struct
{
char ch;
cmd_display_attr_t attr;
} CmdDisplayCell;
typedef struct
{
cmd_size_t size;
CmdDisplayCell cells[CMD_DISPLAY_ROWS][CMD_DISPLAY_COLS];
} CmdDisplayBuffer;
typedef struct CmdDisplayBackend CmdDisplayBackend;
typedef struct CmdDisplayMux CmdDisplayMux;
typedef esp_err_t (*CmdDisplayBackendInitFn)(CmdDisplayBackend *backend);
typedef void (*CmdDisplayBackendDeinitFn)(CmdDisplayBackend *backend);
typedef esp_err_t (*CmdDisplayBackendClearFn)(CmdDisplayBackend *backend);
typedef esp_err_t (*CmdDisplayBackendPresentFn)(CmdDisplayBackend *backend, const CmdDisplayBuffer *buffer);
typedef esp_err_t (*CmdDisplayBackendPutCharFn)(CmdDisplayBackend *backend, int x, int y, char ch, cmd_display_attr_t attr);
typedef esp_err_t (*CmdDisplayBackendWriteTextFn)(CmdDisplayBackend *backend, int x, int y, const char *text, cmd_display_attr_t attr);
typedef esp_err_t (*CmdDisplayBackendGetSizeFn)(CmdDisplayBackend *backend, cmd_size_t *size);
struct CmdDisplayBackend
{
const char *name;
void *ctx;
uint32_t flags;
CmdDisplayBackendInitFn init;
CmdDisplayBackendDeinitFn deinit;
CmdDisplayBackendClearFn clear;
CmdDisplayBackendPresentFn present;
CmdDisplayBackendPutCharFn put_char;
CmdDisplayBackendWriteTextFn write_text;
CmdDisplayBackendGetSizeFn get_size;
};
struct CmdDisplayMux
{
CmdDisplayBackend *backends[CMD_DISPLAY_MUX_MAX_BACKENDS];
size_t count;
};
extern CmdDisplayBackend cmd_display_console_backend;
extern CmdDisplayBackend cmd_display_ft812_backend;
cmd_display_attr_t cmd_display_make_attr(cmd_display_color_t fg, cmd_display_color_t bg, uint8_t flags);
cmd_display_color_t cmd_display_attr_fg(cmd_display_attr_t attr);
cmd_display_color_t cmd_display_attr_bg(cmd_display_attr_t attr);
uint8_t cmd_display_attr_flags(cmd_display_attr_t attr);
const char *cmd_display_mode_name(CmdDisplayMode mode);
bool cmd_display_mode_parse(const char *text, CmdDisplayMode *mode);
bool cmd_display_mode_has_console(CmdDisplayMode mode);
bool cmd_display_mode_has_ft812(CmdDisplayMode mode);
void cmd_display_buffer_init(CmdDisplayBuffer *buffer);
esp_err_t cmd_display_buffer_set_size(CmdDisplayBuffer *buffer, int w, int h);
void cmd_display_buffer_clear(CmdDisplayBuffer *buffer, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_buffer_put_char(CmdDisplayBuffer *buffer, int x, int y, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_buffer_write_text(CmdDisplayBuffer *buffer, int x, int y, const char *text, cmd_display_attr_t attr);
esp_err_t cmd_display_buffer_fill_rect(CmdDisplayBuffer *buffer, const cmd_rect_t *rect, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_buffer_draw_hline(CmdDisplayBuffer *buffer, int x, int y, int w, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_buffer_draw_vline(CmdDisplayBuffer *buffer, int x, int y, int h, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_buffer_draw_box(CmdDisplayBuffer *buffer, const cmd_rect_t *rect, cmd_display_attr_t attr);
CmdDisplayCell *cmd_display_buffer_cell(CmdDisplayBuffer *buffer, int x, int y);
const CmdDisplayCell *cmd_display_buffer_cell_const(const CmdDisplayBuffer *buffer, int x, int y);
bool cmd_display_point_inside(int x, int y);
bool cmd_display_buffer_point_inside(const CmdDisplayBuffer *buffer, int x, int y);
esp_err_t cmd_display_backend_init(CmdDisplayBackend *backend);
void cmd_display_backend_deinit(CmdDisplayBackend *backend);
esp_err_t cmd_display_backend_clear(CmdDisplayBackend *backend);
esp_err_t cmd_display_backend_present(CmdDisplayBackend *backend, const CmdDisplayBuffer *buffer);
esp_err_t cmd_display_backend_put_char(CmdDisplayBackend *backend, int x, int y, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_backend_write_text(CmdDisplayBackend *backend, int x, int y, const char *text, cmd_display_attr_t attr);
esp_err_t cmd_display_backend_get_size(CmdDisplayBackend *backend, cmd_size_t *size);
void cmd_display_mux_init(CmdDisplayMux *mux);
esp_err_t cmd_display_mux_add_backend(CmdDisplayMux *mux, CmdDisplayBackend *backend);
esp_err_t cmd_display_mux_add_mode(CmdDisplayMux *mux, CmdDisplayMode mode);
esp_err_t cmd_display_mux_remove_backend(CmdDisplayMux *mux, CmdDisplayBackend *backend);
esp_err_t cmd_display_mux_init_backends(CmdDisplayMux *mux);
esp_err_t cmd_display_mux_get_size(CmdDisplayMux *mux, cmd_size_t *size);
void cmd_display_mux_deinit_backends(CmdDisplayMux *mux);
esp_err_t cmd_display_mux_clear(CmdDisplayMux *mux);
esp_err_t cmd_display_mux_present(CmdDisplayMux *mux, const CmdDisplayBuffer *buffer);
esp_err_t cmd_display_mux_put_char(CmdDisplayMux *mux, int x, int y, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_mux_write_text(CmdDisplayMux *mux, int x, int y, const char *text, cmd_display_attr_t attr);
esp_err_t cmd_display_console_init(CmdDisplayBackend *backend);
void cmd_display_console_deinit(CmdDisplayBackend *backend);
esp_err_t cmd_display_console_clear(CmdDisplayBackend *backend);
esp_err_t cmd_display_console_present(CmdDisplayBackend *backend, const CmdDisplayBuffer *buffer);
esp_err_t cmd_display_console_put_char(CmdDisplayBackend *backend, int x, int y, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_console_write_text(CmdDisplayBackend *backend, int x, int y, const char *text, cmd_display_attr_t attr);
esp_err_t cmd_display_console_get_size(CmdDisplayBackend *backend, cmd_size_t *size);
esp_err_t cmd_display_ft812_begin(CmdDisplayBackend *backend);
void cmd_display_ft812_end(CmdDisplayBackend *backend);
esp_err_t cmd_display_ft812_clear(CmdDisplayBackend *backend);
esp_err_t cmd_display_ft812_present(CmdDisplayBackend *backend, const CmdDisplayBuffer *buffer);
esp_err_t cmd_display_ft812_put_char(CmdDisplayBackend *backend, int x, int y, char ch, cmd_display_attr_t attr);
esp_err_t cmd_display_ft812_write_text(CmdDisplayBackend *backend, int x, int y, const char *text, cmd_display_attr_t attr);
esp_err_t cmd_display_ft812_get_size(CmdDisplayBackend *backend, cmd_size_t *size);
#ifdef __cplusplus
}
#endif
// --------------- cmd_fileops.h ---------------
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CMD_FILEOPS_BUFFER_SIZE
#define CMD_FILEOPS_BUFFER_SIZE 4096
#endif
#ifndef CMD_FILEOPS_MAX_DEPTH
#define CMD_FILEOPS_MAX_DEPTH 16
#endif
typedef enum
{
CMD_FILEOPS_OP_COPY = 0,
CMD_FILEOPS_OP_MOVE,
CMD_FILEOPS_OP_DELETE
} cmd_fileops_op_t;
typedef enum
{
CMD_FILEOPS_FLAG_NONE = 0,
CMD_FILEOPS_FLAG_RECURSIVE = 1u << 0,
CMD_FILEOPS_FLAG_OVERWRITE = 1u << 1
} cmd_fileops_flags_t;
typedef struct
{
cmd_fileops_op_t op;
const char *filename;
uint64_t bytes_done;
uint64_t bytes_total;
size_t files_done;
size_t files_total;
bool can_cancel;
} CmdFileopsProgress;
typedef bool (*CmdFileopsProgressFn)(const CmdFileopsProgress *progress, void *ctx);
typedef struct
{
uint32_t flags;
CmdFileopsProgressFn progress;
void *progress_ctx;
} CmdFileopsOptions;
esp_err_t cmd_fileops_alloc_global_buffers();
void cmd_fileops_free_global_buffers();
esp_err_t cmd_fileops_copy_path(CmdFsManager *fs,
cmd_device_id_t src_device,
const char *src_path,
cmd_device_id_t dst_device,
const char *dst_path,
const CmdFileopsOptions *options);
esp_err_t cmd_fileops_copy_file(CmdFsManager *fs,
cmd_device_id_t src_device,
const char *src_path,
cmd_device_id_t dst_device,
const char *dst_path,
const CmdFileopsOptions *options);
esp_err_t cmd_fileops_move_path(CmdFsManager *fs,
cmd_device_id_t src_device,
const char *src_path,
cmd_device_id_t dst_device,
const char *dst_path,
const CmdFileopsOptions *options);
esp_err_t cmd_fileops_delete_path(CmdFsManager *fs,
cmd_device_id_t device_id,
const char *path,
const CmdFileopsOptions *options);
esp_err_t cmd_fileops_copy_selected(CmdPanel *active,
CmdPanel *passive,
const CmdFileopsOptions *options);
esp_err_t cmd_fileops_move_selected(CmdPanel *active,
CmdPanel *passive,
const CmdFileopsOptions *options);
esp_err_t cmd_fileops_delete_selected(CmdPanel *panel,
const CmdFileopsOptions *options);
#ifdef __cplusplus
}
#endif
// --------------- cmd_fs.h ---------------
#ifdef __cplusplus
extern "C" {
#endif
#define CMD_FS_DRIVER_MAX CMD_DEVICE_MAX
#ifndef CMD_FS_SD_ROOT_PATH
#define CMD_FS_SD_ROOT_PATH "/sd"
#endif
typedef struct CmdFsDriver CmdFsDriver;
typedef struct CmdFsManager CmdFsManager;
typedef struct CmdFsList CmdFsList;
typedef struct CmdFsFile CmdFsFile;
typedef esp_err_t (*CmdFsMountFn)(CmdFsDriver *driver);
typedef esp_err_t (*CmdFsUnmountFn)(CmdFsDriver *driver);
typedef esp_err_t (*CmdFsListBeginFn)(CmdFsDriver *driver, const char *path, CmdFsList *list);
typedef esp_err_t (*CmdFsListNextFn)(CmdFsDriver *driver, CmdFsList *list, cmd_file_entry_t *entry);
typedef void (*CmdFsListCloseFn)(CmdFsDriver *driver, CmdFsList *list);
typedef esp_err_t (*CmdFsStatFn)(CmdFsDriver *driver, const char *path, cmd_file_entry_t *entry);
typedef esp_err_t (*CmdFsMkdirFn)(CmdFsDriver *driver, const char *path);
typedef esp_err_t (*CmdFsRemoveFn)(CmdFsDriver *driver, const char *path);
typedef esp_err_t (*CmdFsRenameFn)(CmdFsDriver *driver, const char *old_path, const char *new_path);
typedef esp_err_t (*CmdFsOpenFn)(CmdFsDriver *driver, const char *path, CmdFsFile *file);
typedef esp_err_t (*CmdFsReadFn)(CmdFsDriver *driver, CmdFsFile *file, void *dst, size_t size, size_t *out_size);
typedef esp_err_t (*CmdFsWriteFn)(CmdFsDriver *driver, CmdFsFile *file, const void *src, size_t size, size_t *out_size);
typedef esp_err_t (*CmdFsSeekFn)(CmdFsDriver *driver, CmdFsFile *file, int64_t offset, int whence);
typedef esp_err_t (*CmdFsTellFn)(CmdFsDriver *driver, CmdFsFile *file, uint64_t *out_pos);
typedef esp_err_t (*CmdFsCloseFn)(CmdFsDriver *driver, CmdFsFile *file);
struct CmdFsList
{
cmd_device_id_t device_id;
char path[CMD_PATH_MAX];
char abs_path[CMD_PATH_MAX];
void *dir;
size_t index;
bool parent_pending;
uint32_t flags;
};
struct CmdFsFile
{
cmd_device_id_t device_id;
FILE *fp;
void *ctx;
char name[CMD_FILE_NAME_MAX];
uint32_t flags;
};
typedef struct
{
uint64_t total_bytes;
uint64_t free_bytes;
}
CmdFsSpaceInfo;
struct CmdFsDriver
{
cmd_device_id_t device_id;
const char *name;
const char *root_path;
void *ctx;
CmdFsMountFn mount;
CmdFsUnmountFn unmount;
CmdFsListBeginFn list_begin;
CmdFsListNextFn list_next;
CmdFsListCloseFn list_close;
CmdFsStatFn stat;
CmdFsMkdirFn mkdir;
CmdFsRemoveFn remove;
CmdFsRenameFn rename;
CmdFsOpenFn open_read;
CmdFsOpenFn open_write;
CmdFsReadFn read;
CmdFsWriteFn write;
CmdFsSeekFn seek;
CmdFsTellFn tell;
CmdFsCloseFn close;
};
struct CmdFsManager
{
CmdFsDriver *drivers[CMD_FS_DRIVER_MAX];
uint32_t registered_mask;
uint32_t mounted_mask;
};
extern CmdFsDriver cmd_fs_sd_driver;
extern CmdFsDriver cmd_fs_fat_driver;
extern CmdFsDriver cmd_fs_tsf_driver;
bool cmd_fs_is_valid_device_id(cmd_device_id_t device_id);
void cmd_fs_manager_init(CmdFsManager *manager);
esp_err_t cmd_fs_manager_register_driver(CmdFsManager *manager, CmdFsDriver *driver);
esp_err_t cmd_fs_manager_unregister_driver(CmdFsManager *manager, cmd_device_id_t device_id);
CmdFsDriver *cmd_fs_manager_get_driver(CmdFsManager *manager, cmd_device_id_t device_id);
const CmdFsDriver *cmd_fs_manager_get_driver_const(const CmdFsManager *manager, cmd_device_id_t device_id);
esp_err_t cmd_fs_manager_register_default_drivers(CmdFsManager *manager);
esp_err_t cmd_fs_manager_build_abs_path(const CmdFsManager *manager, cmd_device_id_t device_id, const char *current_path, const char *filename, char *out, size_t out_size);
esp_err_t cmd_fs_manager_mount(CmdFsManager *manager, cmd_device_id_t device_id);
esp_err_t cmd_fs_manager_unmount(CmdFsManager *manager, cmd_device_id_t device_id);
esp_err_t cmd_fs_manager_unmount_all(CmdFsManager *manager);
esp_err_t cmd_fs_manager_list_begin(CmdFsManager *manager, cmd_device_id_t device_id, const char *path, CmdFsList *list);
esp_err_t cmd_fs_manager_list_next(CmdFsManager *manager, CmdFsList *list, cmd_file_entry_t *entry);
void cmd_fs_manager_list_close(CmdFsManager *manager, CmdFsList *list);
esp_err_t cmd_fs_manager_stat(CmdFsManager *manager, cmd_device_id_t device_id, const char *path, cmd_file_entry_t *entry);
esp_err_t cmd_fs_manager_space_info(CmdFsManager *manager, cmd_device_id_t device_id, CmdFsSpaceInfo *info);
esp_err_t cmd_fs_manager_mkdir(CmdFsManager *manager, cmd_device_id_t device_id, const char *path);
esp_err_t cmd_fs_manager_remove(CmdFsManager *manager, cmd_device_id_t device_id, const char *path);
esp_err_t cmd_fs_manager_rename(CmdFsManager *manager, cmd_device_id_t device_id, const char *old_path, const char *new_path);
esp_err_t cmd_fs_manager_open_read(CmdFsManager *manager, cmd_device_id_t device_id, const char *path, CmdFsFile *file);
esp_err_t cmd_fs_manager_open_write(CmdFsManager *manager, cmd_device_id_t device_id, const char *path, CmdFsFile *file);
esp_err_t cmd_fs_manager_read(CmdFsManager *manager, CmdFsFile *file, void *dst, size_t size, size_t *out_size);
esp_err_t cmd_fs_manager_write(CmdFsManager *manager, CmdFsFile *file, const void *src, size_t size, size_t *out_size);
esp_err_t cmd_fs_manager_seek(CmdFsManager *manager, CmdFsFile *file, int64_t offset, int whence);
esp_err_t cmd_fs_manager_tell(CmdFsManager *manager, CmdFsFile *file, uint64_t *out_pos);
esp_err_t cmd_fs_manager_close(CmdFsManager *manager, CmdFsFile *file);
esp_err_t cmd_fs_copy_path(const char *src, char *out, size_t out_size);
esp_err_t cmd_fs_join_path(const char *left, const char *right, char *out, size_t out_size);
esp_err_t cmd_fs_join3_path(const char *left, const char *middle, const char *right, char *out, size_t out_size);
esp_err_t cmd_fs_parent_path(const char *path, char *out, size_t out_size);
esp_err_t cmd_fs_extract_filename(const char *path, char *out, size_t out_size);
const char *cmd_fs_filename_ptr(const char *path);
bool cmd_fs_path_is_root(const char *path);
#ifdef __cplusplus
}
#endif
// --------------- cmd_input.h ---------------
#ifdef __cplusplus
extern "C" {
#endif
#define CMD_INPUT_WAIT_FOREVER UINT32_MAX
#define CMD_INPUT_ESC_TIMEOUT_MS 120
typedef struct CmdInputBackend CmdInputBackend;
typedef esp_err_t (*cmd_input_begin_fn)(CmdInputBackend *backend);
typedef void (*cmd_input_end_fn)(CmdInputBackend *backend);
typedef esp_err_t (*cmd_input_poll_event_fn)(CmdInputBackend *backend, cmd_event_t *event, uint32_t timeout_ms);
struct CmdInputBackend
{
const char *name;
void *ctx;
cmd_input_begin_fn begin;
cmd_input_end_fn end;
cmd_input_poll_event_fn poll_event;
};
void cmd_input_event_clear(cmd_event_t *event);
void cmd_input_event_key(cmd_event_t *event, cmd_key_t key, uint32_t mods);
void cmd_input_event_char(cmd_event_t *event, uint32_t ch, uint32_t mods);
void cmd_input_event_quit(cmd_event_t *event);
esp_err_t cmd_input_begin(CmdInputBackend *backend);
void cmd_input_end(CmdInputBackend *backend);
esp_err_t cmd_input_poll_event(CmdInputBackend *backend, cmd_event_t *event, uint32_t timeout_ms);
CmdInputBackend *cmd_input_console_backend();
CmdInputBackend *cmd_input_default_backend();
const char *cmd_input_key_name(cmd_key_t key);
#ifdef __cplusplus
}
#endif
// --------------- cmd_panel.h ---------------
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CMD_PANEL_MAX_ENTRIES
#define CMD_PANEL_MAX_ENTRIES 1000
#endif
#ifndef CMD_PANEL_DEFAULT_PAGE_STEP
#define CMD_PANEL_DEFAULT_PAGE_STEP 10
#endif
typedef enum
{
CMD_PANEL_FLAG_NONE = 0,
CMD_PANEL_FLAG_ACTIVE = 1u << 0,
CMD_PANEL_FLAG_ERROR = 1u << 1,
CMD_PANEL_FLAG_TRUNCATED = 1u << 2
} cmd_panel_flags_t;
struct CmdPanel
{
CmdFsManager *fs;
cmd_device_id_t device_id;
char current_path[CMD_PATH_MAX];
cmd_file_entry_t *entries;
size_t capacity;
size_t count;
size_t cursor;
size_t scroll;
size_t view_rows;
size_t selected_count;
uint64_t selected_size;
CmdFsSpaceInfo space_info;
esp_err_t space_info_error;
bool space_info_valid;
bool active;
uint32_t flags;
esp_err_t last_error;
};
esp_err_t cmd_panel_alloc_global_buffers();
void cmd_panel_free_global_buffers();
esp_err_t cmd_panel_alloc_entries(CmdPanel *panel, size_t capacity);
void cmd_panel_free_entries(CmdPanel *panel);
void cmd_panel_init(CmdPanel *panel, CmdFsManager *fs, cmd_device_id_t device_id);
void cmd_panel_reset(CmdPanel *panel);
void cmd_panel_set_active(CmdPanel *panel, bool active);
bool cmd_panel_is_active(const CmdPanel *panel);
esp_err_t cmd_panel_set_path(CmdPanel *panel, const char *path);
esp_err_t cmd_panel_reload(CmdPanel *panel);
esp_err_t cmd_panel_draw(CmdPanel *panel, CmdDisplayBuffer *buffer, const cmd_rect_t *rect);
void cmd_panel_cursor_up(CmdPanel *panel);
void cmd_panel_cursor_down(CmdPanel *panel);
void cmd_panel_cursor_page_up(CmdPanel *panel);
void cmd_panel_cursor_page_down(CmdPanel *panel);
void cmd_panel_cursor_home(CmdPanel *panel);
void cmd_panel_cursor_end(CmdPanel *panel);
const cmd_file_entry_t *cmd_panel_get_selected_entry(const CmdPanel *panel);
void cmd_panel_toggle_selection(CmdPanel *panel);
void cmd_panel_invert_selection(CmdPanel *panel);
void cmd_panel_clear_selection(CmdPanel *panel);
void cmd_panel_update_selection_stats(CmdPanel *panel);
bool cmd_panel_has_selection(const CmdPanel *panel);
esp_err_t cmd_panel_change_device(CmdPanel *panel, cmd_device_id_t device_id);
esp_err_t cmd_panel_enter_selected_dir(CmdPanel *panel);
esp_err_t cmd_panel_go_parent(CmdPanel *panel);
void cmd_panel_clamp_cursor(CmdPanel *panel);
void cmd_panel_ensure_cursor_visible(CmdPanel *panel);
#ifdef __cplusplus
}
#endif
+179
View File
@@ -0,0 +1,179 @@
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_console.h"
#include "esp_log.h"
#include "esp_vfs_dev.h" // Old headers for the aliasing functions
#include "driver/uart_vfs.h"
#include "driver/uart.h"
#include "argtable3/argtable3.h"
#include "linenoise/linenoise.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "main.h"
#include "ft8xx.h"
#include "console.h"
#include "tracker.h"
#include "sfx.h"
#include "tsf.h"
#include "opl.h"
#include "fatfs.h"
#include "cmd.h"
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
#include "wifi.h"
#endif
#if SOC_SDMMC_HOST_SUPPORTED
#include "sdmmc.h"
#endif
#define PROMPT_STR "zifi32"
#define CONSOLE_UART_RX_BUF_SIZE 1024
#define CONSOLE_PROMPT_MAX_LEN 32
char prompt_buf[CONSOLE_PROMPT_MAX_LEN];
const char *prompt = prompt_buf;
void console_register_commands()
{
esp_console_register_help_command();
esp_console_register_system_commands();
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
esp_console_register_wifi_commands();
#endif
ft_console_register_system_commands();
#if SOC_SDMMC_HOST_SUPPORTED
sdmmc_console_register_system_commands();
#endif
xm_console_register_system_commands();
sfx_console_register_system_commands();
tsf_console_register_system_commands();
opl_console_register_system_commands();
fat_console_register_system_commands();
cmd_console_register_system_commands();
}
void console_update_prompt()
{
if (linenoiseIsDumbMode())
{
snprintf(prompt_buf, sizeof(prompt_buf), PROMPT_STR "> ");
return;
}
#if CONFIG_LOG_COLORS
snprintf(prompt_buf, sizeof(prompt_buf), LOG_COLOR_I PROMPT_STR "> " LOG_RESET_COLOR);
#else
snprintf(prompt_buf, sizeof(prompt_buf), PROMPT_STR "> ");
#endif
}
void initialize_console()
{
// Drain stdout before reconfiguring it
fflush(stdout);
fsync(fileno(stdout));
// Disable buffering on stdin
setvbuf(stdin, NULL, _IONBF, 0);
log_sram_used(__FILE_NAME__ ": setvbuf");
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
uart_vfs_dev_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
uart_vfs_dev_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
// Install UART driver for interrupt-driven reads and writes
ESP_ERROR_CHECK(uart_driver_install((uart_port_t)CONFIG_ESP_CONSOLE_UART_NUM, CONSOLE_UART_RX_BUF_SIZE, 0, 0, NULL, 0));
log_sram_used(__FILE_NAME__ ": uart_driver_install");
// Tell VFS to use UART driver
uart_vfs_dev_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
// Initialize the console
esp_console_config_t console_config =
{
.max_cmdline_length = 256,
.max_cmdline_args = 128,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK(esp_console_init(&console_config));
// Configure linenoise line completion library.
// Single-line mode sends less data on each key press and is much more stable on UART.
linenoiseSetMultiLine(0);
// Detect whether the terminal supports escape sequences.
if (linenoiseProbe() != 0)
linenoiseSetDumbMode(1);
// Tell linenoise where to get command completions.
linenoiseSetCompletionCallback(&esp_console_get_completion);
// Hints trigger additional redraw traffic on every key press.
// Disabling them makes repeated backspace handling much more stable.
// linenoiseSetHintsCallback((linenoiseHintsCallback*)&esp_console_get_hint);
// Set command history size
linenoiseHistorySetMaxLen(100);
// Set command maximum length
linenoiseSetMaxLineLen(console_config.max_cmdline_length);
// Don't return empty lines
linenoiseAllowEmpty(false);
console_update_prompt();
// Register commands
console_register_commands();
log_sram_used(__FILE_NAME__ ": initialize_console end");
}
void console_task(void *arg)
{
_delay_ms(30);
printf("\n"
"Type 'help' to get the list of commands.\n"
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n");
while (true)
{
char* line = linenoise(prompt);
if (line == NULL)
continue;
if (strlen(line) > 0)
linenoiseHistoryAdd(line);
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND)
printf("Unrecognized command\n");
else if (err == ESP_ERR_INVALID_ARG)
{
}
else if (err == ESP_OK && ret != ESP_OK)
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
else if (err != ESP_OK)
printf("Internal error: %s\n", esp_err_to_name(err));
linenoiseFree(line);
}
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
void initialize_console();
void console_task(void *arg);
void esp_console_register_system_commands();
+359
View File
@@ -0,0 +1,359 @@
#include "main.h"
//------------------- DeHrust --------------------------
/* BBStream class */
class BBStream
{
private:
u8* base;
u8* p;
int idx;
int len;
bool eof;
u16 bits;
public:
BBStream(u8* from, int blockSize)
{
base = p = from;
len = blockSize;
idx = 0;
eof = false;
bits = getByte();
bits += 256 * getByte();
}
u8 getByte(void)
{
if (p - base == len)
{
eof = true; return 0;
}
return *p++;
}
u8 getBit()
{
u16 mask[] = { 0x8000, 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
u8 bit = (bits & mask[idx]) ? 1 : 0;
if (idx == 15)
{
bits = getByte();
bits += 256 * getByte();
}
idx = (idx + 1) % 16;
return bit;
}
u8 getBits(int n)
{
u8 r = 0;
do
{
r = 2 * r + getBit();
} while(--n);
return r;
}
bool error(void)
{
return eof;
}
};
/* depacker */
u16 dehrust(u8 *dst, u8 *src, int size)
{
BBStream s(src, size);
if (!dst || !src)
return 0;
u8 *to = dst;
*to++ = s.getByte();
u8 noBits = 2;
u8 mask[] = { 0, 0, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0 };
while (!s.error())
{
while (s.getBit())
*to++ = s.getByte();
u16 len = 0;
u8 bb /* = 0*/;
do
{
bb = s.getBits(2);
len += bb;
} while(bb == 0x03 && len != 0x0f);
short offset = 0;
if (len == 0)
{
offset = 0xfff8 + s.getBits(3);
{ u8 t = to[offset]; *to++ = t; }
continue;
}
if (len == 1)
{
u8 code = s.getBits(2);
if (code == 2)
{
u8 b = s.getByte();
if (b >= 0xe0)
{
b <<= 1; ++b; // rlca
b ^= 2; // xor c
if (b == 0xff)
{
++noBits; continue;
}
offset = 0xff00 + b - 0x0f;
{ u8 t = to[offset]; *to++ = t; }
*to++ = s.getByte();
{ u8 t = to[offset]; *to++ = t; }
continue;
}
offset = 0xff00 + b;
}
if (code == 0 || code == 1)
{
offset = s.getByte();
offset += 256 * (code ? 0xfe : 0xfd);
}
if (code == 3)
offset = 0xffe0 + s.getBits(5);
for (u8 i = 0; i < 2; ++i)
{ u8 t = to[offset]; *to++ = t; }
continue;
}
if (len == 3)
{
if (s.getBit())
{
offset = 0xfff0 + s.getBits(4);
{ u8 t = to[offset]; *to++ = t; }
*to++ = s.getByte();
{ u8 t = to[offset]; *to++ = t; }
continue;
}
if (s.getBit())
{
u8 noBytes = 6 + s.getBits(4);
for (u8 i = 0; i < 2 * noBytes; ++i)
*to++ = s.getByte();
continue;
}
len = s.getBits(7);
if (len == 0x0f)
break; // EOF
if (len < 0x0f)
len = 256 * len + s.getByte();
}
if (len == 2)
++len;
u8 code = s.getBits(2);
if (code == 1)
{
u8 b = s.getByte();
if (b >= 0xe0)
{
if (len > 3)
return false;
b <<= 1; ++b; // rlca
b ^= 3; // xor c
offset = 0xff00 + b - 0x0f;
{ u8 t = to[offset]; *to++ = t; }
*to++ = s.getByte();
{ u8 t = to[offset]; *to++ = t; }
continue;
}
offset = 0xff00 + b;
}
if (code == 0)
offset = 0xfe00 + s.getByte();
if (code == 2)
offset = 0xffe0 + s.getBits(5);
if (code == 3)
{
offset = 256 * (mask[noBits] + s.getBits(noBits));
offset += s.getByte();
}
for (u16 i = 0; i < len; ++i)
{ u8 t = to[offset]; *to++ = t; }
}
return to - dst;
}
//------------------- DeMegaLZ --------------------------
class deMLZ
{
private:
u8 *from;
u8 *to;
u8 bitstream;
int bitcount;
public:
deMLZ(u8 *dst, u8 *src)
{
from = src;
to = dst;
}
void init_bitstream(void)
{
bitstream = get_byte();
bitcount = 8;
}
u8 get_byte(void)
{
return *from++;
}
void put_byte(u8 val)
{
*to++ = val;
}
void repeat(u32 disp, int num)
{
for (int i = 0; i < num; i++)
{
u8 val = *(to - disp);
put_byte(val);
}
}
// gets specified number of bits from bitstream
// returns them LSB-aligned
u32 get_bits(int count)
{
u32 bits = 0;
while (count--)
{
if (bitcount--)
{
bits <<= 1;
bits |= 1 & (bitstream >> 7);
bitstream <<= 1;
}
else
{
init_bitstream();
count++; // repeat loop once more
}
}
return bits;
}
int get_bigdisp(void)
{
u32 bits;
// inter displacement
if (get_bits(1))
{
bits = get_bits(4);
return 0x1100 - (bits << 8) - get_byte();
}
// shorter displacement
else
return 256 - get_byte();
}
};
// depacker
void demlz(u8 *dst, u8 *src, int size)
{
deMLZ s(dst, src);
u32 done = 0;
int i;
// get first byte of packed file and write to output
s.put_byte(s.get_byte());
// second byte goes to bitstream
s.init_bitstream();
// actual depacking loop!
do
{
// get 1st bit - either OUTBYTE or beginning of LZ code
// OUTBYTE
if (s.get_bits(1))
s.put_byte(s.get_byte());
// LZ code
else
{
switch (s.get_bits(2))
{
case 0: // 000
s.repeat(8 - s.get_bits(3), 1);
break;
case 1: // 001
s.repeat(256 - s.get_byte(), 2);
break;
case 2: // 010
s.repeat(s.get_bigdisp(), 3);
break;
case 3: // 011
// extract num of length bits
for (i = 1; !s.get_bits(1); i++)
;
// check for exit code
if (i == 9)
done = 1;
else if (i <= 7)
{
// get length bits itself
int bits = s.get_bits(i);
s.repeat(s.get_bigdisp(), 2 + (1 << i) + bits);
}
break;
}
}
} while (!done);
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "main.h"
u16 dehrust(u8 *dst, u8 *src, int size);
void demlz(u8 *dst, u8 *src, int size);
+244
View File
@@ -0,0 +1,244 @@
#include <stdint.h>
#include <string.h>
#include <elf.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "main.h"
#include "esp_spi_defs.h"
#include "mem_obj.h"
#include "elf.cpp.h"
int load_elf(u8 *elf, MEM_OBJ *obj, int opt)
{
enum
{
TYPE_NONE,
TYPE_TEXT,
TYPE_RODATA,
TYPE_DATA,
TYPE_BSS,
};
Elf32_Ehdr* elf_header = (Elf32_Ehdr*)elf;
Elf32_Shdr* section_headers = (Elf32_Shdr*)(elf + elf_header->e_shoff);
#ifdef VERBOSE
Elf32_Shdr* shstrtab_header = &section_headers[elf_header->e_shstrndx];
const char* shstrtab = (const char*)(elf + shstrtab_header->sh_offset);
#endif
u32 entry = 0;
int size = 0;
int num_sec = elf_header->e_shnum;
Elf32_Rela* rld = NULL;
int rld_num = 0;
// Parse sections to determine type and find dynamic relocation section
for (int i = 0; i < num_sec; i++)
{
Elf32_Shdr* section = &section_headers[i];
if (!section->sh_addr) continue;
section->sh_info = TYPE_NONE;
section->sh_link = 0;
switch (section->sh_type)
{
case SHT_RELA:
section->sh_type = TYPE_NONE;
rld = (Elf32_Rela*)&elf[section->sh_offset];
rld_num = section->sh_size / sizeof(Elf32_Rela);
break;
case SHT_NOBITS:
section->sh_type = TYPE_BSS;
break;
case SHT_PROGBITS:
if (section->sh_flags & SHF_EXECINSTR)
{
section->sh_type = TYPE_TEXT;
section->sh_link = 1; // .text is always used if no symbol refere it
}
else if (section->sh_flags & SHF_WRITE)
section->sh_type = TYPE_DATA;
else
section->sh_type = TYPE_RODATA;
break;
default:
section->sh_type = TYPE_NONE;
}
}
// Parse relocation symbols to find used sections
for (int i = 0; i < rld_num; i++)
{
u32 rld_offs = rld[i].r_offset;
u32 *sym_offs = (u32*)&elf[rld_offs];
u32 sym_val = *sym_offs;
for (int j = 0; j < num_sec; j++)
{
Elf32_Shdr* section = &section_headers[j];
if (section->sh_addr)
{
if ((sym_val >= section->sh_addr) && (sym_val <= (section->sh_addr + section->sh_size - 1)))
{
section->sh_link++;
break;
}
}
}
}
// Allocate memory for sections
#ifdef VERBOSE
printf("\nSections used:\n");
#endif
for (int i = 0; i < num_sec; i++)
{
Elf32_Shdr* section = &section_headers[i];
if (!section->sh_addr) continue;
if (!section->sh_link) continue;
section->sh_info = 0;
switch (section->sh_type)
{
case TYPE_TEXT:
section->sh_info = (u32)heap_caps_aligned_alloc(section->sh_addralign, section->sh_size, MALLOC_CAP_INTERNAL);
if (!section->sh_info) goto cleanup;
entry = elf_header->e_entry - section->sh_offset + section->sh_info + 0x6F0000;
obj->text = (void*)section->sh_info;
obj->sz_text = section->sh_size;
obj->entry = (void*)entry;
size += section->sh_size;
break;
case TYPE_BSS:
#ifdef CONFIG_SPIRAM
section->sh_info = (u32)heap_caps_aligned_alloc(section->sh_addralign, section->sh_size, (opt & ESP_OPT_BSS_SRAM) ? MALLOC_CAP_INTERNAL : MALLOC_CAP_SPIRAM);
#else
section->sh_info = (u32)heap_caps_aligned_alloc(section->sh_addralign, section->sh_size, MALLOC_CAP_INTERNAL);
#endif
if (!section->sh_info) goto cleanup;
obj->bss = (void*)section->sh_info;
obj->sz_bss = section->sh_size;
size += section->sh_size;
break;
case TYPE_DATA:
#ifdef CONFIG_SPIRAM
section->sh_info = (u32)heap_caps_aligned_alloc(section->sh_addralign, section->sh_size, (opt & ESP_OPT_DATA_SRAM) ? MALLOC_CAP_INTERNAL : MALLOC_CAP_SPIRAM);
#else
section->sh_info = (u32)heap_caps_aligned_alloc(section->sh_addralign, section->sh_size, MALLOC_CAP_INTERNAL);
#endif
if (!section->sh_info) goto cleanup;
obj->data = (void*)section->sh_info;
obj->sz_data = section->sh_size;
size += section->sh_size;
break;
case TYPE_RODATA:
#ifdef CONFIG_SPIRAM
section->sh_info = (u32)heap_caps_aligned_alloc(section->sh_addralign, section->sh_size, (opt & ESP_OPT_RODATA_SRAM) ? MALLOC_CAP_INTERNAL : MALLOC_CAP_SPIRAM);
#else
section->sh_info = (u32)heap_caps_aligned_alloc(section->sh_addralign, section->sh_size, MALLOC_CAP_INTERNAL);
#endif
if (!section->sh_info) goto cleanup;
obj->rodata = (void*)section->sh_info;
obj->sz_rodata = section->sh_size;
size += section->sh_size;
break;
}
#ifdef VERBOSE
const char* section_name = shstrtab + section->sh_name;
printf("%-10s Addr: %06lX Size: %-5ld Symbols: %-3ld Alloc: %lX Align: %ld\n", section_name, section->sh_addr, section->sh_size, section->sh_link, section->sh_info, section->sh_addralign);
#endif
}
// Relocate dynamic symbols
#ifdef VERBOSE
printf("\nRelocated symbols:\n");
#endif
for (int i = 0; i < rld_num; i++)
{
u32 rld_offs = rld[i].r_offset;
u32 *sym_offs = (u32*)&elf[rld_offs];
u32 sym_val = *sym_offs;
u32 sym_addr = 0;
#ifdef VERBOSE
const char* name = "";
#endif
for (int j = 0; j < num_sec; j++)
{
Elf32_Shdr* section = &section_headers[j];
if (section->sh_addr)
{
if ((sym_val >= section->sh_addr) && (sym_val <= (section->sh_addr + section->sh_size - 1)))
{
sym_addr = sym_val - section->sh_offset + section->sh_info + ((section->sh_type == TYPE_TEXT) ? 0x6F0000 : 0);
*sym_offs = sym_addr;
#ifdef VERBOSE
name = shstrtab + section->sh_name;
#endif
break;
}
}
}
#ifdef VERBOSE
printf("%-4d Offs: %-5lX Value: %-6lX Addr: %lX Section: %s\n", i, rld_offs, sym_val, sym_addr, name);
#endif
}
// Initialize memory for sections
for (int i = 0; i < num_sec; i++)
{
Elf32_Shdr* section = &section_headers[i];
if (!section->sh_addr) continue;
if (!section->sh_link) continue;
switch (section->sh_type)
{
case TYPE_TEXT:
case TYPE_DATA:
case TYPE_RODATA:
if (section->sh_info) memcpy((void*)section->sh_info, &elf[section->sh_offset], section->sh_size);
break;
case TYPE_BSS:
if (section->sh_info) memset((void*)section->sh_info, 0, section->sh_size);
break;
}
}
#ifdef VERBOSE
printf("\nEntry: %lX\n", entry);
#endif
return size;
cleanup:
if (obj->text) free(obj->text);
if (obj->rodata) free(obj->rodata);
if (obj->data) free(obj->data);
if (obj->bss) free(obj->bss);
return -1;
}
+4
View File
@@ -0,0 +1,4 @@
#pragma once
int load_elf(u8 *elf, MEM_OBJ *obj, int opt);
+375
View File
@@ -0,0 +1,375 @@
#pragma once
#define ZIFI_STR_HELPER(x) #x
#define ZIFI_STR(x) ZIFI_STR_HELPER(x)
#define PROD_VER0 0
#define PROD_VER1 6
#define PROD_VER2 4
#define PROD_VER_STRING ZIFI_STR(PROD_VER0) "." ZIFI_STR(PROD_VER1) "." ZIFI_STR(PROD_VER2)
#define CP_STRING "ESP32 SPI WiFi Module, ver." PROD_VER_STRING ", (c) TS-Labs"
#define API_VER 1
#define FEAT_VER 10
#define API_VER_STRING ZIFI_STR(API_VER)
#define FEAT_VER_STRING ZIFI_STR(FEAT_VER)
#define API_STRING "API version: " API_VER_STRING ", feature version: " FEAT_VER_STRING
// Registers
enum
{
ESP_REG_COMMAND = 0x00, // 1 byte
ESP_REG_STATUS = 0x01, // 1 byte
// String commands
ESP_REG_STRING_TYPE = 0x02, // 1 byte
ESP_REG_STRING_SIZE = 0x02, // 1 byte
ESP_REG_STRING_DATA = 0x03, // byte array
// Version commands
ESP_REG_API = 0x02, // 1 byte
ESP_REG_FEAT = 0x03, // 2 bytes
ESP_REG_VER0 = 0x05, // 1 byte
ESP_REG_VER1 = 0x06, // 1 byte
ESP_REG_VER2 = 0x07, // 1 byte
// Object commands and data transmission commands
ESP_REG_OBJ_TYPE = 0x02, // 1 byte
ESP_REG_DATA_SIZE = 0x03, // 4 bytes
ESP_REG_DATA_OFFSET = 0x07, // 4 bytes
ESP_REG_OBJ_HANDLE = 0x0B, // 1 byte
// Lib commands
ESP_REG_FUNC = 0x02, // 1 byte
ESP_REG_OPT = 0x03, // 1 byte
ESP_REG_ARG = 0x03, // 4 bytes
ESP_REG_RETVAL = 0x07, // 4 bytes
ESP_REG_ARR1_HANDLE = 0x0C, // 1 byte
ESP_REG_ARR2_HANDLE = 0x0D, // 1 byte
ESP_REG_ARR3_HANDLE = 0x0E, // 1 byte
ESP_REG_LIB_HANDLE = 0x0F, // 1 byte
// Network commands
ESP_REG_NETSTATE = 0x02,
ESP_REG_IP = 0x2C, // 4 bytes
ESP_REG_OWN_IP = 0x30, // 4 bytes
ESP_REG_MASK = 0x34, // 4 bytes
ESP_REG_GATE = 0x38, // 4 bytes
// Stats commands
ESP_EXEC_TIME = 0x3C, // 4 bytes
// SFX commands
ESP_REG_SFX_CHANNEL = 0x02, // 1 byte
ESP_REG_SFX_GROUP = 0x03, // 1 byte
ESP_REG_SFX_VOLUME = 0x04, // 1 byte
ESP_REG_SFX_PAN = 0x05, // 1 byte
ESP_REG_SFX_PITCH = 0x06, // 2 bytes, Q4.8 little-endian
ESP_REG_SFX_STATE_ACTIVE = 0x10, // 1 byte
ESP_REG_SFX_STATE_HANDLE = 0x11, // 1 byte
ESP_REG_SFX_STATE_GROUP = 0x12, // 1 byte
ESP_REG_SFX_STATE_ORDER = 0x13, // 1 byte
ESP_REG_SFX_STATE_VOLUME = 0x14, // 1 byte
ESP_REG_SFX_STATE_PAN = 0x15, // 1 byte
ESP_REG_SFX_STATE_PITCH = 0x16, // 2 bytes, Q4.8 little-endian
ESP_REG_SFX_STATE_RATE = 0x18, // 4 bytes
ESP_REG_SFX_STATE_FRAMES = 0x1C, // 4 bytes
ESP_REG_SFX_STATE_POSITION = 0x20, // 4 bytes
ESP_REG_SFX_STATE_BITS = 0x24, // 2 bytes
ESP_REG_SFX_STATE_CHANNELS = 0x26, // 1 byte
ESP_REG_SFX_STATE_SIGNED = 0x27, // 1 byte
// Track (XM/MOD/S3M) get-state/get-info commands
ESP_REG_TRACK_STATUS = 0x28, // 1 byte: TRACK_OBJ_ST_STOPPED / TRACK_OBJ_ST_PLAYING
ESP_REG_TRACK_IS_CURRENT = 0x29, // 1 byte: 1 if this handle is the one currently loaded in the player
ESP_REG_TRACK_PATTERN_INDEX = 0x2A, // 1 byte: position in the order/pattern-table list
ESP_REG_TRACK_PATTERN = 0x2B, // 1 byte: pattern number
ESP_REG_TRACK_ROW = 0x2C, // 1 byte: current row within the pattern
ESP_REG_TRACK_SAMPLES = 0x2D, // 4 bytes: samples elapsed since track start
ESP_REG_TRACK_BPM = 0x31, // 2 bytes
ESP_REG_TRACK_TEMPO = 0x33, // 2 bytes
ESP_REG_TRACK_LOOP_COUNT = 0x35, // 1 byte
ESP_REG_TRACK_TOTAL_SAMPLES = 0x36, // 4 bytes: length of one full pass through the song, computed once at load time
ESP_REG_TRACK_MAX_LOOP_COUNT = 0x3A, // 1 byte: see ESP_CMD_TRACK_SET_PARAMS
// VFS bridge commands (SMB2 network share <-> Wild Commander local storage)
ESP_REG_VFS_PENDING = 0x02, // 1 byte: 1 if a request is waiting, else 0
// NOTE: ESP_CMD_VFS_POLL's output also includes ESP_REG_DATA_SIZE (0x03,
// 4 bytes) - VFS_OP must not overlap that range, unlike most register
// reuse in this header, since both are read back from the SAME command.
ESP_REG_VFS_OP = 0x36, // 1 byte: VFS_OP_* (see below) - meaning of the payload from ESP_CMD_VFS_GET_REQUEST_DATA
ESP_REG_VFS_STATUS = 0x02, // 1 byte: i: 0=success, nonzero=error (ESP_CMD_VFS_SUBMIT_RESULT)
};
// Commands
enum
{
ESP_CMD_NOP = 0x00, // (no parameters)
ESP_CMD_GET_INFO_STR = 0x01, // i: ESP_REG_STRING_TYPE
// o: ESP_REG_STRING_SIZE, ESP_REG_STRING_DATA
ESP_CMD_GET_VER = 0x02, // o: ESP_REG_API, ESP_REG_FEAT, ESP_REG_VER0..2
ESP_CMD_GET_CHIP_INFO = 0x03, // +++
ESP_CMD_GET_NETSTATE = 0x11, // o: ESP_REG_NETSTATE
ESP_CMD_WSCAN = 0x12, // (no parameters)
ESP_CMD_SET_AP_NAME = 0x13, // i: ESP_REG_STRING_SIZE, ESP_REG_STRING_DATA
ESP_CMD_SET_AP_PWD = 0x14, // i: ESP_REG_STRING_SIZE, ESP_REG_STRING_DATA
ESP_CMD_AP_CONNECT = 0x16, // (no parameters)
ESP_CMD_AP_DISCONNECT = 0x17, // (no parameters)
ESP_CMD_SET_URL = 0x18, // (no parameters)
ESP_CMD_GET_IP = 0x19, // o: ESP_REG_IP
ESP_CMD_HTTP_GET = 0x20, // +++
ESP_CMD_HTTPS_GET = 0x21, // (no parameters)
// o: ESP_REG_OBJ_HANDLE, ESP_REG_DATA_SIZE
ESP_CMD_GOPHER_GET = 0x22, // (no parameters)
// o: ESP_REG_OBJ_HANDLE, ESP_REG_DATA_SIZE
ESP_CMD_HTTP_STREAM_START = 0x23, // (no parameters)
// o: ESP_REG_DATA_SIZE (total size)
ESP_CMD_HTTPS_STREAM_START = 0x24, // (no parameters)
// o: ESP_REG_DATA_SIZE (total size)
ESP_CMD_GOPHER_STREAM_START = 0x25, // (no parameters)
// o: ESP_REG_DATA_SIZE (total size)
ESP_CMD_STREAM_READ = 0x26, // (no parameters)
// o: ESP_REG_DATA_SIZE (chunk size)
// (DMA read like ESP_CMD_READ_OBJECT)
ESP_CMD_STREAM_CLOSE = 0x27, // (no parameters)
// VFS bridge: a small generic pipe, not one opcode per filesystem
// operation - the actual op (VFS_OP_STAT/READDIR/OPEN/READ/WRITE/CLOSE/
// DELETE/MKDIR/RMDIR) travels inside the request payload, mirroring how
// the fake filesystem's own Vfs_Dispatch works. The Z80 side polls
// ESP_CMD_VFS_POLL on every idle tick; when a request is pending it
// fetches the payload, does the real filesystem op locally through WC,
// and posts the result back.
ESP_CMD_VFS_SERVER_START = 0x28, // i: ESP_REG_STRING_SIZE, ESP_REG_STRING_DATA (share name)
ESP_CMD_VFS_SERVER_STOP = 0x29, // (no parameters)
ESP_CMD_VFS_POLL = 0x2A, // o: ESP_REG_VFS_PENDING, ESP_REG_VFS_OP, ESP_REG_DATA_SIZE (request payload length, if pending)
ESP_CMD_VFS_GET_REQUEST_DATA = 0x2B, // o: ESP_REG_DATA_SIZE (DMA read like ESP_CMD_READ_OBJECT)
ESP_CMD_VFS_SUBMIT_RESULT = 0x2C, // i: ESP_REG_VFS_STATUS, ESP_REG_DATA_SIZE (DMA write like ESP_CMD_WRITE_OBJECT)
ESP_CMD_WEATHER_GET = 0x2D, // i: OBJ_HANDLE -> country\0zip\0 (DATA/DATAF)
// o: new OBJ_HANDLE, DATA_OFFSET=0, DATA_SIZE=90
ESP_CMD_TRACK_INIT = 0xA0, // i: ESP_REG_OBJ_HANDLE
ESP_CMD_TRACK_PLAY = 0xA1, // i: ESP_REG_OBJ_HANDLE
ESP_CMD_TRACK_STOP = 0xA2, // (no parameters)
ESP_CMD_TRACK_RESET = 0xA3, // i: ESP_REG_OBJ_HANDLE, stop playback and reset XMC context to start
ESP_CMD_TRACK_SET_VOLUME = 0xA4, // +++
ESP_CMD_TRACK_SET_S_RATE = 0xA5, // +++
ESP_CMD_TRACK_SET_PARAMS = 0xA6, // i: ESP_REG_OBJ_HANDLE, ESP_REG_TRACK_MAX_LOOP_COUNT
// (0 = loop forever, the default; N = stop playback after N loops)
ESP_CMD_TRACK_SET_POS = 0xA7, // +++
ESP_CMD_TRACK_GET_INFO = 0xA8, // i: ESP_REG_OBJ_HANDLE
// o: ESP_REG_TRACK_STATUS, ESP_REG_TRACK_IS_CURRENT,
// ESP_REG_TRACK_PATTERN_INDEX, ESP_REG_TRACK_PATTERN, ESP_REG_TRACK_ROW,
// ESP_REG_TRACK_SAMPLES, ESP_REG_TRACK_BPM, ESP_REG_TRACK_TEMPO,
// ESP_REG_TRACK_LOOP_COUNT, ESP_REG_TRACK_TOTAL_SAMPLES
// (position/speed reflect the object's own context - live if
// ESP_REG_TRACK_IS_CURRENT is set, otherwise a frozen snapshot from
// when it last played or was reset)
ESP_CMD_TRACK_GET_STATE = 0xA9, // i: ESP_REG_OBJ_HANDLE
// o: ESP_REG_TRACK_STATUS, ESP_REG_TRACK_IS_CURRENT
ESP_CMD_TRACK_GET_STATS = 0xAA, // +++
ESP_CMD_XM_STREAM_LOAD = 0xAB, // i: ESP_REG_DATA_SIZE (tx chunk, aligned), ESP_REG_DATA_OFFSET (total on first chunk, 0 on next chunks)
// o: ESP_REG_DATA_OFFSET (chunk offset), ESP_REG_DATA_SIZE (chunk size), ESP_REG_OBJ_HANDLE
ESP_CMD_MOD_STREAM_LOAD = 0xAC, // i: ESP_REG_DATA_SIZE (tx chunk, aligned), ESP_REG_DATA_OFFSET (total on first chunk, 0 on next chunks)
// o: ESP_REG_DATA_OFFSET (chunk offset), ESP_REG_DATA_SIZE (chunk size), ESP_REG_OBJ_HANDLE
ESP_CMD_S3M_STREAM_LOAD = 0xAD, // i: ESP_REG_DATA_SIZE (tx chunk, aligned), ESP_REG_DATA_OFFSET (total on first chunk, 0 on next chunks)
// o: ESP_REG_DATA_OFFSET (chunk offset), ESP_REG_DATA_SIZE (chunk size), ESP_REG_OBJ_HANDLE
ESP_CMD_SFX_PLAY = 0xB0, // i: ESP_REG_OBJ_HANDLE, ESP_REG_SFX_GROUP; o: ESP_REG_SFX_CHANNEL
ESP_CMD_SFX_PLAY_EX = 0xB1, // i: ESP_REG_OBJ_HANDLE, ESP_REG_SFX_GROUP, ESP_REG_SFX_VOLUME, ESP_REG_SFX_PAN, ESP_REG_SFX_PITCH; o: ESP_REG_SFX_CHANNEL
ESP_CMD_SFX_STOP = 0xB2, // i: ESP_REG_SFX_CHANNEL
ESP_CMD_SFX_SET_PARAMS = 0xB3, // i: ESP_REG_SFX_CHANNEL, ESP_REG_SFX_VOLUME, ESP_REG_SFX_PAN, ESP_REG_SFX_PITCH
ESP_CMD_SFX_SET_VOLUME = 0xB4, // i: ESP_REG_SFX_VOLUME
ESP_CMD_SFX_GET_STATE = 0xB5, // i: ESP_REG_SFX_CHANNEL; o: ESP_REG_SFX_STATE_*
ESP_CMD_SFX_STOP_GROUP = 0xB6, // i: ESP_REG_SFX_GROUP
ESP_CMD_CONFIG_SPI = 0xC0, // +++
ESP_CMD_LOAD_ELF = 0xD0, // i: ESP_REG_OBJ_HANDLE
// o: ESP_REG_LIB_HANDLE
ESP_CMD_LOAD_ELF_OPT = 0xD1, // i: ESP_REG_OBJ_HANDLE, ESP_REG_OPT
// o: ESP_REG_LIB_HANDLE
ESP_CMD_RUN_FUNC0 = 0xD2, // i: ESP_REG_LIB_HANDLE, ESP_REG_FUNC, ESP_REG_ARG,
// o: ESP_REG_RETVAL
ESP_CMD_RUN_FUNC1 = 0xD3, // i: ESP_REG_LIB_HANDLE, ESP_REG_FUNC, ESP_REG_ARG, ESP_REG_ARR1_HANDLE
// o: ESP_REG_RETVAL
ESP_CMD_RUN_FUNC2 = 0xD4, // i: ESP_REG_LIB_HANDLE, ESP_REG_FUNC, ESP_REG_ARG, ESP_REG_ARR1_HANDLE, ESP_REG_ARR2_HANDLE
// o: ESP_REG_RETVAL
ESP_CMD_RUN_FUNC3 = 0xD5, // i: ESP_REG_LIB_HANDLE, ESP_REG_FUNC, ESP_REG_ARG, ESP_REG_ARR1_HANDLE, ESP_REG_ARR2_HANDLE, ESP_REG_ARR3_HANDLE
// o: ESP_REG_RETVAL
ESP_CMD_MAKE_OBJECT = 0xE0, // i: ESP_REG_DATA_SIZE, ESP_REG_OBJ_TYPE
// o: ESP_REG_OBJ_HANDLE, ESP_REG_DATA_OFFSET, ESP_REG_DATA_SIZE
ESP_CMD_WRITE_OBJECT = 0xE1, // i: ESP_REG_OBJ_HANDLE, ESP_REG_DATA_OFFSET, ESP_REG_DATA_SIZE
// o: ESP_REG_DATA_OFFSET, ESP_REG_DATA_SIZE
ESP_CMD_READ_OBJECT = 0xE2, // i: ESP_REG_OBJ_HANDLE, ESP_REG_DATA_OFFSET, ESP_REG_DATA_SIZE
// o: ESP_REG_DATA_OFFSET, ESP_REG_DATA_SIZE
ESP_CMD_DELETE_OBJECT = 0xE3, // i: ESP_REG_OBJ_HANDLE
ESP_CMD_KILL_OBJECTS = 0xE4, // (no parameters)
ESP_CMD_FILL_OBJECT = 0xE5, // +++
ESP_CMD_COPY_OBJECT = 0xE6, // +++
ESP_CMD_GET_OBJECT_INFO = 0xE7, // +++
ESP_CMD_REBOOT = 0xED, // (no parameters)
ESP_CMD_RESET = 0xEE, // (no parameters)
ESP_CMD_BREAK = 0xEF, // abort active host tracker stream state, READY if idle
ESP_CMD_GET_RND = 0xF0, // i: ESP_REG_DATA_SIZE
ESP_CMD_DEHST = 0xF1, // i: ESP_REG_OBJ_HANDLE, ESP_REG_DATA_SIZE, ESP_REG_OBJ_TYPE
// o: ESP_REG_DATA_OFFSET, ESP_REG_DATA_SIZE
ESP_CMD_UNZIP = 0xF2, // i: ESP_REG_OBJ_HANDLE, ESP_REG_DATA_SIZE, ESP_REG_OBJ_TYPE
// o: ESP_REG_DATA_OFFSET, ESP_REG_DATA_SIZE
ESP_CMD_STREAM_UNZIP = 0xF3, // i: ESP_REG_OBJ_TYPE
// o: ESP_REG_OBJ_HANDLE
};
enum
{
ESP_OPT_DATA_SRAM = 0x01,
ESP_OPT_RODATA_SRAM = 0x02,
ESP_OPT_BSS_SRAM = 0x04,
ESP_OPT_SPI_1BIT = 0x00,
ESP_OPT_SPI_2BIT = 0x40,
ESP_OPT_SPI_4BIT = 0x80,
};
enum
{
// Status codes
ESP_ST_IDLE = 0x00, // Idle
ESP_ST_READY = 0x01, // Command completed
ESP_ST_BUSY = 0x02, // Busy executing command or initializing
ESP_ST_DATA_M2S = 0x03, // Ready to receive data from Master to Slave
ESP_ST_DATA_S2M = 0x04, // Ready to send data from Slave to Master
ESP_ST_RESET = 0x7E, // Reset performed, ready
// Error codes
ESP_ERR_INV_COMMAND = 0x80,
ESP_ERR_INV_PARAM ,
ESP_ERR_INV_STATE ,
ESP_ERR_INV_STR_LEN ,
ESP_ERR_INV_SIZE , // 84
ESP_ERR_INV_OBJ_TYPE ,
ESP_ERR_INV_ARR_NUM ,
ESP_ERR_INV_HANDLE ,
ESP_ERR_INV_XM_HANDLE , // 88
ESP_ERR_INV_LIB_HANDLE ,
ESP_ERR_INV_ELF_HANDLE ,
ESP_ERR_INV_HST_HANDLE ,
ESP_ERR_INV_BSS_HANDLE , // 8C
ESP_ERR_INV_ARG_HANDLE ,
ESP_ERR_INV_SRC_HANDLE ,
ESP_ERR_INV_DST_HANDLE ,
ESP_ERR_INV_XM , // 90,
ESP_ERR_INV_LIB ,
ESP_ERR_INV_ELF ,
ESP_ERR_INV_HST ,
ESP_ERR_INV_ZIP , // 94
ESP_ERR_INV_MOD ,
ESP_ERR_INV_MOD_HANDLE ,
ESP_ERR_INV_S3M ,
ESP_ERR_INV_S3M_HANDLE , // 98
ESP_ERR_OUT_OF_MEMORY ,
ESP_ERR_OUT_OF_HANDLES ,
ESP_ERR_OBJ_NOT_DELETED ,
ESP_ERR_AP_NOT_CONNECTED = 0xB0,
ESP_ERR_NET_BUSY ,
ESP_ERR_WEATHER = 0xB5, // weather HTTP/JSON failure; details in log
ESP_ERR_VFS_NO_REQUEST = 0x9C, // ESP_CMD_VFS_GET_REQUEST_DATA/SUBMIT_RESULT with nothing pending
ESP_ERR_VFS_TIMEOUT = 0x9D, // no VFS client waiting for a result (e.g. it gave up)
};
// VFS bridge request opcodes - travel inside the ESP_CMD_VFS_GET_REQUEST_DATA
// payload, not as separate ESP_CMD_* values (see ESP_CMD_VFS_POLL comment).
enum
{
VFS_OP_STAT = 0x40,
VFS_OP_READDIR = 0x41,
VFS_OP_OPEN = 0x42,
VFS_OP_READ = 0x43,
VFS_OP_WRITE = 0x44,
VFS_OP_CLOSE = 0x45,
VFS_OP_DELETE = 0x46,
VFS_OP_MKDIR = 0x47,
VFS_OP_RMDIR = 0x48,
// 2026-09-08: real filesystem-size/free-space query, via Wild Commander
// Improved's FILEX_OP_GET_FS_INFO (needs FILEX.WMF installed) - see
// do_statfs() in smbvfs/src/main.c. No request payload. Result: u32
// total_clusters, u32 free_clusters, u8 sectors_per_cluster, u16
// bytes_per_sector (11 bytes) - status=0 on success. Was previously
// faked as a hardcoded 512MB/32MB in vfs_bridge.cpp, see
// project_smbvfs_plugin.md.
VFS_OP_STATFS = 0x49,
};
// Info type (ESP_CMD_GET_INFO_STR)
enum
{
GET_INFO_COPYRIGHT = 0x00,
GET_INFO_API = 0x01,
GET_INFO_BUILD = 0x02,
GET_INFO_IDF = 0x03,
};
// Memory object types (add new types to mem_obj.cpp)
enum
{
OBJ_TYPE_NONE = 0,
OBJ_TYPE_DATA = 1, // SPIRAM
OBJ_TYPE_DATAF = 2, // SRAM
OBJ_TYPE_ELF = 3, // SPIRAM
OBJ_TYPE_LIB = 4, // Virtual
OBJ_TYPE_XM = 5, // SPIRAM
OBJ_TYPE_WAV = 6, // SPIRAM
OBJ_TYPE_HST = 7, // SPIRAM
OBJ_TYPE_ZIP = 8, // SPIRAM
OBJ_TYPE_XMC = 9, // SPIRAM
OBJ_TYPE_MOD = 10, // SPIRAM
OBJ_TYPE_MDC = 11, // SPIRAM
OBJ_TYPE_S3M = 12, // SPIRAM
OBJ_TYPE_S3C = 13, // SPIRAM
OBJ_TYPE_MBC = 14, // SPIRAM
};
// Network state
enum
{
NETWORK_CLOSED = 0x00,
NETWORK_OPENING = 0x01,
NETWORK_OPEN = 0x02,
NETWORK_CLOSING = 0x03,
NETWORK_UNKNOWN = 0xFF
};
// SPI Slave Commands (ESP32)
enum
{
ESP_SPI_CMD_WR_REGS = 0x01,
ESP_SPI_CMD_RD_REGS = 0x02,
ESP_SPI_CMD_WR_DATA = 0x03,
ESP_SPI_CMD_RD_DATA = 0x04,
ESP_SPI_CMD_W_END = 0x07,
ESP_SPI_CMD_R_END = 0x08,
};
enum
{
OBJ_ST_NONE = 0,
LIB_OBJ_ST_READY = 0x10, // Library loaded
TRACK_OBJ_ST_STOPPED = 0x20, // Tracker object context created, idling
TRACK_OBJ_ST_PLAYING = 0x21, // Tracker object playing
WAV_OBJ_ST_PLAYING = 0x30, // WAV object is used by SFX renderer
OBJ_ST_DELETING = 0xFE, // Object is being deleted, new users must not attach
OBJ_ST_ERROR = 0xFF // Reserved
};
+618
View File
@@ -0,0 +1,618 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include "esp_console.h"
#include "esp_partition.h"
#include "esp_vfs_fat.h"
#include "diskio_impl.h"
#include "diskio_wl.h"
#include "ff.h"
#include "wear_levelling.h"
#include "fatfs.h"
bool fs_initialized = false;
bool fs_fs_mounted = false;
char fs_fs_base_path[32] = { 0 };
char fs_part_label[16] = { 0 };
wl_handle_t fs_wl_handle = WL_INVALID_HANDLE;
FATFS *fs_fs_fatfs = NULL;
BYTE fs_fs_pdrv = FF_DRV_NOT_USED;
bool fs_fs_vfs_registered = false;
bool fs_fs_diskio_registered = false;
bool fs_fs_fat_mounted = false;
bool fs_fs_no_filesystem = false;
FRESULT fs_fs_last_mount_result = FR_OK;
#define FS_VFS_MAX_FILES 4
int fs_build_full_path(const char *base_path, const char *path, char *full, size_t full_size)
{
int n;
if (!base_path || !base_path[0] || !full || full_size < 2) return 0;
if (!path || !path[0] || strcmp(path, "/") == 0)
{
n = snprintf(full, full_size, "%s", base_path);
return n >= 0 && (size_t)n < full_size;
}
if (path[0] == '/')
{
n = snprintf(full, full_size, "%s%s", base_path, path);
return n >= 0 && (size_t)n < full_size;
}
n = snprintf(full, full_size, "%s/%s", base_path, path);
return n >= 0 && (size_t)n < full_size;
}
const esp_partition_t *fs_find_partition()
{
return esp_partition_find_first(
ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_FAT,
FS_PART_LABEL);
}
void fs_clear_mount_state()
{
fs_initialized = false;
fs_fs_mounted = false;
fs_fs_fatfs = NULL;
fs_fs_pdrv = FF_DRV_NOT_USED;
fs_fs_vfs_registered = false;
fs_fs_diskio_registered = false;
fs_fs_fat_mounted = false;
fs_fs_base_path[0] = 0;
fs_part_label[0] = 0;
fs_wl_handle = WL_INVALID_HANDLE;
}
void fs_unmount_force()
{
char drv[3];
if (fs_fs_pdrv != FF_DRV_NOT_USED)
{
drv[0] = (char)('0' + fs_fs_pdrv);
drv[1] = ':';
drv[2] = 0;
if (fs_fs_fat_mounted)
f_mount(NULL, drv, 0);
}
if (fs_fs_diskio_registered && fs_fs_pdrv != FF_DRV_NOT_USED)
{
ff_diskio_unregister(fs_fs_pdrv);
if (fs_wl_handle != WL_INVALID_HANDLE)
ff_diskio_clear_pdrv_wl(fs_wl_handle);
}
if (fs_fs_vfs_registered)
esp_vfs_fat_unregister_path(fs_fs_base_path[0] ? fs_fs_base_path : FS_BASE_PATH);
if (fs_wl_handle != WL_INVALID_HANDLE)
wl_unmount(fs_wl_handle);
fs_clear_mount_state();
}
esp_err_t fs_register_fat(const esp_partition_t *part, const esp_vfs_fat_mount_config_t *mount_cfg)
{
char drv[3];
esp_err_t err;
if (!part || !mount_cfg) return ESP_ERR_INVALID_ARG;
err = wl_mount(part, &fs_wl_handle);
if (err != ESP_OK) return err;
err = ff_diskio_get_drive(&fs_fs_pdrv);
if (err != ESP_OK || fs_fs_pdrv == FF_DRV_NOT_USED)
return ESP_ERR_NO_MEM;
err = ff_diskio_register_wl_partition(fs_fs_pdrv, fs_wl_handle);
if (err != ESP_OK) return err;
fs_fs_diskio_registered = true;
drv[0] = (char)('0' + fs_fs_pdrv);
drv[1] = ':';
drv[2] = 0;
esp_vfs_fat_conf_t fs_fatfs_conf = {
.base_path = FS_BASE_PATH,
.fat_drive = drv,
.max_files = (size_t)mount_cfg->max_files,
};
err = esp_vfs_fat_register(&fs_fatfs_conf, &fs_fs_fatfs);
if (err != ESP_OK) return err;
fs_fs_vfs_registered = true;
fs_fs_last_mount_result = f_mount(fs_fs_fatfs, drv, 1);
if (fs_fs_last_mount_result != FR_OK)
{
fs_fs_no_filesystem = fs_fs_last_mount_result == FR_NO_FILESYSTEM;
return ESP_FAIL;
}
fs_fs_fat_mounted = true;
return ESP_OK;
}
size_t fs_get_allocation_unit_size(size_t part_size)
{
if (part_size >= (16u * 1024u * 1024u)) return 16u * 1024u;
if (part_size >= (4u * 1024u * 1024u)) return 4u * 1024u;
return 4096;
}
void fs_fill_mount_cfg(esp_vfs_fat_mount_config_t *cfg, bool format_if_mount_failed)
{
const esp_partition_t *part = fs_find_partition();
size_t part_size = part ? part->size : 0;
memset(cfg, 0, sizeof(*cfg));
cfg->format_if_mount_failed = format_if_mount_failed;
cfg->max_files = FS_VFS_MAX_FILES;
cfg->allocation_unit_size = fs_get_allocation_unit_size(part_size);
cfg->disk_status_check_enable = false;
cfg->use_one_fat = true;
}
const char *fs_fat_type_name(BYTE fs_type)
{
switch (fs_type)
{
case FS_FAT12: return "FAT12";
case FS_FAT16: return "FAT16";
case FS_FAT32: return "FAT32";
#if defined(FF_FS_EXFAT) && FF_FS_EXFAT
case FS_EXFAT: return "exFAT";
#endif
default: return "unknown";
}
}
uint32_t fs_fat_sector_size(FATFS *fat)
{
if (!fat) return 0;
#if FF_MAX_SS != FF_MIN_SS
return fat->ssize;
#else
return FF_MIN_SS;
#endif
}
FRESULT fs_get_mounted_fatfs(FATFS **out_fat, DWORD *out_free_clusters)
{
if (!out_fat || !out_free_clusters) return FR_INVALID_PARAMETER;
*out_fat = NULL;
*out_free_clusters = 0;
FRESULT res = f_getfree("", out_free_clusters, out_fat);
if (res == FR_OK) return res;
return f_getfree("0:", out_free_clusters, out_fat);
}
void fs_print_fatfs_details(uint64_t total_bytes, uint64_t free_bytes, esp_err_t usage_err)
{
FATFS *fat = NULL;
DWORD free_clusters_fatfs = 0;
FRESULT res = fs_get_mounted_fatfs(&fat, &free_clusters_fatfs);
if (res != FR_OK || !fat)
{
printf(" fatfs : unavailable (FRESULT=%u)\r\n", (unsigned)res);
return;
}
uint32_t sector_size = fs_fat_sector_size(fat);
uint32_t cluster_size = (uint32_t)fat->csize * sector_size;
uint32_t total_clusters = fat->n_fatent >= 2 ? fat->n_fatent - 2 : 0;
uint32_t free_clusters = (uint32_t)free_clusters_fatfs;
if (free_clusters > total_clusters)
free_clusters = total_clusters;
uint32_t used_clusters = total_clusters >= free_clusters ? total_clusters - free_clusters : 0;
uint64_t data_bytes = (uint64_t)total_clusters * cluster_size;
uint64_t fat_bytes = (uint64_t)fat->fsize * sector_size;
printf(" fat type : %s (%u)\r\n", fs_fat_type_name(fat->fs_type), (unsigned)fat->fs_type);
printf(" fat copies : %u\r\n", (unsigned)fat->n_fats);
printf(" sector : %u bytes\r\n", (unsigned)sector_size);
printf(" cluster : %u sectors, %u bytes\r\n", (unsigned)fat->csize, (unsigned)cluster_size);
printf(" clusters : total=%u used=%u free=%u\r\n", (unsigned)total_clusters, (unsigned)used_clusters, (unsigned)free_clusters);
printf(" data area : %llu bytes\r\n", (unsigned long long)data_bytes);
printf(" fat size : %u sectors, %llu bytes each\r\n", (unsigned)fat->fsize, (unsigned long long)fat_bytes);
printf(" root ents : %u\r\n", (unsigned)fat->n_rootdir);
printf(" vol base : %llu\r\n", (unsigned long long)fat->volbase);
printf(" fat base : %llu\r\n", (unsigned long long)fat->fatbase);
printf(" dir base : %llu\r\n", (unsigned long long)fat->dirbase);
printf(" data base : %llu\r\n", (unsigned long long)fat->database);
if (usage_err == ESP_OK && total_bytes != data_bytes)
printf(" vfs total : %llu bytes\r\n", (unsigned long long)total_bytes);
if (usage_err == ESP_OK && cluster_size > 0 && free_bytes != (uint64_t)free_clusters * cluster_size)
printf(" vfs free : %llu bytes\r\n", (unsigned long long)free_bytes);
}
esp_err_t fs_mount_impl(bool quiet)
{
const esp_partition_t *part;
esp_err_t err;
if (fs_fs_mounted)
{
fs_initialized = true;
return ESP_OK;
}
fs_fs_no_filesystem = false;
fs_fs_last_mount_result = FR_OK;
part = fs_find_partition();
if (!part) return ESP_ERR_NOT_FOUND;
esp_vfs_fat_mount_config_t mount_cfg;
fs_fill_mount_cfg(&mount_cfg, false);
err = fs_register_fat(part, &mount_cfg);
if (err != ESP_OK)
{
fs_unmount_force();
if (fs_fs_no_filesystem)
return ESP_ERR_INVALID_STATE;
if (!quiet)
{
if (fs_fs_last_mount_result != FR_OK)
printf("E: FAT f_mount failed: FRESULT=%u\r\n", (unsigned)fs_fs_last_mount_result);
else
printf("E: FAT mount failed: %s\r\n", esp_err_to_name(err));
}
return err;
}
fs_initialized = true;
fs_fs_mounted = true;
fs_fs_no_filesystem = false;
fs_fs_last_mount_result = FR_OK;
snprintf(fs_fs_base_path, sizeof(fs_fs_base_path), "%s", FS_BASE_PATH);
snprintf(fs_part_label, sizeof(fs_part_label), "%s", FS_PART_LABEL);
return ESP_OK;
}
esp_err_t fs_mount()
{
return fs_mount_impl(false);
}
esp_err_t fs_mount_quiet()
{
return fs_mount_impl(true);
}
esp_err_t fs_format()
{
const esp_partition_t *part = fs_find_partition();
if (!part) return ESP_ERR_NOT_FOUND;
if (fs_fs_mounted)
fs_deinit();
esp_vfs_fat_mount_config_t format_cfg;
fs_fill_mount_cfg(&format_cfg, true);
esp_err_t err = esp_vfs_fat_spiflash_format_cfg_rw_wl(
FS_BASE_PATH,
FS_PART_LABEL,
&format_cfg);
if (err != ESP_OK)
{
printf("E: esp_vfs_fat_spiflash_format_cfg_rw_wl failed: %s\r\n", esp_err_to_name(err));
return err;
}
return fs_mount();
}
esp_err_t fs_delete()
{
const esp_partition_t *part = fs_find_partition();
if (!part) return ESP_ERR_NOT_FOUND;
fs_deinit();
esp_err_t err = esp_partition_erase_range(part, 0, part->size);
if (err == ESP_OK)
{
fs_fs_no_filesystem = true;
fs_fs_last_mount_result = FR_NO_FILESYSTEM;
}
return err;
}
void fs_deinit()
{
fs_unmount_force();
fs_fs_no_filesystem = false;
fs_fs_last_mount_result = FR_OK;
}
esp_err_t fs_ensure_ready()
{
return fs_mount();
}
esp_err_t fs_ensure_ready_quiet()
{
return fs_mount_quiet();
}
esp_err_t fs_list_dir(const char *base_path, const char *path)
{
char full[256];
if (!fs_build_full_path(base_path, path, full, sizeof(full)))
return ESP_ERR_INVALID_ARG;
errno = 0;
DIR *d = opendir(full);
if (!d)
{
int saved_errno = errno;
printf("E: opendir('%s') failed, errno=%d\r\n", full, saved_errno);
return ESP_FAIL;
}
printf("Listing: %s\r\n", full);
for (;;)
{
struct dirent *e = readdir(d);
if (!e) break;
if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0)
continue;
char p[256];
int n = snprintf(p, sizeof(p), "%s/%s", full, e->d_name);
if (n < 0 || (size_t)n >= sizeof(p))
{
printf(" ? ? %s (path too long)\r\n", e->d_name);
continue;
}
struct stat st = {};
if (stat(p, &st) == 0)
{
const char *t = S_ISDIR(st.st_mode) ? "DIR " : "FILE";
printf(" %s %10" PRIu32 " %s\r\n", t, (uint32_t)st.st_size, e->d_name);
}
else
{
printf(" ? ? %s\r\n", e->d_name);
}
}
closedir(d);
return ESP_OK;
}
int fs_info(int argc, char **argv)
{
const esp_partition_t *part;
uint64_t total = 0;
uint64_t free = 0;
uint64_t used = 0;
esp_err_t err;
if (argc < 0 || !argv) return 1;
err = fs_ensure_ready_quiet();
part = fs_find_partition();
printf("FAT info\r\n");
printf(" label : %s\r\n", FS_PART_LABEL);
printf(" base path : %s\r\n", FS_BASE_PATH);
if (err != ESP_OK)
{
if (err == ESP_ERR_NOT_FOUND)
{
printf(" state : partition not found\r\n");
return 0;
}
if (fs_fs_no_filesystem)
{
printf(" state : no FAT filesystem\r\n");
if (part)
{
printf(" address : 0x%08" PRIX32 "\r\n", part->address);
printf(" size : 0x%08lx (%lu bytes)\r\n", (unsigned long)part->size, (unsigned long)part->size);
}
return 0;
}
if (fs_fs_last_mount_result != FR_OK)
printf(" state : unavailable (FRESULT=%u)\r\n", (unsigned)fs_fs_last_mount_result);
else
printf(" state : unavailable (%s)\r\n", esp_err_to_name(err));
return 1;
}
err = esp_vfs_fat_info(FS_BASE_PATH, &total, &free);
if (err == ESP_OK)
used = total - free;
printf(" mounted : %s\r\n", fs_fs_mounted ? "yes" : "no");
esp_vfs_fat_mount_config_t cfg;
fs_fill_mount_cfg(&cfg, false);
printf(" cfg alloc : %u bytes\r\n", (unsigned)cfg.allocation_unit_size);
printf(" cfg fats : %u on next format\r\n", cfg.use_one_fat ? 1u : 2u);
printf(" max files : %d\r\n", cfg.max_files);
if (err == ESP_OK)
{
printf(" total : %llu bytes\r\n", (unsigned long long)total);
printf(" used : %llu bytes\r\n", (unsigned long long)used);
printf(" free : %llu bytes\r\n", (unsigned long long)free);
}
else
{
printf(" usage : unavailable (%s)\r\n", esp_err_to_name(err));
}
fs_print_fatfs_details(total, free, err);
if (part)
{
printf(" address : 0x%08" PRIX32 "\r\n", part->address);
printf(" size : 0x%08lx (%lu bytes)\r\n", (unsigned long)part->size, (unsigned long)part->size);
}
else
{
printf(" partition : not found\r\n");
}
if (fs_wl_handle != WL_INVALID_HANDLE)
{
size_t wl_bytes = wl_size(fs_wl_handle);
size_t wl_sector = wl_sector_size(fs_wl_handle);
printf(" wl size : %u bytes\r\n", (unsigned)wl_bytes);
printf(" wl sector : %u bytes\r\n", (unsigned)wl_sector);
printf(" wl handle : %d\r\n", (int)fs_wl_handle);
}
return 0;
}
esp_err_t fs_init()
{
return fs_format();
}
int fs_format_cmd(int argc, char **argv)
{
if (argc < 0 || !argv) return 1;
const esp_partition_t *part = fs_find_partition();
if (!part)
{
printf("E: partition '%s' not found\r\n", FS_PART_LABEL);
return 1;
}
esp_vfs_fat_mount_config_t format_cfg;
fs_fill_mount_cfg(&format_cfg, true);
esp_err_t err = fs_format();
if (err != ESP_OK) return 1;
printf("FAT formatted and mounted: %s -> %s\r\n", FS_PART_LABEL, FS_BASE_PATH);
printf(" address : 0x%08" PRIX32 "\r\n", part->address);
printf(" size : 0x%08lx (%lu bytes)\r\n", (unsigned long)part->size, (unsigned long)part->size);
printf(" alloc unit : %u bytes\r\n", (unsigned)format_cfg.allocation_unit_size);
printf(" fat copies : %u\r\n", format_cfg.use_one_fat ? 1u : 2u);
return 0;
}
int fs_delete_cmd(int argc, char **argv)
{
const esp_partition_t *part;
esp_err_t err;
if (argc < 0 || !argv) return 1;
part = fs_find_partition();
if (!part)
{
printf("E: partition '%s' not found\r\n", FS_PART_LABEL);
return 1;
}
err = fs_delete();
if (err != ESP_OK)
{
printf("E: FAT erase failed: %s\r\n", esp_err_to_name(err));
return 1;
}
printf("FAT erased: %s\r\n", FS_PART_LABEL);
printf(" address : 0x%08" PRIX32 "\r\n", part->address);
printf(" size : 0x%08lx (%lu bytes)\r\n", (unsigned long)part->size, (unsigned long)part->size);
return 0;
}
int fs_ls(int argc, char **argv)
{
const char *path = "/";
if (argc >= 3)
path = argv[2];
esp_err_t err = fs_ensure_ready();
if (err != ESP_OK) return 1;
err = fs_list_dir(FS_BASE_PATH, path);
return (err == ESP_OK) ? 0 : 1;
}
int fs_cmd(int argc, char **argv)
{
if (argc < 2 || !argv[1])
{
printf("Usage:\r\n");
printf(" fat format\r\n");
printf(" fat info\r\n");
printf(" fat del\r\n");
printf(" fat ls [path]\r\n");
return 0;
}
const char *op = argv[1];
if (!strcmp(op, "format"))
return fs_format_cmd(argc, argv);
if (!strcmp(op, "info"))
return fs_info(argc, argv);
if (!strcmp(op, "del"))
return fs_delete_cmd(argc, argv);
if (!strcmp(op, "ls"))
return fs_ls(argc, argv);
printf("Unknown subcommand: %s\r\n", op);
return 1;
}
void fat_console_register_system_commands()
{
const esp_console_cmd_t cmd =
{
.command = "fat",
.help = "FATFS commands: format/info/del/ls",
.hint = NULL,
.func = &fs_cmd,
.argtable = NULL
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "esp_err.h"
#include "wear_levelling.h"
#define FS_PART_LABEL "fat"
#define FS_BASE_PATH "/fs"
extern bool fs_initialized;
extern bool fs_fs_mounted;
extern char fs_fs_base_path[32];
extern char fs_part_label[16];
extern wl_handle_t fs_wl_handle;
esp_err_t fs_mount();
esp_err_t fs_mount_quiet();
esp_err_t fs_format();
esp_err_t fs_delete();
esp_err_t fs_init();
esp_err_t fs_ensure_ready();
esp_err_t fs_ensure_ready_quiet();
void fs_deinit();
esp_err_t fs_list_dir(const char *base_path, const char *path);
int fs_build_full_path(const char *base_path, const char *path, char *full, size_t full_size);
int fs_info(int argc, char **argv);
int fs_format_cmd(int argc, char **argv);
int fs_delete_cmd(int argc, char **argv);
int fs_ls(int argc, char **argv);
int fs_cmd(int argc, char **argv);
void fat_console_register_system_commands();
+174
View File
@@ -0,0 +1,174 @@
const u8 code_866_fnt[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x82, 0xaa, 0x82,
0x82, 0xba, 0x92, 0x7c, 0x7c, 0xfe, 0xd6, 0xfe, 0xfe, 0xc6, 0xee, 0x7c,
0x44, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x10, 0x38, 0x7c, 0xfe,
0x7c, 0x38, 0x10, 0x00, 0x38, 0x38, 0xd6, 0xfe, 0xd6, 0x10, 0x38, 0x00,
0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x10, 0x38, 0x00, 0x00, 0x00, 0x38, 0x7c,
0x7c, 0x38, 0x00, 0x00, 0xfe, 0xfe, 0xc6, 0x82, 0x82, 0xc6, 0xfe, 0xfe,
0x00, 0x00, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0xfe, 0xfe, 0xc6, 0xba,
0xba, 0xc6, 0xfe, 0xfe, 0x00, 0x0e, 0x06, 0x7a, 0xd8, 0x88, 0xd8, 0x70,
0x38, 0x6c, 0x44, 0x6c, 0x38, 0x10, 0x38, 0x10, 0x1e, 0x12, 0x1e, 0x10,
0x10, 0x70, 0xe0, 0x40, 0x3e, 0x22, 0x3e, 0x22, 0x22, 0x2e, 0xe4, 0x40,
0x92, 0x7c, 0x6c, 0xc6, 0x6c, 0x7c, 0x92, 0x00, 0x80, 0xe0, 0xf8, 0xfe,
0xf8, 0xe0, 0x80, 0x00, 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
0x10, 0x38, 0x7c, 0x10, 0x10, 0x7c, 0x38, 0x10, 0x6c, 0x6c, 0x6c, 0x6c,
0x6c, 0x00, 0x6c, 0x00, 0x7e, 0xb6, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x00,
0x3c, 0x60, 0x3c, 0x66, 0x3c, 0x06, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7c, 0x7c, 0x7c, 0x00, 0x10, 0x38, 0x7c, 0x10, 0x7c, 0x38, 0x10, 0x7c,
0x10, 0x38, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x10, 0x10,
0x7c, 0x38, 0x10, 0x00, 0x00, 0x08, 0x04, 0x7e, 0x04, 0x08, 0x00, 0x00,
0x00, 0x10, 0x20, 0x7e, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40,
0x40, 0x7e, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00,
0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0x00, 0xfe, 0x7c, 0x7c, 0x38,
0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00, 0x00, 0x6c, 0x24, 0x48,
0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x7e, 0x24, 0x24, 0x7e, 0x24, 0x00,
0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00, 0x63, 0x66, 0x0c, 0x18,
0x30, 0x66, 0xc6, 0x00, 0x00, 0x18, 0x34, 0x18, 0x2c, 0x66, 0x3a, 0x00,
0x00, 0x18, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x18,
0x18, 0x0c, 0x06, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
0x00, 0x24, 0x18, 0x7e, 0x18, 0x24, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30,
0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00,
0x00, 0x3c, 0x66, 0x6e, 0x76, 0x66, 0x3c, 0x00, 0x00, 0x18, 0x38, 0x18,
0x18, 0x18, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x06, 0x3c, 0x60, 0x7e, 0x00,
0x00, 0x3c, 0x66, 0x0c, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x66, 0x66, 0x66,
0x7e, 0x06, 0x06, 0x00, 0x00, 0x7e, 0x60, 0x7c, 0x06, 0x66, 0x3c, 0x00,
0x00, 0x3c, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x7e, 0x06, 0x06,
0x0c, 0x18, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00,
0x00, 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x3c, 0x00, 0x00, 0x00, 0x30, 0x00,
0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x10, 0x20,
0x06, 0x0c, 0x18, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x3c, 0x00,
0x3c, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
0x00, 0x3c, 0x66, 0x06, 0x1c, 0x00, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x5a,
0x5e, 0x60, 0x3c, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x00,
0x00, 0x7c, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x3c, 0x66, 0x60,
0x60, 0x66, 0x3c, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00,
0x00, 0x7e, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x7e, 0x60, 0x60,
0x7c, 0x60, 0x60, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x3e, 0x00,
0x00, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x18, 0x18,
0x18, 0x18, 0x3c, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x00,
0x00, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x66, 0x00, 0x00, 0x60, 0x60, 0x60,
0x60, 0x60, 0x7e, 0x00, 0x00, 0x82, 0xc6, 0xee, 0xd6, 0xc6, 0xc6, 0x00,
0x00, 0x46, 0x66, 0x76, 0x6e, 0x66, 0x62, 0x00, 0x00, 0x3c, 0x66, 0x66,
0x66, 0x66, 0x3c, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x00,
0x00, 0x3c, 0x66, 0x66, 0x66, 0x6e, 0x3c, 0x06, 0x00, 0x7c, 0x66, 0x66,
0x7c, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00,
0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66,
0x66, 0x66, 0x3c, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x24, 0x18, 0x00,
0x00, 0xc6, 0xc6, 0xc6, 0xd6, 0xee, 0x44, 0x00, 0x00, 0x66, 0x66, 0x3c,
0x66, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x00,
0x00, 0x7e, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x1e, 0x18, 0x18, 0x18,
0x18, 0x18, 0x1e, 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00,
0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 0x00, 0x18, 0x3c, 0x66,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0x18, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06,
0x3e, 0x66, 0x3e, 0x00, 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00,
0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x00, 0x00, 0x06, 0x06, 0x3e,
0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
0x00, 0x1c, 0x36, 0x30, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00, 0x3e, 0x66,
0x66, 0x3e, 0x06, 0x3c, 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x00,
0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x0c, 0x00, 0x0c,
0x0c, 0x0c, 0x0c, 0x38, 0x00, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x00,
0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00, 0xfc, 0xd6,
0xd6, 0xd6, 0xd6, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00,
0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x7c, 0x66,
0x66, 0x7c, 0x60, 0x60, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06,
0x00, 0x00, 0x3c, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x3c, 0x60,
0x3c, 0x06, 0x7c, 0x00, 0x00, 0x30, 0x78, 0x30, 0x30, 0x30, 0x1c, 0x00,
0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x66, 0x66,
0x66, 0x24, 0x18, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xee, 0x44, 0x00,
0x00, 0x00, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x66, 0x66,
0x66, 0x3e, 0x06, 0x3c, 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00,
0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x00, 0x70, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x70, 0x00,
0x00, 0x34, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x99, 0xa1,
0xa1, 0x99, 0x42, 0x3c, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x00,
0x00, 0x7c, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x7c, 0x66, 0x7c,
0x66, 0x66, 0x7c, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00,
0x00, 0x1e, 0x36, 0x66, 0x66, 0x66, 0xff, 0xc3, 0x00, 0x7e, 0x60, 0x78,
0x60, 0x60, 0x7e, 0x00, 0x00, 0xd6, 0xd6, 0x7c, 0x7c, 0xd6, 0xd6, 0x00,
0x00, 0x3c, 0x66, 0x0c, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x62, 0x66, 0x6e,
0x76, 0x66, 0x46, 0x00, 0x04, 0x6a, 0x66, 0x6e, 0x76, 0x66, 0x46, 0x00,
0x00, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x66, 0x00, 0x00, 0x1e, 0x36, 0x66,
0x66, 0x66, 0x66, 0x00, 0x00, 0x82, 0xc6, 0xee, 0xd6, 0xc6, 0xc6, 0x00,
0x00, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66,
0x66, 0x66, 0x3c, 0x00, 0x00, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00,
0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x00, 0x00, 0x3c, 0x66, 0x60,
0x60, 0x66, 0x3c, 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c, 0x00, 0x00, 0x7c, 0xd6, 0xd6,
0xd6, 0x7c, 0x10, 0x00, 0x00, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x66, 0x00,
0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7f, 0x01, 0x00, 0x66, 0x66, 0x66,
0x3e, 0x06, 0x06, 0x00, 0x00, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x00,
0x00, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xff, 0x01, 0x00, 0xe0, 0x60, 0x7c,
0x66, 0x66, 0x7c, 0x00, 0x00, 0xc6, 0xc6, 0xf6, 0xde, 0xde, 0xf6, 0x00,
0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x3c, 0x66, 0x0e,
0x06, 0x66, 0x3c, 0x00, 0x00, 0xce, 0xdb, 0xfb, 0xdb, 0xdb, 0xce, 0x00,
0x00, 0x3e, 0x66, 0x66, 0x3e, 0x66, 0x66, 0x00, 0x00, 0x00, 0x3c, 0x06,
0x3e, 0x66, 0x3e, 0x00, 0x00, 0x3c, 0x60, 0x3c, 0x66, 0x66, 0x3c, 0x00,
0x00, 0x00, 0x7c, 0x66, 0x7c, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x3c, 0x06,
0x3c, 0x60, 0x3e, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x3c, 0x00,
0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, 0x00, 0x00, 0xd6, 0x7c,
0x7c, 0xd6, 0xd6, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x0c, 0x66, 0x3c, 0x00,
0x00, 0x00, 0x62, 0x66, 0x6e, 0x76, 0x66, 0x00, 0x00, 0x04, 0x6a, 0x66,
0x6e, 0x76, 0x66, 0x00, 0x00, 0x00, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x00,
0x00, 0x00, 0x1e, 0x36, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x82, 0xc6,
0xee, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x00,
0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x7e, 0x66,
0x66, 0x66, 0x66, 0x00, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x77, 0xdd, 0x77, 0xdd,
0x77, 0xdd, 0x77, 0xdd, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18,
0xf8, 0x18, 0x18, 0x18, 0x66, 0x66, 0x66, 0xe6, 0x66, 0x66, 0x66, 0x66,
0x00, 0x00, 0x00, 0xfe, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0xf8, 0x18,
0xf8, 0x18, 0x18, 0x18, 0x66, 0x66, 0xe6, 0x06, 0xe6, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0xfe, 0x06,
0xe6, 0x66, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x06, 0xfe, 0x00, 0x00, 0x00,
0x66, 0x66, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0xf8, 0x18,
0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x66, 0x66, 0x66, 0x67,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x60, 0x7f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f, 0x60, 0x67, 0x66, 0x66, 0x66, 0x66, 0x66, 0xe7, 0x00,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xe7, 0x66, 0x66, 0x66,
0x66, 0x66, 0x67, 0x60, 0x67, 0x66, 0x66, 0x66, 0x00, 0x00, 0xff, 0x00,
0xff, 0x00, 0x00, 0x00, 0x66, 0x66, 0xe7, 0x00, 0xe7, 0x66, 0x66, 0x66,
0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0xff, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x7f,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xff, 0x66, 0x66, 0x66, 0x66,
0x18, 0x18, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60,
0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x7e, 0x18,
0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c,
0x00, 0x00, 0x7c, 0xd6, 0xd6, 0x7c, 0x10, 0x10, 0x00, 0x00, 0x66, 0x66,
0x3c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7f, 0x01,
0x00, 0x00, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0xd6, 0xd6,
0xd6, 0xd6, 0xfe, 0x00, 0x00, 0x00, 0xd6, 0xd6, 0xd6, 0xd6, 0xff, 0x01,
0x00, 0x00, 0xe0, 0x60, 0x7c, 0x66, 0x7c, 0x00, 0x00, 0x00, 0xc6, 0xc6,
0xf6, 0xde, 0xf6, 0x00, 0x00, 0x00, 0x60, 0x60, 0x7c, 0x66, 0x7c, 0x00,
0x00, 0x00, 0x3c, 0x66, 0x0e, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xce, 0xdb,
0xfb, 0xdb, 0xce, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x3e, 0x66, 0x66, 0x00,
0x24, 0x7e, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x24, 0x3c, 0x66,
0x7e, 0x60, 0x3c, 0x00, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x00,
0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x00, 0x00, 0x0e, 0x1b, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00,
0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
0x76, 0xdc, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x0e, 0x0e, 0x0c, 0x0c, 0x6c, 0x3c, 0x1c, 0x00,
0x8f, 0xcd, 0xef, 0xfc, 0xdc, 0xcc, 0xcc, 0x00, 0x38, 0x6c, 0x18, 0x30,
0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x00
};
+8408
View File
File diff suppressed because it is too large Load Diff
+584
View File
@@ -0,0 +1,584 @@
#pragma once
#include <stdint.h>
#include "main.h"
// Types
typedef struct
{
u8 f_mul; // PLL multiplier
u8 f_div; // Pixel Clock divisor
u16 h_fporch; // Horizontal front porch size
u16 h_sync; // Horizontal sync size
u16 h_bporch; // Horizontal back porch size
u16 h_visible; // Horizontal visible area size
u16 v_fporch; // Vertical front porch size
u16 v_sync; // Vertical sync size
u16 v_bporch; // Vertical back porch size
u16 v_visible; // Vertical visible area size
} FT_MODE;
/*
| # | visible | Fpll MHz | Fpix MHz | clocks/line | lines/frame | line kHz | frame Hz |
| -- | -------- | -------- | -------- | ----------- | ----------- | -------- | -------- |
| 0 | 640x480 | 48 | 24 | 800 | 524 | 30.000 | 57.252 |
| 1 | 640x480 | 64 | 32 | 832 | 520 | 38.462 | 73.964 |
| 2 | 640x480 | 64 | 32 | 800 | 524 | 40.000 | 76.336 |
| 3 | 800x600 | 40 | 40 | 1056 | 628 | 37.879 | 60.317 |
| 4 | 800x600 | 80 | 40 | 1056 | 628 | 37.879 | 60.317 |
| 5 | 800x600 | 48 | 48 | 1040 | 666 | 46.154 | 69.300 |
| 6 | 800x600 | 56 | 56 | 1048 | 631 | 53.435 | 84.683 |
| 7 | 1024x768 | 64 | 64 | 1344 | 806 | 47.619 | 59.081 |
| 8 | 1024x768 | 72 | 72 | 1328 | 806 | 54.217 | 67.267 |
| 9 | 1024x768 | 80 | 80 | 1312 | 800 | 60.976 | 76.220 |
| 10 | 640x1024 | 56 | 56 | 844 | 1066 | 66.351 | 62.243 |
| 11 | 1280x720 | 72 | 72 | 1650 | 750 | 43.636 | 58.182 |
| 12 | 1280x720 | 72 | 72 | 1600 | 750 | 45.000 | 60.000 |
*/
enum // const FT_MODE ft_modes[] in ft8xx.c
{
FT_MODE_640_480_57 = 0, // 0: 640x480@57Hz (48MHz)
FT_MODE_640_480_74 = 1, // 1: 640x480@74Hz (64MHz)
FT_MODE_640_480_76 = 2, // 2: 640x480@76Hz (64MHz)
FT_MODE_800_600_60 = 3, // 3: 800x600@60Hz (40MHz)
FT_MODE_800_600_60_80MHZ = 4, // 4: 800x600@60Hz (80MHz)
FT_MODE_800_600_69 = 5, // 5: 800x600@69Hz (48MHz)
FT_MODE_800_600_85 = 6, // 6: 800x600@85Hz (56MHz)
FT_MODE_1024_768_59 = 7, // 7: 1024x768@59Hz (64MHz)
FT_MODE_1024_768_67 = 8, // 8: 1024x768@67Hz (72MHz)
FT_MODE_1024_768_76 = 9, // 9: 1024x768@76Hz (80MHz)
FT_MODE_1280_1024_60_HALF = 10, // 10: 1280/2x1024@60Hz (56MHz)
FT_MODE_1280_720_58 = 11, // 11: 1280x720@58Hz (72MHz)
FT_MODE_1280_720_60 = 12, // 12: 1280x720@60Hz (72MHz)
FT_MODE_MAX
};
// Memory addresses
#define FT_RAM_G 0x000000 // Main graphics RAM
#define FT_ROM_CHIPID 0x0C0000 // Chip ID and revision
#define FT_ROM_FONT 0x1E0000 // Fonts
#define FT_ROM_FONT_ADDR 0x2FFFFC // Font table pointer address
#define FT_RAM_DL 0x300000 // Display list RAM
#define FT_RAM_REG 0x302000 // Registers
#define FT_RAM_CMD 0x308000 // Coprocessor command buffer
#define FT_RAM_ERR_REPORT 0x309800 // Error message
// Commands
#define FT_CMD_ACTIVE 0x00 // cc 00 00
#define FT_CMD_STANDBY 0x41 // cc 00 00
#define FT_CMD_SLEEP 0x42 // cc 00 00
#define FT_CMD_PWRDOWN 0x43 // cc 00 00
#define FT_CMD_CLKEXT 0x44 // cc 00 00
#define FT_CMD_CLKINT 0x48 // cc 00 00
#define FT_CMD_PDROMS 0x49 // cc xx 00
#define FT_CMD_CLKSEL 0x61 // cc xx 00 -> [5:0] - mul, [7:6] - PLL range (0 for mul=0..3, 1 for mul=4..5)
#define FT_CMD_RST_PULSE 0x68 // cc 00 00
// ID
#define FT_ID 0x7C
// Registers
#define FT_REG_ID 0x302000
#define FT_REG_FRAMES 0x302004
#define FT_REG_CLOCK 0x302008
#define FT_REG_FREQUENCY 0x30200C
#define FT_REG_RENDERMODE 0x302010
#define FT_REG_SNAPY 0x302014
#define FT_REG_SNAPSHOT 0x302018
#define FT_REG_SNAPFORMAT 0x30201C
#define FT_REG_CPURESET 0x302020
#define FT_REG_TAP_CRC 0x302024
#define FT_REG_TAP_MASK 0x302028
#define FT_REG_HCYCLE 0x30202C
#define FT_REG_HOFFSET 0x302030
#define FT_REG_HSIZE 0x302034
#define FT_REG_HSYNC0 0x302038
#define FT_REG_HSYNC1 0x30203C
#define FT_REG_VCYCLE 0x302040
#define FT_REG_VOFFSET 0x302044
#define FT_REG_VSIZE 0x302048
#define FT_REG_VSYNC0 0x30204C
#define FT_REG_VSYNC1 0x302050
#define FT_REG_DLSWAP 0x302054
#define FT_REG_ROTATE 0x302058
#define FT_REG_OUTBITS 0x30205C
#define FT_REG_DITHER 0x302060
#define FT_REG_SWIZZLE 0x302064
#define FT_REG_CSPREAD 0x302068
#define FT_REG_PCLK_POL 0x30206C
#define FT_REG_PCLK 0x302070
#define FT_REG_TAG_X 0x302074
#define FT_REG_TAG_Y 0x302078
#define FT_REG_TAG 0x30207C
#define FT_REG_VOL_PB 0x302080
#define FT_REG_VOL_SOUND 0x302084
#define FT_REG_SOUND 0x302088
#define FT_REG_PLAY 0x30208C
#define FT_REG_GPIO_DIR 0x302090
#define FT_REG_GPIO 0x302094
#define FT_REG_GPIOX_DIR 0x302098
#define FT_REG_GPIOX 0x30209C
#define FT_REG_INT_FLAGS 0x3020A8
#define FT_REG_INT_EN 0x3020AC
#define FT_REG_INT_MASK 0x3020B0
#define FT_REG_PLAYBACK_START 0x3020B4
#define FT_REG_PLAYBACK_LENGTH 0x3020B8
#define FT_REG_PLAYBACK_READPTR 0x3020BC
#define FT_REG_PLAYBACK_FREQ 0x3020C0
#define FT_REG_PLAYBACK_FORMAT 0x3020C4
#define FT_REG_PLAYBACK_LOOP 0x3020C8
#define FT_REG_PLAYBACK_PLAY 0x3020CC
#define FT_REG_PWM_HZ 0x3020D0
#define FT_REG_PWM_DUTY 0x3020D4
#define FT_REG_MACRO_0 0x3020D8
#define FT_REG_MACRO_1 0x3020DC
#define FT_REG_CMD_READ 0x3020F8
#define FT_REG_CMD_WRITE 0x3020FC
#define FT_REG_CMD_DL 0x302100
#define FT_REG_TOUCH_MODE 0x302104
#define FT_REG_TOUCH_ADC_MODE 0x302108
#define FT_REG_TOUCH_CHARGE 0x30210C
#define FT_REG_TOUCH_SETTLE 0x302110
#define FT_REG_TOUCH_OVERSAMPLE 0x302114
#define FT_REG_TOUCH_RZTHRESH 0x302118
#define FT_REG_TOUCH_RAW_XY 0x30211C
#define FT_REG_TOUCH_RZ 0x302120
#define FT_REG_TOUCH_SCREEN_XY 0x302124
#define FT_REG_TOUCH_TAG_XY 0x302128
#define FT_REG_TOUCH_TAG 0x30212C
#define FT_REG_TOUCH_TAG1_XY 0x302130
#define FT_REG_TOUCH_TAG1 0x302134
#define FT_REG_TOUCH_TAG2_XY 0x302138
#define FT_REG_TOUCH_TAG2 0x30213C
#define FT_REG_TOUCH_TAG3_XY 0x302140
#define FT_REG_TOUCH_TAG3 0x302144
#define FT_REG_TOUCH_TAG4_XY 0x302148
#define FT_REG_TOUCH_TAG4 0x30214C
#define FT_REG_TOUCH_TRANSFORM_A 0x302150
#define FT_REG_TOUCH_TRANSFORM_B 0x302154
#define FT_REG_TOUCH_TRANSFORM_C 0x302158
#define FT_REG_TOUCH_TRANSFORM_D 0x30215C
#define FT_REG_TOUCH_TRANSFORM_E 0x302160
#define FT_REG_TOUCH_TRANSFORM_F 0x302164
#define FT_REG_TOUCH_CONFIG 0x302168
#define FT_REG_CTOUCH_TOUCH4_X 0x30216C
#define FT_REG_BIST_EN 0x302174
#define FT_REG_TRIM 0x302180
#define FT_REG_ANA_COMP 0x302184
#define FT_REG_SPI_WIDTH 0x302188
#define FT_SPI_WIDTH_SINGLE 0
#define FT_SPI_WIDTH_DUAL 1
#define FT_SPI_WIDTH_QUAD 2
#define FT_REG_TOUCH_DIRECT_XY 0x30218C
#define FT_REG_TOUCH_DIRECT_Z1Z2 0x302190
#define FT_REG_DATESTAMP 0x302564
#define FT_REG_CMDB_SPACE 0x302574
#define FT_REG_CMDB_WRITE 0x302578
#define FT_REG_ADAPTIVE_FRAMERATE 0x30257C
#define FT_REG_PLAYBACK_PAUSE 0x3025EC
#define FT_REG_FLASH_STATUS 0x3025F0
#define REG_MEDIAFIFO_READ 0x309014
#define REG_MEDIAFIFO_WRITE 0x309018
#define FT_REG_FLASH_SIZE 0x309024
#define FT_REG_TRACKER 0x309000
#define FT_REG_TRACKER_1 0x309004
#define FT_REG_TRACKER_2 0x309008
#define FT_REG_TRACKER_3 0x30900C
#define FT_REG_TRACKER_4 0x309010
#define FT_REG_MEDIAFIFO_READ 0x309014
#define FT_REG_MEDIAFIFO_WRITE 0x309018
// Co-processor commands
#define FT_CCMD_APPEND 0xFFFFFF1E
#define FT_CCMD_BGCOLOR 0xFFFFFF09
#define FT_CCMD_BITMAP_TRANSFORM 0xFFFFFF21
#define FT_CCMD_BUTTON 0xFFFFFF0D
#define FT_CCMD_CALIBRATE 0xFFFFFF15
#define FT_CCMD_CLOCK 0xFFFFFF14
#define FT_CCMD_COLDSTART 0xFFFFFF32
#define FT_CCMD_CRC 0xFFFFFF03
#define FT_CCMD_CSKETCH 0xFFFFFF35
#define FT_CCMD_DIAL 0xFFFFFF2D
#define FT_CCMD_DLSTART 0xFFFFFF00
#define FT_CCMD_EXECUTE 0xFFFFFF07
#define FT_CCMD_FGCOLOR 0xFFFFFF0A
#define FT_CCMD_FLASHATTACH 0xFFFFFF49
#define FT_CCMD_FLASHDETACH 0xFFFFFF48
#define FT_CCMD_FLASHERASE 0xFFFFFF44
#define FT_CCMD_FLASHFAST 0xFFFFFF4A
#define FT_CCMD_FLASHRX 0xFFFFFF4D
#define FT_CCMD_FLASHSOURCE 0xFFFFFF4E
#define FT_CCMD_FLASHSPIDESEL 0xFFFFFF4B
#define FT_CCMD_FLASHTX 0xFFFFFF4C
#define FT_CCMD_FLASHUPDATE 0xFFFFFF47
#define FT_CCMD_GAUGE 0xFFFFFF13
#define FT_CCMD_GETMATRIX 0xFFFFFF33
#define FT_CCMD_GETPOINT 0xFFFFFF08
#define FT_CCMD_GETPROPS 0xFFFFFF25
#define FT_CCMD_GETPTR 0xFFFFFF23
#define FT_CCMD_GRADCOLOR 0xFFFFFF34
#define FT_CCMD_GRADIENT 0xFFFFFF0B
#define FT_CCMD_HAMMERAUX 0xFFFFFF04
#define FT_CCMD_IDCT_DELETED 0xFFFFFF06
#define FT_CCMD_INFLATE 0xFFFFFF22
#define FT_CCMD_INTERRUPT 0xFFFFFF02
#define FT_CCMD_INT_RAMSHARED 0xFFFFFF3D
#define FT_CCMD_INT_SWLOADIMAGE 0xFFFFFF3E
#define FT_CCMD_KEYS 0xFFFFFF0E
#define FT_CCMD_LOADIDENTITY 0xFFFFFF26
#define FT_CCMD_LOADIMAGE 0xFFFFFF24
#define FT_CCMD_LOGO 0xFFFFFF31
#define FT_CCMD_MARCH 0xFFFFFF05
#define FT_CCMD_MEDIAFIFO 0xFFFFFF39
#define FT_CCMD_MEMCPY 0xFFFFFF1D
#define FT_CCMD_MEMCRC 0xFFFFFF18
#define FT_CCMD_MEMSET 0xFFFFFF1B
#define FT_CCMD_MEMWRITE 0xFFFFFF1A
#define FT_CCMD_MEMZERO 0xFFFFFF1C
#define FT_CCMD_NUMBER 0xFFFFFF2E
#define FT_CCMD_PLAYVIDEO 0xFFFFFF3A
#define FT_CCMD_PROGRESS 0xFFFFFF0F
#define FT_CCMD_REGREAD 0xFFFFFF19
#define FT_CCMD_ROMFONT 0xFFFFFF3F
#define FT_CCMD_ROTATE 0xFFFFFF29
#define FT_CCMD_SCALE 0xFFFFFF28
#define FT_CCMD_SCREENSAVER 0xFFFFFF2F
#define FT_CCMD_SCROLLBAR 0xFFFFFF11
#define FT_CCMD_SETBASE 0xFFFFFF38
#define FT_CCMD_SETBITMAP 0xFFFFFF43
#define FT_CCMD_SETFONT 0xFFFFFF2B
#define FT_CCMD_SETFONT2 0xFFFFFF3B
#define FT_CCMD_SETMATRIX 0xFFFFFF2A
#define FT_CCMD_SETROTATE 0xFFFFFF36
#define FT_CCMD_SETSCRATCH 0xFFFFFF3C
#define FT_CCMD_SKETCH 0xFFFFFF30
#define FT_CCMD_SLIDER 0xFFFFFF10
#define FT_CCMD_SNAPSHOT 0xFFFFFF1F
#define FT_CCMD_SNAPSHOT2 0xFFFFFF37
#define FT_CCMD_SPINNER 0xFFFFFF16
#define FT_CCMD_STOP 0xFFFFFF17
#define FT_CCMD_SWAP 0xFFFFFF01
#define FT_CCMD_SYNC 0xFFFFFF42
#define FT_CCMD_TEXT 0xFFFFFF0C
#define FT_CCMD_TOGGLE 0xFFFFFF12
#define FT_CCMD_TOUCH_TRANSFORM 0xFFFFFF20
#define FT_CCMD_TRACK 0xFFFFFF2C
#define FT_CCMD_TRANSLATE 0xFFFFFF27
#define FT_CCMD_VIDEOFRAME 0xFFFFFF41
#define FT_CCMD_VIDEOSTART 0xFFFFFF40
#define FT_OPT_CENTER 1536UL
#define FT_OPT_CENTERX 512UL
#define FT_OPT_CENTERY 1024UL
#define FT_OPT_FLAT 256UL
#define FT_OPT_MONO 1UL
#define FT_OPT_NOBACK 4096UL
#define FT_OPT_NODL 2UL
#define FT_OPT_NOHANDS 49152UL
#define FT_OPT_NOHM 16384UL
#define FT_OPT_NOPOINTER 16384UL
#define FT_OPT_NOSECS 32768UL
#define FT_OPT_NOTICKS 8192UL
#define FT_OPT_RIGHTX 2048UL
#define FT_OPT_SIGNED 256UL
#define OPT_NOTEAR 4UL
#define OPT_FULLSCREEN 8UL
#define OPT_MEDIAFIFO 16UL
#define OPT_SOUND 32UL
// Primitives
#define FT_BITMAPS 1
#define FT_POINTS 2
#define FT_LINES 3
#define FT_LINE_STRIP 4
#define FT_EDGE_STRIP_R 5
#define FT_EDGE_STRIP_L 6
#define FT_EDGE_STRIP_A 7
#define FT_EDGE_STRIP_B 8
#define FT_RECTS 9
// Formats
#define FT_ARGB1555 0
#define FT_L1 1
#define FT_L4 2
#define FT_L8 3
#define FT_RGB332 4
#define FT_ARGB2 5
#define FT_ARGB4 6
#define FT_RGB565 7
#define FT_TEXT8X8 9
#define FT_TEXTVGA 10
#define FT_BARGRAPH 11
#define FT_PALETTED565 14
#define FT_PALETTED4444 15
#define FT_PALETTED8 16
#define FT_L2 17
// Blend functions
#define FT_ZERO 0
#define FT_ONE 1
#define FT_SRC_ALPHA 2
#define FT_DST_ALPHA 3
#define FT_ONE_MINUS_SRC_ALPHA 4
#define FT_ONE_MINUS_DST_ALPHA 5
// Stencil test functions
#define FT_NEVER 0
#define FT_LESS 1
#define FT_LEQUAL 2
#define FT_GREATER 3
#define FT_GEQUAL 4
#define FT_EQUAL 5
#define FT_NOTEQUAL 6
#define FT_ALWAYS 7
// Stencil operations
#define FT_KEEP 1
#define FT_REPLACE 2
#define FT_INCR 3
#define FT_DECR 4
#define FT_INVERT 5
#define FT_INCR_WRAP 6 // undocumented???
#define FT_DECR_WRAP 7 // undocumented???
// Bitmap wrap mode
#define FT_REPEAT 1
#define FT_BORDER 0
// Bitmap filtering mode
#define FT_NEAREST 0
#define FT_BILINEAR 1
// DL_SWAP modes
#define FT_DLSWAP_DONE 0
#define FT_DLSWAP_LINE 1
#define FT_DLSWAP_FRAME 2
// INT sources
#define FT_INT_SWAP 1
#define FT_INT_TOUCH 2
#define FT_INT_TAG 4
#define FT_INT_SOUND 8
#define FT_INT_PLAYBACK 16
#define FT_INT_CMDEMPTY 32
#define FT_INT_CMDFLAG 64
#define FT_INT_CONVCOMPLETE 128
// Playback formats
#define FT_LINEAR_SAMPLES 0
#define FT_ULAW_SAMPLES 1
#define FT_ADPCM_SAMPLES 2
// Touch ADC modes
#define FT_ADC_SINGLE_ENDED 0
#define FT_ADC_DIFFERENTIAL 1
// Touch modes
#define FT_TOUCHMODE_OFF 0
#define FT_TOUCHMODE_ONESHOT 1
#define FT_TOUCHMODE_FRAME 2
#define FT_TOUCHMODE_CONTINUOUS 3
// Constants
#define FT_DL_SIZE 8192 // 8KB Display List buffer size
#define FT_CMD_FIFO_SIZE 4096 // 4KB coprocessor Fifo size
#define FT_CMD_SIZE 4 // 4 byte per coprocessor command of EVE
#define FT_GPU_NUMCHAR_PERFONT 128
#define FT_GPU_FONT_TABLE_SIZE 148
// Co-processor
void ft_Append(u32 ptr, u32 num);
void ft_BgColor(u32 c);
void ft_BitmapTransform(i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, i32 tx0, i32 ty0, i32 tx1, i32 ty1, i32 tx2, i32 ty2, u16 result);
void ft_Button(i16 x, i16 y, i16 w, i16 h, i16 font, u16 options, const char *s);
void ft_Calibrate(u32 result);
void ft_Clock(i16 x, i16 y, i16 r, u16 options, u16 h, u16 m, u16 s, u16 ms);
void ft_ColdStart();
void ft_Dial(i16 x, i16 y, i16 r, u16 options, u16 val);
void ft_Dlstart();
void ft_FgColor(u32 c);
void ft_FlashAttach();
void ft_FlashDetach();
void ft_FlashErase();
void ft_FlashFast(u32);
void ft_FlashRx(u32, u32);
void ft_FlashSpiDesel();
void ft_FlashTx(u32);
void ft_FlashUpdate(u32, u32, u32);
void ft_Gauge(i16 x, i16 y, i16 r, u16 options, u16 major, u16 minor, u16 val, u16 range);
void ft_GetMatrix(i32 a, i32 b, i32 c, i32 d, i32 e, i32 f);
void ft_GetProps(u32 ptr, u32 w, u32 h);
void ft_GetPtr(u32 result);
void ft_GradColor(u32 c);
void ft_Gradient(i16 x0, i16 y0, u32 rgb0, i16 x1, i16 y1, u32 rgb1);
void ft_Inflate(u32 ptr);
void ft_Int_RAMShared(u32 ptr);
void ft_Int_SWLoadImage(u32 ptr, u32 options);
void ft_Interrupt(u32 ms);
void ft_Keys(i16 x, i16 y, i16 w, i16 h, i16 font, u16 options, const char *s);
void ft_LoadIdentity();
void ft_LoadImage(u32 ptr, u32 options);
void ft_Logo();
void ft_MediaFifo(u32 ptr, u32 size);
void ft_MemCrc(u32 ptr, u32 num, u32 result);
void ft_MemSet(u32 ptr, u32 value, u32 num);
void ft_MemWrite(u32 ptr, u32 num);
void ft_MemZero(u32 ptr, u32 num);
void ft_Memcpy(u32 dest, u32 src, u32 num);
void ft_Number(i16 x, i16 y, i16 font, u16 options, i32 n);
void ft_PlayVideo(u32 options);
void ft_Progress(i16 x, i16 y, i16 w, i16 h, u16 options, u16 val, u16 range);
void ft_RegRead(u32 ptr, u32 result);
void ft_RomFont(u32 font, u32 romslot);
void ft_Rotate(i32 a);
void ft_Scale(i32 sx, i32 sy);
void ft_ScreenSaver();
void ft_Scrollbar(i16 x, i16 y, i16 w, i16 h, u16 options, u16 val, u16 size, u16 range);
void ft_SetBase(u32 base);
void ft_SetBitmap(u32 source, u16 fmt, u16 w, u16 h);
void ft_SetFont(u32 font, u32 ptr);
void ft_SetFont2(u32 font, u32 ptr, u32 firstchar);
void ft_SetMatrix();
void ft_SetRotate(u32 r);
void ft_SetScratch(u32 handle);
void ft_Sketch(i16 x, i16 y, u16 w, u16 h, u32 ptr, u16 format);
void ft_Slider(i16 x, i16 y, i16 w, i16 h, u16 options, u16 val, u16 range);
void ft_Snapshot(u32 ptr);
void ft_Snapshot2(u32 fmt, u32 ptr, i16 x, i16 y, i16 w, i16 h);
void ft_Spinner(i16 x, i16 y, u16 style, u16 scale);
void ft_Stop();
void ft_Swap();
void ft_Sync();
void ft_Text(i16 x, i16 y, i16 font, u16 options, const char *s);
void ft_Toggle(i16 x, i16 y, i16 w, i16 font, u16 options, u16 state, const char *s);
void ft_TouchTransform(i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, i32 tx0, i32 ty0, i32 tx1, i32 ty1, i32 tx2, i32 ty2, u16 result);
void ft_Track(i16 x, i16 y, i16 w, i16 h, i16 tag);
void ft_Translate(i32 tx, i32 ty);
void ft_VideoFrame(u32 dst, u32 ptr);
void ft_VideoStart();
// Display list
void ft_AlphaFunc(u8 func, u8 ref);
void ft_Begin(u8 prim);
void ft_BitmapHandle(u8 handle);
void ft_BitmapLayout(u8 format, u16 linestride, u16 height);
void ft_BitmapSize(u8 filter, u8 wrapx, u8 wrapy, u16 width, u16 height);
void ft_BitmapSource(u32 addr);
void ft_BitmapTransformA(i32 a);
void ft_BitmapTransformB(i32 b);
void ft_BitmapTransformC(i32 c);
void ft_BitmapTransformD(i32 d);
void ft_BitmapTransformE(i32 e);
void ft_BitmapTransformF(i32 f);
void ft_BlendFunc(u8 src, u8 dst);
void ft_Call(u16 dest);
void ft_Cell(u8 cell);
void ft_Clear(u8 c, u8 s, u8 t);
void ft_ClearAll();
void ft_ClearColorA(u8 alpha);
void ft_ClearColorRGB(u8 red, u8 green, u8 blue);
void ft_ClearColorRGB32(u32 rgb);
void ft_ClearStencil(u8 s);
void ft_ClearTag(u8 s);
void ft_ColorA(u8 alpha);
void ft_ColorMask(u8 r, u8 g, u8 b, u8 a);
void ft_ColorRGB(u8 red, u8 green, u8 blue);
void ft_ColorRGB32(u32 rgb);
void ft_Display();
void ft_End();
void ft_Jump(u16 dest);
void ft_LineWidth(u16 width);
void ft_Macro(u8 m);
void ft_PaletteSource(u32 addr);
void ft_PointSize(u16 size);
void ft_RestoreContext();
void ft_Return();
void ft_SaveContext();
void ft_ScissorSize(u16 width, u16 height);
void ft_ScissorXY(u16 x, u16 y);
void ft_StencilFunc(u8 func, u8 ref, u8 mask);
void ft_StencilMask(u8 mask);
void ft_StencilOp(u8 sfail, u8 spass);
void ft_Tag(u8 s);
void ft_TagMask(u8 mask);
void ft_Vertex2f(i16 x, i16 y);
void ft_Vertex2ii(u16 x, u16 y, u8 handle, u8 cell);
void ft_VertexFormat(u8 f);
void ft_VertexTranslateX(i32 v);
void ft_VertexTranslateY(i32 v);
// Common
void init_ft8xx();
// Transport / mode switch
esp_err_t ft_host_begin();
esp_err_t ft_host_end();
// Command buffer helpers
void ft_ccmd_start(void *addr);
void ft_ccmd(u32 a);
void ft_cstr(const char *s);
esp_err_t ft_ccmd_write();
// RAM / register access
u8 ft_rreg8(u32 a);
u16 ft_rreg16(u32 a);
u32 ft_rreg32(u32 a);
void ft_wreg8(u32 a, u8 v);
void ft_wreg16(u32 a, u16 v);
void ft_wreg32(u32 a, u32 v);
esp_err_t ft_open_session();
esp_err_t ft_close_session();
esp_err_t ft_read(void *addr, u32 ft_addr, u32 size);
esp_err_t ft_write(const void *addr, u32 ft_addr, u32 size);
esp_err_t ft_write_dl(const void *addr, u32 size_dwords);
// Host commands
esp_err_t ft_cmd(u8 a);
esp_err_t ft_cmdp(u8 a, u8 v);
// Coprocessor helpers
esp_err_t ft_cp_wait(uint32_t timeout_ms);
esp_err_t ft_cp_reset();
// Display / init
esp_err_t ft_set_mode(u8 mode);
void ft_swap();
esp_err_t ft_wait_swap(uint32_t timeout_ms);
extern const FT_MODE ft_modes[];
// Math
i16 rcos(i16 r, u16 th);
i16 rsin(i16 r, u16 th);
extern const u16 sintab[];
extern u32 *ft_ccmdb;
extern u16 ft_ccmdp;
// Text attribute display backend
void ft_text_attr_mode_get_size(uint16_t *cols, uint16_t *rows);
esp_err_t ft_text_attr_mode_begin();
esp_err_t ft_text_attr_mode_show();
esp_err_t ft_text_attr_mode_clear(u8 ch, u8 attr);
esp_err_t ft_text_attr_mode_present(const u8 *chars, const u8 *attrs);
void ft_text_attr_mode_end();
// JPEG / RGB888 and DXP viewer helpers
int ft_jpg_ext_match(const char *name);
int ft_dxp_ext_match(const char *name);
esp_err_t ft_jpg_show_file_mode(const char *path, bool stretch, bool quiet = false);
esp_err_t ft_jpg_show_file(const char *path);
esp_err_t ft_dxp_show_file_mode(const char *path, bool quiet = false);
esp_err_t ft_dxp_show_file(const char *path);
esp_err_t ft_rgb888_640x480_show_current(bool stretch);
// Console
void ft_console_register_system_commands();
+252
View File
@@ -0,0 +1,252 @@
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "main.h"
#include "esp_spi_defs.h"
#include "spi_slave.h"
#include "mem_obj.h"
#include "helper.h"
#include "gopher_client.h"
static const char *TAG = "gopher";
#define GOPHER_BUF_SIZE (2 * 1024 * 1024)
static uint8_t *gopher_buf = NULL;
static size_t gopher_buf_size = 0;
static int gopher_sock = -1;
void gopher_buf_free()
{
if (!gopher_buf) return;
heap_caps_free(gopher_buf);
gopher_buf = NULL;
gopher_buf_size = 0;
ESP_LOGI(TAG, "buffer freed");
}
bool gopher_buf_alloc()
{
if (gopher_buf) return true;
gopher_buf = (uint8_t*)heap_caps_malloc(GOPHER_BUF_SIZE, MALLOC_CAP_SPIRAM);
if (!gopher_buf)
{
ESP_LOGW(TAG, "SPIRAM buffer alloc failed, size=%u", (unsigned)GOPHER_BUF_SIZE);
return false;
}
gopher_buf_size = GOPHER_BUF_SIZE;
ESP_LOGI(TAG, "buffer allocated in SPIRAM: %p, size=%u", gopher_buf, (unsigned)gopher_buf_size);
return true;
}
void gopher_close_socket()
{
if (gopher_sock < 0) return;
close(gopher_sock);
gopher_sock = -1;
}
void gopher_cleanup()
{
gopher_close_socket();
gopher_buf_free();
}
void gopher_init()
{
gopher_buf = NULL;
gopher_buf_size = 0;
gopher_sock = -1;
ESP_LOGI(TAG, "init done");
}
void gopher_do_get()
{
ESP_LOGI(TAG, "GOPHER_GET");
if (!gopher_buf_alloc())
{
wr_reg8(ESP_REG_STATUS, 0xA0);
return;
}
if (!net.url[0])
{
ESP_LOGW(TAG, "URL not set");
gopher_cleanup();
wr_reg8(ESP_REG_STATUS, 0x81);
return;
}
char * url = (char*)net.url;
if (strncmp(url, "gopher://", 9) != 0)
{
ESP_LOGW(TAG, "invalid URL format");
gopher_cleanup();
wr_reg8(ESP_REG_STATUS, 0x81);
return;
}
ESP_LOGI(TAG, "GET %s", url);
char host[256];
int port = 70;
char selector[256];
memset(host, 0, sizeof(host));
memset(selector, 0, sizeof(selector));
selector[0] = '\0';
const char * p = url + 9;
const char * slash = strchr(p, '/');
const char * colon = strchr(p, ':');
if (colon && (!slash || colon < slash))
{
int host_len = colon - p;
strncpy(host, p, host_len);
host[host_len] = 0;
port = atoi(colon + 1);
if (slash)
{
const char * sel_start = slash + 1;
if (sel_start[0] && sel_start[1] == '/' && strchr("0123456789+gIihs", sel_start[0]))
sel_start += 2;
strncpy(selector, sel_start, sizeof(selector) - 1);
selector[sizeof(selector) - 1] = 0;
}
}
else if (slash)
{
int host_len = slash - p;
strncpy(host, p, host_len);
host[host_len] = 0;
const char * sel_start = slash + 1;
if (sel_start[0] && sel_start[1] == '/' && strchr("0123456789+gIihs", sel_start[0]))
sel_start += 2;
strncpy(selector, sel_start, sizeof(selector) - 1);
selector[sizeof(selector) - 1] = 0;
}
else
{
strncpy(host, p, sizeof(host) - 1);
host[sizeof(host) - 1] = 0;
}
ESP_LOGI(TAG, "host=%s port=%d selector=%s", host, port, selector);
struct hostent * server = gethostbyname(host);
if (!server)
{
ESP_LOGW(TAG, "DNS lookup failed");
gopher_cleanup();
wr_reg8(ESP_REG_STATUS, 0xB1);
return;
}
gopher_sock = socket(AF_INET, SOCK_STREAM, 0);
if (gopher_sock < 0)
{
ESP_LOGW(TAG, "socket creation failed");
gopher_cleanup();
wr_reg8(ESP_REG_STATUS, 0xB2);
return;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
if (connect(gopher_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
ESP_LOGW(TAG, "connection failed");
gopher_cleanup();
wr_reg8(ESP_REG_STATUS, 0xB2);
return;
}
ESP_LOGI(TAG, "connected");
char request[512];
memset(request, 0, sizeof(request));
snprintf(request, sizeof(request), "/%s\r\n", selector);
int sent = write(gopher_sock, request, strlen(request));
if (sent < 0)
{
ESP_LOGW(TAG, "send failed");
gopher_cleanup();
wr_reg8(ESP_REG_STATUS, 0xB2);
return;
}
ESP_LOGI(TAG, "sent %d bytes", sent);
int total = 0;
int len;
int last_progress = 0;
while ((len = read(gopher_sock, (char*)gopher_buf + total, gopher_buf_size - total)) > 0)
{
total += len;
if ((size_t)total >= gopher_buf_size) break;
if (total - last_progress >= 65536)
{
wr_reg32(ESP_REG_DATA_SIZE, total);
last_progress = total;
}
vTaskDelay(1);
}
gopher_close_socket();
ESP_LOGI(TAG, "recv %d bytes total", total);
if (total == 0)
{
ESP_LOGW(TAG, "empty response");
gopher_buf_free();
wr_reg8(ESP_REG_STATUS, 0xB4);
return;
}
int resp = make_obj(total, OBJ_TYPE_DATA);
if (resp < 0)
{
ESP_LOGW(TAG, "make_obj failed");
gopher_buf_free();
wr_reg8(ESP_REG_STATUS, 0xA0);
return;
}
memcpy(mem_obj[resp].addr, gopher_buf, total);
gopher_buf_free();
wr_reg8(ESP_REG_OBJ_HANDLE, resp);
wr_reg32(ESP_REG_DATA_SIZE, total);
wr_reg8(ESP_REG_STATUS, ESP_ST_READY);
ESP_LOGI(TAG, "done: handle=%d size=%d", resp, total);
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
void gopher_init(void);
void gopher_do_get(void);
+806
View File
@@ -0,0 +1,806 @@
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "main.h"
#include "esp_spi_defs.h"
#include "miniz.h"
#include "spi_slave.h"
#include "mem_obj.h"
#include "elf.cpp.h"
#include "depack.h"
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
#include "wifi.h"
#include "http_client.h"
#include "weather.h"
#include "gopher_client.h"
#include "stream_client.h"
#endif
#include "helper.h"
#include "tracker.h"
#include "sfx.h"
#include "vfs_bridge.h"
const char TAG[] = "helper";
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
EXT_RAM_BSS_ATTR u8 url_buf[1024];
#endif
QueueHandle_t helper_queue;
NET net;
#include <stdio.h>
#include <string.h>
#include "miniz.h"
#include "esp_log.h"
void IRAM_ATTR put_helper_isr(int task)
{
xQueueSendFromISR(helper_queue, &task, 0);
portYIELD_FROM_ISR();
}
void *f_malloc(size_t x)
{
#ifdef VERBOSE
printf("f_malloc %u\r\n", x);
#endif
return malloc_spiram(x);
}
void f_free(void *p)
{
#ifdef VERBOSE
printf("f_free\r\n");
#endif
free(p);
}
void *f_realloc(void *p, size_t x)
{
#ifdef VERBOSE
printf("f_realloc %u\r\n", x);
#endif
auto m = malloc_spiram(x);
memcpy(m, p, x);
free(p);
return m;
}
u8 sfx_status_from_err(esp_err_t err)
{
switch (err)
{
case ESP_OK:
return ESP_ST_READY;
case ESP_ERR_INVALID_ARG:
return ESP_ERR_INV_PARAM;
case ESP_ERR_INVALID_SIZE:
return ESP_ERR_INV_SIZE;
case ESP_ERR_NOT_FOUND:
return ESP_ERR_INV_STATE;
case ESP_ERR_NOT_SUPPORTED:
case ESP_ERR_INVALID_RESPONSE:
return ESP_ERR_INV_PARAM;
case ESP_ERR_NO_MEM:
return ESP_ERR_OUT_OF_HANDLES;
default:
return ESP_ERR_INV_STATE;
}
}
void helper_task(void *arg)
{
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
net.url = url_buf;
memset(net.url, 0, 1024);
#endif
while (1)
{
int task;
if (xQueueReceive(helper_queue, &task, portMAX_DELAY))
{
switch (task)
{
case TASK_RUN_FUNC0:
case TASK_RUN_FUNC1:
case TASK_RUN_FUNC2:
case TASK_RUN_FUNC3:
{
int handle = rd_reg8(ESP_REG_LIB_HANDLE);
int func = rd_reg8(ESP_REG_FUNC);
int arg = rd_reg32(ESP_REG_ARG);
if (!check_handle(handle) || (mem_obj[handle].type != OBJ_TYPE_LIB))
{
set_status(ESP_ERR_INV_LIB_HANDLE);
break;
}
int arr1h = 0;
int arr2h = 0;
int arr3h = 0;
if (task >= TASK_RUN_FUNC1)
{
arr1h = rd_reg8(ESP_REG_ARR1_HANDLE);
if (!check_handle(arr1h) || ((mem_obj[arr1h].type != OBJ_TYPE_DATA) && (mem_obj[arr1h].type != OBJ_TYPE_DATAF)))
{
set_status(ESP_ERR_INV_ARG_HANDLE);
#ifdef VERBOSE
printf("Handle error, arr1h %d, type %d\r\n", arr1h, mem_obj[arr1h].type);
#endif
break;
}
}
if (task >= TASK_RUN_FUNC2)
{
arr2h = rd_reg8(ESP_REG_ARR2_HANDLE);
if (!check_handle(arr2h) || ((mem_obj[arr2h].type != OBJ_TYPE_DATA) && (mem_obj[arr2h].type != OBJ_TYPE_DATAF)))
{
set_status(ESP_ERR_INV_ARG_HANDLE);
break;
}
}
if (task == TASK_RUN_FUNC3)
{
arr3h = rd_reg8(ESP_REG_ARR3_HANDLE);
if (!check_handle(arr3h) || ((mem_obj[arr3h].type != OBJ_TYPE_DATA) && (mem_obj[arr3h].type != OBJ_TYPE_DATAF)))
{
set_status(ESP_ERR_INV_ARG_HANDLE);
break;
}
}
void *entry = mem_obj[handle].entry;
void *arr1 = mem_obj[arr1h].addr;
void *arr2 = mem_obj[arr2h].addr;
void *arr3 = mem_obj[arr3h].addr;
int rc = 0;
switch (task)
{
case TASK_RUN_FUNC0:
{
typedef int (*F0)(int func, int arg);
F0 f = (F0)entry;
rc = f(func, arg);
}
break;
case TASK_RUN_FUNC1:
{
typedef int (*F1)(int func, int arg, void *arr1);
F1 f = (F1)entry;
rc = f(func, arg, arr1);
}
break;
case TASK_RUN_FUNC2:
{
typedef int (*F2)(int func, int arg, void *arr1, void *arr2);
F2 f = (F2)entry;
rc = f(func, arg, arr1, arr2);
}
break;
case TASK_RUN_FUNC3:
{
typedef int (*F3)(int func, int arg, void *arr1, void *arr2, void *arr3);
F3 f = (F3)entry;
rc = f(func, arg, arr1, arr2, arr3);
}
break;
}
#ifdef VERBOSE
printf("RC: 0x%08lX\r\n", (u32)rc);
#endif
wr_reg32(ESP_REG_RETVAL, rc);
set_status(ESP_ST_READY);
}
break;
case TASK_LOAD_ELF:
case TASK_LOAD_ELF_OPT:
{
int handle = rd_reg8(ESP_REG_OBJ_HANDLE);
int opt = (task == TASK_LOAD_ELF_OPT) ? rd_reg8(ESP_REG_OPT) : 0;
if (!check_handle(handle) || (mem_obj[handle].type != OBJ_TYPE_ELF))
{
set_status(ESP_ERR_INV_ELF_HANDLE);
break;
}
int new_size = load_elf((u8*)mem_obj[handle].addr, &mem_obj[handle], opt);
if (new_size == -1)
set_status(ESP_ERR_OUT_OF_MEMORY);
else
{
// Delete ELF from memory and change object type to library
if (mem_obj[handle].addr) free(mem_obj[handle].addr);
mem_obj[handle].addr = (void*)-1;
mem_obj[handle].size = new_size;
mem_obj[handle].type = OBJ_TYPE_LIB;
mem_obj[handle].state = LIB_OBJ_ST_READY;
wr_reg8(ESP_REG_LIB_HANDLE, handle);
set_status(ESP_ST_READY);
}
}
break;
case TASK_MAKE_OBJ:
{
size_t obj_size = rd_reg32(ESP_REG_DATA_SIZE);
int obj_type = rd_reg8(ESP_REG_OBJ_TYPE);
int obj_hdl = make_obj(obj_size, obj_type); // set_status is done there
if (obj_hdl >= 0)
{
wr_reg8(ESP_REG_OBJ_HANDLE, obj_hdl);
wr_reg32(ESP_REG_DATA_OFFSET, 0);
wr_reg32(ESP_REG_DATA_SIZE, min(obj_size, DMA_BUF_SIZE));
set_status(ESP_ST_READY);
}
}
break;
case TASK_DEL_OBJ:
{
int handle = rd_reg8(ESP_REG_OBJ_HANDLE);
if (!check_handle(handle))
{
set_status(ESP_ERR_INV_HANDLE);
ESP_LOGE(TAG, "Wrong handle! (%02X)", handle);
break;
}
auto rc = delete_obj(handle);
if (!rc)
{
set_status(ESP_ERR_OBJ_NOT_DELETED);
ESP_LOGE(TAG, "Object cannot be deleted! (%02X)", handle);
}
else
{
set_status(ESP_ST_READY);
#ifdef VERBOSE
printf("Object deleted: handle %02X\r\n", handle);
#endif
}
}
break;
case TASK_KILL_OBJ:
{
while (1)
{
int playing = -1;
for (int i = 0; i < OBJ_HANDLES_MAX; i++)
{
if (mem_obj[i].addr && (mem_obj[i].type == OBJ_TYPE_XMC || mem_obj[i].type == OBJ_TYPE_MDC || mem_obj[i].type == OBJ_TYPE_S3C) && mem_obj[i].state == TRACK_OBJ_ST_PLAYING)
{
playing = i;
break;
}
}
if (playing < 0) break;
TRACK_TASK t = {};
t.task = TRACK_TASK_STOP;
t.handle = playing;
xQueueSend(xm_queue, &t, portMAX_DELAY);
while (mem_obj[playing].addr && mem_obj[playing].state == TRACK_OBJ_ST_PLAYING)
vTaskDelay(1);
}
for (int i = 0; i < OBJ_HANDLES_MAX; i++)
{
if (check_handle(i))
{
auto rc = delete_obj(i);
if (!rc)
ESP_LOGE(TAG, "Object cannot be deleted! (%02X)", i);
#ifdef VERBOSE
else
printf("Object deleted: handle %02X\r\n", i);
#endif
}
}
set_status(ESP_ST_READY);
}
break;
case TASK_DEHST:
{
int hsrc = rd_reg8(ESP_REG_OBJ_HANDLE);
u32 ssize = rd_reg32(ESP_REG_DATA_SIZE);
int dtype = rd_reg8(ESP_REG_OBJ_TYPE);
if (!check_handle(hsrc))
{
set_status(ESP_ERR_INV_HST_HANDLE);
break;
}
if (mem_obj[hsrc].type != OBJ_TYPE_HST)
{
set_status(ESP_ERR_INV_OBJ_TYPE);
break;
}
// Check source size
if (!ssize || (ssize > 65536))
{
set_status(ESP_ERR_INV_SIZE);
break;
}
// Unpack the HST
u8 *tmp = (u8*)malloc_spiram(65536);
if (!tmp)
{
set_status(ESP_ERR_OUT_OF_MEMORY);
break;
}
u32 dsize = dehrust(tmp, (u8*)mem_obj[hsrc].addr, ssize);
if (!dsize)
{
free(tmp);
set_status(ESP_ERR_INV_HST);
break;
}
void *dst = malloc_spiram(dsize);
if (!dst)
{
free(tmp);
set_status(ESP_ERR_OUT_OF_MEMORY);
break;
}
memcpy(dst, tmp, dsize);
free(tmp);
free(mem_obj[hsrc].addr);
mem_obj[hsrc].addr = dst;
mem_obj[hsrc].size = dsize;
mem_obj[hsrc].type = dtype;
wr_reg32(ESP_REG_DATA_OFFSET, 0);
wr_reg32(ESP_REG_DATA_SIZE, min(dsize, DMA_BUF_SIZE));
set_status(ESP_ST_READY);
}
break;
case TASK_UNZIP:
{
int hsrc = rd_reg8(ESP_REG_OBJ_HANDLE);
size_t ssize = rd_reg32(ESP_REG_DATA_SIZE);
int dtype = rd_reg8(ESP_REG_OBJ_TYPE);
if (!check_handle(hsrc))
{
set_status(ESP_ERR_INV_HST_HANDLE);
break;
}
if (mem_obj[hsrc].type != OBJ_TYPE_ZIP)
{
set_status(ESP_ERR_INV_OBJ_TYPE);
break;
}
auto src = (u8*)mem_obj[hsrc].addr;
size_t dsize = *(u32*)src;
void *dst = (u8*)malloc_spiram(dsize);
if (!dst)
{
set_status(ESP_ERR_OUT_OF_MEMORY);
break;
}
tinfl_init(decomp);
ssize -= 4;
tinfl_status decomp_status = tinfl_decompress(decomp, &src[4], &ssize, (u8*)dst, (u8*)dst, &dsize, TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
#ifdef VERBOSE
printf("Unzip rc = %d, inbytes = %d, outbytes = %d\r\n", decomp_status, ssize, dsize);
#endif
if (decomp_status != TINFL_STATUS_DONE)
{
set_status(ESP_ERR_INV_ZIP);
break;
}
mem_obj[hsrc].addr = dst;
mem_obj[hsrc].size = dsize;
mem_obj[hsrc].type = dtype;
wr_reg32(ESP_REG_DATA_OFFSET, 0);
wr_reg32(ESP_REG_DATA_SIZE, min(dsize, DMA_BUF_SIZE));
set_status(ESP_ST_READY);
}
break;
case TASK_RESET_STREAM_UNZIP:
process_rx_data(DREQ_ZIP, 0); // Reset stream depacker
break;
case TASK_XM_STREAM_LOAD:
xm_host_stream_prepare_command(rd_reg32(ESP_REG_DATA_OFFSET), rd_reg32(ESP_REG_DATA_SIZE));
break;
case TASK_MOD_STREAM_LOAD:
mod_host_stream_prepare_command(rd_reg32(ESP_REG_DATA_OFFSET), rd_reg32(ESP_REG_DATA_SIZE));
break;
case TASK_S3M_STREAM_LOAD:
s3m_host_stream_prepare_command(rd_reg32(ESP_REG_DATA_OFFSET), rd_reg32(ESP_REG_DATA_SIZE));
break;
case TASK_TRACK_STREAM_BREAK:
xm_host_stream_abort_current();
set_status(ESP_ST_READY);
break;
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
case TASK_WSCAN:
{
if (net.is_busy)
{
set_status(ESP_ERR_NET_BUSY);
break;
}
net.is_busy = true;
esp_err_t wscan_err = (esp_err_t)wf_scan_locked(300 /* timeout */);
net.is_busy = false;
if (wscan_err != ESP_OK)
{
printf("E: WiFi scan failed: %s\r\n", esp_err_to_name(wscan_err));
set_status(ESP_ERR_INV_STATE);
break;
}
put_txq(DREQ_WSCAN);
}
break;
case TASK_CONN:
{
if (net.is_busy)
{
set_status(ESP_ERR_NET_BUSY);
break;
}
net.is_busy = true;
net.state = NETWORK_OPENING;
auto rc = wifi_connect_locked((const char*)net.ssid, (const char*)net.pwd, 10000);
net.is_busy = false;
net.state = rc ? NETWORK_OPEN : NETWORK_CLOSED;
if (rc)
{
get_ip(net.ip.own_ip, net.ip.mask, net.ip.gate);
set_status(ESP_ST_READY);
}
else
set_status(ESP_ERR_AP_NOT_CONNECTED);
}
break;
case TASK_DISCONN:
{
wifi_disconnect_now_locked();
net.state = NETWORK_CLOSED;
set_status(ESP_ST_READY);
}
break;
case TASK_HTTP_GET:
http_do_get();
break;
case TASK_WEATHER_GET:
weather_do_get();
break;
case TASK_HTTPS_GET:
https_do_get();
break;
case TASK_GOPHER_GET:
gopher_do_get();
break;
case TASK_HTTP_STREAM_START:
stream_http_start();
break;
case TASK_HTTPS_STREAM_START:
stream_https_start();
break;
case TASK_GOPHER_STREAM_START:
stream_gopher_start();
break;
case TASK_STREAM_READ:
stream_read();
break;
case TASK_STREAM_CLOSE:
stream_close();
break;
case TASK_HTTP_STREAM_READ:
http_stream_read_task();
break;
#else
case TASK_WSCAN:
case TASK_CONN:
case TASK_DISCONN:
case TASK_HTTP_GET:
case TASK_WEATHER_GET:
case TASK_HTTPS_GET:
case TASK_GOPHER_GET:
case TASK_HTTP_STREAM_START:
case TASK_HTTPS_STREAM_START:
case TASK_GOPHER_STREAM_START:
case TASK_STREAM_READ:
case TASK_STREAM_CLOSE:
case TASK_HTTP_STREAM_READ:
set_status(ESP_ERR_INV_COMMAND);
break;
#endif
case TASK_SFX_PLAY:
{
int handle = rd_reg8(ESP_REG_OBJ_HANDLE);
if (!check_handle(handle) || mem_obj[handle].type != OBJ_TYPE_WAV)
{
set_status(ESP_ERR_INV_HANDLE);
break;
}
int channel = -1;
esp_err_t err = sfx_play_sync(handle, rd_reg8(ESP_REG_SFX_GROUP), SFX_VOLUME_MAX, SFX_PAN_CENTER, SFX_PITCH_ONE, &channel);
if (err != ESP_OK)
{
set_status(sfx_status_from_err(err));
break;
}
wr_reg8(ESP_REG_SFX_CHANNEL, channel);
set_status(ESP_ST_READY);
}
break;
case TASK_SFX_PLAY_EX:
{
int handle = rd_reg8(ESP_REG_OBJ_HANDLE);
if (!check_handle(handle) || mem_obj[handle].type != OBJ_TYPE_WAV)
{
set_status(ESP_ERR_INV_HANDLE);
break;
}
u16 pitch = 0;
rd_regs(ESP_REG_SFX_PITCH, &pitch, sizeof(pitch));
int channel = -1;
esp_err_t err = sfx_play_sync(
handle,
rd_reg8(ESP_REG_SFX_GROUP),
rd_reg8(ESP_REG_SFX_VOLUME),
rd_reg8(ESP_REG_SFX_PAN),
pitch,
&channel);
if (err != ESP_OK)
{
set_status(sfx_status_from_err(err));
break;
}
wr_reg8(ESP_REG_SFX_CHANNEL, channel);
set_status(ESP_ST_READY);
}
break;
case TASK_SFX_STOP:
{
esp_err_t err = sfx_stop_sync(rd_reg8(ESP_REG_SFX_CHANNEL));
set_status(sfx_status_from_err(err));
}
break;
case TASK_SFX_SET_PARAMS:
{
u16 pitch = 0;
rd_regs(ESP_REG_SFX_PITCH, &pitch, sizeof(pitch));
esp_err_t err = sfx_set_params_sync(
rd_reg8(ESP_REG_SFX_CHANNEL),
rd_reg8(ESP_REG_SFX_VOLUME),
rd_reg8(ESP_REG_SFX_PAN),
pitch);
set_status(sfx_status_from_err(err));
}
break;
case TASK_SFX_SET_VOLUME:
{
esp_err_t err = sfx_set_volume_sync(rd_reg8(ESP_REG_SFX_VOLUME));
set_status(sfx_status_from_err(err));
}
break;
case TASK_SFX_GET_STATE:
{
SFX_CHANNEL_STATE state = {};
esp_err_t err = sfx_get_state_sync(rd_reg8(ESP_REG_SFX_CHANNEL), &state);
if (err != ESP_OK)
{
set_status(sfx_status_from_err(err));
break;
}
wr_reg8(ESP_REG_SFX_STATE_ACTIVE, state.active);
wr_reg8(ESP_REG_SFX_STATE_HANDLE, state.handle);
wr_reg8(ESP_REG_SFX_STATE_GROUP, state.group);
wr_reg8(ESP_REG_SFX_STATE_ORDER, state.group_order);
wr_reg8(ESP_REG_SFX_STATE_VOLUME, state.volume);
wr_reg8(ESP_REG_SFX_STATE_PAN, state.pan);
wr_regs(ESP_REG_SFX_STATE_PITCH, &state.pitch, sizeof(state.pitch));
wr_reg32(ESP_REG_SFX_STATE_RATE, state.sample_rate);
wr_reg32(ESP_REG_SFX_STATE_FRAMES, state.frame_count);
wr_reg32(ESP_REG_SFX_STATE_POSITION, state.position);
wr_regs(ESP_REG_SFX_STATE_BITS, &state.bits_per_sample, sizeof(state.bits_per_sample));
wr_reg8(ESP_REG_SFX_STATE_CHANNELS, state.channels);
wr_reg8(ESP_REG_SFX_STATE_SIGNED, state.is_signed);
set_status(ESP_ST_READY);
}
break;
case TASK_TRACK_GET_STATE:
{
int handle = rd_reg8(ESP_REG_OBJ_HANDLE);
if (!check_handle(handle) || (mem_obj[handle].type != OBJ_TYPE_XMC && mem_obj[handle].type != OBJ_TYPE_MDC && mem_obj[handle].type != OBJ_TYPE_S3C))
{
set_status(ESP_ERR_INV_XM_HANDLE);
break;
}
wr_reg8(ESP_REG_TRACK_STATUS, mem_obj[handle].state);
wr_reg8(ESP_REG_TRACK_IS_CURRENT, curr_xm_handle == handle);
set_status(ESP_ST_READY);
}
break;
case TASK_TRACK_GET_INFO:
{
int handle = rd_reg8(ESP_REG_OBJ_HANDLE);
if (!check_handle(handle) || !mem_obj[handle].addr || (mem_obj[handle].type != OBJ_TYPE_XMC && mem_obj[handle].type != OBJ_TYPE_MDC && mem_obj[handle].type != OBJ_TYPE_S3C))
{
set_status(ESP_ERR_INV_XM_HANDLE);
break;
}
xm_context_t *ctx = (xm_context_t*)mem_obj[handle].addr;
uint8_t pattern_index = 0, pattern = 0, row = 0;
uint64_t samples = 0;
uint16_t bpm = 0, tempo = 0;
xm_get_position(ctx, &pattern_index, &pattern, &row, &samples);
xm_get_playing_speed(ctx, &bpm, &tempo);
wr_reg8(ESP_REG_TRACK_STATUS, mem_obj[handle].state);
wr_reg8(ESP_REG_TRACK_IS_CURRENT, curr_xm_handle == handle);
wr_reg8(ESP_REG_TRACK_PATTERN_INDEX, pattern_index);
wr_reg8(ESP_REG_TRACK_PATTERN, pattern);
wr_reg8(ESP_REG_TRACK_ROW, row);
wr_reg32(ESP_REG_TRACK_SAMPLES, (u32)samples);
wr_regs(ESP_REG_TRACK_BPM, &bpm, sizeof(bpm));
wr_regs(ESP_REG_TRACK_TEMPO, &tempo, sizeof(tempo));
wr_reg8(ESP_REG_TRACK_LOOP_COUNT, xm_get_loop_count(ctx));
wr_reg32(ESP_REG_TRACK_TOTAL_SAMPLES, xm_get_total_samples(handle));
set_status(ESP_ST_READY);
}
break;
case TASK_SFX_STOP_GROUP:
{
esp_err_t err = sfx_stop_group_sync(rd_reg8(ESP_REG_SFX_GROUP));
set_status(sfx_status_from_err(err));
}
break;
case TASK_REBOOT:
ESP_LOGE(TAG, "software restart requested by SPI command REBOOT (0xED)");
vTaskDelay(pdMS_TO_TICKS(100));
esp_restart();
break;
case TASK_RESET:
ESP_LOGE(TAG, "software restart requested by SPI command RESET (0xEE)");
vTaskDelay(pdMS_TO_TICKS(100));
esp_restart();
break;
case TASK_VFS_SERVER_START:
vfs_bridge_start_server();
set_status(ESP_ST_READY);
break;
case TASK_VFS_SERVER_STOP:
vfs_bridge_stop_server();
set_status(ESP_ST_READY);
break;
}
}
}
}
+83
View File
@@ -0,0 +1,83 @@
#pragma once
enum
{
TASK_MAKE_OBJ,
TASK_DEL_OBJ,
TASK_KILL_OBJ,
TASK_LOAD_ELF,
TASK_LOAD_ELF_OPT,
TASK_RUN_FUNC0,
TASK_RUN_FUNC1,
TASK_RUN_FUNC2,
TASK_RUN_FUNC3,
TASK_DEHST,
TASK_UNZIP,
TASK_RESET_STREAM_UNZIP,
TASK_XM_STREAM_LOAD,
TASK_MOD_STREAM_LOAD,
TASK_S3M_STREAM_LOAD,
TASK_TRACK_STREAM_BREAK,
TASK_TRACK_GET_STATE,
TASK_TRACK_GET_INFO,
TASK_WSCAN,
TASK_CONN,
TASK_DISCONN,
TASK_HTTP_GET,
TASK_WEATHER_GET,
TASK_HTTPS_GET,
TASK_GOPHER_GET,
TASK_HTTP_STREAM_START,
TASK_HTTPS_STREAM_START,
TASK_GOPHER_STREAM_START,
TASK_STREAM_READ,
TASK_STREAM_CLOSE,
TASK_HTTP_STREAM_READ,
TASK_SFX_PLAY,
TASK_SFX_PLAY_EX,
TASK_SFX_STOP,
TASK_SFX_SET_PARAMS,
TASK_SFX_SET_VOLUME,
TASK_SFX_GET_STATE,
TASK_SFX_STOP_GROUP,
TASK_REBOOT,
TASK_RESET,
TASK_VFS_SERVER_START,
TASK_VFS_SERVER_STOP,
};
#pragma pack(1)
struct NET
{
bool is_init = false;
bool is_busy = false;
u8 state;
u8 ssid[33];
u8 pwd[62];
u8 *url;
struct
{
u8 ip[4];
u8 own_ip[4];
u8 mask[4];
u8 gate[4];
} ip;
};
#pragma pack()
extern QueueHandle_t helper_queue;
extern struct NET net;
void put_txq(int type);
void put_txq_isr(int type);
void put_rxq(int type);
void put_rxq_isr(int type);
void put_helper_isr(int task);
void helper_task(void *arg);
u32 prepare_tx_data(u8 type, size_t size);
void process_rx_data(u8 type, size_t size);
+184
View File
@@ -0,0 +1,184 @@
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_http_client.h"
#include "esp_heap_caps.h"
#include "esp_crt_bundle.h"
#include "main.h"
#include "esp_spi_defs.h"
#include "spi_slave.h"
#include "mem_obj.h"
#include "helper.h"
#include "http_client.h"
const char *TAG = "http";
#define HTTP_BUF_SIZE (2 * 1024 * 1024)
void http_init() {}
void http_do_get()
{
uint8_t *http_buf = NULL;
uint8_t url_handle = rd_reg8(ESP_REG_OBJ_HANDLE);
char *url;
int total = 0;
int len;
int code;
esp_http_client_handle_t client;
esp_http_client_config_t cfg = {};
esp_err_t err;
int resp;
ESP_LOGI(TAG, "HTTP_GET handle=%d", url_handle);
http_buf = (uint8_t*)heap_caps_malloc(HTTP_BUF_SIZE, MALLOC_CAP_SPIRAM);
if (!http_buf)
{
set_status(0xA0);
ESP_LOGE(TAG, "HTTP buf malloc error");
return;
}
if (!net.url[0])
{
ESP_LOGW(TAG, "URL not set");
set_status(0x81);
return;
}
url = (char*)net.url;
if (strncmp(url, "http://", 7) != 0 && strncmp(url, "https://", 8) != 0)
{
ESP_LOGW(TAG, "invalid URL format");
set_status(0x81);
return;
}
ESP_LOGI(TAG, "GET %s", url);
cfg.url = url;
cfg.timeout_ms = 30000;
cfg.buffer_size = 4096;
cfg.user_agent = "ZiFi/1.0";
// Enable TLS for HTTPS
if (strncmp(url, "https://", 8) == 0)
cfg.crt_bundle_attach = esp_crt_bundle_attach;
client = esp_http_client_init(&cfg);
if (!client)
{
ESP_LOGW(TAG, "init fail");
set_status(0xB2);
return;
}
err = esp_http_client_open(client, 0);
if (err != ESP_OK)
{
ESP_LOGW(TAG, "open fail");
esp_http_client_cleanup(client);
set_status(0xB2);
goto cleanup;
}
esp_http_client_fetch_headers(client);
code = esp_http_client_get_status_code(client);
ESP_LOGI(TAG, "status %d", code);
if (code < 200 || code >= 300)
{
esp_http_client_close(client);
esp_http_client_cleanup(client);
set_status(0xB3);
goto cleanup;
}
for (;;)
{
len = esp_http_client_read(client, (char*)http_buf + total, HTTP_BUF_SIZE - total);
if (len > 0)
{
total += len;
wr_reg32(ESP_REG_DATA_SIZE, total);
ESP_LOGI(TAG, "got: %d bytes, progress: %d bytes", len, total);
if (total >= HTTP_BUF_SIZE) break;
}
else if (len == -ESP_ERR_HTTP_EAGAIN)
continue;
else if (len == 0)
{
ESP_LOGI(TAG, "empty response received");
break;
}
else
{
ESP_LOGE(TAG, "error: %d", -len);
break;
}
vTaskDelay(1);
}
esp_http_client_close(client);
esp_http_client_cleanup(client);
ESP_LOGI(TAG, "recv %d bytes", total);
if (total == 0)
{
ESP_LOGW(TAG, "empty response");
set_status(0xB4);
goto cleanup;
}
resp = make_obj(total, OBJ_TYPE_DATA);
if (resp < 0)
{
ESP_LOGE(TAG, "make_obj failed");
set_status(0xA0);
goto cleanup;
}
memcpy(mem_obj[resp].addr, http_buf, total);
wr_reg8(ESP_REG_OBJ_HANDLE, resp);
wr_reg32(ESP_REG_DATA_SIZE, total);
set_status(ESP_ST_READY);
ESP_LOGI(TAG, "done: handle=%d size=%d", resp, total);
cleanup:
if (http_buf) free(http_buf);
}
void https_do_get()
{
// HTTPS uses same logic as HTTP - esp_http_client handles both
// Just need to ensure URL starts with https://
http_do_get();
}
void http_stream_read_task() {}
void http_stream_delete(uint8_t handle) {}
bool http_is_stream_handle(uint8_t handle) { return false; }
void http_stream_read(uint8_t handle, uint32_t offset, uint32_t size) {}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
void http_init(void);
void http_do_get(void);
void https_do_get(void);
// Streaming API
void http_stream_read_task(void); // Вызывается из helper task
void http_stream_delete(uint8_t handle);
bool http_is_stream_handle(uint8_t handle);
void http_stream_read(uint8_t handle, uint32_t offset, uint32_t size); // legacy
+18
View File
@@ -0,0 +1,18 @@
## IDF Component Manager Manifest File
dependencies:
## Required IDF version
idf:
version: '>=4.1.0'
# # Put list of dependencies here
# # For components maintained by Espressif:
# component: "~1.0.0"
# # For 3rd party components:
# username/component: ">=1.0.0,<2.0.0"
# username2/component2:
# version: "~1.0.0"
# # For transient dependencies `public` flag can be set.
# # `public` flag doesn't have an effect dependencies of the `main` component.
# # All dependencies of `main` are public by default.
# public: true
espressif/usb_host_hid: ^1.1.0
espressif/esp_new_jpeg: ^1.0.0
+238
View File
@@ -0,0 +1,238 @@
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_err.h"
#include "bootloader_random.h"
#include "esp_random.h"
#include "nvs.h"
#include "miniz.h"
#include "nvs_flash.h"
#include <esp_crc.h>
#include "main.h"
#include "esp_spi_defs.h"
#include "spi_slave.h"
#include "console.h"
#include "nvs_params.h"
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
#include "wifi.h"
#include "http_client.h"
#include "gopher_client.h"
#include "stream_client.h"
#include "at_uart.h"
#include "at_cip.h"
#include "vfs_bridge.h"
#include "vfs_request.h"
#endif
#include "mem_obj.h"
#include "tracker.h"
#include "stats.h"
#include "ft8xx.h"
#include "elf.h"
#include "sdmmc.h"
#include "helper.h"
#include "depack.h"
#include "ps2_mouse.h"
#include "usb_mouse.h"
// #define ISR_PRINTF
EXT_RAM_BSS_ATTR tinfl_decompressor decomp_buf;
tinfl_decompressor *decomp = &decomp_buf;
const char TAG[] = "main";
int usb_mouse_start_on_boot = 0;
u32 task_ram_type_critical = MALLOC_CAP_INTERNAL;
u32 task_ram_type_non_critical = MALLOC_CAP_SPIRAM;
// ------------- Debug helpers
size_t sram_used_prev;
void log_sram_used(const char *name)
{
size_t total = heap_caps_get_total_size(MALLOC_CAP_INTERNAL);
size_t free = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
size_t used = total - free;
int delta = (int)used - (int)sram_used_prev;
ESP_LOGI("SRAM", "%s, SRAM used: %d bytes", name, delta);
sram_used_prev = used;
}
#ifdef ISR_PRINTF
#define PRINT_QUEUE_LENGTH 100
static QueueHandle_t printQueue;
void print_task(void *pvParameters)
{
char *message;
while (1)
{
if (xQueueReceive(printQueue, &message, portMAX_DELAY))
printf("%s", message);
}
}
extern "C"
{
void print_isr(const char* msg)
{
xQueueSendFromISR(printQueue, &msg, NULL);
}
}
#endif // ISR_PRINTF
// void IRAM_ATTR splash(int n)
// {
// while (n--)
// {
// gpio_set_level((gpio_num_t)GPIO_TEST1, 1); gpio_set_level((gpio_num_t)GPIO_TEST1, 0); // !!!
// }
// }
// ------------- System start and initialization
extern "C" void app_main()
{
esp_err_t err;
// ----- Test GPIO init
// gpio_set_direction((gpio_num_t)GPIO_TEST1, GPIO_MODE_OUTPUT);
// gpio_set_direction((gpio_num_t)GPIO_TEST2, GPIO_MODE_OUTPUT);
// gpio_set_direction((gpio_num_t)GPIO_TEST3, GPIO_MODE_OUTPUT);
sram_used_prev = heap_caps_get_total_size(MALLOC_CAP_INTERNAL) - heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
// ----- Runtime inits
stats::init();
bootloader_random_enable();
#ifdef ISR_PRINTF
// ----- ISR printf init
printQueue = xQueueCreate(PRINT_QUEUE_LENGTH, sizeof(char*));
log_sram_used("QueueCreate printQueue");
xTaskCreatePinnedToCoreWithCaps(print_task, "isr-printf", 2048, NULL, 1, NULL, 0, task_ram_type_non_critical);
log_sram_used("TaskCreate print_task");
#endif
// ----- NVS + params init
initialize_nvs();
ESP_ERROR_CHECK(app_params_load());
usb_mouse_start_on_boot = app_params.usb_mode ? 1 : 0;
ESP_LOGI("MAIN", "usb_mode=%u, usb_mouse_start_on_boot=%d", app_params.usb_mode, usb_mouse_start_on_boot);
// ----- Wi-Fi init
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
net.is_init = false;
net.state = NETWORK_CLOSED;
if (wifi_is_enabled())
{
initialize_wifi();
wifi_start_autoconnect();
net.is_init = true;
log_sram_used("initialize_wifi");
}
else
ESP_LOGI("MAIN", "WiFi disabled by config");
#endif
// ----- Helper init
helper_queue = xQueueCreateWithCaps(2, sizeof(int), task_ram_type_non_critical);
log_sram_used("QueueCreate helper_queue");
xTaskCreatePinnedToCoreWithCaps(helper_task, "helper", 6144, NULL, HELPER_TASK_PRIO, NULL, 0, task_ram_type_critical);
log_sram_used("TaskCreate helper_task");
// ----- LibXM init
initialize_xm();
// ----- Network client init
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
http_init();
log_sram_used("http_init");
gopher_init();
log_sram_used("gopher_init");
stream_init();
log_sram_used("stream_init");
#endif
// ----- SDMMC init
#if defined(CONFIG_IDF_TARGET_ESP32P4)
sd_ldo_init();
#endif
// ----- SPI slave init
init_slave_hd();
// ----- FT812 init
init_ft8xx();
log_sram_used("init_ft8xx");
// ----- PS/2 mouse init
err = ps2_mouse_start();
if (err != ESP_OK)
ESP_LOGE("PS2", "ps2_mouse_start failed: %s", esp_err_to_name(err));
log_sram_used("PS/2 mouse init");
// ----- USB mouse init
if (usb_mouse_start_on_boot)
{
err = usb_mouse_start();
if (err != ESP_OK)
ESP_LOGE("USBM", "usb_mouse_start failed: %s", esp_err_to_name(err));
}
log_sram_used("USB mouse init");
// ----- Console init
initialize_console();
xTaskCreatePinnedToCoreWithCaps(console_task, "console", 6144, NULL, CONSOLE_TASK_PRIO, NULL, 0, task_ram_type_critical);
log_sram_used("TaskCreate console_task");
// ----- AT command UART init
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
at_cip_init();
at_uart_init();
// Stack sizes sized for AT+CIPSTART="SSL": esp_tls_conn_new_sync()'s DNS lookup
// (getaddrinfo -> lwip_getaddrinfo -> netconn_gethostbyname_addrtype_n -> ...)
// is a much deeper call chain than the plain-TCP path's gethostbyname(), and
// mbedtls's own handshake/record processing adds further stack depth on top -
// the original 4096/3072 sizes (fine for plain TCP/UDP) overflowed and crashed
// with a seemingly unrelated LoadProhibited panic inside a spinlock deep in
// that chain, confirmed via backtrace against esp-spi.elf.
xTaskCreatePinnedToCoreWithCaps(at_task, "at-cmd", 12288, NULL, AT_TASK_PRIO, &g_at_task_handle, 1, task_ram_type_critical);
xTaskCreatePinnedToCoreWithCaps(at_cip_rx_task, "at-cip-rx", 8192, NULL, AT_TASK_PRIO, &g_at_cip_rx_task_handle, 1, task_ram_type_critical);
log_sram_used("TaskCreate at_task");
#endif
// ----- SMB2/3 network share (Phase 1 fake filesystem + Phase 2 SPI bridge to the Z80, see project plan)
// The task is created here unconditionally (cheap - it just registers
// console commands/queues and then blocks), but it does NOT start
// listening on its own anymore: it sits idle until ESP_CMD_VFS_SERVER_START
// (see vfs_bridge_start_server()/TASK_VFS_SERVER_START in helper.cpp) -
// the smbvfs.wmf plugin explicitly starts/stops the server (and the other
// services it pauses meanwhile) instead of it riding along with WiFi.
#if defined(CONFIG_ESP_WIFI_ENABLED) && CONFIG_ESP_WIFI_ENABLED
vfs_request_init();
vfs_bridge_init();
xTaskCreatePinnedToCoreWithCaps(smb_server_task, "smb-server", 8192, NULL, SMB_TASK_PRIO, NULL, 1, task_ram_type_critical);
log_sram_used("TaskCreate smb_server_task");
#endif
}
+58
View File
@@ -0,0 +1,58 @@
#pragma once
#include <stdint.h>
#include "miniz.h"
#include "tracker.h"
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
extern u32 task_ram_type_critical;
extern u32 task_ram_type_non_critical;
enum
{
// Core 0
CONSOLE_TASK_PRIO = 2,
WIFI_AUTOCONNECT_TASK_PRIO = 3,
STREAMER_TASK_PRIO = 4,
TCPIP_TASK_PRIO = 5,
WIFI_TASK_PRIO = 6,
SPI_BG_TASK_PRIO = 7,
TRACKER_HELPER_TASK_PRIO = 8,
HELPER_TASK_PRIO = 9,
USB_MOUSE_TASK_PRIO = 10,
USB_MOUSE_LIB_TASK_PRIO = 10,
USB_MOUSE_HID_TASK_PRIO = 11,
SLAVE_TASK_PRIO = 14,
// Core 1
TRACKER_PLAYER_TASK_PRIO = 10,
I2S_TASK_PRIO = 12,
AT_TASK_PRIO = 4,
SMB_TASK_PRIO = 4,
};
#define max(x, y) (((x) > (y)) ? (x) : (y))
#define min(x, y) (((x) < (y)) ? (x) : (y))
extern tinfl_decompressor *decomp;
// #define VERBOSE
#define _delay_ms(a) vTaskDelay(pdMS_TO_TICKS(a));
#ifdef CONFIG_SPIRAM
#define malloc_spiram(a) heap_caps_malloc(a, MALLOC_CAP_SPIRAM)
#else
#define malloc_spiram(a) malloc(a)
#endif
void log_sram_used(const char *name);
+231
View File
@@ -0,0 +1,231 @@
#include <stdint.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "main.h"
#include "esp_spi_defs.h"
#include "spi_slave.h"
#include "helper.h"
#include "mem_obj.h"
#include "tracker.h"
#include "sfx.h"
const char TAG[] = "mem_obj";
EXT_RAM_BSS_ATTR MEM_OBJ mem_obj[OBJ_HANDLES_MAX] = {};
int find_avail_handle()
{
for (int i = 0; i < OBJ_HANDLES_MAX; i++)
if (!mem_obj[i].type)
return i;
return -1;
}
int check_handle(int h)
{
return mem_obj[h].type != 0;
}
int mem_obj_can_flat_rw(int h)
{
if (!check_handle(h)) return 0;
switch (mem_obj[h].type)
{
case OBJ_TYPE_LIB:
case OBJ_TYPE_XMC:
case OBJ_TYPE_MDC:
case OBJ_TYPE_S3C:
return 0;
}
return 1;
}
int delete_obj(int h)
{
switch (mem_obj[h].type)
{
case OBJ_TYPE_LIB:
{
if (mem_obj[h].text) free(mem_obj[h].text);
if (mem_obj[h].data) free(mem_obj[h].data);
if (mem_obj[h].rodata) free(mem_obj[h].rodata);
if (mem_obj[h].bss) free(mem_obj[h].bss);
memset(&mem_obj[h], 0, sizeof(MEM_OBJ));
return 1;
}
case OBJ_TYPE_XMC:
case OBJ_TYPE_MDC:
case OBJ_TYPE_S3C:
if (mem_obj[h].state == TRACK_OBJ_ST_PLAYING)
{
ESP_LOGE(TAG, "Cannot delete playing tracker object: handle %02X", h);
return 0;
}
if (mem_obj[h].addr)
{
xm_free_context((xm_context_t*)mem_obj[h].addr);
memset(&mem_obj[h], 0, sizeof(MEM_OBJ));
return 1;
}
break;
case OBJ_TYPE_WAV:
{
u8 old_state = mem_obj[h].state;
mem_obj[h].state = OBJ_ST_DELETING;
esp_err_t err = sfx_stop_handle_sync(h);
if (err != ESP_OK)
{
mem_obj[h].state = old_state;
ESP_LOGE(TAG, "Cannot stop WAV object users before delete: handle %02X, err %s", h, esp_err_to_name(err));
return 0;
}
}
break;
}
if (mem_obj[h].addr) free(mem_obj[h].addr);
memset(&mem_obj[h], 0, sizeof(MEM_OBJ));
return 1;
}
void write_obj(int handle, const void *from, int size)
{
memcpy(mem_obj[handle].addr, from, max(mem_obj[handle].size, size));
}
int attach_obj(void *addr, int obj_size, int obj_type)
{
if (!addr || obj_size <= 0)
{
set_status(ESP_ERR_INV_PARAM);
return -1;
}
int handle = find_avail_handle();
if (handle == -1)
{
set_status(ESP_ERR_OUT_OF_HANDLES);
ESP_LOGE(TAG, "Cannot allocate handle for an object!");
return -1;
}
switch (obj_type)
{
case OBJ_TYPE_XM:
case OBJ_TYPE_XMC:
case OBJ_TYPE_MOD:
case OBJ_TYPE_MDC:
case OBJ_TYPE_S3M:
case OBJ_TYPE_S3C:
case OBJ_TYPE_WAV:
case OBJ_TYPE_DATA:
case OBJ_TYPE_ELF:
case OBJ_TYPE_HST:
case OBJ_TYPE_ZIP:
case OBJ_TYPE_DATAF:
break;
default:
ESP_LOGE(TAG, "Cannot attach unknown type object!");
set_status(ESP_ERR_INV_OBJ_TYPE);
return -1;
}
if (obj_type == OBJ_TYPE_XMC || obj_type == OBJ_TYPE_MDC || obj_type == OBJ_TYPE_S3C)
{
xm_context_t *ctx = (xm_context_t*)addr;
if (!tracker_context_has_registered_segments(ctx))
{
size_t context_size = ctx->ctx_size ? ctx->ctx_size : (size_t)obj_size;
if (!ctx->ctx_size) ctx->ctx_size = context_size;
if (!tracker_context_register_segment(ctx, addr, context_size, TRACKER_CONTEXT_SEG_CONTEXT))
{
set_status(ESP_ERR_INV_STATE);
ESP_LOGE(TAG, "Cannot attach tracker object without segment ownership!");
return -1;
}
}
}
mem_obj[handle].addr = addr;
mem_obj[handle].size = obj_size;
mem_obj[handle].type = obj_type;
mem_obj[handle].state = OBJ_ST_NONE;
return handle;
}
int make_obj(int obj_size, int obj_type)
{
void *obj_addr = NULL;
int handle = find_avail_handle();
if (handle == -1)
{
set_status(ESP_ERR_OUT_OF_HANDLES);
ESP_LOGE(TAG, "Cannot allocate handle for an object!");
return -1;
}
switch (obj_type)
{
case OBJ_TYPE_XM:
case OBJ_TYPE_MOD:
case OBJ_TYPE_S3M:
case OBJ_TYPE_WAV:
case OBJ_TYPE_DATA:
case OBJ_TYPE_ELF:
case OBJ_TYPE_HST:
case OBJ_TYPE_ZIP:
obj_addr = malloc_spiram(obj_size);
break;
case OBJ_TYPE_DATAF:
obj_addr = heap_caps_malloc(obj_size, MALLOC_CAP_INTERNAL);
break;
default:
{
ESP_LOGE(TAG, "Cannot create unknown type object!");
set_status(ESP_ERR_INV_OBJ_TYPE);
return -1;
}
}
if (!obj_addr)
{
set_status(ESP_ERR_OUT_OF_MEMORY);
ESP_LOGE(TAG, "Cannot allocate memory for an object! (%d bytes)", obj_size);
return -1;
}
#ifdef VERBOSE
else
printf("Object created: type %02X, handle %02X, addr %08X, size %u\r\n", obj_type, handle, (unsigned int)obj_addr, obj_size);
#endif
mem_obj[handle].addr = obj_addr;
mem_obj[handle].size = obj_size;
mem_obj[handle].type = obj_type;
mem_obj[handle].state = OBJ_ST_NONE;
return handle;
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#pragma pack(1)
typedef struct
{
// Common
void *addr;
size_t size;
u8 type;
u8 state;
// Lib
void *text;
void *rodata;
void *data;
void *bss;
u32 sz_text;
u32 sz_rodata;
u32 sz_data;
u32 sz_bss;
void *entry;
} MEM_OBJ;
#pragma pack()
#define OBJ_HANDLES_MAX 256
extern MEM_OBJ mem_obj[OBJ_HANDLES_MAX];
int find_avail_handle();
int check_handle(int h);
int delete_obj(int h);
int make_obj(int obj_size, int obj_type);
int attach_obj(void *addr, int obj_size, int obj_type);
int mem_obj_can_flat_rw(int h);
void write_obj(int handle, const void *from, int size);
+466
View File
@@ -0,0 +1,466 @@
// See named_pipe_rpc.h. Minimal MS-RPCE (bind/bind_ack/request/response/
// fault) plus just enough MS-WKST NetrWkstaGetInfo and MS-SRVS
// NetrShareGetInfo/NetrServerGetInfo to answer what Windows Explorer asks
// before it will browse a share. Field layouts follow the standard NDR
// wire rules (conformant/varying strings, deferred pointer data,
// embedded-union encoding). A packet capture confirmed Explorer really
// does require a working reply here (clean STATUS_NOT_IMPLEMENTED
// rejections just get retried forever) and confirmed which two pipes/
// opnums are being probed - but this specific bind/response encoding is
// new and not yet verified against a real exchange; the opnum logging
// below is there to check that on the next capture.
#include <string.h>
#include <stdlib.h>
#include "esp_log.h"
#include "named_pipe_rpc.h"
static const char *TAG = "rpc_pipe";
// ---- DCE/RPC wire constants -----------------------------------------
#define RPC_PTYPE_REQUEST 0
#define RPC_PTYPE_RESPONSE 2
#define RPC_PTYPE_FAULT 3
#define RPC_PTYPE_BIND 11
#define RPC_PTYPE_BIND_ACK 12
#define RPC_PTYPE_BIND_NAK 13
#define PFC_FIRST_FRAG 0x01
#define PFC_LAST_FRAG 0x02
// UUID 8a885d04-1ceb-11c9-9fe8-08002b104860 v2.0 - the classic NDR transfer
// syntax essentially every DCE/RPC client/server supports.
static const uint8_t NDR_TRANSFER_SYNTAX_UUID[16] = {
0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11,
0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60
};
#define NDR_TRANSFER_SYNTAX_VERSION 2
// ---- tiny append-only byte buffer for building replies ---------------
#define RPC_BUF_MAX 512
struct rpc_buf
{
uint8_t data[RPC_BUF_MAX];
uint32_t len;
};
static void buf_reset(rpc_buf *b) { b->len = 0; }
static void buf_put(rpc_buf *b, const void *src, uint32_t n)
{
if (b->len + n > RPC_BUF_MAX) n = RPC_BUF_MAX - b->len;
memcpy(b->data + b->len, src, n);
b->len += n;
}
static void buf_u8(rpc_buf *b, uint8_t v) { buf_put(b, &v, 1); }
static void buf_u16(rpc_buf *b, uint16_t v) { buf_put(b, &v, 2); }
static void buf_u32(rpc_buf *b, uint32_t v) { buf_put(b, &v, 4); }
static void buf_align4(rpc_buf *b)
{
while (b->len % 4) buf_u8(b, 0);
}
// NDR conformant+varying WCHAR string: max_count, offset(=0), actual_count
// (all in WCHARs, including the NUL terminator), then the UTF-16LE bytes,
// padded to a 4-byte boundary.
static void buf_ndr_string(rpc_buf *b, const char *s)
{
uint32_t n = (uint32_t)strlen(s) + 1; // include NUL
buf_u32(b, n); // max_count
buf_u32(b, 0); // offset
buf_u32(b, n); // actual_count
for (uint32_t i = 0; i < n; i++)
{
buf_u16(b, (uint8_t)s[i]); // ASCII-safe names only, widened to UTF-16LE
}
buf_align4(b);
}
// ---- per-handle pipe state --------------------------------------------
#define MAX_RPC_PIPES 4
struct rpc_pipe_state
{
bool in_use;
smb2_file_id file_id;
rpc_pipe_type type;
uint32_t call_id;
rpc_buf reply;
bool reply_pending;
struct smb2_context *owner;
};
static rpc_pipe_state s_pipes[MAX_RPC_PIPES];
static rpc_pipe_state *find_pipe(const smb2_file_id file_id)
{
for (int i = 0; i < MAX_RPC_PIPES; i++)
if (s_pipes[i].in_use && !memcmp(s_pipes[i].file_id, file_id, sizeof(smb2_file_id)))
return &s_pipes[i];
return NULL;
}
rpc_pipe_type rpc_pipe_type_for_name(const char *name)
{
if (!name) return RPC_PIPE_NONE;
if (!strcmp(name, "wkssvc")) return RPC_PIPE_WKSSVC;
if (!strcmp(name, "srvsvc")) return RPC_PIPE_SRVSVC;
return RPC_PIPE_NONE;
}
void rpc_pipe_open(const smb2_file_id file_id, rpc_pipe_type type, struct smb2_context *owner)
{
rpc_pipe_state *slot = find_pipe(file_id);
if (!slot)
{
for (int i = 0; i < MAX_RPC_PIPES; i++)
if (!s_pipes[i].in_use) { slot = &s_pipes[i]; break; }
}
if (!slot)
{
ESP_LOGW(TAG, "rpc_pipe_open: no free slot");
return;
}
memset(slot, 0, sizeof(*slot));
slot->in_use = true;
memcpy(slot->file_id, file_id, sizeof(smb2_file_id));
slot->type = type;
slot->owner = owner;
}
void rpc_pipe_close(const smb2_file_id file_id)
{
rpc_pipe_state *slot = find_pipe(file_id);
if (slot) slot->in_use = false;
}
bool rpc_pipe_is_open(const smb2_file_id file_id)
{
return find_pipe(file_id) != NULL;
}
void rpc_pipe_reset_for_owner(struct smb2_context *owner)
{
for (int i = 0; i < MAX_RPC_PIPES; i++)
if (s_pipes[i].in_use && s_pipes[i].owner == owner)
s_pipes[i].in_use = false;
}
// ---- DCE/RPC common header helpers ------------------------------------
static void put_rpc_header(rpc_buf *b, uint8_t ptype, uint32_t call_id)
{
buf_u8(b, 5); // rpc_vers
buf_u8(b, 0); // rpc_vers_minor
buf_u8(b, ptype);
buf_u8(b, PFC_FIRST_FRAG | PFC_LAST_FRAG);
buf_u8(b, 0x10); buf_u8(b, 0x00); buf_u8(b, 0x00); buf_u8(b, 0x00); // drep: little-endian/ASCII/IEEE
buf_u16(b, 0); // frag_length - patched in fixup_frag_length()
buf_u16(b, 0); // auth_length
buf_u32(b, call_id);
}
static void fixup_frag_length(rpc_buf *b)
{
uint16_t len = (uint16_t)b->len;
memcpy(b->data + 8, &len, 2); // frag_length is at header offset 8
}
// ---- BIND handling ------------------------------------------------------
// Parses one client bind PDU (after the 16-byte common header) and builds
// a bind_ack accepting the first context whose offered transfer syntax
// matches classic NDR, rejecting (per-context) any others - modern Windows
// clients routinely offer NDR64 and a "bind time feature negotiation"
// pseudo-context alongside classic NDR in the same bind.
static void handle_bind(rpc_pipe_state *pipe, const uint8_t *body, uint32_t body_len)
{
rpc_buf *out = &pipe->reply;
buf_reset(out);
put_rpc_header(out, RPC_PTYPE_BIND_ACK, pipe->call_id);
if (body_len < 12) { fixup_frag_length(out); pipe->reply_pending = true; return; }
uint16_t max_xmit; memcpy(&max_xmit, body + 0, 2);
uint16_t max_recv; memcpy(&max_recv, body + 2, 2);
uint8_t n_context = body[8];
buf_u16(out, max_xmit);
buf_u16(out, max_recv);
buf_u32(out, 0x12345678); // assoc_group_id - arbitrary nonzero
// Secondary address string: a real Windows server sends "\PIPE\<name>\0"
// here (confirmed via packet capture); we sent an empty string before.
const char *sec_addr = (pipe->type == RPC_PIPE_SRVSVC) ? "\\PIPE\\srvsvc" : "\\PIPE\\wkssvc";
uint16_t sec_addr_len = (uint16_t)strlen(sec_addr) + 1; // include NUL
buf_u16(out, sec_addr_len);
buf_put(out, sec_addr, sec_addr_len);
buf_align4(out); // realign before results array
if (n_context > 32) n_context = 0; // sanity guard
buf_u8(out, n_context);
buf_u8(out, 0); buf_u8(out, 0); buf_u8(out, 0); // pad
uint32_t off = 12;
for (uint8_t c = 0; c < n_context; c++)
{
if (off + 4 > body_len) break;
uint8_t n_transfer = body[off + 2];
// p_cont_id(2) + n_transfer_syn(1) + reserved(1) + abstract uuid(16) + abstract version(4)
uint32_t ts_base = off + 4 + 16 + 4;
bool accepted = false;
for (uint8_t t = 0; t < n_transfer; t++)
{
uint32_t ts_off = ts_base + (uint32_t)t * 20;
if (ts_off + 16 > body_len) break;
if (!memcmp(body + ts_off, NDR_TRANSFER_SYNTAX_UUID, 16))
{
accepted = true;
break;
}
}
if (accepted)
{
buf_u16(out, 0); // ack_result: acceptance
buf_u16(out, 0); // ack_reason
buf_put(out, NDR_TRANSFER_SYNTAX_UUID, 16);
buf_u32(out, NDR_TRANSFER_SYNTAX_VERSION);
}
else
{
buf_u16(out, 2); // ack_result: provider_rejection
buf_u16(out, 2); // ack_reason: transfer syntaxes not supported
uint8_t zero_uuid[16] = {0};
buf_put(out, zero_uuid, 16);
buf_u32(out, 0);
}
// Advance past every transfer syntax offered for this context (not
// just however many we scanned before matching/giving up), so the
// next context element is found at the right offset.
off = ts_base + (uint32_t)n_transfer * 20;
}
fixup_frag_length(out);
pipe->reply_pending = true;
}
// ---- WKSSVC: NetrWkstaGetInfo (opnum 0) --------------------------------
static void wkssvc_dispatch(rpc_pipe_state *pipe, uint16_t opnum, uint16_t context_id,
const uint8_t *stub, uint32_t stub_len)
{
rpc_buf *out = &pipe->reply;
buf_reset(out);
if (opnum != 0)
{
put_rpc_header(out, RPC_PTYPE_FAULT, pipe->call_id);
buf_u32(out, 0); buf_u16(out, context_id); buf_u8(out, 0); buf_u8(out, 0);
buf_u32(out, 0x1c010002); // nca_s_op_rng_error
fixup_frag_length(out);
pipe->reply_pending = true;
return;
}
put_rpc_header(out, RPC_PTYPE_RESPONSE, pipe->call_id);
buf_u32(out, 0); // alloc_hint - patched below
buf_u16(out, context_id);
buf_u8(out, 0); buf_u8(out, 0); // cancel_count, reserved
uint32_t stub_start = out->len;
// WkstaInfo is a top-level [out, switch_is(Level)] pointer parameter - a
// ref pointer by default, which NDR does NOT prefix with a referent ID
// (only unique/embedded pointers get one). A stray extra referent here
// shifted every field after it by 4 bytes for any strict NDR client -
// confirmed via Wireshark's own MS-SRVS/MS-WKST dissector misreading our
// "referent" value as the Level field ("Level: 262144" = 0x00040000, our
// old srvsvc referent) and flagging the rest of the packet malformed.
// Real Windows Explorer's RPC runtime is equally strict; only Samba's
// permissive smbclient tolerated this, which is why Day-1 testing looked
// fine. The union's own switch value is the first real stub byte.
buf_u32(out, 100); // encapsulated union switch: level 100
buf_u32(out, 500); // wki100_platform_id = PLATFORM_ID_NT
buf_u32(out, 0x00020004); // referent: computer_name
buf_u32(out, 0x00020008); // referent: langroup
buf_u32(out, 10); // wki100_ver_major
buf_u32(out, 0); // wki100_ver_minor
buf_ndr_string(out, "ZXEVO"); // deferred: computer_name
buf_ndr_string(out, "WORKGROUP"); // deferred: langroup
buf_u32(out, 0); // NET_API_STATUS return: NERR_Success
uint32_t stub_len_out = out->len - stub_start;
memcpy(out->data + 16, &stub_len_out, 4); // alloc_hint field, right after the 16-byte common header
fixup_frag_length(out);
pipe->reply_pending = true;
(void)stub; (void)stub_len;
}
// ---- SRVSVC: NetrShareGetInfo (16) / NetrServerGetInfo (21) ------------
static void srvsvc_dispatch(rpc_pipe_state *pipe, uint16_t opnum, uint16_t context_id,
const uint8_t *stub, uint32_t stub_len)
{
rpc_buf *out = &pipe->reply;
buf_reset(out);
if (opnum != 15 && opnum != 16 && opnum != 21)
{
put_rpc_header(out, RPC_PTYPE_FAULT, pipe->call_id);
buf_u32(out, 0); buf_u16(out, context_id); buf_u8(out, 0); buf_u8(out, 0);
buf_u32(out, 0x1c010002); // nca_s_op_rng_error
fixup_frag_length(out);
pipe->reply_pending = true;
return;
}
put_rpc_header(out, RPC_PTYPE_RESPONSE, pipe->call_id);
buf_u32(out, 0); // alloc_hint placeholder
buf_u16(out, context_id);
buf_u8(out, 0); buf_u8(out, 0);
uint32_t stub_start = out->len;
if (opnum == 15)
{
// NetrShareEnum -> [in,out] LPSHARE_ENUM_STRUCT InfoStruct, level 1,
// one entry. Explorer cross-checks the share it navigated to actually
// appears in this list before it will browse it - this used to say
// "test" (a leftover from Phase 1's own fake test share, never updated
// once tree_connect_handler started accepting real share names) while
// the real share is named "share" (see smbvfs's own share_name[] and
// what clients actually tree_connect to, \\<esp32-ip>\share) - the
// mismatch was confirmed on real hardware to make Explorer repeatedly
// re-open and re-query srvsvc many times (never trusting the mismatch
// enough to actually browse the share) before eventually giving up
// entirely, while a real TREE_CONNECT to "share" itself kept
// succeeding cleanly the whole time.
// InfoStruct is a top-level [in,out] ref pointer parameter - no
// referent ID at this level (see wkssvc_dispatch's comment above);
// Level is the first real stub byte. Everything below this - the
// union's embedded Ctr1 pointer, the Buffer array pointer, the
// per-entry name/remark pointers - are genuinely embedded/unique
// pointers and correctly keep their referent IDs.
buf_u32(out, 1); // SHARE_ENUM_STRUCT.Level
buf_u32(out, 1); // encapsulated union switch (repeats Level)
buf_u32(out, 0x00040004); // referent: SHARE_INFO_1_CONTAINER*
buf_u32(out, 1); // container.EntriesRead
buf_u32(out, 0x00040008); // referent: Buffer (array of SHARE_INFO_1)
buf_u32(out, 1); // conformant array max_count
buf_u32(out, 0x0004000c); // entry[0] referent: shi1_netname
buf_u32(out, 0); // entry[0] shi1_type: STYPE_DISKTREE
buf_u32(out, 0x00040010); // entry[0] referent: shi1_remark
buf_ndr_string(out, "share");
buf_ndr_string(out, "ZX Evolution share");
buf_u32(out, 1); // [out] TotalEntries
buf_u32(out, 0); // [in,out, unique] ResumeHandle: NULL (done)
}
else if (opnum == 16)
{
// NetrShareGetInfo -> LPSHARE_INFO union, level 1 (SHARE_INFO_1).
// Top-level [out, switch_is(Level)] ref pointer - no referent ID here
// either (see wkssvc_dispatch's comment); the union's switch value is
// the first real stub byte.
buf_u32(out, 1); // switch: level 1
buf_u32(out, 0x00030004); // referent: shi1_netname
buf_u32(out, 0); // shi1_type: STYPE_DISKTREE
buf_u32(out, 0x00030008); // referent: shi1_remark
buf_ndr_string(out, "share");
buf_ndr_string(out, "ZX Evolution share");
}
else
{
// NetrServerGetInfo -> LPSERVER_INFO union, level 100 (SERVER_INFO_100).
// Same top-level-ref-pointer-has-no-referent rule as above.
buf_u32(out, 100); // switch: level 100
buf_u32(out, 500); // sv100_platform_id = PLATFORM_ID_NT
buf_u32(out, 0x00030014); // referent: sv100_name
buf_ndr_string(out, "ZXEVO");
}
buf_u32(out, 0); // NET_API_STATUS return: NERR_Success
uint32_t stub_len_out = out->len - stub_start;
memcpy(out->data + 16, &stub_len_out, 4);
fixup_frag_length(out);
pipe->reply_pending = true;
(void)stub; (void)stub_len;
}
// ---- entry points -------------------------------------------------------
int rpc_pipe_write(const smb2_file_id file_id, const uint8_t *data, uint32_t len)
{
rpc_pipe_state *pipe = find_pipe(file_id);
if (!pipe) return -1;
if (!data) return -1;
if (len < 16)
{
ESP_LOGW(TAG, "rpc_pipe_write: PDU too short (%u)", (unsigned)len);
return -1;
}
uint8_t ptype = data[2];
uint32_t call_id; memcpy(&call_id, data + 12, 4);
pipe->call_id = call_id;
const uint8_t *body = data + 16;
uint32_t body_len = len - 16;
if (ptype == RPC_PTYPE_BIND)
{
handle_bind(pipe, body, body_len);
}
else if (ptype == RPC_PTYPE_REQUEST)
{
if (body_len < 8)
{
ESP_LOGW(TAG, "rpc request: body too short");
return -1;
}
uint16_t context_id; memcpy(&context_id, body + 4, 2);
uint16_t opnum; memcpy(&opnum, body + 6, 2);
const uint8_t *stub = body + 8;
uint32_t stub_len = body_len - 8;
if (pipe->type == RPC_PIPE_WKSSVC)
wkssvc_dispatch(pipe, opnum, context_id, stub, stub_len);
else if (pipe->type == RPC_PIPE_SRVSVC)
srvsvc_dispatch(pipe, opnum, context_id, stub, stub_len);
else
ESP_LOGW(TAG, "rpc request: pipe type=%d not wkssvc/srvsvc, opnum=%u ignored", (int)pipe->type, (unsigned)opnum);
ESP_LOGI(TAG, "rpc request: type=%d opnum=%u reply_pending=%d reply_len=%u",
(int)pipe->type, (unsigned)opnum, (int)pipe->reply_pending, (unsigned)pipe->reply.len);
}
else
{
ESP_LOGW(TAG, "rpc_pipe_write: unhandled ptype=%u", (unsigned)ptype);
return -1;
}
return 0;
}
int rpc_pipe_read(const smb2_file_id file_id, const uint8_t **out_data, uint32_t *out_len)
{
rpc_pipe_state *pipe = find_pipe(file_id);
if (!pipe || !pipe->reply_pending)
{
if (out_len) *out_len = 0;
return 0;
}
*out_data = pipe->reply.data;
*out_len = pipe->reply.len;
pipe->reply_pending = false;
return 0;
}
+59
View File
@@ -0,0 +1,59 @@
#pragma once
// Minimal DCE/RPC-over-named-pipe support for the wkssvc/srvsvc endpoints
// Windows Explorer probes before it will browse an SMB share (see the SMB
// server plan). Only enough of MS-RPCE/MS-WKST/MS-SRVS is implemented to
// satisfy that initial probe (bind + one or two read-only info calls) -
// this is not a general-purpose RPC server.
#include <stdint.h>
#include <time.h> // smb2.h uses time_t without including <ctime> itself
#include "smb2.h"
enum rpc_pipe_type
{
RPC_PIPE_NONE = 0,
RPC_PIPE_WKSSVC,
RPC_PIPE_SRVSVC,
};
rpc_pipe_type rpc_pipe_type_for_name(const char *name);
// file_id is the SMB2 file id (16 bytes) the CREATE handler assigned - used
// to correlate the WRITE (request) and READ (response) that make up one
// RPC round trip, and to remember which pipe (and thus which RPC
// interface) a given open handle is bound to.
// owner identifies which connection this pipe belongs to (see
// rpc_pipe_reset_for_owner() below) - libsmb2 hands every handler its
// struct smb2_context* already, just thread it through.
void rpc_pipe_open(const smb2_file_id file_id, rpc_pipe_type type, struct smb2_context *owner);
void rpc_pipe_close(const smb2_file_id file_id);
// Frees every pipe slot belonging to `owner` - for use when THAT specific
// client connection is torn down (a dropped/reset TCP connection never
// sends a matching CLOSE for whatever pipes were open). Scoped to one
// owner, not a blanket reset: this table is global and can hold slots for
// more than one concurrent connection (e.g. Explorer's own IPC$ probe
// connections briefly overlapping the one it actually browses with) -
// wiping every connection's pipes just because one of them disconnected
// was confirmed on real hardware to force the *other*, still-active
// connection to restart its RPC bind from scratch in an infinite loop.
void rpc_pipe_reset_for_owner(struct smb2_context *owner);
// True if file_id is a currently-open RPC pipe handle. QUERY_INFO on a
// named pipe must report zero size - Windows' redirector uses a nonzero
// end_of_file as a sign this isn't really a pipe and quietly gives up
// without ever attempting the RPC bind (confirmed by packet capture: it
// was doing CREATE -> QUERY_INFO -> CLOSE in a loop, never WRITE).
bool rpc_pipe_is_open(const smb2_file_id file_id);
// Feeds a WRITE's payload (a DCE/RPC PDU: bind or request) to the pipe
// identified by file_id. Builds the reply PDU internally; returns 0 on
// success (including "we didn't understand this, queued a fault reply"),
// <0 if file_id isn't a known open RPC pipe.
int rpc_pipe_write(const smb2_file_id file_id, const uint8_t *data, uint32_t len);
// Retrieves the reply PDU queued by the preceding write, if any. *out_data
// points into internal storage valid until the next write/close on this
// file_id. Returns 0 with out_len=0 if nothing is queued.
int rpc_pipe_read(const smb2_file_id file_id, const uint8_t **out_data, uint32_t *out_len);
+149
View File
@@ -0,0 +1,149 @@
#include <string.h>
#include <stdlib.h>
#include "nvs.h"
#include "nvs_flash.h"
#include "main.h"
#include "nvs_params.h"
app_params_t app_params;
char NVS_PARAMS_NS[] = "appcfg";
char NVS_KEY_USB_MODE[] = "usb_mode";
char NVS_KEY_WIFI_MODE[] = "wifi_mode";
char NVS_KEY_WIFI_AP[] = "wifi_ap";
char NVS_KEY_WIFI_PSW[] = "wifi_psw";
void initialize_nvs()
{
esp_err_t err = nvs_flash_init();
log_sram_used(__FILE_NAME__ ": nvs_flash_init");
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
log_sram_used(__FILE_NAME__ ": initialize_nvs end");
}
void app_params_set_defaults()
{
memset(&app_params, 0, sizeof(app_params));
}
esp_err_t app_params_load_u8(nvs_handle_t h, const char *key, uint8_t *value)
{
esp_err_t err = nvs_get_u8(h, key, value);
if (err == ESP_ERR_NVS_NOT_FOUND) return ESP_OK;
return err;
}
esp_err_t app_params_load_str(nvs_handle_t h, const char *key, char *buf, size_t buf_size)
{
size_t size = buf_size;
esp_err_t err = nvs_get_str(h, key, buf, &size);
if (err == ESP_ERR_NVS_NOT_FOUND) return ESP_OK;
if (err == ESP_ERR_NVS_INVALID_LENGTH)
{
buf[0] = 0;
return ESP_OK;
}
return err;
}
esp_err_t app_params_load()
{
app_params_set_defaults();
nvs_handle_t h = 0;
esp_err_t err = nvs_open(NVS_PARAMS_NS, NVS_READONLY, &h);
if (err == ESP_ERR_NVS_NOT_FOUND) return ESP_OK;
if (err != ESP_OK) return err;
err = app_params_load_u8(h, NVS_KEY_USB_MODE, &app_params.usb_mode);
if (err != ESP_OK) goto end;
err = app_params_load_u8(h, NVS_KEY_WIFI_MODE, &app_params.wifi_mode);
if (err != ESP_OK) goto end;
err = app_params_load_str(h, NVS_KEY_WIFI_AP, app_params.wifi_ap, sizeof(app_params.wifi_ap));
if (err != ESP_OK) goto end;
err = app_params_load_str(h, NVS_KEY_WIFI_PSW, app_params.wifi_psw, sizeof(app_params.wifi_psw));
if (err != ESP_OK) goto end;
end:
nvs_close(h);
return err;
}
esp_err_t app_params_save()
{
nvs_handle_t h = 0;
esp_err_t err = nvs_open(NVS_PARAMS_NS, NVS_READWRITE, &h);
if (err != ESP_OK) return err;
err = nvs_set_u8(h, NVS_KEY_USB_MODE, app_params.usb_mode);
if (err != ESP_OK) goto end;
err = nvs_set_u8(h, NVS_KEY_WIFI_MODE, app_params.wifi_mode);
if (err != ESP_OK) goto end;
err = nvs_set_str(h, NVS_KEY_WIFI_AP, app_params.wifi_ap);
if (err != ESP_OK) goto end;
err = nvs_set_str(h, NVS_KEY_WIFI_PSW, app_params.wifi_psw);
if (err != ESP_OK) goto end;
err = nvs_commit(h);
end:
nvs_close(h);
return err;
}
esp_err_t app_params_reset()
{
app_params_set_defaults();
return app_params_save();
}
bool app_params_set_by_name(const char *name, const char *value)
{
char *endp = NULL;
unsigned long v = 0;
if (!strcmp(name, "usb_mode"))
{
v = strtoul(value, &endp, 0);
if (!endp || *endp || v > 255) return false;
app_params.usb_mode = (uint8_t)v;
return true;
}
if (!strcmp(name, "wifi_mode"))
{
v = strtoul(value, &endp, 0);
if (!endp || *endp || v > 255) return false;
app_params.wifi_mode = (uint8_t)v;
return true;
}
if (!strcmp(name, "wifi_ap"))
{
strlcpy(app_params.wifi_ap, value, sizeof(app_params.wifi_ap));
return true;
}
if (!strcmp(name, "wifi_psw"))
{
strlcpy(app_params.wifi_psw, value, sizeof(app_params.wifi_psw));
return true;
}
return false;
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
struct app_params_t
{
uint8_t usb_mode;
uint8_t wifi_mode;
char wifi_ap[64];
char wifi_psw[64];
};
extern app_params_t app_params;
void initialize_nvs();
void app_params_set_defaults();
esp_err_t app_params_load();
esp_err_t app_params_save();
esp_err_t app_params_reset();
bool app_params_set_by_name(const char *name, const char *value);
+4620
View File
File diff suppressed because it is too large Load Diff
+87
View File
@@ -0,0 +1,87 @@
#pragma once
#include "esp_err.h"
#include "main.h"
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
OPL_MODE_OFF = 0,
OPL_MODE_YMF262,
OPL_MODE_YMF278
} OPL_MODE;
typedef enum
{
OPL_FM_RENDER_OPL3_FULL = 0,
OPL_FM_RENDER_OPL2_MELODY
} OPL_FM_RENDER_MODE;
#define OPL_PORT_FM_REG1 0x00C4
#define OPL_PORT_FM_DAT1 0x00C5
#define OPL_PORT_FM_REG2 0x00C6
#define OPL_PORT_FM_DAT2 0x00C7
#define OPL_PORT_WAVE_REG 0x007E
#define OPL_PORT_WAVE_DAT 0x007F
#define OPL_ROM_PART_LABEL "oplrom"
#define OPL_ROM_PART_SUBTYPE 0x85
#define OPL_ROM_SIZE 0x200000
#define OPL_YMF278_DEFAULT_RAM_SIZE 0x400000
#define OPL_FM_BANK_COUNT 2
#define OPL_FM_REG_COUNT 256
#define OPL_WAVE_REG_COUNT 256
typedef struct
{
u16 reg;
u8 value;
} OPL_FM_REG_WRITE;
typedef struct
{
OPL_MODE mode;
u8 enabled;
size_t rom_size;
size_t ram_size;
u8 fm_latch_bank;
u8 fm_latch[2];
u8 wave_latch;
u8 status;
u32 queued_writes;
} OPL_INFO;
esp_err_t opl_enable(OPL_MODE mode, size_t ram_size);
void opl_disable();
void opl_reset();
int opl_is_enabled();
esp_err_t opl_get_info(OPL_INFO *info);
u8 opl_read_status();
u8 opl_read_port(u16 port);
void opl_write_port(u16 port, u8 value);
esp_err_t opl_queue_write_port(u16 port, u8 value);
esp_err_t opl_queue_write_fm_reg(u16 reg, u8 value);
esp_err_t opl_queue_write_wave_reg(u8 reg, u8 value);
u8 opl_read_fm_reg(u16 reg);
void opl_write_fm_reg(u16 reg, u8 value);
esp_err_t opl_write_fm_regs(const OPL_FM_REG_WRITE *writes, size_t count);
u8 opl_read_wave_reg(u8 reg);
void opl_write_wave_reg(u8 reg, u8 value);
esp_err_t opl_set_fm_render_mode(OPL_FM_RENDER_MODE mode);
esp_err_t opl_set_volume(u8 volume);
u8 opl_get_volume();
void opl_render(float *mix, int sample_count, int sample_rate);
void opl_console_register_system_commands();
#ifdef __cplusplus
}
#endif
+922
View File
@@ -0,0 +1,922 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"
#include "driver/gptimer.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_console.h"
#include "esp_err.h"
#include "esp_intr_alloc.h"
#include "ps2_mouse.h"
const gpio_num_t PS2_MOUSE_GPIO_DATA = GPIO_NUM_5;
const gpio_num_t PS2_MOUSE_GPIO_CLK = GPIO_NUM_7;
enum
{
PS2_TX_FIFO_SIZE = 64,
PS2_CLK_LOW_US = 35,
PS2_CLK_HIGH_US = 35,
PS2_TX_HIGH_HOLD_US = 15,
PS2_TX_SETUP_US = 20,
PS2_TX_INTERBYTE_US = 80,
PS2_HOST_RX_START_US = 30,
PS2_HOST_RX_SAMPLE_US = 18,
PS2_HOST_ACK_SETUP_US = 15,
PS2_HOST_INHIBIT_US = 120,
PS2_TX_RETRY_US = 200,
};
enum ps2_bus_mode_t
{
PS2_BUS_IDLE = 0,
PS2_BUS_DEV_TX,
PS2_BUS_HOST_RX,
PS2_BUS_HOST_ACK,
};
enum ps2_timer_evt_t
{
PS2_TIMER_EVT_NONE = 0,
PS2_TIMER_EVT_DEV_TX_FALL,
PS2_TIMER_EVT_DEV_TX_RISE,
PS2_TIMER_EVT_DEV_TX_NEXT,
PS2_TIMER_EVT_DEV_TX_DONE,
PS2_TIMER_EVT_HOST_RX_FALL,
PS2_TIMER_EVT_HOST_RX_RISE,
PS2_TIMER_EVT_HOST_RX_SAMPLE,
PS2_TIMER_EVT_HOST_ACK_PREPARE,
PS2_TIMER_EVT_HOST_ACK_FALL,
PS2_TIMER_EVT_HOST_ACK_RISE,
PS2_TIMER_EVT_HOST_ACK_DONE,
PS2_TIMER_EVT_TX_RETRY,
};
gptimer_handle_t ps2_mouse_timer = NULL;
portMUX_TYPE ps2_mouse_lock = portMUX_INITIALIZER_UNLOCKED;
bool ps2_mouse_gpio_isr_service_ready = false;
volatile bool ps2_mouse_active = false;
volatile bool ps2_mouse_streaming = false;
volatile bool ps2_mouse_remote_mode = false;
volatile bool ps2_mouse_scaling21 = false;
volatile bool ps2_mouse_expect_param = false;
volatile uint8_t ps2_mouse_pending_cmd = 0;
volatile uint8_t ps2_mouse_sample_rate = 100;
volatile uint8_t ps2_mouse_resolution = 2;
volatile int ps2_mouse_dx_accum = 0;
volatile int ps2_mouse_dy_accum = 0;
volatile uint8_t ps2_mouse_buttons = 0;
volatile bool ps2_mouse_motion_pending = false;
volatile uint8_t ps2_mouse_tx_fifo[PS2_TX_FIFO_SIZE];
volatile unsigned ps2_mouse_tx_rd = 0;
volatile unsigned ps2_mouse_tx_wr = 0;
volatile unsigned ps2_mouse_tx_count = 0;
volatile bool ps2_mouse_tx_resume_valid = false;
volatile uint8_t ps2_mouse_tx_resume_byte = 0;
volatile ps2_bus_mode_t ps2_mouse_bus_mode = PS2_BUS_IDLE;
volatile ps2_timer_evt_t ps2_mouse_timer_evt = PS2_TIMER_EVT_NONE;
volatile uint8_t ps2_mouse_tx_byte = 0;
volatile uint8_t ps2_mouse_tx_parity = 1;
volatile int ps2_mouse_tx_bit = 0;
volatile uint8_t ps2_mouse_rx_byte = 0;
volatile uint8_t ps2_mouse_rx_parity = 1;
volatile int ps2_mouse_rx_bit = 0;
volatile bool ps2_mouse_rx_start_ok = false;
volatile bool ps2_mouse_rx_parity_ok = false;
volatile bool ps2_mouse_rx_stop_ok = false;
volatile uint8_t ps2_mouse_rx_done_byte = 0;
volatile bool ps2_mouse_rx_frame_ok = false;
volatile uint32_t ps2_mouse_clk_low_since_us = 0;
void IRAM_ATTR ps2_mouse_clk_low()
{
gpio_set_level(PS2_MOUSE_GPIO_CLK, 0);
}
void IRAM_ATTR ps2_mouse_clk_release()
{
gpio_set_level(PS2_MOUSE_GPIO_CLK, 1);
}
void IRAM_ATTR ps2_mouse_data_low()
{
gpio_set_level(PS2_MOUSE_GPIO_DATA, 0);
}
void IRAM_ATTR ps2_mouse_data_release()
{
gpio_set_level(PS2_MOUSE_GPIO_DATA, 1);
}
int IRAM_ATTR ps2_mouse_clk_level()
{
return gpio_get_level(PS2_MOUSE_GPIO_CLK);
}
int IRAM_ATTR ps2_mouse_data_level()
{
return gpio_get_level(PS2_MOUSE_GPIO_DATA);
}
int IRAM_ATTR ps2_mouse_line_idle()
{
return ps2_mouse_clk_level() && ps2_mouse_data_level();
}
uint32_t IRAM_ATTR ps2_mouse_get_time_us_isr()
{
uint64_t now = 0;
if (!ps2_mouse_timer)
return 0;
gptimer_get_raw_count(ps2_mouse_timer, &now);
return (uint32_t)now;
}
void IRAM_ATTR ps2_mouse_timer_schedule_isr(ps2_timer_evt_t evt, uint32_t delta_us)
{
uint64_t now = 0;
gptimer_alarm_config_t alarm = {};
if (!ps2_mouse_timer)
return;
gptimer_get_raw_count(ps2_mouse_timer, &now);
ps2_mouse_timer_evt = evt;
alarm.alarm_count = now + delta_us;
alarm.flags.auto_reload_on_alarm = false;
gptimer_set_alarm_action(ps2_mouse_timer, &alarm);
}
void IRAM_ATTR ps2_mouse_reset_device_state_isr()
{
ps2_mouse_streaming = false;
ps2_mouse_remote_mode = false;
ps2_mouse_scaling21 = false;
ps2_mouse_expect_param = false;
ps2_mouse_pending_cmd = 0;
ps2_mouse_sample_rate = 100;
ps2_mouse_resolution = 2;
ps2_mouse_dx_accum = 0;
ps2_mouse_dy_accum = 0;
ps2_mouse_buttons = 0;
ps2_mouse_motion_pending = false;
}
void IRAM_ATTR ps2_mouse_reset_protocol_state_isr()
{
ps2_mouse_reset_device_state_isr();
ps2_mouse_tx_rd = 0;
ps2_mouse_tx_wr = 0;
ps2_mouse_tx_count = 0;
ps2_mouse_tx_resume_valid = false;
ps2_mouse_tx_resume_byte = 0;
ps2_mouse_bus_mode = PS2_BUS_IDLE;
ps2_mouse_timer_evt = PS2_TIMER_EVT_NONE;
ps2_mouse_tx_byte = 0;
ps2_mouse_tx_parity = 1;
ps2_mouse_tx_bit = 0;
ps2_mouse_rx_byte = 0;
ps2_mouse_rx_parity = 1;
ps2_mouse_rx_bit = 0;
ps2_mouse_rx_start_ok = false;
ps2_mouse_rx_parity_ok = false;
ps2_mouse_rx_stop_ok = false;
ps2_mouse_rx_done_byte = 0;
ps2_mouse_rx_frame_ok = false;
ps2_mouse_clk_low_since_us = 0;
}
int IRAM_ATTR ps2_mouse_tx_push_isr(uint8_t v)
{
if (ps2_mouse_tx_count >= PS2_TX_FIFO_SIZE)
return 0;
ps2_mouse_tx_fifo[ps2_mouse_tx_wr] = v;
ps2_mouse_tx_wr = ps2_mouse_tx_wr + 1;
if (ps2_mouse_tx_wr >= PS2_TX_FIFO_SIZE)
ps2_mouse_tx_wr = 0;
ps2_mouse_tx_count = ps2_mouse_tx_count + 1;
return 1;
}
int IRAM_ATTR ps2_mouse_tx_pop_isr(uint8_t *v)
{
if (ps2_mouse_tx_resume_valid)
{
*v = ps2_mouse_tx_resume_byte;
ps2_mouse_tx_resume_valid = false;
return 1;
}
if (!ps2_mouse_tx_count)
return 0;
*v = ps2_mouse_tx_fifo[ps2_mouse_tx_rd];
ps2_mouse_tx_rd = ps2_mouse_tx_rd + 1;
if (ps2_mouse_tx_rd >= PS2_TX_FIFO_SIZE)
ps2_mouse_tx_rd = 0;
ps2_mouse_tx_count = ps2_mouse_tx_count - 1;
return 1;
}
int IRAM_ATTR ps2_mouse_clamp_9bit(int v)
{
if (v > 255)
return 255;
if (v < -255)
return -255;
return v;
}
void IRAM_ATTR ps2_mouse_build_packet_isr(uint8_t packet[3], bool clear_motion)
{
int dx = ps2_mouse_clamp_9bit(ps2_mouse_dx_accum);
int dy = ps2_mouse_clamp_9bit(ps2_mouse_dy_accum);
packet[0] = 0x08 | (ps2_mouse_buttons & 0x07);
if (dx < 0)
packet[0] |= 0x10;
if (dy < 0)
packet[0] |= 0x20;
if (dx != ps2_mouse_dx_accum)
packet[0] |= 0x40;
if (dy != ps2_mouse_dy_accum)
packet[0] |= 0x80;
packet[1] = (uint8_t)(dx & 0xFF);
packet[2] = (uint8_t)(dy & 0xFF);
if (clear_motion)
{
ps2_mouse_dx_accum = 0;
ps2_mouse_dy_accum = 0;
ps2_mouse_motion_pending = false;
}
}
void IRAM_ATTR ps2_mouse_queue_motion_if_possible_isr()
{
uint8_t packet[3];
if (!ps2_mouse_streaming)
return;
if (ps2_mouse_remote_mode)
return;
if (!ps2_mouse_motion_pending)
return;
if (ps2_mouse_tx_count > (PS2_TX_FIFO_SIZE - 3))
return;
ps2_mouse_build_packet_isr(packet, true);
ps2_mouse_tx_push_isr(packet[0]);
ps2_mouse_tx_push_isr(packet[1]);
ps2_mouse_tx_push_isr(packet[2]);
}
int IRAM_ATTR ps2_mouse_get_tx_bit_isr(int bit_index)
{
if (bit_index == 0)
return 0;
if (bit_index >= 1 && bit_index <= 8)
return (ps2_mouse_tx_byte >> (bit_index - 1)) & 1;
if (bit_index == 9)
return ps2_mouse_tx_parity;
return 1;
}
void IRAM_ATTR ps2_mouse_drive_tx_bit_isr(int bit_index)
{
if (ps2_mouse_get_tx_bit_isr(bit_index))
ps2_mouse_data_release();
else
ps2_mouse_data_low();
}
void IRAM_ATTR ps2_mouse_prepare_tx_byte_isr(uint8_t v)
{
ps2_mouse_tx_byte = v;
ps2_mouse_tx_parity = 1;
for (int i = 0; i < 8; i++)
ps2_mouse_tx_parity ^= (uint8_t)((v >> i) & 1);
ps2_mouse_tx_bit = 0;
ps2_mouse_bus_mode = PS2_BUS_DEV_TX;
ps2_mouse_clk_release();
ps2_mouse_drive_tx_bit_isr(0);
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_DEV_TX_FALL, PS2_TX_SETUP_US);
}
int IRAM_ATTR ps2_mouse_host_inhibit_active_isr()
{
uint32_t now;
if (ps2_mouse_clk_level())
return 0;
now = ps2_mouse_get_time_us_isr();
if ((uint32_t)(now - ps2_mouse_clk_low_since_us) < PS2_HOST_INHIBIT_US)
return 0;
return 1;
}
void IRAM_ATTR ps2_mouse_dev_tx_abort_isr()
{
if (ps2_mouse_bus_mode != PS2_BUS_DEV_TX)
return;
ps2_mouse_tx_resume_valid = true;
ps2_mouse_tx_resume_byte = ps2_mouse_tx_byte;
ps2_mouse_data_release();
ps2_mouse_clk_release();
ps2_mouse_bus_mode = PS2_BUS_IDLE;
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_TX_RETRY, PS2_TX_RETRY_US);
}
void IRAM_ATTR ps2_mouse_try_start_tx_isr()
{
uint8_t v = 0;
if (!ps2_mouse_active)
return;
if (ps2_mouse_bus_mode != PS2_BUS_IDLE)
return;
ps2_mouse_queue_motion_if_possible_isr();
if (!ps2_mouse_tx_resume_valid && !ps2_mouse_tx_count)
return;
if (!ps2_mouse_line_idle())
{
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_TX_RETRY, PS2_TX_RETRY_US);
return;
}
if (!ps2_mouse_tx_pop_isr(&v))
return;
ps2_mouse_prepare_tx_byte_isr(v);
}
void IRAM_ATTR ps2_mouse_start_host_rx_isr()
{
if (!ps2_mouse_active)
return;
if (ps2_mouse_bus_mode != PS2_BUS_IDLE)
return;
ps2_mouse_bus_mode = PS2_BUS_HOST_RX;
ps2_mouse_rx_byte = 0;
ps2_mouse_rx_parity = 1;
ps2_mouse_rx_bit = 0;
ps2_mouse_rx_start_ok = true;
ps2_mouse_rx_parity_ok = false;
ps2_mouse_rx_stop_ok = false;
ps2_mouse_rx_done_byte = 0;
ps2_mouse_rx_frame_ok = false;
ps2_mouse_data_release();
ps2_mouse_clk_release();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_HOST_RX_FALL, PS2_HOST_RX_START_US);
}
void IRAM_ATTR ps2_mouse_host_rx_sample_isr()
{
int dat = ps2_mouse_data_level();
if (ps2_mouse_rx_bit >= 0 && ps2_mouse_rx_bit <= 7)
{
if (dat)
ps2_mouse_rx_byte |= (uint8_t)(1U << ps2_mouse_rx_bit);
ps2_mouse_rx_parity ^= (uint8_t)dat;
}
else if (ps2_mouse_rx_bit == 8)
{
ps2_mouse_rx_parity_ok = ((uint8_t)dat == ps2_mouse_rx_parity);
}
else if (ps2_mouse_rx_bit == 9)
{
ps2_mouse_rx_stop_ok = (dat == 1);
ps2_mouse_rx_done_byte = ps2_mouse_rx_byte;
ps2_mouse_rx_frame_ok = ps2_mouse_rx_start_ok && ps2_mouse_rx_parity_ok && ps2_mouse_rx_stop_ok;
}
ps2_mouse_rx_bit = ps2_mouse_rx_bit + 1;
}
void IRAM_ATTR ps2_mouse_handle_cmd_isr(uint8_t value)
{
uint8_t packet[3];
uint8_t status = 0;
if (ps2_mouse_expect_param)
{
ps2_mouse_expect_param = false;
if (ps2_mouse_pending_cmd == 0xF3)
ps2_mouse_sample_rate = value;
if (ps2_mouse_pending_cmd == 0xE8)
ps2_mouse_resolution = value & 0x03;
ps2_mouse_pending_cmd = 0;
ps2_mouse_tx_push_isr(0xFA);
ps2_mouse_try_start_tx_isr();
return;
}
switch (value)
{
case 0xFF:
ps2_mouse_reset_device_state_isr();
ps2_mouse_tx_push_isr(0xFA);
ps2_mouse_tx_push_isr(0xAA);
ps2_mouse_tx_push_isr(0x00);
break;
case 0xF6:
ps2_mouse_reset_device_state_isr();
ps2_mouse_tx_push_isr(0xFA);
break;
case 0xF5:
ps2_mouse_streaming = false;
ps2_mouse_tx_push_isr(0xFA);
break;
case 0xF4:
ps2_mouse_streaming = true;
ps2_mouse_remote_mode = false;
ps2_mouse_tx_push_isr(0xFA);
ps2_mouse_queue_motion_if_possible_isr();
break;
case 0xF2:
ps2_mouse_tx_push_isr(0xFA);
ps2_mouse_tx_push_isr(0x00);
break;
case 0xEB:
ps2_mouse_tx_push_isr(0xFA);
ps2_mouse_build_packet_isr(packet, true);
ps2_mouse_tx_push_isr(packet[0]);
ps2_mouse_tx_push_isr(packet[1]);
ps2_mouse_tx_push_isr(packet[2]);
break;
case 0xEA:
ps2_mouse_remote_mode = false;
ps2_mouse_tx_push_isr(0xFA);
break;
case 0xF0:
ps2_mouse_remote_mode = true;
ps2_mouse_streaming = false;
ps2_mouse_tx_push_isr(0xFA);
break;
case 0xE6:
ps2_mouse_scaling21 = false;
ps2_mouse_tx_push_isr(0xFA);
break;
case 0xE7:
ps2_mouse_scaling21 = true;
ps2_mouse_tx_push_isr(0xFA);
break;
case 0xE9:
if (ps2_mouse_scaling21)
status |= 0x10;
if (ps2_mouse_streaming)
status |= 0x20;
if (ps2_mouse_remote_mode)
status |= 0x40;
status |= (ps2_mouse_buttons & 0x07);
ps2_mouse_tx_push_isr(0xFA);
ps2_mouse_tx_push_isr(status);
ps2_mouse_tx_push_isr(ps2_mouse_resolution);
ps2_mouse_tx_push_isr(ps2_mouse_sample_rate);
break;
case 0xF3:
ps2_mouse_expect_param = true;
ps2_mouse_pending_cmd = 0xF3;
ps2_mouse_tx_push_isr(0xFA);
break;
case 0xE8:
ps2_mouse_expect_param = true;
ps2_mouse_pending_cmd = 0xE8;
ps2_mouse_tx_push_isr(0xFA);
break;
default:
ps2_mouse_tx_push_isr(0xFE);
break;
}
ps2_mouse_try_start_tx_isr();
}
bool IRAM_ATTR ps2_mouse_timer_cb(gptimer_handle_t, const gptimer_alarm_event_data_t *, void *)
{
portENTER_CRITICAL_ISR(&ps2_mouse_lock);
if (!ps2_mouse_active)
{
portEXIT_CRITICAL_ISR(&ps2_mouse_lock);
return false;
}
switch (ps2_mouse_timer_evt)
{
case PS2_TIMER_EVT_DEV_TX_FALL:
if (ps2_mouse_bus_mode == PS2_BUS_DEV_TX)
{
if (ps2_mouse_host_inhibit_active_isr())
{
ps2_mouse_dev_tx_abort_isr();
break;
}
ps2_mouse_clk_low();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_DEV_TX_RISE, PS2_CLK_LOW_US);
}
break;
case PS2_TIMER_EVT_DEV_TX_RISE:
if (ps2_mouse_bus_mode == PS2_BUS_DEV_TX)
{
ps2_mouse_clk_release();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_DEV_TX_NEXT, PS2_TX_HIGH_HOLD_US);
}
break;
case PS2_TIMER_EVT_DEV_TX_NEXT:
if (ps2_mouse_bus_mode == PS2_BUS_DEV_TX)
{
if (ps2_mouse_host_inhibit_active_isr())
{
ps2_mouse_dev_tx_abort_isr();
break;
}
ps2_mouse_tx_bit = ps2_mouse_tx_bit + 1;
if (ps2_mouse_tx_bit <= 10)
{
ps2_mouse_drive_tx_bit_isr(ps2_mouse_tx_bit);
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_DEV_TX_FALL, PS2_TX_SETUP_US);
}
else
{
ps2_mouse_data_release();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_DEV_TX_DONE, PS2_TX_SETUP_US);
}
}
break;
case PS2_TIMER_EVT_DEV_TX_DONE:
if (ps2_mouse_bus_mode == PS2_BUS_DEV_TX)
{
ps2_mouse_data_release();
ps2_mouse_clk_release();
ps2_mouse_bus_mode = PS2_BUS_IDLE;
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_TX_RETRY, PS2_TX_INTERBYTE_US);
}
break;
case PS2_TIMER_EVT_HOST_RX_FALL:
if (ps2_mouse_bus_mode == PS2_BUS_HOST_RX)
{
ps2_mouse_data_release();
ps2_mouse_clk_low();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_HOST_RX_RISE, PS2_CLK_LOW_US);
}
break;
case PS2_TIMER_EVT_HOST_RX_RISE:
if (ps2_mouse_bus_mode == PS2_BUS_HOST_RX)
{
ps2_mouse_clk_release();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_HOST_RX_SAMPLE, PS2_HOST_RX_SAMPLE_US);
}
break;
case PS2_TIMER_EVT_HOST_RX_SAMPLE:
if (ps2_mouse_bus_mode == PS2_BUS_HOST_RX)
{
ps2_mouse_host_rx_sample_isr();
if (ps2_mouse_rx_bit <= 9)
{
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_HOST_RX_FALL, PS2_CLK_HIGH_US - PS2_HOST_RX_SAMPLE_US);
}
else
{
ps2_mouse_bus_mode = PS2_BUS_HOST_ACK;
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_HOST_ACK_PREPARE, PS2_CLK_HIGH_US - PS2_HOST_RX_SAMPLE_US);
}
}
break;
case PS2_TIMER_EVT_HOST_ACK_PREPARE:
if (ps2_mouse_bus_mode == PS2_BUS_HOST_ACK)
{
if (ps2_mouse_rx_frame_ok)
ps2_mouse_data_low();
else
ps2_mouse_data_release();
ps2_mouse_clk_release();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_HOST_ACK_FALL, PS2_HOST_ACK_SETUP_US);
}
break;
case PS2_TIMER_EVT_HOST_ACK_FALL:
if (ps2_mouse_bus_mode == PS2_BUS_HOST_ACK)
{
ps2_mouse_clk_low();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_HOST_ACK_RISE, PS2_CLK_LOW_US);
}
break;
case PS2_TIMER_EVT_HOST_ACK_RISE:
if (ps2_mouse_bus_mode == PS2_BUS_HOST_ACK)
{
ps2_mouse_clk_release();
ps2_mouse_timer_schedule_isr(PS2_TIMER_EVT_HOST_ACK_DONE, PS2_CLK_HIGH_US);
}
break;
case PS2_TIMER_EVT_HOST_ACK_DONE:
if (ps2_mouse_bus_mode == PS2_BUS_HOST_ACK)
{
ps2_mouse_data_release();
ps2_mouse_clk_release();
ps2_mouse_bus_mode = PS2_BUS_IDLE;
if (ps2_mouse_rx_frame_ok)
ps2_mouse_handle_cmd_isr(ps2_mouse_rx_done_byte);
else
ps2_mouse_try_start_tx_isr();
}
break;
case PS2_TIMER_EVT_TX_RETRY:
if (ps2_mouse_bus_mode == PS2_BUS_IDLE)
ps2_mouse_try_start_tx_isr();
break;
default:
break;
}
portEXIT_CRITICAL_ISR(&ps2_mouse_lock);
return false;
}
void IRAM_ATTR ps2_mouse_clk_isr(void *)
{
int clk;
int dat;
uint32_t now;
uint32_t low_time;
portENTER_CRITICAL_ISR(&ps2_mouse_lock);
if (!ps2_mouse_active)
{
portEXIT_CRITICAL_ISR(&ps2_mouse_lock);
return;
}
clk = ps2_mouse_clk_level();
dat = ps2_mouse_data_level();
now = ps2_mouse_get_time_us_isr();
if (!clk)
{
ps2_mouse_clk_low_since_us = now;
portEXIT_CRITICAL_ISR(&ps2_mouse_lock);
return;
}
low_time = now - ps2_mouse_clk_low_since_us;
if (ps2_mouse_bus_mode == PS2_BUS_IDLE)
{
if (low_time >= PS2_HOST_INHIBIT_US && !dat)
{
ps2_mouse_start_host_rx_isr();
portEXIT_CRITICAL_ISR(&ps2_mouse_lock);
return;
}
}
portEXIT_CRITICAL_ISR(&ps2_mouse_lock);
}
void IRAM_ATTR ps2_mouse_data_isr(void *)
{
}
esp_err_t ps2_mouse_init()
{
esp_err_t err;
gpio_config_t io = {};
gptimer_config_t tcfg = {};
gptimer_event_callbacks_t tcbs = {};
io.pin_bit_mask = (1ULL << PS2_MOUSE_GPIO_DATA) | (1ULL << PS2_MOUSE_GPIO_CLK);
io.mode = GPIO_MODE_INPUT_OUTPUT_OD;
io.pull_up_en = GPIO_PULLUP_ENABLE;
io.pull_down_en = GPIO_PULLDOWN_DISABLE;
io.intr_type = GPIO_INTR_DISABLE;
err = gpio_config(&io);
if (err != ESP_OK) return err;
err = gpio_set_pull_mode(PS2_MOUSE_GPIO_DATA, GPIO_PULLUP_ONLY);
if (err != ESP_OK) return err;
err = gpio_set_pull_mode(PS2_MOUSE_GPIO_CLK, GPIO_PULLUP_ONLY);
if (err != ESP_OK) return err;
ps2_mouse_data_release();
ps2_mouse_clk_release();
tcfg.clk_src = GPTIMER_CLK_SRC_DEFAULT;
tcfg.direction = GPTIMER_COUNT_UP;
tcfg.resolution_hz = 1000000;
err = gptimer_new_timer(&tcfg, &ps2_mouse_timer);
if (err != ESP_OK) return err;
tcbs.on_alarm = ps2_mouse_timer_cb;
err = gptimer_register_event_callbacks(ps2_mouse_timer, &tcbs, NULL);
if (err != ESP_OK) return err;
err = gptimer_enable(ps2_mouse_timer);
if (err != ESP_OK) return err;
err = gptimer_start(ps2_mouse_timer);
if (err != ESP_OK) return err;
if (!ps2_mouse_gpio_isr_service_ready)
{
err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
if (err == ESP_ERR_INVALID_STATE)
ps2_mouse_gpio_isr_service_ready = true;
else if (err != ESP_OK)
return err;
else
ps2_mouse_gpio_isr_service_ready = true;
}
err = gpio_set_intr_type(PS2_MOUSE_GPIO_CLK, GPIO_INTR_ANYEDGE);
if (err != ESP_OK) return err;
err = gpio_set_intr_type(PS2_MOUSE_GPIO_DATA, GPIO_INTR_ANYEDGE);
if (err != ESP_OK) return err;
err = gpio_isr_handler_add(PS2_MOUSE_GPIO_CLK, ps2_mouse_clk_isr, NULL);
if (err != ESP_OK) return err;
err = gpio_isr_handler_add(PS2_MOUSE_GPIO_DATA, ps2_mouse_data_isr, NULL);
if (err != ESP_OK) return err;
ps2_mouse_clk_low_since_us = 0;
return ESP_OK;
}
void ps2_mouse_deinit()
{
gpio_intr_disable(PS2_MOUSE_GPIO_CLK);
gpio_intr_disable(PS2_MOUSE_GPIO_DATA);
gpio_isr_handler_remove(PS2_MOUSE_GPIO_CLK);
gpio_isr_handler_remove(PS2_MOUSE_GPIO_DATA);
ps2_mouse_data_release();
ps2_mouse_clk_release();
if (ps2_mouse_timer)
{
gptimer_stop(ps2_mouse_timer);
gptimer_disable(ps2_mouse_timer);
gptimer_del_timer(ps2_mouse_timer);
ps2_mouse_timer = NULL;
}
}
bool ps2_mouse_is_active()
{
return ps2_mouse_active;
}
esp_err_t ps2_mouse_send_movement(int dx, int dy, unsigned buttons)
{
if (!ps2_mouse_active)
return ESP_ERR_INVALID_STATE;
portENTER_CRITICAL(&ps2_mouse_lock);
if (!ps2_mouse_streaming || ps2_mouse_remote_mode)
{
portEXIT_CRITICAL(&ps2_mouse_lock);
return ESP_ERR_INVALID_STATE;
}
ps2_mouse_dx_accum += dx;
ps2_mouse_dy_accum += dy;
ps2_mouse_buttons = (uint8_t)(buttons & 0x07);
ps2_mouse_motion_pending = true;
ps2_mouse_queue_motion_if_possible_isr();
ps2_mouse_try_start_tx_isr();
portEXIT_CRITICAL(&ps2_mouse_lock);
return ESP_OK;
}
esp_err_t ps2_mouse_start()
{
esp_err_t err;
if (ps2_mouse_active)
return ESP_OK;
portENTER_CRITICAL(&ps2_mouse_lock);
ps2_mouse_active = true;
ps2_mouse_reset_protocol_state_isr();
portEXIT_CRITICAL(&ps2_mouse_lock);
err = ps2_mouse_init();
if (err != ESP_OK)
{
portENTER_CRITICAL(&ps2_mouse_lock);
ps2_mouse_active = false;
portEXIT_CRITICAL(&ps2_mouse_lock);
ps2_mouse_deinit();
return err;
}
portENTER_CRITICAL(&ps2_mouse_lock);
ps2_mouse_streaming = true;
ps2_mouse_remote_mode = false;
ps2_mouse_expect_param = false;
ps2_mouse_pending_cmd = 0;
portEXIT_CRITICAL(&ps2_mouse_lock);
ESP_LOGI("ps2_mouse", "ps/2 mouse started on GPIO%u=data, GPIO%u=clk, streaming enabled", PS2_MOUSE_GPIO_DATA, PS2_MOUSE_GPIO_CLK);
return ESP_OK;
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "esp_err.h"
esp_err_t ps2_mouse_start();
esp_err_t ps2_mouse_send_movement(int dx, int dy, unsigned buttons);
bool ps2_mouse_is_active();
+1207
View File
File diff suppressed because it is too large Load Diff
+99
View File
@@ -0,0 +1,99 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "esp_err.h"
#include "esp_log.h"
#include "driver/sdmmc_host.h"
#include "sdmmc_cmd.h"
#include "sdmmc.h"
#if defined(CONFIG_IDF_TARGET_ESP32P4)
#define SD_SLOT SDMMC_HOST_SLOT_1
#define SD_CLK GPIO_NUM_43
#define SD_CMD GPIO_NUM_44
#define SD_D0 GPIO_NUM_39
#define SD_D1 GPIO_NUM_40
#define SD_D2 GPIO_NUM_41
#define SD_D3 GPIO_NUM_42
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
#define SD_SLOT SDMMC_HOST_SLOT_1
#define SD_CLK GPIO_NUM_40
#define SD_CMD GPIO_NUM_39
#define SD_D0 GPIO_NUM_38
#define SD_D1 GPIO_NUM_2
#define SD_D2 GPIO_NUM_41
#define SD_D3 GPIO_NUM_42
#endif
#ifndef SDMMC_CMD13_SEND_STATUS
#define SDMMC_CMD13_SEND_STATUS 13U
#endif
#ifndef SDMMC_ARG_RCA
#define SDMMC_ARG_RCA(rca_) ((uint32_t)(rca_) << 16)
#endif
#ifndef SDMMC_R1
#define SDMMC_R1(resp_) ((uint32_t)((resp_)[0]))
#endif
#ifndef SDMMC_CMD7_SELECT_CARD
#define SDMMC_CMD7_SELECT_CARD 7U
#endif
#define SD_ST_OUT_OF_RANGE (1U << 31)
#define SD_ST_ADDRESS_ERROR (1U << 30)
#define SD_ST_BLOCK_LEN_ERROR (1U << 29)
#define SD_ST_ERASE_SEQ_ERROR (1U << 28)
#define SD_ST_ERASE_PARAM (1U << 27)
#define SD_ST_WP_VIOLATION (1U << 26)
#define SD_ST_CARD_IS_LOCKED (1U << 25)
#define SD_ST_LOCK_UNLOCK_FAILED (1U << 24)
#define SD_ST_COM_CRC_ERROR (1U << 23)
#define SD_ST_ILLEGAL_COMMAND (1U << 22)
#define SD_ST_CARD_ECC_FAILED (1U << 21)
#define SD_ST_CC_ERROR (1U << 20)
#define SD_ST_ERROR (1U << 19)
#define SD_ST_CSD_OVERWRITE (1U << 16)
#define SD_ST_WP_ERASE_SKIP (1U << 15)
#define SD_ST_ERASE_RESET (1U << 13)
extern sdmmc_host_t sd_host;
extern sdmmc_slot_config_t sd_slot;
extern sdmmc_card_t sd_card;
extern sdmmc_card_t *sd_card_ptr;
extern bool sd_initialized;
extern bool sd_fs_mounted;
extern char sd_fs_base_path[32];
extern sdmmc_card_t *sd_fs_card;
esp_err_t sd_init();
esp_err_t sd_reinit();
esp_err_t sd_ensure_ready();
esp_err_t sd_probe_card();
esp_err_t sd_fs_sense(const char *base_path);
esp_log_level_t sd_host_log_suppress_begin();
void sd_host_log_suppress_end(esp_log_level_t old_sd_host_level);
void sd_fs_unmount_force();
void sd_deinit();
esp_err_t sd_read_sectors(uint32_t sec, uint32_t num);
esp_err_t sd_card_erase();
esp_err_t sd_fs_mount(const char *base_path, sdmmc_card_t **out_card);
esp_err_t sd_fs_mount_quiet(const char *base_path, sdmmc_card_t **out_card);
void sd_fs_unmount(const char *base_path, sdmmc_card_t *card);
esp_err_t sd_fs_list_dir(const char *base_path, const char *path);
esp_err_t sd_fs_read_file(const char *base_path, const char *path, void *dst, size_t dst_size, size_t *out_size);
int sd_fs_build_full_path(const char *base_path, const char *path, char *full, size_t full_size);
bool sd_has_retryable_errno(int err);
bool sd_error_needs_reinit(esp_err_t err);
void sd_log_cid(const sdmmc_cid_t *cid);
void sd_log_ocr(const sdmmc_card_t *card);
void sd_log_scr(const sdmmc_card_t *card);
void sdmmc_console_register_system_commands();
#if defined(CONFIG_IDF_TARGET_ESP32P4)
void sd_ldo_init();
#endif
+1400
View File
File diff suppressed because it is too large Load Diff
+75
View File
@@ -0,0 +1,75 @@
#pragma once
#include "esp_err.h"
#include "main.h"
#define SFX_CHANNELS 32
#define SFX_VOLUME_MAX 255
#define SFX_PAN_CENTER 128
#define SFX_PITCH_ONE 0x0100
typedef struct
{
const u8 *data;
u32 data_size;
u32 frame_count;
u32 sample_rate;
u16 block_align;
u16 bits_per_sample;
u8 channels;
u8 is_signed;
} SFX_WAV_INFO;
typedef struct
{
u8 active;
u8 handle;
u8 group;
u8 group_order;
u8 volume;
u8 pan;
u16 pitch;
float position;
SFX_WAV_INFO wav;
} SFX_CHANNEL;
typedef struct
{
u8 active;
u8 handle;
u8 group;
u8 group_order;
u8 volume;
u8 pan;
u16 pitch;
u32 sample_rate;
u32 frame_count;
u32 position;
u16 bits_per_sample;
u8 channels;
u8 is_signed;
} SFX_CHANNEL_STATE;
extern SFX_CHANNEL sfx_channels[SFX_CHANNELS];
extern u8 sfx_global_volume;
void sfx_init();
esp_err_t sfx_parse_wav_object(int handle, SFX_WAV_INFO *info);
esp_err_t sfx_load_file(const char *path, int *out_handle, SFX_WAV_INFO *out_info);
esp_err_t sfx_play(int handle, u8 group, u8 volume, u8 pan, u16 pitch, int *out_channel);
esp_err_t sfx_stop(int channel);
esp_err_t sfx_stop_group(u8 group);
esp_err_t sfx_stop_handle(int handle);
esp_err_t sfx_set_params(int channel, u8 volume, u8 pan, u16 pitch);
esp_err_t sfx_set_volume(u8 volume);
esp_err_t sfx_get_state(int channel, SFX_CHANNEL_STATE *state);
esp_err_t sfx_get_state_snapshot(int channel, SFX_CHANNEL_STATE *state);
esp_err_t sfx_play_sync(int handle, u8 group, u8 volume, u8 pan, u16 pitch, int *out_channel);
esp_err_t sfx_stop_sync(int channel);
esp_err_t sfx_stop_group_sync(u8 group);
esp_err_t sfx_stop_handle_sync(int handle);
esp_err_t sfx_set_params_sync(int channel, u8 volume, u8 pan, u16 pitch);
esp_err_t sfx_set_volume_sync(u8 volume);
esp_err_t sfx_get_state_sync(int channel, SFX_CHANNEL_STATE *state);
void sfx_render(float *mix, int sample_count, int output_sample_rate);
void sfx_console_register_system_commands();
File diff suppressed because it is too large Load Diff
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include "freertos/FreeRTOS.h"
#define DMA_BUF_SIZE 16384
enum
{
DREQ_WSCAN,
DREQ_DATA,
DREQ_ZIP,
DREQ_RND,
DREQ_URL,
DREQ_STREAM,
DREQ_TRACK_STREAM,
DREQ_VFS_REQUEST,
DREQ_VFS_RESULT,
};
u8 rd_reg8(u8 reg);
u32 rd_reg32(u8 reg);
void rd_regs(u8 reg, const void *data, int size);
void wr_reg8(u8 reg, u8 val);
void wr_reg32(u8 reg, u32 val);
void wr_regs(u8 reg, const void *data, int size);
void init_spi_configs();
void init_slave_hd();
esp_err_t spi_switch_to_master();
esp_err_t spi_switch_to_slave();
void spi_master_set_clock_hz(u32 hz);
u32 spi_master_get_actual_freq_hz();
esp_err_t spi_master_set_data_lines(u8 lines);
esp_err_t spi_master_write_buf(u8 cmd, u16 addr, const void *tx_data, size_t size);
esp_err_t spi_master_read_buf(u8 cmd, u16 addr, void *rx_data, size_t size);
esp_err_t spi_master_xfer(void *tx_data, void *rx_data, size_t size);
esp_err_t spi_master_write_inline(u8 cmd, u16 addr, u32 value, size_t size);
esp_err_t spi_master_read_inline(u8 cmd, u16 addr, u32 *value, size_t size);
esp_err_t spi_master_host_cmd_inline(u8 cmd, u8 param);
bool spi_master_bg_is_busy();
bool spi_master_bg_is_done();
esp_err_t spi_master_bg_get_result();
esp_err_t spi_master_bg_wait_done(TickType_t ticks_to_wait);
esp_err_t spi_master_write_buf_bg(u8 cmd, u16 addr, const void *tx_data, size_t size);
esp_err_t spi_master_read_buf_bg(u8 cmd, u16 addr, void *rx_data, size_t size);
void spi_master_bg_task(void *arg);
void command();
void sender_task(void *arg);
void receiver_task(void *arg);
void put_rxq(int type);
void put_txq(int type);
u32 prepare_tx_data(u8 type, size_t size);
void process_rx_data(u8 type, size_t size);
void set_status(u8 err);
u8* get_dma_buf(void);
void spi_slave_dma_send_direct(size_t len);
void wait_status(u8 status);
void wait_not_status(u8 status);
+80
View File
@@ -0,0 +1,80 @@
#include <limits.h>
#include "esp_timer.h"
#include "stats.h"
namespace stats
{
STATS_t _st = {};
int64_t start_t;
u32 last_cmd_exec_time;
void reset_audio_render()
{
_st.audio_tracker_last_us = 0;
_st.audio_tracker_min_us = INT_MAX;
_st.audio_tracker_max_us = 0;
_st.audio_tracker_last_cpu_x10 = 0;
_st.audio_tracker_min_cpu_x10 = INT_MAX;
_st.audio_tracker_max_cpu_x10 = 0;
_st.audio_opl_last_us = 0;
_st.audio_opl_min_us = INT_MAX;
_st.audio_opl_max_us = 0;
_st.audio_opl_last_cpu_x10 = 0;
_st.audio_opl_min_cpu_x10 = INT_MAX;
_st.audio_opl_max_cpu_x10 = 0;
_st.audio_sfx_last_us = 0;
_st.audio_sfx_min_us = INT_MAX;
_st.audio_sfx_max_us = 0;
_st.audio_sfx_last_cpu_x10 = 0;
_st.audio_sfx_min_cpu_x10 = INT_MAX;
_st.audio_sfx_max_cpu_x10 = 0;
_st.audio_total_last_us = 0;
_st.audio_total_min_us = INT_MAX;
_st.audio_total_max_us = 0;
_st.audio_total_last_cpu_x10 = 0;
_st.audio_total_min_cpu_x10 = INT_MAX;
_st.audio_total_max_cpu_x10 = 0;
}
void init()
{
_st.drq_data_start_min_us = INT_MAX;
_st.drq_data_end_min_us = INT_MAX;
reset_audio_render();
}
void update_audio_render(int *last_us, int *min_us, int *max_us, int *last_cpu_x10, int *min_cpu_x10, int *max_cpu_x10, int us, int sample_rate, int sample_count)
{
int cpu_x10 = 0;
if (sample_count > 0)
cpu_x10 = (int)((int64_t)us * sample_rate * 10 / sample_count / 10000);
*last_us = us;
if (us < *min_us) *min_us = us;
if (us > *max_us) *max_us = us;
*last_cpu_x10 = cpu_x10;
if (cpu_x10 < *min_cpu_x10) *min_cpu_x10 = cpu_x10;
if (cpu_x10 > *max_cpu_x10) *max_cpu_x10 = cpu_x10;
}
void set_start()
{
start_t = esp_timer_get_time();
}
void set_end()
{
last_cmd_exec_time = esp_timer_get_time() - start_t;
}
u32 get_time()
{
return last_cmd_exec_time;
}
};
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include "main.h"
typedef struct
{
int drq_data_t;
int drq_data_start_last_us;
int drq_data_start_min_us;
int drq_data_start_max_us;
int drq_data_end_last_us;
int drq_data_end_min_us;
int drq_data_end_max_us;
int audio_tracker_last_us;
int audio_tracker_min_us;
int audio_tracker_max_us;
int audio_tracker_last_cpu_x10;
int audio_tracker_min_cpu_x10;
int audio_tracker_max_cpu_x10;
int audio_opl_last_us;
int audio_opl_min_us;
int audio_opl_max_us;
int audio_opl_last_cpu_x10;
int audio_opl_min_cpu_x10;
int audio_opl_max_cpu_x10;
int audio_sfx_last_us;
int audio_sfx_min_us;
int audio_sfx_max_us;
int audio_sfx_last_cpu_x10;
int audio_sfx_min_cpu_x10;
int audio_sfx_max_cpu_x10;
int audio_total_last_us;
int audio_total_min_us;
int audio_total_max_us;
int audio_total_last_cpu_x10;
int audio_total_min_cpu_x10;
int audio_total_max_cpu_x10;
float xm_samp_min;
float xm_samp_max;
char runtime_stats_buffer[2048];
} STATS_t;
namespace stats
{
extern STATS_t _st;
void init();
void reset_audio_render();
void update_audio_render(int *last_us, int *min_us, int *max_us, int *last_cpu_x10, int *min_cpu_x10, int *max_cpu_x10, int us, int sample_rate, int sample_count);
void set_start();
void set_end();
u32 get_time();
};
+502
View File
@@ -0,0 +1,502 @@
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_http_client.h"
#include "esp_crt_bundle.h"
#include "main.h"
#include "esp_spi_defs.h"
#include "spi_slave.h"
#include "helper.h"
#include "stream_client.h"
static const char * TAG = "stream";
#define RING_BUF_SIZE (64 * 1024)
static uint8_t * ring_buf = NULL;
static volatile uint32_t ring_head = 0;
static volatile uint32_t ring_tail = 0;
static volatile bool ring_eof = false;
static SemaphoreHandle_t ring_mutex = NULL;
static TaskHandle_t reader_task_handle = NULL;
static int stream_sock = -1;
static esp_http_client_handle_t stream_http = NULL;
static uint32_t stream_total_size = 0;
static uint32_t stream_read_size = 0;
static bool stream_is_gopher = false;
static void reader_task(void *arg);
static uint32_t ring_available(void)
{
uint32_t h = ring_head;
uint32_t t = ring_tail;
if (h >= t) return h - t;
return RING_BUF_SIZE - t + h;
}
static uint32_t ring_free(void)
{
return RING_BUF_SIZE - ring_available() - 1;
}
static esp_err_t stream_ring_alloc(void)
{
if (ring_buf) return ESP_OK;
ring_buf = (uint8_t*)heap_caps_malloc(RING_BUF_SIZE, MALLOC_CAP_SPIRAM);
if (!ring_buf)
{
ESP_LOGE(TAG, "ring buffer alloc failed");
return ESP_ERR_NO_MEM;
}
ESP_LOGI(TAG, "ring buffer allocated: %p", ring_buf);
return ESP_OK;
}
static void stream_ring_free(void)
{
if (!ring_buf) return;
heap_caps_free(ring_buf);
ring_buf = NULL;
ESP_LOGI(TAG, "ring buffer freed");
}
static void stream_ring_reset(void)
{
xSemaphoreTake(ring_mutex, portMAX_DELAY);
ring_head = 0;
ring_tail = 0;
ring_eof = false;
xSemaphoreGive(ring_mutex);
}
static void stream_transport_close(void)
{
if (stream_http)
{
esp_http_client_close(stream_http);
esp_http_client_cleanup(stream_http);
stream_http = NULL;
}
if (stream_sock >= 0)
{
close(stream_sock);
stream_sock = -1;
}
}
static esp_err_t stream_reader_start(void)
{
esp_err_t err = stream_ring_alloc();
if (err != ESP_OK) return err;
stream_ring_reset();
stream_read_size = 0;
if (xTaskCreatePinnedToCoreWithCaps(reader_task, "stream_reader", 4096, NULL, STREAMER_TASK_PRIO, &reader_task_handle, 0, task_ram_type_non_critical) == pdPASS)
return ESP_OK;
stream_ring_free();
return ESP_FAIL;
}
static void stream_state_reset(void)
{
stream_total_size = 0;
stream_read_size = 0;
stream_is_gopher = false;
stream_ring_reset();
}
static void reader_task(void *arg)
{
uint8_t temp_buf[512];
while (true)
{
int len = 0;
if (stream_http)
{
len = esp_http_client_read(stream_http, (char*)temp_buf, sizeof(temp_buf));
}
else if (stream_sock >= 0)
{
len = read(stream_sock, temp_buf, sizeof(temp_buf));
}
if (len <= 0)
{
xSemaphoreTake(ring_mutex, portMAX_DELAY);
ring_eof = true;
xSemaphoreGive(ring_mutex);
ESP_LOGI(TAG, "reader: EOF, total=%u", stream_read_size);
break;
}
stream_read_size += len;
// Wait for space in ring buffer
while (true)
{
xSemaphoreTake(ring_mutex, portMAX_DELAY);
uint32_t free = ring_free();
xSemaphoreGive(ring_mutex);
if (free >= (uint32_t) len) break;
vTaskDelay(1);
}
// Write to ring buffer
xSemaphoreTake(ring_mutex, portMAX_DELAY);
uint32_t h = ring_head;
for(int i = 0; i < len; i++)
{
ring_buf[h] = temp_buf[i];
h = (h + 1) % RING_BUF_SIZE;
}
ring_head = h;
xSemaphoreGive(ring_mutex);
}
reader_task_handle = NULL;
vTaskDelete(NULL);
}
void stream_init(void)
{
if (!ring_mutex)
ring_mutex = xSemaphoreCreateMutexWithCaps(task_ram_type_non_critical);
stream_sock = -1;
stream_http = NULL;
reader_task_handle = NULL;
stream_state_reset();
ESP_LOGI(TAG, "init done");
}
static void stream_http_common(bool use_https)
{
ESP_LOGI(TAG, "STREAM_%s_START", use_https ? "HTTPS" : "HTTP");
stream_state_reset();
if (!net.url[0])
{
ESP_LOGW(TAG, "URL not set");
wr_reg8(ESP_REG_STATUS, 0x81);
return;
}
char * url = (char*)net.url;
const char * prefix = use_https ? "https://" : "http://";
int prefix_len = use_https ? 8 : 7;
if (strncmp(url, prefix, prefix_len) != 0)
{
ESP_LOGW(TAG, "invalid URL format");
wr_reg8(ESP_REG_STATUS, 0x81);
return;
}
ESP_LOGI(TAG, "GET %s", url);
esp_http_client_config_t cfg = {};
cfg.url = url;
cfg.timeout_ms = 30000;
cfg.buffer_size = 512;
cfg.user_agent = "ZiFi/1.0";
if (use_https)
cfg.crt_bundle_attach = esp_crt_bundle_attach;
stream_http = esp_http_client_init( & cfg);
if (!stream_http)
{
ESP_LOGW(TAG, "init fail");
wr_reg8(ESP_REG_STATUS, 0xB2);
return;
}
esp_err_t err = esp_http_client_open(stream_http, 0);
if (err != ESP_OK)
{
ESP_LOGW(TAG, "open fail");
esp_http_client_cleanup(stream_http);
stream_http = NULL;
wr_reg8(ESP_REG_STATUS, 0xB2);
return;
}
esp_http_client_fetch_headers(stream_http);
int code = esp_http_client_get_status_code(stream_http);
ESP_LOGI(TAG, "status %d", code);
if (code < 200 || code >= 300)
{
esp_http_client_close(stream_http);
esp_http_client_cleanup(stream_http);
stream_http = NULL;
wr_reg8(ESP_REG_STATUS, 0xB3);
return;
}
int content_length = esp_http_client_get_content_length(stream_http);
if (content_length < 0)
stream_total_size = 0xFFFFFFFF;
else
stream_total_size = (uint32_t) content_length;
ESP_LOGI(TAG, "connected, size=%u", stream_total_size);
if (stream_reader_start() != ESP_OK)
{
stream_transport_close();
wr_reg8(ESP_REG_STATUS, 0xB0);
return;
}
wr_reg32(ESP_REG_DATA_SIZE, stream_total_size);
wr_reg8(ESP_REG_STATUS, ESP_ST_READY);
}
void stream_http_start(void)
{
stream_http_common(false);
}
void stream_https_start(void)
{
stream_http_common(true);
}
void stream_gopher_start(void)
{
ESP_LOGI(TAG, "STREAM_GOPHER_START");
stream_state_reset();
stream_is_gopher = true;
if (!net.url[0])
{
ESP_LOGW(TAG, "URL not set");
wr_reg8(ESP_REG_STATUS, 0x81);
return;
}
char * url = (char*)net.url;
if (strncmp(url, "gopher://", 9) != 0)
{
ESP_LOGW(TAG, "invalid URL format");
wr_reg8(ESP_REG_STATUS, 0x81);
return;
}
ESP_LOGI(TAG, "GET %s", url);
char host[256];
int port = 70;
char selector[256];
memset(host, 0, sizeof(host));
memset(selector, 0, sizeof(selector));
//strcpy(selector, "/");
selector[0] = '\0';
const char * p = url + 9;
const char * slash = strchr(p, '/');
const char * colon = strchr(p, ':');
if (colon && (!slash || colon < slash))
{
int host_len = colon - p;
strncpy(host, p, host_len);
host[host_len] = 0;
port = atoi(colon + 1);
if (slash)
{
const char * sel_start = slash + 1;
// Skip Gopher type character and slash if present (e.g., "9/" -> "")
if (sel_start[0] && sel_start[1] == '/' && strchr("0123456789+gIihs", sel_start[0]))
sel_start += 2;
strncpy(selector, sel_start, sizeof(selector) - 1);
}
}
else if (slash)
{
int host_len = slash - p;
strncpy(host, p, host_len);
host[host_len] = 0;
const char * sel_start = slash + 1;
// Skip Gopher type character and slash if present
if (sel_start[0] && sel_start[1] == '/' && strchr("0123456789+gIihs", sel_start[0]))
sel_start += 2;
strncpy(selector, sel_start, sizeof(selector) - 1);
}
else
strncpy(host, p, sizeof(host) - 1);
struct hostent * server = gethostbyname(host);
if (!server)
{
ESP_LOGW(TAG, "DNS lookup failed");
wr_reg8(ESP_REG_STATUS, 0xB1);
return;
}
stream_sock = socket(AF_INET, SOCK_STREAM, 0);
if (stream_sock < 0)
{
ESP_LOGW(TAG, "socket creation failed");
wr_reg8(ESP_REG_STATUS, 0xB2);
return;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
if (connect(stream_sock, (struct sockaddr*)& serv_addr, sizeof(serv_addr)) < 0)
{
ESP_LOGW(TAG, "connection failed");
close(stream_sock);
stream_sock = -1;
wr_reg8(ESP_REG_STATUS, 0xB2);
return;
}
// Set socket timeout to 30 seconds
struct timeval timeout;
timeout.tv_sec = 30;
timeout.tv_usec = 0;
setsockopt(stream_sock, SOL_SOCKET, SO_RCVTIMEO, & timeout, sizeof(timeout));
char request[512];
snprintf(request, sizeof(request), "/%s\r\n", selector);
write(stream_sock, request, strlen(request));
ESP_LOGI(TAG, "sent selector: [%s]", selector);
// Size unknown from Gopher
stream_total_size = 0xFFFFFFFF;
ESP_LOGI(TAG, "connected");
if (stream_reader_start() != ESP_OK)
{
stream_transport_close();
wr_reg8(ESP_REG_STATUS, 0xB0);
return;
}
wr_reg32(ESP_REG_DATA_SIZE, stream_total_size);
wr_reg8(ESP_REG_STATUS, ESP_ST_READY);
}
void stream_read(void)
{
if (stream_sock < 0 && !stream_http)
{
ESP_LOGW(TAG, "stream not opened");
wr_reg8(ESP_REG_STATUS, 0x82);
return;
}
// Wait for data in ring buffer or EOF
int timeout = 0;
while (timeout < 30000)
{
xSemaphoreTake(ring_mutex, portMAX_DELAY);
uint32_t avail = ring_available();
bool eof = ring_eof;
xSemaphoreGive(ring_mutex);
if (avail > 0 || eof) break;
vTaskDelay(1);
timeout++;
}
xSemaphoreTake(ring_mutex, portMAX_DELAY);
uint32_t avail = ring_available();
bool eof = ring_eof;
if (avail == 0 && eof)
{
xSemaphoreGive(ring_mutex);
ESP_LOGI(TAG, "stream end, total=%u", stream_read_size);
wr_reg32(ESP_REG_DATA_SIZE, 0);
wr_reg8(ESP_REG_STATUS, ESP_ST_READY);
return;
}
if (avail == 0)
{
xSemaphoreGive(ring_mutex);
ESP_LOGW(TAG, "timeout waiting for data");
wr_reg32(ESP_REG_DATA_SIZE, 0);
wr_reg8(ESP_REG_STATUS, ESP_ST_READY);
return;
}
// Read up to 512 bytes from ring buffer
uint32_t to_read = avail > 512 ? 512 : avail;
uint8_t * dma = get_dma_buf();
uint32_t t = ring_tail;
for (uint32_t i = 0; i < to_read; i++)
{
dma[i] = ring_buf[t];
t = (t + 1) % RING_BUF_SIZE;
}
ring_tail = t;
xSemaphoreGive(ring_mutex);
spi_slave_dma_send_direct(to_read);
}
void stream_close(void)
{
if (reader_task_handle)
{
vTaskDelete(reader_task_handle);
reader_task_handle = NULL;
}
stream_transport_close();
ESP_LOGI(TAG, "stream closed, total read=%u", stream_read_size);
stream_state_reset();
stream_ring_free();
wr_reg8(ESP_REG_STATUS, ESP_ST_READY);
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
void stream_init(void);
void stream_http_start(void);
void stream_https_start(void);
void stream_gopher_start(void);
void stream_read(void);
void stream_close(void);
+1917
View File
File diff suppressed because it is too large Load Diff
+13366
View File
File diff suppressed because it is too large Load Diff
+932
View File
@@ -0,0 +1,932 @@
#pragma once
#include "esp_attr.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "esp_err.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
// ============================================================================
// Common tracker declarations
// ============================================================================
// -------------------- Common tracker types --------------------
typedef enum tracker_format_e
{
TRACKER_FORMAT_XM = 0,
TRACKER_FORMAT_MOD,
TRACKER_FORMAT_S3M
} tracker_format_t;
#define TRACKER_CONTEXT_SEGMENT_PAGE_CAPACITY 64
#define TRACKER_CONTEXT_SEGMENTS_MAGIC 0x5453474dU
#define TRACKER_CONTEXT_SEGMENTS_MAGIC_CHECK 0xaba7b8b2U
typedef enum tracker_context_segment_type_e
{
TRACKER_CONTEXT_SEG_CONTEXT = 0,
TRACKER_CONTEXT_SEG_RUNTIME,
TRACKER_CONTEXT_SEG_PATTERNS,
TRACKER_CONTEXT_SEG_INSTRUMENTS,
TRACKER_CONTEXT_SEG_SAMPLES,
TRACKER_CONTEXT_SEG_FORMAT_EXTRA
} tracker_context_segment_type_t;
typedef struct tracker_context_segment_s
{
void *addr;
size_t size;
uint8_t type;
uint8_t flags;
uint16_t index;
} tracker_context_segment_t;
typedef struct tracker_context_segment_page_s tracker_context_segment_page_t;
// -------------------- Common byte order helpers --------------------
uint16_t tracker_rd_be16(const uint8_t *p);
uint16_t tracker_rd_le16(const uint8_t *p);
uint32_t tracker_rd_le24(const uint8_t *p);
uint32_t tracker_rd_le32(const uint8_t *p);
// -------------------- Common size helpers --------------------
int tracker_size_add(size_t *dst, size_t add);
int tracker_size_mul(size_t a, size_t b, size_t *out);
// -------------------- Common string/memory helpers --------------------
void tracker_copy_trimmed(char *dst, size_t dst_size, const uint8_t *src, size_t src_size);
void tracker_memcpy_pad(void *dst, size_t dst_len, const void *src, size_t src_len, size_t offset);
// -------------------- XM format helpers --------------------
// -------------------- XM grouped context allocation helpers --------------------
typedef struct xm_context_group_sizes_s
{
size_t context_size;
size_t patterns_size;
size_t instruments_size;
size_t samples_size;
size_t runtime_size;
size_t format_extra_size;
size_t total_size;
} xm_context_group_sizes_t;
typedef struct xm_context_group_cursor_s
{
char *patterns;
char *patterns_end;
char *instruments;
char *instruments_end;
char *samples;
char *samples_end;
char *runtime;
char *runtime_end;
char *format_extra;
char *format_extra_end;
} xm_context_group_cursor_t;
// -------------------- MOD format helpers --------------------
// -------------------- S3M format helpers --------------------
// ============================================================================
// XM public API
// ============================================================================
/* Author: Romain "Artefact2" Dalmaso <artefact2@gmail.com> */
/* Contributor: Dan Spencer <dan@atomicpotato.net> */
/* This program is free software. It comes without any warranty, to the
* extent permitted by applicable law. You can redistribute it and/or
* modify it under the terms of the Do What The Fuck You Want To Public
* License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#define XM_DEFENSIVE 1
#define XM_LIBXMIZE_DELTA_SAMPLES 0
#define XM_DEBUG 0
#define XM_LINEAR_INTERPOLATION 1
#define XM_RAMPING 1
#define XM_STRINGS 1
struct xm_context_s;
typedef struct xm_context_s xm_context_t;
typedef struct s3m_adlib_instrument_s
{
uint8_t type;
uint8_t volume;
uint32_t c4speed;
uint8_t regs[12];
} s3m_adlib_instrument_t;
typedef void* (*MFUNC)(size_t);
/** Create a XM context.
*
* @param moddata the contents of the module
* @param moddata_length the length of the contents of the module, in bytes
* @param rate play rate in Hz, recommended value of 48000
*
* @returns size on success
* @returns -1 if module data is not sane
* @returns -2 if memory allocation failed
*/
int xm_create_context_safe(xm_context_t**, void* moddata, size_t moddata_length, uint32_t rate, MFUNC mfunc);
/** Create a XM context.
*
* This function will produce smaller code size compared to
* xm_create_context(), but requires converting the .xm file to a
* non-portable format beforehand.
*
* This function doesn't do any kind of error checking.
*
* @param libxmizeddata: pointer to module data, needs to point to a
* writable area, freeing it is equivalent as calling
* xm_free_context().
*
* @see xm_create_context()
*/
void xm_create_context_from_libxmize(xm_context_t**, char* libxmizeddata, uint32_t rate);
/** Free a XM context created by xm_create_context(). */
void xm_free_context(xm_context_t*);
int tracker_context_register_segment(xm_context_t *ctx, void *addr, size_t size, uint8_t type);
int tracker_context_has_registered_segments(const xm_context_t *ctx);
uint16_t tracker_context_get_segment_count(const xm_context_t *ctx);
int tracker_context_get_segment(const xm_context_t *ctx, uint16_t index, tracker_context_segment_t *segment);
void tracker_context_free_segments(xm_context_t *ctx);
void xm_sample(xm_context_t* ctx, float* p_left, float* p_right);
void xm_render(xm_context_t* ctx, float* out, size_t samples);
/** Set the maximum number of times a module can loop. After the
* specified number of loops, calls to xm_generate_samples will only
* generate silence. You can control the current number of loops with
* xm_get_loop_count().
*
* @param loopcnt maximum number of loops. Use 0 to loop
* indefinitely. */
void xm_set_max_loop_count(xm_context_t*, uint8_t loopcnt);
/** Get the loop count of the currently playing module. This value is
* 0 when the module is still playing, 1 when the module has looped
* once, etc. */
uint8_t xm_get_loop_count(xm_context_t*);
/** Get the length, in samples, of one full pass through the module loaded
* into the given object handle (computed once at TRACK_INIT time). 0 if
* the handle has no track info yet. */
uint32_t xm_get_total_samples(int handle);
/** Seek to a specific position in a module.
*
* WARNING, WITH BIG LETTERS: seeking modules is broken by design,
* don't expect miracles.
*/
void xm_seek(xm_context_t*, uint8_t pot, uint8_t row, uint16_t tick);
/** Mute or unmute a channel.
*
* @note Channel numbers go from 1 to xm_get_number_of_channels(...).
*
* @return whether the channel was muted.
*/
bool xm_mute_channel(xm_context_t*, uint16_t, bool);
/** Mute or unmute an instrument.
*
* @note Instrument numbers go from 1 to
* xm_get_number_of_instruments(...).
*
* @return whether the instrument was muted.
*/
bool xm_mute_instrument(xm_context_t*, uint16_t, bool);
/** Get the module name as a NUL-terminated string. */
const char* xm_get_module_name(xm_context_t*);
/** Get the tracker name as a NUL-terminated string. */
const char* xm_get_tracker_name(xm_context_t*);
/** Get the number of channels. */
uint16_t xm_get_number_of_channels(xm_context_t*);
/** Get the module length (in patterns). */
uint16_t xm_get_module_length(xm_context_t*);
/** Get the number of patterns. */
uint16_t xm_get_number_of_patterns(xm_context_t*);
/** Get the number of rows of a pattern.
*
* @note Pattern numbers go from 0 to
* xm_get_number_of_patterns(...)-1.
*/
uint16_t xm_get_number_of_rows(xm_context_t*, uint16_t);
/** Get the number of instruments. */
uint16_t xm_get_number_of_instruments(xm_context_t*);
/** Get the number of samples of an instrument.
*
* @note Instrument numbers go from 1 to
* xm_get_number_of_instruments(...).
*/
uint16_t xm_get_number_of_samples(xm_context_t*, uint16_t);
/** Get the internal buffer for a given sample waveform.
*
* This buffer can be read from or written to, at any time, but the
* length cannot change. The buffer must be cast to (int8_t*) or
* (int16_t*) depending on the sample type.
*
* @note Instrument numbers go from 1 to
* xm_get_number_of_instruments(...).
*
* @note Sample numbers go from 0 to
* xm_get_nubmer_of_samples(...,instr)-1.
*/
void* xm_get_sample_waveform(xm_context_t*, uint16_t instr, uint16_t sample, size_t* length, uint8_t* bits);
/** Get the current module speed.
*
* @param bpm will receive the current BPM
* @param tempo will receive the current tempo (ticks per line)
*/
void xm_get_playing_speed(xm_context_t*, uint16_t* bpm, uint16_t* tempo);
/** Get the current position in the module being played.
*
* @param pattern_index if not NULL, will receive the current pattern
* index in the POT (pattern order table)
*
* @param pattern if not NULL, will receive the current pattern number
*
* @param row if not NULL, will receive the current row
*
* @param samples if not NULL, will receive the total number of
* generated samples (divide by sample rate to get seconds of
* generated audio)
*/
void xm_get_position(xm_context_t*, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples);
/** Get the latest time (in number of generated samples) when a
* particular instrument was triggered in any channel.
*
* @note Instrument numbers go from 1 to
* xm_get_number_of_instruments(...).
*/
uint64_t xm_get_latest_trigger_of_instrument(xm_context_t*, uint16_t);
/** Get the latest time (in number of generated samples) when a
* particular sample was triggered in any channel.
*
* @note Instrument numbers go from 1 to
* xm_get_number_of_instruments(...).
*
* @note Sample numbers go from 0 to
* xm_get_nubmer_of_samples(...,instr)-1.
*/
uint64_t xm_get_latest_trigger_of_sample(xm_context_t*, uint16_t instr, uint16_t sample);
/** Get the latest time (in number of generated samples) when any
* instrument was triggered in a given channel.
*
* @note Channel numbers go from 1 to xm_get_number_of_channels(...).
*/
uint64_t xm_get_latest_trigger_of_channel(xm_context_t*, uint16_t);
/** Checks whether a channel is active (ie: is playing something).
*
* @note Channel numbers go from 1 to xm_get_number_of_channels(...).
*/
bool xm_is_channel_active(xm_context_t*, uint16_t);
/** Get the instrument number currently playing in a channel.
*
* @returns instrument number, or 0 if channel is not active.
*
* @note Channel numbers go from 1 to xm_get_number_of_channels(...).
*
* @note Instrument numbers go from 1 to
* xm_get_number_of_instruments(...).
*/
uint16_t xm_get_instrument_of_channel(xm_context_t*, uint16_t);
/** Get the frequency of the sample currently playing in a channel.
*
* @returns a frequency in Hz. If the channel is not active, return
* value is undefined.
*
* @note Channel numbers go from 1 to xm_get_number_of_channels(...).
*/
float xm_get_frequency_of_channel(xm_context_t*, uint16_t);
/** Get the volume of the sample currently playing in a channel. This
* takes into account envelopes, etc.
*
* @returns a volume between 0 or 1. If the channel is not active,
* return value is undefined.
*
* @note Channel numbers go from 1 to xm_get_number_of_channels(...).
*/
float xm_get_volume_of_channel(xm_context_t*, uint16_t);
/** Get the panning of the sample currently playing in a channel. This
* takes into account envelopes, etc.
*
* @returns a panning between 0 (L) and 1 (R). If the channel is not
* active, return value is undefined.
*
* @note Channel numbers go from 1 to xm_get_number_of_channels(...).
*/
float xm_get_panning_of_channel(xm_context_t*, uint16_t);
// ============================================================================
// Common tracker runtime internals
// ============================================================================
/* Author: Romain "Artefact2" Dalmaso <artefact2@gmail.com> */
/* This program is free software. It comes without any warranty, to the
* extent permitted by applicable law. You can redistribute it and/or
* modify it under the terms of the Do What The Fuck You Want To Public
* License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#if XM_DEBUG
#include <stdio.h>
#define DEBUG(fmt, ...) \
{ \
fprintf(stderr, "%s(): " fmt "\n", __func__, __VA_ARGS__); \
fflush(stderr); \
}
#else
#define DEBUG(...)
#endif
#if XM_BIG_ENDIAN
#error "Big endian platforms are not yet supported, sorry"
/* Make sure the compiler stops, even if #error is ignored */
extern int __fail[-1];
#endif
/* ----- XM constants ----- */
#define SAMPLE_NAME_LENGTH 22
#define INSTRUMENT_NAME_LENGTH 22
#define MODULE_NAME_LENGTH 20
#define TRACKER_NAME_LENGTH 20
#define PATTERN_ORDER_TABLE_LENGTH 256
#define NUM_NOTES 96
#define NUM_ENVELOPE_POINTS 12
#define MAX_NUM_ROWS 256
#if XM_RAMPING
#define XM_SAMPLE_RAMPING_POINTS 32
#endif
/* ----- Data types ----- */
enum xm_waveform_type_e {
XM_SINE_WAVEFORM = 0,
XM_RAMP_DOWN_WAVEFORM = 1,
XM_SQUARE_WAVEFORM = 2,
XM_RANDOM_WAVEFORM = 3,
XM_RAMP_UP_WAVEFORM = 4,
};
typedef enum xm_waveform_type_e xm_waveform_type_t;
enum xm_loop_type_e {
XM_NO_LOOP,
XM_FORWARD_LOOP,
XM_PING_PONG_LOOP,
};
typedef enum xm_loop_type_e xm_loop_type_t;
enum xm_frequency_type_e {
XM_LINEAR_FREQUENCIES,
XM_AMIGA_FREQUENCIES,
XM_S3M_FREQUENCIES,
};
#define XM_EFFECT_S3M_SPEED 34
#define XM_EFFECT_S3M_TEMPO 35
#define XM_EFFECT_S3M_FINE_VIBRATO 36
#define XM_EFFECT_S3M_CHANNEL_VOLUME 37
#define XM_EFFECT_S3M_CHANNEL_VOLUME_SLIDE 38
#define XM_EFFECT_S3M_POSITION_JUMP 39
#define XM_EFFECT_S3M_PATTERN_BREAK 40
#define XM_EFFECT_S3M_VOLUME_SLIDE 41
#define XM_EFFECT_S3M_PORTAMENTO_DOWN 42
#define XM_EFFECT_S3M_PORTAMENTO_UP 43
#define XM_EFFECT_S3M_TONE_PORTAMENTO 44
#define XM_EFFECT_S3M_VIBRATO 45
#define XM_EFFECT_S3M_TREMOR 46
#define XM_EFFECT_S3M_ARPEGGIO 47
#define XM_EFFECT_S3M_VIBRATO_VOLUME_SLIDE 48
#define XM_EFFECT_S3M_TONE_PORTAMENTO_VOLUME_SLIDE 49
#define XM_EFFECT_S3M_SAMPLE_OFFSET 50
#define XM_EFFECT_S3M_PANNING_SLIDE 51
#define XM_EFFECT_S3M_RETRIG 52
#define XM_EFFECT_S3M_TREMOLO 53
#define XM_EFFECT_S3M_GLOBAL_VOLUME 54
#define XM_EFFECT_S3M_GLOBAL_VOLUME_SLIDE 55
#define XM_EFFECT_S3M_PANNING 56
#define XM_EFFECT_S3M_EXTENDED 57
#define XM_EFFECT_S3M_PANBRELLO 58
#define XM_EFFECT_S3M_MIDI_MACRO 59
#define XM_EFFECT_S3M_NONE 60
typedef enum xm_frequency_type_e xm_frequency_type_t;
struct xm_envelope_point_s {
uint16_t frame;
uint16_t value;
};
typedef struct xm_envelope_point_s xm_envelope_point_t;
struct xm_envelope_s {
xm_envelope_point_t points[NUM_ENVELOPE_POINTS];
uint8_t num_points;
uint8_t sustain_point;
uint8_t loop_start_point;
uint8_t loop_end_point;
bool enabled;
bool sustain_enabled;
bool loop_enabled;
};
typedef struct xm_envelope_s xm_envelope_t;
struct xm_sample_s {
#if XM_STRINGS
char name[SAMPLE_NAME_LENGTH + 1];
#endif
uint8_t bits; /* Either 8 or 16 */
uint32_t length;
uint32_t loop_start;
uint32_t loop_length;
uint32_t loop_end;
float volume;
int8_t finetune;
xm_loop_type_t loop_type;
float panning;
int8_t relative_note;
uint32_t c4speed; /* S3M C4Speed / OpenMPT c5speed field; used by S3M frequency mode. */
uint64_t latest_trigger;
union {
int8_t* data8;
int16_t* data16;
};
};
typedef struct xm_sample_s xm_sample_t;
struct xm_instrument_s {
#if XM_STRINGS
char name[INSTRUMENT_NAME_LENGTH + 1];
#endif
uint16_t num_samples;
uint8_t sample_of_notes[NUM_NOTES];
xm_envelope_t volume_envelope;
xm_envelope_t panning_envelope;
xm_waveform_type_t vibrato_type;
uint8_t vibrato_sweep;
uint8_t vibrato_depth;
uint8_t vibrato_rate;
uint16_t volume_fadeout;
uint64_t latest_trigger;
bool muted;
xm_sample_t* samples;
};
typedef struct xm_instrument_s xm_instrument_t;
struct xm_pattern_slot_s {
uint16_t period; /* MOD raw Amiga period; 0 means XM note-based slot */
uint8_t note; /* 1-96, 97 = Key Off note */
uint8_t instrument; /* 1-128 */
uint8_t volume_column;
uint8_t effect_type;
uint8_t effect_param;
};
typedef struct xm_pattern_slot_s xm_pattern_slot_t;
struct xm_pattern_s {
uint16_t num_rows;
xm_pattern_slot_t* slots; /* Array of size num_rows * num_channels */
};
typedef struct xm_pattern_s xm_pattern_t;
struct xm_mod_efx_backup_s {
xm_sample_t* sample;
int8_t* backup8;
uint32_t loop_start;
uint32_t loop_length;
};
typedef struct xm_mod_efx_backup_s xm_mod_efx_backup_t;
struct xm_module_s {
#if XM_STRINGS
char name[MODULE_NAME_LENGTH + 1];
char trackername[TRACKER_NAME_LENGTH + 1];
#endif
uint16_t length;
uint16_t restart_position;
uint16_t num_channels;
uint16_t num_patterns;
uint16_t num_instruments;
xm_frequency_type_t frequency_type;
uint8_t pattern_table[PATTERN_ORDER_TABLE_LENGTH];
xm_pattern_t* patterns;
xm_instrument_t* instruments; /* Instrument 1 has index 0,
* instrument 2 has index 1, etc. */
};
typedef struct xm_module_s xm_module_t;
struct xm_channel_context_s {
float note;
float orig_note; /* The original note before effect modifications, as read in the pattern. */
xm_instrument_t* instrument; /* Could be NULL */
xm_sample_t* sample; /* Could be NULL */
xm_pattern_slot_t* current;
float sample_position;
float period;
float frequency;
float step;
bool ping; /* For ping-pong samples: true is -->, false is <-- */
float volume; /* Ideally between 0 (muted) and 1 (loudest) */
float channel_volume; /* S3M Mxx/Nxy channel global volume multiplier, 0..1. */
float panning; /* Between 0 (left) and 1 (right); 0.5 is centered */
float default_panning; /* < 0: use sample panning; >= 0: use channel default panning */
bool surround; /* S3M XA4: inverse right channel phase around centered panning. */
tracker_format_t tracker_format;
float period_note_offset; /* MOD finetune offset for raw period slots */
uint16_t autovibrato_ticks;
bool sustained;
float fadeout_volume;
float volume_envelope_volume;
float panning_envelope_panning;
uint16_t volume_envelope_frame_count;
uint16_t panning_envelope_frame_count;
float autovibrato_note_offset;
bool arp_in_progress;
uint8_t arp_note_offset;
uint8_t arpeggio_param;
uint8_t volume_slide_param;
uint8_t fine_volume_slide_param;
uint8_t global_volume_slide_param;
uint8_t channel_volume_slide_param;
uint8_t panning_slide_param;
uint8_t portamento_up_param;
uint8_t portamento_down_param;
uint8_t fine_portamento_up_param;
uint8_t fine_portamento_down_param;
uint8_t extra_fine_portamento_up_param;
uint8_t extra_fine_portamento_down_param;
uint8_t tone_portamento_param;
float tone_portamento_target_period;
uint8_t multi_retrig_param;
uint8_t note_delay_param;
uint8_t sample_offset_param;
uint8_t s3m_high_sample_offset_param;
uint8_t invert_loop_speed;
uint16_t invert_loop_delay;
uint32_t invert_loop_offset;
uint8_t pattern_loop_origin; /* Where to restart a E6y loop */
uint8_t pattern_loop_count; /* How many loop passes have been done */
bool vibrato_in_progress;
xm_waveform_type_t vibrato_waveform;
bool vibrato_waveform_retrigger; /* True if a new note retriggers the waveform */
uint8_t vibrato_param;
uint16_t vibrato_ticks; /* Position in the waveform */
float vibrato_note_offset;
float vibrato_period_offset;
xm_waveform_type_t tremolo_waveform;
bool tremolo_waveform_retrigger;
uint8_t tremolo_param;
uint8_t tremolo_ticks;
float tremolo_volume;
xm_waveform_type_t panbrello_waveform;
bool panbrello_waveform_retrigger;
bool panbrello_in_progress;
uint8_t panbrello_param;
uint16_t panbrello_ticks;
float panbrello_panning_offset;
uint8_t tremor_param;
uint8_t s3m_effect_memory;
bool s3m_glissando_control;
bool s3m_note_cut;
bool tremor_on;
uint8_t s3m_opl_key_on;
uint8_t s3m_opl_instrument;
uint32_t s3m_opl_trigger_id;
uint32_t s3m_opl_synced_trigger_id;
uint64_t latest_trigger;
bool muted;
#if XM_RAMPING
/* These values are updated at the end of each tick, to save
* a couple of float operations on every generated sample. */
float target_panning;
float target_volume;
unsigned long frame_count;
float end_of_previous_sample[XM_SAMPLE_RAMPING_POINTS];
#endif
float actual_panning;
float actual_volume;
};
typedef struct xm_channel_context_s xm_channel_context_t;
struct xm_context_s {
size_t ctx_size; /* Must be first, see xm_create_context_from_libxmize() */
xm_module_t module;
tracker_format_t tracker_format;
bool s3m_fast_volume_slides;
uint8_t s3m_tempo_slide_param;
uint16_t s3m_adlib_instrument_count;
s3m_adlib_instrument_t *s3m_adlib_instruments;
uint8_t *s3m_channel_opl;
uint32_t rate;
uint16_t tempo;
uint16_t bpm;
float global_volume;
float amplification;
#if XM_RAMPING
/* How much is a channel final volume allowed to change per
* sample; this is used to avoid abrubt volume changes which
* manifest as "clicks" in the generated sound. */
float volume_ramp;
float panning_ramp; /* Same for panning. */
#endif
uint8_t current_table_index;
uint8_t current_row;
uint16_t current_tick; /* Can go below 255, with high tempo and a pattern delay */
float remaining_samples_in_tick;
uint64_t generated_samples;
bool position_jump;
bool pattern_break;
uint8_t jump_dest;
uint8_t jump_row;
/* Extra ticks to be played before going to the next row -
* Used for EEy effect */
uint16_t extra_ticks;
uint8_t* row_loop_count; /* Array of size MAX_NUM_ROWS * module_length */
uint8_t loop_count;
uint8_t max_loop_count;
xm_channel_context_t* channels;
uint16_t mod_efx_backup_count;
xm_mod_efx_backup_t* mod_efx_backups;
int8_t* mod_efx_backup_data;
uint32_t tracker_segments_magic;
uint32_t tracker_segments_magic_check;
uint16_t tracker_segment_count;
tracker_context_segment_page_t* tracker_segment_pages;
};
/* ----- Internal API ----- */
/** Check the module data for errors/inconsistencies.
*
* @returns 0 if everything looks OK. Module should be safe to load.
*/
int xm_check_sanity_preload(const char*, size_t);
/** Check a loaded module for errors/inconsistencies.
*
* @returns 0 if everything looks OK.
*/
int xm_check_sanity_postload(xm_context_t*);
/** Get the number of bytes needed to store the module data in a
* dynamically allocated blank context.
*
* Things that are dynamically allocated:
* - sample data
* - sample structures in instruments
* - pattern data
* - row loop count arrays
* - pattern structures in module
* - instrument structures in module
* - channel contexts
* - context structure itself
* @returns 0 if everything looks OK.
*/
size_t xm_get_memory_needed_for_context(const char*, size_t);
/** Populate the context from module data.
*
* @returns non-zero on success
*/
int xm_load_module(xm_context_t*, const char*, size_t, xm_context_group_cursor_t*);
// ============================================================================
// MOD module support
// ============================================================================
#define MOD_TITLE_SIZE 20
#define MOD_SAMPLE_COUNT 31
#define MOD_15_SAMPLE_COUNT 15
#define MOD_15_SONG_LENGTH_OFFSET 470
#define MOD_15_RESTART_OFFSET 471
#define MOD_15_ORDER_OFFSET 472
#define MOD_15_HEADER_SIZE 600
#define MOD_SAMPLE_HEADER_SIZE 30
#define MOD_SONG_LENGTH_OFFSET 950
#define MOD_RESTART_OFFSET 951
#define MOD_ORDER_OFFSET 952
#define MOD_ORDER_COUNT 128
#define MOD_MAGIC_OFFSET 1080
#define MOD_HEADER_SIZE 1084
#define MOD_ROWS_PER_PATTERN 64
#define MOD_DEFAULT_TEMPO 6
#define MOD_DEFAULT_BPM 125
#define MOD_MAX_CHANNELS 32
struct xm_module_s;
struct xm_pattern_slot_s;
typedef struct
{
uint16_t channels;
uint16_t patterns;
uint16_t song_length;
uint16_t restart_position;
uint16_t sample_count;
size_t header_size;
size_t pattern_data_offset;
size_t sample_data_offset;
uint32_t sample_length[MOD_SAMPLE_COUNT];
uint32_t sample_loop_start[MOD_SAMPLE_COUNT];
uint32_t sample_loop_length[MOD_SAMPLE_COUNT];
uint32_t sample_file_length[MOD_SAMPLE_COUNT];
size_t pattern_bytes;
size_t sample_bytes;
size_t expected_size;
uint32_t efx_sample_mask;
uint16_t efx_backup_count;
size_t efx_backup_bytes;
} mod_layout_t;
int mod_create_context_safe(xm_context_t **ctxp, void *moddata, size_t moddata_length, uint32_t rate, MFUNC mfunc, size_t *out_bytes_needed);
int mod_check_sanity_preload(const void *moddata, size_t moddata_length);
uint16_t mod_get_channel_count(const void *moddata, size_t moddata_length);
int mod_read_layout_from_header(const uint8_t *header, size_t header_length, size_t moddata_length, mod_layout_t *layout);
size_t mod_get_memory_needed_for_layout(const mod_layout_t *layout);
int mod_layout_set_efx_backup_mask(mod_layout_t *layout, uint32_t sample_mask);
int mod_layout_set_efx_backup_all_looped(mod_layout_t *layout);
uint32_t mod_scan_efx_sample_mask_from_context(const xm_context_t *ctx, const mod_layout_t *layout);
uint32_t mod_scan_efx_sample_mask_from_data(const void *moddata, const mod_layout_t *layout);
int mod_get_group_sizes_for_layout(const mod_layout_t *layout, xm_context_group_sizes_t *sizes);
int mod_setup_module_header(xm_context_t *ctx, const uint8_t *header, const mod_layout_t *layout, xm_context_group_cursor_t *cursor);
int mod_setup_sample_metadata(xm_context_t *ctx, const uint8_t *header, const mod_layout_t *layout, uint16_t i, xm_context_group_cursor_t *cursor, MFUNC mfunc);
void mod_decode_pattern_slot(struct xm_pattern_slot_s *slot, const uint8_t *entry);
int mod_setup_context_runtime(xm_context_t *ctx, xm_context_group_cursor_t *cursor, uint32_t rate);
int mod_setup_efx_backups(xm_context_t *ctx, const mod_layout_t *layout, xm_context_group_cursor_t *cursor);
void mod_restore_efx_backups(xm_context_t *ctx);
int mod_finish_context(xm_context_t **ctxp, xm_context_t *ctx);
// ============================================================================
// S3M module support
// ============================================================================
#define S3M_HEADER_SIZE 96
#define S3M_MAGIC_OFFSET 44
#define S3M_MIN_CHANNELS 1
#define S3M_MAX_CHANNELS 32
#define S3M_MAX_ORDERS 256
#define S3M_MAX_SAMPLES 99
#define S3M_MAX_PATTERNS 256
#define S3M_SAMPLE_HEADER_SIZE 80
#define S3M_ROWS_PER_PATTERN 64
#define S3M_DEFAULT_C4SPEED 8363
int s3m_create_context_safe(xm_context_t **ctxp, void *s3mdata, size_t s3mdata_length, uint32_t rate, MFUNC mfunc, size_t *out_bytes_needed);
const char *s3m_get_last_error();
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
// ============================================================================
// ESP32 tracker task / CLI integration
// ============================================================================
enum
{
TRACK_TASK_INIT,
TRACK_TASK_PLAY,
TRACK_TASK_STOP,
TRACK_TASK_RESET,
TRACK_TASK_SET_POS,
TRACK_TASK_SET_PARAMS,
};
typedef struct
{
uint8_t task;
int handle;
} TRACK_TASK;
// Records the last time a TRACK_PLAY/RESET/SET_PARAMS command was rejected with
// ESP_ERR_INV_XM_HANDLE, since that check runs in ISR context (spi_slave.cpp)
// where ESP_LOGx isn't safe to call. Inspect via the "xm lasterr" console command.
typedef struct
{
volatile bool valid;
volatile uint8_t cmd;
volatile int handle;
volatile bool handle_in_range;
volatile uint8_t obj_type;
volatile int64_t timestamp_us; // esp_timer_get_time() at rejection - directly comparable to xm_task's own TIMING logs
} TrackHandleErrorInfo;
extern TrackHandleErrorInfo g_track_handle_error;
void record_track_handle_error(uint8_t cmd, int handle);
typedef struct
{
uint8_t task;
xm_context_t *ctx;
} PLAYER_TASK;
extern QueueHandle_t xm_queue;
extern int master_volume;
extern int mix_volume;
extern int curr_xm_handle;
int xm_cmd(int argc, char **argv);
void xm_task(void *arg);
void initialize_xm();
void xm_console_register_system_commands();
// Pause/resume XM tracker playback AND the shared i2s_task/player_task
// pipeline it renders through (also used by SFX and OPL, see their
// player_task usage) - used while the SMB2/3 server is active.
void tracker_suspend();
void tracker_resume();
int xm_load_play_file(const char *path, bool quiet = false);
int xm_play_cmd(int handle, bool quiet = false);
int xm_stop_cmd(bool quiet = false);
int xm_reset_cmd(int handle, bool quiet = false);
esp_err_t xm_host_stream_prepare_command(size_t module_size, size_t rx_size);
esp_err_t mod_host_stream_prepare_command(size_t module_size, size_t rx_size);
esp_err_t s3m_host_stream_prepare_command(size_t module_size, size_t rx_size);
void xm_host_stream_abort_current();
void xm_host_stream_process_rx_data(const uint8_t *data, size_t size);
#endif
+1195
View File
File diff suppressed because it is too large Load Diff
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "tsspiffs.h"
#define TSF_PART_LABEL "tsf"
#define TSF_BASE_PATH "/tsf"
#define TSF_PART_SUBTYPE_VALUE 0x84
#define TSF_PART_SUBTYPE_NAME "tsf"
#define TSF_BLOCK_SIZE 4096
#define TSF_HAL_BUF_SIZE 256
#define TSF_VFS_MAX_FILES 4
extern bool tsf_storage_initialized;
extern bool tsf_storage_mounted;
extern char tsf_storage_base_path[32];
extern char tsf_storage_part_label[16];
esp_err_t tsf_storage_mount();
esp_err_t tsf_storage_mount_quiet();
esp_err_t tsf_storage_format();
esp_err_t tsf_storage_delete();
esp_err_t tsf_storage_init();
esp_err_t tsf_storage_ensure_ready();
esp_err_t tsf_storage_ensure_ready_quiet();
void tsf_storage_deinit();
TSF_VOLUME *tsf_storage_volume();
esp_err_t tsf_storage_space_info(uint64_t *total_bytes, uint64_t *free_bytes);
bool tsf_storage_path_has_base(const char *path);
esp_err_t tsf_storage_name_from_path(const char *path, char *out, size_t out_size);
bool tsf_storage_path_is_root(const char *path);
esp_err_t tsf_storage_open_read(const char *path, TSF_FILE *file, size_t *out_size);
esp_err_t tsf_storage_read_file(const char *path, void *dst, size_t size, size_t *out_size);
esp_err_t tsf_storage_list_dir(const char *path);
esp_err_t tsf_storage_result_to_err(TSF_RESULT res);
const char *tsf_storage_result_name(TSF_RESULT res);
const char *tsf_storage_subtype_name();
esp_err_t tsf_storage_register_vfs();
int tsf_storage_info_cmd(int argc, char **argv);
int tsf_storage_format_cmd(int argc, char **argv);
int tsf_storage_delete_cmd(int argc, char **argv);
int tsf_storage_ls_cmd(int argc, char **argv);
int tsf_storage_cmd(int argc, char **argv);
void tsf_console_register_system_commands();
+517
View File
@@ -0,0 +1,517 @@
#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include "tsspiffs.h"
TSF_RESULT tsf_format(TSF_CONFIG *cfg)
{
if (!cfg || !cfg->block_size || !cfg->bulk_size) return TSF_RES_FS_ERROR;
if ((cfg->bulk_size % cfg->block_size) != 0) return TSF_RES_FS_ERROR;
if (cfg->block_size <= sizeof(TSF_CHUNK)) return TSF_RES_FS_ERROR;
if (!cfg->hal_read_func || !cfg->hal_write_func) return TSF_RES_FS_ERROR;
if (!cfg->hal_erase_func && !cfg->hal_erase_range_func) return TSF_RES_FS_ERROR;
return tsf_init_chunk_range(cfg, cfg->bulk_start, cfg->bulk_size);
}
TSF_RESULT tsf_init_chunk(TSF_CONFIG *cfg, u32 addr)
{
if (!cfg) return TSF_RES_FS_ERROR;
return tsf_init_chunk_range(cfg, addr, cfg->block_size);
}
TSF_RESULT tsf_init_erased_chunk(TSF_CONFIG *cfg, u32 addr)
{
if (!cfg || !cfg->block_size) return TSF_RES_FS_ERROR;
if (!cfg->hal_write_func) return TSF_RES_FS_ERROR;
#ifdef TSF_CHECK_BLANK
TSF_RESULT rc = tsf_check_blank(cfg, addr, cfg->block_size);
if (rc != TSF_RES_OK)
return rc;
#endif
u32 magic = TSF_MAGIC;
cfg->hal_write_func(addr, &magic, sizeof(magic));
return TSF_RES_OK;
}
TSF_RESULT tsf_init_chunk_range(TSF_CONFIG *cfg, u32 addr, u32 size)
{
TSF_RESULT rc;
if (!cfg || !cfg->block_size || !size) return TSF_RES_FS_ERROR;
if ((size % cfg->block_size) != 0) return TSF_RES_FS_ERROR;
if (!cfg->hal_erase_func && !cfg->hal_erase_range_func) return TSF_RES_FS_ERROR;
if (cfg->hal_erase_range_func)
{
rc = cfg->hal_erase_range_func(addr, size);
if (rc != TSF_RES_OK)
return rc;
}
else
{
for (u32 pos = 0; pos < size; pos += cfg->block_size)
{
rc = cfg->hal_erase_func(addr + pos);
if (rc != TSF_RES_OK)
return rc;
}
}
for (u32 pos = 0; pos < size; pos += cfg->block_size)
{
rc = tsf_init_erased_chunk(cfg, addr + pos);
if (rc != TSF_RES_OK)
return rc;
}
return TSF_RES_OK;
}
TSF_RESULT tsf_check_blank(TSF_CONFIG *cfg, u32 addr, u32 size)
{
if (!cfg || !cfg->buf || !cfg->buf_size) return TSF_RES_FS_ERROR;
while (size)
{
u16 sz = (u16)min(cfg->buf_size, size);
u8 *buf = cfg->buf;
cfg->hal_read_func(addr, buf, sz);
for (u16 j = 0; j < sz; j++)
if (*buf++ != 0xFF)
return TSF_RES_NOT_BLANK;
size -= sz;
addr += sz;
}
return TSF_RES_OK;
}
TSF_RESULT tsf_take_new_chunk(TSF_VOLUME *vol, u8 type, u32 *chunk_addr)
{
if (!vol || !vol->cfg || !chunk_addr) return TSF_RES_FS_ERROR;
if (!vol->free) return TSF_RES_BULK_FULL; // to speed up the search
TSF_CONFIG *cfg = vol->cfg;
u16 chunk_num = cfg->last_written_chunk;
u32 addr = chunk_addr(chunk_num);
TSF_CHUNK chunk;
do
{
cfg->hal_read_func(addr, &chunk, sizeof(TSF_CHUNK));
if ((chunk.magic == TSF_MAGIC) && (chunk.type == TSF_CHUNK_FREE))
{
*chunk_addr = addr;
cfg->last_written_chunk = chunk_num;
vol->free -= cfg->block_size;
cfg->hal_write_func(addr + offsetof(TSF_CHUNK, type), &type, sizeof(type));
return TSF_RES_OK;
}
addr += cfg->block_size;
chunk_num++;
if (addr >= cfg->bulk_start + cfg->bulk_size)
{
addr = cfg->bulk_start;
chunk_num = 0;
}
} while (chunk_num != cfg->last_written_chunk);
return TSF_RES_BULK_FULL;
}
TSF_RESULT tsf_mount(TSF_CONFIG *cfg, TSF_VOLUME *vol)
{
if (!cfg || !vol) return TSF_RES_FS_ERROR;
if (!cfg->block_size || !cfg->bulk_size) return TSF_RES_FS_ERROR;
if ((cfg->bulk_size % cfg->block_size) != 0) return TSF_RES_FS_ERROR;
if (cfg->block_size <= sizeof(TSF_CHUNK)) return TSF_RES_FS_ERROR;
if (!cfg->hal_read_func || !cfg->hal_write_func) return TSF_RES_FS_ERROR;
if (!cfg->hal_erase_func && !cfg->hal_erase_range_func) return TSF_RES_FS_ERROR;
cfg->last_written_chunk = 0;
vol->cfg = cfg;
vol->list_addr = cfg->bulk_start;
return tsf_vol_stat(vol);
}
TSF_RESULT tsf_vol_stat(TSF_VOLUME *vol)
{
if (!vol || !vol->cfg) return TSF_RES_FS_ERROR;
TSF_CONFIG *cfg = vol->cfg;
vol->free = 0;
vol->chunks_number = 0;
vol->files_number = 0;
for (u32 i = 0; i < cfg->bulk_size; i += cfg->block_size)
{
TSF_CHUNK chunk;
cfg->hal_read_func(i + cfg->bulk_start, &chunk, sizeof(TSF_CHUNK));
if (chunk.magic == TSF_MAGIC)
{
vol->chunks_number++;
if (chunk.type == TSF_CHUNK_FREE)
vol->free += cfg->block_size;
else if (chunk.type == (u8)TSF_CHUNK_HEAD)
vol->files_number++;
}
}
return TSF_RES_OK;
}
TSF_RESULT tsf_search(TSF_VOLUME *vol, u32 *chunk_addr, const char *name)
{
if (!vol || !vol->cfg || !chunk_addr || !name) return TSF_RES_FS_ERROR;
TSF_CONFIG *cfg = vol->cfg;
size_t name_len = strlen(name);
if (name_len > 255) return TSF_RES_FS_ERROR;
if (name_len && (!cfg->buf || (cfg->buf_size < name_len))) return TSF_RES_FS_ERROR;
char search_name[256];
memcpy(search_name, name, name_len);
search_name[name_len] = 0;
u8 fnlen = (u8)name_len;
for (u32 i = 0; i < cfg->bulk_size; i += cfg->block_size)
{
u32 addr = i + cfg->bulk_start;
TSF_CHUNK chunk;
cfg->hal_read_func(addr, &chunk, sizeof(TSF_CHUNK));
if ((chunk.magic != TSF_MAGIC) || (chunk.type != (u8)TSF_CHUNK_HEAD))
continue;
u8 stored_fnlen;
cfg->hal_read_func(addr + sizeof(TSF_CHUNK) + offsetof(TSF_HDR, fnlen), &stored_fnlen, sizeof(stored_fnlen));
if (fnlen != stored_fnlen)
continue;
if (fnlen)
{
cfg->hal_read_func(addr + sizeof(TSF_CHUNK) + sizeof(TSF_HDR), cfg->buf, fnlen);
if (memcmp(search_name, cfg->buf, fnlen))
continue;
}
*chunk_addr = addr;
return TSF_RES_OK;
}
return TSF_RES_FILE_NOT_FOUND;
}
TSF_RESULT tsf_open(TSF_VOLUME *vol, TSF_FILE *file, const char *name, u8 mode)
{
if (!vol || !file || !name) return TSF_RES_FS_ERROR;
file->mode = mode;
file->vol = vol;
if (mode == TSF_MODE_READ)
return tsf_open_for_read(file, name);
else if (mode == TSF_MODE_CREATE_WRITE)
return tsf_create(file, name);
else
return TSF_RES_MODE_ERROR;
}
TSF_RESULT tsf_open_for_read(TSF_FILE *file, const char *name)
{
TSF_VOLUME *vol = file->vol;
u32 addr;
TSF_RESULT rc = tsf_search(vol, &addr, name);
if (rc != TSF_RES_OK)
return rc;
file->addr = file->chunk_addr = addr;
vol->cfg->hal_read_func(addr + sizeof(TSF_CHUNK) + offsetof(TSF_HDR, size), &file->size, sizeof(file->size));
file->seek = 0;
file->chunk_offset = sizeof(TSF_CHUNK) + sizeof(TSF_HDR) + (u8)strlen(name);
vol->cfg->hal_read_func(addr + offsetof(TSF_CHUNK, next_chunk), &file->next_chunk, sizeof(file->next_chunk));
return TSF_RES_OK;
}
TSF_RESULT tsf_create(TSF_FILE *file, const char *name)
{
TSF_VOLUME *vol = file->vol;
u32 addr;
size_t name_len = strlen(name);
if (name_len > 255) return TSF_RES_FS_ERROR;
TSF_CONFIG *cfg = vol->cfg;
if ((sizeof(TSF_CHUNK) + sizeof(TSF_HDR) + name_len) > cfg->block_size)
return TSF_RES_FS_ERROR;
u8 fnlen = (u8)name_len;
#ifdef TSF_CHECK_EXIST_ON_CREATE
{
TSF_RESULT rc = tsf_search(vol, &addr, name);
if (rc != TSF_RES_FILE_NOT_FOUND)
{
if (rc == TSF_RES_OK)
return TSF_RES_FILE_EXISTS;
else
return rc;
}
}
#endif
if (tsf_take_new_chunk(vol, TSF_CHUNK_HEAD, &addr) != TSF_RES_OK)
return TSF_RES_BULK_FULL;
file->addr = addr;
file->size = 0;
file->seek = 0;
file->chunk_addr = addr;
file->chunk_offset = sizeof(TSF_CHUNK) + sizeof(TSF_HDR) + fnlen;
file->prev_chunk_addr = addr;
file->next_chunk = 0xFFFF;
addr += sizeof(TSF_CHUNK);
TSF_HDR hdr;
hdr.size = 0xFFFFFFFF;
hdr.fnlen = fnlen;
cfg->hal_write_func(addr, &hdr, sizeof(TSF_HDR));
addr += sizeof(TSF_HDR);
cfg->hal_write_func(addr, name, fnlen);
return TSF_RES_OK;
}
TSF_RESULT tsf_close(TSF_FILE *file)
{
if ((file->mode & TSF_MODE_WRITE) == TSF_MODE_WRITE)
{
file->vol->cfg->hal_write_func(file->addr + sizeof(TSF_CHUNK) + offsetof(TSF_HDR, size), &file->size, sizeof(file->size)); // save file size
file->vol->files_number++;
}
return TSF_RES_OK;
}
TSF_RESULT tsf_read(TSF_FILE *file, void *buf, u32 size)
{
return tsf_read_int(file, buf, size);
}
TSF_RESULT tsf_seek(TSF_FILE *file, u32 size)
{
return tsf_read_int(file, 0, size);
}
TSF_RESULT tsf_read_int(TSF_FILE *file, void *buf, u32 size)
{
if (!file || !file->vol || !file->vol->cfg) return TSF_RES_FS_ERROR;
u8 *data = (u8*)buf;
TSF_CONFIG *cfg = file->vol->cfg;
u32 addr = file->chunk_addr;
if (file->seek >= file->size) return TSF_RES_OK;
size = min(size, file->size - file->seek);
while (size)
{
if (file->chunk_offset == cfg->block_size) // new chunk
{
if (file->next_chunk == 0xFFFF) return TSF_RES_FS_ERROR;
addr = file->chunk_addr = chunk_addr(file->next_chunk);
file->chunk_offset = sizeof(TSF_CHUNK);
cfg->hal_read_func(addr + offsetof(TSF_CHUNK, next_chunk), &file->next_chunk, sizeof(file->next_chunk));
}
u16 sz = (u16)min(cfg->block_size - file->chunk_offset, size);
if (buf)
{
cfg->hal_read_func(file->chunk_addr + file->chunk_offset, data, sz);
data += sz;
}
file->seek += sz;
file->chunk_offset += sz;
size -= sz;
}
return TSF_RES_OK;
}
TSF_RESULT tsf_write(TSF_FILE *file, const void *buf, u32 size)
{
if (!file || !file->vol || !file->vol->cfg) return TSF_RES_FS_ERROR;
if (size && !buf) return TSF_RES_FS_ERROR;
const u8 *data = (const u8*)buf;
TSF_CONFIG *cfg = file->vol->cfg;
u32 addr = file->chunk_addr;
while (size)
{
if (file->chunk_offset == cfg->block_size) // new chunk
{
if (tsf_take_new_chunk(file->vol, TSF_CHUNK_BODY, &addr) != TSF_RES_OK)
return TSF_RES_BULK_FULL;
file->chunk_addr = addr;
file->chunk_offset = sizeof(TSF_CHUNK);
cfg->hal_write_func(file->prev_chunk_addr + offsetof(TSF_CHUNK, next_chunk), &cfg->last_written_chunk, sizeof(cfg->last_written_chunk)); // save next chunk address to the previous one, cfg->last_written_chunk is set after tsf_take_new_chunk()
}
u16 sz = (u16)min(cfg->block_size - file->chunk_offset, size);
cfg->hal_write_func(file->chunk_addr + file->chunk_offset, data, sz);
file->seek += sz;
file->size += sz;
file->chunk_offset += sz;
data += sz;
size -= sz;
if (file->chunk_offset == cfg->block_size) // close current chunk
file->prev_chunk_addr = file->chunk_addr;
}
return TSF_RES_OK;
}
TSF_RESULT tsf_delete_chunk_range(TSF_VOLUME *vol, u32 addr, u32 blocks)
{
TSF_RESULT rc;
if (!vol || !vol->cfg || !blocks) return TSF_RES_FS_ERROR;
rc = tsf_init_chunk_range(vol->cfg, addr, blocks * vol->cfg->block_size);
if (rc != TSF_RES_OK)
return rc;
vol->free += blocks * vol->cfg->block_size;
return TSF_RES_OK;
}
TSF_RESULT tsf_delete(TSF_VOLUME *vol, const char *name)
{
if (!vol || !vol->cfg || !name) return TSF_RES_FS_ERROR;
u32 addr;
u16 next;
u32 range_addr = 0;
u32 range_blocks = 0;
TSF_CONFIG *cfg = vol->cfg;
TSF_RESULT rc = tsf_search(vol, &addr, name);
if (rc != TSF_RES_OK)
return rc;
do
{
u32 cur_addr = addr;
cfg->hal_read_func(addr + offsetof(TSF_CHUNK, next_chunk), &next, sizeof(next));
if (!range_blocks)
{
range_addr = cur_addr;
range_blocks = 1;
}
else if (cur_addr == range_addr + range_blocks * cfg->block_size)
{
range_blocks++;
}
else
{
rc = tsf_delete_chunk_range(vol, range_addr, range_blocks);
if (rc != TSF_RES_OK)
return rc;
range_addr = cur_addr;
range_blocks = 1;
}
if (next != 0xFFFF)
addr = chunk_addr(next);
} while (next != 0xFFFF);
rc = tsf_delete_chunk_range(vol, range_addr, range_blocks);
if (rc != TSF_RES_OK)
return rc;
if (vol->files_number)
vol->files_number--;
return TSF_RES_OK;
}
TSF_RESULT tsf_stat(TSF_VOLUME *vol, TSF_FILE_STAT *stat, const char *name)
{
if (!vol || !stat || !name) return TSF_RES_FS_ERROR;
u32 addr;
TSF_RESULT rc = tsf_search(vol, &addr, name);
if (rc != TSF_RES_OK)
return rc;
vol->cfg->hal_read_func(addr + sizeof(TSF_CHUNK) + offsetof(TSF_HDR, size), &stat->size, sizeof(stat->size));
return TSF_RES_OK;
}
TSF_RESULT tsf_list(TSF_VOLUME *vol, u8 flag)
{
if (!vol || !vol->cfg) return TSF_RES_FS_ERROR;
TSF_CONFIG *cfg = vol->cfg;
if (!cfg->buf || !cfg->buf_size) return TSF_RES_FS_ERROR;
if (flag == TSF_LIST_START)
{
vol->list_addr = cfg->bulk_start;
return TSF_RES_OK;
}
for (u32 addr = vol->list_addr; addr < (cfg->bulk_start + cfg->bulk_size); addr += cfg->block_size)
{
TSF_CHUNK chunk;
cfg->hal_read_func(addr, &chunk, sizeof(TSF_CHUNK));
if ((chunk.magic != TSF_MAGIC) || (chunk.type != (u8)TSF_CHUNK_HEAD))
continue;
u8 fnlen;
cfg->hal_read_func(addr + sizeof(TSF_CHUNK) + offsetof(TSF_HDR, fnlen), &fnlen, sizeof(fnlen));
fnlen = min(fnlen, cfg->buf_size - 1);
cfg->hal_read_func(addr + sizeof(TSF_CHUNK) + sizeof(TSF_HDR), cfg->buf, fnlen);
cfg->buf[fnlen] = 0;
vol->list_addr = addr + cfg->block_size;
return TSF_RES_OK;
}
return TSF_RES_NO_MORE_FILES;
}
+177
View File
@@ -0,0 +1,177 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// #define TSF_CHECK_BLANK // to test block after erase
// #define TSF_CHECK_EXIST_ON_CREATE // to look for existing filename when file is created
typedef int64_t s64;
typedef uint64_t u64;
typedef int32_t s32;
typedef uint32_t u32;
typedef int16_t s16;
typedef uint16_t u16;
typedef int8_t s8;
typedef uint8_t u8;
#ifndef min
#define min(x,y) ((x) < (y) ? (x) : (y))
#endif
#ifndef max
#define max(x,y) ((x) > (y) ? (x) : (y))
#endif
// enums
enum
{
TSF_MAGIC = ((u32)0x78E15CA3)
};
typedef enum
{
TSF_RES_OK = 0,
TSF_RES_FS_ERROR,
TSF_RES_FILE_NOT_FOUND,
TSF_RES_FILE_EXISTS,
TSF_RES_MODE_ERROR,
TSF_RES_BULK_FULL,
TSF_RES_NOT_BLANK,
TSF_RES_NO_MORE_FILES
} TSF_RESULT;
typedef enum
{
TSF_CHUNK_HEAD = 0x00,
TSF_CHUNK_BODY = 0x01,
TSF_CHUNK_FREE = 0xFF
} TSF_CHUNK_TYPE;
typedef enum
{
TSF_MODE_READ = 0x01,
TSF_MODE_WRITE = 0x02,
TSF_MODE_CREATE = 0x04,
TSF_MODE_CREATE_WRITE = TSF_MODE_CREATE | TSF_MODE_WRITE,
} TSF_MODE;
typedef enum
{
TSF_LIST_NEXT = 0,
TSF_LIST_START
} TSF_LIST;
// types
typedef TSF_RESULT (*tsf_read_t)(u32, void*, u32);
typedef TSF_RESULT (*tsf_write_t)(u32, const void*, u32);
typedef TSF_RESULT (*tsf_erase_t)(u32);
typedef TSF_RESULT (*tsf_erase_range_t)(u32, u32);
#ifdef _MSC_VER
#pragma pack(push, 1)
#define TSF_PACKED
#elif defined(__GNUC__)
#define TSF_PACKED __attribute__((packed))
#else
#define TSF_PACKED
#endif
typedef struct TSF_PACKED
{
u32 magic;
u16 next_chunk;
u8 type;
} TSF_CHUNK;
typedef struct TSF_PACKED
{
u32 size;
u8 fnlen;
} TSF_HDR;
typedef struct
{
u32 bulk_start; // start address in flash IC
u32 bulk_size; // size of the TSF
u32 block_size; // erase block size
u16 last_written_chunk; // for wear levelling
tsf_read_t hal_read_func;
tsf_write_t hal_write_func;
tsf_erase_t hal_erase_func;
tsf_erase_range_t hal_erase_range_func;
u8 *buf;
u16 buf_size;
} TSF_CONFIG;
typedef struct
{
u32 free;
u16 chunks_number;
u16 files_number;
u32 list_addr;
TSF_CONFIG *cfg;
} TSF_VOLUME;
typedef struct
{
u32 addr; // first chunk address
u32 size; // file size
u32 seek; // current pointer
u32 chunk_addr; // current chunk address
u32 chunk_offset; // current offset in chunk
u32 prev_chunk_addr; // address of the previous chunk
u16 next_chunk; // next chunk
u8 mode;
TSF_VOLUME *vol;
} TSF_FILE;
typedef struct
{
u32 size;
} TSF_FILE_STAT;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef TSF_PACKED
// functions
// file operations
TSF_RESULT tsf_open(TSF_VOLUME*, TSF_FILE*, const char*, u8);
TSF_RESULT tsf_read(TSF_FILE*, void*, u32);
TSF_RESULT tsf_write(TSF_FILE*, const void*, u32);
TSF_RESULT tsf_close(TSF_FILE*);
TSF_RESULT tsf_seek(TSF_FILE*, u32);
TSF_RESULT tsf_delete(TSF_VOLUME*, const char*);
TSF_RESULT tsf_stat(TSF_VOLUME*, TSF_FILE_STAT*, const char*);
TSF_RESULT tsf_list(TSF_VOLUME*, u8);
// volume operations
TSF_RESULT tsf_mount(TSF_CONFIG*, TSF_VOLUME*);
TSF_RESULT tsf_format(TSF_CONFIG*);
TSF_RESULT tsf_check(TSF_CONFIG*);
// auxilliary functions
TSF_RESULT tsf_init_chunk(TSF_CONFIG*, u32);
TSF_RESULT tsf_init_erased_chunk(TSF_CONFIG*, u32);
TSF_RESULT tsf_init_chunk_range(TSF_CONFIG*, u32, u32);
TSF_RESULT tsf_check_blank(TSF_CONFIG*, u32, u32);
TSF_RESULT tsf_search(TSF_VOLUME*, u32*, const char*);
TSF_RESULT tsf_open_for_read(TSF_FILE*, const char*);
TSF_RESULT tsf_create(TSF_FILE*, const char*);
TSF_RESULT tsf_take_new_chunk(TSF_VOLUME*, u8, u32*);
TSF_RESULT tsf_vol_stat(TSF_VOLUME*);
TSF_RESULT tsf_read_int(TSF_FILE*, void*, u32);
#define chunk_addr(chunk) (cfg->bulk_start + ((chunk) * cfg->block_size))
#ifdef __cplusplus
}
#endif
+434
View File
@@ -0,0 +1,434 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include <unistd.h>
#include <math.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/soc_caps.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_chip_info.h"
#include "esp_sleep.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "esp_private/esp_clk.h"
#include <esp_crc.h>
#include "driver/rtc_io.h"
#include "driver/uart.h"
#include "argtable3/argtable3.h"
#include "sdkconfig.h"
#include <esp_heap_caps.h>
#include "esp_timer.h"
#include "esp_err.h"
#include "esp_intr_alloc.h"
#include "freertos/queue.h"
#include "usb/usb_host.h"
#include "usb/hid_host.h"
#include "usb/hid_usage_mouse.h"
#include "main.h"
#include "usb_mouse.h"
#include "ps2_mouse.h"
#include "nvs_params.h"
enum usb_mouse_evt_group_t
{
USB_MOUSE_EVT_QUIT = 0,
USB_MOUSE_EVT_HID,
};
typedef struct
{
usb_mouse_evt_group_t event_group;
struct
{
hid_host_device_handle_t handle;
hid_host_driver_event_t event;
void *arg;
} hid;
} usb_mouse_evt_t;
QueueHandle_t usb_mouse_evt_queue = NULL;
TaskHandle_t usb_mouse_task_handle = NULL;
TaskHandle_t usb_mouse_lib_task_handle = NULL;
hid_host_device_handle_t usb_mouse_dev = NULL;
volatile bool usb_mouse_mode_active = false;
int usb_mouse_abs_x = 0;
int usb_mouse_abs_y = 0;
void usb_mouse_report_callback(const uint8_t *data, int length)
{
if (length < (int)sizeof(hid_mouse_input_report_boot_t))
return;
const hid_mouse_input_report_boot_t *r = (const hid_mouse_input_report_boot_t *)data;
usb_mouse_abs_x += r->x_displacement;
usb_mouse_abs_y += r->y_displacement;
ps2_mouse_send_movement((int)r->x_displacement,
-(int)r->y_displacement,
(unsigned)r->buttons.val);
}
void usb_mouse_interface_callback(hid_host_device_handle_t hid_device_handle, hid_host_interface_event_t event, void *arg)
{
(void)arg;
uint8_t data[64];
size_t data_length = 0;
hid_host_dev_params_t dev_params;
memset(data, 0, sizeof(data));
if (hid_host_device_get_params(hid_device_handle, &dev_params) != ESP_OK)
return;
switch (event)
{
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
if (hid_host_device_get_raw_input_report_data(hid_device_handle, data, sizeof(data), &data_length) != ESP_OK)
return;
if (dev_params.sub_class == HID_SUBCLASS_BOOT_INTERFACE && dev_params.proto == HID_PROTOCOL_MOUSE)
usb_mouse_report_callback(data, (int)data_length);
break;
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
printf("usb mouse disconnected\r\n");
if (usb_mouse_dev == hid_device_handle)
usb_mouse_dev = NULL;
usb_mouse_abs_x = 0;
usb_mouse_abs_y = 0;
hid_host_device_close(hid_device_handle);
break;
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
printf("usb mouse transfer error\r\n");
break;
default:
break;
}
}
void usb_mouse_process_device_event(hid_host_device_handle_t hid_device_handle, hid_host_driver_event_t event, void *arg)
{
(void)arg;
hid_host_dev_params_t dev_params;
if (hid_host_device_get_params(hid_device_handle, &dev_params) != ESP_OK)
return;
if (event != HID_HOST_DRIVER_EVENT_CONNECTED)
return;
if (dev_params.proto != HID_PROTOCOL_MOUSE)
{
printf("usb hid connected, but not a mouse (proto=%d)\r\n", (int)dev_params.proto);
return;
}
if (dev_params.sub_class != HID_SUBCLASS_BOOT_INTERFACE)
{
printf("usb mouse connected, but not boot protocol\r\n");
return;
}
const hid_host_device_config_t dev_config =
{
.callback = usb_mouse_interface_callback,
.callback_arg = NULL,
};
if (hid_host_device_open(hid_device_handle, &dev_config) != ESP_OK)
{
printf("hid_host_device_open failed\r\n");
return;
}
if (hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT) != ESP_OK)
{
printf("hid_class_request_set_protocol failed\r\n");
hid_host_device_close(hid_device_handle);
return;
}
if (hid_host_device_start(hid_device_handle) != ESP_OK)
{
printf("hid_host_device_start failed\r\n");
hid_host_device_close(hid_device_handle);
return;
}
usb_mouse_dev = hid_device_handle;
usb_mouse_abs_x = 0;
usb_mouse_abs_y = 0;
printf("usb mouse connected\r\n");
}
void usb_mouse_device_callback(hid_host_device_handle_t hid_device_handle, hid_host_driver_event_t event, void *arg)
{
usb_mouse_evt_t evt = {};
evt.event_group = USB_MOUSE_EVT_HID;
evt.hid.handle = hid_device_handle;
evt.hid.event = event;
evt.hid.arg = arg;
if (usb_mouse_evt_queue)
xQueueSend(usb_mouse_evt_queue, &evt, 0);
}
void usb_mouse_lib_task(void *arg)
{
const usb_host_config_t host_config =
{
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LOWMED,
};
esp_err_t err = usb_host_install(&host_config);
if (err != ESP_OK)
{
printf("usb_host_install failed: %s\r\n", esp_err_to_name(err));
usb_mouse_lib_task_handle = NULL;
xTaskNotifyGive((TaskHandle_t)arg);
vTaskDelete(NULL);
return;
}
xTaskNotifyGive((TaskHandle_t)arg);
while (1)
{
uint32_t event_flags = 0;
err = usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (err != ESP_OK)
{
printf("usb_host_lib_handle_events failed: %s\r\n", esp_err_to_name(err));
break;
}
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
{
usb_host_device_free_all();
break;
}
}
vTaskDelay(pdMS_TO_TICKS(10));
usb_host_uninstall();
usb_mouse_lib_task_handle = NULL;
vTaskDelete(NULL);
}
void usb_mouse_task(void *arg)
{
(void)arg;
usb_mouse_evt_t evt;
esp_err_t err;
usb_mouse_evt_queue = xQueueCreateWithCaps(8, sizeof(usb_mouse_evt_t), task_ram_type_non_critical);
if (!usb_mouse_evt_queue)
{
printf("usb mouse queue create failed\r\n");
usb_mouse_mode_active = false;
usb_mouse_task_handle = NULL;
vTaskDelete(NULL);
return;
}
if (xTaskCreatePinnedToCoreWithCaps(usb_mouse_lib_task, "usb_mouse_lib", 2048, xTaskGetCurrentTaskHandle(), USB_MOUSE_LIB_TASK_PRIO, &usb_mouse_lib_task_handle, 0, task_ram_type_critical) != pdTRUE)
{
printf("usb mouse lib task create failed\r\n");
vQueueDeleteWithCaps(usb_mouse_evt_queue);
usb_mouse_evt_queue = NULL;
usb_mouse_mode_active = false;
usb_mouse_task_handle = NULL;
vTaskDelete(NULL);
return;
}
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000));
const hid_host_driver_config_t hid_cfg =
{
.create_background_task = true,
.task_priority = USB_MOUSE_HID_TASK_PRIO,
.stack_size = 4096,
.core_id = 0,
.callback = usb_mouse_device_callback,
.callback_arg = NULL,
};
err = hid_host_install(&hid_cfg);
if (err != ESP_OK)
{
printf("hid_host_install failed: %s\r\n", esp_err_to_name(err));
vQueueDeleteWithCaps(usb_mouse_evt_queue);
usb_mouse_evt_queue = NULL;
usb_mouse_mode_active = false;
usb_mouse_task_handle = NULL;
vTaskDelete(NULL);
return;
}
printf("USB switched to host mode, connect mouse\r\n");
while (1)
{
if (!xQueueReceive(usb_mouse_evt_queue, &evt, portMAX_DELAY))
continue;
if (evt.event_group == USB_MOUSE_EVT_QUIT)
break;
if (evt.event_group == USB_MOUSE_EVT_HID)
usb_mouse_process_device_event(evt.hid.handle, evt.hid.event, evt.hid.arg);
}
if (usb_mouse_dev)
{
hid_host_device_stop(usb_mouse_dev);
hid_host_device_close(usb_mouse_dev);
usb_mouse_dev = NULL;
}
hid_host_uninstall();
for (int i = 0; i < 100; i++)
{
if (usb_mouse_lib_task_handle == NULL)
break;
vTaskDelay(pdMS_TO_TICKS(10));
}
QueueHandle_t q = usb_mouse_evt_queue;
usb_mouse_evt_queue = NULL;
if (q) vQueueDeleteWithCaps(q);
usb_mouse_mode_active = false;
usb_mouse_task_handle = NULL;
vTaskDelete(NULL);
}
extern "C" esp_err_t usb_mouse_start()
{
if (usb_mouse_mode_active)
return ESP_ERR_INVALID_STATE;
usb_mouse_mode_active = true;
if (xTaskCreatePinnedToCoreWithCaps(usb_mouse_task, "usb_mouse", 4096, NULL, USB_MOUSE_TASK_PRIO, &usb_mouse_task_handle, 0, task_ram_type_non_critical) != pdTRUE)
{
usb_mouse_mode_active = false;
usb_mouse_task_handle = NULL;
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
int usb_cmd(int argc, char **argv)
{
if (argc < 2)
{
printf("Usage:\r\n");
printf(" usb en <0|1>\r\n");
printf(" usb up\r\n");
return 0;
}
if (!strcmp(argv[1], "up"))
{
if (argc != 2)
{
printf("Usage: usb up\r\n");
return 1;
}
esp_err_t err = usb_mouse_start();
if (err == ESP_ERR_INVALID_STATE)
{
printf("usb mouse mode already active\r\n");
return 0;
}
if (err != ESP_OK)
{
printf("usb mouse task create failed: %s\r\n", esp_err_to_name(err));
return 1;
}
printf("usb mouse host started until reset (NVS unchanged)\r\n");
return 0;
}
if (strcmp(argv[1], "en"))
{
printf("Unknown subcommand: %s\r\n", argv[1]);
printf("Usage:\r\n");
printf(" usb en <0|1>\r\n");
printf(" usb up\r\n");
return 1;
}
if (argc < 3)
{
printf("Usage: usb en <0|1>\r\n");
return 1;
}
char *endp = NULL;
unsigned long en = strtoul(argv[2], &endp, 0);
if (!endp || *endp || en > 1)
{
printf("Bad <0|1>: %s\r\n", argv[2]);
return 1;
}
app_params.usb_mode = (uint8_t)en;
esp_err_t err = app_params_save();
if (err != ESP_OK)
{
printf("app_params_save failed: %s\r\n", esp_err_to_name(err));
return 1;
}
printf("USB enable: %u\r\n", app_params.usb_mode);
if (app_params.usb_mode)
{
err = usb_mouse_start();
if (err == ESP_ERR_INVALID_STATE)
{
printf("usb mouse mode already active\r\n");
return 0;
}
if (err != ESP_OK)
{
printf("usb mouse task create failed: %s\r\n", esp_err_to_name(err));
return 1;
}
printf("usb mouse host started\r\n");
return 0;
}
if (usb_mouse_mode_active)
printf("USB disable saved, active host mode will stay until reset\r\n");
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "esp_err.h"
extern "C" esp_err_t usb_mouse_start();
int usb_cmd(int argc, char **argv);
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
#pragma once
// Phase 1 of the SMB2/3 network-share feature (see project plan): an SMB2/3
// server task with a hardcoded fake filesystem, to prove the protocol layer
// works before anything is wired to the Z80/Wild Commander side.
#define VFS_SHARE_NAME_MAX 32
extern char vfs_share_name[VFS_SHARE_NAME_MAX + 1];
void vfs_bridge_init();
void smb_server_task(void *arg);
// Start/stop the SMB2/3 server on demand (ESP_CMD_VFS_SERVER_START/STOP,
// dispatched via TASK_VFS_SERVER_START/STOP in helper_task) instead of it
// running unconditionally once WiFi comes up - the Z80 side is single-
// tasking (it can only ever be driving one plugin at a time), so there is
// no point keeping the SMB server (or the other services it pauses -
// at_uart_suspend()/tracker_suspend()) active except while the smbvfs.wmf
// plugin is actually the thing running.
void vfs_bridge_start_server();
void vfs_bridge_stop_server();
+124
View File
@@ -0,0 +1,124 @@
// See vfs_request.h. Bridges vfs_bridge.cpp's SMB2 filesystem callbacks
// (running on smb_server_task) to the Z80 side over the SPI-slave
// protocol. Shared state is protected by a portMUX spinlock rather than a
// FreeRTOS mutex because vfs_spi_poll()/vfs_spi_get_request_data() run in
// spi_slave.cpp's IRAM_ATTR ISR-context command dispatch (mirrors
// ps2_mouse.cpp's ps2_mouse_lock pattern already used in this codebase).
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "main.h"
#include "spi_slave.h"
#include "vfs_request.h"
static portMUX_TYPE vfs_lock = portMUX_INITIALIZER_UNLOCKED;
static SemaphoreHandle_t vfs_call_mtx = NULL; // serializes concurrent vfs_command_sync() callers
static QueueHandle_t vfs_ack_queue = NULL; // depth 1, posted to by vfs_spi_submit_result()
static uint8_t s_req_buf[VFS_REQUEST_MAX];
static uint32_t s_req_len = 0;
static uint8_t s_req_op = 0;
static bool s_req_pending = false;
static uint8_t s_result_buf[DMA_BUF_SIZE];
static uint32_t s_result_len = 0;
static uint8_t s_result_status = 0;
void vfs_request_init()
{
if (!vfs_call_mtx) vfs_call_mtx = xSemaphoreCreateMutex();
if (!vfs_ack_queue) vfs_ack_queue = xQueueCreate(1, sizeof(uint8_t));
}
bool vfs_command_sync(uint8_t op, const uint8_t *req_payload, uint32_t req_len,
uint32_t timeout_ms, uint8_t *out_status,
const uint8_t **out_data, uint32_t *out_len)
{
if (req_len > VFS_REQUEST_MAX) return false;
ESP_LOGI("vfs_req", "sync: op=%u waiting for mutex", (unsigned)op);
xSemaphoreTake(vfs_call_mtx, portMAX_DELAY);
ESP_LOGI("vfs_req", "sync: op=%u got mutex, queuing request", (unsigned)op);
xQueueReset(vfs_ack_queue); // drop any stale ack from a previous timed-out call
portENTER_CRITICAL(&vfs_lock);
memcpy(s_req_buf, req_payload, req_len);
s_req_len = req_len;
s_req_op = op;
s_req_pending = true;
portEXIT_CRITICAL(&vfs_lock);
uint8_t dummy;
bool got_answer = xQueueReceive(vfs_ack_queue, &dummy, pdMS_TO_TICKS(timeout_ms)) == pdTRUE;
ESP_LOGI("vfs_req", "sync: op=%u %s", (unsigned)op, got_answer ? "got answer" : "TIMED OUT");
if (!got_answer)
{
// Give up waiting. A late answer from the Z80 side after this point
// would otherwise land on whatever the NEXT call queues up - clearing
// req_pending here means a late GET_REQUEST_DATA/SUBMIT_RESULT for
// THIS request just sees nothing pending / is harmlessly dropped.
portENTER_CRITICAL(&vfs_lock);
s_req_pending = false;
portEXIT_CRITICAL(&vfs_lock);
xSemaphoreGive(vfs_call_mtx);
return false;
}
portENTER_CRITICAL(&vfs_lock);
*out_status = s_result_status;
*out_data = s_result_buf;
*out_len = s_result_len;
portEXIT_CRITICAL(&vfs_lock);
xSemaphoreGive(vfs_call_mtx);
return true;
}
bool IRAM_ATTR vfs_spi_poll(uint8_t *out_op, uint32_t *out_size)
{
portENTER_CRITICAL_ISR(&vfs_lock);
bool pending = s_req_pending;
if (pending)
{
*out_op = s_req_op;
*out_size = s_req_len;
}
portEXIT_CRITICAL_ISR(&vfs_lock);
return pending;
}
uint32_t IRAM_ATTR vfs_spi_get_request_data(uint8_t *dma_buf, uint32_t max_size)
{
portENTER_CRITICAL_ISR(&vfs_lock);
uint32_t n = s_req_len;
if (n > max_size) n = max_size;
if (s_req_pending) memcpy(dma_buf, s_req_buf, n);
else n = 0;
portEXIT_CRITICAL_ISR(&vfs_lock);
return n;
}
void IRAM_ATTR vfs_spi_submit_result(uint8_t status, const uint8_t *dma_buf, uint32_t size)
{
if (size > DMA_BUF_SIZE) size = DMA_BUF_SIZE;
portENTER_CRITICAL_ISR(&vfs_lock);
memcpy(s_result_buf, dma_buf, size);
s_result_len = size;
s_result_status = status;
s_req_pending = false;
portEXIT_CRITICAL_ISR(&vfs_lock);
uint8_t dummy = 1;
xQueueSendFromISR(vfs_ack_queue, &dummy, NULL);
portYIELD_FROM_ISR();
}
+65
View File
@@ -0,0 +1,65 @@
#pragma once
// SPI-side half of the VFS bridge (see project plan, "Phase 2"). Turns an
// SMB2 filesystem callback (from vfs_bridge.cpp) into one request/response
// round trip over the existing SPI-slave protocol, answered by whatever is
// polling ESP_CMD_VFS_POLL on the Z80 side - a hardcoded test responder for
// now (test_spi_esp32), the real Wild Commander plugin once vfs.asm is
// ported (Phase 3).
//
// Wire format (all multi-byte fields little-endian; this is our own
// convention for now, not yet matched to vfs.asm's - revisit when porting
// it in Phase 3):
//
// Request (from ESP_CMD_VFS_GET_REQUEST_DATA), path-based ops
// (STAT/OPEN/DELETE/MKDIR/RMDIR/READDIR):
// u8 op (VFS_OP_*)
// u16 path_len
// u8 path[path_len] (UTF-8, no NUL terminator)
//
// Request, handle-based ops:
// READ: u8 op, u32 handle, u64 offset, u32 size
// WRITE: u8 op, u32 handle, u64 offset, u32 size, u8 data[size]
// CLOSE: u8 op, u32 handle
//
// Result (to ESP_CMD_VFS_SUBMIT_RESULT): status travels in ESP_REG_VFS_STATUS
// (0 = success), separate from the DMA payload below, which on error is empty:
// STAT: u8 is_dir, u64 size
// READDIR: u16 count, then count * { u8 name_len, u8 name[name_len], u8 is_dir, u64 size }
// OPEN: u32 handle
// READ: u8 data[N] (N inferred from the transfer size)
// WRITE: u32 written
// CLOSE/DELETE/MKDIR/RMDIR: (empty)
#include <stdint.h>
#include <stddef.h>
#define VFS_REQUEST_MAX 512 // path-based requests are small; enough headroom
// Result buffer is sized to DMA_BUF_SIZE (spi_slave.h, 16384) inside
// vfs_request.cpp - not redeclared here to avoid a second source of truth.
void vfs_request_init();
// Called from the SMB2 server task (vfs_bridge.cpp) for every filesystem
// operation. Builds the request payload, blocks until the Z80 side answers
// via ESP_CMD_VFS_SUBMIT_RESULT (or times out), and returns the raw result
// bytes. Returns false on timeout (no answer within timeout_ms); *out_data
// is only valid until the next call to vfs_command_sync (single shared
// buffer, single in-flight request at a time - matches this codebase's
// existing single-session philosophy).
bool vfs_command_sync(uint8_t op, const uint8_t *req_payload, uint32_t req_len,
uint32_t timeout_ms, uint8_t *out_status,
const uint8_t **out_data, uint32_t *out_len);
// ---- Called from spi_slave.cpp's ISR dispatch / DREQ handlers -----------
// ESP_CMD_VFS_POLL: cheap, answered directly in the ISR.
bool vfs_spi_poll(uint8_t *out_op, uint32_t *out_size);
// DREQ_VFS_REQUEST (prepare_tx_data): copies the pending request's payload
// into dma_buf. Returns the number of bytes copied.
uint32_t vfs_spi_get_request_data(uint8_t *dma_buf, uint32_t max_size);
// DREQ_VFS_RESULT (process_rx_data): copies dma_buf into the result buffer
// and wakes up the waiting vfs_command_sync() call.
void vfs_spi_submit_result(uint8_t status, const uint8_t *dma_buf, uint32_t size);
+243
View File
@@ -0,0 +1,243 @@
// WEATHER_GET adapted from ZiFi-ESP32-S3-Zero (616bee6); wire parser retained.
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstddef>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_http_client.h"
#include "esp_timer.h"
#include "esp_log.h"
#include "nvs.h"
#include "main.h"
#include "esp_spi_defs.h"
#include "spi_slave.h"
#include "mem_obj.h"
#include "wifi.h"
#include "weather.h"
#include "zifi/weather_parse.hpp"
namespace {
static const char *TAG = "weather";
constexpr size_t body_capacity = 4096;
char cached_country[3] = {};
char cached_zip[32] = {};
zifi::GeoResult cached_geo = {};
bool have_geo = false;
constexpr uint32_t geo_cache_magic = 0x314f4547; // "GEO1"
struct PersistedGeo {
uint32_t magic;
char country[3];
char zip[32];
zifi::GeoResult geo;
uint32_t checksum;
};
uint32_t geo_checksum(const PersistedGeo &value)
{
const auto *bytes = reinterpret_cast<const uint8_t *>(&value);
uint32_t hash = 2166136261u;
for (size_t i = 0; i < offsetof(PersistedGeo, checksum); ++i) {
hash = (hash ^ bytes[i]) * 16777619u;
}
return hash;
}
bool load_geo_cache(const char *country, const char *zip)
{
nvs_handle_t handle = 0;
if (nvs_open("weather", NVS_READONLY, &handle) != ESP_OK) return false;
PersistedGeo saved = {};
size_t size = sizeof(saved);
esp_err_t result = nvs_get_blob(handle, "geo", &saved, &size);
nvs_close(handle);
if (result != ESP_OK || size != sizeof(saved) || saved.magic != geo_cache_magic ||
saved.checksum != geo_checksum(saved) ||
saved.country[sizeof(saved.country) - 1] != 0 ||
saved.zip[sizeof(saved.zip) - 1] != 0 ||
saved.geo.place[sizeof(saved.geo.place) - 1] != 0 ||
strcmp(saved.country, country) || strcmp(saved.zip, zip) ||
!std::isfinite(saved.geo.latitude) || !std::isfinite(saved.geo.longitude) ||
saved.geo.latitude < -90.0f || saved.geo.latitude > 90.0f ||
saved.geo.longitude < -180.0f || saved.geo.longitude > 180.0f) return false;
cached_geo = saved.geo;
strcpy(cached_country, saved.country);
strcpy(cached_zip, saved.zip);
have_geo = true;
ESP_LOGI(TAG, "using saved coordinates for %s/%s", country, zip);
return true;
}
void save_geo_cache(const char *country, const char *zip)
{
PersistedGeo saved = {};
saved.magic = geo_cache_magic;
strlcpy(saved.country, country, sizeof(saved.country));
strlcpy(saved.zip, zip, sizeof(saved.zip));
saved.geo = cached_geo;
saved.checksum = geo_checksum(saved);
nvs_handle_t handle = 0;
esp_err_t result = nvs_open("weather", NVS_READWRITE, &handle);
if (result == ESP_OK) {
result = nvs_set_blob(handle, "geo", &saved, sizeof(saved));
if (result == ESP_OK) result = nvs_commit(handle);
nvs_close(handle);
}
if (result != ESP_OK) ESP_LOGW(TAG, "cannot save coordinates: %s", esp_err_to_name(result));
}
// Each attempt has a 15s I/O timeout and body deadline. New attempts may
// only start during the first 30s of the command, including after backoff.
bool download(const char *url, char *body, size_t &length, int64_t deadline,
char *error, size_t error_size)
{
for (int attempt = 0; attempt < 3; ++attempt) {
if (esp_timer_get_time() >= deadline) {
snprintf(error, error_size, "timeout");
return false;
}
esp_http_client_config_t cfg = {};
cfg.url = url;
cfg.timeout_ms = 15000;
cfg.disable_auto_redirect = true;
cfg.user_agent = "ZX-Evo-weather/1";
auto client = esp_http_client_init(&cfg);
if (!client) {
snprintf(error, error_size, "no memory");
return false;
}
length = 0;
bool ok = false, retry = true;
ESP_LOGI(TAG, "GET attempt %d: %s", attempt + 1, url);
snprintf(error, error_size, "connection failed");
esp_err_t open_result = esp_http_client_open(client, 0);
if (open_result == ESP_OK && esp_http_client_fetch_headers(client) >= 0) {
int status = esp_http_client_get_status_code(client);
ESP_LOGI(TAG, "HTTP %d from %s", status, url);
if (status < 200 || status >= 300) {
snprintf(error, error_size, "http %d", status);
retry = status == 429 || status >= 500;
} else if (esp_http_client_get_content_length(client) >= body_capacity) {
snprintf(error, error_size, "reply too long");
retry = false;
} else {
const int64_t body_deadline = esp_timer_get_time() + 15000000;
snprintf(error, error_size, "incomplete reply");
while (esp_timer_get_time() < body_deadline) {
int n = esp_http_client_read(client, body + length, body_capacity - 1 - length);
if (n > 0) length += n;
if (esp_http_client_is_complete_data_received(client)) {
ok = length != 0;
break;
}
if (length == body_capacity - 1) {
snprintf(error, error_size, "reply too long");
retry = false;
break;
}
if (n == -ESP_ERR_HTTP_EAGAIN) { vTaskDelay(1); continue; }
if (n <= 0) break;
}
}
} else if (open_result != ESP_OK) {
snprintf(error, error_size, "connect: %s", esp_err_to_name(open_result));
ESP_LOGW(TAG, "open failed for %s: %s", url, esp_err_to_name(open_result));
} else {
snprintf(error, error_size, "header failed");
ESP_LOGW(TAG, "headers failed for %s", url);
}
esp_http_client_cleanup(client);
body[length] = 0;
if (ok) return true;
if (!retry || attempt == 2) return false;
ESP_LOGW(TAG, "retrying after: %s", error);
vTaskDelay(pdMS_TO_TICKS(1000 * (attempt + 1)));
}
return false;
}
}
void weather_do_get()
{
const int input = rd_reg8(ESP_REG_OBJ_HANDLE);
wr_reg32(ESP_REG_DATA_SIZE, 0);
if (!check_handle(input) ||
(mem_obj[input].type != OBJ_TYPE_DATA && mem_obj[input].type != OBJ_TYPE_DATAF)) {
set_status(ESP_ERR_INV_HANDLE);
return;
}
// Request: two NUL-terminated strings, ISO country code and postal code.
const char *data = static_cast<const char *>(mem_obj[input].addr);
const size_t size = mem_obj[input].size;
if (size < 5 || !data || data[2] != 0 ||
!((data[0] >= 'A' && data[0] <= 'Z') || (data[0] >= 'a' && data[0] <= 'z')) ||
!((data[1] >= 'A' && data[1] <= 'Z') || (data[1] >= 'a' && data[1] <= 'z'))) {
set_status(ESP_ERR_INV_PARAM);
return;
}
size_t zip_len = strnlen(data + 3, size - 3 < sizeof(cached_zip) ? size - 3 : sizeof(cached_zip));
if (!zip_len || zip_len >= sizeof(cached_zip) || zip_len >= size - 3) {
set_status(ESP_ERR_INV_STR_LEN);
return;
}
char country[3] = {data[0], data[1], 0};
char zip[32];
memcpy(zip, data + 3, zip_len + 1);
if (!wifi_is_connected()) { set_status(ESP_ERR_AP_NOT_CONNECTED); return; }
char *body = static_cast<char *>(malloc(body_capacity));
if (!body) { set_status(ESP_ERR_OUT_OF_MEMORY); return; }
const int64_t deadline = esp_timer_get_time() + 30000000;
char url[512], error[64] = {};
size_t length = 0;
bool ok = true;
if (!have_geo || strcmp(country, cached_country) || strcmp(zip, cached_zip)) {
have_geo = load_geo_cache(country, zip);
}
if (!have_geo) {
char encoded[94];
size_t j = 0;
for (size_t i = 0; i < zip_len; ++i) {
unsigned char c = zip[i];
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') || c == '-') encoded[j++] = c;
else { snprintf(encoded + j, sizeof(encoded) - j, "%%%02X", c); j += 3; }
}
encoded[j] = 0;
snprintf(url, sizeof(url), "http://api.zippopotam.us/%s/%s", country, encoded);
ok = download(url, body, length, deadline, error, sizeof(error)) &&
zifi::parseZippopotam(body, length, cached_geo, error, sizeof(error));
if (ok) {
strcpy(cached_country, country);
strcpy(cached_zip, zip);
have_geo = true;
save_geo_cache(country, zip);
}
}
uint8_t record[zifi::kWeatherRecordSize] = {};
if (ok) {
snprintf(url, sizeof(url),
"http://api.open-meteo.com/v1/forecast?latitude=%.4f&longitude=%.4f"
"&current=temperature_2m,weather_code,is_day,wind_speed_10m,precipitation,surface_pressure"
"&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_sum,sunrise,sunset"
"&timezone=auto&forecast_days=6&timeformat=unixtime",
static_cast<double>(cached_geo.latitude), static_cast<double>(cached_geo.longitude));
ok = download(url, body, length, deadline, error, sizeof(error)) &&
zifi::parseOpenMeteo(body, length, cached_geo.place, record, error, sizeof(error));
}
free(body);
if (!ok) {
ESP_LOGW(TAG, "%s", error);
set_status(ESP_ERR_WEATHER);
return;
}
int output = make_obj(sizeof(record), OBJ_TYPE_DATA);
if (output < 0) return; // make_obj publishes the precise allocation error.
memcpy(mem_obj[output].addr, record, sizeof(record));
wr_reg8(ESP_REG_OBJ_HANDLE, output);
wr_reg32(ESP_REG_DATA_OFFSET, 0);
wr_reg32(ESP_REG_DATA_SIZE, sizeof(record));
set_status(ESP_ST_READY);
}
+4
View File
@@ -0,0 +1,4 @@
#pragma once
// Called only by the serialized helper task.
void weather_do_get();
+723
View File
@@ -0,0 +1,723 @@
#include "zifi/weather_parse.hpp"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
namespace zifi {
namespace {
// --- крошечный разбор JSON: ровно то, что нужно двум ответам -----------------------
// Полного дерева не строим: ищем ключи на верхнем уровне объекта, пропуская
// чужие значения целиком. Этого достаточно, потому что структура ответов
// известна заранее, а память ESP дорога.
void setError(char* error, size_t errorSize, const char* text) {
if (error != nullptr && errorSize != 0) {
snprintf(error, errorSize, "%s", text);
}
}
bool isSpace(char c) {
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
const char* skipSpaces(const char* p, const char* end) {
while (p < end && isSpace(*p)) {
++p;
}
return p;
}
// p указывает на открывающую кавычку; вернуть позицию за закрывающей.
const char* skipString(const char* p, const char* end) {
++p;
while (p < end) {
if (*p == '\\') {
p += 2;
continue;
}
if (*p == '"') {
return p + 1;
}
++p;
}
return nullptr;
}
// Пропустить любое значение: объект, массив, строку, число или литерал.
const char* skipValue(const char* p, const char* end) {
p = skipSpaces(p, end);
if (p >= end) {
return nullptr;
}
if (*p == '"') {
return skipString(p, end);
}
if (*p == '{' || *p == '[') {
int depth = 0;
while (p < end) {
if (*p == '"') {
p = skipString(p, end);
if (p == nullptr) {
return nullptr;
}
continue;
}
if (*p == '{' || *p == '[') {
++depth;
} else if (*p == '}' || *p == ']') {
--depth;
if (depth == 0) {
return p + 1;
}
}
++p;
}
return nullptr;
}
while (p < end && *p != ',' && *p != '}' && *p != ']' && !isSpace(*p)) {
++p;
}
return p;
}
// Найти ключ на верхнем уровне объекта [begin, end) и вернуть указатель на
// его значение; nullptr — ключа нет.
const char* findKey(const char* begin, const char* end, const char* key) {
const size_t keyLength = strlen(key);
const char* p = skipSpaces(begin, end);
if (p < end && *p == '{') {
++p;
}
while (true) {
p = skipSpaces(p, end);
if (p >= end || *p != '"') {
return nullptr;
}
const char* nameEnd = skipString(p, end);
if (nameEnd == nullptr) {
return nullptr;
}
const bool match = static_cast<size_t>(nameEnd - p) == keyLength + 2 &&
memcmp(p + 1, key, keyLength) == 0;
p = skipSpaces(nameEnd, end);
if (p >= end || *p != ':') {
return nullptr;
}
p = skipSpaces(p + 1, end);
if (match) {
return p;
}
p = skipValue(p, end);
if (p == nullptr) {
return nullptr;
}
p = skipSpaces(p, end);
if (p < end && *p == ',') {
++p;
}
}
}
// Число (возможно в кавычках, как lat/lon у Nominatim); null считается нулём.
bool parseNumber(const char* p, const char* end, double& out, const char** next) {
p = skipSpaces(p, end);
if (p >= end) {
return false;
}
bool quoted = false;
if (*p == '"') {
quoted = true;
++p;
}
if (end - p >= 4 && memcmp(p, "null", 4) == 0) {
out = 0.0;
p += 4;
} else {
char* stop = nullptr;
out = strtod(p, &stop);
if (stop == p || stop > end) {
return false;
}
p = stop;
}
if (quoted) {
if (p >= end || *p != '"') {
return false;
}
++p;
}
if (next != nullptr) {
*next = p;
}
return true;
}
void appendUtf8(char* out, size_t capacity, size_t& length, uint32_t code) {
char buffer[4];
size_t count;
if (code < 0x80) {
buffer[0] = static_cast<char>(code);
count = 1;
} else if (code < 0x800) {
buffer[0] = static_cast<char>(0xC0 | (code >> 6));
buffer[1] = static_cast<char>(0x80 | (code & 0x3F));
count = 2;
} else {
buffer[0] = static_cast<char>(0xE0 | (code >> 12));
buffer[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
buffer[2] = static_cast<char>(0x80 | (code & 0x3F));
count = 3;
}
for (size_t i = 0; i < count && length + 1 < capacity; ++i) {
out[length++] = buffer[i];
}
}
// Строка в кавычках с раскодированием \", \\, \/ и \uXXXX (в UTF-8).
bool parseString(const char* p, const char* end, char* out, size_t capacity) {
p = skipSpaces(p, end);
if (p >= end || *p != '"' || capacity == 0) {
return false;
}
++p;
size_t length = 0;
while (p < end && *p != '"') {
char c = *p++;
if (c == '\\' && p < end) {
c = *p++;
if (c == 'u' && end - p >= 4) {
char hex[5] = {p[0], p[1], p[2], p[3], 0};
appendUtf8(out, capacity, length, static_cast<uint32_t>(strtoul(hex, nullptr, 16)));
p += 4;
continue;
}
if (c == 'n' || c == 't' || c == 'r') {
c = ' ';
}
}
if (length + 1 < capacity) {
out[length++] = c;
}
}
out[length] = 0;
return p < end;
}
// Массив чисел -> out[0..max); вернуть число прочитанных элементов.
size_t parseNumberArray(const char* p, const char* end, double* out, size_t max) {
p = skipSpaces(p, end);
if (p >= end || *p != '[') {
return 0;
}
++p;
size_t count = 0;
while (true) {
p = skipSpaces(p, end);
if (p >= end || *p == ']') {
return count;
}
double value = 0.0;
const char* next = nullptr;
if (!parseNumber(p, end, value, &next)) {
return count;
}
if (count < max) {
out[count] = value;
}
++count;
p = skipSpaces(next, end);
if (p < end && *p == ',') {
++p;
}
}
}
bool objectNumber(const char* begin, const char* end, const char* key, double& out) {
const char* value = findKey(begin, end, key);
return value != nullptr && parseNumber(value, end, out, nullptr);
}
// --- календарь для unixtime -----------------------------------------------------------
// Дата по числу дней от 1970-01-01 (алгоритм Говарда Хиннанта).
void civilFromDays(int64_t z, int& year, unsigned& month, unsigned& day) {
z += 719468;
const int64_t era = (z >= 0 ? z : z - 146096) / 146097;
const unsigned doe = static_cast<unsigned>(z - era * 146097);
const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
const int64_t y = static_cast<int64_t>(yoe) + era * 400;
const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
const unsigned mp = (5 * doy + 2) / 153;
day = doy - (153 * mp + 2) / 5 + 1;
month = mp < 10 ? mp + 3 : mp - 9;
year = static_cast<int>(y + (month <= 2 ? 1 : 0));
}
struct LocalTime {
int year;
unsigned month;
unsigned day;
unsigned weekday; // 0 — понедельник
unsigned hour;
unsigned minute;
};
LocalTime localTime(int64_t unixSeconds, int32_t offsetSeconds) {
const int64_t local = unixSeconds + offsetSeconds;
int64_t days = local / 86400;
int64_t seconds = local - days * 86400;
if (seconds < 0) {
seconds += 86400;
--days;
}
LocalTime result;
civilFromDays(days, result.year, result.month, result.day);
// 1970-01-01 — четверг, то есть 3 при нумерации с понедельника.
result.weekday = static_cast<unsigned>(((days + 3) % 7 + 7) % 7);
result.hour = static_cast<unsigned>(seconds / 3600);
result.minute = static_cast<unsigned>((seconds % 3600) / 60);
return result;
}
uint8_t clampU8(double value) {
const long rounded = lround(value);
return static_cast<uint8_t>(rounded < 0 ? 0 : (rounded > 255 ? 255 : rounded));
}
int8_t clampI8(double value) {
const long rounded = lround(value);
return static_cast<int8_t>(rounded < -128 ? -128 : (rounded > 127 ? 127 : rounded));
}
uint16_t clampU16(double value) {
const long rounded = lround(value);
return static_cast<uint16_t>(rounded < 0 ? 0 : (rounded > 65535 ? 65535 : rounded));
}
void putU16(uint8_t* at, uint16_t value) {
at[0] = static_cast<uint8_t>(value);
at[1] = static_cast<uint8_t>(value >> 8);
}
// Латиница с диакритикой (U+00C0..U+017F) -> базовая буква ASCII.
char latinBase(uint32_t code) {
static const struct {
uint32_t first;
uint32_t last;
char base;
} kRanges[] = {
{0xC0, 0xC5, 'A'}, {0xC7, 0xC7, 'C'}, {0xC8, 0xCB, 'E'}, {0xCC, 0xCF, 'I'},
{0xD1, 0xD1, 'N'}, {0xD2, 0xD6, 'O'}, {0xD8, 0xD8, 'O'}, {0xD9, 0xDC, 'U'},
{0xDD, 0xDD, 'Y'}, {0xDF, 0xDF, 's'}, {0xE0, 0xE5, 'a'}, {0xE7, 0xE7, 'c'},
{0xE8, 0xEB, 'e'}, {0xEC, 0xEF, 'i'}, {0xF1, 0xF1, 'n'}, {0xF2, 0xF6, 'o'},
{0xF8, 0xF8, 'o'}, {0xF9, 0xFC, 'u'}, {0xFD, 0xFD, 'y'}, {0xFF, 0xFF, 'y'},
{0x100, 0x105, 'a'}, {0x106, 0x10D, 'c'}, {0x10E, 0x111, 'd'}, {0x112, 0x11B, 'e'},
{0x11C, 0x123, 'g'}, {0x124, 0x127, 'h'}, {0x128, 0x131, 'i'}, {0x134, 0x135, 'j'},
{0x136, 0x138, 'k'}, {0x139, 0x142, 'l'}, {0x143, 0x149, 'n'}, {0x14C, 0x151, 'o'},
{0x154, 0x159, 'r'}, {0x15A, 0x161, 's'}, {0x162, 0x167, 't'}, {0x168, 0x173, 'u'},
{0x174, 0x175, 'w'}, {0x176, 0x178, 'y'}, {0x179, 0x17E, 'z'},
};
for (const auto& range : kRanges) {
if (code >= range.first && code <= range.last) {
return range.base;
}
}
return '?';
}
// Знаки вне А..я и Ё ё, у которых есть замена в CP866; 0 — замены нет.
// Украинские и белорусские Є є Ї ї Ў ў в CP866 есть (#F2..#F7). І і там
// нет — пишем латинские I i, они выглядят так же; Ґ ґ заменяем на Г г.
// Типографские апостроф, тире и кавычки бывают в названиях OpenStreetMap
// («Кам’янець-Подільський») — их заменяют знаки ASCII.
char cp866Extra(uint32_t code) {
static const struct {
uint16_t code;
uint8_t cp866;
} kExtra[] = {
{0x404, 0xF2}, {0x454, 0xF3}, {0x407, 0xF4}, {0x457, 0xF5}, // Є є Ї ї
{0x40E, 0xF6}, {0x45E, 0xF7}, // Ў ў
{0x406, 'I'}, {0x456, 'i'}, {0x490, 0x83}, {0x491, 0xA3}, // І і Ґ ґ
{0xB0, 0xF8}, // °
{0xA0, ' '}, // неразрывный пробел
{0x2BC, '\''}, {0x2018, '\''}, {0x2019, '\''}, // апострофы
{0x2013, '-'}, {0x2014, '-'}, // тире
{0xAB, '"'}, {0xBB, '"'}, {0x201C, '"'}, {0x201D, '"'}, {0x201E, '"'},
};
for (const auto& extra : kExtra) {
if (code == extra.code) {
return static_cast<char>(extra.cp866);
}
}
return 0;
}
// --- кодировки значений zifi.ini ----------------------------------------------------------
// Буква кириллицы по коду CP866 (#80..#FF); 0 — не буква (псевдографика, знаки).
uint32_t cp866Letter(uint8_t code) {
static const uint16_t kF0[] = {0x401, 0x451, 0x404, 0x454, 0x407, 0x457, 0x40E, 0x45E};
if (code >= 0x80 && code <= 0xAF) {
return 0x410 + (code - 0x80); // А..п
}
if (code >= 0xE0 && code <= 0xEF) {
return 0x440 + (code - 0xE0); // р..я
}
if (code >= 0xF0 && code <= 0xF7) {
return kF0[code - 0xF0]; // Ё ё Є є Ї ї Ў ў
}
return 0;
}
// Буква кириллицы по коду CP1251 (#80..#FF); 0 — не буква.
uint32_t cp1251Letter(uint8_t code) {
static const struct {
uint8_t code;
uint16_t letter;
} kExtra[] = {
{0xA8, 0x401}, {0xB8, 0x451}, {0xAA, 0x404}, {0xBA, 0x454}, // Ё ё Є є
{0xAF, 0x407}, {0xBF, 0x457}, {0xB2, 0x406}, {0xB3, 0x456}, // Ї ї І і
{0xA5, 0x490}, {0xB4, 0x491}, {0xA1, 0x40E}, {0xA2, 0x45E}, // Ґ ґ Ў ў
};
if (code >= 0xC0) {
return 0x410 + (code - 0xC0); // А..я
}
for (const auto& extra : kExtra) {
if (code == extra.code) {
return extra.letter;
}
}
return 0;
}
// Длина правильной последовательности UTF-8 с первого байта p (1..4);
// 0 — это не UTF-8 (одиночный байт продолжения, обрыв, «длинная» запись).
size_t utf8SequenceLength(const unsigned char* p) {
if (p[0] < 0x80) {
return 1;
}
size_t count;
if (p[0] >= 0xC2 && p[0] <= 0xDF) {
count = 2;
} else if (p[0] >= 0xE0 && p[0] <= 0xEF) {
count = 3;
} else if (p[0] >= 0xF0 && p[0] <= 0xF4) {
count = 4;
} else {
return 0;
}
for (size_t i = 1; i < count; ++i) {
if ((p[i] & 0xC0) != 0x80) {
return 0; // в том числе конец строки
}
}
if ((p[0] == 0xE0 && p[1] < 0xA0) || (p[0] == 0xED && p[1] >= 0xA0) ||
(p[0] == 0xF0 && p[1] < 0x90) || (p[0] == 0xF4 && p[1] >= 0x90)) {
return 0; // «длинная» запись или суррогат
}
return count;
}
} // namespace
void utf8ToCp866(const char* text, char* out, size_t capacity) {
if (capacity == 0) {
return;
}
size_t length = 0;
const unsigned char* p = reinterpret_cast<const unsigned char*>(text);
while (*p != 0 && length + 1 < capacity) {
uint32_t code;
if (*p < 0x80) {
code = *p++;
} else if ((*p & 0xE0) == 0xC0 && (p[1] & 0xC0) == 0x80) {
code = ((*p & 0x1F) << 6) | (p[1] & 0x3F);
p += 2;
} else if ((*p & 0xF0) == 0xE0 && (p[1] & 0xC0) == 0x80 && (p[2] & 0xC0) == 0x80) {
code = ((*p & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F);
p += 3;
} else {
code = '?';
++p;
while ((*p & 0xC0) == 0x80) {
++p;
}
}
char c;
if (code < 0x80) {
c = static_cast<char>(code);
} else if (code >= 0x410 && code <= 0x43F) {
c = static_cast<char>(0x80 + (code - 0x410)); // А..п
} else if (code >= 0x440 && code <= 0x44F) {
c = static_cast<char>(0xE0 + (code - 0x440)); // р..я
} else if (code == 0x401) {
c = static_cast<char>(0xF0); // Ё
} else if (code == 0x451) {
c = static_cast<char>(0xF1); // ё
} else {
c = cp866Extra(code);
if (c == 0) {
c = latinBase(code);
}
}
out[length++] = c;
}
out[length] = 0;
}
void iniTextToUtf8(const char* text, char* out, size_t capacity) {
if (capacity == 0) {
return;
}
const unsigned char* src = reinterpret_cast<const unsigned char*>(text);
size_t length = 0;
// Уже UTF-8 (или чистый ASCII) — копируем целыми буквами.
bool utf8 = true;
for (const unsigned char* p = src; *p != 0;) {
const size_t count = utf8SequenceLength(p);
if (count == 0) {
utf8 = false;
break;
}
p += count;
}
if (utf8) {
for (const unsigned char* p = src; *p != 0;) {
const size_t count = utf8SequenceLength(p);
if (length + count >= capacity) {
break;
}
memcpy(out + length, p, count);
length += count;
p += count;
}
out[length] = 0;
return;
}
// Однобайтовая кириллица: CP866 или CP1251 — какая даёт больше букв.
// В CP866 прописные CP1251 (#C0..#DF) — псевдографика, а в CP1251 почти
// все буквы CP866 #80..#AF — знаки, поэтому ошибиться трудно. Поровну —
// CP866, родная кодировка Спектрума.
size_t letters866 = 0;
size_t letters1251 = 0;
for (const unsigned char* p = src; *p != 0; ++p) {
if (*p >= 0x80) {
letters866 += cp866Letter(*p) != 0 ? 1 : 0;
letters1251 += cp1251Letter(*p) != 0 ? 1 : 0;
}
}
const bool cp1251 = letters1251 > letters866;
for (const unsigned char* p = src; *p != 0; ++p) {
uint32_t code = *p;
if (code >= 0x80) {
code = cp1251 ? cp1251Letter(*p) : cp866Letter(*p);
if (code == 0) {
code = '?';
}
}
const size_t count = code < 0x80 ? 1 : 2; // кириллица в UTF-8 — два байта
if (length + count >= capacity) {
break;
}
appendUtf8(out, capacity, length, code);
}
out[length] = 0;
}
bool parseCitySearch(const char* json, size_t length, GeoResult& out, bool& notFound,
char* error, size_t errorSize) {
const char* end = json + length;
notFound = false;
const char* p = skipSpaces(json, end);
if (p >= end || *p != '{') {
setError(error, errorSize, "city: broken json");
return false;
}
// Ничего не нашёл — в ответе нет results: {"generationtime_ms":0.4}.
const char* results = findKey(json, end, "results");
if (results == nullptr) {
notFound = true;
setError(error, errorSize, "city: not found");
return false;
}
if (*results != '[') {
setError(error, errorSize, "city: broken json");
return false;
}
p = skipSpaces(results + 1, end);
if (p < end && *p == ']') {
notFound = true;
setError(error, errorSize, "city: not found");
return false;
}
// count=1: первое место списка — самое крупное из одноимённых.
const char* placeEnd = (p < end && *p == '{') ? skipValue(p, end) : nullptr;
if (placeEnd == nullptr) {
setError(error, errorSize, "city: broken json");
return false;
}
double latitude = 0.0;
double longitude = 0.0;
if (!objectNumber(p, placeEnd, "latitude", latitude) ||
!objectNumber(p, placeEnd, "longitude", longitude)) {
setError(error, errorSize, "city: no coordinates");
return false;
}
GeoResult result = {};
result.latitude = static_cast<float>(latitude);
result.longitude = static_cast<float>(longitude);
const char* name = findKey(p, placeEnd, "name");
if (name == nullptr || !parseString(name, placeEnd, result.place, sizeof(result.place))) {
result.place[0] = 0; // без названия — только погода
}
out = result;
return true;
}
bool parseZippopotam(const char* json, size_t length, GeoResult& out,
char* error, size_t errorSize) {
const char* end = json + length;
const char* places = findKey(json, end, "places");
if (places == nullptr || *places != '[') {
setError(error, errorSize, "zip: no places");
return false;
}
const char* p = places + 1;
bool found = false;
while (true) {
p = skipSpaces(p, end);
if (p >= end || *p == ']') {
break;
}
if (*p != '{') {
break;
}
const char* objectEnd = skipValue(p, end);
if (objectEnd == nullptr) {
break;
}
GeoResult candidate = {};
double latitude = 0.0;
double longitude = 0.0;
const char* name = findKey(p, objectEnd, "place name");
if (objectNumber(p, objectEnd, "latitude", latitude) &&
objectNumber(p, objectEnd, "longitude", longitude) &&
name != nullptr && parseString(name, objectEnd, candidate.place, sizeof(candidate.place))) {
candidate.latitude = static_cast<float>(latitude);
candidate.longitude = static_cast<float>(longitude);
out = candidate; // остаётся последнее место списка
found = true;
}
p = skipSpaces(objectEnd, end);
if (p < end && *p == ',') {
++p;
}
}
if (!found) {
setError(error, errorSize, "zip: no coordinates");
}
return found;
}
bool parseOpenMeteo(const char* json, size_t length, const char* placeUtf8,
uint8_t* record, char* error, size_t errorSize) {
const char* end = json + length;
memset(record, 0, kWeatherRecordSize);
record[kWrVersion] = kWeatherRecordVersion;
double offset = 0.0;
if (!objectNumber(json, end, "utc_offset_seconds", offset)) {
setError(error, errorSize, "meteo: no utc offset");
return false;
}
const int32_t offsetSeconds = static_cast<int32_t>(offset);
const char* current = findKey(json, end, "current");
const char* daily = findKey(json, end, "daily");
if (current == nullptr || *current != '{' || daily == nullptr || *daily != '{') {
setError(error, errorSize, "meteo: no current/daily");
return false;
}
const char* currentEnd = skipValue(current, end);
const char* dailyEnd = skipValue(daily, end);
if (currentEnd == nullptr || dailyEnd == nullptr) {
setError(error, errorSize, "meteo: broken json");
return false;
}
double temperature, code, isDay, wind, precipitation, pressure, dataTime;
if (!objectNumber(current, currentEnd, "temperature_2m", temperature) ||
!objectNumber(current, currentEnd, "weather_code", code) ||
!objectNumber(current, currentEnd, "is_day", isDay) ||
!objectNumber(current, currentEnd, "wind_speed_10m", wind) ||
!objectNumber(current, currentEnd, "precipitation", precipitation) ||
!objectNumber(current, currentEnd, "surface_pressure", pressure) ||
!objectNumber(current, currentEnd, "time", dataTime)) {
setError(error, errorSize, "meteo: current fields");
return false;
}
// double, а не float: unixtime ~1,8e9 во float (24 бита мантиссы) теряет
// до двух минут — восход и закат уезжали бы на минуту.
double times[kWeatherMaxDays], codes[kWeatherMaxDays], tmax[kWeatherMaxDays];
double tmin[kWeatherMaxDays], sums[kWeatherMaxDays];
double sunrise[kWeatherMaxDays], sunset[kWeatherMaxDays];
struct {
const char* key;
double* out;
} arrays[] = {
{"time", times}, {"weather_code", codes}, {"temperature_2m_max", tmax},
{"temperature_2m_min", tmin}, {"precipitation_sum", sums},
{"sunrise", sunrise}, {"sunset", sunset},
};
size_t count = kWeatherMaxDays;
for (const auto& array : arrays) {
const char* value = findKey(daily, dailyEnd, array.key);
const size_t got = value == nullptr ? 0 : parseNumberArray(value, dailyEnd, array.out,
kWeatherMaxDays);
if (got == 0) {
setError(error, errorSize, "meteo: daily fields");
return false;
}
if (got < count) {
count = got;
}
}
record[kWrStatus] = 1;
utf8ToCp866(placeUtf8 == nullptr ? "" : placeUtf8,
reinterpret_cast<char*>(record + kWrPlace), kWeatherPlaceLength);
record[kWrTemp] = static_cast<uint8_t>(clampI8(temperature));
record[kWrCode] = clampU8(code);
record[kWrIsDay] = isDay != 0.0 ? 1 : 0;
putU16(record + kWrWind10, clampU16(wind * 10.0));
putU16(record + kWrPrecip10, clampU16(precipitation * 10.0));
putU16(record + kWrPressure, clampU16(pressure * 0.750062)); // гПа -> мм рт. ст.
const LocalTime rise = localTime(static_cast<int64_t>(sunrise[0]), offsetSeconds);
const LocalTime set = localTime(static_cast<int64_t>(sunset[0]), offsetSeconds);
const LocalTime now = localTime(static_cast<int64_t>(dataTime), offsetSeconds);
record[kWrSunrise] = static_cast<uint8_t>(rise.hour);
record[kWrSunrise + 1] = static_cast<uint8_t>(rise.minute);
record[kWrSunset] = static_cast<uint8_t>(set.hour);
record[kWrSunset + 1] = static_cast<uint8_t>(set.minute);
record[kWrDataTime] = static_cast<uint8_t>(now.hour);
record[kWrDataTime + 1] = static_cast<uint8_t>(now.minute);
record[kWrDayCount] = static_cast<uint8_t>(count);
for (size_t i = 0; i < count; ++i) {
uint8_t* day = record + kWrDays + i * kWeatherDaySize;
// Полдень местного дня: unixtime полуночи плюс смещение уже даёт нужную
// дату, а полдень защищает от округления на границе суток.
const LocalTime date = localTime(static_cast<int64_t>(times[i]) + 43200, offsetSeconds);
day[0] = static_cast<uint8_t>(date.day);
day[1] = static_cast<uint8_t>(date.month);
day[2] = static_cast<uint8_t>(date.weekday);
day[3] = clampU8(codes[i]);
day[4] = static_cast<uint8_t>(clampI8(tmin[i]));
day[5] = static_cast<uint8_t>(clampI8(tmax[i]));
putU16(day + 6, clampU16(sums[i] * 10.0));
}
return true;
}
} // namespace zifi
+1527
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define CONNECT_TIMEOUT_MS (10000)
#define DEFAULT_SCAN_LIST_SIZE 64
void initialize_wifi();
int wf_scan(int timeout);
uint16_t wf_get_ap_num();
void wf_get_ap(int idx, uint8_t &auth, int8_t &rssi, uint8_t &chan, uint8_t *&ssid);
void get_ip(uint8_t *i, uint8_t *m, uint8_t *g);
bool wifi_connect(const char *ssid, const char *pass, int timeout_ms);
bool wifi_connect_saved(int timeout_ms);
bool wifi_is_enabled();
bool wifi_is_connected();
bool wifi_has_saved_ap();
void wifi_start_autoconnect();
void wifi_disconnect_now();
void esp_console_register_wifi_commands();
// Shared mutex serializing WiFi radio access between the SPI-slave path
// (main/helper.cpp) and the AT-command UART path (main/at_uart.cpp) - there is
// only one physical radio, so both interfaces must take this before touching
// esp_wifi_*/net.state.
extern SemaphoreHandle_t net_mtx;
bool wifi_connect_locked(const char *ssid, const char *pass, int timeout_ms);
void wifi_disconnect_now_locked();
int wf_scan_locked(int timeout);
// SNTP config, backing AT+CIPSNTPCFG / AT+CIPSNTPTIME (main/at_uart.cpp).
// timezone is a plain hour offset (-12..14, per ESP-AT), applied on top of the
// UTC time SNTP itself provides - not a full POSIX TZ. srv2/srv3 may be NULL.
void sntp_apply_config(bool enable, int timezone, const char *srv1, const char *srv2, const char *srv3);
void sntp_get_config(bool &enable, int &timezone, char *srv1, char *srv2, char *srv3, size_t buflen);
void sntp_format_time(char *buf, size_t buflen);
+79
View File
@@ -0,0 +1,79 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
// Разбор ответов погодных сервисов в двоичную запись для Z80.
//
// Модуль не зависит от Arduino: его собирает и host-тест на ПК
// (tests/test_weather_parse.py) на сохранённых ответах серверов.
// Формат записи един для прошивки, плагина (Weather screensaver/src/weather.asm)
// и эталона (Weather screensaver/tools/design.py); менять только вместе.
namespace zifi {
constexpr uint8_t kWeatherRecordVersion = 1;
constexpr size_t kWeatherPlaceLength = 24;
constexpr size_t kWeatherMaxDays = 6;
constexpr size_t kWeatherDaySize = 8;
// Смещения полей записи.
enum WeatherRecordLayout : size_t {
kWrStatus = 0, // 1 — данные есть
kWrVersion = 1, // kWeatherRecordVersion
kWrPlace = 2, // название места, CP866, ноль в конце, 24 байта
kWrTemp = 26, // температура, °C, знаковый байт
kWrCode = 27, // код погоды WMO
kWrIsDay = 28, // 1 — день
kWrWind10 = 29, // ветер, км/ч x10, u16
kWrPrecip10 = 31, // осадки за час, мм x10, u16
kWrPressure = 33, // давление у поверхности, мм рт. ст., u16
kWrSunrise = 35, // час, минута
kWrSunset = 37, // час, минута
kWrDataTime = 39, // местное время данных: час, минута
kWrDayCount = 41, // число дневных записей
kWrDays = 42, // дневные записи по 8 байт: число, месяц, день недели
// (0 — понедельник), код WMO, tmin, tmax, осадки x10 u16
};
constexpr size_t kWeatherRecordSize = kWrDays + kWeatherDaySize * kWeatherMaxDays;
struct GeoResult {
float latitude;
float longitude;
char place[64]; // UTF-8, название места для экрана
};
// Ответ геокодера Open-Meteo на поиск по названию
// (geocoding-api.open-meteo.com/v1/search?name=…&count=1&language=en|ru):
// координаты и название места на языке запроса («Kyiv», «Рим»; у маленьких
// мест без перевода — местное название). notFound=true — в ответе
// нет results: такого названия геокодер не знает, повтор не поможет.
bool parseCitySearch(const char* json, size_t length, GeoResult& out, bool& notFound,
char* error, size_t errorSize);
// Ответ api.zippopotam.us/<страна>/<индекс> (поиск по почтовому индексу,
// ключ zip:): координаты и название места (берётся последнее место списка —
// для итальянских CAP это коммуна).
bool parseZippopotam(const char* json, size_t length, GeoResult& out,
char* error, size_t errorSize);
// Значение из zifi.ini -> UTF-8 (в out, с нулём в конце). Файл могли
// сохранить в UTF-8 (редактор на ПК), в CP866 (редактор Wild Commander) или
// в CP1251 (Блокнот Windows «ANSI»). Правильный UTF-8 остаётся как есть,
// иначе берётся та из CP866 и CP1251, в которой больше кириллических букв.
void iniTextToUtf8(const char* text, char* out, size_t capacity);
// Ответ api.open-meteo.com/v1/forecast (current + daily, timeformat=unixtime,
// timezone=auto) -> запись kWeatherRecordSize байт. placeUtf8 — название
// места в UTF-8, как его дал справочник; в CP866 оно переводится только
// здесь, один раз (повторный перевод CP866 превращал кириллицу в «??»).
bool parseOpenMeteo(const char* json, size_t length, const char* placeUtf8,
uint8_t* record, char* error, size_t errorSize);
// UTF-8 -> CP866: кириллица переводится (с украинскими и белорусскими
// буквами), латиница с диакритикой упрощается до базовой буквы, типографские
// апостроф, тире и кавычки — до знаков ASCII, остальное заменяется '?'.
// Строка обрезается по capacity.
void utf8ToCp866(const char* text, char* out, size_t capacity);
} // namespace zifi
+435
View File
@@ -0,0 +1,435 @@
const u8 zx_screen[6912] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0x9F, 0x00, 0xFD, 0xE6,
0x00, 0x07, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x3E, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xF0, 0x3B, 0xFC, 0x60, 0x80, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0xC7, 0xE0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x30,
0x00, 0x02, 0x00, 0x09, 0xC0, 0x2F, 0xF0, 0xC8, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xA0, 0x63, 0xC4, 0x00, 0xFC, 0xD0, 0x0D, 0x40, 0x00, 0x00,
0x00, 0xFF, 0x00, 0x18, 0x00, 0x00, 0x4F, 0x8C, 0x06, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x61, 0xC0, 0x40, 0x13, 0xE0, 0xE0, 0x78, 0x2F, 0x81, 0xC2, 0x05, 0xC6,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x08, 0x30, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0C, 0x00, 0x00, 0x89, 0x03, 0x89, 0x1F, 0x70, 0x00, 0x02, 0xAD, 0xEF, 0xFD, 0xF8,
0x10, 0x0E, 0xFF, 0x00, 0x00, 0x40, 0x00, 0x63, 0xBF, 0x80, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x23, 0xD9, 0x89, 0xCC, 0x6A, 0x0F, 0x00, 0x02, 0x03, 0xA4, 0x5D, 0xAC,
0xFC, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x14, 0x07, 0x49, 0xE1, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xF7, 0x23, 0x20, 0xD3, 0xC9,
0x0C, 0x0C, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x78, 0xFC, 0x40, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xC0, 0x67, 0xE1, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x1D, 0x11, 0x00, 0x00, 0x0D, 0xE0, 0x00, 0x06, 0x60,
0x00, 0x00, 0x02, 0x07, 0x08, 0x8F, 0xC0, 0xF0, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x90, 0xF8, 0x27, 0xC8, 0x40, 0xF0, 0xE0, 0x0A, 0x80, 0x00, 0x00,
0x00, 0xFF, 0x00, 0x20, 0x00, 0x00, 0x1E, 0x31, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x82, 0x80, 0x11, 0x26, 0x24, 0xE0, 0x38, 0x1F, 0x11, 0x44, 0x03, 0xC8,
0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x10, 0x30, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x00, 0x30, 0x05, 0x55, 0x82, 0x9F, 0xB0, 0x00, 0x01, 0x5A, 0x5B, 0xFC, 0xA0,
0x00, 0x1C, 0xFF, 0x00, 0x00, 0x01, 0x00, 0xC7, 0xFC, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x5B, 0x1D, 0xA4, 0x64, 0x8F, 0x40, 0x48, 0x35, 0x10, 0xAB, 0xA0,
0xF0, 0x02, 0x00, 0x0E, 0x04, 0xFF, 0x00, 0x0C, 0x43, 0xDA, 0x84, 0x08, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAC, 0x07, 0xC0, 0x83, 0x03,
0x38, 0x18, 0xC1, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x81, 0xE0, 0xF7, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0x00,
0x01, 0x80, 0x8F, 0x48, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0E, 0x10, 0x00, 0x01, 0x03, 0xC0, 0x00, 0x1F, 0x00,
0x00, 0x00, 0x00, 0x0C, 0x10, 0x37, 0x00, 0xE0, 0x08, 0xF8, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xF4, 0x26, 0x89, 0x00, 0xF0, 0xD0, 0x05, 0x40, 0x00, 0x00,
0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x78, 0xC1, 0x18, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x01, 0x06, 0x09, 0x0C, 0x28, 0xA0, 0x78, 0x2F, 0x08, 0xC2, 0x0B, 0xC0,
0xC0, 0x00, 0x00, 0x40, 0xFF, 0xFF, 0x02, 0x00, 0x60, 0xEC, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x71, 0xC5, 0x46, 0x86, 0x9E, 0x70, 0x00, 0x08, 0x00, 0x25, 0xFE, 0x80,
0x07, 0x20, 0xFF, 0x00, 0x02, 0x02, 0x00, 0x8A, 0x71, 0x06, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xCE, 0x73, 0x2C, 0xE9, 0xC6, 0x8F, 0x04, 0x01, 0x01, 0x81, 0x11, 0xC0,
0xE6, 0x00, 0x00, 0x1C, 0x00, 0xFF, 0x01, 0x0A, 0x46, 0xB0, 0x08, 0x30, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x50, 0x04, 0x81, 0x06, 0x01,
0x70, 0x30, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x02, 0xDE, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0x20,
0x02, 0x00, 0x1E, 0x01, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7A, 0x22, 0x00, 0x06, 0x07, 0x80, 0x00, 0x78, 0x80,
0x00, 0x00, 0x00, 0x10, 0x60, 0xCC, 0x00, 0xC0, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xD0, 0x26, 0x4E, 0x10, 0xB0, 0xE0, 0x01, 0xC0, 0x00, 0x01,
0x00, 0xFF, 0x40, 0x00, 0x00, 0x01, 0xE2, 0x03, 0x20, 0x01, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x0C, 0x55, 0x19, 0x59, 0x70, 0x38, 0x05, 0x05, 0x60, 0x17, 0x80,
0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x41, 0xC1, 0x88, 0x02, 0x80, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x00, 0xE4, 0xC7, 0xAE, 0x0D, 0xB9, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x82,
0x0D, 0xE1, 0xFF, 0x00, 0x18, 0x0C, 0x00, 0x05, 0x43, 0x1C, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x9C, 0x6B, 0x1D, 0xA0, 0xC4, 0x0F, 0x00, 0x12, 0x90, 0x28, 0x6B, 0xC0,
0xB8, 0x10, 0x00, 0x30, 0x00, 0xFF, 0x00, 0x88, 0x22, 0xB1, 0x13, 0xE0, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x19, 0x00, 0x08, 0x07, 0x0C, 0x00,
0xE0, 0xE0, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x08, 0x0C, 0xB0, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x00,
0x00, 0x00, 0x38, 0x13, 0x38, 0x38, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x9B, 0xC2, 0x00, 0x38, 0x0E, 0x00, 0x00, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE3, 0x30, 0x00, 0x00, 0x06, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x45, 0x4C, 0x41, 0x70, 0xD0, 0x0A, 0xC0, 0x00, 0x00,
0x00, 0xFF, 0xE0, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x03, 0x1C, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x21, 0x82, 0x15, 0xF3, 0xD0, 0x30, 0x4A, 0x00, 0xFD, 0x0E, 0xC0,
0xB0, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0x03, 0x87, 0x80, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xC0, 0x97, 0xAF, 0x4B, 0x30, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88,
0x17, 0xC0, 0xFF, 0x00, 0x30, 0x18, 0x00, 0x40, 0x46, 0x98, 0x46, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x30, 0x2B, 0x39, 0x64, 0x80, 0x07, 0x00, 0x81, 0x05, 0x45, 0x81, 0x80,
0xF0, 0x40, 0x00, 0xC0, 0x00, 0xFF, 0x08, 0x24, 0x02, 0x69, 0x24, 0xC2, 0x00, 0x1E, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x37, 0x00, 0x01, 0x1F, 0x00, 0x00,
0xCB, 0xC0, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x39, 0x00, 0x00, 0x00, 0x60, 0xFF, 0xFF, 0x00,
0x00, 0x00, 0x60, 0x01, 0x70, 0x5C, 0x80, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x05, 0x8A, 0x44, 0x00, 0x70, 0x58, 0x00, 0x01, 0xE3, 0x00,
0x02, 0x00, 0x00, 0x03, 0xC6, 0x40, 0x03, 0x00, 0x4D, 0xE0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x09, 0x55, 0x5B, 0x83, 0x38, 0xE0, 0x04, 0x80, 0x58, 0x00,
0x08, 0xFF, 0x40, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x04, 0x06, 0x38, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0x10, 0x07, 0x22, 0x2C, 0xA6, 0xF0, 0x40, 0x01, 0x0C, 0xDB, 0x87, 0xC3,
0xE0, 0x20, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x0F, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x26, 0xA6, 0xC3, 0x6C, 0xF0, 0x00, 0x02, 0x02, 0x02, 0x01, 0xC0,
0x1F, 0x98, 0xFF, 0x00, 0x00, 0x30, 0x00, 0xA2, 0x92, 0x36, 0xEC, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x40, 0x2A, 0x33, 0x70, 0xA4, 0x63, 0x00, 0x0A, 0x20, 0xBA, 0x88, 0x80,
0xC0, 0x00, 0x02, 0x00, 0x01, 0xFF, 0x00, 0x56, 0x23, 0x53, 0x4C, 0x8C, 0x00, 0x78, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF0, 0x8F, 0x00, 0x03, 0x7F, 0x20, 0x00,
0xD7, 0x81, 0x00, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x18, 0xE7, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0x80,
0x00, 0x00, 0x80, 0x03, 0xC3, 0xFB, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x69, 0x90, 0x44, 0x03, 0x08, 0x00, 0x0A, 0x04, 0x76, 0x00,
0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x17, 0xC0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x06, 0x04, 0x0D, 0x5C, 0x07, 0x70, 0xD0, 0x81, 0xC0, 0x1E, 0x04,
0x60, 0xFF, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1F, 0x02, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x44, 0x0C, 0x18, 0xF0, 0x00, 0x12, 0x2A, 0xAB, 0x4E, 0xCC,
0xC3, 0x81, 0xC0, 0x00, 0xFF, 0xFF, 0x11, 0x3C, 0x18, 0x94, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x11, 0x0E, 0x94, 0x87, 0x59, 0xF0, 0x04, 0x02, 0x23, 0xA8, 0x20, 0xC0,
0x3B, 0xC0, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x04, 0x02, 0x56, 0xB9, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x22, 0x6B, 0x38, 0xCA, 0xC3, 0x00, 0x01, 0x09, 0x7C, 0x45, 0x00,
0x80, 0x00, 0x20, 0x00, 0x00, 0xFF, 0x00, 0x43, 0x22, 0x2F, 0x58, 0x38, 0x01, 0xF0, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04,
0x02, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0F, 0xC0, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00,
0xBF, 0x87, 0x81, 0x01, 0xF0, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x73, 0xCF, 0x40, 0x00, 0x01, 0xE0, 0xFF, 0xFF, 0x04,
0x00, 0x00, 0x00, 0x06, 0x9F, 0xAC, 0x03, 0x82, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x9D, 0xC0, 0x91, 0xC4, 0x08, 0x20, 0x20, 0x3F, 0x80, 0x28, 0x00,
0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x37, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3F, 0x0F, 0x04, 0x9B, 0x78, 0x0F, 0x38, 0xE0, 0x0A, 0xC4, 0x1F, 0x93,
0xC0, 0xFF, 0x00, 0x00, 0x80, 0x00, 0x00, 0x3F, 0x00, 0x19, 0x38, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x40, 0x1E, 0x45, 0x09, 0x78, 0xD0, 0x00, 0x01, 0x15, 0x56, 0xB7, 0xBE,
0x07, 0x3F, 0x80, 0x00, 0xFF, 0xFF, 0x03, 0xFA, 0x31, 0x68, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x16, 0x18, 0xC2, 0x40, 0xF0, 0x02, 0x15, 0x55, 0x55, 0x62, 0x40,
0xF7, 0x00, 0xFF, 0x03, 0x00, 0x82, 0x02, 0x14, 0x03, 0x2C, 0xBE, 0xC2, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x92, 0x6B, 0xB4, 0x13, 0x03, 0x00, 0x44, 0x03, 0xFE, 0x80, 0x00,
0x00, 0x00, 0x06, 0x00, 0x08, 0xFF, 0x02, 0x32, 0x15, 0x1A, 0x48, 0x70, 0x03, 0xE0, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x6B, 0xB8, 0x80, 0x09, 0x00, 0x09, 0x12, 0x00, 0x59, 0x01,
0x00, 0xF7, 0xE3, 0x00, 0x90, 0x00, 0x00, 0x0C, 0x14, 0xC6, 0xB1, 0xE4, 0x0F, 0xC0, 0xE0, 0x00,
0x00, 0x01, 0xFE, 0x07, 0xD4, 0x11, 0x11, 0xE1, 0x58, 0x7C, 0x00, 0x09, 0x03, 0x0A, 0x12, 0x78,
0x38, 0xC0, 0xE0, 0x1C, 0xEE, 0x10, 0x00, 0x15, 0x29, 0xCA, 0x58, 0x70, 0x04, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x19, 0xE1, 0xC4, 0x86, 0x20, 0x18, 0x00, 0x13, 0x05, 0x5B, 0x18, 0x00,
0x80, 0x01, 0xF0, 0x60, 0x18, 0x00, 0x10, 0x8A, 0x25, 0x84, 0x81, 0x04, 0x10, 0x0C, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x90, 0xD0, 0x86, 0x84, 0xB8, 0x78, 0x00, 0x09, 0x92, 0x00, 0x40, 0x80,
0x16, 0x00, 0x80, 0x5E, 0x80, 0x00, 0x72, 0x92, 0x11, 0xEC, 0x63, 0x91, 0x40, 0x0C, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x18, 0x00, 0x09, 0x3A, 0x50, 0x00, 0x13, 0x47, 0xFF, 0x34, 0xC7,
0x06, 0xF0, 0x6E, 0x01, 0x01, 0x80, 0x00, 0x8A, 0x48, 0xA1, 0x64, 0x29, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x00, 0x02, 0x02, 0x43, 0x6C, 0xFD, 0xC0, 0x00, 0x09, 0x03, 0x63, 0x9A, 0xC1,
0x01, 0xC0, 0x00, 0x20, 0x0C, 0x0D, 0x00, 0xC9, 0x12, 0xA4, 0xC6, 0x20, 0xC8, 0x09, 0x00, 0x00,
0x00, 0x00, 0x00, 0x16, 0x00, 0x18, 0x03, 0x13, 0x68, 0xC0, 0x00, 0x09, 0x84, 0x82, 0x07, 0x9B,
0x00, 0xC0, 0x80, 0x00, 0x80, 0x73, 0x00, 0x1C, 0x19, 0x2E, 0xC8, 0x89, 0x26, 0x33, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x02, 0x9F, 0x00, 0x00, 0x00, 0x02, 0x78, 0x80, 0xA2, 0x0C,
0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x82, 0x00, 0xDD, 0x05, 0xC2, 0xC6, 0xF0, 0xE0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x00, 0xD8, 0x4B, 0x31, 0xD0, 0x73, 0x00, 0x02, 0x06, 0x00, 0x8C, 0x87,
0x00, 0xCF, 0x87, 0x01, 0x30, 0x02, 0x00, 0x82, 0x09, 0x44, 0xE0, 0xC0, 0x1F, 0xC1, 0xC0, 0x00,
0x00, 0x03, 0x3C, 0x0E, 0x64, 0x04, 0x98, 0xCD, 0x50, 0x7C, 0x00, 0x23, 0x14, 0xF7, 0x24, 0x40,
0x70, 0x72, 0x08, 0x70, 0xBC, 0x00, 0x11, 0x05, 0x95, 0xAA, 0xB2, 0x80, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1D, 0xCB, 0x70, 0x60, 0x0A, 0x41, 0x3C, 0x00, 0x07, 0x12, 0xED, 0x1A, 0x87,
0x0C, 0x66, 0xE0, 0x90, 0x30, 0x00, 0x84, 0x4A, 0x4D, 0x94, 0x40, 0x00, 0x10, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x09, 0xF0, 0xA6, 0x8C, 0x41, 0xF0, 0x00, 0x82, 0x04, 0x00, 0x88, 0x80,
0x6C, 0x00, 0x80, 0xA0, 0xC0, 0x04, 0x12, 0x8A, 0x32, 0x58, 0x67, 0x33, 0x00, 0x38, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x14, 0x80, 0x02, 0xE8, 0xB8, 0x01, 0x0F, 0x12, 0x08, 0x09, 0xCE,
0x0C, 0xE0, 0x93, 0x08, 0x80, 0xC0, 0x00, 0x8A, 0x0C, 0xE1, 0x6C, 0x48, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x40, 0x0C, 0x0A, 0x63, 0x70, 0x68, 0xF0, 0x00, 0x02, 0x14, 0x21, 0x58, 0xC0,
0x02, 0xD0, 0x00, 0x10, 0x43, 0x1B, 0x00, 0x95, 0x21, 0x94, 0xE0, 0x23, 0x93, 0x60, 0x00, 0x00,
0x00, 0x08, 0x05, 0x03, 0x90, 0x18, 0x00, 0x2B, 0xC0, 0xC0, 0x00, 0x82, 0x31, 0x10, 0xC1, 0x69,
0x80, 0xC0, 0x40, 0x00, 0x40, 0x06, 0x04, 0x0E, 0x4C, 0xA3, 0xD1, 0x08, 0x4C, 0x66, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x32, 0x06, 0x04, 0x02, 0x02, 0x00, 0x00, 0x4B, 0x90, 0x80, 0x11, 0x65,
0x61, 0x00, 0x00, 0x18, 0x01, 0x80, 0x08, 0x00, 0x26, 0x0A, 0x86, 0x89, 0xE8, 0x20, 0x00, 0x00,
0x00, 0x00, 0x02, 0xC0, 0x31, 0x99, 0xCF, 0x31, 0x20, 0x83, 0x00, 0x24, 0x66, 0x00, 0x20, 0x1F,
0x00, 0x86, 0x0E, 0x83, 0x60, 0x00, 0x00, 0x00, 0x0D, 0x9B, 0x62, 0x90, 0x3D, 0x83, 0xB0, 0x00,
0x00, 0x06, 0x78, 0x18, 0x62, 0x16, 0xAC, 0x08, 0x70, 0x7C, 0x00, 0x09, 0x4C, 0x5A, 0x01, 0x00,
0xFC, 0xFC, 0x01, 0xC1, 0x78, 0x00, 0x08, 0x96, 0x41, 0x2C, 0x31, 0x08, 0x60, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x39, 0xC1, 0x68, 0x24, 0x44, 0x0C, 0x4C, 0x02, 0x0B, 0x81, 0x5C, 0x58, 0x9E,
0x39, 0xFC, 0xE0, 0x88, 0x80, 0x00, 0x00, 0x0A, 0x84, 0x82, 0x82, 0x10, 0x20, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x9A, 0xD8, 0x2C, 0x11, 0xE1, 0xF8, 0x00, 0x24, 0x06, 0x00, 0x61, 0x80,
0xD8, 0x00, 0x80, 0x07, 0x40, 0x00, 0x0B, 0x12, 0x19, 0x11, 0x4D, 0x26, 0x00, 0x70, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x18, 0x50, 0x8C, 0xC1, 0x18, 0x00, 0x07, 0x01, 0xA5, 0x40, 0xD8,
0x20, 0xC0, 0x09, 0x03, 0xE0, 0x00, 0x04, 0xC4, 0x84, 0xC3, 0x4C, 0x90, 0x4C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x60, 0x04, 0x28, 0x03, 0x50, 0xF4, 0xF0, 0x00, 0x24, 0x0D, 0x0C, 0xB2, 0xC0,
0x09, 0xE0, 0xC0, 0x10, 0x30, 0xF3, 0x12, 0x33, 0x22, 0xAC, 0xA0, 0x4E, 0x0C, 0x80, 0x00, 0x00,
0x00, 0x05, 0x2F, 0xA4, 0xE8, 0x10, 0x84, 0x88, 0x00, 0xC0, 0x00, 0x24, 0x12, 0xB8, 0x46, 0x22,
0x80, 0x80, 0x00, 0x00, 0x20, 0x00, 0x01, 0x73, 0x42, 0x45, 0xA0, 0x30, 0x91, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x04, 0x08, 0xCD, 0x03, 0x80, 0x00, 0x25, 0xB0, 0x40, 0x88, 0x20,
0xC9, 0x00, 0x00, 0x04, 0x01, 0x80, 0xC0, 0x00, 0x09, 0x05, 0xCC, 0xC3, 0x01, 0x80, 0x00, 0x00,
0x00, 0x00, 0x01, 0xC0, 0x73, 0x99, 0x8A, 0x74, 0x00, 0x83, 0x02, 0x09, 0x20, 0x00, 0x82, 0x8D,
0x00, 0x0C, 0x20, 0x05, 0xC0, 0x18, 0x00, 0x21, 0x05, 0x2A, 0xC4, 0x30, 0xF1, 0x03, 0x70, 0x00,
0x00, 0x08, 0x60, 0x20, 0x30, 0x32, 0x8A, 0x50, 0xE4, 0x20, 0x00, 0x17, 0x01, 0x94, 0xC8, 0x03,
0xE1, 0xF0, 0x43, 0x07, 0xC0, 0x00, 0x48, 0x12, 0x51, 0x05, 0x63, 0x10, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x72, 0x62, 0x70, 0xB0, 0x44, 0x07, 0x24, 0x00, 0x25, 0x0A, 0x97, 0x24, 0xB0,
0x3F, 0xF0, 0xC0, 0xA8, 0x00, 0x00, 0xC2, 0x24, 0x4C, 0x95, 0x20, 0x00, 0x60, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0A, 0x04, 0xE6, 0xE8, 0x68, 0x32, 0x9E, 0xE0, 0x00, 0x09, 0x10, 0x80, 0xC4, 0x01,
0x1C, 0x00, 0x80, 0x09, 0x20, 0x00, 0xC9, 0x2B, 0x2C, 0x25, 0x48, 0x64, 0x00, 0xF8, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x20, 0x0C, 0x41, 0x14, 0x70, 0xC8, 0x00, 0x21, 0x4A, 0xD9, 0x88, 0x80,
0x00, 0xF0, 0x01, 0x80, 0x18, 0x70, 0x10, 0x48, 0x89, 0xA7, 0x98, 0x30, 0x5C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x2C, 0x84, 0x0C, 0x42, 0xF0, 0xA0, 0xF0, 0x00, 0x09, 0x50, 0x45, 0x65, 0xC0,
0x47, 0xE0, 0x20, 0x04, 0x0C, 0x07, 0x01, 0xE2, 0x91, 0xD8, 0x85, 0x4D, 0x62, 0x00, 0x00, 0x00,
0x00, 0x00, 0x98, 0x90, 0xC4, 0x10, 0x0A, 0x53, 0x00, 0xC0, 0x03, 0x09, 0xC4, 0x44, 0x0E, 0x80,
0x00, 0x80, 0x00, 0x00, 0x08, 0x01, 0x00, 0xCD, 0x85, 0x23, 0xC1, 0x31, 0x26, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x0C, 0x0E, 0x04, 0x38, 0xC3, 0x00, 0x01, 0x0A, 0xA0, 0x80, 0x08, 0x83,
0xF1, 0x00, 0x80, 0x04, 0x03, 0x03, 0x98, 0x00, 0x02, 0xA3, 0x44, 0x4C, 0x16, 0x30, 0x00, 0x00,
0x00, 0x00, 0x07, 0x80, 0xBB, 0x89, 0x94, 0x6C, 0x09, 0x0B, 0x00, 0x04, 0x0A, 0x00, 0x50, 0x91,
0x01, 0x10, 0x01, 0x0F, 0x80, 0x30, 0x08, 0x31, 0x09, 0x29, 0x45, 0x61, 0x82, 0x04, 0x60, 0x00,
0x00, 0x10, 0xC0, 0x00, 0x58, 0x22, 0x56, 0xA2, 0xC9, 0x1C, 0x00, 0x81, 0x04, 0x07, 0x92, 0x3F,
0x9B, 0xC0, 0x08, 0x1E, 0x00, 0x00, 0x00, 0x82, 0x07, 0x97, 0x66, 0x41, 0xE0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xC4, 0x22, 0x38, 0x10, 0xC9, 0x12, 0x70, 0x00, 0x0B, 0x00, 0xBE, 0x40, 0x80,
0x6F, 0xE0, 0xC0, 0x74, 0x00, 0x40, 0x91, 0x24, 0xC6, 0x88, 0x44, 0x20, 0xC4, 0x00, 0x00, 0x00,
0x00, 0x00, 0x21, 0x08, 0xC5, 0x6C, 0x44, 0x28, 0x33, 0xD8, 0x00, 0x04, 0x4A, 0x80, 0x50, 0x80,
0x38, 0x01, 0x80, 0x80, 0xE0, 0x00, 0x21, 0x06, 0x9D, 0x69, 0x54, 0x48, 0x05, 0xA0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x30, 0x18, 0x00, 0x80, 0x1E, 0xF8, 0x00, 0x0A, 0x00, 0xA8, 0x10, 0xC0,
0x04, 0xE0, 0x04, 0x80, 0xC4, 0x20, 0x30, 0x45, 0x15, 0x86, 0xD1, 0x33, 0xBC, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x0C, 0x2C, 0x40, 0xA2, 0xC8, 0xF0, 0x00, 0x04, 0x05, 0x02, 0x52, 0x80,
0x1E, 0xC0, 0x00, 0x04, 0x03, 0x0F, 0x00, 0xC2, 0xDC, 0xA8, 0x4A, 0xCA, 0xCC, 0x00, 0x04, 0x00,
0x00, 0x00, 0x03, 0x49, 0x70, 0x10, 0x04, 0x8F, 0x04, 0xC0, 0x07, 0x04, 0x48, 0x82, 0x24, 0x1B,
0x40, 0x80, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xCA, 0xF5, 0xA1, 0x78, 0x1C, 0xE0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x1A, 0x00, 0x23, 0x03, 0x06, 0x03, 0xE0, 0x00, 0x04, 0x4D,
0xF0, 0x00, 0x00, 0x08, 0x02, 0x01, 0xFC, 0x00, 0x00, 0x45, 0x9A, 0xA3, 0x9B, 0x80, 0x00, 0x00,
0x00, 0x00, 0x2F, 0x01, 0xB3, 0x89, 0x90, 0xE0, 0x12, 0x13, 0x08, 0x41, 0x04, 0x80, 0xA0, 0x03,
0x00, 0x00, 0x03, 0x5E, 0x08, 0x40, 0x00, 0x08, 0x8B, 0x49, 0x2B, 0x40, 0x04, 0x00, 0x80, 0x00,
0x00, 0x01, 0x00, 0x81, 0x98, 0x47, 0x2B, 0xC6, 0x8B, 0x78, 0x22, 0x0A, 0x30, 0x8D, 0x08, 0x7F,
0xE6, 0x00, 0x00, 0x07, 0x41, 0x00, 0x22, 0x12, 0x13, 0x0B, 0xCC, 0xC3, 0x84, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0x22, 0xB9, 0x32, 0x80, 0x29, 0xB8, 0x00, 0x1F, 0x05, 0x45, 0x30, 0x80,
0x87, 0x80, 0xC0, 0xFC, 0x00, 0x00, 0x69, 0xE5, 0x4B, 0x8A, 0x28, 0x44, 0x98, 0x00, 0x80, 0x00,
0x00, 0x00, 0x00, 0xB4, 0x40, 0x24, 0x04, 0x20, 0xE1, 0x78, 0x00, 0x43, 0x04, 0xC0, 0xA4, 0x80,
0x60, 0x01, 0x00, 0x60, 0xF8, 0x00, 0x51, 0x8D, 0x54, 0x44, 0xC8, 0x98, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0x21, 0x81, 0x31, 0x70, 0x00, 0x01, 0x35, 0x41, 0x08, 0xC0,
0x18, 0xE0, 0x00, 0xC4, 0xF0, 0x00, 0x61, 0x2A, 0x03, 0x2E, 0x93, 0x63, 0x60, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x02, 0x1D, 0xA0, 0xD0, 0x00, 0x41, 0x20, 0x91, 0xEC, 0xE0,
0x17, 0xC0, 0x80, 0x02, 0x00, 0xFD, 0x00, 0x51, 0x75, 0x59, 0x1C, 0xB0, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0xB6, 0x5C, 0x00, 0x03, 0x1F, 0x08, 0xC0, 0x02, 0x43, 0x11, 0x80, 0x01, 0x8D,
0xC8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB0, 0x33, 0xC3, 0x72, 0x70, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x00, 0x0C, 0x30, 0x80, 0x03, 0x06, 0x0E, 0x46, 0xA0, 0x80, 0x04, 0x84,
0xA0, 0x01, 0x00, 0x00, 0x00, 0x03, 0xFE, 0x01, 0x00, 0xA3, 0x71, 0x00, 0x7C, 0xE0, 0x00, 0x00,
0x00, 0x00, 0x7E, 0x83, 0x59, 0x89, 0x91, 0x41, 0x26, 0x63, 0x00, 0x0A, 0x12, 0x80, 0x15, 0x0F,
0x31, 0x00, 0x06, 0x3C, 0x30, 0x00, 0x00, 0x85, 0x52, 0x89, 0x57, 0x82, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x2C, 0x0F, 0x8B, 0x8D, 0xD3, 0xF0, 0x00, 0x01, 0x29, 0x58, 0x19, 0x79,
0xC0, 0x00, 0x01, 0xE2, 0x00, 0x00, 0x10, 0x91, 0x05, 0x12, 0xD0, 0xC3, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x8E, 0x9D, 0x64, 0x50, 0x7A, 0xE0, 0x00, 0x45, 0x0A, 0x82, 0x02, 0x82,
0x0E, 0x00, 0xC0, 0xF2, 0x01, 0x00, 0x04, 0xB5, 0x22, 0x95, 0x58, 0xC1, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x68, 0x30, 0x30, 0x00, 0x42, 0xB6, 0xE8, 0x00, 0x0B, 0x12, 0xC0, 0x16, 0x80,
0xC0, 0x01, 0x00, 0x1C, 0xC4, 0x00, 0x0C, 0x92, 0x49, 0x4C, 0xD0, 0x34, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x02, 0x20, 0x20, 0x42, 0x89, 0x20, 0x60, 0x00, 0x44, 0x12, 0x94, 0x5A, 0xC0,
0x30, 0xE0, 0x00, 0x40, 0xD8, 0x08, 0x82, 0xAA, 0x11, 0x26, 0xC3, 0xE7, 0x19, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x84, 0x08, 0x48, 0xF8, 0x50, 0xC0, 0x00, 0x0A, 0x09, 0x38, 0x95, 0xF0,
0xAD, 0xE0, 0x40, 0x03, 0x04, 0x03, 0x00, 0xA2, 0xBB, 0x3D, 0x9D, 0xA1, 0x48, 0x80, 0x20, 0x00,
0x00, 0x00, 0x02, 0x08, 0x96, 0x00, 0x0E, 0x1F, 0x30, 0x80, 0x04, 0x0B, 0x26, 0x80, 0x80, 0xA0,
0xD0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x14, 0xC4, 0x1E, 0xC1, 0xF1, 0xE1, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE0, 0x0C, 0x06, 0xC2, 0x73, 0x00, 0x07, 0x15, 0x20, 0x00, 0x05, 0x93,
0x80, 0x80, 0x80, 0x11, 0x00, 0x0F, 0xF4, 0x38, 0x00, 0x56, 0xA2, 0x5B, 0x3E, 0x38, 0x00, 0x00,
0x00, 0x00, 0xFF, 0x07, 0xA9, 0x03, 0x29, 0x95, 0x24, 0x41, 0x00, 0x01, 0x04, 0xC0, 0x80, 0x1E,
0xE3, 0x00, 0x31, 0x78, 0xE1, 0x00, 0x00, 0x62, 0x49, 0x89, 0x97, 0x18, 0x00, 0x00, 0x00, 0x00,
0x00, 0x44, 0x00, 0x04, 0x15, 0x2D, 0x8D, 0x06, 0x76, 0x84, 0x04, 0x4B, 0x02, 0xB7, 0xB8, 0x72,
0x00, 0x00, 0x07, 0xF9, 0x04, 0x00, 0x08, 0x91, 0x2B, 0x95, 0xA1, 0x86, 0x08, 0x00, 0x80, 0x00,
0x00, 0x00, 0x01, 0x00, 0x92, 0x0F, 0x46, 0x86, 0x71, 0x78, 0x00, 0x13, 0x27, 0x01, 0xC0, 0x00,
0x04, 0x00, 0xC0, 0xFE, 0x02, 0x00, 0x24, 0x96, 0x25, 0x89, 0x71, 0x88, 0x40, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x30, 0x10, 0x00, 0x0D, 0x40, 0x00, 0x05, 0x06, 0xC0, 0x80, 0x83,
0x01, 0x01, 0x00, 0x07, 0x02, 0x00, 0x0A, 0x89, 0x04, 0x1E, 0x40, 0x6A, 0x0A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x21, 0x83, 0x40, 0x90, 0x00, 0x13, 0x01, 0x48, 0x98, 0xC0,
0x60, 0xC0, 0x00, 0x21, 0xE4, 0x18, 0x00, 0x7A, 0x02, 0x6C, 0xA7, 0xEF, 0x26, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x22, 0x00, 0x08, 0x01, 0x6C, 0x80, 0xC0, 0x00, 0x01, 0x02, 0x44, 0xD2, 0xF6,
0x7F, 0xC0, 0x00, 0x01, 0x01, 0x0D, 0x00, 0x16, 0x11, 0x94, 0x13, 0x81, 0x90, 0x00, 0x80, 0x00,
0x00, 0x00, 0x01, 0x84, 0x0C, 0x02, 0x18, 0x0E, 0xE0, 0x80, 0x00, 0x05, 0x54, 0x40, 0x00, 0x02,
0xB0, 0x00, 0x00, 0x00, 0x02, 0x2D, 0x00, 0x03, 0x68, 0x0B, 0x87, 0xE7, 0x1C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x10, 0x52, 0x08, 0x19, 0x38, 0x7B, 0x00, 0x09, 0x08, 0xA0, 0x00, 0x05, 0x05,
0xC0, 0x01, 0x00, 0x50, 0x00, 0x07, 0xDB, 0xF0, 0x00, 0x0F, 0xE1, 0x0C, 0x61, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x60, 0x14, 0x08, 0xC1, 0x80, 0xC0, 0x00, 0x00, 0xA6, 0x20, 0x00, 0x04, 0x44,
0xC0, 0x01, 0x00, 0x02, 0x60, 0xF0, 0x00, 0x3F, 0x10, 0x13, 0x97, 0x38, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x15, 0x40, 0x88, 0x40, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x05, 0x03,
0x3F, 0x3F, 0x00, 0xC2, 0x04, 0x07, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x28, 0xC0, 0x00, 0x00,
0x00, 0x22, 0xFB, 0x36, 0x21, 0x02, 0x40, 0x00, 0x00, 0x10, 0x00, 0x01, 0x06, 0x00, 0xFF, 0xFB,
0x76, 0x81, 0x00, 0x02, 0x04, 0x03, 0x30, 0x80, 0x8F, 0x83, 0xF3, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x89, 0x25, 0x20, 0x04, 0x00, 0x00, 0x07, 0xC1, 0x40, 0x00,
0x02, 0xF8, 0x00, 0x00, 0x08, 0x0A, 0xB1, 0x03, 0x08, 0xC2, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x6C, 0x14, 0x40, 0x40, 0x0B, 0x80, 0x02, 0x08, 0x48, 0x08,
0x00, 0xF8, 0x00, 0x00, 0x00, 0x03, 0x30, 0xC5, 0xFF, 0x00, 0x77, 0x96, 0x6C, 0x60, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x28, 0xEC, 0x3C, 0xFE, 0x77, 0x90, 0x04, 0x08, 0x67, 0x29,
0x6D, 0x20, 0x00, 0x0F, 0x00, 0x04, 0xF9, 0x7B, 0x42, 0xBB, 0xB0, 0x71, 0xB8, 0xC5, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x04, 0x20, 0x00, 0x51, 0x6F, 0xDC, 0x00,
0x57, 0xFE, 0x9A, 0xDE, 0xD5, 0x01, 0xFD, 0xFE, 0xDB, 0xFF, 0xEE, 0x68, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x42, 0x48, 0x64, 0x00, 0x80,
0x08, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x10, 0x38, 0x32, 0x00, 0xC1, 0x40, 0xC0, 0x00, 0x00, 0x03, 0x20, 0x00, 0x05, 0x88,
0x81, 0x00, 0x00, 0x03, 0x20, 0xE0, 0x00, 0xFE, 0x41, 0x05, 0x2E, 0x16, 0x80, 0x20, 0x40, 0x00,
0x00, 0x01, 0x14, 0x6B, 0x04, 0x01, 0x00, 0xF0, 0x01, 0x00, 0x08, 0x48, 0x20, 0x00, 0x05, 0x01,
0x2F, 0x3F, 0x00, 0x00, 0x02, 0x07, 0x06, 0x10, 0x00, 0xC0, 0x00, 0x05, 0x76, 0x28, 0x00, 0x00,
0x00, 0x05, 0xFC, 0x6D, 0x11, 0x00, 0x20, 0x00, 0x00, 0x30, 0x62, 0x60, 0x65, 0x00, 0xDB, 0xAF,
0x24, 0x21, 0x00, 0x20, 0x12, 0x01, 0x01, 0x03, 0x7C, 0x87, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8C, 0x17, 0x00, 0x00, 0x40, 0x06, 0x00, 0x04, 0xA4, 0x00,
0x80, 0xB8, 0x00, 0x04, 0x00, 0x16, 0x8C, 0x08, 0x20, 0xC0, 0x61, 0xFC, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x33, 0x26, 0x01, 0x04, 0x00, 0x10, 0x42, 0x20, 0x01, 0x04, 0x80,
0x04, 0xB0, 0x00, 0x00, 0x40, 0x05, 0x30, 0x2B, 0x9F, 0x00, 0x0C, 0x1D, 0xC0, 0xC0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x06, 0x89, 0x25, 0x99, 0x3D, 0x00, 0xC6, 0x45, 0xB4, 0x84,
0xF4, 0x24, 0x81, 0x0E, 0x10, 0x02, 0x5D, 0xB9, 0x88, 0x6C, 0xE0, 0xB4, 0x1C, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x08, 0x04, 0x80, 0x32, 0x18, 0xA0, 0x99, 0xA6, 0x89,
0xCF, 0x73, 0x09, 0x88, 0x51, 0x84, 0xAB, 0xB5, 0xED, 0x2F, 0xB9, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x24, 0x40, 0x08, 0x08,
0x40, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x84, 0x68, 0x10, 0x82, 0x30, 0x00, 0x02, 0x02, 0x31, 0x20, 0x00, 0x05, 0x84,
0x11, 0x00, 0x00, 0x01, 0x04, 0xC0, 0x03, 0xF9, 0x8C, 0x0E, 0x90, 0x0D, 0x00, 0xF0, 0x00, 0x00,
0x00, 0x00, 0x0A, 0x37, 0x00, 0x01, 0x18, 0xA0, 0x00, 0x03, 0x0E, 0x60, 0x20, 0x00, 0x05, 0x0C,
0x4D, 0x3F, 0x00, 0x01, 0x13, 0x03, 0x30, 0xC0, 0x04, 0xA0, 0x10, 0x01, 0xBC, 0x80, 0x00, 0x00,
0x00, 0x00, 0x6E, 0xD3, 0x01, 0x02, 0x00, 0x00, 0x00, 0x18, 0xB3, 0x30, 0x7A, 0x00, 0xEA, 0xC6,
0x89, 0x43, 0x00, 0x01, 0x02, 0x11, 0x00, 0xCC, 0xF9, 0x83, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x05, 0x9F, 0x42, 0x00, 0x21, 0x19, 0x10, 0x0F, 0x8D, 0x20,
0x20, 0xF0, 0x00, 0x00, 0x04, 0x07, 0xC3, 0xC0, 0x00, 0xC1, 0xCC, 0xF8, 0x28, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x80, 0x8E, 0xC2, 0x33, 0x58, 0x23, 0x10, 0x00, 0xA1, 0x02,
0x41, 0x70, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x3F, 0x20, 0x01, 0x0B, 0x60, 0x28, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x32, 0x09, 0x38, 0xFE, 0x23, 0x30, 0x66, 0x2A, 0x13,
0xA6, 0x08, 0x01, 0x0A, 0x58, 0x02, 0x75, 0xED, 0x84, 0x70, 0x24, 0x2E, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x04, 0x20, 0xC4, 0x11, 0x3E, 0x34, 0x80, 0xCB, 0x0C,
0x42, 0xA9, 0x00, 0xC4, 0xE5, 0x8C, 0x8C, 0xE2, 0x7C, 0x6F, 0x73, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, 0x04,
0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x28, 0x01, 0xB0, 0x00, 0x82, 0x08, 0x00, 0x03, 0x06, 0x1A, 0xA0, 0x00, 0x05, 0x40,
0xA1, 0x00, 0x00, 0x00, 0x82, 0x40, 0x0F, 0xC0, 0xF8, 0x05, 0x28, 0x0B, 0x86, 0x79, 0x00, 0x00,
0x00, 0x2B, 0x00, 0x4C, 0x24, 0x01, 0x3C, 0x90, 0x00, 0x03, 0x85, 0x20, 0x00, 0x00, 0x01, 0x24,
0x3B, 0x1F, 0x00, 0x01, 0x83, 0x03, 0x83, 0x80, 0xF0, 0xC0, 0x42, 0x12, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x44, 0xE0, 0x01, 0x01, 0x00, 0x02, 0x00, 0x0C, 0x51, 0x03, 0x18, 0x82, 0x60, 0x12,
0x5A, 0x33, 0x00, 0x10, 0x40, 0x11, 0x00, 0x20, 0xE0, 0xC3, 0x22, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0xCA, 0x24, 0x22, 0x0C, 0xB0, 0xEC, 0xD2, 0x40, 0x00,
0x60, 0xF0, 0x00, 0x00, 0x00, 0x07, 0xC0, 0xE8, 0x81, 0xC0, 0x78, 0xF8, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xC8, 0x03, 0x22, 0x19, 0x9E, 0x20, 0x80, 0x42, 0x10, 0x44,
0x28, 0x70, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x2D, 0x40, 0x04, 0x10, 0xC1, 0x70, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x73, 0x66, 0xCC, 0x09, 0x2A, 0xC9, 0x49,
0x2B, 0x21, 0x00, 0x40, 0x70, 0x03, 0x7E, 0xCA, 0x26, 0xEA, 0x10, 0x0B, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x40, 0x84, 0x1D, 0x18, 0x40, 0x2D, 0x64,
0x24, 0xCC, 0x44, 0x48, 0xA0, 0x89, 0x24, 0x93, 0x28, 0x45, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x5D, 0xBE, 0x80, 0x00, 0x80, 0x04, 0x00, 0x01, 0x04, 0x07, 0x20, 0x00, 0x04, 0x44,
0xE0, 0x41, 0x00, 0x08, 0xC0, 0x80, 0x3C, 0x01, 0x40, 0x0E, 0x90, 0x05, 0x23, 0x8E, 0x80, 0x00,
0x00, 0x06, 0x40, 0xD5, 0x24, 0x01, 0x3C, 0x80, 0x03, 0x15, 0x00, 0x00, 0x20, 0x00, 0x05, 0x00,
0x48, 0x0E, 0x00, 0x00, 0x89, 0x43, 0x1E, 0x0F, 0x80, 0x82, 0x80, 0x42, 0xE6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x40, 0x81, 0x02, 0x00, 0x01, 0x08, 0x07, 0x00, 0x81, 0xC3, 0xA3, 0x00, 0x01,
0x8E, 0x23, 0x00, 0x10, 0x21, 0x18, 0x03, 0x00, 0x83, 0xC1, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x6F, 0x41, 0x71, 0x18, 0x07, 0xBC, 0x9F, 0x68, 0x00, 0x0C,
0x02, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x42, 0x7E, 0x04, 0xC0, 0xE2, 0xF0, 0x58, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x4C, 0x30, 0x10, 0x80, 0x0F, 0xC0, 0x04, 0x10, 0x59, 0x20,
0x04, 0xF0, 0x00, 0x00, 0x00, 0x01, 0x08, 0x10, 0x0B, 0xC0, 0x00, 0x01, 0x82, 0xE4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x18, 0x44, 0x61, 0x55, 0xC3, 0xB8, 0x14, 0x94, 0x54, 0x80,
0x52, 0x55, 0x00, 0x88, 0xD8, 0x05, 0x6A, 0xD9, 0xB2, 0xA0, 0x41, 0x13, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x0E, 0x84, 0x69, 0x08, 0xD2,
0xB2, 0x44, 0x80, 0x00, 0x80, 0x00, 0x00, 0x21, 0x24, 0x20, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x83, 0x3B, 0xC6, 0x18, 0x00, 0x00, 0x00, 0x80, 0x00, 0x60, 0x32, 0x00, 0x00, 0x05, 0x40,
0xC0, 0xE2, 0x00, 0x00, 0x48, 0xC0, 0x40, 0x07, 0x32, 0x0D, 0x40, 0x00, 0x4C, 0xF0, 0x00, 0x00,
0x00, 0x01, 0x18, 0xA4, 0x2E, 0x01, 0x3E, 0x00, 0x46, 0x00, 0x0C, 0x06, 0x00, 0x00, 0x01, 0x03,
0x15, 0x00, 0x00, 0x08, 0x09, 0x06, 0x3C, 0x3C, 0x00, 0x81, 0x08, 0x29, 0xCC, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x99, 0x09, 0x00, 0x45, 0x2D, 0x80, 0xD4, 0x18, 0x93, 0x75, 0x00, 0x14,
0x53, 0x43, 0x00, 0x08, 0x30, 0x28, 0x0F, 0x01, 0x1C, 0x83, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x25, 0x18, 0x7B, 0x80, 0x03, 0x1F, 0x0A, 0x00, 0x82, 0x1A,
0x39, 0xE0, 0x00, 0x00, 0x00, 0x0B, 0x61, 0x3F, 0x00, 0xC0, 0x88, 0xF0, 0x54, 0x90, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x09, 0x0E, 0xA4, 0x11, 0x08, 0x00, 0xC0, 0x01, 0x02, 0x08, 0xAA, 0x32,
0x80, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x82, 0x00, 0xE1, 0x00, 0x16, 0x00, 0xD8, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x20, 0x0C, 0x78, 0x13, 0x4A, 0x74, 0x3A, 0xA1, 0xBA, 0x21,
0x45, 0xB2, 0x04, 0xC5, 0x68, 0x00, 0xB4, 0x94, 0xD1, 0xD4, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x12, 0x08, 0xAA, 0x20, 0x9B,
0x92, 0xA0, 0xA8, 0x20, 0x00, 0x90, 0x52, 0x00, 0x08, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0xFF, 0xF0, 0x10, 0x00, 0x00, 0x11, 0x80, 0x08, 0x72, 0x3B, 0x20, 0x00, 0x04, 0x80,
0xE0, 0xE0, 0x00, 0x10, 0x00, 0xB4, 0x00, 0x0E, 0xFD, 0x16, 0x00, 0x00, 0x52, 0x10, 0x00, 0x00,
0x00, 0x04, 0x0E, 0x0E, 0x6E, 0x01, 0x7F, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x00, 0x00, 0x10, 0x09,
0x23, 0x40, 0x00, 0x00, 0x4C, 0x40, 0x81, 0xF7, 0x18, 0xC5, 0x60, 0x92, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFD, 0x01, 0x00, 0x63, 0x7F, 0xD2, 0xFD, 0x1E, 0xBC, 0x3E, 0xA0, 0x42,
0xB5, 0x17, 0x00, 0x08, 0x10, 0x0C, 0xE1, 0x0F, 0x61, 0x81, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x8C, 0x0D, 0x34, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x74, 0xE0, 0x00, 0x00, 0x80, 0x03, 0x64, 0x5D, 0xA1, 0x40, 0x40, 0xC0, 0xF5, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x80, 0x18, 0xF8, 0x02, 0x8D, 0x8D, 0x20, 0x26, 0x50, 0xCC, 0x66, 0x11,
0xA2, 0x79, 0x00, 0x80, 0x00, 0x00, 0x04, 0x21, 0x00, 0xC2, 0x00, 0x80, 0xD5, 0x70, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0xBC, 0x02, 0x05, 0xFA, 0x4D, 0x6B, 0x10, 0x04,
0x06, 0xEB, 0x00, 0xD7, 0x2C, 0x02, 0x11, 0x08, 0xEB, 0xC0, 0x80, 0x09, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x02, 0x02, 0x32, 0xAF,
0x48, 0x42, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x10, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x2B, 0xEE, 0xF8, 0xE0, 0x00, 0x00, 0x0B, 0x00, 0x30, 0x31, 0x19, 0x00, 0x00, 0x05, 0x40,
0xC0, 0xC0, 0x00, 0x20, 0x24, 0xFC, 0x00, 0x20, 0xFA, 0x29, 0x00, 0x03, 0xAF, 0x80, 0x00, 0x00,
0x00, 0x00, 0x01, 0x29, 0x5E, 0x01, 0x7F, 0x00, 0x10, 0x60, 0x2E, 0x07, 0x08, 0x00, 0x09, 0x00,
0x01, 0x81, 0x00, 0x04, 0x04, 0x01, 0x0F, 0x9F, 0xE0, 0x81, 0xC2, 0x3F, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0x05, 0x81, 0x37, 0xBE, 0xF7, 0x7B, 0xCB, 0x11, 0xDF, 0x60, 0x26,
0x08, 0x27, 0x00, 0x00, 0x08, 0x08, 0x3F, 0x79, 0x0F, 0xC1, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x3E, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04,
0x00, 0xE8, 0x00, 0x00, 0x80, 0x03, 0x21, 0x8B, 0xFB, 0x00, 0x00, 0x80, 0xD9, 0x20, 0x00, 0x00,
0x00, 0x00, 0x00, 0xA0, 0x00, 0x20, 0xAD, 0x40, 0xE6, 0xD8, 0x0D, 0x03, 0x32, 0x64, 0xD5, 0xA5,
0x34, 0xEB, 0x00, 0xA0, 0x00, 0x00, 0x04, 0x09, 0x04, 0xFF, 0x00, 0x23, 0x82, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x7F, 0x1C, 0xBF, 0x04, 0x00,
0x82, 0xFD, 0x90, 0xFF, 0x04, 0x04, 0x00, 0x00, 0xFE, 0x90, 0x00, 0x4F, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x20, 0x10, 0xFD,
0x91, 0x10, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x00, 0x00, 0x00, 0x41,
0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41, 0x48, 0x48, 0x41, 0x48, 0x48,
0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41, 0x48, 0x48, 0x4D, 0x4D, 0x41, 0x41, 0x4D,
0x48, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x41, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41, 0x48, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D,
0x4D, 0x48, 0x4D, 0x48, 0x48, 0x48, 0x48, 0x41, 0x01, 0x01, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x41, 0x48, 0x41, 0x41, 0x48, 0x48, 0x41, 0x48, 0x4D, 0x4D, 0x4D, 0x4D,
0x4D, 0x41, 0x4D, 0x48, 0x4D, 0x48, 0x48, 0x48, 0x01, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x41, 0x41, 0x41, 0x41, 0x41, 0x48, 0x41, 0x41, 0x41, 0x4D, 0x4D, 0x4D, 0x5D,
0x4B, 0x4B, 0x4B, 0x4B, 0x41, 0x41, 0x48, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x41, 0x41, 0x48, 0x41, 0x41, 0x41, 0x41, 0x01, 0x41, 0x41, 0x45, 0x45, 0x4D,
0x4B, 0x4B, 0x41, 0x4B, 0x4B, 0x4B, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41, 0x41, 0x48, 0x48, 0x01, 0x41, 0x4D, 0x68, 0x69, 0x59,
0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x41, 0x48, 0x48, 0x48, 0x41, 0x48, 0x41, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x01, 0x01, 0x41, 0x41, 0x41, 0x41, 0x48, 0x48, 0x01, 0x41, 0x4D, 0x43, 0x69, 0x59,
0x4B, 0x59, 0x59, 0x4B, 0x4B, 0x4B, 0x4B, 0x48, 0x48, 0x48, 0x48, 0x41, 0x01, 0x01, 0x01, 0x00,
0x00, 0x01, 0x01, 0x01, 0x41, 0x41, 0x41, 0x41, 0x48, 0x41, 0x01, 0x41, 0x4D, 0x4D, 0x69, 0x59,
0x59, 0x59, 0x59, 0x59, 0x4B, 0x4B, 0x48, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00,
0x00, 0x00, 0x41, 0x41, 0x41, 0x48, 0x41, 0x41, 0x41, 0x43, 0x01, 0x41, 0x4D, 0x45, 0x69, 0x5D,
0x5D, 0x5D, 0x43, 0x48, 0x4B, 0x4B, 0x48, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00,
0x00, 0x00, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x58, 0x43, 0x00, 0x41, 0x4D, 0x43, 0x69, 0x5D,
0x5D, 0x58, 0x43, 0x41, 0x48, 0x4B, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00,
0x00, 0x00, 0x41, 0x43, 0x41, 0x41, 0x43, 0x43, 0x58, 0x43, 0x01, 0x41, 0x4D, 0x69, 0x69, 0x5D,
0x5D, 0x43, 0x41, 0x41, 0x48, 0x48, 0x4B, 0x48, 0x48, 0x48, 0x41, 0x48, 0x41, 0x41, 0x00, 0x00,
0x00, 0x00, 0x41, 0x43, 0x43, 0x43, 0x43, 0x58, 0x73, 0x46, 0x00, 0x41, 0x4D, 0x4D, 0x69, 0x5D,
0x5E, 0x46, 0x41, 0x41, 0x01, 0x41, 0x48, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00,
0x00, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x46, 0x70, 0x46, 0x41, 0x41, 0x4D, 0x45, 0x45, 0x4D,
0x71, 0x46, 0x41, 0x00, 0x01, 0x41, 0x48, 0x48, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x00, 0x00,
0x00, 0x00, 0x42, 0x42, 0x42, 0x46, 0x46, 0x70, 0x70, 0x01, 0x05, 0x41, 0x05, 0x41, 0x01, 0x45,
0x71, 0x46, 0x46, 0x01, 0x46, 0x43, 0x4B, 0x4B, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x00, 0x00,
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x70, 0x70, 0x46, 0x41, 0x05, 0x41, 0x45, 0x00, 0x01, 0x45,
0x71, 0x46, 0x00, 0x01, 0x41, 0x58, 0x59, 0x59, 0x4B, 0x48, 0x48, 0x48, 0x41, 0x41, 0x41, 0x00,
0x00, 0x42, 0x56, 0x50, 0x46, 0x46, 0x70, 0x46, 0x01, 0x05, 0x45, 0x45, 0x05, 0x00, 0x41, 0x45,
0x4E, 0x70, 0x00, 0x01, 0x41, 0x43, 0x5E, 0x5E, 0x5E, 0x4B, 0x4B, 0x4B, 0x43, 0x43, 0x00, 0x00,
0x00, 0x42, 0x42, 0x46, 0x70, 0x41, 0x41, 0x41, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x68, 0x68,
0x69, 0x68, 0x00, 0x01, 0x41, 0x43, 0x5E, 0x73, 0x5E, 0x4B, 0x59, 0x58, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x41, 0x4D, 0x69, 0x69, 0x68, 0x68, 0x68, 0x69, 0x68, 0x68,
0x68, 0x45, 0x00, 0x01, 0x01, 0x45, 0x69, 0x71, 0x71, 0x4E, 0x5E, 0x43, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x41, 0x48, 0x4D, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69,
0x69, 0x45, 0x00, 0x45, 0x01, 0x45, 0x69, 0x69, 0x69, 0x4D, 0x4B, 0x48, 0x08, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x41, 0x41, 0x41, 0x45, 0x45, 0x45, 0x68, 0x69, 0x69, 0x4D, 0x4D,
0x4D, 0x69, 0x41, 0x69, 0x45, 0x45, 0x4D, 0x4D, 0x69, 0x4D, 0x4D, 0x48, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x41, 0x01, 0x41, 0x05, 0x45, 0x41, 0x41, 0x48,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x01, 0x01, 0x41,
0x01, 0x01, 0x41, 0x00, 0x01, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
+7
View File
@@ -0,0 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x1F0000,
fat, data, fat, , 7M,
tsf, data, 0x84, , 5M,
oplrom, data, 0x85, , 2M,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x6000
3 phy_init data phy 0xf000 0x1000
4 factory app factory 0x10000 0x1F0000
5 fat data fat 7M
6 tsf data 0x84 5M
7 oplrom data 0x85 2M
+4056
View File
File diff suppressed because it is too large Load Diff
+79
View File
@@ -0,0 +1,79 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 6.0.0 Project Minimal Configuration
#
# default:
CONFIG_IDF_TARGET="esp32s3"
CONFIG_BOOTLOADER_LOG_COLORS=y
CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=n
CONFIG_BOOTLOADER_WDT_ENABLE=n
CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS=y
CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_COMPILER_OPTIMIZATION_PERF=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
CONFIG_CONSOLE_SORTED_HELP=y
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=y
CONFIG_SPI_SLAVE_IN_IRAM=y
CONFIG_UART_ISR_IN_IRAM=y
CONFIG_ETH_USE_SPI_ETHERNET=n
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_ESP_PHY_REDUCE_TX_POWER=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_MEMTEST=n
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=2048
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=0
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP_SYSTEM_MEMPROT=n
CONFIG_ESP_INT_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=6
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=16
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=8
CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=16
CONFIG_ESP_WIFI_RX_BA_WIN=6
CONFIG_ESP_WIFI_GMAC_SUPPORT=n
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
CONFIG_FATFS_VOLUME_COUNT=2
CONFIG_FATFS_CODEPAGE_866=y
CONFIG_SPIFFS_OBJ_NAME_LEN=188
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
CONFIG_FREERTOS_ISR_STACKSIZE=2096
CONFIG_LOG_VERSION_2=y
CONFIG_LOG_COLORS=y
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=1024
# Raised from the 3072 Kconfig default while chasing a LoadProhibited crash on
# AT+CIPSTART - turned out not to be the actual cause (that was tcpip_task's
# mailbox not existing yet because WiFi hadn't been initialized, fixed in
# at_cip.cpp instead), but a bit more headroom for a task now doing real TLS
# work is reasonable regardless, so left in place.
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=6144
CONFIG_LWIP_TCP_WND_DEFAULT=65535
CONFIG_LWIP_TCP_RECVMBOX_SIZE=1024
CONFIG_LWIP_TCP_ACCEPTMBOX_SIZE=255
CONFIG_LWIP_TCP_SACK_OUT=y
CONFIG_LWIP_WND_SCALE=y
CONFIG_LWIP_TCP_RCV_SCALE=14
CONFIG_LWIP_UDP_RECVMBOX_SIZE=16
CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0=y
# AT+CIPSNTPCFG accepts up to 3 servers, matching real ESP-AT.
CONFIG_LWIP_SNTP_MAX_SERVERS=3
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
#CONFIG_ESP_CONSOLE_UART_CUSTOM=y
#CONFIG_ESP_CONSOLE_UART_CUSTOM_NUM_0=y
#CONFIG_ESP_CONSOLE_UART_NUM=0
#CONFIG_ESP_CONSOLE_UART_TX_GPIO=-1
#CONFIG_ESP_CONSOLE_UART_RX_GPIO=-1
#CONFIG_ESP_CONSOLE_UART_BAUDRATE=921600
+1
View File
@@ -0,0 +1 @@
{"latitude":41.875,"longitude":12.5,"generationtime_ms":0.22363662719726562,"utc_offset_seconds":7200,"timezone":"Europe/Rome","timezone_abbreviation":"GMT+2","elevation":35.0,"current_units":{"time":"unixtime","interval":"seconds","temperature_2m":"°C","weather_code":"wmo code","is_day":"","wind_speed_10m":"km/h","precipitation":"mm","surface_pressure":"hPa"},"current":{"time":1789122600,"interval":900,"temperature_2m":28.3,"weather_code":2,"is_day":1,"wind_speed_10m":6.3,"precipitation":0.00,"surface_pressure":1012.8},"daily_units":{"time":"unixtime","weather_code":"wmo code","temperature_2m_max":"°C","temperature_2m_min":"°C","precipitation_sum":"mm","sunrise":"unixtime","sunset":"unixtime"},"daily":{"time":[1789077600,1789164000,1789250400,1789336800,1789423200,1789509600],"weather_code":[3,3,2,3,1,2],"temperature_2m_max":[29.8,30.3,29.8,29.5,30.8,30.4],"temperature_2m_min":[22.0,19.5,19.7,19.0,19.2,17.9],"precipitation_sum":[0.00,0.00,0.00,0.00,0.00,0.00],"sunrise":[1789101951,1789188412,1789274874,1789361336,1789447798,1789534260],"sunset":[1789147598,1789233893,1789320188,1789406484,1789492780,1789579076]}}
+1
View File
@@ -0,0 +1 @@
{"results":[{"id":703448,"name":"Kyiv","latitude":50.45466,"longitude":30.5238,"elevation":179.0,"feature_code":"PPLC","country_code":"UA","admin1_id":703447,"timezone":"Europe/Kyiv","population":2952301,"country_id":690791,"country":"Ukraine","admin1":"Kyiv City"}],"generationtime_ms":0.31280518}
+1
View File
@@ -0,0 +1 @@
{"generationtime_ms":0.35643578}
+1
View File
@@ -0,0 +1 @@
{"results":[{"id":3169070,"name":"Рим","latitude":41.89193,"longitude":12.51133,"elevation":20.0,"feature_code":"PPLC","country_code":"IT","admin1_id":3174976,"admin2_id":3169069,"admin3_id":3169071,"timezone":"Europe/Rome","population":2318895,"postcodes":["00187"],"country_id":3175395,"country":"Италия","admin1":"Лацио","admin2":"Città metropolitana di Roma Capitale","admin3":"Roma"}],"generationtime_ms":2.6960373}
@@ -0,0 +1 @@
{"results":[{"id":3169070,"name":"Rome","latitude":41.89193,"longitude":12.51133,"elevation":20.0,"feature_code":"PPLC","country_code":"IT","admin1_id":3174976,"admin2_id":3169069,"admin3_id":3169071,"timezone":"Europe/Rome","population":2318895,"postcodes":["00187"],"country_id":3175395,"country":"Italy","admin1":"Lazio","admin2":"Province of Rome","admin3":"Rome"}],"generationtime_ms":1.3924837}
+1
View File
@@ -0,0 +1 @@
{"country": "Italy", "country abbreviation": "IT", "post code": "00144", "places": [{"place name": "Decima", "longitude": "12.4333", "latitude": "41.75", "state": "Lazio", "state abbreviation": "LZ"}, {"place name": "Roma", "longitude": "12.4833", "latitude": "41.9", "state": "Lazio", "state abbreviation": "LZ"}]}
+249
View File
@@ -0,0 +1,249 @@
"""Host tests for the transferred weather parser; requires MSVC."""
import datetime
import json
import math
import pathlib
import struct
import subprocess
import unittest
ROOT = pathlib.Path(__file__).resolve().parents[1]
FIXTURES = ROOT / 'tests' / 'fixtures' / 'weather'
BUILD = ROOT / '.test-build' / 'weather_parse'
VSWHERE = pathlib.Path(r'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe')
# Смещения записи — как в include/zifi/weather_parse.hpp.
REC_SIZE = 90
REC_PLACE = 2
REC_TEMP = 26
REC_DAYS = 42
def find_vcvars() -> pathlib.Path | None:
if not VSWHERE.exists():
return None
result = subprocess.run([str(VSWHERE), '-products', '*', '-latest', '-find',
r'VC\Auxiliary\Build\vcvars64.bat'],
capture_output=True, text=True)
path = result.stdout.strip().splitlines()
return pathlib.Path(path[0]) if path else None
def short(path: pathlib.Path) -> str:
"""Короткий 8.3-путь: cmd.exe читает .bat в OEM-кодировке, и кириллица
профиля в пути до исходников ломает команду."""
import ctypes
buffer = ctypes.create_unicode_buffer(32768)
if not ctypes.windll.kernel32.GetShortPathNameW(str(path), buffer, len(buffer)):
return str(path)
return buffer.value
def build_exe(build: pathlib.Path, exe_name: str, includes: list[pathlib.Path],
sources: list[tuple[pathlib.Path, str]]) -> pathlib.Path | None:
"""Собрать программу MSVC. sources — (исходник, имя объекта): у исходников
бывают одинаковые короткие имена (WEATHE~1.CPP), поэтому каждый
компилируется отдельно в объект со своим именем."""
vcvars = find_vcvars()
if vcvars is None:
return None
build.mkdir(parents=True, exist_ok=True)
exe = build / exe_name
script = build / 'build_host.bat'
flags = '/nologo /c /EHsc /std:c++17 /O2 /utf-8 ' + ' '.join(
f'/I "{short(path)}"' for path in includes)
lines = ['@echo off', f'call "{short(vcvars)}" >nul 2>nul || exit /b 1']
lines += [f'cl {flags} "{short(source)}" /Fo:{obj} || exit /b 1' for source, obj in sources]
lines.append('cl /nologo ' + ' '.join(obj for _, obj in sources) + f' /Fe:{exe_name}')
script.write_text('\r\n'.join(lines) + '\r\n', encoding='ascii')
result = subprocess.run(['cmd.exe', '/c', short(script)], capture_output=True,
cwd=short(build))
if result.returncode != 0:
output = result.stdout.decode('cp866', 'replace') + result.stderr.decode('cp866', 'replace')
raise AssertionError(f'сборка {exe_name} не удалась:\n{output}')
return exe
def build_host() -> pathlib.Path | None:
return build_exe(BUILD, 'weather_parse_host.exe', [ROOT / 'main'],
[(ROOT / 'main' / 'weather_parse.cpp', 'parse.obj'),
(ROOT / 'tests' / 'weather_parse_host.cpp', 'host.obj')])
def rnd(value: float) -> int:
"""Округление как lround в прошивке: половина — от нуля (22.5 -> 23),
а не банковское округление round() в Python."""
return int(math.copysign(math.floor(abs(value) + 0.5), value))
def local_hm(unix: int, offset: int) -> tuple[int, int]:
t = datetime.datetime.fromtimestamp(unix + offset, datetime.timezone.utc)
return t.hour, t.minute
def local_date(unix: int, offset: int) -> tuple[int, int, int]:
t = datetime.datetime.fromtimestamp(unix + offset, datetime.timezone.utc)
return t.day, t.month, t.weekday()
class WeatherParseHostTest(unittest.TestCase):
exe: pathlib.Path | None = None
@classmethod
def setUpClass(cls) -> None:
cls.exe = build_host()
if cls.exe is None:
raise unittest.SkipTest('MSVC не найден: host-тест разбора пропущен')
def run_host(self, *args: str) -> str:
result = subprocess.run([str(self.exe), *args], capture_output=True)
output = result.stdout.decode('utf-8', 'replace').strip()
self.assertEqual(result.returncode, 0, output)
return output
def rejected(self, mode: str, body: bytes) -> bytes:
"""Ответ, который разбор отвергает: строка вывода host-обёртки."""
sample = BUILD / 'rejected.json'
sample.write_bytes(body)
result = subprocess.run([str(self.exe), mode, str(sample)], capture_output=True)
self.assertEqual(result.returncode, 1, result.stdout)
return result.stdout.strip()
def geo(self, mode: str, fixture: str) -> list[str]:
"""Разобрать ответ справочника места: [GEO, широта, долгота, место]."""
parts = self.run_host(mode, str(FIXTURES / fixture)).split(' ', 3)
self.assertEqual(parts[0], 'GEO', parts)
return parts
def test_city_search_english(self) -> None:
# city: Kyiv — поиск с language=en, название по-английски
place = json.loads((FIXTURES / 'openmeteo_city_kyiv.json').read_text('utf-8'))['results'][0]
parts = self.geo('city', 'openmeteo_city_kyiv.json')
self.assertAlmostEqual(float(parts[1]), place['latitude'], places=4)
self.assertAlmostEqual(float(parts[2]), place['longitude'], places=4)
self.assertEqual(parts[3], 'Kyiv')
def test_city_search_russian(self) -> None:
# кириллица ищется с language=ru — и название приходит по-русски
self.assertEqual(self.geo('city', 'openmeteo_city_rim_ru.json')[3], 'Рим')
self.assertEqual(self.geo('city', 'openmeteo_city_rome_it.json')[3], 'Rome')
def test_city_not_found(self) -> None:
# ничего не нашёл — ответ без results; только это служба запоминает
self.assertEqual(self.rejected('city', (FIXTURES / 'openmeteo_city_none.json').read_bytes()),
b'NOTFOUND city: not found')
self.assertEqual(self.rejected('city', b'{"results":[]}'), b'NOTFOUND city: not found')
def test_city_broken_reply(self) -> None:
# испорченный ответ — ошибка, но не «места нет»: повтор может помочь
self.assertEqual(self.rejected('city', b'[]'), b'ERR city: broken json')
self.assertEqual(self.rejected('city', b'{"results":{}}'), b'ERR city: broken json')
self.assertEqual(self.rejected('city', b'{"results":[{"name":"x",'), b'ERR city: broken json')
self.assertEqual(self.rejected('city', b'{"results":[{"name":"x"}]}'),
b'ERR city: no coordinates')
def test_zippopotam_takes_last_place(self) -> None:
places = json.loads((FIXTURES / 'zippopotam_it_00144.json').read_text('utf-8'))['places']
last = places[-1]
parts = self.geo('zip', 'zippopotam_it_00144.json')
self.assertAlmostEqual(float(parts[1]), float(last['latitude']), places=4)
self.assertAlmostEqual(float(parts[2]), float(last['longitude']), places=4)
self.assertEqual(parts[3], last['place name'])
self.assertEqual(parts[3], 'Roma')
def test_zippopotam_rejects_empty_reply(self) -> None:
self.assertEqual(self.rejected('zip', b'{}'), b'ERR zip: no places')
def test_ini_text_to_utf8(self) -> None:
# zifi.ini правят и на ПК (UTF-8, CP1251 из Блокнота «ANSI»), и в
# редакторе Wild Commander (CP866): геокодеру нужен UTF-8.
cases = [
('Kyiv', 'Kyiv'.encode('ascii')),
('Рим', 'Рим'.encode('utf-8')),
('Рим', 'Рим'.encode('cp866')),
('Рим', 'Рим'.encode('cp1251')),
('Одесса', 'Одесса'.encode('cp866')),
('Одесса', 'Одесса'.encode('cp1251')),
('Нью-Йорк', 'Нью-Йорк'.encode('cp1251')),
('Київ', 'Київ'.encode('cp1251')),
('Київ', 'Київ'.encode('cp866')),
('Zürich', 'Zürich'.encode('utf-8')),
]
for i, (expected, raw) in enumerate(cases):
sample = BUILD / f'ini_{i}.txt'
sample.write_bytes(raw)
output = self.run_host('ini', str(sample))
self.assertTrue(output.startswith('UTF8'), output)
self.assertEqual(bytes.fromhex(output[5:]).decode('utf-8'), expected, raw)
def test_open_meteo_record(self) -> None:
fixture = FIXTURES / 'open_meteo_roma.json'
output = self.run_host('meteo', str(fixture), 'Roma')
self.assertTrue(output.startswith('REC '), output)
record = bytes.fromhex(output[4:])
self.assertEqual(len(record), REC_SIZE)
src = json.loads(fixture.read_text('utf-8'))
offset = src['utc_offset_seconds']
cur = src['current']
daily = src['daily']
self.assertEqual(record[0], 1)
self.assertEqual(record[1], 1)
self.assertEqual(record[REC_PLACE:REC_PLACE + 24].split(b'\0')[0], b'Roma')
temp, code, is_day, wind10, precip10, press = struct.unpack_from('<bBBHHH', record, REC_TEMP)
self.assertEqual(temp, rnd(cur['temperature_2m']))
self.assertEqual(code, cur['weather_code'])
self.assertEqual(is_day, cur['is_day'])
self.assertEqual(wind10, rnd(cur['wind_speed_10m'] * 10))
self.assertEqual(precip10, rnd(cur['precipitation'] * 10))
self.assertEqual(press, rnd(cur['surface_pressure'] * 0.750062))
self.assertEqual(tuple(record[35:37]), local_hm(daily['sunrise'][0], offset))
self.assertEqual(tuple(record[37:39]), local_hm(daily['sunset'][0], offset))
self.assertEqual(tuple(record[39:41]), local_hm(cur['time'], offset))
self.assertEqual(record[41], 6)
for i in range(6):
day = record[REC_DAYS + i * 8:REC_DAYS + (i + 1) * 8]
d, m, wday = local_date(daily['time'][i] + 43200, offset)
self.assertEqual((day[0], day[1], day[2]), (d, m, wday), f'день {i}')
self.assertEqual(day[3], daily['weather_code'][i])
tmin, tmax, dprecip = struct.unpack_from('<bbH', day, 4)
self.assertEqual(tmin, rnd(daily['temperature_2m_min'][i]))
self.assertEqual(tmax, rnd(daily['temperature_2m_max'][i]))
self.assertEqual(dprecip, rnd(daily['precipitation_sum'][i] * 10))
def test_place_from_utf8_file(self) -> None:
# Название места — в UTF-8, как его дал справочник; передаётся файлом:
# так делает модель ESP стенда Unreal (аргументы Windows приходят в ANSI).
for text, expected in (('Città di Castello', b'Citta di Castello'),
('Одесса', 'Одесса'.encode('cp866'))):
place = BUILD / 'place_utf8.txt'
place.write_bytes(text.encode('utf-8'))
output = self.run_host('meteo', str(FIXTURES / 'open_meteo_roma.json'), '@' + str(place))
record = bytes.fromhex(output[4:])
self.assertEqual(record[REC_PLACE:REC_PLACE + 24].split(b'\0')[0], expected)
def test_utf8_to_cp866(self) -> None:
sample = BUILD / 'sample_utf8.txt'
sample.write_bytes('Zürich Gréoux Москва Ёж 15°'.encode('utf-8'))
output = self.run_host('cp866', str(sample))
self.assertTrue(output.startswith('CP866 '))
self.assertEqual(bytes.fromhex(output[6:]),
'Zurich Greoux Москва Ёж 15°'.encode('cp866'))
def test_utf8_to_cp866_ukrainian(self) -> None:
# Є є Ї ї Ў ў в CP866 есть; І і пишутся латинскими I i (выглядят так
# же), Ґ ґ — как Г г; типографские апостроф, кавычки и тире — ASCII.
text = 'Київ Єнакієве Ґалаґан Ўсход Кам’янець «Ліс» — І'
sample = BUILD / 'sample_ua.txt'
sample.write_bytes(text.encode('utf-8'))
output = self.run_host('cp866', str(sample))
simple = text.translate(str.maketrans({'і': 'i', 'І': 'I', 'ґ': 'г', 'Ґ': 'Г',
'’': "'", '«': '"', '»': '"', '—': '-'}))
self.assertEqual(bytes.fromhex(output[6:]), simple.encode('cp866'))
if __name__ == '__main__':
unittest.main()
+116
View File
@@ -0,0 +1,116 @@
// Host-обёртка для tests/test_weather_parse.py: запускает разбор ответов
// погодных сервисов из src/weather_parse.cpp на ПК и печатает результат.
// weather_parse_host city <файл json> -> GEO <lat> <lon> <place utf-8>
// (NOTFOUND <текст> — места нет)
// weather_parse_host zip <файл json> -> GEO <lat> <lon> <place utf-8>
// weather_parse_host meteo <файл json> <место> -> REC <hex записи>
// weather_parse_host cp866 <файл utf-8> -> CP866 <hex>
// weather_parse_host ini <файл> -> UTF8 <hex> (значение zifi.ini в UTF-8)
// Место с не-ASCII буквами передаётся как @<файл UTF-8>: аргументы командной
// строки Windows приходят в main() в кодовой странице ANSI, а не в UTF-8.
// Этим пользуется модель ESP стенда Unreal (shared/weather/esp_model.py).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include "zifi/weather_parse.hpp"
namespace {
std::vector<char> readFile(const char* path) {
std::vector<char> data;
FILE* file = fopen(path, "rb");
if (file == nullptr) {
return data;
}
char buffer[4096];
size_t got;
while ((got = fread(buffer, 1, sizeof(buffer), file)) != 0) {
data.insert(data.end(), buffer, buffer + got);
}
fclose(file);
data.push_back(0);
return data;
}
void printHex(const unsigned char* data, size_t length) {
for (size_t i = 0; i < length; ++i) {
printf("%02x", data[i]);
}
printf("\n");
}
} // namespace
int main(int argc, char** argv) {
if (argc < 3) {
printf("ERR usage\n");
return 2;
}
const std::string mode = argv[1];
std::vector<char> data = readFile(argv[2]);
if (data.size() <= 1) {
printf("ERR read %s\n", argv[2]);
return 2;
}
const size_t length = data.size() - 1;
char error[96] = {};
if (mode == "city") {
zifi::GeoResult geo = {};
bool notFound = false;
if (!zifi::parseCitySearch(data.data(), length, geo, notFound, error, sizeof(error))) {
printf("%s %s\n", notFound ? "NOTFOUND" : "ERR", error);
return 1;
}
printf("GEO %.4f %.4f %s\n", geo.latitude, geo.longitude, geo.place);
return 0;
}
if (mode == "zip") {
zifi::GeoResult geo = {};
if (!zifi::parseZippopotam(data.data(), length, geo, error, sizeof(error))) {
printf("ERR %s\n", error);
return 1;
}
printf("GEO %.4f %.4f %s\n", geo.latitude, geo.longitude, geo.place);
return 0;
}
if (mode == "ini") {
char out[256];
zifi::iniTextToUtf8(data.data(), out, sizeof(out));
printf("UTF8 ");
printHex(reinterpret_cast<const unsigned char*>(out), strlen(out));
return 0;
}
if (mode == "meteo") {
unsigned char record[zifi::kWeatherRecordSize];
const char* place = argc > 3 ? argv[3] : "";
std::vector<char> placeFile;
if (place[0] == '@') {
placeFile = readFile(place + 1);
if (placeFile.empty()) {
printf("ERR read %s\n", place + 1);
return 2;
}
place = placeFile.data();
}
if (!zifi::parseOpenMeteo(data.data(), length, place, record, error, sizeof(error))) {
printf("ERR %s\n", error);
return 1;
}
printf("REC ");
printHex(record, sizeof(record));
return 0;
}
if (mode == "cp866") {
char out[64];
zifi::utf8ToCp866(data.data(), out, sizeof(out));
printf("CP866 ");
printHex(reinterpret_cast<const unsigned char*>(out), strlen(out));
return 0;
}
printf("ERR mode\n");
return 2;
}
+272
View File
@@ -0,0 +1,272 @@
if(NOT ESP_PLATFORM)
if(CMAKE_SYSTEM_NAME MATCHES NintendoSwitch OR CMAKE_SYSTEM_NAME MATCHES Nintendo3DS OR CMAKE_SYSTEM_NAME MATCHES dreamcast OR CMAKE_SYSTEM_NAME MATCHES NintendoGameCube OR CMAKE_SYSTEM_NAME MATCHES NintendoDS OR CMAKE_SYSTEM_NAME MATCHES NintendoWii OR CMAKE_SYSTEM_NAME MATCHES CafeOS)
cmake_minimum_required(VERSION 3.13)
elseif(VITA)
cmake_minimum_required(VERSION 3.2)
elseif(PICO_BOARD)
cmake_minimum_required(VERSION 3.12)
message(STATUS "Configuring libsmb2 for ${PICO_BOARD}")
else()
cmake_minimum_required(VERSION 3.5)
endif()
if(NOT IOP AND BUILD_IRX)
project(smb2man
LANGUAGES C
VERSION 2.3.0
)
set(VERSION 2.3.0)
set(PACKAGE "smb2man")
set(PACKAGE_BUGREPORT "ronniesahlberg@gmail.com or andregui17@outlook.com")
set(PACKAGE_NAME "smb2man")
set(PACKAGE_VERSION "${VERSION}")
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME "${PACKAGE}")
set(PACKAGE_URL "https://github.com/sahlberg/libsmb2")
elseif(PICO_BOARD)
project(libsmb2 C CXX ASM)
else()
project(libsmb2
LANGUAGES C
VERSION 6.1.0
)
set(VERSION 6.1.0)
set(PACKAGE "libsmb2")
set(PACKAGE_BUGREPORT "ronniesahlberg@gmail.com")
set(PACKAGE_NAME "libsmb2")
set(PACKAGE_VERSION "${VERSION}")
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME "${PACKAGE}")
set(PACKAGE_URL "https://github.com/sahlberg/libsmb2")
endif()
configure_file(libsmb2.pc.in libsmb2.pc @ONLY)
endif()
if(NOT ESP_PLATFORM)
set(SOVERSION 1 CACHE STRING "" FORCE)
if(NOT IOP AND NOT BUILD_IRX)
set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Installation directory for libraries")
set(INSTALL_INC_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "Installation directory for headers")
set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files")
set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/cmake/libsmb2" CACHE PATH "Installation directory for cmake (.cmake) files")
endif()
if(CMAKE_SYSTEM_NAME MATCHES NintendoSwitch OR CMAKE_SYSTEM_NAME MATCHES Nintendo3DS OR PS4 OR PS2 OR VITA OR CMAKE_SYSTEM_NAME MATCHES dreamcast OR CMAKE_SYSTEM_NAME MATCHES NintendoGameCube OR CMAKE_SYSTEM_NAME MATCHES NintendoDS OR CMAKE_SYSTEM_NAME MATCHES NintendoWii OR CMAKE_SYSTEM_NAME MATCHES CafeOS)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
else()
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
endif()
option(ENABLE_EXAMPLES "Build example programs" OFF)
option(ENABLE_LIBKRB5 "Enable libkrb5 support" ON)
option(ENABLE_GSSAPI "Enable gssapi support" ON)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
endif()
# Embedded platforms: libsmb2 only (share-enum NetrShareEnum 0/1/2).
# Full DCE/RPC is a separate library (libdcerpc) on full-size platforms.
# Windows: never build libdcerpc — its symbols collide with Microsoft RPC.
set(_SMB2_EMBEDDED_DCERPC FALSE)
if(ESP_PLATFORM OR PICO_BOARD OR IOP OR BUILD_IRX OR EE OR PS2 OR PS4 OR VITA)
set(_SMB2_EMBEDDED_DCERPC TRUE)
elseif(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|Nintendo3DS|dreamcast|NintendoGameCube|NintendoDS|NintendoWii|CafeOS")
set(_SMB2_EMBEDDED_DCERPC TRUE)
endif()
if(_SMB2_EMBEDDED_DCERPC)
set(ENABLE_LIBDCERPC OFF)
message(STATUS "Embedded platform: libsmb2 only (minimal DCE/RPC share enum)")
elseif(WIN32 OR CMAKE_SYSTEM_NAME MATCHES "Windows")
set(ENABLE_LIBDCERPC OFF)
message(STATUS "Windows: libdcerpc disabled (symbol collisions with Microsoft RPC)")
else()
if(NOT ESP_PLATFORM)
option(ENABLE_LIBDCERPC "Build libdcerpc (full DCE/RPC; depends on libsmb2)" ON)
endif()
if(ENABLE_LIBDCERPC)
message(STATUS "Build libdcerpc (full DCE/RPC interfaces)")
else()
message(STATUS "libdcerpc disabled; libsmb2 keeps NetrShareEnum 0/1/2 only")
endif()
endif()
if(CMAKE_SYSTEM_NAME MATCHES Linux)
if (ENABLE_LIBKRB5)
find_package(LibKrb5)
endif()
elseif(IOS)
if (ENABLE_GSSAPI)
find_package(GSSAPI)
endif()
endif()
if(NOT ESP_PLATFORM)
include(cmake/ConfigureChecks.cmake)
endif()
if(PICO_BOARD)
include_directories(libsmb2 STATIC
${PICO_SDK_PATH}/lib/lwip/src/include
${PICO_SDK_PATH}/src/rp2_common/pico_lwip/include
lib
include
include/smb2
include/picow
)
elseif(ESP_PLATFORM)
set(COMPONENT_ADD_INCLUDEDIRS
"lib"
include
include/smb2
include/esp
)
else()
include_directories(${CMAKE_CURRENT_BINARY_DIR}
include
include/smb2
)
endif()
if(CMAKE_SYSTEM_NAME MATCHES Linux)
if (ENABLE_LIBKRB5)
set(core_DEPENDS ${LIBKRB5_LIBRARY} CACHE STRING "" FORCE)
endif()
elseif(IOS)
if (ENABLE_GSSAPI)
set(core_DEPENDS ${GSSAPI_LIBRARIES} CACHE STRING "" FORCE)
endif()
endif()
if(MSVC AND BUILD_SHARED_LIBS)
# If we are building dll with msvc, then generate a def file according to the syms file
set(SYMS_FILE_PATH "${PROJECT_SOURCE_DIR}/lib/libsmb2.syms")
set(DEF_FILE_PATH "${PROJECT_BINARY_DIR}/lib/libsmb2.def")
file(READ ${SYMS_FILE_PATH} EXPORT_SYMBOLS)
file(WRITE ${DEF_FILE_PATH} "LIBRARY smb2\nEXPORTS\n\n${EXPORT_SYMBOLS}")
# Pass def file to the linker
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEF:${DEF_FILE_PATH}")
if(ENABLE_LIBDCERPC)
set(DCERPC_SYMS_FILE_PATH "${PROJECT_SOURCE_DIR}/libdcerpc/libdcerpc.syms")
set(DCERPC_DEF_FILE_PATH "${PROJECT_BINARY_DIR}/libdcerpc/libdcerpc.def")
file(READ ${DCERPC_SYMS_FILE_PATH} DCERPC_EXPORT_SYMBOLS)
file(WRITE ${DCERPC_DEF_FILE_PATH} "LIBRARY dcerpc\nEXPORTS\n\n${DCERPC_EXPORT_SYMBOLS}")
endif()
endif()
if(CMAKE_SYSTEM_NAME MATCHES Windows)
list(APPEND CORE_LIBRARIES ws2_32.lib)
add_definitions(-DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS -DHAVE_LINGER -DNEED_GETLOGIN_R -DNEED_GETPID -DNEED_RANDOM -DNEED_SRANDOM)
elseif(CMAKE_SYSTEM_NAME MATCHES NintendoSwitch)
add_definitions(-DNEED_READV -DNEED_WRITEV -DNEED_GETLOGIN_R)
elseif(CMAKE_SYSTEM_NAME MATCHES Nintendo3DS)
add_definitions(-DARM11 -D_3DS -D__3DS__ -DNEED_READV -DNEED_WRITEV -DNEED_GETLOGIN_R)
elseif(CMAKE_SYSTEM_NAME MATCHES NintendoDS)
add_definitions(-DNEED_READV -DHAVE_SOCKADDR_STORAGE -DNEED_WRITEV -DNEED_GETLOGIN_R -DNEED_GETADDRINFO -DNEED_FREEADDRINFO -DNEED_POLL)
elseif(CMAKE_SYSTEM_NAME MATCHES NintendoGameCube)
add_definitions(-DNEED_READV -DNEED_WRITEV -DNEED_GETLOGIN_R -DNEED_GETADDRINFO -DNEED_FREEADDRINFO -DNEED_POLL)
elseif(CMAKE_SYSTEM_NAME MATCHES NintendoWii)
add_definitions(-DNEED_READV -DNEED_WRITEV -DNEED_GETLOGIN_R -DNEED_GETADDRINFO -DNEED_FREEADDRINFO -DNEED_POLL)
elseif(CMAKE_SYSTEM_NAME MATCHES CafeOS)
add_definitions(-DNEED_READV -DNEED_WRITEV -DNEED_GETLOGIN_R)
elseif(PS4)
add_definitions(-DPS4_PLATFORM)
set(CMAKE_C_FLAGS "-funwind-tables")
#-fuse-init-array -emit-llvm broken...
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
add_compile_options("${CMAKE_C_FLAGS}")
elseif(EE)
add_definitions(-DNEED_BE64TOH -DNEED_POLL -DNEED_WRITEV -DNEED_READV -DNEED_GETLOGIN_R)
if(EE AND PS2RPC)
add_definitions(-DPS2RPC)
endif()
elseif(IOP)
add_definitions(-G 0 -DHAVE_CONFIG_H -DNEED_BE64TOH -DNEED_STRDUP -DNEED_READV -DNEED_WRITEV -DNEED_POLL -DNEED_GETPID -DNEED_RANDOM -DNEED_SRANDOM -DNEED_GETLOGIN_R -DHAVE_SYS_FCNTL_H)
if(IOP AND BUILD_IRX)
add_definitions(-D__ps2sdk_iop__)
if(DEBUG)
add_definitions(-DDEBUG)
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fno-builtin")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s")
endif()
set(CMAKE_C_FLAGS "-Wall")
include_directories(${CMAKE_CURRENT_BINARY_DIR} include/ps2)
add_compile_options("${CMAKE_C_FLAGS}")
elseif(VITA)
add_definitions(-DNEED_READV -DNEED_WRITEV -DNEED_GETLOGIN_R)
elseif(PICO_BOARD)
add_definitions(-DNEED_BE64TOH -DNEED_POLL -DNEED_GETLOGIN_R)
elseif(ESP_PLATFORM)
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
add_definitions(-DHAVE_CONFIG_H)
endif()
add_definitions(-DNEED_READV -DNEED_WRITEV -DNEED_GETLOGIN_R -DNEED_RANDOM -DNEED_SRANDOM)
elseif(CMAKE_SYSTEM_NAME MATCHES dreamcast)
add_definitions(-DNEED_WRITEV -DNEED_READV -DHAVE_CONFIG_H -DNEED_GETLOGIN_R)
elseif(CMAKE_SYSTEM_NAME STREQUAL Solaris)
find_library(SOCKET_LIBRARY socket)
find_library(NSL_LIBRARY nsl)
list(APPEND CORE_LIBRARIES ${SOCKET_LIBRARY} ${NSL_LIBRARY})
endif()
# Look for a cryptographically strong source of random bytes for
# smb2_random_bytes(). Only probed on the host-like systems: anything we
# do not positively identify here simply falls back to the random()
# based path, so the console and embedded toolchains are left alone.
if(CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin|.*BSD|Solaris|Android")
include(CheckSymbolExists)
check_symbol_exists(arc4random_buf "stdlib.h" HAVE_ARC4RANDOM_BUF)
check_symbol_exists(getrandom "sys/random.h" HAVE_GETRANDOM)
if(HAVE_ARC4RANDOM_BUF)
add_definitions(-DHAVE_ARC4RANDOM_BUF)
endif()
if(HAVE_GETRANDOM)
add_definitions(-DHAVE_SYS_RANDOM_H -DHAVE_GETRANDOM)
endif()
if(EXISTS "/dev/urandom")
add_definitions(-DHAVE_DEV_URANDOM)
endif()
endif()
add_subdirectory(lib)
if(ENABLE_LIBDCERPC)
add_subdirectory(libdcerpc)
endif()
if(ENABLE_EXAMPLES)
add_subdirectory(examples)
endif()
if(NOT PICO_BOARD OR ESP_PLATFORM)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/libsmb2-config-version.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
if(NOT IOP AND NOT BUILD_IRX)
configure_file(cmake/libsmb2.pc.cmake
${CMAKE_CURRENT_BINARY_DIR}/libsmb2.pc @ONLY)
if(ENABLE_LIBDCERPC)
configure_file(cmake/libdcerpc.pc.cmake
${CMAKE_CURRENT_BINARY_DIR}/libdcerpc.pc @ONLY)
endif()
install(DIRECTORY include/smb2
DESTINATION ${INSTALL_INC_DIR})
if(ENABLE_LIBDCERPC)
install(DIRECTORY include/dcerpc
DESTINATION ${INSTALL_INC_DIR})
endif()
install(FILES cmake/FindSMB2.cmake
${CMAKE_CURRENT_BINARY_DIR}/libsmb2-config-version.cmake
DESTINATION ${INSTALL_CMAKE_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libsmb2.pc
DESTINATION ${INSTALL_PKGCONFIG_DIR})
if(ENABLE_LIBDCERPC)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libdcerpc.pc
DESTINATION ${INSTALL_PKGCONFIG_DIR})
endif()
endif()
endif()
+35
View File
@@ -0,0 +1,35 @@
Libsmb2 components fall under separate licences
The lib and include directories (except DCE/RPC)
===============================================
The smb2 client library itself, i.e. the lib directory and non-DCE/RPC
headers under include/, is licenced under GNU Lesser General Public
License as published by the Free Software Foundation; either version 2.1
of the License, or (at your option) any later version.
A copy of LGPL 2.1 is included in the file LICENCE-LGPL-2.1.txt but can
also be downloaded from
http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
This is the licence that applies to the libsmb2 library and its use.
The libdcerpc directory and DCE/RPC headers
===========================================
The DCE/RPC library (libdcerpc/) and the DCE/RPC public headers
(include/smb2/libsmb2-dcerpc*.h) are licenced under the 2-Clause BSD
License.
To avoid any confusion, every source file also contain a licence boilerplate.
The examples directory
======================
The example applications using this library, i.e. the examples directory,
is licenced under 2-Clause BSD License.
These are executable applications that link with the libsmb2 library
and/or libdcerpc.
To avoid any confusion, every source file also contain a licence boilerplate.
+502
View File
@@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
+21
View File
@@ -0,0 +1,21 @@
if ENABLE_EXAMPLES
MAYBE_EXAMPLES = examples
endif
if BUILD_LIBDCERPC
MAYBE_LIBDCERPC = libdcerpc
MAYBE_LIBDCERPC_PC = libdcerpc.pc
endif
SUBDIRS = include lib $(MAYBE_LIBDCERPC) utils . $(MAYBE_EXAMPLES)
ACLOCAL_AMFLAGS = -I m4
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsmb2.pc $(MAYBE_LIBDCERPC_PC)
EXTRA_DIST = \
libsmb2.pc.in \
libdcerpc.pc.in
test: $(SUBDIRS)
cd tests; make test
+112
View File
@@ -0,0 +1,112 @@
# -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*-
#
# Copyright (C) 2024 by André Guilherme <andregui17@outlook.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
.PHONY: gc_all gc_install nds_all nds_install wii_all wii_install 3ds_all 3ds_install wiiu_all wiiu_install switch_all switch_install dc_all dc_install ps2_ee_all ps2_ee_install ps2_ips_all ps2_ips_install ps2_iop_all ps2_iop_install ps4_all ps4_install
gc_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/GameCube.cmake"
cmake --build build
gc_install: gc_all
cmake --build build --target install
nds_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/NDS.cmake"
cmake --build build
nds_install: nds_all
cmake --build build --target install
wii_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/Wii.cmake"
cmake --build build
wii_install: wii_all
cmake --build build --target install
3ds_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/3DS.cmake"
cmake --build build
3ds_install: 3ds_all
cmake --build build --target install
wiiu_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/WiiU.cmake"
cmake --build build
wiiu_install: wiiu_all
cmake --build build --target install
switch_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/Switch.cmake"
cmake --build build
switch_install: switch_all
cmake --build build --target install
dc_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${KOS_CMAKE_TOOLCHAIN}" "-DCMAKE_INSTALL_PREFIX=${KOS_BASE}/addons/" "-DCMAKE_PREFIX_PATH=${KOS_BASE}/addons/"
cmake --build build
dc_install: dc_all
cmake --build build --target install
ps2_ee_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${PS2DEV}/share/ps2dev.cmake" "-DCMAKE_INSTALL_PREFIX=${PS2SDK}/ports" "-DCMAKE_PREFIX_PATH=${PS2SDK}/ports"
cmake --build build
ps2_ee_install: ps2_ee_all
cmake --build build --target install
ps2_rpc_all:
cmake -S . -B build -DPS2RPC=1 "-DCMAKE_TOOLCHAIN_FILE=${PS2DEV}/share/ps2dev.cmake" "-DCMAKE_INSTALL_PREFIX=${PS2SDK}/ports" "-DCMAKE_INSTALL_PREFIX=${PS2SDK}/ports"
cmake --build build
ps2_rpc_install: ps2_rpc_all
cmake --build build --target install
ps2_iop_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${PS2DEV}/share/ps2dev_iop.cmake" "-DCMAKE_INSTALL_PREFIX=${PS2SDK}/ports_iop" "-DCMAKE_INSTALL_PREFIX=${PS2SDK}/ports_iop"
cmake --build build
ps2_iop_install: ps2_iop_all
cmake --build build --target install
ps2_irx_all:
cmake -S . -B build -DBUILD_IRX=1 "-DCMAKE_TOOLCHAIN_FILE=${PS2DEV}/share/ps2dev_iop.cmake"
cmake --build build
ps2_irx_install: ps2_irx_all
cmake --build build --target install
ps4_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=/opt/pacbrew/ps4/openorbis/cmake/ps4.cmake" "-DCMAKE_INSTALL_PREFIX=/opt/pacbrew/ps4/openorbis/" "-DCMAKE_PREFIX_PATH=/opt/pacbrew/ps4/openorbis/"
cmake --build build
vita_all:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake"
cmake --build build
vita_install: vita_all
cmake --build build --target install
ps4_install: ps4_all
cmake --build build --target install
clean:
rm -rf build
+444
View File
@@ -0,0 +1,444 @@
Libsmb2 is a userspace client/server library for accessing or serving
SMB2/SMB3 shares on a network.
It is high performance and fully async. It supports both zero-copy
for SMB READ/WRITE commands as well as compounded commands.
Libsmb2 (the SMB2 client library) is distributed under the LGPLv2.1 licence.
libdcerpc is distributed under the 2-Clause BSD licence. See COPYING.
API
===
Libsmb2 implements three different APIs for accessing remote SMB shares :
1, High level synchronous posix-like API:
This is a simple API for accessing a share.
The functions in this API are modelled to be be similar to the corresponding
POSIX functions.
This API is described in libsmb2.h
2, High level async posix-like API:
This is a high performance, fully non-blocking and async API.
The functions in this API are modelled to be be similar to the corresponding
POSIX functions.
This is the recommended API.
This API is described in libsmb2.h
3, Low level async RAW API:
This is a low level API that provides direct access to the SMB2 PDUs
and data structures.
This API is described in libsmb2-raw.h
DCE/RPC (libdcerpc)
===================
Full DCE/RPC support (named-pipe transport over SMB2, NDR/YAML/JSON coders,
srvsvc/lsa/winreg/wkssvc/epm) is provided by a separate library, **libdcerpc**
(sources under `libdcerpc/`), which depends on libsmb2 for transport.
Link with: -ldcerpc -lsmb2
pkg-config: libdcerpc
libsmb2 still provides high-level share enumeration only
(smb2_share_enum_async / smb2_share_enum_sync for NetrShareEnum levels 0/1/2).
Internal DCE/RPC symbols used for that path are prefixed with libsmb2_ so
applications can link both libraries without symbol conflicts.
libdcerpc is not built on Windows (native or MinGW): its symbols collide with
Microsoft's own RPC libraries. On Windows, only the minimal share-enum path
in libsmb2 is available.
Public DCE/RPC headers are under include/dcerpc/.
Libsmb2 implements a synchronous API for running an SMB server. You could
run an async server if you implement the main loop yourself however
SMB URL Format
==============
The SMB URL format is currently a small subset of the URL format that is
defined/used by the Samba project.
The goal is to eventually support the full URL format, thus making URLs
interchangeable between Samba utilities and Libsmb2 but we are not there yet.
smb://[<domain>;][<user>@]<server>[:<port>]/<share>[/path][?arg=val[&arg=val]*]
<server> is either a hostname, an IPv4 or an IPv6 address.
Arguments supported by libsmb2 are :
sec=<mech> : Mechanism to use to authenticate to the server. Default
is any available mech, but can be overridden by :
krb5: Use Kerberos using credentials from kinit.
krb5cc: Use Kerberos using credentials from credentials
cache.
ntlmssp : Only use NTLMSSP
vers=<version> : Which SMB version to negotiate:
2: Negotiate any version of SMB2
3: Negotiate any version of SMB3
2.02, 2.10, 3.00, 3.02, 3.1.1 : negotiate a specific version.
Default is to negotiate any SMB2 or SMB3 version.
seal : Enable SMB3 encryption.
sign : Require SMB2/3 signing.
timeout : Timeout in seconds when to cancel a command.
Default it 0: No timeout.
ndr32 : DCERPC: only offer NDR32 transfer syntax. (default)
ndr64 : DCERPC: only offer NDR64 transfer syntax.
ndr3264 : DCERPC: offer both NRD32 and NDR64 transfer syntax.
le : DCERPC: send PDUs in Little-Endian format
be : DCERPC: send PDUs in Big-Endian format
NOTE:-
When using krb5cc mode use smb2_set_domain() and smb2_set_password() in the examples and applications
SMB Server
==========
There is an example server implementation in examples/smb2-server-sync.c. The library
server function is handed an array of function pointers that the library will call
for each client command. It is up to your implementation to fill out and return
replies for each according to your use. The example simulates a disk with a few files.
Your handler will be also be called to pre-authenticate users as part of ntlmssp (krb
authenticaton not yet implemented). Your application will also be called-back each
time a client connect to allow your to configure the context prior to negotiation.
Authentication
==============
Libsmb2 provides has builtin support for NTLMSSP username/password
authentication.
It can also, optionally, be built with (MIT) Kerberos authentication.
Libsmb2 will try to build with Kerberos if these libraries are present.
You can force a build without Kerberos support by using the flag
--without-libkrb5 to configure. In this case only NTLMSSP authentication
will be available.
DCE/RPC interfaces
==================
On full-size non-Windows platforms (Linux, macOS, ...), the full DCE/RPC
interface set is built as libdcerpc by default (srvsvc, lsarpc, winreg,
wkssvc, epm). Disable with cmake -DENABLE_LIBDCERPC=OFF.
Windows and embedded targets (ESP, Pico, consoles, PS2/PS3/PS4, Vita, ...)
always build only the minimal ShareEnum surface in libsmb2; libdcerpc is
not available there (on Windows, due to symbol collisions with Microsoft RPC).
MIT KERBEROS
============
Authentication is implemented using MIT Kerberos and it supports both KRB5
for authentication against Active Directory as well as NTLMSSP (optional).
MIT Kerberos can be configured to also provide NTLMSSP authentication,
as an alternative to the builtin NTLMSSP implementation using an
external mech plugin.
To use this Kerberos/NTLMSSP module you will need to build and install
GSS-NTLMSSP from [https://github.com/simo5/gss-ntlmssp]
If you are uncertain you can skip this module and just use the NTLMSSP
module that is provided by libsmb2.
NTLM Authentication
-------------------
NTLM credentials are stored in a text file of the form :
DOMAIN:USERNAME:PASSWORD
with one line per username.
You need to set up the environment variable NTLM_USER_FILE to point to this
file.
You need one entry in this file for each local user account you want to be able
to use libsmb2 for accessing a remote share.
By default, NTLM authentication will use the username for the current process.
This can be overridden by specifying a different username in the SMB URL :
smb://guest@server/share?sec=ntlmssp
KRB5 Authentication
-------------------
Kerberos authentication can be used when the linux workstation as well as
the file server are part of Active Directory.
You should be able to authenticate to the file server using krb5
by specifying sec=krb5 in the URL :
smb://server/share?sec=krb5
The application needs to set the username, password and the domain fqdn in the context using
smb2_set_user(), smb2_set_password() and smb2_set_domain() respectively.
NTLM Credentials
================
This applies to both the builtin NTLMSSP implementation as well as when
using Kerberos with the NTLMSSP mech plugin.
NTLM credentials are stored in a text file of the form :
DOMAIN:USERNAME:PASSWORD
with one line per username.
You need to set up the environment variable NTLM_USER_FILE to point to this
file.
You need one entry in this file for each local user account you want to be able
to use libsmb2 for accessing a remote share.
By default, NTLM authentication will use the username for the current process.
This can be overridden by specifying a different username in the SMB URL :
smb://guest@server/share?sec=ntlmssp
Alternatively you can provide the username and password from your application
by calling :
smb2_set_user(smb2, <username>);
smb2_set_password(smb2, <password>);
(For server, you don't need to set the user, as that is supplied by the client)
SMB2/3 SIGNING
==============
Signing is supported with KRB5, with the builtin ntlmssp support and with
gss-ntlmssp mech plugin.
SMB3 Encryption
===============
Encryption is only supported with KRB5 or with the builtin ntlmssp support.
Encryption is not supported when the gss-ntlmssp mech plugin is used.
Encryption can be enabled either using the "seal" URL argument or by calling
smb3_set_seal(smb2, 1);
BUILDING LIBSMB2
===============
Windows
---------------------------
You have to install CMake (https://cmake.org/) and Visual Studio (https://www.visualstudio.com/)
to build libsmb2 for Windows (including Universal Windows Platform).
Please follow the next steps to build shared library:
mkdir build
cd build
cmake -G "Visual Studio 15 2017" ..
cmake --build . --config RelWithDebInfo
Static library:
mkdir build
cd build
cmake -G "Visual Studio 15 2017" -DBUILD_SHARED_LIBS=0 ..
cmake --build . --config RelWithDebInfo
macOS, iOS, tvOS, watchOS
---------------------------
You can use AMSMB2 (https://github.com/amosavian/AMSMB2) universal framework
which incorporates precompiled libsmb2 for Apple devices.
It is written in Swift but can be used in both Swift and Objective-C codes.
If you want to rebuild libsmb2 in AMSMB2, please follow these steps:
git clone https://github.com/amosavian/AMSMB2
cd AMSMB2/buildtools
./build.sh
Precompiled binaries don't include Kerberos support by default.
If you want build libraries with Kerberos support, execute this script instead:
./build-with-krb5.sh
ESP32
-----
libsmb2 is pre-configured for the ESP32 micro-controller using the esp-idf
toolchain (Arduino is not supported). Simply clone this project in the
'components' directory of the ESP32 project and it will automatically be
included in the build process.
Raspberry Pi Pico W (RP2040)
----------------------------
libsmb2 will compile on the RP2040 using gcc-arm-none-eabi, the pico-sdk
and FreeRTOS-Kernel. In examples/picow is a CMakeLists.txt that can be
edited to point at the pico-sdk and FreeRTOS-Kernel, and will then build
libsmb2 and a sample - this can be used as a starting point. Inside
include/picow are some configuration files for lwip, FreeRTOS and any
applications built with libsmb2. These can also be used as a starting
point and adjusted as needed for your applications.
The only define needed for libsmb2 on the RP2040, other than the RP2040
defines such as PICO_BOARD=pico_w, is PICO_PLATFORM.
Playstation 2
------------
EE, Emotion-Engine, is the main CPU for the PS2.
To compile libsmb2 for the PS2 EE, first install the PS2 toolchain and
PS2 SDK and set it up.
To build libsmb2.a, a version of libsmb2 for the EE tcpip stack:
$ make -f Makefile.platform clean
$ make -f Makefile.platform ps2_ee_install
EE Using IOP Stack, It´s a different of EE
version for when the LWIP stack is running on the IOP (libsmb2_rpc and linking
with -lps2ips)
To build libsmb2_rpc.a, a version of libsmb2 for ee with IOP tcpip stack:
$ make -f Makefile.platform clean
$ make -f Makefile.platform ps2_rpc_install
IOP, IO-Processor is the secondary CPU for the PS2.
The library is been used to build smb2man.irx module but it doesn´t comes with the library installed, To install libsmb2 for the PS2 IOP and smb2man.irx, first install the PS2 toolchain and PS2SDK and set it up.
Then to build libsmb2, run
$ make -f Makefile.platform clean
$ make -f Makefile.platform ps2_iop_install
$ make -f Makefile.platform clean
$ make -f Makefile.platform ps2_irx_install
PlayStation 3
-------------
PPU, PowerPC, is the main CPU for the PS3.
To compile libsmb2 for the PS3 PPU, first install the PS3 toolchain and
PSL1GHT SDK and set it up.
Then to build libsmb2, run
$ cd lib
$ make -f Makefile.PS3_PPU install
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
PSL1GHT SDK portlibs folder.
PlayStation Vita
-------------
ARM® Cortex™ - A9 core (4 core), is the main CPU for the PSVITA.
To compile libsmb2 for the PSVITA, first install the VitaSDK using
vdpm
Then to build libsmb2, run
$ make vita_install -f Makefile.platform
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
VitaSDK libs folder.
PlayStation 4
-------------
x86_64 is the main CPU for the PS4.
To compile libsmb2 for the PS4 PPU, first install the PS4 toolchain and
OpenOrbis SDK and set it up.
Then to build libsmb2, run
$ make -f Makefile.platform ps4_install
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
OpenOrbis SDK include folder.
Nintendo 3DS
-------------
Nintendo 3DS CPU is a ARM11 MPCore variant.
To compile libsmb2 for the Nintendo 3DS, first install the devkitPro with libctru to set it up.
Then to build libsmb2, run
$ make -f Makefile.platform 3ds_install
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
devkitPro 3ds portlibs folder.
Nintendo Switch
-------------
Nintendo Switch CPU is a Custom Nvidia Tegra X1.
To compile libsmb2 for the Nintendo Switch, first install the devkitPro with libnx to set it up.
Then to build libsmb2, run
$ make -f Makefile.platform switch_install
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
devkitPro switch portlibs folder.
Nintendo Wii
-------------
Nintendo Wii CPU is a Broadway PowerPC processor.
To compile libsmb2 for the Nintendo Wii, first install the devkitPro with libogc using pacman to set it up.
Then to build libsmb2, run
$ make -f Makefile.platform wii_install
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
devkitPro wii portlibs folder.
Nintendo Gamecube
-------------
Nintendo GameCube CPU is a IBM "Gekko" PowerPC CPU.
To compile libsmb2 for the Gamecube, first install the devkitPro with libogc using pacman to set it up.
Then to build libsmb2, run
$ make -f Makefile.platform gc_install
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
devkitPro gamecube portlibs folder.
Nintendo DS
-------------
Nintendo DS CPU is both ARM7TDMI and ARM946E-S.
To compile libsmb2 for the Nintendo DS, first install the devkitPro with libnds using pacman to set it up.
Then to build libsmb2, run
$ make -f Makefile.platform ds_install
The process will copy the resulting libsmb29.a and the include/smb2 headers to your
devkitPro ds portlibs folder on: lib/arm9.
Nintendo WII-U
-------------
Nintendo Wii-U CPU is a IBM "Espresso" PowerPC-based 45 nm, with 4 cores and a clock speed of 1.24 GHz.
To compile libsmb2 for the Nintendo WII-U, first install the devkitPro with libwut using pacman to set it up.
Then to build libsmb2, run
$ make -f Makefile.platform wiiu_install
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
devkitPro wiiu portlibs folder.
Amiga (AmigaOS)
----------------------
AmigaOS is Operating system which the main processor is a Microprocessor PowerPC.
There are 3 versions:
AmigaOS4(Makefile.AMIGA)
AmigaOS3(Makefile.AMIGA_OS3)
AmigaAROS(Makefile.AMIGA_AROS)
To compile libsmb2 for the AmigaOS, you need to set newlib.library V53.40 or newer (or V53.30 as included in 4.1 FE) and
filesysbox.library 54.4 or newer to set it up.
Then to build libsmb2, choose the makefile acording your AmigaOS system and hit
$ cd lib
$ make -f Makefile.YOUR_AMIGA_OS_USED clean install
The process will copy the resulting libsmb2.a and the include/smb2 headers in the bin folder inside of the lib folder
NOTE: Amiga AROS is a Open Source version of AmigaOS, So do not build this version unless you are using the AmigaAROS.
Dreamcast (KallistiOS)
----------------------
Hitachi SH4 in little-endian mode is the main CPU for the Dreamcast.
To compile libsmb2 for the Dreamcast, first install the KOS toolchain and
and set it up.
Then to build libsmb2, run
$ make -f Makefile.platform clean dc_install
The process will copy the resulting libsmb2.a and the include/smb2 headers to your
KallistiOS toolchain install location addons folder.
NOTE: There is not yet a kos-ports entry for libsmb2 but once a versioned release
that includes Dreamcast support is created installing from kos-ports will become
the preferred method of installation.
Xbox (Xbox XDK)
----------------------
Xbox CPU is a custom Intel Pentium III Coppermine-based processor which only supports litlle endian values.
To compile libsmb2 for the Xbox, first install the RXDK from the Team Resurgent.
Then to build libsmb2, go to Xbox folder
and open the provided .sln file, Then hit the green button to build:
The process will result a libsmb2.lib. So you can copy the include files
and the .lib file to your Xbox project.
Xbox 360 (Xbox 360 SDK)
----------------------
Xbox 360 CPU is a PPC(PowerPC) Xenon which only supports only big endian values.
To compile libsmb2 for the Xbox 360, first install the Xbox 360 SDK(with all features), Microsoft Visual C++ 2010 Ultimate and Windows XP(Recommended) or Windows 7.
Then to build libsmb2, go to Xbox 360 folder
and open the provided .sln file, Then hit the green button to build:
The process will result a libsmb2.lib. So you can copy the include files
and the .lib file to your Xbox 360 project.
NOTE: Both ports was based on XBMC-360 port by BDC(Brent De Cartet) and now being updated to libsmb2 standards to best performance.
+32
View File
@@ -0,0 +1,32 @@
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsmb2", "libsmb2.vcxproj", "{B2F1D3EE-7D56-4862-9286-5937E1A554A4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
CodeAnalysis|Xbox 360 = CodeAnalysis|Xbox 360
Debug|Xbox 360 = Debug|Xbox 360
Profile_FastCap|Xbox 360 = Profile_FastCap|Xbox 360
Profile|Xbox 360 = Profile|Xbox 360
Release_LTCG|Xbox 360 = Release_LTCG|Xbox 360
Release|Xbox 360 = Release|Xbox 360
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.CodeAnalysis|Xbox 360.ActiveCfg = CodeAnalysis|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.CodeAnalysis|Xbox 360.Build.0 = CodeAnalysis|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Debug|Xbox 360.ActiveCfg = Debug|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Debug|Xbox 360.Build.0 = Debug|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Profile_FastCap|Xbox 360.ActiveCfg = Profile_FastCap|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Profile_FastCap|Xbox 360.Build.0 = Profile_FastCap|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Profile|Xbox 360.ActiveCfg = Profile|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Profile|Xbox 360.Build.0 = Profile|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Release_LTCG|Xbox 360.ActiveCfg = Release_LTCG|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Release_LTCG|Xbox 360.Build.0 = Release_LTCG|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Release|Xbox 360.ActiveCfg = Release|Xbox 360
{B2F1D3EE-7D56-4862-9286-5937E1A554A4}.Release|Xbox 360.Build.0 = Release|Xbox 360
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
+327
View File
@@ -0,0 +1,327 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="CodeAnalysis|Xbox 360">
<Configuration>CodeAnalysis</Configuration>
<Platform>Xbox 360</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Xbox 360">
<Configuration>Debug</Configuration>
<Platform>Xbox 360</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Profile|Xbox 360">
<Configuration>Profile</Configuration>
<Platform>Xbox 360</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Profile_FastCap|Xbox 360">
<Configuration>Profile_FastCap</Configuration>
<Platform>Xbox 360</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Xbox 360">
<Configuration>Release</Configuration>
<Platform>Xbox 360</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release_LTCG|Xbox 360">
<Configuration>Release_LTCG</Configuration>
<Platform>Xbox 360</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B2F1D3EE-7D56-4862-9286-5937E1A554A4}</ProjectGuid>
<Keyword>Xbox360Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='CodeAnalysis|Xbox 360'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Xbox 360'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Xbox 360'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile_FastCap|Xbox 360'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_LTCG|Xbox 360'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='CodeAnalysis|Xbox 360'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Xbox 360'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Profile|Xbox 360'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Profile_FastCap|Xbox 360'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release_LTCG|Xbox 360'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Xbox 360'">
<OutputFile>$(OutDir)$(ProjectName).lib</OutputFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='CodeAnalysis|Xbox 360'">
<OutputFile>$(OutDir)$(ProjectName).lib</OutputFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Xbox 360'">
<OutputFile>$(OutDir)$(ProjectName).lib</OutputFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile_FastCap|Xbox 360'">
<OutputFile>$(OutDir)$(ProjectName).lib</OutputFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">
<OutputFile>$(OutDir)$(ProjectName).lib</OutputFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_LTCG|Xbox 360'">
<OutputFile>$(OutDir)$(ProjectName).lib</OutputFile>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Xbox 360'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
<ExceptionHandling>false</ExceptionHandling>
<MinimalRebuild>true</MinimalRebuild>
<BufferSecurityCheck>false</BufferSecurityCheck>
<PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_DEBUG;_XBOX;_LIB;NEED_POLL;NEED_GETPID;NEED_GETLOGIN_R;NEED_RANDOM;NEED_SRANDOM;NEED_GETADDRINFO;NEED_FREEADDRINFO;XBOX_360_PLATFORM;HAVE_CONFIG_H;_U_=/**/</PreprocessorDefinitions>
<CallAttributedProfiling>Callcap</CallAttributedProfiling>
<CompileAs>CompileAsC</CompileAs>
<AdditionalIncludeDirectories>..\include\;..\include\smb2;..\include\xbox 360</AdditionalIncludeDirectories>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='CodeAnalysis|Xbox 360'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
<ExceptionHandling>false</ExceptionHandling>
<MinimalRebuild>true</MinimalRebuild>
<PREfast>AnalyzeOnly</PREfast>
<BufferSecurityCheck>false</BufferSecurityCheck>
<PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_DEBUG;_XBOX;_LIB</PreprocessorDefinitions>
<CallAttributedProfiling>Callcap</CallAttributedProfiling>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Xbox 360'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<ExceptionHandling>false</ExceptionHandling>
<StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<BufferSecurityCheck>false</BufferSecurityCheck>
<PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>NDEBUG;_XBOX;PROFILE;_LIB</PreprocessorDefinitions>
<CallAttributedProfiling>Callcap</CallAttributedProfiling>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<IgnoreSpecificDefaultLibraries>xapilib.lib</IgnoreSpecificDefaultLibraries>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile_FastCap|Xbox 360'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<ExceptionHandling>false</ExceptionHandling>
<StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallAttributedProfiling>Fastcap</CallAttributedProfiling>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<BufferSecurityCheck>false</BufferSecurityCheck>
<PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>NDEBUG;_XBOX;PROFILE;FASTCAP;_LIB</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<ExceptionHandling>false</ExceptionHandling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>NDEBUG;_XBOX;_LIB;NEED_POLL;NEED_GETPID;NEED_GETLOGIN_R;NEED_RANDOM;NEED_SRANDOM;NEED_GETADDRINFO;NEED_FREEADDRINFO;XBOX_360_PLATFORM;HAVE_CONFIG_H;_U_=/**/</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\include\;..\include\smb2;..\include\xbox 360</AdditionalIncludeDirectories>
<CompileAs>CompileAsC</CompileAs>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_LTCG|Xbox 360'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<StringPooling>true</StringPooling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<ExceptionHandling>false</ExceptionHandling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>NDEBUG;_XBOX;LTCG;_LIB;NEED_POLL;NEED_GETPID;NEED_GETLOGIN_R;NEED_RANDOM;NEED_SRANDOM;NEED_GETADDRINFO;NEED_FREEADDRINFO;XBOX_360_PLATFORM;HAVE_CONFIG_H;_U_=/**/</PreprocessorDefinitions>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>..\include\;..\include\smb2;..\include\xbox 360</AdditionalIncludeDirectories>
<CompileAs>CompileAsC</CompileAs>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<None Include="..\COPYING" />
<None Include="..\include\Makefile.am" />
<None Include="..\lib\CMakeLists.txt" />
<None Include="..\lib\libsmb2.syms" />
<None Include="..\lib\Makefile.am" />
<None Include="..\lib\Makefile.AMIGA" />
<None Include="..\lib\Makefile.AMIGA_AROS" />
<None Include="..\lib\Makefile.AMIGA_OS3" />
<None Include="..\lib\Makefile.PS3_PPU" />
<None Include="..\README" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\asprintf.h" />
<ClInclude Include="..\include\libsmb2-private.h" />
<ClInclude Include="..\include\portable-endian.h" />
<ClInclude Include="..\include\slist.h" />
<ClInclude Include="..\include\smb2\libsmb2-dcerpc-lsa.h" />
<ClInclude Include="..\include\smb2\libsmb2-dcerpc-srvsvc.h" />
<ClInclude Include="..\include\smb2\libsmb2-dcerpc.h" />
<ClInclude Include="..\include\smb2\libsmb2-raw.h" />
<ClInclude Include="..\include\smb2\libsmb2.h" />
<ClInclude Include="..\include\smb2\smb2-errors.h" />
<ClInclude Include="..\include\smb2\smb2.h" />
<ClInclude Include="..\include\xbox 360\config.h" />
<ClInclude Include="..\lib\aes.h" />
<ClInclude Include="..\lib\aes128ccm.h" />
<ClInclude Include="..\lib\asn1-ber.h" />
<ClInclude Include="..\lib\compat.h" />
<ClInclude Include="..\lib\hmac-md5.h" />
<ClInclude Include="..\lib\md4.h" />
<ClInclude Include="..\lib\md5.h" />
<ClInclude Include="..\lib\ntlmssp.h" />
<ClInclude Include="..\lib\sha-private.h" />
<ClInclude Include="..\lib\sha.h" />
<ClInclude Include="..\lib\smb2-signing.h" />
<ClInclude Include="..\lib\smb3-seal.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\lib\aes.c" />
<ClCompile Include="..\lib\aes128ccm.c" />
<ClCompile Include="..\lib\alloc.c" />
<ClCompile Include="..\lib\asn1-ber.c" />
<ClCompile Include="..\lib\compat.c" />
<ClCompile Include="..\lib\dcerpc-lsa.c" />
<ClCompile Include="..\lib\dcerpc-srvsvc.c" />
<ClCompile Include="..\lib\errors.c" />
<ClCompile Include="..\lib\hmac-md5.c" />
<ClCompile Include="..\lib\hmac.c" />
<ClCompile Include="..\lib\init.c" />
<ClCompile Include="..\lib\libsmb2.c" />
<ClCompile Include="..\lib\md4c.c" />
<ClCompile Include="..\lib\md5.c" />
<ClCompile Include="..\lib\ntlmssp.c" />
<ClCompile Include="..\lib\pdu.c" />
<ClCompile Include="..\lib\sha1.c" />
<ClCompile Include="..\lib\sha224-256.c" />
<ClCompile Include="..\lib\sha384-512.c" />
<ClCompile Include="..\lib\smb2-cmd-close.c" />
<ClCompile Include="..\lib\smb2-cmd-create.c" />
<ClCompile Include="..\lib\smb2-cmd-echo.c" />
<ClCompile Include="..\lib\smb2-cmd-error.c" />
<ClCompile Include="..\lib\smb2-cmd-flush.c" />
<ClCompile Include="..\lib\smb2-cmd-ioctl.c" />
<ClCompile Include="..\lib\smb2-cmd-lock.c" />
<ClCompile Include="..\lib\smb2-cmd-logoff.c" />
<ClCompile Include="..\lib\smb2-cmd-negotiate.c" />
<ClCompile Include="..\lib\smb2-cmd-notify-change.c" />
<ClCompile Include="..\lib\smb2-cmd-query-directory.c" />
<ClCompile Include="..\lib\smb2-cmd-query-info.c" />
<ClCompile Include="..\lib\smb2-cmd-read.c" />
<ClCompile Include="..\lib\smb2-cmd-session-setup.c" />
<ClCompile Include="..\lib\smb2-cmd-set-info.c" />
<ClCompile Include="..\lib\smb2-cmd-tree-connect.c" />
<ClCompile Include="..\lib\smb2-cmd-tree-disconnect.c" />
<ClCompile Include="..\lib\smb2-cmd-write.c" />
<ClCompile Include="..\lib\smb2-data-file-info.c" />
<ClCompile Include="..\lib\smb2-data-filesystem-info.c" />
<ClCompile Include="..\lib\smb2-data-reparse-point.c" />
<ClCompile Include="..\lib\smb2-data-security-descriptor.c" />
<ClCompile Include="..\lib\smb2-share-enum.c" />
<ClCompile Include="..\lib\smb2-signing.c" />
<ClCompile Include="..\lib\smb3-seal.c" />
<ClCompile Include="..\lib\socket.c" />
<ClCompile Include="..\lib\sync.c" />
<ClCompile Include="..\lib\timestamps.c" />
<ClCompile Include="..\lib\unicode.c" />
<ClCompile Include="..\lib\usha.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+268
View File
@@ -0,0 +1,268 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="..\include\Makefile.am">
<Filter>include</Filter>
</None>
<None Include="..\lib\CMakeLists.txt">
<Filter>lib</Filter>
</None>
<None Include="..\COPYING" />
<None Include="..\README" />
<None Include="..\lib\libsmb2.syms">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.am">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.AMIGA">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.AMIGA_AROS">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.AMIGA_OS3">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.PS3_PPU">
<Filter>lib</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Filter Include="include">
<UniqueIdentifier>{028f1ea9-92cf-4908-a035-80459a8cf956}</UniqueIdentifier>
</Filter>
<Filter Include="include\smb2">
<UniqueIdentifier>{28810bac-fb60-4810-8ce9-1d5c989d4c75}</UniqueIdentifier>
</Filter>
<Filter Include="lib">
<UniqueIdentifier>{8f3e89d9-a92f-4650-a970-3af5fddd92fe}</UniqueIdentifier>
</Filter>
<Filter Include="include\xbox 360">
<UniqueIdentifier>{6c01c6cd-316c-4bff-82ea-f611e60057e9}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\smb2\libsmb2.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2-dcerpc.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2-dcerpc-lsa.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2-dcerpc-srvsvc.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2-raw.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\smb2.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\smb2-errors.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\libsmb2-private.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\portable-endian.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\slist.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\lib\aes.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\aes128ccm.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\compat.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\hmac-md5.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\md4.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\md5.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\ntlmssp.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\sha.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\sha-private.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\smb2-signing.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\smb3-seal.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\include\asprintf.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\xbox 360\config.h">
<Filter>include\xbox 360</Filter>
</ClInclude>
<ClInclude Include="..\lib\asn1-ber.h">
<Filter>lib</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\lib\aes.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\aes128ccm.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\alloc.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\compat.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\dcerpc-lsa.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\dcerpc-srvsvc.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\errors.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\hmac.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\hmac-md5.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\init.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\libsmb2.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\md4c.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\md5.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\ntlmssp.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\pdu.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\sha224-256.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\sha384-512.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-close.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-create.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-echo.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-error.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-flush.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-ioctl.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-logoff.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-negotiate.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-query-directory.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-query-info.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-read.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-session-setup.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-set-info.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-tree-connect.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-tree-disconnect.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-write.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-data-file-info.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-data-filesystem-info.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-data-reparse-point.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-data-security-descriptor.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-share-enum.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-signing.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb3-seal.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\socket.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\sync.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\timestamps.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\unicode.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\usha.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\asn1-ber.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\sha1.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-lock.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-notify-change.c">
<Filter>lib</Filter>
</ClCompile>
</ItemGroup>
</Project>
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36623.8 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsmb2", "libsmb2.vcxproj", "{EF73A5DF-B372-4E1C-BCB4-3DA1A4002BDD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EF73A5DF-B372-4E1C-BCB4-3DA1A4002BDD}.Debug|x86.ActiveCfg = Debug|Win32
{EF73A5DF-B372-4E1C-BCB4-3DA1A4002BDD}.Debug|x86.Build.0 = Debug|Win32
{EF73A5DF-B372-4E1C-BCB4-3DA1A4002BDD}.Release|x86.ActiveCfg = Release|Win32
{EF73A5DF-B372-4E1C-BCB4-3DA1A4002BDD}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9B80C829-742C-4582-992A-ABCEAB26925E}
EndGlobalSection
EndGlobal
+248
View File
@@ -0,0 +1,248 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\asprintf.h" />
<ClInclude Include="..\include\libsmb2-private.h" />
<ClInclude Include="..\include\portable-endian.h" />
<ClInclude Include="..\include\slist.h" />
<ClInclude Include="..\include\smb2\libsmb2-dcerpc-lsa.h" />
<ClInclude Include="..\include\smb2\libsmb2-dcerpc-srvsvc.h" />
<ClInclude Include="..\include\smb2\libsmb2-dcerpc.h" />
<ClInclude Include="..\include\smb2\libsmb2-raw.h" />
<ClInclude Include="..\include\smb2\libsmb2.h" />
<ClInclude Include="..\include\smb2\smb2-errors.h" />
<ClInclude Include="..\include\smb2\smb2-ioctl.h" />
<ClInclude Include="..\include\smb2\smb2.h" />
<ClInclude Include="..\include\xbox\config.h" />
<ClInclude Include="..\lib\aes.h" />
<ClInclude Include="..\lib\aes128ccm.h" />
<ClInclude Include="..\lib\aes_apple.h" />
<ClInclude Include="..\lib\aes_reference.h" />
<ClInclude Include="..\lib\asn1-ber.h" />
<ClInclude Include="..\lib\compat.h" />
<ClInclude Include="..\lib\hmac-md5.h" />
<ClInclude Include="..\lib\krb5-wrapper.h" />
<ClInclude Include="..\lib\md4.h" />
<ClInclude Include="..\lib\md5.h" />
<ClInclude Include="..\lib\ntlmssp.h" />
<ClInclude Include="..\lib\sha-private.h" />
<ClInclude Include="..\lib\sha.h" />
<ClInclude Include="..\lib\smb2-signing.h" />
<ClInclude Include="..\lib\smb3-seal.h" />
<ClInclude Include="..\lib\spnego-wrapper.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\include\Makefile.am" />
<None Include="..\include\module.modulemap" />
<None Include="..\lib\libsmb2.syms" />
<None Include="..\lib\Makefile.am" />
<None Include="..\lib\Makefile.AMIGA" />
<None Include="..\lib\Makefile.AMIGA_AROS" />
<None Include="..\lib\Makefile.AMIGA_OS3" />
<None Include="..\lib\Makefile.PS3_PPU" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\lib\aes.c" />
<ClCompile Include="..\lib\aes128ccm.c" />
<ClCompile Include="..\lib\aes_apple.c" />
<ClCompile Include="..\lib\aes_reference.c" />
<ClCompile Include="..\lib\alloc.c" />
<ClCompile Include="..\lib\asn1-ber.c" />
<ClCompile Include="..\lib\compat.c" />
<ClCompile Include="..\lib\dcerpc-lsa.c" />
<ClCompile Include="..\lib\dcerpc-srvsvc.c" />
<ClCompile Include="..\lib\dcerpc.c" />
<ClCompile Include="..\lib\errors.c" />
<ClCompile Include="..\lib\hmac-md5.c" />
<ClCompile Include="..\lib\hmac.c" />
<ClCompile Include="..\lib\init.c" />
<ClCompile Include="..\lib\krb5-wrapper.c" />
<ClCompile Include="..\lib\libsmb2.c" />
<ClCompile Include="..\lib\md4c.c" />
<ClCompile Include="..\lib\md5.c" />
<ClCompile Include="..\lib\ntlmssp.c" />
<ClCompile Include="..\lib\pdu.c" />
<ClCompile Include="..\lib\sha1.c" />
<ClCompile Include="..\lib\sha224-256.c" />
<ClCompile Include="..\lib\sha384-512.c" />
<ClCompile Include="..\lib\smb2-cmd-close.c" />
<ClCompile Include="..\lib\smb2-cmd-create.c" />
<ClCompile Include="..\lib\smb2-cmd-echo.c" />
<ClCompile Include="..\lib\smb2-cmd-error.c" />
<ClCompile Include="..\lib\smb2-cmd-flush.c" />
<ClCompile Include="..\lib\smb2-cmd-ioctl.c" />
<ClCompile Include="..\lib\smb2-cmd-lock.c" />
<ClCompile Include="..\lib\smb2-cmd-logoff.c" />
<ClCompile Include="..\lib\smb2-cmd-negotiate.c" />
<ClCompile Include="..\lib\smb2-cmd-notify-change.c" />
<ClCompile Include="..\lib\smb2-cmd-oplock-break.c" />
<ClCompile Include="..\lib\smb2-cmd-query-directory.c" />
<ClCompile Include="..\lib\smb2-cmd-query-info.c" />
<ClCompile Include="..\lib\smb2-cmd-read.c" />
<ClCompile Include="..\lib\smb2-cmd-session-setup.c" />
<ClCompile Include="..\lib\smb2-cmd-set-info.c" />
<ClCompile Include="..\lib\smb2-cmd-tree-connect.c" />
<ClCompile Include="..\lib\smb2-cmd-tree-disconnect.c" />
<ClCompile Include="..\lib\smb2-cmd-write.c" />
<ClCompile Include="..\lib\smb2-data-file-info.c" />
<ClCompile Include="..\lib\smb2-data-filesystem-info.c" />
<ClCompile Include="..\lib\smb2-data-reparse-point.c" />
<ClCompile Include="..\lib\smb2-data-security-descriptor.c" />
<ClCompile Include="..\lib\smb2-share-enum.c" />
<ClCompile Include="..\lib\smb2-signing.c" />
<ClCompile Include="..\lib\smb3-seal.c" />
<ClCompile Include="..\lib\socket.c" />
<ClCompile Include="..\lib\spnego-wrapper.c" />
<ClCompile Include="..\lib\sync.c" />
<ClCompile Include="..\lib\timestamps.c" />
<ClCompile Include="..\lib\unicode.c" />
<ClCompile Include="..\lib\usha.c" />
</ItemGroup>
<ItemGroup>
<Text Include="..\lib\CMakeLists.txt" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{EF73A5DF-B372-4E1C-BCB4-3DA1A4002BDD}</ProjectGuid>
<RootNamespace>Triangle</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>libsmb2</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(VC_IncludePath);$(RXDK_LIBS)include</IncludePath>
<ExternalIncludePath>$(RXDK_LIBS)include</ExternalIncludePath>
<LibraryWPath />
<SourcePath />
<ExcludePath>$(CommonExcludePath);$(VC_ExecutablePath_x86);$(RXDK_LIBS)lib</ExcludePath>
<ReferencePath>$(RXDK_LIBS)lib</ReferencePath>
<LibraryPath>$(RXDK_LIBS)lib</LibraryPath>
<OutDir>$(ProjectDir)$(Configuration)\</OutDir>
<TargetExt>.lib</TargetExt>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>$(VC_IncludePath);$(RXDK_LIBS)include</IncludePath>
<ExternalIncludePath>$(RXDK_LIBS)include</ExternalIncludePath>
<LibraryWPath />
<SourcePath />
<ExcludePath>$(CommonExcludePath);$(VC_ExecutablePath_x86);$(RXDK_LIBS)lib</ExcludePath>
<ReferencePath>$(RXDK_LIBS)lib</ReferencePath>
<LibraryPath>$(RXDK_LIBS)lib</LibraryPath>
<OutDir>$(ProjectDir)$(Configuration)\</OutDir>
<TargetExt>.lib</TargetExt>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>XBOX;_XBOX;_DEBUG;XBOX_PLATFORM;NEED_POLL;NEED_GETADDRINFO;NEED_FREEADDRINFO;NEED_GETPID;NEED_GETLOGIN_R;NEED_RANDOM;NEED_SRANDOM;HAVE_CONFIG_H;_U_=/**/;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalIncludeDirectories>..\include\;..\include\xbox;..\include\smb2</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/arch:SSE %(AdditionalOptions)</AdditionalOptions>
<SupportJustMyCode>false</SupportJustMyCode>
<ControlFlowGuard>false</ControlFlowGuard>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>xboxkrnl.lib;libcmtd.lib;xapilibd.lib;d3d8d.lib;d3dx8d.lib;xbdm.lib</AdditionalDependencies>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<BaseAddress>
</BaseAddress>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
<GenerateMapFile>true</GenerateMapFile>
<FixedBaseAddress>false</FixedBaseAddress>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<SectionAlignment>32</SectionAlignment>
<MergeSections>.rdata=.text</MergeSections>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>XBOX;_XBOX;NDEBUG;XBOX_PLATFORM;NEED_POLL;NEED_GETADDRINFO;NEED_FREEADDRINFO;NEED_GETPID;NEED_GETLOGIN_R;NEED_RANDOM;NEED_SRANDOM;HAVE_CONFIG_H;_U_=/**/%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalIncludeDirectories>..\include\;..\include\xbox;..\include\smb2</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/arch:SSE %(AdditionalOptions)</AdditionalOptions>
<ControlFlowGuard>false</ControlFlowGuard>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>xboxkrnl.lib;libcmt.lib;xapilib.lib;d3d8.lib;d3dx8.lib;xbdm.lib</AdditionalDependencies>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<BaseAddress>
</BaseAddress>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
<GenerateMapFile>true</GenerateMapFile>
<FixedBaseAddress>false</FixedBaseAddress>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<SectionAlignment>32</SectionAlignment>
<MergeSections>.rdata=.text</MergeSections>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project requires Team Resurgent's XDK Libs installed on this computer. Search for them on http://www.theusualplaces.co.uk.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists($(RXDK_LIBS))" Text="$([System.String]::Format('$(ErrorText)'))" />
</Target>
</Project>
+304
View File
@@ -0,0 +1,304 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="include">
<UniqueIdentifier>{14ea9c37-9c42-430e-9bb6-08ab8684bb7c}</UniqueIdentifier>
</Filter>
<Filter Include="include\xbox">
<UniqueIdentifier>{02dca661-43e1-4614-89a1-1dcf99ddb204}</UniqueIdentifier>
</Filter>
<Filter Include="include\smb2">
<UniqueIdentifier>{dc99cb31-fef5-40c5-b2f1-1eb6df86f297}</UniqueIdentifier>
</Filter>
<Filter Include="lib">
<UniqueIdentifier>{7a3b701b-58d5-43d1-b8c0-41218fb3f08d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\xbox\config.h">
<Filter>include\xbox</Filter>
</ClInclude>
<ClInclude Include="..\include\asprintf.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\libsmb2-private.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\portable-endian.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\slist.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2-dcerpc.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2-dcerpc-lsa.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2-dcerpc-srvsvc.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\libsmb2-raw.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\smb2.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\smb2-errors.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\include\smb2\smb2-ioctl.h">
<Filter>include\smb2</Filter>
</ClInclude>
<ClInclude Include="..\lib\aes.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\aes_apple.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\aes_reference.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\aes128ccm.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\asn1-ber.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\compat.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\hmac-md5.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\krb5-wrapper.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\md4.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\md5.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\ntlmssp.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\sha.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\sha-private.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\smb2-signing.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\smb3-seal.h">
<Filter>lib</Filter>
</ClInclude>
<ClInclude Include="..\lib\spnego-wrapper.h">
<Filter>lib</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\include\Makefile.am">
<Filter>include</Filter>
</None>
<None Include="..\include\module.modulemap">
<Filter>include</Filter>
</None>
<None Include="..\lib\libsmb2.syms">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.am">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.AMIGA">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.AMIGA_AROS">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.AMIGA_OS3">
<Filter>lib</Filter>
</None>
<None Include="..\lib\Makefile.PS3_PPU">
<Filter>lib</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\lib\aes.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\aes_apple.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\aes_reference.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\aes128ccm.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\alloc.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\asn1-ber.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\compat.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\dcerpc.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\dcerpc-lsa.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\dcerpc-srvsvc.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\errors.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\hmac.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\hmac-md5.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\init.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\krb5-wrapper.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\libsmb2.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\md4c.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\md5.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\ntlmssp.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\pdu.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\sha1.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\sha224-256.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\sha384-512.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-close.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-create.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-echo.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-error.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-flush.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-ioctl.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-lock.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-logoff.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-negotiate.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-notify-change.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-oplock-break.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-query-directory.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-query-info.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-read.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-session-setup.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-set-info.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-tree-connect.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-tree-disconnect.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-cmd-write.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-data-file-info.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-data-filesystem-info.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-data-reparse-point.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-data-security-descriptor.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-share-enum.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb2-signing.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\smb3-seal.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\socket.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\spnego-wrapper.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\sync.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\timestamps.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\unicode.c">
<Filter>lib</Filter>
</ClCompile>
<ClCompile Include="..\lib\usha.c">
<Filter>lib</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="..\lib\CMakeLists.txt">
<Filter>lib</Filter>
</Text>
</ItemGroup>
</Project>
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh
autoreconf -vif

Some files were not shown because too many files have changed in this diff Show More