From 00a75bd80b7ce7a26b9f09b642fb812ade36a654 Mon Sep 17 00:00:00 2001 From: sorgelig Date: Fri, 22 Mar 2019 20:38:14 +0800 Subject: [PATCH] Fix EOL in sources. --- DiskImage.cpp | 1246 ++++++------ DiskImage.h | 384 ++-- battery.cpp | 14 +- bootcore.cpp | 462 ++--- file_io.cpp | 2506 ++++++++++++------------- file_io.h | 176 +- fpga_io.cpp | 1306 ++++++------- fpga_io.h | 74 +- hardware.cpp | 154 +- hardware.h | 38 +- input.cpp | 4988 ++++++++++++++++++++++++------------------------- input.h | 40 +- logo.h | 190 +- main.cpp | 138 +- menu.cpp | 16 +- menu.h | 20 +- osd.cpp | 1382 +++++++------- osd.h | 6 +- spi.cpp | 662 +++---- spi.h | 122 +- sxmlc.c | 4582 ++++++++++++++++++++++----------------------- sxmlc.h | 1658 ++++++++-------- tzx2wav.cpp | 2790 +++++++++++++-------------- tzx2wav.h | 6 +- user_io.cpp | 4 +- user_io.h | 50 +- 26 files changed, 11507 insertions(+), 11507 deletions(-) diff --git a/DiskImage.cpp b/DiskImage.cpp index 08470b2..35caab5 100644 --- a/DiskImage.cpp +++ b/DiskImage.cpp @@ -27,602 +27,602 @@ char errsect[] = "ERROR: THIS SECTOR NOT FOUND OR IN NON TR-DOS FORMAT!"; -static const unsigned char sbootimage[] = { - 0x00, 0x01, 0x1a, 0x00, 0xf9, 0xc0, 0xb0, 0x22, 0x31, 0x35, 0x36, 0x31, 0x39, 0x22, 0x3a, 0xea, - 0x3a, 0xf7, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x0d, 0x00, 0x02, - 0x0f, 0x00, 0xf9, 0xc0, 0xb0, 0x22, 0x31, 0x35, 0x36, 0x31, 0x39, 0x22, 0x3a, 0xea, 0x3a, 0xf7, - 0x0d, 0x00, 0x03, 0x0b, 0x00, 0xf9, 0xc0, 0xb0, 0x22, 0x31, 0x35, 0x36, 0x31, 0x36, 0x22, 0x0d, - 0x00, 0x04, 0x47, 0x00, 0xea, 0x21, 0xa8, 0x61, 0x11, 0xa9, 0x61, 0x01, 0x07, 0x9d, 0x36, 0x00, - 0xed, 0xb0, 0x21, 0xa8, 0x61, 0xed, 0x5b, 0xf4, 0x5c, 0x01, 0x05, 0x0f, 0xcd, 0x13, 0x3d, 0x21, - 0x00, 0xa6, 0xed, 0x5b, 0xf4, 0x5c, 0x01, 0x05, 0x08, 0xcd, 0x13, 0x3d, 0x21, 0x50, 0xeb, 0xed, - 0x5b, 0xf4, 0x5c, 0x01, 0x05, 0x0d, 0xcd, 0x13, 0x3d, 0xcd, 0xa8, 0x61, 0x21, 0xa8, 0x61, 0x11, - 0xa9, 0x61, 0x01, 0x07, 0x9d, 0x36, 0x00, 0xed, 0xb0, 0xc9, 0x0d, 0x00, 0x0a, 0x25, 0x00, 0xe7, - 0xc3, 0xa7, 0x3a, 0xda, 0xc3, 0xa7, 0x3a, 0xd9, 0xb0, 0x22, 0x37, 0x22, 0x3a, 0xfd, 0xb0, 0x22, - 0x36, 0x35, 0x33, 0x34, 0x37, 0x22, 0x3a, 0xf9, 0xc0, 0xb0, 0x22, 0x32, 0x33, 0x39, 0x33, 0x36, - 0x22, 0x3a, 0xf7, 0x0d, 0x80, 0xaa, 0x0a, 0x00, 0x6f, 0x6f, 0x74, 0x31, 0x31, 0x22, 0xca, 0x31, - 0x30, 0x0d, 0x80, 0x31, 0x35, 0x36, 0x31, 0x36, 0x0e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x0d, 0x80, - 0xca, 0xf3, 0x5e, 0x06, 0x00, 0x00, 0x00, 0xf3, 0x5e, 0x06, 0x00, 0x00, 0x00, 0x0a, 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, - 0xfd, 0x22, 0xc8, 0xaf, 0xd9, 0x22, 0xca, 0xaf, 0xed, 0x73, 0xcc, 0xaf, 0xed, 0x57, 0x32, 0xce, - 0xaf, 0x11, 0x00, 0x00, 0x21, 0x00, 0x9d, 0x01, 0x05, 0x08, 0xcd, 0x13, 0x3d, 0x31, 0x68, 0xbf, - 0xf3, 0x21, 0x00, 0xfd, 0x11, 0x01, 0xfd, 0x01, 0x00, 0x01, 0x36, 0xfc, 0xed, 0xb0, 0x21, 0x5a, - 0x67, 0x22, 0xfd, 0xfc, 0x3e, 0xc3, 0x32, 0xfc, 0xfc, 0xed, 0x5e, 0x3e, 0xfd, 0xed, 0x47, 0xcd, - 0x53, 0xeb, 0xcd, 0x50, 0xeb, 0xcd, 0x13, 0x65, 0xfb, 0x21, 0x9b, 0x6e, 0x11, 0x00, 0x77, 0x01, - 0xc8, 0x00, 0xed, 0xb0, 0x21, 0x77, 0x6b, 0x11, 0x00, 0x76, 0x0e, 0x28, 0xed, 0xb0, 0x21, 0x9f, - 0x6b, 0x11, 0x5c, 0x76, 0x0e, 0x09, 0xed, 0xb0, 0x11, 0x00, 0x78, 0x3e, 0x40, 0x32, 0x8e, 0x76, - 0xcd, 0xf4, 0x62, 0x21, 0x00, 0x78, 0x11, 0x00, 0x81, 0x06, 0x08, 0xc5, 0xcd, 0xd9, 0x63, 0x24, - 0x14, 0xc1, 0x10, 0xf7, 0x21, 0x00, 0x77, 0xcd, 0x25, 0x64, 0xcd, 0x25, 0x64, 0xcd, 0x25, 0x64, - 0x11, 0x00, 0x8a, 0x3e, 0x80, 0x32, 0x8e, 0x76, 0xcd, 0xf4, 0x62, 0x21, 0x00, 0x8a, 0x11, 0x00, - 0x93, 0x06, 0x08, 0xc5, 0xcd, 0xd9, 0x63, 0x24, 0x14, 0xc1, 0x10, 0xf7, 0x06, 0x11, 0x21, 0x00, - 0x8a, 0xc5, 0xcd, 0x25, 0x64, 0x24, 0xc1, 0x10, 0xf8, 0xcd, 0xc9, 0x6d, 0x3e, 0xff, 0x32, 0xb0, - 0xb3, 0x3c, 0x32, 0xb1, 0xb3, 0x3e, 0x05, 0x32, 0xb2, 0xb3, 0x3e, 0x08, 0x32, 0xb3, 0xb3, 0x3e, - 0x0e, 0x32, 0xb4, 0xb3, 0xcd, 0x61, 0x64, 0x3a, 0xb0, 0xb3, 0x3c, 0x32, 0xb0, 0xb3, 0x3a, 0xb0, - 0xb3, 0xe6, 0x01, 0xfe, 0x01, 0x20, 0x18, 0x21, 0x84, 0xac, 0x22, 0x87, 0x64, 0xcd, 0x85, 0x64, - 0xcd, 0x85, 0x64, 0x21, 0xfc, 0xac, 0x22, 0x87, 0x64, 0xcd, 0x85, 0x64, 0xcd, 0x85, 0x64, 0x3a, - 0xb0, 0xb3, 0xe6, 0x03, 0xfe, 0x03, 0x20, 0x21, 0x21, 0x00, 0x40, 0x11, 0x00, 0x78, 0x01, 0xb1, - 0xb3, 0xcd, 0xdd, 0x64, 0x21, 0x1b, 0x40, 0x11, 0x00, 0x8a, 0x01, 0xb2, 0xb3, 0xcd, 0xdd, 0x64, - 0x21, 0x74, 0xad, 0x22, 0x87, 0x64, 0xcd, 0x85, 0x64, 0x3a, 0xb0, 0xb3, 0xe6, 0x07, 0xfe, 0x07, - 0x20, 0xa5, 0x21, 0x60, 0x50, 0x11, 0x00, 0x8a, 0x01, 0xb3, 0xb3, 0xcd, 0xdd, 0x64, 0x21, 0x7b, - 0x50, 0x11, 0x00, 0x78, 0x01, 0xb4, 0xb3, 0xcd, 0xdd, 0x64, 0x18, 0x8b, 0xfd, 0x21, 0x5c, 0x76, - 0x0e, 0x08, 0x21, 0x00, 0x77, 0x06, 0x28, 0xdd, 0x21, 0x00, 0x76, 0xcd, 0x3e, 0x63, 0xc5, 0x06, - 0x05, 0xdd, 0x7e, 0x00, 0xe6, 0x3f, 0xcd, 0x57, 0x63, 0xdd, 0x2c, 0x10, 0xf4, 0x0d, 0x20, 0xef, - 0xc1, 0xcd, 0x67, 0x63, 0x10, 0xe1, 0x14, 0x1e, 0x00, 0x0d, 0x20, 0xd6, 0xeb, 0x11, 0x00, 0x76, - 0x3a, 0x8e, 0x76, 0x47, 0x2c, 0x2c, 0x1a, 0xb8, 0x38, 0x02, 0x36, 0x18, 0x2c, 0x2c, 0x2c, 0x1c, - 0x7d, 0xfe, 0xc8, 0x20, 0xef, 0xc9, 0xd9, 0x21, 0x98, 0x76, 0x11, 0x99, 0x76, 0x01, 0x27, 0x00, - 0x70, 0xed, 0xb0, 0xd9, 0x79, 0x32, 0x52, 0x63, 0xfd, 0x7e, 0x00, 0x32, 0xc0, 0x76, 0xc9, 0xdd, - 0xe5, 0xdd, 0x21, 0x98, 0x76, 0x32, 0x62, 0x63, 0xdd, 0x36, 0x00, 0x01, 0xdd, 0xe1, 0xc9, 0xc5, - 0xfd, 0xe5, 0xd5, 0xe5, 0xdd, 0xe1, 0xe5, 0x3a, 0xc0, 0x76, 0xe6, 0xf8, 0x0f, 0x0f, 0x0f, 0x4f, - 0x06, 0x00, 0xeb, 0x09, 0xe5, 0xfd, 0xe1, 0x21, 0x98, 0x76, 0x3a, 0xc0, 0x76, 0xe6, 0x07, 0x4f, - 0x3e, 0x07, 0x91, 0x4f, 0x16, 0x05, 0x1e, 0x7e, 0x7b, 0x32, 0x98, 0x63, 0xaf, 0xdd, 0xcb, 0x00, - 0x46, 0x28, 0x02, 0x3e, 0x40, 0x08, 0x7e, 0xfe, 0x01, 0x20, 0x1b, 0x06, 0x86, 0x08, 0xb0, 0x47, - 0x79, 0x07, 0x07, 0x07, 0xb0, 0x32, 0xb3, 0x63, 0xfd, 0xcb, 0x00, 0x86, 0x0d, 0x79, 0xfe, 0xff, - 0x20, 0x04, 0xfd, 0x2c, 0x0e, 0x07, 0x2c, 0x7b, 0xd6, 0x08, 0x5f, 0xfe, 0x3e, 0x20, 0xc9, 0xdd, - 0x23, 0x15, 0x20, 0xc2, 0xe1, 0xd1, 0x01, 0x05, 0x00, 0x09, 0xeb, 0x09, 0xeb, 0xfd, 0xe1, 0xc1, - 0xc9, 0xe5, 0xdd, 0xe1, 0xd5, 0xfd, 0xe1, 0x06, 0x28, 0xc5, 0xdd, 0x4e, 0x00, 0xcd, 0x1c, 0x64, - 0xdd, 0x7e, 0x04, 0xfd, 0x71, 0x04, 0x4f, 0xcd, 0x1c, 0x64, 0xfd, 0x71, 0x00, 0xdd, 0x4e, 0x01, - 0xcd, 0x1c, 0x64, 0xdd, 0x7e, 0x03, 0xfd, 0x71, 0x03, 0x4f, 0xcd, 0x1c, 0x64, 0xfd, 0x71, 0x01, - 0xdd, 0x4e, 0x02, 0xcd, 0x1c, 0x64, 0xfd, 0x71, 0x02, 0x01, 0x05, 0x00, 0xdd, 0x09, 0xfd, 0x09, - 0xc1, 0x10, 0xc6, 0xc9, 0x06, 0x08, 0xcb, 0x19, 0x17, 0x10, 0xfb, 0x4f, 0xc9, 0xe5, 0x11, 0x00, - 0x9c, 0x06, 0x05, 0xc5, 0xe5, 0x06, 0x08, 0xc5, 0xe5, 0x06, 0x28, 0xc5, 0xcb, 0x16, 0xd5, 0x06, - 0x05, 0x1a, 0x1f, 0x12, 0x1c, 0x10, 0xfa, 0xd1, 0x01, 0x05, 0x00, 0x09, 0xc1, 0x10, 0xec, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0xe1, 0xc1, 0x10, 0xdf, 0xe1, 0xc1, 0x23, 0x10, 0xd6, 0xd1, 0xd5, 0x21, - 0x00, 0x9c, 0x01, 0xc8, 0x00, 0xed, 0xb0, 0xe1, 0xc9, 0xdd, 0x21, 0x84, 0xac, 0x06, 0x3c, 0xdd, - 0x6e, 0x00, 0xdd, 0x66, 0x01, 0xdd, 0x7e, 0x04, 0x32, 0x75, 0x64, 0xaf, 0xcb, 0x46, 0x28, 0x02, - 0x3e, 0x01, 0xdd, 0x77, 0x05, 0x11, 0x06, 0x00, 0xdd, 0x19, 0x10, 0xe3, 0xc9, 0xdd, 0x21, 0x84, - 0xac, 0x06, 0x14, 0xc5, 0xdd, 0x6e, 0x00, 0xdd, 0x66, 0x01, 0xdd, 0x56, 0x02, 0xdd, 0x5e, 0x03, - 0xdd, 0x7e, 0x05, 0xfe, 0x01, 0x7a, 0x20, 0x01, 0x7b, 0x32, 0xa5, 0x64, 0xcb, 0x86, 0x24, 0x7c, - 0xe6, 0x07, 0x20, 0x18, 0x7c, 0xd6, 0x08, 0x67, 0x7d, 0xc6, 0x20, 0x6f, 0xe6, 0xe0, 0x20, 0x0c, - 0x7c, 0xc6, 0x08, 0x67, 0xe6, 0x18, 0xfe, 0x18, 0x20, 0x02, 0x26, 0x40, 0xdd, 0x75, 0x00, 0xdd, - 0x74, 0x01, 0x06, 0x01, 0xcd, 0x67, 0x64, 0x3a, 0xa5, 0x64, 0xcb, 0xf7, 0x32, 0xd8, 0x64, 0xcb, - 0xc6, 0xc1, 0x10, 0xaf, 0xc9, 0x0a, 0xc5, 0x4f, 0x06, 0x00, 0xdd, 0x21, 0xe0, 0x6a, 0xdd, 0x09, - 0xdd, 0x7e, 0x00, 0x82, 0x57, 0xcd, 0xf7, 0x64, 0xe1, 0x7e, 0x3c, 0xe6, 0x1f, 0x77, 0xc9, 0x06, - 0x05, 0xc5, 0x06, 0x08, 0xc5, 0x01, 0x05, 0x00, 0xeb, 0xed, 0xb0, 0xeb, 0x01, 0xfb, 0x00, 0x09, - 0xc1, 0x10, 0xf1, 0x01, 0x20, 0xf8, 0x09, 0xc1, 0x10, 0xe7, 0xc9, 0x11, 0x9b, 0x6e, 0xd5, 0xd5, - 0xd5, 0x21, 0x00, 0x40, 0xcd, 0xf7, 0x64, 0x21, 0x1b, 0x40, 0xd1, 0xcd, 0xf7, 0x64, 0x21, 0x60, - 0x50, 0xd1, 0xcd, 0xf7, 0x64, 0x21, 0x7b, 0x50, 0xd1, 0xcd, 0xf7, 0x64, 0x11, 0x00, 0xa9, 0x21, - 0x06, 0x40, 0xcd, 0xf7, 0x64, 0x11, 0xc8, 0xa9, 0x21, 0x0b, 0x40, 0xcd, 0xf7, 0x64, 0x11, 0x90, - 0xaa, 0x21, 0x10, 0x40, 0xcd, 0xf7, 0x64, 0x11, 0x58, 0xab, 0x21, 0x15, 0x40, 0xcd, 0xf7, 0x64, - 0x11, 0x06, 0x58, 0x21, 0x20, 0xac, 0x06, 0x05, 0xc5, 0x01, 0x14, 0x00, 0xed, 0xb0, 0x01, 0x0c, - 0x00, 0xeb, 0x09, 0xeb, 0xc1, 0x10, 0xf1, 0xcd, 0xc2, 0x6c, 0x21, 0x00, 0xa5, 0x22, 0x36, 0x5c, - 0x21, 0x00, 0x6b, 0xcd, 0x23, 0x67, 0x21, 0x00, 0x40, 0x11, 0x00, 0xc0, 0x01, 0x00, 0x1b, 0xed, - 0xb0, 0xcd, 0x44, 0x6e, 0x21, 0x00, 0xc0, 0x11, 0x00, 0x40, 0x01, 0x00, 0x1b, 0xed, 0xb0, 0xcd, - 0x33, 0x69, 0x3e, 0x01, 0x32, 0xbf, 0xb3, 0xdd, 0x21, 0x00, 0x9d, 0xcd, 0xca, 0x65, 0xcd, 0x4b, - 0x68, 0x7e, 0x32, 0xcb, 0xb3, 0xdd, 0x22, 0xc1, 0xb3, 0x21, 0x00, 0x00, 0x22, 0x78, 0x5c, 0xaf, - 0x32, 0xac, 0xde, 0x21, 0x00, 0x40, 0x11, 0x00, 0xc0, 0x01, 0x00, 0x1b, 0xed, 0xb0, 0xcd, 0x61, - 0x64, 0xc9, 0xdd, 0xe5, 0xe1, 0xaf, 0x32, 0x78, 0x5c, 0x16, 0x00, 0x7e, 0xcd, 0x50, 0x67, 0xa7, - 0x28, 0x22, 0xcd, 0x3a, 0x67, 0x7e, 0xa3, 0xfe, 0x42, 0x20, 0x01, 0x14, 0xfe, 0x43, 0xf5, 0x01, - 0x05, 0x00, 0x09, 0xf1, 0x20, 0x06, 0x7e, 0xfe, 0xc0, 0x20, 0x01, 0x14, 0x23, 0x23, 0x23, 0x7c, - 0xfe, 0xa5, 0x20, 0xd7, 0x7a, 0x32, 0xb5, 0xb3, 0x21, 0x00, 0x00, 0x22, 0xb8, 0xb3, 0x22, 0xba, - 0xb3, 0x22, 0xbc, 0xb3, 0xa7, 0x20, 0x12, 0x21, 0x44, 0x6b, 0xcd, 0x23, 0x67, 0x21, 0x0b, 0x09, - 0x22, 0xb6, 0xb3, 0x3e, 0x0e, 0x32, 0xbe, 0xb3, 0xc9, 0xfe, 0x0d, 0x30, 0x22, 0x32, 0xbb, 0xb3, - 0x21, 0x06, 0x0e, 0x22, 0xb6, 0xb3, 0x3e, 0x01, 0x32, 0xb8, 0xb3, 0x32, 0xc0, 0xb3, 0x3e, 0x08, - 0x32, 0xbe, 0xb3, 0xdd, 0xe5, 0xe1, 0x01, 0x06, 0x0a, 0x1e, 0x41, 0xcd, 0x91, 0x66, 0xc9, 0xfe, - 0x19, 0x30, 0x38, 0xa7, 0x1f, 0x32, 0xbc, 0xb3, 0xce, 0x00, 0x32, 0xbb, 0xb3, 0x21, 0x06, 0x06, - 0x22, 0xb6, 0xb3, 0x3e, 0x02, 0x32, 0xb8, 0xb3, 0x3d, 0x32, 0xc0, 0xb3, 0x3e, 0x08, 0x32, 0xbe, - 0xb3, 0xdd, 0xe5, 0xe1, 0x01, 0x06, 0x02, 0x1e, 0x41, 0x3a, 0xbb, 0xb3, 0x57, 0xcd, 0x91, 0x66, - 0x01, 0x06, 0x12, 0x3a, 0xbc, 0xb3, 0x57, 0xcd, 0x91, 0x66, 0xc9, 0x3e, 0x18, 0xcd, 0x4b, 0x66, - 0x22, 0xc3, 0xb3, 0x3e, 0x01, 0x32, 0xb9, 0xb3, 0xc9, 0xed, 0x43, 0xcf, 0xb3, 0xd5, 0x7e, 0xcd, - 0x50, 0x67, 0x1e, 0xff, 0xcd, 0x3a, 0x67, 0x16, 0x06, 0x7e, 0xa3, 0xfe, 0x42, 0x28, 0x14, 0xfe, - 0x43, 0xf5, 0x01, 0x05, 0x00, 0x09, 0xf1, 0x20, 0x05, 0x7e, 0xfe, 0xc0, 0x28, 0x0b, 0x23, 0x23, - 0x23, 0x18, 0xdb, 0x01, 0x05, 0x00, 0x09, 0x16, 0x04, 0x01, 0x0d, 0x00, 0xa7, 0xed, 0x42, 0x7a, - 0x32, 0xda, 0xb3, 0x11, 0xdb, 0xb3, 0x06, 0x08, 0x7e, 0xfe, 0x20, 0x30, 0x02, 0x3e, 0x2e, 0xfe, - 0x80, 0x38, 0x02, 0x3e, 0x2e, 0x12, 0x13, 0x23, 0x10, 0xee, 0x3e, 0xff, 0x12, 0xd1, 0xe5, 0x6b, - 0x26, 0x3e, 0x22, 0xd5, 0xb3, 0x21, 0x20, 0x20, 0x22, 0xd7, 0xb3, 0x3e, 0x10, 0x32, 0xd9, 0xb3, - 0x3e, 0x16, 0x32, 0xce, 0xb3, 0x21, 0x13, 0x01, 0x22, 0xd1, 0xb3, 0x21, 0x10, 0x05, 0x22, 0xd3, - 0xb3, 0xd5, 0x21, 0xce, 0xb3, 0xcd, 0x23, 0x67, 0xd1, 0xe1, 0x01, 0x08, 0x00, 0x09, 0x3a, 0xcf, - 0xb3, 0x3c, 0x32, 0xcf, 0xb3, 0x1c, 0x15, 0xc2, 0x95, 0x66, 0xc9, 0xe5, 0x2a, 0xca, 0xaf, 0xd9, - 0xfd, 0x2a, 0xc8, 0xaf, 0x3e, 0x02, 0xcd, 0x01, 0x16, 0xe1, 0x7e, 0xfe, 0xff, 0xc8, 0xd7, 0x23, - 0x18, 0xf8, 0xd5, 0x0e, 0x00, 0x06, 0x09, 0x11, 0x6e, 0x6b, 0x1a, 0xbe, 0x28, 0x02, 0x0e, 0xff, - 0x23, 0x13, 0x10, 0xf6, 0xd1, 0x59, 0x2b, 0xc9, 0xfe, 0x01, 0xc0, 0x01, 0x10, 0x00, 0x09, 0x7e, - 0x18, 0xf6, 0xfd, 0xe5, 0xdd, 0xe5, 0xe5, 0xd5, 0xc5, 0xf5, 0xd9, 0xe5, 0xd5, 0xc5, 0x2a, 0xca, - 0xaf, 0xd9, 0x08, 0xf5, 0xfd, 0x2a, 0xc8, 0xaf, 0xcd, 0x56, 0xeb, 0x2a, 0x78, 0x5c, 0x23, 0x22, - 0x78, 0x5c, 0x3a, 0xac, 0xde, 0xc6, 0x02, 0x32, 0xac, 0xde, 0xcd, 0xa8, 0x6b, 0xcd, 0x07, 0x6d, - 0x3a, 0x78, 0x5c, 0xe6, 0x03, 0xfe, 0x03, 0x20, 0x21, 0xcd, 0x4b, 0x68, 0xed, 0x5b, 0xbe, 0xb3, - 0x43, 0x72, 0x23, 0x10, 0xfc, 0xcb, 0x72, 0xcb, 0xf2, 0x28, 0x0b, 0xcb, 0xb2, 0x14, 0xcb, 0x5a, - 0x28, 0x04, 0x14, 0x14, 0xcb, 0x9a, 0x7a, 0x32, 0xbf, 0xb3, 0x3a, 0x78, 0x5c, 0xfe, 0x32, 0x20, - 0x58, 0x2a, 0xb6, 0xb3, 0x22, 0xcc, 0xb3, 0x3a, 0xbe, 0xb3, 0x32, 0xec, 0xb3, 0x21, 0xcc, 0xb3, - 0x22, 0x4d, 0x68, 0xcd, 0x4b, 0x68, 0x2a, 0xc9, 0xb3, 0x7c, 0x07, 0x07, 0x07, 0xf6, 0xc0, 0x67, - 0x22, 0xae, 0xde, 0x3a, 0xec, 0xb3, 0x47, 0xc5, 0xe5, 0x11, 0xed, 0xb3, 0xd5, 0x06, 0x08, 0x7e, - 0x12, 0x24, 0x13, 0x10, 0xfa, 0xe1, 0xd1, 0xd5, 0x06, 0x08, 0x0e, 0x08, 0xe5, 0xcb, 0x16, 0x1f, - 0x23, 0x0d, 0x20, 0xf9, 0x12, 0x14, 0xe1, 0x10, 0xf1, 0xe1, 0x23, 0xc1, 0x10, 0xd9, 0x21, 0xb6, - 0xb3, 0x22, 0x4d, 0x68, 0xcd, 0xa5, 0x6c, 0x18, 0x1f, 0xfe, 0x3c, 0x28, 0xb0, 0xfe, 0x46, 0x28, - 0xac, 0xfe, 0x50, 0x28, 0xa8, 0xfe, 0x5a, 0x20, 0x0f, 0x3e, 0xc9, 0x32, 0xdb, 0x67, 0xcd, 0xc5, - 0x67, 0x3e, 0x3a, 0x32, 0xdb, 0x67, 0x18, 0xd6, 0x3a, 0x78, 0x5c, 0xe6, 0x07, 0xfe, 0x07, 0xcc, - 0x67, 0x68, 0xd9, 0xf1, 0x08, 0xc1, 0xd1, 0xe1, 0xd9, 0xf1, 0xc1, 0xd1, 0xe1, 0xdd, 0xe1, 0xfd, - 0xe1, 0xfb, 0xc9, 0xed, 0x5b, 0xb6, 0xb3, 0x21, 0x00, 0x00, 0x01, 0x20, 0x00, 0x7b, 0xa7, 0x28, - 0x04, 0x09, 0x1d, 0x18, 0xf8, 0x4a, 0x09, 0x22, 0xc9, 0xb3, 0x01, 0x00, 0x58, 0x09, 0xc9, 0x0e, - 0x41, 0x21, 0x56, 0x6b, 0x7e, 0x0f, 0x0f, 0xe6, 0x38, 0xf6, 0x87, 0x32, 0x79, 0x68, 0x3e, 0xff, - 0xcb, 0x87, 0xdb, 0xfe, 0x2f, 0x47, 0x7e, 0xe6, 0x1f, 0xa0, 0xc4, 0x54, 0x6a, 0x0c, 0x23, 0x79, - 0xfe, 0x59, 0x20, 0xe0, 0x3e, 0xf7, 0xdb, 0xfe, 0x1f, 0xd4, 0x6a, 0x69, 0x1f, 0xd4, 0x6a, 0x69, - 0x1f, 0xd4, 0xc3, 0x69, 0x1f, 0xd4, 0x3d, 0x6a, 0x1f, 0xd4, 0x42, 0x6a, 0x3e, 0xef, 0xdb, 0xfe, - 0x1f, 0xd4, 0x42, 0x6a, 0x1f, 0xd4, 0x3d, 0x6a, 0x1f, 0xd4, 0xc3, 0x69, 0x1f, 0xd4, 0x6a, 0x69, - 0x1f, 0xd4, 0x6a, 0x69, 0x3e, 0x00, 0x1f, 0xdc, 0x6a, 0x69, 0x1f, 0xdc, 0x6a, 0x69, 0x1f, 0xdc, - 0xc3, 0x69, 0x1f, 0xdc, 0x3d, 0x6a, 0x1f, 0xdc, 0x42, 0x6a, 0x3e, 0xfe, 0xdb, 0xfe, 0x1f, 0x38, - 0x0f, 0x2a, 0xc1, 0xb3, 0x01, 0x01, 0x9d, 0xa7, 0xed, 0x42, 0x38, 0x04, 0xcd, 0x89, 0x65, 0xc9, - 0x3e, 0xbf, 0xdb, 0xfe, 0x1f, 0xd4, 0x42, 0x6a, 0x3e, 0x7f, 0xdb, 0xfe, 0x1f, 0x38, 0x21, 0x2a, - 0xca, 0xaf, 0xd9, 0xfd, 0x2a, 0xc8, 0xaf, 0xcd, 0x44, 0x6e, 0x21, 0x00, 0x9d, 0x11, 0x00, 0x00, - 0x01, 0x05, 0x08, 0xf3, 0xed, 0x56, 0xcd, 0x13, 0x3d, 0xf3, 0xed, 0x5e, 0xcd, 0x89, 0x65, 0xc9, - 0x1f, 0xd8, 0x3a, 0xb9, 0xb3, 0xa7, 0xc8, 0x2a, 0xc3, 0xb3, 0xaf, 0x32, 0xb9, 0xb3, 0x22, 0xa1, - 0x65, 0xcd, 0x89, 0x65, 0x21, 0x00, 0x9d, 0x22, 0xa1, 0x65, 0xc9, 0x21, 0xc0, 0x40, 0x0e, 0x60, - 0x06, 0x20, 0x36, 0x00, 0x2c, 0x10, 0xfb, 0x0d, 0x28, 0x1a, 0x7d, 0xd6, 0x20, 0x6f, 0x24, 0x7c, - 0xe6, 0x07, 0x20, 0xec, 0x7c, 0xd6, 0x08, 0x67, 0x7d, 0xc6, 0x20, 0x6f, 0x20, 0xe2, 0x7c, 0xc6, - 0x08, 0x67, 0x18, 0xdc, 0x21, 0xc0, 0x58, 0x11, 0xc1, 0x58, 0x01, 0x7f, 0x01, 0x36, 0x07, 0xed, - 0xb0, 0xc9, 0xf5, 0x3a, 0xbc, 0xb3, 0xa7, 0x20, 0x02, 0xf1, 0xc9, 0xcd, 0x4b, 0x68, 0x3a, 0xbe, - 0xb3, 0x47, 0x3a, 0xcb, 0xb3, 0x77, 0x23, 0x10, 0xfc, 0x3a, 0xbb, 0xb3, 0x47, 0x3a, 0xc0, 0xb3, - 0x4f, 0x3d, 0x90, 0x30, 0x23, 0xfe, 0xff, 0x20, 0x13, 0x2a, 0xbb, 0xb3, 0x7c, 0xbd, 0x28, 0x0c, - 0x3e, 0x01, 0x32, 0xc0, 0xb3, 0x3e, 0x06, 0x32, 0xb6, 0xb3, 0x18, 0x16, 0x79, 0x80, 0x32, 0xc0, - 0xb3, 0x3e, 0x16, 0x32, 0xb7, 0xb3, 0x18, 0x0a, 0x79, 0x90, 0x32, 0xc0, 0xb3, 0x3e, 0x06, 0x32, - 0xb7, 0xb3, 0xcd, 0x4b, 0x68, 0x7e, 0x32, 0xcb, 0xb3, 0xf1, 0xc9, 0xf5, 0x06, 0x00, 0x3a, 0xbc, - 0xb3, 0xa7, 0x20, 0x09, 0x3a, 0xbb, 0xb3, 0xa7, 0x28, 0x9f, 0x3d, 0x28, 0x9c, 0xc5, 0xcd, 0x4b, - 0x68, 0x3a, 0xbe, 0xb3, 0x47, 0x3a, 0xcb, 0xb3, 0x77, 0x23, 0x10, 0xfc, 0xc1, 0x3a, 0xc0, 0xb3, - 0x4f, 0xed, 0x5b, 0xbb, 0xb3, 0x3a, 0xb6, 0xb3, 0x6f, 0x3a, 0xb7, 0xb3, 0xfe, 0x16, 0x28, 0x21, - 0x78, 0xa7, 0x20, 0x0e, 0x79, 0xbb, 0x3e, 0x06, 0x26, 0x01, 0x28, 0x2f, 0x61, 0x24, 0x7d, 0x3c, - 0x18, 0x29, 0x79, 0xfe, 0x01, 0x20, 0x04, 0x63, 0x7b, 0x18, 0x1e, 0x61, 0x25, 0x7d, 0x3d, 0x18, - 0x1a, 0x78, 0xa7, 0x20, 0x0b, 0x7b, 0x82, 0xb9, 0x20, 0xe2, 0x63, 0x24, 0x3e, 0x06, 0x18, 0x0b, - 0x7b, 0x3c, 0xb9, 0x20, 0xe6, 0x7b, 0x82, 0x67, 0x7a, 0xc6, 0x05, 0x32, 0xb6, 0xb3, 0x7c, 0x32, - 0xc0, 0xb3, 0xc3, 0xba, 0x69, 0x06, 0x01, 0xf5, 0x18, 0x84, 0xf5, 0x3a, 0xbb, 0xb3, 0x47, 0x3a, - 0xbc, 0xb3, 0xb0, 0xca, 0x71, 0x69, 0x3a, 0xc0, 0xb3, 0xc6, 0x40, 0x4f, 0x3a, 0xb5, 0xb3, 0xc6, - 0x40, 0xb9, 0xd8, 0x06, 0x40, 0xd9, 0x2a, 0xc1, 0xb3, 0x7e, 0xcd, 0x50, 0x67, 0x1e, 0xff, 0xcd, - 0x3a, 0x67, 0x16, 0xec, 0x7e, 0xa3, 0xfe, 0x42, 0x28, 0x14, 0xfe, 0x43, 0xf5, 0x01, 0x05, 0x00, - 0x09, 0xf1, 0x20, 0x05, 0x7e, 0xfe, 0xc0, 0x28, 0x0b, 0x23, 0x23, 0x23, 0x18, 0xdb, 0x01, 0x05, - 0x00, 0x09, 0x16, 0xf7, 0x01, 0x0d, 0x00, 0xa7, 0xed, 0x42, 0xd9, 0x04, 0x78, 0xb9, 0xd9, 0x7e, - 0x36, 0x01, 0x20, 0xc5, 0x77, 0xd5, 0xe5, 0x21, 0x4c, 0x5d, 0x72, 0x23, 0x23, 0xeb, 0xe1, 0x01, - 0x08, 0x00, 0xed, 0xb0, 0x13, 0x0e, 0x20, 0xe1, 0x7c, 0xfe, 0xf7, 0x28, 0x02, 0x0e, 0xaf, 0x79, - 0x12, 0x21, 0x00, 0x00, 0x22, 0x78, 0x5c, 0x3a, 0xce, 0xaf, 0xed, 0x47, 0x2a, 0xca, 0xaf, 0xd9, - 0xfd, 0x2a, 0xc8, 0xaf, 0xed, 0x7b, 0xcc, 0xaf, 0x21, 0x00, 0x3c, 0x22, 0x36, 0x5c, 0xcd, 0x4b, - 0x6e, 0xcd, 0x75, 0x6e, 0xed, 0x56, 0xfb, 0xc9, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x16, 0x13, 0x07, 0x13, 0x01, 0x10, 0x05, 0x53, - 0x50, 0x41, 0x43, 0x45, 0x20, 0x20, 0x54, 0x4f, 0x20, 0x4e, 0x45, 0x57, 0x20, 0x44, 0x49, 0x53, - 0x4b, 0x16, 0x14, 0x07, 0x22, 0x53, 0x53, 0x22, 0x20, 0x54, 0x4f, 0x20, 0x4e, 0x45, 0x58, 0x54, - 0x20, 0x50, 0x41, 0x47, 0x45, 0x53, 0x16, 0x15, 0x07, 0x22, 0x43, 0x53, 0x22, 0x20, 0x54, 0x4f, - 0x20, 0x46, 0x49, 0x52, 0x53, 0x54, 0x20, 0x50, 0x41, 0x47, 0x45, 0xff, 0x16, 0x0b, 0x09, 0x4e, - 0x4f, 0x20, 0x45, 0x58, 0x45, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x20, 0x21, 0xff, 0x21, 0xf0, - 0x08, 0x24, 0x44, 0x28, 0x30, 0xd0, 0xa4, 0xc8, 0xc4, 0xc2, 0xe4, 0xe8, 0xa2, 0xa1, 0x41, 0x48, - 0x22, 0x50, 0xa8, 0x10, 0x42, 0x04, 0x62, 0x6f, 0x6f, 0x74, 0x20, 0x20, 0x20, 0x20, 0x42, 0x08, - 0x10, 0x13, 0x17, 0x1f, 0x05, 0x0c, 0x15, 0x1b, 0x22, 0x01, 0x03, 0x0a, 0x1d, 0x24, 0x27, 0x07, - 0x0e, 0x19, 0x20, 0x02, 0x0b, 0x12, 0x16, 0x1c, 0x26, 0x00, 0x0f, 0x1e, 0x23, 0x09, 0x04, 0x11, - 0x18, 0x25, 0x14, 0x06, 0x0d, 0x1a, 0x21, 0x13, 0x12, 0x0f, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x00, - 0x3a, 0xac, 0xde, 0xa7, 0x20, 0x20, 0x2a, 0xa8, 0xde, 0x23, 0x7c, 0xe6, 0x07, 0x67, 0x22, 0xa8, - 0xde, 0x06, 0x08, 0x4e, 0x21, 0xec, 0xad, 0xed, 0x5f, 0xae, 0xa9, 0x81, 0x4f, 0x3a, 0xb0, 0xb3, - 0xa9, 0x77, 0x23, 0x10, 0xf2, 0xc9, 0x3a, 0xac, 0xde, 0xe6, 0x3f, 0xfe, 0x3e, 0x20, 0x21, 0x3a, - 0xb0, 0xde, 0x3c, 0xe6, 0x07, 0x32, 0xb0, 0xde, 0x28, 0x08, 0x3a, 0xac, 0xde, 0xd6, 0x02, 0x32, - 0xac, 0xde, 0x3e, 0xc9, 0x32, 0x1c, 0x6c, 0xcd, 0x00, 0x6c, 0x3e, 0x3a, 0x32, 0x1c, 0x6c, 0xc9, - 0x3a, 0xac, 0xde, 0xe6, 0x07, 0xfe, 0x02, 0xc0, 0x3a, 0xac, 0xde, 0x07, 0x07, 0x07, 0xe6, 0x06, - 0x4f, 0x06, 0x00, 0x21, 0xec, 0xad, 0x09, 0xcd, 0x59, 0x6c, 0xf5, 0xd4, 0xae, 0x6b, 0xf1, 0x30, - 0xe7, 0xcd, 0x74, 0x6c, 0x3a, 0xac, 0xde, 0xe6, 0x20, 0x0e, 0x60, 0x20, 0x02, 0x0e, 0x00, 0x3a, - 0xac, 0xde, 0x07, 0x07, 0xa9, 0xe6, 0x60, 0x4f, 0x06, 0x00, 0x21, 0x63, 0x6f, 0x09, 0xeb, 0x06, - 0x10, 0x2a, 0xaa, 0xde, 0x4e, 0x1a, 0xb1, 0x77, 0x2c, 0x13, 0x4e, 0x1a, 0xb1, 0x77, 0x13, 0x2d, - 0x24, 0x7c, 0xe6, 0x07, 0x20, 0x08, 0x7c, 0xd6, 0x08, 0x67, 0x7d, 0xc6, 0x20, 0x6f, 0x10, 0xe4, - 0xc9, 0x06, 0x1a, 0x23, 0x7e, 0xe6, 0x18, 0x28, 0x06, 0xfe, 0x10, 0x28, 0x02, 0x06, 0x1f, 0x2b, - 0x7e, 0xfe, 0xe0, 0xd0, 0xe6, 0x1f, 0xb8, 0xd0, 0xee, 0x1f, 0xb8, 0xc9, 0x23, 0x7e, 0xe6, 0x18, - 0xfe, 0x18, 0x20, 0x02, 0x3e, 0x08, 0x2b, 0x6e, 0xf6, 0x40, 0x67, 0x22, 0xaa, 0xde, 0x06, 0x10, - 0x7c, 0xf6, 0x80, 0x57, 0x5d, 0x1a, 0x77, 0x2c, 0x1c, 0x1a, 0x77, 0x2d, 0x24, 0x7c, 0xe6, 0x07, - 0x20, 0x08, 0x7c, 0xd6, 0x08, 0x67, 0x7d, 0xc6, 0x20, 0x6f, 0x10, 0xe4, 0xc9, 0x2a, 0xae, 0xde, - 0x7c, 0xe6, 0x7f, 0x57, 0x5d, 0x06, 0x08, 0xc5, 0x3a, 0xec, 0xb3, 0x4f, 0x06, 0x00, 0xe5, 0xd5, - 0xed, 0xb0, 0xd1, 0xe1, 0x24, 0x14, 0xc1, 0x10, 0xee, 0xc9, 0x21, 0x00, 0x58, 0xcd, 0xdb, 0x6c, - 0x21, 0x7b, 0x5a, 0xcd, 0xdb, 0x6c, 0x11, 0x1b, 0x58, 0xcd, 0xf2, 0x6c, 0x11, 0x60, 0x5a, 0xcd, - 0xf2, 0x6c, 0xc9, 0x06, 0x05, 0x11, 0xf4, 0xad, 0x0e, 0x05, 0x1a, 0x77, 0x23, 0x0d, 0x20, 0xfb, - 0x13, 0xc5, 0x01, 0x1b, 0x00, 0x09, 0xc1, 0x10, 0xef, 0xc9, 0x06, 0x05, 0xc5, 0x01, 0x05, 0x00, - 0x21, 0xf4, 0xad, 0xed, 0xb0, 0xeb, 0x01, 0x1b, 0x00, 0x09, 0xeb, 0xc1, 0x10, 0xee, 0xc9, 0x3a, - 0x78, 0x5c, 0xfe, 0x40, 0xd0, 0x21, 0x06, 0x58, 0x22, 0x41, 0x6d, 0x21, 0x19, 0x58, 0x22, 0x44, - 0x6d, 0x21, 0x80, 0x58, 0x22, 0x57, 0x6d, 0x3a, 0x79, 0x5c, 0xe6, 0x01, 0x28, 0x12, 0x21, 0x66, - 0x5a, 0x22, 0x41, 0x6d, 0x21, 0x79, 0x5a, 0x22, 0x44, 0x6d, 0x21, 0xe0, 0x5a, 0x22, 0x57, 0x6d, - 0x3a, 0x78, 0x5c, 0xa7, 0x20, 0x09, 0x3e, 0x02, 0x32, 0x06, 0x58, 0x32, 0x19, 0x58, 0xc9, 0xe6, - 0x01, 0xc0, 0x3a, 0x78, 0x5c, 0x0f, 0xe6, 0x1f, 0xfe, 0x1b, 0xd0, 0x4f, 0x06, 0x05, 0x21, 0x80, - 0x58, 0x0d, 0xcd, 0x6d, 0x6d, 0x0c, 0xcd, 0x8d, 0x6d, 0x0c, 0xc5, 0x01, 0x20, 0x00, 0xa7, 0xed, - 0x42, 0xc1, 0x10, 0xed, 0xc9, 0xc5, 0xe5, 0x7d, 0xb1, 0x6f, 0x5d, 0x7c, 0xf6, 0x80, 0x57, 0xe5, - 0xd5, 0xeb, 0x01, 0x03, 0x00, 0xed, 0xb0, 0xe1, 0xd1, 0x7d, 0xee, 0x1f, 0x6f, 0x5d, 0x0e, 0x03, - 0xed, 0xb8, 0xe1, 0xc1, 0xc9, 0xc5, 0xe5, 0x7d, 0xb1, 0x6f, 0xe5, 0xcd, 0xb1, 0x6d, 0x23, 0xcd, - 0xbd, 0x6d, 0x23, 0xcd, 0xb1, 0x6d, 0xe1, 0x7d, 0xee, 0x1f, 0x6f, 0xcd, 0xb1, 0x6d, 0x2b, 0xcd, - 0xbd, 0x6d, 0x2b, 0xcd, 0xb1, 0x6d, 0xe1, 0xc1, 0xc9, 0x7d, 0xe6, 0x1f, 0xfe, 0x06, 0xd8, 0xfe, - 0x1a, 0xd0, 0x36, 0x02, 0xc9, 0x7d, 0xe6, 0x1f, 0xfe, 0x06, 0xd8, 0xfe, 0x1a, 0xd0, 0x36, 0x42, - 0xc9, 0x21, 0x00, 0xa9, 0xcd, 0x38, 0x6e, 0x21, 0xc8, 0xa9, 0xcd, 0x38, 0x6e, 0x21, 0x90, 0xaa, - 0xcd, 0x38, 0x6e, 0x21, 0x58, 0xab, 0xcd, 0x38, 0x6e, 0xf3, 0x11, 0x00, 0xa9, 0x21, 0x66, 0xd0, - 0xcd, 0xf7, 0x64, 0x11, 0xc8, 0xa9, 0x21, 0x6b, 0xd0, 0xcd, 0xf7, 0x64, 0x11, 0x90, 0xaa, 0x21, - 0x70, 0xd0, 0xcd, 0xf7, 0x64, 0x11, 0x58, 0xab, 0x21, 0x75, 0xd0, 0xcd, 0xf7, 0x64, 0x06, 0x05, - 0x21, 0x06, 0xd8, 0x11, 0xe6, 0xda, 0xc5, 0x01, 0x14, 0x00, 0xed, 0xb0, 0x0e, 0x0c, 0x09, 0x0e, - 0x34, 0xa7, 0xeb, 0xed, 0x42, 0xeb, 0xc1, 0x10, 0xed, 0x21, 0x00, 0xd0, 0x11, 0x00, 0x50, 0x01, - 0x00, 0x08, 0xed, 0xb0, 0x21, 0x60, 0xda, 0x11, 0x60, 0x5a, 0x0e, 0xa0, 0xed, 0xb0, 0xfb, 0xc9, - 0xcd, 0x25, 0x64, 0xcd, 0x25, 0x64, 0x54, 0x5d, 0xcd, 0xd9, 0x63, 0xc9, 0x21, 0x30, 0x75, 0xcd, - 0x52, 0x6e, 0xc9, 0x21, 0x67, 0x6e, 0xcd, 0x52, 0x6e, 0xc9, 0xaf, 0x01, 0xfd, 0xff, 0xed, 0x79, - 0xf5, 0x7e, 0x01, 0xfd, 0xbf, 0xed, 0x79, 0x23, 0xf1, 0x3c, 0xfe, 0x0e, 0xc8, 0x18, 0xec, 0x30, - 0x00, 0x01, 0x00, 0x50, 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0xc4, 0x50, 0x01, 0x16, 0x08, 0x0e, - 0x10, 0x21, 0x00, 0x40, 0x41, 0xaf, 0xcb, 0x1e, 0x23, 0xaf, 0xcb, 0x16, 0x23, 0x10, 0xf6, 0x41, - 0xaf, 0xcb, 0x16, 0x23, 0xaf, 0xcb, 0x1e, 0x23, 0x10, 0xf6, 0x7c, 0xfe, 0x58, 0x20, 0xe5, 0x15, - 0x20, 0xdd, 0xc9, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x72, - 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x80, 0x00, 0x00, 0x03, 0xd0, 0x40, - 0x00, 0x00, 0x07, 0xa8, 0x20, 0x00, 0x00, 0x0b, 0x54, 0x10, 0x00, 0x00, 0x1e, 0xaa, 0x08, 0x00, - 0x00, 0x2d, 0x55, 0x04, 0x00, 0x00, 0x7a, 0xaa, 0x82, 0x00, 0x00, 0xb5, 0x55, 0x41, 0x00, 0x01, - 0x6a, 0xaa, 0xa0, 0x80, 0x02, 0xd5, 0x45, 0x50, 0x40, 0x05, 0x2a, 0x82, 0xa8, 0x20, 0x09, 0x55, - 0x01, 0x54, 0x10, 0x12, 0xaa, 0x00, 0xaa, 0x08, 0x25, 0x54, 0x00, 0x55, 0x04, 0x22, 0xa8, 0x00, - 0x2a, 0x84, 0x25, 0x50, 0x00, 0x15, 0x44, 0x2a, 0xa4, 0x00, 0x3a, 0xa4, 0x35, 0x42, 0x00, 0x7d, - 0x54, 0x2a, 0xa1, 0x00, 0xba, 0xa8, 0x15, 0x51, 0x01, 0xf5, 0x50, 0x0a, 0xa9, 0x03, 0xea, 0xa0, - 0x05, 0x55, 0x05, 0xd5, 0x40, 0x02, 0xab, 0x0f, 0xaa, 0x80, 0x01, 0x55, 0x17, 0x55, 0x00, 0x00, - 0xaa, 0x2e, 0xaa, 0x00, 0x00, 0x54, 0x55, 0x54, 0x00, 0x00, 0x28, 0x9a, 0xa8, 0x00, 0x00, 0x11, - 0x35, 0x50, 0x00, 0x00, 0x01, 0x2a, 0xa0, 0x00, 0x00, 0x01, 0x55, 0x40, 0x00, 0x00, 0x01, 0xaa, - 0x80, 0x00, 0x00, 0x01, 0x55, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, - 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x05, 0xd0, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x90, 0x02, 0x80, 0x01, 0xc0, 0x2f, 0xf4, 0x01, 0xc0, 0x00, - 0xa0, 0x04, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x80, 0x10, 0x08, 0x00, 0x80, 0x04, 0x80, 0x02, 0xa0, 0x01, 0xc0, 0x5f, 0xfd, 0x01, 0xc0, 0x02, - 0x80, 0x04, 0x90, 0x00, 0x88, 0x10, 0x80, 0x00, 0x00, 0x00, 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x10, 0x10, 0x00, 0x10, 0x00, - 0x00, 0x36, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x7e, 0x24, 0x24, 0x7e, 0x24, 0x00, - 0x00, 0x08, 0x3e, 0x68, 0x3e, 0x0a, 0x7e, 0x08, 0x00, 0x66, 0x4c, 0x18, 0x30, 0x66, 0x4e, 0x00, - 0x18, 0x24, 0x2c, 0x78, 0xda, 0xcc, 0x7e, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x30, 0x18, 0x18, 0x18, 0x18, 0x30, 0x00, - 0x00, 0x00, 0x14, 0x08, 0x7e, 0x18, 0x2c, 0x00, 0x00, 0x00, 0x08, 0x08, 0x7e, 0x18, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, - 0x38, 0x6c, 0xce, 0xd6, 0xe6, 0x6c, 0x38, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x3e, 0x7c, 0x00, - 0x78, 0xcc, 0x1c, 0x38, 0x70, 0xe6, 0xfe, 0x00, 0x7e, 0xcc, 0x18, 0x3c, 0x0e, 0xce, 0x7c, 0x00, - 0x24, 0x6c, 0xcc, 0xcc, 0xfc, 0x0c, 0x0c, 0x00, 0xfe, 0xc2, 0xf8, 0xcc, 0x0e, 0xce, 0x7c, 0x00, - 0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00, 0x7e, 0x66, 0x0e, 0x1c, 0x38, 0x30, 0x30, 0x00, - 0x7c, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00, - 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, - 0x00, 0x00, 0x0c, 0x18, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x7e, 0x00, 0x00, - 0x00, 0x00, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0x1c, 0x30, 0x00, 0x30, 0x00, - 0x00, 0x3c, 0x4a, 0x56, 0xfe, 0xc0, 0x7c, 0x00, 0x3c, 0x66, 0xc6, 0xfe, 0xc6, 0xe6, 0x66, 0x00, - 0xf8, 0xcc, 0xcc, 0xfc, 0xc6, 0xc6, 0xfc, 0x00, 0x38, 0x6c, 0xc6, 0xc0, 0xc0, 0xe6, 0x7c, 0x00, - 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0xfe, 0xe6, 0x60, 0x7c, 0x60, 0xe6, 0xfe, 0x00, - 0xfe, 0xe6, 0x60, 0x7c, 0x60, 0xe0, 0xc0, 0x00, 0x3c, 0x66, 0xc0, 0xde, 0xc6, 0xee, 0x7c, 0x00, - 0xc4, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x46, 0x00, 0x7e, 0x98, 0x30, 0x30, 0x30, 0x1a, 0xfc, 0x00, - 0x3e, 0x0c, 0x0c, 0xe6, 0x66, 0xc6, 0x7c, 0x00, 0xe6, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0xc6, 0x00, - 0xc0, 0xe0, 0x60, 0x60, 0x60, 0xe6, 0xfe, 0x00, 0x46, 0xee, 0xfe, 0xd6, 0xd6, 0xc6, 0x46, 0x00, - 0xc4, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0x46, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xee, 0x7c, 0x00, - 0xfc, 0xe6, 0xc6, 0xce, 0xfc, 0xc0, 0xc0, 0x00, 0x38, 0x6c, 0xc6, 0xd6, 0xce, 0xec, 0x7a, 0x00, - 0xfc, 0xc6, 0xc6, 0xfc, 0xd8, 0xcc, 0xc6, 0x00, 0x3e, 0x66, 0x70, 0x1c, 0xc6, 0xc6, 0x7c, 0x00, - 0xfe, 0xba, 0x18, 0x18, 0x18, 0x38, 0x30, 0x00, 0xe6, 0x66, 0xc6, 0xc6, 0xc6, 0xe6, 0x7c, 0x00, - 0xce, 0xcc, 0xc6, 0xc6, 0x66, 0x7c, 0x38, 0x00, 0xce, 0xc6, 0xd6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, - 0xc6, 0xee, 0x6c, 0x38, 0x6c, 0xee, 0xc6, 0x00, 0xc6, 0xc6, 0x6e, 0x3c, 0x18, 0x18, 0x18, 0x00, - 0x76, 0xec, 0x98, 0x30, 0x62, 0xde, 0xbc, 0x00, 0x00, 0x1c, 0x10, 0x10, 0x30, 0x30, 0x3c, 0x00, - 0x00, 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x1c, 0x04, 0x04, 0x0c, 0x0c, 0x3c, 0x00, - 0x00, 0x08, 0x1c, 0x2a, 0x08, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, - 0x00, 0x1c, 0x22, 0x78, 0x60, 0x60, 0xfc, 0x00, 0x00, 0x00, 0x1c, 0x02, 0x3e, 0x66, 0x3e, 0x00, - 0x00, 0x20, 0x20, 0x3c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x1c, 0x20, 0x60, 0x60, 0x3c, 0x00, - 0x00, 0x02, 0x02, 0x1e, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x7c, 0x60, 0x3e, 0x00, - 0x00, 0x0c, 0x10, 0x18, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x1e, 0x22, 0x66, 0x3e, 0x06, 0x3c, - 0x00, 0x20, 0x20, 0x3c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x08, 0x00, 0x08, 0x18, 0x18, 0x3c, 0x00, - 0x00, 0x04, 0x00, 0x04, 0x0c, 0x0c, 0x6c, 0x38, 0x00, 0x20, 0x28, 0x30, 0x70, 0x78, 0x6c, 0x00, - 0x00, 0x10, 0x10, 0x10, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x34, 0x2a, 0x6a, 0x6a, 0x6a, 0x00, - 0x00, 0x00, 0x3c, 0x22, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x66, 0x66, 0x3c, 0x00, - 0x00, 0x00, 0x3c, 0x22, 0x66, 0x7c, 0x60, 0x60, 0x00, 0x00, 0x3c, 0x44, 0xcc, 0x7c, 0x0c, 0x0e, - 0x00, 0x00, 0x1c, 0x20, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x1c, 0x20, 0x3c, 0x06, 0x7c, 0x00, - 0x00, 0x10, 0x38, 0x10, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x22, 0x22, 0x66, 0x66, 0x3c, 0x00, - 0x00, 0x00, 0x44, 0x44, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x22, 0x2a, 0x6a, 0x7e, 0x3c, 0x00, - 0x00, 0x00, 0x22, 0x14, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x22, 0x22, 0x66, 0x3e, 0x06, 0x3c, - 0x00, 0x00, 0x3e, 0x04, 0x18, 0x30, 0x7e, 0x00, 0x00, 0x0e, 0x08, 0x30, 0x10, 0x18, 0x1e, 0x00, - 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x38, 0x08, 0x04, 0x08, 0x18, 0x78, 0x00, - 0x00, 0x12, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x99, 0xa1, 0xa1, 0x99, 0x42, 0x3c, - 0x09, 0x20, 0x00, 0x00, 0x00, 0x05, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x91, 0xc0, 0x00, 0x07, 0xbe, - 0xe4, 0x1e, 0x00, 0x70, 0x02, 0x7e, 0xa0, 0xff, 0x02, 0x01, 0x21, 0xf5, 0x00, 0x57, 0x09, 0x60, - 0x1f, 0x95, 0xf8, 0x01, 0x20, 0x00, 0xff, 0xe0, 0x01, 0x11, 0xfc, 0x2d, 0xff, 0x01, 0x13, 0x9f, - 0xff, 0xe1, 0x00, 0x17, 0xff, 0xff, 0x99, 0x01, 0x16, 0x04, 0xbf, 0xfe, 0x00, 0x2c, 0x01, 0xfd, - 0xdf, 0x01, 0x2c, 0xff, 0x07, 0x57, 0x00, 0x3f, 0x00, 0x0e, 0xbb, 0x00, 0x78, 0x00, 0x0a, 0x96, - 0x00, 0x00, 0x00, 0x15, 0x02, 0x00, 0x10, 0x00, 0x20, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x04, 0x00, - 0x10, 0x00, 0x80, 0x09, 0x00, 0x10, 0x83, 0x00, 0x12, 0x01, 0x10, 0x0a, 0x00, 0xa4, 0x00, 0x12, - 0x3c, 0x21, 0x48, 0x00, 0x54, 0xd2, 0x12, 0xb0, 0x00, 0x3b, 0x29, 0x41, 0x50, 0x57, 0xec, 0xd1, - 0x22, 0xa0, 0x00, 0x5b, 0xe4, 0x0c, 0x40, 0x00, 0xbf, 0x5b, 0x58, 0x80, 0x00, 0x77, 0xff, 0xf1, - 0x03, 0x02, 0xdf, 0xb7, 0x56, 0xfc, 0x01, 0xbf, 0xff, 0xe8, 0x00, 0x01, 0x3f, 0x7f, 0xc7, 0x29, - 0x02, 0x63, 0xc7, 0xf1, 0xff, 0x02, 0xdf, 0x8b, 0xf8, 0x1b, 0x04, 0x07, 0xfe, 0xdf, 0xff, 0x04, - 0x01, 0xff, 0xff, 0xff, 0x04, 0xf8, 0x7f, 0xef, 0xff, 0x0b, 0x07, 0x8b, 0xff, 0x02, 0x0c, 0x00, - 0x7f, 0x74, 0x1f, 0x08, 0x00, 0x00, 0xff, 0xf0, 0x07, 0x00, 0x00, 0x0f, 0xc0, 0x71, 0x00, 0x00, - 0x10, 0x38, 0x02, 0x80, 0x00, 0x2f, 0x8c, 0x5e, 0x80, 0x00, 0x60, 0x72, 0xe4, 0x80, 0x00, 0x40, - 0x99, 0x85, 0x00, 0x00, 0x43, 0x79, 0x09, 0x00, 0x00, 0x45, 0xfc, 0x0a, 0x00, 0x00, 0x4b, 0xb4, - 0x12, 0x00, 0x00, 0x2f, 0x94, 0x92, 0x00, 0x00, 0x2f, 0x12, 0xe4, 0x00, 0x01, 0x17, 0x9a, 0xd8, - 0x00, 0x00, 0x17, 0x89, 0xa8, 0x00, 0x00, 0x0f, 0xdd, 0x90, 0x00, 0x01, 0x05, 0xfc, 0x60, 0x00, - 0x00, 0x03, 0xfc, 0xa0, 0x00, 0x21, 0x01, 0x7e, 0xc0, 0x00, 0x09, 0x00, 0xef, 0x40, 0x00, 0x05, - 0x40, 0x3f, 0x80, 0x00, 0x03, 0x80, 0x0b, 0x00, 0x05, 0xbe, 0xea, 0x45, 0x00, 0x00, 0x01, 0x80, - 0x02, 0x00, 0x00, 0x03, 0x40, 0x02, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x31, 0x10, 0x01, - 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x0f, 0x79, 0x00, 0x00, 0x00, 0x77, 0xb7, 0x00, 0x00, 0x0f, - 0x98, 0xb3, 0x00, 0x09, 0xf0, 0x26, 0x6a, 0x00, 0x01, 0x02, 0xbc, 0x4a, 0x00, 0x09, 0xa7, 0xfd, - 0x84, 0x00, 0x82, 0xf9, 0xf7, 0x18, 0x00, 0x0b, 0xe1, 0xbc, 0x70, 0x00, 0x2a, 0x8f, 0xf0, 0xc0, - 0x00, 0x1a, 0xff, 0xe3, 0x00, 0x05, 0xf4, 0xff, 0x8c, 0x00, 0x00, 0x1c, 0xec, 0xf0, 0x00, 0x00, - 0x2c, 0xff, 0x00, 0x00, 0x00, 0x4c, 0xe0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x01, 0x00, 0x0f, 0x40, 0x0e, 0x00, 0x00, 0x30, 0x00, 0x11, 0xc1, 0x10, 0x4f, 0x48, - 0x2c, 0x31, 0x00, 0x70, 0x50, 0x43, 0x89, 0x40, 0x80, 0x2d, 0x40, 0x75, 0x80, 0x83, 0xd0, 0x81, - 0x92, 0xea, 0x87, 0xc0, 0x87, 0x8b, 0x80, 0x47, 0x80, 0x9f, 0xcb, 0x00, 0x4d, 0x41, 0x3d, 0xcb, - 0x20, 0x2e, 0x41, 0x7d, 0x93, 0x00, 0x3e, 0x42, 0xf7, 0x95, 0x00, 0x16, 0x27, 0xe7, 0xa4, 0x00, - 0x0e, 0x95, 0xcf, 0x29, 0x00, 0x05, 0x4f, 0xdf, 0x48, 0x00, 0x02, 0x6f, 0xfe, 0x91, 0x00, 0x01, - 0xfb, 0xff, 0xa0, 0x00, 0x00, 0xdf, 0xfb, 0x40, 0x00, 0x00, 0xff, 0xb6, 0x80, 0x00, 0x00, 0xbf, - 0xa4, 0x80, 0x00, 0x40, 0xdb, 0x45, 0x00, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x40, 0x00, 0x0a, - 0x00, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x04, 0x42, 0x80, 0x8a, 0x00, 0x00, 0x47, 0x80, 0x01, 0x00, - 0x01, 0x4f, 0x80, 0x25, 0x00, 0x00, 0xf7, 0x52, 0x45, 0x01, 0x5f, 0xaf, 0x41, 0xa8, 0x80, 0x00, - 0x3f, 0x56, 0xf2, 0x80, 0x01, 0x5f, 0xfb, 0xb6, 0x80, 0x02, 0xbb, 0xff, 0xfa, 0x40, 0x00, 0xbf, - 0xe6, 0xfd, 0x40, 0x09, 0x6f, 0xcf, 0xf9, 0x40, 0x01, 0x5d, 0xe7, 0xf8, 0x80, 0x01, 0x1f, 0x7f, - 0xf1, 0x00, 0x02, 0x17, 0x12, 0x8e, 0x00, 0x02, 0x0f, 0x00, 0x1c, 0x00, 0x02, 0x04, 0x03, 0xe0, - 0x00, 0x03, 0x01, 0xfc, 0x00, 0x00, 0x01, 0xfe, 0xf0, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x3e, - 0xa8, 0x00, 0xec, 0x00, 0x41, 0xf0, 0x00, 0x72, 0x00, 0xbc, 0xdf, 0x60, 0xca, 0x01, 0x03, 0x30, - 0x00, 0xc9, 0x02, 0x60, 0xa8, 0x00, 0xe5, 0x04, 0xf0, 0xa0, 0x00, 0xf4, 0x85, 0xcc, 0xa2, 0x00, - 0xfa, 0x4f, 0xce, 0x20, 0x00, 0x7f, 0x5b, 0x8d, 0x40, 0x00, 0x7b, 0x3f, 0x9e, 0x40, 0x00, 0x7f, - 0xbf, 0xba, 0xa0, 0x00, 0xdb, 0xfd, 0x74, 0xa0, 0x00, 0xbf, 0xb7, 0xf5, 0x00, 0x00, 0x4d, 0xf7, - 0x49, 0x20, 0x00, 0x19, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x54, 0xb2, 0x00, 0x00, 0x42, 0x01, 0x2c, - 0x00, 0x00, 0x20, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x40, 0x10, 0xc5, 0x00, - 0x00, 0xa1, 0x04, 0x24, 0x80, 0x00, 0x02, 0x00, 0x2a, 0x40, 0x00, 0x74, 0x20, 0x6b, 0x40, 0x00, - 0x69, 0x59, 0x5f, 0x21, 0x00, 0xad, 0x5b, 0xde, 0xa0, 0x00, 0xfd, 0xaf, 0xf7, 0x91, 0x00, 0x6f, - 0x27, 0x73, 0x50, 0x00, 0xff, 0x47, 0xfb, 0xc9, 0x00, 0xde, 0x85, 0xb9, 0xa9, 0x10, 0xbd, 0x03, - 0xbc, 0xa5, 0x00, 0x3d, 0x02, 0xfc, 0xd5, 0x40, 0x3a, 0x02, 0xdc, 0xd5, 0x80, 0xba, 0x01, 0x5c, - 0xd2, 0xf5, 0xf4, 0x01, 0x3c, 0xcb, 0x80, 0x68, 0x00, 0x8b, 0xcd, 0x00, 0xb0, 0x00, 0x82, 0x89, - 0x20, 0xc0, 0x00, 0x80, 0x10, 0x00, 0x80, 0x00, 0x40, 0xe1, 0x00, 0x00, 0x00, 0x3f, 0x01, 0x00, - 0x47, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x47, 0x47, 0x47, 0x47, 0x47, 0x07, 0x47, 0x47, - 0x47, 0x47, 0x47, 0x07, 0x47, 0x47, 0x07, 0x07, 0x47, 0x07, 0x47, 0x07, 0x47, 0x47, 0x47, 0x07, - 0x07, 0x47, 0x07, 0x07, 0x47, 0x47, 0x07, 0x07, 0x05, 0x45, 0x47, 0x47, 0x47, 0x07, 0x45, 0x47, - 0x07, 0x47, 0x07, 0x07, 0x07, 0x07, 0x47, 0x07, 0x47, 0x07, 0x07, 0x07, 0x05, 0x45, 0x45, 0x45, - 0x05, 0x05, 0x45, 0x05, 0x45, 0x45, 0x05, 0x05, 0x05, 0x05, 0x45, 0x45, 0x45, 0x45, 0x05, 0x07, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x45, 0x45, 0x05, 0x45, 0x45, 0x05, 0x45, 0x45, 0x05, - 0x45, 0x05, 0x45, 0x07, 0x78, 0x41, 0xa6, 0xe6, 0x66, 0x00, 0x78, 0x41, 0x9e, 0xde, 0x5e, 0x00, - 0xfe, 0x42, 0x9e, 0xde, 0x5e, 0x00, 0xfe, 0x42, 0x96, 0xd6, 0x56, 0x00, 0x82, 0x43, 0x9e, 0xde, - 0x5e, 0x00, 0x82, 0x43, 0x96, 0xd6, 0x56, 0x00, 0xac, 0x46, 0xa6, 0xe6, 0x66, 0x00, 0xac, 0x46, - 0x9e, 0xde, 0x5e, 0x00, 0xdb, 0x48, 0xbe, 0xfe, 0x7e, 0x00, 0xdb, 0x48, 0xb6, 0xf6, 0x76, 0x00, - 0xe8, 0x49, 0xa6, 0xe6, 0x66, 0x00, 0xe8, 0x49, 0x9e, 0xde, 0x5e, 0x00, 0x25, 0x4e, 0xbe, 0xfe, - 0x7e, 0x00, 0x25, 0x4e, 0xb6, 0xf6, 0x76, 0x00, 0x53, 0x4e, 0xbe, 0xfe, 0x7e, 0x00, 0x53, 0x4e, - 0xb6, 0xf6, 0x76, 0x00, 0x8e, 0x56, 0x8e, 0xce, 0x4e, 0x00, 0x8e, 0x56, 0x86, 0xc6, 0x46, 0x00, - 0xa1, 0x56, 0xa6, 0xe6, 0x66, 0x00, 0xa1, 0x56, 0x9e, 0xde, 0x5e, 0x00, 0x78, 0x42, 0xa6, 0xe6, - 0x66, 0x00, 0x78, 0x42, 0x9e, 0xde, 0x5e, 0x00, 0xfe, 0x43, 0x9e, 0xde, 0x5e, 0x00, 0xfe, 0x43, - 0x96, 0xd6, 0x56, 0x00, 0x82, 0x44, 0x9e, 0xde, 0x5e, 0x00, 0x82, 0x44, 0x96, 0xd6, 0x56, 0x00, - 0xac, 0x47, 0xa6, 0xe6, 0x66, 0x00, 0xac, 0x47, 0x9e, 0xde, 0x5e, 0x00, 0xdb, 0x49, 0xbe, 0xfe, - 0x7e, 0x00, 0xdb, 0x49, 0xb6, 0xf6, 0x76, 0x00, 0xe8, 0x4a, 0xa6, 0xe6, 0x66, 0x00, 0xe8, 0x4a, - 0x9e, 0xde, 0x5e, 0x00, 0x25, 0x4f, 0xbe, 0xfe, 0x7e, 0x00, 0x25, 0x4f, 0xb6, 0xf6, 0x76, 0x00, - 0x53, 0x4f, 0xbe, 0xfe, 0x7e, 0x00, 0x53, 0x4f, 0xb6, 0xf6, 0x76, 0x00, 0x8e, 0x57, 0x8e, 0xce, - 0x4e, 0x00, 0x8e, 0x57, 0x86, 0xc6, 0x46, 0x00, 0xa1, 0x57, 0xa6, 0xe6, 0x66, 0x00, 0xa1, 0x57, - 0x9e, 0xde, 0x5e, 0x00, 0xad, 0x43, 0x8e, 0xce, 0x4e, 0x00, 0xe2, 0x43, 0xb6, 0xf6, 0x76, 0x00, - 0xea, 0x43, 0xa6, 0xe6, 0x66, 0x00, 0x46, 0x44, 0xbe, 0xfe, 0x7e, 0x00, 0x2f, 0x48, 0x86, 0xc6, - 0x46, 0x00, 0xa7, 0x49, 0x9e, 0xde, 0x5e, 0x00, 0xea, 0x49, 0x8e, 0xce, 0x4e, 0x00, 0x6c, 0x4a, - 0xb6, 0xf6, 0x76, 0x00, 0xb4, 0x4b, 0x8e, 0xce, 0x4e, 0x00, 0xe3, 0x4b, 0x86, 0xc6, 0x46, 0x00, - 0x3a, 0x4d, 0xbe, 0xfe, 0x7e, 0x00, 0x80, 0x4d, 0x96, 0xd6, 0x56, 0x00, 0x9d, 0x4d, 0x86, 0xc6, - 0x46, 0x00, 0xb0, 0x4d, 0x96, 0xd6, 0x56, 0x00, 0xd2, 0x52, 0x86, 0xc6, 0x46, 0x00, 0x2d, 0x53, - 0xae, 0xee, 0x6e, 0x00, 0x32, 0x53, 0x9e, 0xde, 0x5e, 0x00, 0x17, 0x54, 0xa6, 0xe6, 0x66, 0x00, - 0x9c, 0x55, 0xb6, 0xf6, 0x76, 0x00, 0x89, 0x57, 0xb6, 0xf6, 0x76, 0x00, 0x2c, 0x40, 0x06, 0x48, - 0xd4, 0x78, 0x49, 0x50, 0x07, 0x06, 0x46, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc3, 0x59, 0xeb, 0xc3, 0xda, 0xeb, 0xc3, 0x44, 0xed, 0xf3, 0x2a, 0x35, 0xf0, 0x22, 0x60, 0xec, - 0x2a, 0x37, 0xf0, 0x22, 0x74, 0xec, 0x2a, 0x39, 0xf0, 0x22, 0x88, 0xec, 0x3e, 0x01, 0x32, 0x62, - 0xec, 0x32, 0x76, 0xec, 0x32, 0x8a, 0xec, 0x3e, 0x08, 0x32, 0x63, 0xec, 0x32, 0x77, 0xec, 0x32, - 0x8b, 0xec, 0x21, 0x20, 0xec, 0x11, 0x20, 0x00, 0x22, 0xfa, 0xeb, 0x19, 0x22, 0xfc, 0xeb, 0x19, - 0x22, 0xfe, 0xeb, 0x21, 0x00, 0x00, 0x22, 0x6e, 0xec, 0x22, 0x82, 0xec, 0x22, 0x96, 0xec, 0x22, - 0x70, 0xec, 0x22, 0x84, 0xec, 0x22, 0x98, 0xec, 0xaf, 0x32, 0xf4, 0xeb, 0xc9, 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, 0x3e, 0xff, 0x16, 0x07, 0xcd, 0x9b, - 0xed, 0xcd, 0x85, 0xed, 0x00, 0xc9, 0xe3, 0xff, 0xf6, 0x03, 0xbd, 0x00, 0x0d, 0xff, 0x0a, 0x0d, - 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x03, 0x4f, 0x69, 0x5c, 0xc4, 0x16, 0xc4, 0x3c, 0xc4, 0x5c, 0xc4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xc7, 0x1c, 0xd8, 0x94, 0xc7, 0x94, 0xc7, - 0x16, 0x95, 0xd7, 0xc7, 0xb5, 0xc6, 0x68, 0xaa, 0x5a, 0xaa, 0xfe, 0x07, 0x58, 0xaa, 0xfe, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xc7, 0x1c, 0xd8, - 0x94, 0xc7, 0xb5, 0xc6, 0xb5, 0xc6, 0x63, 0x8c, 0x94, 0xc7, 0xb5, 0xc6, 0x05, 0xab, 0xe6, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xc7, 0x94, 0xc7, 0x00, 0x94, - 0x94, 0xc7, 0x94, 0xc7, 0x57, 0x89, 0x94, 0xc7, 0x57, 0x89, 0xb5, 0xc6, 0xc5, 0xab, 0xe6, 0x02, - 0xfa, 0xaa, 0x05, 0x01, 0xd4, 0xaa, 0x01, 0xe3, 0xff, 0x0a, 0xcc, 0xaa, 0x05, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x4d, 0x0f, 0x09, 0xab, 0x0f, 0x08, 0x9d, 0xab, 0x01, 0xf6, 0x03, 0x0d, 0x97, 0xab, - 0xa7, 0xab, 0x00, 0x00, 0xa2, 0xab, 0x19, 0x08, 0xcf, 0xab, 0x05, 0x08, 0xd0, 0xad, 0x01, 0xbd, - 0x00, 0x0a, 0xc4, 0xad, 0xda, 0xad, 0x00, 0x00, 0xd5, 0xad, 0x36, 0x04, 0xbf, 0x0f, 0xdc, 0x0e, - 0x07, 0x0e, 0x3d, 0x0d, 0x7f, 0x0c, 0xcc, 0x0b, 0x22, 0x0b, 0x82, 0x0a, 0xeb, 0x09, 0x5d, 0x09, - 0xd6, 0x08, 0x57, 0x08, 0xdf, 0x07, 0x6e, 0x07, 0x03, 0x07, 0x9f, 0x06, 0x40, 0x06, 0xe6, 0x05, - 0x91, 0x05, 0x41, 0x05, 0xf6, 0x04, 0xae, 0x04, 0x6b, 0x04, 0x2c, 0x04, 0xf0, 0x03, 0xb7, 0x03, - 0x82, 0x03, 0x4f, 0x03, 0x20, 0x03, 0xf3, 0x02, 0xc8, 0x02, 0xa1, 0x02, 0x7b, 0x02, 0x57, 0x02, - 0x36, 0x02, 0x16, 0x02, 0xf8, 0x01, 0xdc, 0x01, 0xc1, 0x01, 0xa8, 0x01, 0x90, 0x01, 0x79, 0x01, - 0x64, 0x01, 0x50, 0x01, 0x3d, 0x01, 0x2c, 0x01, 0x1b, 0x01, 0x0b, 0x01, 0xfc, 0x00, 0xee, 0x00, - 0xe0, 0x00, 0xd4, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x96, 0x00, - 0x8d, 0x00, 0x85, 0x00, 0x7e, 0x00, 0x77, 0x00, 0x70, 0x00, 0x6a, 0x00, 0x64, 0x00, 0x5e, 0x00, - 0x59, 0x00, 0x54, 0x00, 0x4f, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x43, 0x00, 0x3f, 0x00, 0x3b, 0x00, - 0x38, 0x00, 0x35, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x2d, 0x00, 0x2a, 0x00, 0x28, 0x00, 0x25, 0x00, - 0x23, 0x00, 0x21, 0x00, 0xdd, 0x21, 0x60, 0xec, 0x2a, 0xfa, 0xeb, 0x3e, 0x01, 0xcd, 0xb0, 0xed, - 0x22, 0xfa, 0xeb, 0xdd, 0x21, 0x74, 0xec, 0x2a, 0xfc, 0xeb, 0x3e, 0x02, 0xcd, 0xb0, 0xed, 0x22, - 0xfc, 0xeb, 0xdd, 0x21, 0x88, 0xec, 0x2a, 0xfe, 0xeb, 0x3e, 0x03, 0xcd, 0xb0, 0xed, 0x22, 0xfe, - 0xeb, 0x3a, 0x8b, 0xec, 0x07, 0x47, 0x3a, 0x77, 0xec, 0xb0, 0x07, 0x47, 0x3a, 0x63, 0xec, 0xb0, - 0x16, 0x07, 0xcd, 0x9b, 0xed, 0x16, 0x0d, 0x21, 0xf3, 0xeb, 0x7a, 0x01, 0xfd, 0xff, 0xed, 0x79, - 0x7e, 0x06, 0xbf, 0xed, 0x79, 0x2b, 0x15, 0xf2, 0x8a, 0xed, 0xc9, 0x01, 0xe6, 0xeb, 0x6a, 0x26, - 0x00, 0x09, 0x77, 0xc9, 0xed, 0x73, 0xf8, 0xeb, 0x2a, 0xf8, 0xeb, 0xed, 0x7b, 0xf6, 0xeb, 0xc9, - 0xed, 0x73, 0xf6, 0xeb, 0xf9, 0x32, 0xf5, 0xeb, 0xdd, 0x35, 0x02, 0xca, 0xbf, 0xee, 0xdd, 0x35, - 0x06, 0x20, 0x36, 0xdd, 0x6e, 0x04, 0xdd, 0x66, 0x05, 0x7e, 0xfe, 0x80, 0x20, 0x0c, 0x23, 0x5e, - 0x23, 0x56, 0xdd, 0x73, 0x04, 0xdd, 0x72, 0x05, 0x18, 0xe9, 0xfe, 0x1e, 0x38, 0x0c, 0xd6, 0x32, - 0xdd, 0x77, 0x09, 0xdd, 0x36, 0x06, 0x01, 0x23, 0x18, 0x09, 0xdd, 0x77, 0x09, 0x23, 0x7e, 0xdd, - 0x77, 0x06, 0x23, 0xdd, 0x75, 0x04, 0xdd, 0x74, 0x05, 0xdd, 0x7e, 0x07, 0xdd, 0xb6, 0x08, 0xca, - 0x82, 0xee, 0xdd, 0xcb, 0x0e, 0x56, 0xc2, 0x82, 0xee, 0xdd, 0x6e, 0x0c, 0xdd, 0x66, 0x0d, 0x7e, - 0x23, 0xdd, 0x75, 0x0c, 0xdd, 0x74, 0x0d, 0xfe, 0x80, 0x20, 0x06, 0x7e, 0x23, 0x66, 0x6f, 0x18, - 0xee, 0xfe, 0x82, 0xc2, 0x2d, 0xee, 0xdd, 0xcb, 0x0e, 0xde, 0xc3, 0x0f, 0xee, 0xfe, 0x83, 0xc2, - 0x39, 0xee, 0xdd, 0xcb, 0x0e, 0x9e, 0xc3, 0x0f, 0xee, 0xfe, 0x84, 0xc2, 0x49, 0xee, 0x3e, 0x09, - 0xdd, 0xae, 0x03, 0xdd, 0x77, 0x03, 0xc3, 0x0f, 0xee, 0xdd, 0xcb, 0x0e, 0x5e, 0xca, 0x6b, 0xee, - 0xdd, 0x86, 0x12, 0xdd, 0x77, 0x12, 0x3d, 0x87, 0x5f, 0x16, 0x00, 0x21, 0x9c, 0xec, 0x19, 0x7e, - 0xdd, 0x77, 0x07, 0x23, 0x7e, 0xdd, 0x77, 0x08, 0xc3, 0x82, 0xee, 0x5f, 0x16, 0x00, 0xdd, 0x6e, - 0x07, 0xdd, 0x66, 0x08, 0xe6, 0x80, 0xca, 0x7b, 0xee, 0x16, 0xff, 0x19, 0xdd, 0x75, 0x07, 0xdd, - 0x74, 0x08, 0x3a, 0xf4, 0xeb, 0x16, 0x06, 0xcd, 0x9b, 0xed, 0xdd, 0xcb, 0x0e, 0x96, 0x3a, 0xf5, - 0xeb, 0xc6, 0x07, 0x57, 0xdd, 0x7e, 0x07, 0xdd, 0xb6, 0x08, 0x28, 0x03, 0xdd, 0x7e, 0x09, 0xcd, - 0x9b, 0xed, 0x3a, 0xf5, 0xeb, 0x3d, 0x87, 0x57, 0xdd, 0x7e, 0x07, 0xcd, 0x9b, 0xed, 0x14, 0xdd, - 0x7e, 0x08, 0xcd, 0x9b, 0xed, 0xc3, 0xa4, 0xed, 0xdd, 0x75, 0x00, 0xdd, 0x74, 0x01, 0xc9, 0xdd, - 0x6e, 0x00, 0xdd, 0x66, 0x01, 0x7e, 0x23, 0xcd, 0xb8, 0xee, 0xcb, 0x7f, 0xc2, 0x3c, 0xef, 0x22, - 0xf8, 0xeb, 0xb7, 0x28, 0x1b, 0xdd, 0x86, 0x0f, 0xdd, 0x77, 0x12, 0xdd, 0xcb, 0x0e, 0x9e, 0x3d, - 0x87, 0x5f, 0x16, 0x00, 0x21, 0x9c, 0xec, 0x19, 0x5e, 0x23, 0x56, 0x2a, 0xf8, 0xeb, 0x18, 0x03, - 0x11, 0x00, 0x00, 0x7e, 0x23, 0xcd, 0xb8, 0xee, 0xdd, 0x77, 0x02, 0xdd, 0x73, 0x07, 0xdd, 0x72, - 0x08, 0xdd, 0x7e, 0x10, 0xdd, 0x77, 0x0c, 0xdd, 0x7e, 0x11, 0xdd, 0x77, 0x0d, 0xdd, 0xcb, 0x0e, - 0xd6, 0xdd, 0xcb, 0x0e, 0x4e, 0xc2, 0xbe, 0xed, 0xdd, 0xcb, 0x0e, 0x46, 0xca, 0x23, 0xef, 0xdd, - 0xcb, 0x0e, 0xce, 0xdd, 0x4e, 0x0a, 0xdd, 0x46, 0x0b, 0x0a, 0xdd, 0x77, 0x09, 0x03, 0x0a, 0x03, - 0xdd, 0x77, 0x06, 0xdd, 0x71, 0x04, 0xdd, 0x70, 0x05, 0xc3, 0x82, 0xee, 0xe6, 0x7f, 0x22, 0xf8, - 0xeb, 0x87, 0x5f, 0x16, 0x00, 0x21, 0x52, 0xef, 0x19, 0x7e, 0x23, 0x66, 0x6f, 0xe5, 0x2a, 0xf8, - 0xeb, 0xc9, 0x70, 0xef, 0x7c, 0xef, 0x8a, 0xef, 0x94, 0xef, 0xa6, 0xef, 0xb1, 0xef, 0xde, 0xef, - 0xbc, 0xef, 0xcc, 0xef, 0xd7, 0xef, 0xee, 0xef, 0xfc, 0xef, 0x0a, 0xf0, 0x17, 0xf0, 0x27, 0xf0, - 0x7e, 0xdd, 0x77, 0x00, 0x23, 0x7e, 0xdd, 0x77, 0x01, 0xc3, 0xbf, 0xee, 0x7e, 0xdd, 0x77, 0x00, - 0x23, 0x7e, 0xdd, 0x77, 0x01, 0x23, 0xe5, 0xc3, 0xbf, 0xee, 0x46, 0xc5, 0x23, 0xe5, 0xcd, 0xb8, - 0xee, 0xc3, 0xbf, 0xee, 0xd1, 0xc1, 0x10, 0x03, 0xc3, 0xbf, 0xee, 0xc5, 0xd5, 0xdd, 0x73, 0x00, - 0xdd, 0x72, 0x01, 0xc3, 0xbf, 0xee, 0x7e, 0x23, 0x32, 0xf4, 0xeb, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, - 0xee, 0x7e, 0x23, 0xcd, 0xb8, 0xee, 0xdd, 0x77, 0x03, 0xc3, 0xbf, 0xee, 0x7e, 0xdd, 0x77, 0x0a, - 0x23, 0x7e, 0xdd, 0x77, 0x0b, 0x23, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x7e, 0x23, 0xcd, 0xb8, - 0xee, 0xdd, 0x77, 0x0f, 0xc3, 0xbf, 0xee, 0xe1, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x7e, 0xdd, - 0x77, 0x10, 0x23, 0x7e, 0xdd, 0x77, 0x11, 0x23, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0xdd, 0xcb, - 0x0e, 0xc6, 0xdd, 0xcb, 0x0e, 0x8e, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0xdd, 0xcb, 0x0e, 0x86, - 0xdd, 0xcb, 0x0e, 0x8e, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x5e, 0x23, 0x56, 0x23, 0xcd, 0xb8, - 0xee, 0x01, 0xbf, 0xee, 0xc5, 0xd5, 0xc9, 0x3a, 0xf4, 0xeb, 0x86, 0xe6, 0x0f, 0x32, 0xf4, 0xeb, - 0x23, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x7e, 0xdd, 0x86, 0x0f, 0xdd, 0x77, 0x0f, 0x23, 0xcd, - 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x99, 0xf5, 0x96, 0xf4, 0x3b, 0xf0, 0x82, 0x04, 0x81, 0x57, 0xf4, - 0x83, 0x81, 0x6d, 0xf3, 0x81, 0x6d, 0xf3, 0x81, 0x00, 0xf4, 0x81, 0x33, 0xf3, 0x81, 0x33, 0xf3, - 0x81, 0x17, 0xf3, 0x81, 0x6a, 0xf2, 0x81, 0x6a, 0xf2, 0x88, 0x00, 0x81, 0xba, 0xf2, 0x81, 0xba, - 0xf2, 0x88, 0x02, 0x81, 0xba, 0xf2, 0x88, 0x00, 0x81, 0xef, 0xf2, 0x82, 0x04, 0x81, 0x49, 0xf2, - 0x83, 0x81, 0x17, 0xf3, 0x81, 0x49, 0xf2, 0x81, 0x49, 0xf2, 0x81, 0x1a, 0xf3, 0x81, 0x49, 0xf2, - 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x2b, 0x23, 0x86, 0x9b, 0xf7, 0x2b, 0x0c, 0x86, 0x5e, 0xf7, - 0x2d, 0x48, 0x2b, 0x0c, 0x2d, 0x0c, 0x32, 0x0c, 0x30, 0x0c, 0x2d, 0x18, 0x30, 0x24, 0x86, 0x9b, - 0xf7, 0x30, 0x0c, 0x86, 0x5e, 0xf7, 0x32, 0x48, 0x32, 0x0c, 0x37, 0x0c, 0x36, 0x0c, 0x32, 0x0c, - 0x2d, 0x18, 0x2b, 0x24, 0x86, 0x9b, 0xf7, 0x2b, 0x0c, 0x86, 0x5e, 0xf7, 0x2d, 0x48, 0x32, 0x0c, - 0x86, 0x97, 0xf7, 0x32, 0x0c, 0x86, 0x5e, 0xf7, 0x32, 0x0c, 0x30, 0x0c, 0x2d, 0x18, 0x32, 0x24, - 0x86, 0x97, 0xf7, 0x32, 0x0c, 0x86, 0x5e, 0xf7, 0x30, 0x48, 0x32, 0x0c, 0x34, 0x0c, 0x37, 0x0c, - 0x36, 0x0c, 0x37, 0x0c, 0x39, 0x0c, 0x37, 0x24, 0x86, 0x9b, 0xf7, 0x37, 0x0c, 0x86, 0x5e, 0xf7, - 0x39, 0x30, 0x37, 0x06, 0x39, 0x0c, 0x37, 0x06, 0x39, 0x0c, 0x37, 0x06, 0x36, 0x0c, 0x37, 0x06, - 0x36, 0x0c, 0x34, 0x0c, 0x32, 0x0c, 0x30, 0x06, 0x32, 0x0c, 0x30, 0x06, 0x32, 0x0c, 0x30, 0x06, - 0x2f, 0x0c, 0x30, 0x06, 0x2f, 0x0c, 0x2d, 0x0c, 0x2b, 0x0c, 0x2d, 0x06, 0x2d, 0x0c, 0x2b, 0x06, - 0x2d, 0x0c, 0x2f, 0x06, 0x30, 0x0c, 0x32, 0x06, 0x34, 0x0c, 0x36, 0x0c, 0x37, 0x0c, 0x3b, 0x24, - 0x86, 0x97, 0xf7, 0x3b, 0x0c, 0x86, 0x5e, 0xf7, 0x39, 0x48, 0x37, 0x0c, 0x39, 0x0c, 0x3c, 0x06, - 0x3c, 0x06, 0x39, 0x0c, 0x3e, 0x0c, 0x3c, 0x0c, 0x40, 0x24, 0x86, 0x97, 0xf7, 0x40, 0x0c, 0x86, - 0x5e, 0xf7, 0x3e, 0x60, 0x00, 0x06, 0x3e, 0x06, 0x3e, 0x06, 0x40, 0x06, 0x41, 0x06, 0x40, 0x06, - 0x3c, 0x06, 0x3e, 0x07, 0x81, 0xd9, 0xf1, 0x81, 0x11, 0xf2, 0x82, 0x02, 0x3d, 0x0b, 0x36, 0x0c, - 0x3b, 0x0c, 0x36, 0x06, 0x3a, 0x0c, 0x36, 0x06, 0x38, 0x0c, 0x36, 0x06, 0x36, 0x06, 0x3b, 0x06, - 0x3d, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x36, 0x0c, 0x3b, 0x0c, 0x36, 0x06, 0x3a, 0x0c, 0x36, 0x06, - 0x38, 0x0c, 0x36, 0x06, 0x36, 0x06, 0x3b, 0x06, 0x3d, 0x07, 0x83, 0x81, 0x6a, 0xf2, 0x81, 0x49, - 0xf2, 0x81, 0xc3, 0xf1, 0x81, 0x1a, 0xf3, 0x81, 0x4c, 0xf2, 0x81, 0xba, 0xf2, 0x81, 0x4c, 0xf2, - 0x88, 0x02, 0x81, 0xba, 0xf2, 0x88, 0x00, 0x81, 0xef, 0xf2, 0x81, 0x4c, 0xf2, 0x81, 0xc3, 0xf1, - 0x80, 0x3b, 0xf0, 0x87, 0xff, 0xf6, 0x46, 0x18, 0x00, 0x30, 0x87, 0x33, 0xf7, 0x86, 0x1b, 0xf7, - 0x28, 0x0b, 0x00, 0x01, 0x28, 0x0b, 0x00, 0x01, 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x82, - 0x02, 0x40, 0x0b, 0x39, 0x0c, 0x3e, 0x0c, 0x39, 0x06, 0x3c, 0x0c, 0x39, 0x06, 0x3b, 0x0c, 0x3c, - 0x06, 0x39, 0x06, 0x3c, 0x06, 0x3e, 0x06, 0x40, 0x06, 0x40, 0x06, 0x39, 0x0c, 0x3e, 0x0c, 0x39, - 0x06, 0x3c, 0x0c, 0x39, 0x06, 0x3b, 0x0c, 0x3c, 0x06, 0x39, 0x06, 0x3c, 0x06, 0x3e, 0x07, 0x83, - 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x82, 0x02, 0x40, 0x0b, 0x39, 0x0c, 0x3f, 0x0c, 0x39, - 0x06, 0x3d, 0x0c, 0x3d, 0x06, 0x3b, 0x0c, 0x3d, 0x06, 0x39, 0x06, 0x3d, 0x06, 0x3f, 0x06, 0x40, - 0x06, 0x40, 0x06, 0x39, 0x0c, 0x3f, 0x0c, 0x39, 0x06, 0x3d, 0x0c, 0x3d, 0x06, 0x3b, 0x0c, 0x3d, - 0x06, 0x39, 0x06, 0x3d, 0x06, 0x3f, 0x07, 0x83, 0x89, 0x81, 0x4c, 0xf2, 0x86, 0x45, 0xf7, 0x87, - 0x9f, 0xf7, 0x82, 0x04, 0x46, 0x06, 0x83, 0x46, 0x0c, 0x44, 0x06, 0x46, 0x0a, 0x00, 0x02, 0x44, - 0x06, 0x46, 0x0a, 0x00, 0x02, 0x46, 0x0c, 0x44, 0x0c, 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, - 0x36, 0x23, 0x86, 0x97, 0xf7, 0x36, 0x0c, 0x86, 0x5e, 0xf7, 0x34, 0x18, 0x31, 0x0c, 0x2f, 0x18, - 0x2e, 0x0c, 0x2e, 0x0c, 0x81, 0xef, 0xf3, 0x8e, 0xfd, 0x81, 0xef, 0xf3, 0x8e, 0x03, 0x87, 0x4f, - 0xf7, 0x86, 0x5e, 0xf7, 0x2e, 0x0c, 0x2f, 0x0c, 0x31, 0x0c, 0x31, 0x12, 0x31, 0x12, 0x31, 0x0c, - 0x34, 0x12, 0x31, 0x12, 0x2f, 0x0c, 0x31, 0x18, 0x86, 0x97, 0xf7, 0x31, 0x30, 0x81, 0xef, 0xf3, - 0x8e, 0xfd, 0x81, 0xef, 0xf3, 0x00, 0x01, 0x8e, 0x03, 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, - 0x32, 0x18, 0x81, 0xef, 0xf3, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x32, 0x06, 0x32, 0x06, 0x34, - 0x12, 0x32, 0x12, 0x30, 0x0c, 0x32, 0x18, 0x81, 0xef, 0xf3, 0x8e, 0xfb, 0x81, 0xef, 0xf3, 0x81, - 0x87, 0xf5, 0x86, 0x1b, 0xf7, 0x87, 0x33, 0xf7, 0x8e, 0x05, 0x2b, 0x0c, 0x28, 0x0c, 0x89, 0x87, - 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x34, 0x18, 0x81, 0xef, 0xf3, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, - 0x34, 0x06, 0x34, 0x06, 0x36, 0x12, 0x34, 0x12, 0x32, 0x0c, 0x34, 0x12, 0x36, 0x12, 0x38, 0x0c, - 0x36, 0x12, 0x38, 0x12, 0x39, 0x0c, 0x89, 0x81, 0x1a, 0xf3, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, - 0x82, 0x02, 0x82, 0x02, 0x30, 0x06, 0x2f, 0x0c, 0x2e, 0x06, 0x2d, 0x0c, 0x83, 0x30, 0x0c, 0x32, - 0x0c, 0x83, 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x7c, 0xf7, 0x82, 0x04, 0x30, 0x06, 0x2f, 0x0c, 0x2e, - 0x06, 0x2d, 0x0c, 0x83, 0x30, 0x06, 0x2f, 0x0c, 0x2d, 0x06, 0x35, 0x0c, 0x34, 0x0c, 0x82, 0x02, - 0x35, 0x06, 0x34, 0x0c, 0x33, 0x06, 0x32, 0x0c, 0x83, 0x35, 0x0c, 0x34, 0x0c, 0x82, 0x02, 0x2f, - 0x06, 0x2e, 0x0c, 0x2d, 0x06, 0x2c, 0x0c, 0x83, 0x30, 0x0c, 0x2f, 0x0c, 0x89, 0x87, 0x4f, 0xf7, - 0x86, 0x5e, 0xf7, 0x88, 0x00, 0x00, 0x05, 0x2d, 0x06, 0x2d, 0x0c, 0x2d, 0x0c, 0x2d, 0x0c, 0x2d, - 0x18, 0x2d, 0x1e, 0x2d, 0x06, 0x34, 0x0c, 0x34, 0x0c, 0x34, 0x0c, 0x34, 0x18, 0x81, 0xef, 0xf3, - 0x00, 0x0c, 0x35, 0x18, 0x34, 0x18, 0x35, 0x18, 0x34, 0x0c, 0x32, 0x12, 0x2f, 0x06, 0x2f, 0x0c, - 0x2f, 0x0c, 0x2f, 0x0c, 0x2f, 0x18, 0x81, 0xef, 0xf3, 0x00, 0x0c, 0x00, 0x06, 0x30, 0x06, 0x30, - 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x18, 0x30, 0x1e, 0x30, 0x06, 0x37, 0x0c, 0x37, 0x0c, 0x37, - 0x0c, 0x37, 0x18, 0x81, 0xef, 0xf3, 0x00, 0x0c, 0x38, 0x18, 0x37, 0x18, 0x38, 0x18, 0x37, 0x0c, - 0x35, 0x12, 0x32, 0x06, 0x32, 0x0c, 0x32, 0x0c, 0x32, 0x0c, 0x32, 0x18, 0x87, 0x33, 0xf7, 0x86, - 0x1b, 0xf7, 0x00, 0x01, 0x2d, 0x0b, 0x00, 0x01, 0x28, 0x05, 0x00, 0x01, 0x28, 0x06, 0x89, 0x87, - 0x33, 0xf7, 0x86, 0x1b, 0xf7, 0x00, 0x01, 0x2b, 0x0b, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x89, - 0x87, 0x69, 0xf7, 0x84, 0x1f, 0x85, 0x01, 0x82, 0x04, 0x07, 0x06, 0x8d, 0xfe, 0x83, 0x85, 0x08, - 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x2d, 0x0b, 0x2d, 0x0c, 0x2d, 0x06, 0x2d, 0x06, 0x2d, 0x0c, - 0x2f, 0x0c, 0x30, 0x0c, 0x32, 0x06, 0x32, 0x06, 0x32, 0x0c, 0x32, 0x0c, 0x32, 0x0c, 0x32, 0x18, - 0x81, 0xef, 0xf3, 0x32, 0x06, 0x34, 0x06, 0x35, 0x06, 0x35, 0x06, 0x34, 0x0c, 0x32, 0x0c, 0x30, - 0x0c, 0x2f, 0x0c, 0x2d, 0x0c, 0x2c, 0x1e, 0x2d, 0x06, 0x2d, 0x0c, 0x2d, 0x0c, 0x2f, 0x0c, 0x2d, - 0x18, 0x81, 0xef, 0xf3, 0x00, 0x0d, 0x89, 0x88, 0x07, 0x87, 0x3e, 0xf7, 0x86, 0x45, 0xf7, 0x45, - 0x04, 0x00, 0x02, 0x45, 0x04, 0x00, 0x02, 0x39, 0x0c, 0x87, 0x1f, 0xf7, 0x86, 0x38, 0xf7, 0x2d, - 0x0b, 0x2d, 0x0c, 0x2e, 0x12, 0x2d, 0x12, 0x2b, 0x0c, 0x2b, 0x06, 0x2d, 0x06, 0x2d, 0x06, 0x2d, - 0x06, 0x2d, 0x0c, 0x2b, 0x0c, 0x2d, 0x06, 0x2b, 0x06, 0x2d, 0x0d, 0x87, 0x3e, 0xf7, 0x86, 0x45, - 0xf7, 0x40, 0x0c, 0x39, 0x0c, 0x89, 0x82, 0x04, 0x81, 0x6e, 0xf5, 0x83, 0x81, 0x60, 0xf5, 0x81, - 0x60, 0xf5, 0x82, 0x06, 0x81, 0x6e, 0xf5, 0x83, 0x81, 0x2f, 0xf5, 0x81, 0x2f, 0xf5, 0x8e, 0xf8, - 0x81, 0x2c, 0xf5, 0x81, 0x2c, 0xf5, 0x8e, 0x01, 0x81, 0x2c, 0xf5, 0x8e, 0x02, 0x81, 0x2c, 0xf5, - 0x8e, 0x05, 0x88, 0xf4, 0x82, 0x04, 0x81, 0x49, 0xf2, 0x83, 0x88, 0x00, 0x81, 0x2f, 0xf5, 0x81, - 0x2f, 0xf5, 0x88, 0xf4, 0x81, 0x49, 0xf2, 0x81, 0x49, 0xf2, 0x88, 0x00, 0x81, 0x2f, 0xf5, 0x88, - 0xf4, 0x81, 0x49, 0xf2, 0x88, 0x00, 0x82, 0x08, 0x81, 0x2f, 0xf5, 0x83, 0x81, 0x2c, 0xf5, 0x88, - 0xf8, 0x81, 0x2c, 0xf5, 0x81, 0x2c, 0xf5, 0x81, 0x2c, 0xf5, 0x88, 0xf4, 0x81, 0x49, 0xf2, 0x88, - 0xff, 0x81, 0xc3, 0xf1, 0x88, 0x00, 0x81, 0x2f, 0xf5, 0x81, 0x24, 0xf5, 0x88, 0xf9, 0x81, 0x2f, - 0xf5, 0x81, 0x24, 0xf5, 0x88, 0xfb, 0x81, 0x2c, 0xf5, 0x81, 0x24, 0xf5, 0x81, 0xc3, 0xf1, 0x88, - 0x00, 0x80, 0x96, 0xf4, 0x88, 0xf4, 0x81, 0x4c, 0xf2, 0x88, 0xff, 0x89, 0x81, 0x2f, 0xf5, 0x87, - 0x8c, 0xf7, 0x86, 0x38, 0xf7, 0x43, 0x0c, 0x45, 0x0c, 0x86, 0x84, 0xf7, 0x8e, 0x0c, 0x2d, 0x0c, - 0x2d, 0x0c, 0x2d, 0x24, 0x2b, 0x06, 0x2b, 0x0c, 0x2d, 0x06, 0x2d, 0x0c, 0x2d, 0x0c, 0x2b, 0x0c, - 0x2d, 0x06, 0x2b, 0x06, 0x2d, 0x0c, 0x8e, 0xf4, 0x86, 0x38, 0xf7, 0x43, 0x0c, 0x45, 0x0c, 0x89, - 0x81, 0x6b, 0xf5, 0x8e, 0x03, 0x81, 0x6b, 0xf5, 0x8e, 0xfd, 0x89, 0x81, 0x6e, 0xf5, 0x81, 0x87, - 0xf5, 0x34, 0x0c, 0x34, 0x0c, 0x35, 0x12, 0x34, 0x12, 0x32, 0x0c, 0x81, 0x87, 0xf5, 0x34, 0x0c, - 0x34, 0x0c, 0x34, 0x18, 0x00, 0x18, 0x89, 0x84, 0x0a, 0x85, 0x01, 0x87, 0x69, 0xf7, 0x86, 0x2b, - 0xf7, 0x07, 0x18, 0x85, 0x08, 0x87, 0x1f, 0xf7, 0x89, 0x81, 0xa0, 0xf6, 0x81, 0xa0, 0xf6, 0x81, - 0x95, 0xf6, 0x81, 0x95, 0xf6, 0x81, 0xb6, 0xf6, 0x88, 0x05, 0x81, 0xb6, 0xf6, 0x88, 0xfb, 0x81, - 0xb6, 0xf6, 0x88, 0x00, 0x81, 0xb6, 0xf6, 0x81, 0xa0, 0xf6, 0x81, 0xa0, 0xf6, 0x81, 0xb3, 0xf6, - 0x88, 0x05, 0x81, 0xb3, 0xf6, 0x88, 0xfd, 0x81, 0x8a, 0xf6, 0x81, 0x8a, 0xf6, 0x88, 0x05, 0x81, - 0xb0, 0xf6, 0x88, 0xfb, 0x81, 0xb0, 0xf6, 0x88, 0x00, 0x82, 0x04, 0x81, 0x55, 0xf6, 0x83, 0x81, - 0xb3, 0xf6, 0x88, 0x05, 0x81, 0xb3, 0xf6, 0x88, 0x00, 0x81, 0x55, 0xf6, 0x81, 0x55, 0xf6, 0x81, - 0xb6, 0xf6, 0x88, 0x05, 0x81, 0xb6, 0xf6, 0x88, 0x00, 0x81, 0x55, 0xf6, 0x82, 0x04, 0x88, 0x00, - 0x81, 0xb3, 0xf6, 0x88, 0x05, 0x81, 0xb3, 0xf6, 0x83, 0x88, 0x00, 0x81, 0xb3, 0xf6, 0x88, 0x05, - 0x81, 0xb3, 0xf6, 0x88, 0x04, 0x81, 0xb3, 0xf6, 0x88, 0x00, 0x81, 0xb3, 0xf6, 0x88, 0xfd, 0x81, - 0xb3, 0xf6, 0x88, 0x04, 0x81, 0xb3, 0xf6, 0x88, 0xfd, 0x81, 0x8a, 0xf6, 0x88, 0x00, 0x81, 0x55, - 0xf6, 0x00, 0x60, 0x81, 0xb6, 0xf6, 0x88, 0x05, 0x81, 0xb6, 0xf6, 0x81, 0x58, 0xf6, 0x88, 0x05, - 0x81, 0xb3, 0xf6, 0x81, 0x58, 0xf6, 0x88, 0xfb, 0x81, 0xb0, 0xf6, 0x81, 0x58, 0xf6, 0x00, 0x60, - 0x88, 0x00, 0x80, 0x99, 0xf5, 0x81, 0x58, 0xf6, 0x88, 0x00, 0x86, 0x1b, 0xf7, 0x82, 0x04, 0x81, - 0x76, 0xf6, 0x83, 0x2b, 0x09, 0x00, 0x03, 0x82, 0x06, 0x81, 0x76, 0xf6, 0x83, 0x2b, 0x09, 0x00, - 0x03, 0x27, 0x09, 0x00, 0x03, 0x89, 0x87, 0x33, 0xf7, 0x85, 0x01, 0x84, 0x1c, 0x07, 0x01, 0x00, - 0x01, 0x84, 0x02, 0x07, 0x01, 0x00, 0x03, 0x85, 0x08, 0x89, 0x81, 0xb3, 0xf6, 0x8e, 0x07, 0x81, - 0xb3, 0xf6, 0x8e, 0xf9, 0x89, 0x81, 0xa0, 0xf6, 0x8e, 0x03, 0x81, 0xa0, 0xf6, 0x8e, 0xfd, 0x89, - 0x81, 0xb3, 0xf6, 0x8e, 0x01, 0x81, 0xb6, 0xf6, 0x8e, 0xfa, 0x81, 0xb6, 0xf6, 0x8e, 0x05, 0x89, - 0x81, 0xb3, 0xf6, 0x81, 0xb6, 0xf6, 0x87, 0xeb, 0xf6, 0x86, 0xf4, 0xf6, 0x15, 0x0c, 0x15, 0x06, - 0x21, 0x06, 0x81, 0xd1, 0xf6, 0x15, 0x0c, 0x15, 0x18, 0x81, 0xd1, 0xf6, 0x1f, 0x06, 0x21, 0x06, - 0x89, 0x87, 0xff, 0xf6, 0x84, 0x1f, 0x85, 0x01, 0x8a, 0x82, 0x06, 0x07, 0x01, 0x8d, 0xf0, 0x07, - 0x01, 0x8d, 0x0e, 0x83, 0x8b, 0x85, 0x08, 0x87, 0xeb, 0xf6, 0x89, 0x0f, 0x01, 0x40, 0x3f, 0x0c, - 0xff, 0x80, 0xeb, 0xf6, 0x00, 0x00, 0x05, 0x05, 0x03, 0xfd, 0xfb, 0xfb, 0x80, 0xf4, 0xf6, 0x0f, - 0x01, 0x40, 0x3b, 0x3f, 0x3e, 0x3b, 0x3d, 0x3c, 0x3b, 0x3b, 0x3a, 0x3b, 0x39, 0x38, 0x3b, 0x37, - 0x36, 0x3b, 0x35, 0x34, 0x3b, 0x33, 0x00, 0xff, 0x80, 0xff, 0xf6, 0x32, 0x80, 0x1b, 0xf7, 0x0f, - 0x01, 0x40, 0x3f, 0x3e, 0x3d, 0x3a, 0x0b, 0x02, 0x80, 0x25, 0xf7, 0x82, 0x0c, 0xf4, 0x07, 0xf9, - 0x80, 0x2b, 0xf7, 0x0f, 0xff, 0x80, 0x33, 0xf7, 0x82, 0x0c, 0xf4, 0x80, 0x38, 0xf7, 0x0f, 0x01, - 0x3f, 0x3a, 0x80, 0x3e, 0xf7, 0x82, 0x05, 0x03, 0xf8, 0x05, 0x07, 0xf4, 0x80, 0x45, 0xf7, 0x00, - 0x01, 0x0f, 0x03, 0x0e, 0x04, 0x0d, 0x04, 0x0c, 0x04, 0x0b, 0xff, 0x80, 0x4f, 0xf7, 0x02, 0x01, - 0xff, 0xfe, 0xfe, 0xff, 0x01, 0x02, 0x80, 0x5e, 0xf7, 0x0f, 0x02, 0x00, 0x04, 0x0e, 0x02, 0x00, - 0x04, 0x0d, 0x03, 0x00, 0x03, 0x0c, 0x03, 0x00, 0x03, 0x80, 0x69, 0xf7, 0x83, 0x05, 0x82, 0x18, - 0xe8, 0x80, 0x7c, 0xf7, 0x82, 0x00, 0x0c, 0xf4, 0x00, 0x80, 0x84, 0xf7, 0x0e, 0x01, 0x3f, 0x3e, - 0x0b, 0x02, 0x0a, 0x02, 0x80, 0x90, 0xf7, 0x02, 0x80, 0x97, 0xf7, 0xfe, 0x80, 0x9b, 0xf7, 0x0e, - 0x01, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x80, 0x9f, 0xf7, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; +static const unsigned char sbootimage[] = { + 0x00, 0x01, 0x1a, 0x00, 0xf9, 0xc0, 0xb0, 0x22, 0x31, 0x35, 0x36, 0x31, 0x39, 0x22, 0x3a, 0xea, + 0x3a, 0xf7, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x0d, 0x00, 0x02, + 0x0f, 0x00, 0xf9, 0xc0, 0xb0, 0x22, 0x31, 0x35, 0x36, 0x31, 0x39, 0x22, 0x3a, 0xea, 0x3a, 0xf7, + 0x0d, 0x00, 0x03, 0x0b, 0x00, 0xf9, 0xc0, 0xb0, 0x22, 0x31, 0x35, 0x36, 0x31, 0x36, 0x22, 0x0d, + 0x00, 0x04, 0x47, 0x00, 0xea, 0x21, 0xa8, 0x61, 0x11, 0xa9, 0x61, 0x01, 0x07, 0x9d, 0x36, 0x00, + 0xed, 0xb0, 0x21, 0xa8, 0x61, 0xed, 0x5b, 0xf4, 0x5c, 0x01, 0x05, 0x0f, 0xcd, 0x13, 0x3d, 0x21, + 0x00, 0xa6, 0xed, 0x5b, 0xf4, 0x5c, 0x01, 0x05, 0x08, 0xcd, 0x13, 0x3d, 0x21, 0x50, 0xeb, 0xed, + 0x5b, 0xf4, 0x5c, 0x01, 0x05, 0x0d, 0xcd, 0x13, 0x3d, 0xcd, 0xa8, 0x61, 0x21, 0xa8, 0x61, 0x11, + 0xa9, 0x61, 0x01, 0x07, 0x9d, 0x36, 0x00, 0xed, 0xb0, 0xc9, 0x0d, 0x00, 0x0a, 0x25, 0x00, 0xe7, + 0xc3, 0xa7, 0x3a, 0xda, 0xc3, 0xa7, 0x3a, 0xd9, 0xb0, 0x22, 0x37, 0x22, 0x3a, 0xfd, 0xb0, 0x22, + 0x36, 0x35, 0x33, 0x34, 0x37, 0x22, 0x3a, 0xf9, 0xc0, 0xb0, 0x22, 0x32, 0x33, 0x39, 0x33, 0x36, + 0x22, 0x3a, 0xf7, 0x0d, 0x80, 0xaa, 0x0a, 0x00, 0x6f, 0x6f, 0x74, 0x31, 0x31, 0x22, 0xca, 0x31, + 0x30, 0x0d, 0x80, 0x31, 0x35, 0x36, 0x31, 0x36, 0x0e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x0d, 0x80, + 0xca, 0xf3, 0x5e, 0x06, 0x00, 0x00, 0x00, 0xf3, 0x5e, 0x06, 0x00, 0x00, 0x00, 0x0a, 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, + 0xfd, 0x22, 0xc8, 0xaf, 0xd9, 0x22, 0xca, 0xaf, 0xed, 0x73, 0xcc, 0xaf, 0xed, 0x57, 0x32, 0xce, + 0xaf, 0x11, 0x00, 0x00, 0x21, 0x00, 0x9d, 0x01, 0x05, 0x08, 0xcd, 0x13, 0x3d, 0x31, 0x68, 0xbf, + 0xf3, 0x21, 0x00, 0xfd, 0x11, 0x01, 0xfd, 0x01, 0x00, 0x01, 0x36, 0xfc, 0xed, 0xb0, 0x21, 0x5a, + 0x67, 0x22, 0xfd, 0xfc, 0x3e, 0xc3, 0x32, 0xfc, 0xfc, 0xed, 0x5e, 0x3e, 0xfd, 0xed, 0x47, 0xcd, + 0x53, 0xeb, 0xcd, 0x50, 0xeb, 0xcd, 0x13, 0x65, 0xfb, 0x21, 0x9b, 0x6e, 0x11, 0x00, 0x77, 0x01, + 0xc8, 0x00, 0xed, 0xb0, 0x21, 0x77, 0x6b, 0x11, 0x00, 0x76, 0x0e, 0x28, 0xed, 0xb0, 0x21, 0x9f, + 0x6b, 0x11, 0x5c, 0x76, 0x0e, 0x09, 0xed, 0xb0, 0x11, 0x00, 0x78, 0x3e, 0x40, 0x32, 0x8e, 0x76, + 0xcd, 0xf4, 0x62, 0x21, 0x00, 0x78, 0x11, 0x00, 0x81, 0x06, 0x08, 0xc5, 0xcd, 0xd9, 0x63, 0x24, + 0x14, 0xc1, 0x10, 0xf7, 0x21, 0x00, 0x77, 0xcd, 0x25, 0x64, 0xcd, 0x25, 0x64, 0xcd, 0x25, 0x64, + 0x11, 0x00, 0x8a, 0x3e, 0x80, 0x32, 0x8e, 0x76, 0xcd, 0xf4, 0x62, 0x21, 0x00, 0x8a, 0x11, 0x00, + 0x93, 0x06, 0x08, 0xc5, 0xcd, 0xd9, 0x63, 0x24, 0x14, 0xc1, 0x10, 0xf7, 0x06, 0x11, 0x21, 0x00, + 0x8a, 0xc5, 0xcd, 0x25, 0x64, 0x24, 0xc1, 0x10, 0xf8, 0xcd, 0xc9, 0x6d, 0x3e, 0xff, 0x32, 0xb0, + 0xb3, 0x3c, 0x32, 0xb1, 0xb3, 0x3e, 0x05, 0x32, 0xb2, 0xb3, 0x3e, 0x08, 0x32, 0xb3, 0xb3, 0x3e, + 0x0e, 0x32, 0xb4, 0xb3, 0xcd, 0x61, 0x64, 0x3a, 0xb0, 0xb3, 0x3c, 0x32, 0xb0, 0xb3, 0x3a, 0xb0, + 0xb3, 0xe6, 0x01, 0xfe, 0x01, 0x20, 0x18, 0x21, 0x84, 0xac, 0x22, 0x87, 0x64, 0xcd, 0x85, 0x64, + 0xcd, 0x85, 0x64, 0x21, 0xfc, 0xac, 0x22, 0x87, 0x64, 0xcd, 0x85, 0x64, 0xcd, 0x85, 0x64, 0x3a, + 0xb0, 0xb3, 0xe6, 0x03, 0xfe, 0x03, 0x20, 0x21, 0x21, 0x00, 0x40, 0x11, 0x00, 0x78, 0x01, 0xb1, + 0xb3, 0xcd, 0xdd, 0x64, 0x21, 0x1b, 0x40, 0x11, 0x00, 0x8a, 0x01, 0xb2, 0xb3, 0xcd, 0xdd, 0x64, + 0x21, 0x74, 0xad, 0x22, 0x87, 0x64, 0xcd, 0x85, 0x64, 0x3a, 0xb0, 0xb3, 0xe6, 0x07, 0xfe, 0x07, + 0x20, 0xa5, 0x21, 0x60, 0x50, 0x11, 0x00, 0x8a, 0x01, 0xb3, 0xb3, 0xcd, 0xdd, 0x64, 0x21, 0x7b, + 0x50, 0x11, 0x00, 0x78, 0x01, 0xb4, 0xb3, 0xcd, 0xdd, 0x64, 0x18, 0x8b, 0xfd, 0x21, 0x5c, 0x76, + 0x0e, 0x08, 0x21, 0x00, 0x77, 0x06, 0x28, 0xdd, 0x21, 0x00, 0x76, 0xcd, 0x3e, 0x63, 0xc5, 0x06, + 0x05, 0xdd, 0x7e, 0x00, 0xe6, 0x3f, 0xcd, 0x57, 0x63, 0xdd, 0x2c, 0x10, 0xf4, 0x0d, 0x20, 0xef, + 0xc1, 0xcd, 0x67, 0x63, 0x10, 0xe1, 0x14, 0x1e, 0x00, 0x0d, 0x20, 0xd6, 0xeb, 0x11, 0x00, 0x76, + 0x3a, 0x8e, 0x76, 0x47, 0x2c, 0x2c, 0x1a, 0xb8, 0x38, 0x02, 0x36, 0x18, 0x2c, 0x2c, 0x2c, 0x1c, + 0x7d, 0xfe, 0xc8, 0x20, 0xef, 0xc9, 0xd9, 0x21, 0x98, 0x76, 0x11, 0x99, 0x76, 0x01, 0x27, 0x00, + 0x70, 0xed, 0xb0, 0xd9, 0x79, 0x32, 0x52, 0x63, 0xfd, 0x7e, 0x00, 0x32, 0xc0, 0x76, 0xc9, 0xdd, + 0xe5, 0xdd, 0x21, 0x98, 0x76, 0x32, 0x62, 0x63, 0xdd, 0x36, 0x00, 0x01, 0xdd, 0xe1, 0xc9, 0xc5, + 0xfd, 0xe5, 0xd5, 0xe5, 0xdd, 0xe1, 0xe5, 0x3a, 0xc0, 0x76, 0xe6, 0xf8, 0x0f, 0x0f, 0x0f, 0x4f, + 0x06, 0x00, 0xeb, 0x09, 0xe5, 0xfd, 0xe1, 0x21, 0x98, 0x76, 0x3a, 0xc0, 0x76, 0xe6, 0x07, 0x4f, + 0x3e, 0x07, 0x91, 0x4f, 0x16, 0x05, 0x1e, 0x7e, 0x7b, 0x32, 0x98, 0x63, 0xaf, 0xdd, 0xcb, 0x00, + 0x46, 0x28, 0x02, 0x3e, 0x40, 0x08, 0x7e, 0xfe, 0x01, 0x20, 0x1b, 0x06, 0x86, 0x08, 0xb0, 0x47, + 0x79, 0x07, 0x07, 0x07, 0xb0, 0x32, 0xb3, 0x63, 0xfd, 0xcb, 0x00, 0x86, 0x0d, 0x79, 0xfe, 0xff, + 0x20, 0x04, 0xfd, 0x2c, 0x0e, 0x07, 0x2c, 0x7b, 0xd6, 0x08, 0x5f, 0xfe, 0x3e, 0x20, 0xc9, 0xdd, + 0x23, 0x15, 0x20, 0xc2, 0xe1, 0xd1, 0x01, 0x05, 0x00, 0x09, 0xeb, 0x09, 0xeb, 0xfd, 0xe1, 0xc1, + 0xc9, 0xe5, 0xdd, 0xe1, 0xd5, 0xfd, 0xe1, 0x06, 0x28, 0xc5, 0xdd, 0x4e, 0x00, 0xcd, 0x1c, 0x64, + 0xdd, 0x7e, 0x04, 0xfd, 0x71, 0x04, 0x4f, 0xcd, 0x1c, 0x64, 0xfd, 0x71, 0x00, 0xdd, 0x4e, 0x01, + 0xcd, 0x1c, 0x64, 0xdd, 0x7e, 0x03, 0xfd, 0x71, 0x03, 0x4f, 0xcd, 0x1c, 0x64, 0xfd, 0x71, 0x01, + 0xdd, 0x4e, 0x02, 0xcd, 0x1c, 0x64, 0xfd, 0x71, 0x02, 0x01, 0x05, 0x00, 0xdd, 0x09, 0xfd, 0x09, + 0xc1, 0x10, 0xc6, 0xc9, 0x06, 0x08, 0xcb, 0x19, 0x17, 0x10, 0xfb, 0x4f, 0xc9, 0xe5, 0x11, 0x00, + 0x9c, 0x06, 0x05, 0xc5, 0xe5, 0x06, 0x08, 0xc5, 0xe5, 0x06, 0x28, 0xc5, 0xcb, 0x16, 0xd5, 0x06, + 0x05, 0x1a, 0x1f, 0x12, 0x1c, 0x10, 0xfa, 0xd1, 0x01, 0x05, 0x00, 0x09, 0xc1, 0x10, 0xec, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0xe1, 0xc1, 0x10, 0xdf, 0xe1, 0xc1, 0x23, 0x10, 0xd6, 0xd1, 0xd5, 0x21, + 0x00, 0x9c, 0x01, 0xc8, 0x00, 0xed, 0xb0, 0xe1, 0xc9, 0xdd, 0x21, 0x84, 0xac, 0x06, 0x3c, 0xdd, + 0x6e, 0x00, 0xdd, 0x66, 0x01, 0xdd, 0x7e, 0x04, 0x32, 0x75, 0x64, 0xaf, 0xcb, 0x46, 0x28, 0x02, + 0x3e, 0x01, 0xdd, 0x77, 0x05, 0x11, 0x06, 0x00, 0xdd, 0x19, 0x10, 0xe3, 0xc9, 0xdd, 0x21, 0x84, + 0xac, 0x06, 0x14, 0xc5, 0xdd, 0x6e, 0x00, 0xdd, 0x66, 0x01, 0xdd, 0x56, 0x02, 0xdd, 0x5e, 0x03, + 0xdd, 0x7e, 0x05, 0xfe, 0x01, 0x7a, 0x20, 0x01, 0x7b, 0x32, 0xa5, 0x64, 0xcb, 0x86, 0x24, 0x7c, + 0xe6, 0x07, 0x20, 0x18, 0x7c, 0xd6, 0x08, 0x67, 0x7d, 0xc6, 0x20, 0x6f, 0xe6, 0xe0, 0x20, 0x0c, + 0x7c, 0xc6, 0x08, 0x67, 0xe6, 0x18, 0xfe, 0x18, 0x20, 0x02, 0x26, 0x40, 0xdd, 0x75, 0x00, 0xdd, + 0x74, 0x01, 0x06, 0x01, 0xcd, 0x67, 0x64, 0x3a, 0xa5, 0x64, 0xcb, 0xf7, 0x32, 0xd8, 0x64, 0xcb, + 0xc6, 0xc1, 0x10, 0xaf, 0xc9, 0x0a, 0xc5, 0x4f, 0x06, 0x00, 0xdd, 0x21, 0xe0, 0x6a, 0xdd, 0x09, + 0xdd, 0x7e, 0x00, 0x82, 0x57, 0xcd, 0xf7, 0x64, 0xe1, 0x7e, 0x3c, 0xe6, 0x1f, 0x77, 0xc9, 0x06, + 0x05, 0xc5, 0x06, 0x08, 0xc5, 0x01, 0x05, 0x00, 0xeb, 0xed, 0xb0, 0xeb, 0x01, 0xfb, 0x00, 0x09, + 0xc1, 0x10, 0xf1, 0x01, 0x20, 0xf8, 0x09, 0xc1, 0x10, 0xe7, 0xc9, 0x11, 0x9b, 0x6e, 0xd5, 0xd5, + 0xd5, 0x21, 0x00, 0x40, 0xcd, 0xf7, 0x64, 0x21, 0x1b, 0x40, 0xd1, 0xcd, 0xf7, 0x64, 0x21, 0x60, + 0x50, 0xd1, 0xcd, 0xf7, 0x64, 0x21, 0x7b, 0x50, 0xd1, 0xcd, 0xf7, 0x64, 0x11, 0x00, 0xa9, 0x21, + 0x06, 0x40, 0xcd, 0xf7, 0x64, 0x11, 0xc8, 0xa9, 0x21, 0x0b, 0x40, 0xcd, 0xf7, 0x64, 0x11, 0x90, + 0xaa, 0x21, 0x10, 0x40, 0xcd, 0xf7, 0x64, 0x11, 0x58, 0xab, 0x21, 0x15, 0x40, 0xcd, 0xf7, 0x64, + 0x11, 0x06, 0x58, 0x21, 0x20, 0xac, 0x06, 0x05, 0xc5, 0x01, 0x14, 0x00, 0xed, 0xb0, 0x01, 0x0c, + 0x00, 0xeb, 0x09, 0xeb, 0xc1, 0x10, 0xf1, 0xcd, 0xc2, 0x6c, 0x21, 0x00, 0xa5, 0x22, 0x36, 0x5c, + 0x21, 0x00, 0x6b, 0xcd, 0x23, 0x67, 0x21, 0x00, 0x40, 0x11, 0x00, 0xc0, 0x01, 0x00, 0x1b, 0xed, + 0xb0, 0xcd, 0x44, 0x6e, 0x21, 0x00, 0xc0, 0x11, 0x00, 0x40, 0x01, 0x00, 0x1b, 0xed, 0xb0, 0xcd, + 0x33, 0x69, 0x3e, 0x01, 0x32, 0xbf, 0xb3, 0xdd, 0x21, 0x00, 0x9d, 0xcd, 0xca, 0x65, 0xcd, 0x4b, + 0x68, 0x7e, 0x32, 0xcb, 0xb3, 0xdd, 0x22, 0xc1, 0xb3, 0x21, 0x00, 0x00, 0x22, 0x78, 0x5c, 0xaf, + 0x32, 0xac, 0xde, 0x21, 0x00, 0x40, 0x11, 0x00, 0xc0, 0x01, 0x00, 0x1b, 0xed, 0xb0, 0xcd, 0x61, + 0x64, 0xc9, 0xdd, 0xe5, 0xe1, 0xaf, 0x32, 0x78, 0x5c, 0x16, 0x00, 0x7e, 0xcd, 0x50, 0x67, 0xa7, + 0x28, 0x22, 0xcd, 0x3a, 0x67, 0x7e, 0xa3, 0xfe, 0x42, 0x20, 0x01, 0x14, 0xfe, 0x43, 0xf5, 0x01, + 0x05, 0x00, 0x09, 0xf1, 0x20, 0x06, 0x7e, 0xfe, 0xc0, 0x20, 0x01, 0x14, 0x23, 0x23, 0x23, 0x7c, + 0xfe, 0xa5, 0x20, 0xd7, 0x7a, 0x32, 0xb5, 0xb3, 0x21, 0x00, 0x00, 0x22, 0xb8, 0xb3, 0x22, 0xba, + 0xb3, 0x22, 0xbc, 0xb3, 0xa7, 0x20, 0x12, 0x21, 0x44, 0x6b, 0xcd, 0x23, 0x67, 0x21, 0x0b, 0x09, + 0x22, 0xb6, 0xb3, 0x3e, 0x0e, 0x32, 0xbe, 0xb3, 0xc9, 0xfe, 0x0d, 0x30, 0x22, 0x32, 0xbb, 0xb3, + 0x21, 0x06, 0x0e, 0x22, 0xb6, 0xb3, 0x3e, 0x01, 0x32, 0xb8, 0xb3, 0x32, 0xc0, 0xb3, 0x3e, 0x08, + 0x32, 0xbe, 0xb3, 0xdd, 0xe5, 0xe1, 0x01, 0x06, 0x0a, 0x1e, 0x41, 0xcd, 0x91, 0x66, 0xc9, 0xfe, + 0x19, 0x30, 0x38, 0xa7, 0x1f, 0x32, 0xbc, 0xb3, 0xce, 0x00, 0x32, 0xbb, 0xb3, 0x21, 0x06, 0x06, + 0x22, 0xb6, 0xb3, 0x3e, 0x02, 0x32, 0xb8, 0xb3, 0x3d, 0x32, 0xc0, 0xb3, 0x3e, 0x08, 0x32, 0xbe, + 0xb3, 0xdd, 0xe5, 0xe1, 0x01, 0x06, 0x02, 0x1e, 0x41, 0x3a, 0xbb, 0xb3, 0x57, 0xcd, 0x91, 0x66, + 0x01, 0x06, 0x12, 0x3a, 0xbc, 0xb3, 0x57, 0xcd, 0x91, 0x66, 0xc9, 0x3e, 0x18, 0xcd, 0x4b, 0x66, + 0x22, 0xc3, 0xb3, 0x3e, 0x01, 0x32, 0xb9, 0xb3, 0xc9, 0xed, 0x43, 0xcf, 0xb3, 0xd5, 0x7e, 0xcd, + 0x50, 0x67, 0x1e, 0xff, 0xcd, 0x3a, 0x67, 0x16, 0x06, 0x7e, 0xa3, 0xfe, 0x42, 0x28, 0x14, 0xfe, + 0x43, 0xf5, 0x01, 0x05, 0x00, 0x09, 0xf1, 0x20, 0x05, 0x7e, 0xfe, 0xc0, 0x28, 0x0b, 0x23, 0x23, + 0x23, 0x18, 0xdb, 0x01, 0x05, 0x00, 0x09, 0x16, 0x04, 0x01, 0x0d, 0x00, 0xa7, 0xed, 0x42, 0x7a, + 0x32, 0xda, 0xb3, 0x11, 0xdb, 0xb3, 0x06, 0x08, 0x7e, 0xfe, 0x20, 0x30, 0x02, 0x3e, 0x2e, 0xfe, + 0x80, 0x38, 0x02, 0x3e, 0x2e, 0x12, 0x13, 0x23, 0x10, 0xee, 0x3e, 0xff, 0x12, 0xd1, 0xe5, 0x6b, + 0x26, 0x3e, 0x22, 0xd5, 0xb3, 0x21, 0x20, 0x20, 0x22, 0xd7, 0xb3, 0x3e, 0x10, 0x32, 0xd9, 0xb3, + 0x3e, 0x16, 0x32, 0xce, 0xb3, 0x21, 0x13, 0x01, 0x22, 0xd1, 0xb3, 0x21, 0x10, 0x05, 0x22, 0xd3, + 0xb3, 0xd5, 0x21, 0xce, 0xb3, 0xcd, 0x23, 0x67, 0xd1, 0xe1, 0x01, 0x08, 0x00, 0x09, 0x3a, 0xcf, + 0xb3, 0x3c, 0x32, 0xcf, 0xb3, 0x1c, 0x15, 0xc2, 0x95, 0x66, 0xc9, 0xe5, 0x2a, 0xca, 0xaf, 0xd9, + 0xfd, 0x2a, 0xc8, 0xaf, 0x3e, 0x02, 0xcd, 0x01, 0x16, 0xe1, 0x7e, 0xfe, 0xff, 0xc8, 0xd7, 0x23, + 0x18, 0xf8, 0xd5, 0x0e, 0x00, 0x06, 0x09, 0x11, 0x6e, 0x6b, 0x1a, 0xbe, 0x28, 0x02, 0x0e, 0xff, + 0x23, 0x13, 0x10, 0xf6, 0xd1, 0x59, 0x2b, 0xc9, 0xfe, 0x01, 0xc0, 0x01, 0x10, 0x00, 0x09, 0x7e, + 0x18, 0xf6, 0xfd, 0xe5, 0xdd, 0xe5, 0xe5, 0xd5, 0xc5, 0xf5, 0xd9, 0xe5, 0xd5, 0xc5, 0x2a, 0xca, + 0xaf, 0xd9, 0x08, 0xf5, 0xfd, 0x2a, 0xc8, 0xaf, 0xcd, 0x56, 0xeb, 0x2a, 0x78, 0x5c, 0x23, 0x22, + 0x78, 0x5c, 0x3a, 0xac, 0xde, 0xc6, 0x02, 0x32, 0xac, 0xde, 0xcd, 0xa8, 0x6b, 0xcd, 0x07, 0x6d, + 0x3a, 0x78, 0x5c, 0xe6, 0x03, 0xfe, 0x03, 0x20, 0x21, 0xcd, 0x4b, 0x68, 0xed, 0x5b, 0xbe, 0xb3, + 0x43, 0x72, 0x23, 0x10, 0xfc, 0xcb, 0x72, 0xcb, 0xf2, 0x28, 0x0b, 0xcb, 0xb2, 0x14, 0xcb, 0x5a, + 0x28, 0x04, 0x14, 0x14, 0xcb, 0x9a, 0x7a, 0x32, 0xbf, 0xb3, 0x3a, 0x78, 0x5c, 0xfe, 0x32, 0x20, + 0x58, 0x2a, 0xb6, 0xb3, 0x22, 0xcc, 0xb3, 0x3a, 0xbe, 0xb3, 0x32, 0xec, 0xb3, 0x21, 0xcc, 0xb3, + 0x22, 0x4d, 0x68, 0xcd, 0x4b, 0x68, 0x2a, 0xc9, 0xb3, 0x7c, 0x07, 0x07, 0x07, 0xf6, 0xc0, 0x67, + 0x22, 0xae, 0xde, 0x3a, 0xec, 0xb3, 0x47, 0xc5, 0xe5, 0x11, 0xed, 0xb3, 0xd5, 0x06, 0x08, 0x7e, + 0x12, 0x24, 0x13, 0x10, 0xfa, 0xe1, 0xd1, 0xd5, 0x06, 0x08, 0x0e, 0x08, 0xe5, 0xcb, 0x16, 0x1f, + 0x23, 0x0d, 0x20, 0xf9, 0x12, 0x14, 0xe1, 0x10, 0xf1, 0xe1, 0x23, 0xc1, 0x10, 0xd9, 0x21, 0xb6, + 0xb3, 0x22, 0x4d, 0x68, 0xcd, 0xa5, 0x6c, 0x18, 0x1f, 0xfe, 0x3c, 0x28, 0xb0, 0xfe, 0x46, 0x28, + 0xac, 0xfe, 0x50, 0x28, 0xa8, 0xfe, 0x5a, 0x20, 0x0f, 0x3e, 0xc9, 0x32, 0xdb, 0x67, 0xcd, 0xc5, + 0x67, 0x3e, 0x3a, 0x32, 0xdb, 0x67, 0x18, 0xd6, 0x3a, 0x78, 0x5c, 0xe6, 0x07, 0xfe, 0x07, 0xcc, + 0x67, 0x68, 0xd9, 0xf1, 0x08, 0xc1, 0xd1, 0xe1, 0xd9, 0xf1, 0xc1, 0xd1, 0xe1, 0xdd, 0xe1, 0xfd, + 0xe1, 0xfb, 0xc9, 0xed, 0x5b, 0xb6, 0xb3, 0x21, 0x00, 0x00, 0x01, 0x20, 0x00, 0x7b, 0xa7, 0x28, + 0x04, 0x09, 0x1d, 0x18, 0xf8, 0x4a, 0x09, 0x22, 0xc9, 0xb3, 0x01, 0x00, 0x58, 0x09, 0xc9, 0x0e, + 0x41, 0x21, 0x56, 0x6b, 0x7e, 0x0f, 0x0f, 0xe6, 0x38, 0xf6, 0x87, 0x32, 0x79, 0x68, 0x3e, 0xff, + 0xcb, 0x87, 0xdb, 0xfe, 0x2f, 0x47, 0x7e, 0xe6, 0x1f, 0xa0, 0xc4, 0x54, 0x6a, 0x0c, 0x23, 0x79, + 0xfe, 0x59, 0x20, 0xe0, 0x3e, 0xf7, 0xdb, 0xfe, 0x1f, 0xd4, 0x6a, 0x69, 0x1f, 0xd4, 0x6a, 0x69, + 0x1f, 0xd4, 0xc3, 0x69, 0x1f, 0xd4, 0x3d, 0x6a, 0x1f, 0xd4, 0x42, 0x6a, 0x3e, 0xef, 0xdb, 0xfe, + 0x1f, 0xd4, 0x42, 0x6a, 0x1f, 0xd4, 0x3d, 0x6a, 0x1f, 0xd4, 0xc3, 0x69, 0x1f, 0xd4, 0x6a, 0x69, + 0x1f, 0xd4, 0x6a, 0x69, 0x3e, 0x00, 0x1f, 0xdc, 0x6a, 0x69, 0x1f, 0xdc, 0x6a, 0x69, 0x1f, 0xdc, + 0xc3, 0x69, 0x1f, 0xdc, 0x3d, 0x6a, 0x1f, 0xdc, 0x42, 0x6a, 0x3e, 0xfe, 0xdb, 0xfe, 0x1f, 0x38, + 0x0f, 0x2a, 0xc1, 0xb3, 0x01, 0x01, 0x9d, 0xa7, 0xed, 0x42, 0x38, 0x04, 0xcd, 0x89, 0x65, 0xc9, + 0x3e, 0xbf, 0xdb, 0xfe, 0x1f, 0xd4, 0x42, 0x6a, 0x3e, 0x7f, 0xdb, 0xfe, 0x1f, 0x38, 0x21, 0x2a, + 0xca, 0xaf, 0xd9, 0xfd, 0x2a, 0xc8, 0xaf, 0xcd, 0x44, 0x6e, 0x21, 0x00, 0x9d, 0x11, 0x00, 0x00, + 0x01, 0x05, 0x08, 0xf3, 0xed, 0x56, 0xcd, 0x13, 0x3d, 0xf3, 0xed, 0x5e, 0xcd, 0x89, 0x65, 0xc9, + 0x1f, 0xd8, 0x3a, 0xb9, 0xb3, 0xa7, 0xc8, 0x2a, 0xc3, 0xb3, 0xaf, 0x32, 0xb9, 0xb3, 0x22, 0xa1, + 0x65, 0xcd, 0x89, 0x65, 0x21, 0x00, 0x9d, 0x22, 0xa1, 0x65, 0xc9, 0x21, 0xc0, 0x40, 0x0e, 0x60, + 0x06, 0x20, 0x36, 0x00, 0x2c, 0x10, 0xfb, 0x0d, 0x28, 0x1a, 0x7d, 0xd6, 0x20, 0x6f, 0x24, 0x7c, + 0xe6, 0x07, 0x20, 0xec, 0x7c, 0xd6, 0x08, 0x67, 0x7d, 0xc6, 0x20, 0x6f, 0x20, 0xe2, 0x7c, 0xc6, + 0x08, 0x67, 0x18, 0xdc, 0x21, 0xc0, 0x58, 0x11, 0xc1, 0x58, 0x01, 0x7f, 0x01, 0x36, 0x07, 0xed, + 0xb0, 0xc9, 0xf5, 0x3a, 0xbc, 0xb3, 0xa7, 0x20, 0x02, 0xf1, 0xc9, 0xcd, 0x4b, 0x68, 0x3a, 0xbe, + 0xb3, 0x47, 0x3a, 0xcb, 0xb3, 0x77, 0x23, 0x10, 0xfc, 0x3a, 0xbb, 0xb3, 0x47, 0x3a, 0xc0, 0xb3, + 0x4f, 0x3d, 0x90, 0x30, 0x23, 0xfe, 0xff, 0x20, 0x13, 0x2a, 0xbb, 0xb3, 0x7c, 0xbd, 0x28, 0x0c, + 0x3e, 0x01, 0x32, 0xc0, 0xb3, 0x3e, 0x06, 0x32, 0xb6, 0xb3, 0x18, 0x16, 0x79, 0x80, 0x32, 0xc0, + 0xb3, 0x3e, 0x16, 0x32, 0xb7, 0xb3, 0x18, 0x0a, 0x79, 0x90, 0x32, 0xc0, 0xb3, 0x3e, 0x06, 0x32, + 0xb7, 0xb3, 0xcd, 0x4b, 0x68, 0x7e, 0x32, 0xcb, 0xb3, 0xf1, 0xc9, 0xf5, 0x06, 0x00, 0x3a, 0xbc, + 0xb3, 0xa7, 0x20, 0x09, 0x3a, 0xbb, 0xb3, 0xa7, 0x28, 0x9f, 0x3d, 0x28, 0x9c, 0xc5, 0xcd, 0x4b, + 0x68, 0x3a, 0xbe, 0xb3, 0x47, 0x3a, 0xcb, 0xb3, 0x77, 0x23, 0x10, 0xfc, 0xc1, 0x3a, 0xc0, 0xb3, + 0x4f, 0xed, 0x5b, 0xbb, 0xb3, 0x3a, 0xb6, 0xb3, 0x6f, 0x3a, 0xb7, 0xb3, 0xfe, 0x16, 0x28, 0x21, + 0x78, 0xa7, 0x20, 0x0e, 0x79, 0xbb, 0x3e, 0x06, 0x26, 0x01, 0x28, 0x2f, 0x61, 0x24, 0x7d, 0x3c, + 0x18, 0x29, 0x79, 0xfe, 0x01, 0x20, 0x04, 0x63, 0x7b, 0x18, 0x1e, 0x61, 0x25, 0x7d, 0x3d, 0x18, + 0x1a, 0x78, 0xa7, 0x20, 0x0b, 0x7b, 0x82, 0xb9, 0x20, 0xe2, 0x63, 0x24, 0x3e, 0x06, 0x18, 0x0b, + 0x7b, 0x3c, 0xb9, 0x20, 0xe6, 0x7b, 0x82, 0x67, 0x7a, 0xc6, 0x05, 0x32, 0xb6, 0xb3, 0x7c, 0x32, + 0xc0, 0xb3, 0xc3, 0xba, 0x69, 0x06, 0x01, 0xf5, 0x18, 0x84, 0xf5, 0x3a, 0xbb, 0xb3, 0x47, 0x3a, + 0xbc, 0xb3, 0xb0, 0xca, 0x71, 0x69, 0x3a, 0xc0, 0xb3, 0xc6, 0x40, 0x4f, 0x3a, 0xb5, 0xb3, 0xc6, + 0x40, 0xb9, 0xd8, 0x06, 0x40, 0xd9, 0x2a, 0xc1, 0xb3, 0x7e, 0xcd, 0x50, 0x67, 0x1e, 0xff, 0xcd, + 0x3a, 0x67, 0x16, 0xec, 0x7e, 0xa3, 0xfe, 0x42, 0x28, 0x14, 0xfe, 0x43, 0xf5, 0x01, 0x05, 0x00, + 0x09, 0xf1, 0x20, 0x05, 0x7e, 0xfe, 0xc0, 0x28, 0x0b, 0x23, 0x23, 0x23, 0x18, 0xdb, 0x01, 0x05, + 0x00, 0x09, 0x16, 0xf7, 0x01, 0x0d, 0x00, 0xa7, 0xed, 0x42, 0xd9, 0x04, 0x78, 0xb9, 0xd9, 0x7e, + 0x36, 0x01, 0x20, 0xc5, 0x77, 0xd5, 0xe5, 0x21, 0x4c, 0x5d, 0x72, 0x23, 0x23, 0xeb, 0xe1, 0x01, + 0x08, 0x00, 0xed, 0xb0, 0x13, 0x0e, 0x20, 0xe1, 0x7c, 0xfe, 0xf7, 0x28, 0x02, 0x0e, 0xaf, 0x79, + 0x12, 0x21, 0x00, 0x00, 0x22, 0x78, 0x5c, 0x3a, 0xce, 0xaf, 0xed, 0x47, 0x2a, 0xca, 0xaf, 0xd9, + 0xfd, 0x2a, 0xc8, 0xaf, 0xed, 0x7b, 0xcc, 0xaf, 0x21, 0x00, 0x3c, 0x22, 0x36, 0x5c, 0xcd, 0x4b, + 0x6e, 0xcd, 0x75, 0x6e, 0xed, 0x56, 0xfb, 0xc9, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x16, 0x13, 0x07, 0x13, 0x01, 0x10, 0x05, 0x53, + 0x50, 0x41, 0x43, 0x45, 0x20, 0x20, 0x54, 0x4f, 0x20, 0x4e, 0x45, 0x57, 0x20, 0x44, 0x49, 0x53, + 0x4b, 0x16, 0x14, 0x07, 0x22, 0x53, 0x53, 0x22, 0x20, 0x54, 0x4f, 0x20, 0x4e, 0x45, 0x58, 0x54, + 0x20, 0x50, 0x41, 0x47, 0x45, 0x53, 0x16, 0x15, 0x07, 0x22, 0x43, 0x53, 0x22, 0x20, 0x54, 0x4f, + 0x20, 0x46, 0x49, 0x52, 0x53, 0x54, 0x20, 0x50, 0x41, 0x47, 0x45, 0xff, 0x16, 0x0b, 0x09, 0x4e, + 0x4f, 0x20, 0x45, 0x58, 0x45, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x20, 0x21, 0xff, 0x21, 0xf0, + 0x08, 0x24, 0x44, 0x28, 0x30, 0xd0, 0xa4, 0xc8, 0xc4, 0xc2, 0xe4, 0xe8, 0xa2, 0xa1, 0x41, 0x48, + 0x22, 0x50, 0xa8, 0x10, 0x42, 0x04, 0x62, 0x6f, 0x6f, 0x74, 0x20, 0x20, 0x20, 0x20, 0x42, 0x08, + 0x10, 0x13, 0x17, 0x1f, 0x05, 0x0c, 0x15, 0x1b, 0x22, 0x01, 0x03, 0x0a, 0x1d, 0x24, 0x27, 0x07, + 0x0e, 0x19, 0x20, 0x02, 0x0b, 0x12, 0x16, 0x1c, 0x26, 0x00, 0x0f, 0x1e, 0x23, 0x09, 0x04, 0x11, + 0x18, 0x25, 0x14, 0x06, 0x0d, 0x1a, 0x21, 0x13, 0x12, 0x0f, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x00, + 0x3a, 0xac, 0xde, 0xa7, 0x20, 0x20, 0x2a, 0xa8, 0xde, 0x23, 0x7c, 0xe6, 0x07, 0x67, 0x22, 0xa8, + 0xde, 0x06, 0x08, 0x4e, 0x21, 0xec, 0xad, 0xed, 0x5f, 0xae, 0xa9, 0x81, 0x4f, 0x3a, 0xb0, 0xb3, + 0xa9, 0x77, 0x23, 0x10, 0xf2, 0xc9, 0x3a, 0xac, 0xde, 0xe6, 0x3f, 0xfe, 0x3e, 0x20, 0x21, 0x3a, + 0xb0, 0xde, 0x3c, 0xe6, 0x07, 0x32, 0xb0, 0xde, 0x28, 0x08, 0x3a, 0xac, 0xde, 0xd6, 0x02, 0x32, + 0xac, 0xde, 0x3e, 0xc9, 0x32, 0x1c, 0x6c, 0xcd, 0x00, 0x6c, 0x3e, 0x3a, 0x32, 0x1c, 0x6c, 0xc9, + 0x3a, 0xac, 0xde, 0xe6, 0x07, 0xfe, 0x02, 0xc0, 0x3a, 0xac, 0xde, 0x07, 0x07, 0x07, 0xe6, 0x06, + 0x4f, 0x06, 0x00, 0x21, 0xec, 0xad, 0x09, 0xcd, 0x59, 0x6c, 0xf5, 0xd4, 0xae, 0x6b, 0xf1, 0x30, + 0xe7, 0xcd, 0x74, 0x6c, 0x3a, 0xac, 0xde, 0xe6, 0x20, 0x0e, 0x60, 0x20, 0x02, 0x0e, 0x00, 0x3a, + 0xac, 0xde, 0x07, 0x07, 0xa9, 0xe6, 0x60, 0x4f, 0x06, 0x00, 0x21, 0x63, 0x6f, 0x09, 0xeb, 0x06, + 0x10, 0x2a, 0xaa, 0xde, 0x4e, 0x1a, 0xb1, 0x77, 0x2c, 0x13, 0x4e, 0x1a, 0xb1, 0x77, 0x13, 0x2d, + 0x24, 0x7c, 0xe6, 0x07, 0x20, 0x08, 0x7c, 0xd6, 0x08, 0x67, 0x7d, 0xc6, 0x20, 0x6f, 0x10, 0xe4, + 0xc9, 0x06, 0x1a, 0x23, 0x7e, 0xe6, 0x18, 0x28, 0x06, 0xfe, 0x10, 0x28, 0x02, 0x06, 0x1f, 0x2b, + 0x7e, 0xfe, 0xe0, 0xd0, 0xe6, 0x1f, 0xb8, 0xd0, 0xee, 0x1f, 0xb8, 0xc9, 0x23, 0x7e, 0xe6, 0x18, + 0xfe, 0x18, 0x20, 0x02, 0x3e, 0x08, 0x2b, 0x6e, 0xf6, 0x40, 0x67, 0x22, 0xaa, 0xde, 0x06, 0x10, + 0x7c, 0xf6, 0x80, 0x57, 0x5d, 0x1a, 0x77, 0x2c, 0x1c, 0x1a, 0x77, 0x2d, 0x24, 0x7c, 0xe6, 0x07, + 0x20, 0x08, 0x7c, 0xd6, 0x08, 0x67, 0x7d, 0xc6, 0x20, 0x6f, 0x10, 0xe4, 0xc9, 0x2a, 0xae, 0xde, + 0x7c, 0xe6, 0x7f, 0x57, 0x5d, 0x06, 0x08, 0xc5, 0x3a, 0xec, 0xb3, 0x4f, 0x06, 0x00, 0xe5, 0xd5, + 0xed, 0xb0, 0xd1, 0xe1, 0x24, 0x14, 0xc1, 0x10, 0xee, 0xc9, 0x21, 0x00, 0x58, 0xcd, 0xdb, 0x6c, + 0x21, 0x7b, 0x5a, 0xcd, 0xdb, 0x6c, 0x11, 0x1b, 0x58, 0xcd, 0xf2, 0x6c, 0x11, 0x60, 0x5a, 0xcd, + 0xf2, 0x6c, 0xc9, 0x06, 0x05, 0x11, 0xf4, 0xad, 0x0e, 0x05, 0x1a, 0x77, 0x23, 0x0d, 0x20, 0xfb, + 0x13, 0xc5, 0x01, 0x1b, 0x00, 0x09, 0xc1, 0x10, 0xef, 0xc9, 0x06, 0x05, 0xc5, 0x01, 0x05, 0x00, + 0x21, 0xf4, 0xad, 0xed, 0xb0, 0xeb, 0x01, 0x1b, 0x00, 0x09, 0xeb, 0xc1, 0x10, 0xee, 0xc9, 0x3a, + 0x78, 0x5c, 0xfe, 0x40, 0xd0, 0x21, 0x06, 0x58, 0x22, 0x41, 0x6d, 0x21, 0x19, 0x58, 0x22, 0x44, + 0x6d, 0x21, 0x80, 0x58, 0x22, 0x57, 0x6d, 0x3a, 0x79, 0x5c, 0xe6, 0x01, 0x28, 0x12, 0x21, 0x66, + 0x5a, 0x22, 0x41, 0x6d, 0x21, 0x79, 0x5a, 0x22, 0x44, 0x6d, 0x21, 0xe0, 0x5a, 0x22, 0x57, 0x6d, + 0x3a, 0x78, 0x5c, 0xa7, 0x20, 0x09, 0x3e, 0x02, 0x32, 0x06, 0x58, 0x32, 0x19, 0x58, 0xc9, 0xe6, + 0x01, 0xc0, 0x3a, 0x78, 0x5c, 0x0f, 0xe6, 0x1f, 0xfe, 0x1b, 0xd0, 0x4f, 0x06, 0x05, 0x21, 0x80, + 0x58, 0x0d, 0xcd, 0x6d, 0x6d, 0x0c, 0xcd, 0x8d, 0x6d, 0x0c, 0xc5, 0x01, 0x20, 0x00, 0xa7, 0xed, + 0x42, 0xc1, 0x10, 0xed, 0xc9, 0xc5, 0xe5, 0x7d, 0xb1, 0x6f, 0x5d, 0x7c, 0xf6, 0x80, 0x57, 0xe5, + 0xd5, 0xeb, 0x01, 0x03, 0x00, 0xed, 0xb0, 0xe1, 0xd1, 0x7d, 0xee, 0x1f, 0x6f, 0x5d, 0x0e, 0x03, + 0xed, 0xb8, 0xe1, 0xc1, 0xc9, 0xc5, 0xe5, 0x7d, 0xb1, 0x6f, 0xe5, 0xcd, 0xb1, 0x6d, 0x23, 0xcd, + 0xbd, 0x6d, 0x23, 0xcd, 0xb1, 0x6d, 0xe1, 0x7d, 0xee, 0x1f, 0x6f, 0xcd, 0xb1, 0x6d, 0x2b, 0xcd, + 0xbd, 0x6d, 0x2b, 0xcd, 0xb1, 0x6d, 0xe1, 0xc1, 0xc9, 0x7d, 0xe6, 0x1f, 0xfe, 0x06, 0xd8, 0xfe, + 0x1a, 0xd0, 0x36, 0x02, 0xc9, 0x7d, 0xe6, 0x1f, 0xfe, 0x06, 0xd8, 0xfe, 0x1a, 0xd0, 0x36, 0x42, + 0xc9, 0x21, 0x00, 0xa9, 0xcd, 0x38, 0x6e, 0x21, 0xc8, 0xa9, 0xcd, 0x38, 0x6e, 0x21, 0x90, 0xaa, + 0xcd, 0x38, 0x6e, 0x21, 0x58, 0xab, 0xcd, 0x38, 0x6e, 0xf3, 0x11, 0x00, 0xa9, 0x21, 0x66, 0xd0, + 0xcd, 0xf7, 0x64, 0x11, 0xc8, 0xa9, 0x21, 0x6b, 0xd0, 0xcd, 0xf7, 0x64, 0x11, 0x90, 0xaa, 0x21, + 0x70, 0xd0, 0xcd, 0xf7, 0x64, 0x11, 0x58, 0xab, 0x21, 0x75, 0xd0, 0xcd, 0xf7, 0x64, 0x06, 0x05, + 0x21, 0x06, 0xd8, 0x11, 0xe6, 0xda, 0xc5, 0x01, 0x14, 0x00, 0xed, 0xb0, 0x0e, 0x0c, 0x09, 0x0e, + 0x34, 0xa7, 0xeb, 0xed, 0x42, 0xeb, 0xc1, 0x10, 0xed, 0x21, 0x00, 0xd0, 0x11, 0x00, 0x50, 0x01, + 0x00, 0x08, 0xed, 0xb0, 0x21, 0x60, 0xda, 0x11, 0x60, 0x5a, 0x0e, 0xa0, 0xed, 0xb0, 0xfb, 0xc9, + 0xcd, 0x25, 0x64, 0xcd, 0x25, 0x64, 0x54, 0x5d, 0xcd, 0xd9, 0x63, 0xc9, 0x21, 0x30, 0x75, 0xcd, + 0x52, 0x6e, 0xc9, 0x21, 0x67, 0x6e, 0xcd, 0x52, 0x6e, 0xc9, 0xaf, 0x01, 0xfd, 0xff, 0xed, 0x79, + 0xf5, 0x7e, 0x01, 0xfd, 0xbf, 0xed, 0x79, 0x23, 0xf1, 0x3c, 0xfe, 0x0e, 0xc8, 0x18, 0xec, 0x30, + 0x00, 0x01, 0x00, 0x50, 0x00, 0x00, 0x38, 0x10, 0x10, 0x10, 0xc4, 0x50, 0x01, 0x16, 0x08, 0x0e, + 0x10, 0x21, 0x00, 0x40, 0x41, 0xaf, 0xcb, 0x1e, 0x23, 0xaf, 0xcb, 0x16, 0x23, 0x10, 0xf6, 0x41, + 0xaf, 0xcb, 0x16, 0x23, 0xaf, 0xcb, 0x1e, 0x23, 0x10, 0xf6, 0x7c, 0xfe, 0x58, 0x20, 0xe5, 0x15, + 0x20, 0xdd, 0xc9, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x72, + 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x80, 0x00, 0x00, 0x03, 0xd0, 0x40, + 0x00, 0x00, 0x07, 0xa8, 0x20, 0x00, 0x00, 0x0b, 0x54, 0x10, 0x00, 0x00, 0x1e, 0xaa, 0x08, 0x00, + 0x00, 0x2d, 0x55, 0x04, 0x00, 0x00, 0x7a, 0xaa, 0x82, 0x00, 0x00, 0xb5, 0x55, 0x41, 0x00, 0x01, + 0x6a, 0xaa, 0xa0, 0x80, 0x02, 0xd5, 0x45, 0x50, 0x40, 0x05, 0x2a, 0x82, 0xa8, 0x20, 0x09, 0x55, + 0x01, 0x54, 0x10, 0x12, 0xaa, 0x00, 0xaa, 0x08, 0x25, 0x54, 0x00, 0x55, 0x04, 0x22, 0xa8, 0x00, + 0x2a, 0x84, 0x25, 0x50, 0x00, 0x15, 0x44, 0x2a, 0xa4, 0x00, 0x3a, 0xa4, 0x35, 0x42, 0x00, 0x7d, + 0x54, 0x2a, 0xa1, 0x00, 0xba, 0xa8, 0x15, 0x51, 0x01, 0xf5, 0x50, 0x0a, 0xa9, 0x03, 0xea, 0xa0, + 0x05, 0x55, 0x05, 0xd5, 0x40, 0x02, 0xab, 0x0f, 0xaa, 0x80, 0x01, 0x55, 0x17, 0x55, 0x00, 0x00, + 0xaa, 0x2e, 0xaa, 0x00, 0x00, 0x54, 0x55, 0x54, 0x00, 0x00, 0x28, 0x9a, 0xa8, 0x00, 0x00, 0x11, + 0x35, 0x50, 0x00, 0x00, 0x01, 0x2a, 0xa0, 0x00, 0x00, 0x01, 0x55, 0x40, 0x00, 0x00, 0x01, 0xaa, + 0x80, 0x00, 0x00, 0x01, 0x55, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, + 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x05, 0xd0, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x90, 0x02, 0x80, 0x01, 0xc0, 0x2f, 0xf4, 0x01, 0xc0, 0x00, + 0xa0, 0x04, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x80, 0x10, 0x08, 0x00, 0x80, 0x04, 0x80, 0x02, 0xa0, 0x01, 0xc0, 0x5f, 0xfd, 0x01, 0xc0, 0x02, + 0x80, 0x04, 0x90, 0x00, 0x88, 0x10, 0x80, 0x00, 0x00, 0x00, 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x10, 0x10, 0x00, 0x10, 0x00, + 0x00, 0x36, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x7e, 0x24, 0x24, 0x7e, 0x24, 0x00, + 0x00, 0x08, 0x3e, 0x68, 0x3e, 0x0a, 0x7e, 0x08, 0x00, 0x66, 0x4c, 0x18, 0x30, 0x66, 0x4e, 0x00, + 0x18, 0x24, 0x2c, 0x78, 0xda, 0xcc, 0x7e, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x30, 0x18, 0x18, 0x18, 0x18, 0x30, 0x00, + 0x00, 0x00, 0x14, 0x08, 0x7e, 0x18, 0x2c, 0x00, 0x00, 0x00, 0x08, 0x08, 0x7e, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x38, 0x6c, 0xce, 0xd6, 0xe6, 0x6c, 0x38, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x3e, 0x7c, 0x00, + 0x78, 0xcc, 0x1c, 0x38, 0x70, 0xe6, 0xfe, 0x00, 0x7e, 0xcc, 0x18, 0x3c, 0x0e, 0xce, 0x7c, 0x00, + 0x24, 0x6c, 0xcc, 0xcc, 0xfc, 0x0c, 0x0c, 0x00, 0xfe, 0xc2, 0xf8, 0xcc, 0x0e, 0xce, 0x7c, 0x00, + 0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00, 0x7e, 0x66, 0x0e, 0x1c, 0x38, 0x30, 0x30, 0x00, + 0x7c, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0x1c, 0x30, 0x00, 0x30, 0x00, + 0x00, 0x3c, 0x4a, 0x56, 0xfe, 0xc0, 0x7c, 0x00, 0x3c, 0x66, 0xc6, 0xfe, 0xc6, 0xe6, 0x66, 0x00, + 0xf8, 0xcc, 0xcc, 0xfc, 0xc6, 0xc6, 0xfc, 0x00, 0x38, 0x6c, 0xc6, 0xc0, 0xc0, 0xe6, 0x7c, 0x00, + 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0xfe, 0xe6, 0x60, 0x7c, 0x60, 0xe6, 0xfe, 0x00, + 0xfe, 0xe6, 0x60, 0x7c, 0x60, 0xe0, 0xc0, 0x00, 0x3c, 0x66, 0xc0, 0xde, 0xc6, 0xee, 0x7c, 0x00, + 0xc4, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x46, 0x00, 0x7e, 0x98, 0x30, 0x30, 0x30, 0x1a, 0xfc, 0x00, + 0x3e, 0x0c, 0x0c, 0xe6, 0x66, 0xc6, 0x7c, 0x00, 0xe6, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0xc6, 0x00, + 0xc0, 0xe0, 0x60, 0x60, 0x60, 0xe6, 0xfe, 0x00, 0x46, 0xee, 0xfe, 0xd6, 0xd6, 0xc6, 0x46, 0x00, + 0xc4, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0x46, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xee, 0x7c, 0x00, + 0xfc, 0xe6, 0xc6, 0xce, 0xfc, 0xc0, 0xc0, 0x00, 0x38, 0x6c, 0xc6, 0xd6, 0xce, 0xec, 0x7a, 0x00, + 0xfc, 0xc6, 0xc6, 0xfc, 0xd8, 0xcc, 0xc6, 0x00, 0x3e, 0x66, 0x70, 0x1c, 0xc6, 0xc6, 0x7c, 0x00, + 0xfe, 0xba, 0x18, 0x18, 0x18, 0x38, 0x30, 0x00, 0xe6, 0x66, 0xc6, 0xc6, 0xc6, 0xe6, 0x7c, 0x00, + 0xce, 0xcc, 0xc6, 0xc6, 0x66, 0x7c, 0x38, 0x00, 0xce, 0xc6, 0xd6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, + 0xc6, 0xee, 0x6c, 0x38, 0x6c, 0xee, 0xc6, 0x00, 0xc6, 0xc6, 0x6e, 0x3c, 0x18, 0x18, 0x18, 0x00, + 0x76, 0xec, 0x98, 0x30, 0x62, 0xde, 0xbc, 0x00, 0x00, 0x1c, 0x10, 0x10, 0x30, 0x30, 0x3c, 0x00, + 0x00, 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x1c, 0x04, 0x04, 0x0c, 0x0c, 0x3c, 0x00, + 0x00, 0x08, 0x1c, 0x2a, 0x08, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x1c, 0x22, 0x78, 0x60, 0x60, 0xfc, 0x00, 0x00, 0x00, 0x1c, 0x02, 0x3e, 0x66, 0x3e, 0x00, + 0x00, 0x20, 0x20, 0x3c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x1c, 0x20, 0x60, 0x60, 0x3c, 0x00, + 0x00, 0x02, 0x02, 0x1e, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x7c, 0x60, 0x3e, 0x00, + 0x00, 0x0c, 0x10, 0x18, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x1e, 0x22, 0x66, 0x3e, 0x06, 0x3c, + 0x00, 0x20, 0x20, 0x3c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x08, 0x00, 0x08, 0x18, 0x18, 0x3c, 0x00, + 0x00, 0x04, 0x00, 0x04, 0x0c, 0x0c, 0x6c, 0x38, 0x00, 0x20, 0x28, 0x30, 0x70, 0x78, 0x6c, 0x00, + 0x00, 0x10, 0x10, 0x10, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x34, 0x2a, 0x6a, 0x6a, 0x6a, 0x00, + 0x00, 0x00, 0x3c, 0x22, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0x3c, 0x22, 0x66, 0x7c, 0x60, 0x60, 0x00, 0x00, 0x3c, 0x44, 0xcc, 0x7c, 0x0c, 0x0e, + 0x00, 0x00, 0x1c, 0x20, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x1c, 0x20, 0x3c, 0x06, 0x7c, 0x00, + 0x00, 0x10, 0x38, 0x10, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x22, 0x22, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0x44, 0x44, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x22, 0x2a, 0x6a, 0x7e, 0x3c, 0x00, + 0x00, 0x00, 0x22, 0x14, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x22, 0x22, 0x66, 0x3e, 0x06, 0x3c, + 0x00, 0x00, 0x3e, 0x04, 0x18, 0x30, 0x7e, 0x00, 0x00, 0x0e, 0x08, 0x30, 0x10, 0x18, 0x1e, 0x00, + 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x38, 0x08, 0x04, 0x08, 0x18, 0x78, 0x00, + 0x00, 0x12, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x99, 0xa1, 0xa1, 0x99, 0x42, 0x3c, + 0x09, 0x20, 0x00, 0x00, 0x00, 0x05, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x91, 0xc0, 0x00, 0x07, 0xbe, + 0xe4, 0x1e, 0x00, 0x70, 0x02, 0x7e, 0xa0, 0xff, 0x02, 0x01, 0x21, 0xf5, 0x00, 0x57, 0x09, 0x60, + 0x1f, 0x95, 0xf8, 0x01, 0x20, 0x00, 0xff, 0xe0, 0x01, 0x11, 0xfc, 0x2d, 0xff, 0x01, 0x13, 0x9f, + 0xff, 0xe1, 0x00, 0x17, 0xff, 0xff, 0x99, 0x01, 0x16, 0x04, 0xbf, 0xfe, 0x00, 0x2c, 0x01, 0xfd, + 0xdf, 0x01, 0x2c, 0xff, 0x07, 0x57, 0x00, 0x3f, 0x00, 0x0e, 0xbb, 0x00, 0x78, 0x00, 0x0a, 0x96, + 0x00, 0x00, 0x00, 0x15, 0x02, 0x00, 0x10, 0x00, 0x20, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x04, 0x00, + 0x10, 0x00, 0x80, 0x09, 0x00, 0x10, 0x83, 0x00, 0x12, 0x01, 0x10, 0x0a, 0x00, 0xa4, 0x00, 0x12, + 0x3c, 0x21, 0x48, 0x00, 0x54, 0xd2, 0x12, 0xb0, 0x00, 0x3b, 0x29, 0x41, 0x50, 0x57, 0xec, 0xd1, + 0x22, 0xa0, 0x00, 0x5b, 0xe4, 0x0c, 0x40, 0x00, 0xbf, 0x5b, 0x58, 0x80, 0x00, 0x77, 0xff, 0xf1, + 0x03, 0x02, 0xdf, 0xb7, 0x56, 0xfc, 0x01, 0xbf, 0xff, 0xe8, 0x00, 0x01, 0x3f, 0x7f, 0xc7, 0x29, + 0x02, 0x63, 0xc7, 0xf1, 0xff, 0x02, 0xdf, 0x8b, 0xf8, 0x1b, 0x04, 0x07, 0xfe, 0xdf, 0xff, 0x04, + 0x01, 0xff, 0xff, 0xff, 0x04, 0xf8, 0x7f, 0xef, 0xff, 0x0b, 0x07, 0x8b, 0xff, 0x02, 0x0c, 0x00, + 0x7f, 0x74, 0x1f, 0x08, 0x00, 0x00, 0xff, 0xf0, 0x07, 0x00, 0x00, 0x0f, 0xc0, 0x71, 0x00, 0x00, + 0x10, 0x38, 0x02, 0x80, 0x00, 0x2f, 0x8c, 0x5e, 0x80, 0x00, 0x60, 0x72, 0xe4, 0x80, 0x00, 0x40, + 0x99, 0x85, 0x00, 0x00, 0x43, 0x79, 0x09, 0x00, 0x00, 0x45, 0xfc, 0x0a, 0x00, 0x00, 0x4b, 0xb4, + 0x12, 0x00, 0x00, 0x2f, 0x94, 0x92, 0x00, 0x00, 0x2f, 0x12, 0xe4, 0x00, 0x01, 0x17, 0x9a, 0xd8, + 0x00, 0x00, 0x17, 0x89, 0xa8, 0x00, 0x00, 0x0f, 0xdd, 0x90, 0x00, 0x01, 0x05, 0xfc, 0x60, 0x00, + 0x00, 0x03, 0xfc, 0xa0, 0x00, 0x21, 0x01, 0x7e, 0xc0, 0x00, 0x09, 0x00, 0xef, 0x40, 0x00, 0x05, + 0x40, 0x3f, 0x80, 0x00, 0x03, 0x80, 0x0b, 0x00, 0x05, 0xbe, 0xea, 0x45, 0x00, 0x00, 0x01, 0x80, + 0x02, 0x00, 0x00, 0x03, 0x40, 0x02, 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x31, 0x10, 0x01, + 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x0f, 0x79, 0x00, 0x00, 0x00, 0x77, 0xb7, 0x00, 0x00, 0x0f, + 0x98, 0xb3, 0x00, 0x09, 0xf0, 0x26, 0x6a, 0x00, 0x01, 0x02, 0xbc, 0x4a, 0x00, 0x09, 0xa7, 0xfd, + 0x84, 0x00, 0x82, 0xf9, 0xf7, 0x18, 0x00, 0x0b, 0xe1, 0xbc, 0x70, 0x00, 0x2a, 0x8f, 0xf0, 0xc0, + 0x00, 0x1a, 0xff, 0xe3, 0x00, 0x05, 0xf4, 0xff, 0x8c, 0x00, 0x00, 0x1c, 0xec, 0xf0, 0x00, 0x00, + 0x2c, 0xff, 0x00, 0x00, 0x00, 0x4c, 0xe0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x01, 0x00, 0x0f, 0x40, 0x0e, 0x00, 0x00, 0x30, 0x00, 0x11, 0xc1, 0x10, 0x4f, 0x48, + 0x2c, 0x31, 0x00, 0x70, 0x50, 0x43, 0x89, 0x40, 0x80, 0x2d, 0x40, 0x75, 0x80, 0x83, 0xd0, 0x81, + 0x92, 0xea, 0x87, 0xc0, 0x87, 0x8b, 0x80, 0x47, 0x80, 0x9f, 0xcb, 0x00, 0x4d, 0x41, 0x3d, 0xcb, + 0x20, 0x2e, 0x41, 0x7d, 0x93, 0x00, 0x3e, 0x42, 0xf7, 0x95, 0x00, 0x16, 0x27, 0xe7, 0xa4, 0x00, + 0x0e, 0x95, 0xcf, 0x29, 0x00, 0x05, 0x4f, 0xdf, 0x48, 0x00, 0x02, 0x6f, 0xfe, 0x91, 0x00, 0x01, + 0xfb, 0xff, 0xa0, 0x00, 0x00, 0xdf, 0xfb, 0x40, 0x00, 0x00, 0xff, 0xb6, 0x80, 0x00, 0x00, 0xbf, + 0xa4, 0x80, 0x00, 0x40, 0xdb, 0x45, 0x00, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x40, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x04, 0x42, 0x80, 0x8a, 0x00, 0x00, 0x47, 0x80, 0x01, 0x00, + 0x01, 0x4f, 0x80, 0x25, 0x00, 0x00, 0xf7, 0x52, 0x45, 0x01, 0x5f, 0xaf, 0x41, 0xa8, 0x80, 0x00, + 0x3f, 0x56, 0xf2, 0x80, 0x01, 0x5f, 0xfb, 0xb6, 0x80, 0x02, 0xbb, 0xff, 0xfa, 0x40, 0x00, 0xbf, + 0xe6, 0xfd, 0x40, 0x09, 0x6f, 0xcf, 0xf9, 0x40, 0x01, 0x5d, 0xe7, 0xf8, 0x80, 0x01, 0x1f, 0x7f, + 0xf1, 0x00, 0x02, 0x17, 0x12, 0x8e, 0x00, 0x02, 0x0f, 0x00, 0x1c, 0x00, 0x02, 0x04, 0x03, 0xe0, + 0x00, 0x03, 0x01, 0xfc, 0x00, 0x00, 0x01, 0xfe, 0xf0, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x3e, + 0xa8, 0x00, 0xec, 0x00, 0x41, 0xf0, 0x00, 0x72, 0x00, 0xbc, 0xdf, 0x60, 0xca, 0x01, 0x03, 0x30, + 0x00, 0xc9, 0x02, 0x60, 0xa8, 0x00, 0xe5, 0x04, 0xf0, 0xa0, 0x00, 0xf4, 0x85, 0xcc, 0xa2, 0x00, + 0xfa, 0x4f, 0xce, 0x20, 0x00, 0x7f, 0x5b, 0x8d, 0x40, 0x00, 0x7b, 0x3f, 0x9e, 0x40, 0x00, 0x7f, + 0xbf, 0xba, 0xa0, 0x00, 0xdb, 0xfd, 0x74, 0xa0, 0x00, 0xbf, 0xb7, 0xf5, 0x00, 0x00, 0x4d, 0xf7, + 0x49, 0x20, 0x00, 0x19, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x54, 0xb2, 0x00, 0x00, 0x42, 0x01, 0x2c, + 0x00, 0x00, 0x20, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x40, 0x10, 0xc5, 0x00, + 0x00, 0xa1, 0x04, 0x24, 0x80, 0x00, 0x02, 0x00, 0x2a, 0x40, 0x00, 0x74, 0x20, 0x6b, 0x40, 0x00, + 0x69, 0x59, 0x5f, 0x21, 0x00, 0xad, 0x5b, 0xde, 0xa0, 0x00, 0xfd, 0xaf, 0xf7, 0x91, 0x00, 0x6f, + 0x27, 0x73, 0x50, 0x00, 0xff, 0x47, 0xfb, 0xc9, 0x00, 0xde, 0x85, 0xb9, 0xa9, 0x10, 0xbd, 0x03, + 0xbc, 0xa5, 0x00, 0x3d, 0x02, 0xfc, 0xd5, 0x40, 0x3a, 0x02, 0xdc, 0xd5, 0x80, 0xba, 0x01, 0x5c, + 0xd2, 0xf5, 0xf4, 0x01, 0x3c, 0xcb, 0x80, 0x68, 0x00, 0x8b, 0xcd, 0x00, 0xb0, 0x00, 0x82, 0x89, + 0x20, 0xc0, 0x00, 0x80, 0x10, 0x00, 0x80, 0x00, 0x40, 0xe1, 0x00, 0x00, 0x00, 0x3f, 0x01, 0x00, + 0x47, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x47, 0x47, 0x47, 0x47, 0x47, 0x07, 0x47, 0x47, + 0x47, 0x47, 0x47, 0x07, 0x47, 0x47, 0x07, 0x07, 0x47, 0x07, 0x47, 0x07, 0x47, 0x47, 0x47, 0x07, + 0x07, 0x47, 0x07, 0x07, 0x47, 0x47, 0x07, 0x07, 0x05, 0x45, 0x47, 0x47, 0x47, 0x07, 0x45, 0x47, + 0x07, 0x47, 0x07, 0x07, 0x07, 0x07, 0x47, 0x07, 0x47, 0x07, 0x07, 0x07, 0x05, 0x45, 0x45, 0x45, + 0x05, 0x05, 0x45, 0x05, 0x45, 0x45, 0x05, 0x05, 0x05, 0x05, 0x45, 0x45, 0x45, 0x45, 0x05, 0x07, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x45, 0x45, 0x05, 0x45, 0x45, 0x05, 0x45, 0x45, 0x05, + 0x45, 0x05, 0x45, 0x07, 0x78, 0x41, 0xa6, 0xe6, 0x66, 0x00, 0x78, 0x41, 0x9e, 0xde, 0x5e, 0x00, + 0xfe, 0x42, 0x9e, 0xde, 0x5e, 0x00, 0xfe, 0x42, 0x96, 0xd6, 0x56, 0x00, 0x82, 0x43, 0x9e, 0xde, + 0x5e, 0x00, 0x82, 0x43, 0x96, 0xd6, 0x56, 0x00, 0xac, 0x46, 0xa6, 0xe6, 0x66, 0x00, 0xac, 0x46, + 0x9e, 0xde, 0x5e, 0x00, 0xdb, 0x48, 0xbe, 0xfe, 0x7e, 0x00, 0xdb, 0x48, 0xb6, 0xf6, 0x76, 0x00, + 0xe8, 0x49, 0xa6, 0xe6, 0x66, 0x00, 0xe8, 0x49, 0x9e, 0xde, 0x5e, 0x00, 0x25, 0x4e, 0xbe, 0xfe, + 0x7e, 0x00, 0x25, 0x4e, 0xb6, 0xf6, 0x76, 0x00, 0x53, 0x4e, 0xbe, 0xfe, 0x7e, 0x00, 0x53, 0x4e, + 0xb6, 0xf6, 0x76, 0x00, 0x8e, 0x56, 0x8e, 0xce, 0x4e, 0x00, 0x8e, 0x56, 0x86, 0xc6, 0x46, 0x00, + 0xa1, 0x56, 0xa6, 0xe6, 0x66, 0x00, 0xa1, 0x56, 0x9e, 0xde, 0x5e, 0x00, 0x78, 0x42, 0xa6, 0xe6, + 0x66, 0x00, 0x78, 0x42, 0x9e, 0xde, 0x5e, 0x00, 0xfe, 0x43, 0x9e, 0xde, 0x5e, 0x00, 0xfe, 0x43, + 0x96, 0xd6, 0x56, 0x00, 0x82, 0x44, 0x9e, 0xde, 0x5e, 0x00, 0x82, 0x44, 0x96, 0xd6, 0x56, 0x00, + 0xac, 0x47, 0xa6, 0xe6, 0x66, 0x00, 0xac, 0x47, 0x9e, 0xde, 0x5e, 0x00, 0xdb, 0x49, 0xbe, 0xfe, + 0x7e, 0x00, 0xdb, 0x49, 0xb6, 0xf6, 0x76, 0x00, 0xe8, 0x4a, 0xa6, 0xe6, 0x66, 0x00, 0xe8, 0x4a, + 0x9e, 0xde, 0x5e, 0x00, 0x25, 0x4f, 0xbe, 0xfe, 0x7e, 0x00, 0x25, 0x4f, 0xb6, 0xf6, 0x76, 0x00, + 0x53, 0x4f, 0xbe, 0xfe, 0x7e, 0x00, 0x53, 0x4f, 0xb6, 0xf6, 0x76, 0x00, 0x8e, 0x57, 0x8e, 0xce, + 0x4e, 0x00, 0x8e, 0x57, 0x86, 0xc6, 0x46, 0x00, 0xa1, 0x57, 0xa6, 0xe6, 0x66, 0x00, 0xa1, 0x57, + 0x9e, 0xde, 0x5e, 0x00, 0xad, 0x43, 0x8e, 0xce, 0x4e, 0x00, 0xe2, 0x43, 0xb6, 0xf6, 0x76, 0x00, + 0xea, 0x43, 0xa6, 0xe6, 0x66, 0x00, 0x46, 0x44, 0xbe, 0xfe, 0x7e, 0x00, 0x2f, 0x48, 0x86, 0xc6, + 0x46, 0x00, 0xa7, 0x49, 0x9e, 0xde, 0x5e, 0x00, 0xea, 0x49, 0x8e, 0xce, 0x4e, 0x00, 0x6c, 0x4a, + 0xb6, 0xf6, 0x76, 0x00, 0xb4, 0x4b, 0x8e, 0xce, 0x4e, 0x00, 0xe3, 0x4b, 0x86, 0xc6, 0x46, 0x00, + 0x3a, 0x4d, 0xbe, 0xfe, 0x7e, 0x00, 0x80, 0x4d, 0x96, 0xd6, 0x56, 0x00, 0x9d, 0x4d, 0x86, 0xc6, + 0x46, 0x00, 0xb0, 0x4d, 0x96, 0xd6, 0x56, 0x00, 0xd2, 0x52, 0x86, 0xc6, 0x46, 0x00, 0x2d, 0x53, + 0xae, 0xee, 0x6e, 0x00, 0x32, 0x53, 0x9e, 0xde, 0x5e, 0x00, 0x17, 0x54, 0xa6, 0xe6, 0x66, 0x00, + 0x9c, 0x55, 0xb6, 0xf6, 0x76, 0x00, 0x89, 0x57, 0xb6, 0xf6, 0x76, 0x00, 0x2c, 0x40, 0x06, 0x48, + 0xd4, 0x78, 0x49, 0x50, 0x07, 0x06, 0x46, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc3, 0x59, 0xeb, 0xc3, 0xda, 0xeb, 0xc3, 0x44, 0xed, 0xf3, 0x2a, 0x35, 0xf0, 0x22, 0x60, 0xec, + 0x2a, 0x37, 0xf0, 0x22, 0x74, 0xec, 0x2a, 0x39, 0xf0, 0x22, 0x88, 0xec, 0x3e, 0x01, 0x32, 0x62, + 0xec, 0x32, 0x76, 0xec, 0x32, 0x8a, 0xec, 0x3e, 0x08, 0x32, 0x63, 0xec, 0x32, 0x77, 0xec, 0x32, + 0x8b, 0xec, 0x21, 0x20, 0xec, 0x11, 0x20, 0x00, 0x22, 0xfa, 0xeb, 0x19, 0x22, 0xfc, 0xeb, 0x19, + 0x22, 0xfe, 0xeb, 0x21, 0x00, 0x00, 0x22, 0x6e, 0xec, 0x22, 0x82, 0xec, 0x22, 0x96, 0xec, 0x22, + 0x70, 0xec, 0x22, 0x84, 0xec, 0x22, 0x98, 0xec, 0xaf, 0x32, 0xf4, 0xeb, 0xc9, 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, 0x3e, 0xff, 0x16, 0x07, 0xcd, 0x9b, + 0xed, 0xcd, 0x85, 0xed, 0x00, 0xc9, 0xe3, 0xff, 0xf6, 0x03, 0xbd, 0x00, 0x0d, 0xff, 0x0a, 0x0d, + 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x03, 0x4f, 0x69, 0x5c, 0xc4, 0x16, 0xc4, 0x3c, 0xc4, 0x5c, 0xc4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xc7, 0x1c, 0xd8, 0x94, 0xc7, 0x94, 0xc7, + 0x16, 0x95, 0xd7, 0xc7, 0xb5, 0xc6, 0x68, 0xaa, 0x5a, 0xaa, 0xfe, 0x07, 0x58, 0xaa, 0xfe, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xc7, 0x1c, 0xd8, + 0x94, 0xc7, 0xb5, 0xc6, 0xb5, 0xc6, 0x63, 0x8c, 0x94, 0xc7, 0xb5, 0xc6, 0x05, 0xab, 0xe6, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xc7, 0x94, 0xc7, 0x00, 0x94, + 0x94, 0xc7, 0x94, 0xc7, 0x57, 0x89, 0x94, 0xc7, 0x57, 0x89, 0xb5, 0xc6, 0xc5, 0xab, 0xe6, 0x02, + 0xfa, 0xaa, 0x05, 0x01, 0xd4, 0xaa, 0x01, 0xe3, 0xff, 0x0a, 0xcc, 0xaa, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4d, 0x0f, 0x09, 0xab, 0x0f, 0x08, 0x9d, 0xab, 0x01, 0xf6, 0x03, 0x0d, 0x97, 0xab, + 0xa7, 0xab, 0x00, 0x00, 0xa2, 0xab, 0x19, 0x08, 0xcf, 0xab, 0x05, 0x08, 0xd0, 0xad, 0x01, 0xbd, + 0x00, 0x0a, 0xc4, 0xad, 0xda, 0xad, 0x00, 0x00, 0xd5, 0xad, 0x36, 0x04, 0xbf, 0x0f, 0xdc, 0x0e, + 0x07, 0x0e, 0x3d, 0x0d, 0x7f, 0x0c, 0xcc, 0x0b, 0x22, 0x0b, 0x82, 0x0a, 0xeb, 0x09, 0x5d, 0x09, + 0xd6, 0x08, 0x57, 0x08, 0xdf, 0x07, 0x6e, 0x07, 0x03, 0x07, 0x9f, 0x06, 0x40, 0x06, 0xe6, 0x05, + 0x91, 0x05, 0x41, 0x05, 0xf6, 0x04, 0xae, 0x04, 0x6b, 0x04, 0x2c, 0x04, 0xf0, 0x03, 0xb7, 0x03, + 0x82, 0x03, 0x4f, 0x03, 0x20, 0x03, 0xf3, 0x02, 0xc8, 0x02, 0xa1, 0x02, 0x7b, 0x02, 0x57, 0x02, + 0x36, 0x02, 0x16, 0x02, 0xf8, 0x01, 0xdc, 0x01, 0xc1, 0x01, 0xa8, 0x01, 0x90, 0x01, 0x79, 0x01, + 0x64, 0x01, 0x50, 0x01, 0x3d, 0x01, 0x2c, 0x01, 0x1b, 0x01, 0x0b, 0x01, 0xfc, 0x00, 0xee, 0x00, + 0xe0, 0x00, 0xd4, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x96, 0x00, + 0x8d, 0x00, 0x85, 0x00, 0x7e, 0x00, 0x77, 0x00, 0x70, 0x00, 0x6a, 0x00, 0x64, 0x00, 0x5e, 0x00, + 0x59, 0x00, 0x54, 0x00, 0x4f, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x43, 0x00, 0x3f, 0x00, 0x3b, 0x00, + 0x38, 0x00, 0x35, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x2d, 0x00, 0x2a, 0x00, 0x28, 0x00, 0x25, 0x00, + 0x23, 0x00, 0x21, 0x00, 0xdd, 0x21, 0x60, 0xec, 0x2a, 0xfa, 0xeb, 0x3e, 0x01, 0xcd, 0xb0, 0xed, + 0x22, 0xfa, 0xeb, 0xdd, 0x21, 0x74, 0xec, 0x2a, 0xfc, 0xeb, 0x3e, 0x02, 0xcd, 0xb0, 0xed, 0x22, + 0xfc, 0xeb, 0xdd, 0x21, 0x88, 0xec, 0x2a, 0xfe, 0xeb, 0x3e, 0x03, 0xcd, 0xb0, 0xed, 0x22, 0xfe, + 0xeb, 0x3a, 0x8b, 0xec, 0x07, 0x47, 0x3a, 0x77, 0xec, 0xb0, 0x07, 0x47, 0x3a, 0x63, 0xec, 0xb0, + 0x16, 0x07, 0xcd, 0x9b, 0xed, 0x16, 0x0d, 0x21, 0xf3, 0xeb, 0x7a, 0x01, 0xfd, 0xff, 0xed, 0x79, + 0x7e, 0x06, 0xbf, 0xed, 0x79, 0x2b, 0x15, 0xf2, 0x8a, 0xed, 0xc9, 0x01, 0xe6, 0xeb, 0x6a, 0x26, + 0x00, 0x09, 0x77, 0xc9, 0xed, 0x73, 0xf8, 0xeb, 0x2a, 0xf8, 0xeb, 0xed, 0x7b, 0xf6, 0xeb, 0xc9, + 0xed, 0x73, 0xf6, 0xeb, 0xf9, 0x32, 0xf5, 0xeb, 0xdd, 0x35, 0x02, 0xca, 0xbf, 0xee, 0xdd, 0x35, + 0x06, 0x20, 0x36, 0xdd, 0x6e, 0x04, 0xdd, 0x66, 0x05, 0x7e, 0xfe, 0x80, 0x20, 0x0c, 0x23, 0x5e, + 0x23, 0x56, 0xdd, 0x73, 0x04, 0xdd, 0x72, 0x05, 0x18, 0xe9, 0xfe, 0x1e, 0x38, 0x0c, 0xd6, 0x32, + 0xdd, 0x77, 0x09, 0xdd, 0x36, 0x06, 0x01, 0x23, 0x18, 0x09, 0xdd, 0x77, 0x09, 0x23, 0x7e, 0xdd, + 0x77, 0x06, 0x23, 0xdd, 0x75, 0x04, 0xdd, 0x74, 0x05, 0xdd, 0x7e, 0x07, 0xdd, 0xb6, 0x08, 0xca, + 0x82, 0xee, 0xdd, 0xcb, 0x0e, 0x56, 0xc2, 0x82, 0xee, 0xdd, 0x6e, 0x0c, 0xdd, 0x66, 0x0d, 0x7e, + 0x23, 0xdd, 0x75, 0x0c, 0xdd, 0x74, 0x0d, 0xfe, 0x80, 0x20, 0x06, 0x7e, 0x23, 0x66, 0x6f, 0x18, + 0xee, 0xfe, 0x82, 0xc2, 0x2d, 0xee, 0xdd, 0xcb, 0x0e, 0xde, 0xc3, 0x0f, 0xee, 0xfe, 0x83, 0xc2, + 0x39, 0xee, 0xdd, 0xcb, 0x0e, 0x9e, 0xc3, 0x0f, 0xee, 0xfe, 0x84, 0xc2, 0x49, 0xee, 0x3e, 0x09, + 0xdd, 0xae, 0x03, 0xdd, 0x77, 0x03, 0xc3, 0x0f, 0xee, 0xdd, 0xcb, 0x0e, 0x5e, 0xca, 0x6b, 0xee, + 0xdd, 0x86, 0x12, 0xdd, 0x77, 0x12, 0x3d, 0x87, 0x5f, 0x16, 0x00, 0x21, 0x9c, 0xec, 0x19, 0x7e, + 0xdd, 0x77, 0x07, 0x23, 0x7e, 0xdd, 0x77, 0x08, 0xc3, 0x82, 0xee, 0x5f, 0x16, 0x00, 0xdd, 0x6e, + 0x07, 0xdd, 0x66, 0x08, 0xe6, 0x80, 0xca, 0x7b, 0xee, 0x16, 0xff, 0x19, 0xdd, 0x75, 0x07, 0xdd, + 0x74, 0x08, 0x3a, 0xf4, 0xeb, 0x16, 0x06, 0xcd, 0x9b, 0xed, 0xdd, 0xcb, 0x0e, 0x96, 0x3a, 0xf5, + 0xeb, 0xc6, 0x07, 0x57, 0xdd, 0x7e, 0x07, 0xdd, 0xb6, 0x08, 0x28, 0x03, 0xdd, 0x7e, 0x09, 0xcd, + 0x9b, 0xed, 0x3a, 0xf5, 0xeb, 0x3d, 0x87, 0x57, 0xdd, 0x7e, 0x07, 0xcd, 0x9b, 0xed, 0x14, 0xdd, + 0x7e, 0x08, 0xcd, 0x9b, 0xed, 0xc3, 0xa4, 0xed, 0xdd, 0x75, 0x00, 0xdd, 0x74, 0x01, 0xc9, 0xdd, + 0x6e, 0x00, 0xdd, 0x66, 0x01, 0x7e, 0x23, 0xcd, 0xb8, 0xee, 0xcb, 0x7f, 0xc2, 0x3c, 0xef, 0x22, + 0xf8, 0xeb, 0xb7, 0x28, 0x1b, 0xdd, 0x86, 0x0f, 0xdd, 0x77, 0x12, 0xdd, 0xcb, 0x0e, 0x9e, 0x3d, + 0x87, 0x5f, 0x16, 0x00, 0x21, 0x9c, 0xec, 0x19, 0x5e, 0x23, 0x56, 0x2a, 0xf8, 0xeb, 0x18, 0x03, + 0x11, 0x00, 0x00, 0x7e, 0x23, 0xcd, 0xb8, 0xee, 0xdd, 0x77, 0x02, 0xdd, 0x73, 0x07, 0xdd, 0x72, + 0x08, 0xdd, 0x7e, 0x10, 0xdd, 0x77, 0x0c, 0xdd, 0x7e, 0x11, 0xdd, 0x77, 0x0d, 0xdd, 0xcb, 0x0e, + 0xd6, 0xdd, 0xcb, 0x0e, 0x4e, 0xc2, 0xbe, 0xed, 0xdd, 0xcb, 0x0e, 0x46, 0xca, 0x23, 0xef, 0xdd, + 0xcb, 0x0e, 0xce, 0xdd, 0x4e, 0x0a, 0xdd, 0x46, 0x0b, 0x0a, 0xdd, 0x77, 0x09, 0x03, 0x0a, 0x03, + 0xdd, 0x77, 0x06, 0xdd, 0x71, 0x04, 0xdd, 0x70, 0x05, 0xc3, 0x82, 0xee, 0xe6, 0x7f, 0x22, 0xf8, + 0xeb, 0x87, 0x5f, 0x16, 0x00, 0x21, 0x52, 0xef, 0x19, 0x7e, 0x23, 0x66, 0x6f, 0xe5, 0x2a, 0xf8, + 0xeb, 0xc9, 0x70, 0xef, 0x7c, 0xef, 0x8a, 0xef, 0x94, 0xef, 0xa6, 0xef, 0xb1, 0xef, 0xde, 0xef, + 0xbc, 0xef, 0xcc, 0xef, 0xd7, 0xef, 0xee, 0xef, 0xfc, 0xef, 0x0a, 0xf0, 0x17, 0xf0, 0x27, 0xf0, + 0x7e, 0xdd, 0x77, 0x00, 0x23, 0x7e, 0xdd, 0x77, 0x01, 0xc3, 0xbf, 0xee, 0x7e, 0xdd, 0x77, 0x00, + 0x23, 0x7e, 0xdd, 0x77, 0x01, 0x23, 0xe5, 0xc3, 0xbf, 0xee, 0x46, 0xc5, 0x23, 0xe5, 0xcd, 0xb8, + 0xee, 0xc3, 0xbf, 0xee, 0xd1, 0xc1, 0x10, 0x03, 0xc3, 0xbf, 0xee, 0xc5, 0xd5, 0xdd, 0x73, 0x00, + 0xdd, 0x72, 0x01, 0xc3, 0xbf, 0xee, 0x7e, 0x23, 0x32, 0xf4, 0xeb, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, + 0xee, 0x7e, 0x23, 0xcd, 0xb8, 0xee, 0xdd, 0x77, 0x03, 0xc3, 0xbf, 0xee, 0x7e, 0xdd, 0x77, 0x0a, + 0x23, 0x7e, 0xdd, 0x77, 0x0b, 0x23, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x7e, 0x23, 0xcd, 0xb8, + 0xee, 0xdd, 0x77, 0x0f, 0xc3, 0xbf, 0xee, 0xe1, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x7e, 0xdd, + 0x77, 0x10, 0x23, 0x7e, 0xdd, 0x77, 0x11, 0x23, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0xdd, 0xcb, + 0x0e, 0xc6, 0xdd, 0xcb, 0x0e, 0x8e, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0xdd, 0xcb, 0x0e, 0x86, + 0xdd, 0xcb, 0x0e, 0x8e, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x5e, 0x23, 0x56, 0x23, 0xcd, 0xb8, + 0xee, 0x01, 0xbf, 0xee, 0xc5, 0xd5, 0xc9, 0x3a, 0xf4, 0xeb, 0x86, 0xe6, 0x0f, 0x32, 0xf4, 0xeb, + 0x23, 0xcd, 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x7e, 0xdd, 0x86, 0x0f, 0xdd, 0x77, 0x0f, 0x23, 0xcd, + 0xb8, 0xee, 0xc3, 0xbf, 0xee, 0x99, 0xf5, 0x96, 0xf4, 0x3b, 0xf0, 0x82, 0x04, 0x81, 0x57, 0xf4, + 0x83, 0x81, 0x6d, 0xf3, 0x81, 0x6d, 0xf3, 0x81, 0x00, 0xf4, 0x81, 0x33, 0xf3, 0x81, 0x33, 0xf3, + 0x81, 0x17, 0xf3, 0x81, 0x6a, 0xf2, 0x81, 0x6a, 0xf2, 0x88, 0x00, 0x81, 0xba, 0xf2, 0x81, 0xba, + 0xf2, 0x88, 0x02, 0x81, 0xba, 0xf2, 0x88, 0x00, 0x81, 0xef, 0xf2, 0x82, 0x04, 0x81, 0x49, 0xf2, + 0x83, 0x81, 0x17, 0xf3, 0x81, 0x49, 0xf2, 0x81, 0x49, 0xf2, 0x81, 0x1a, 0xf3, 0x81, 0x49, 0xf2, + 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x2b, 0x23, 0x86, 0x9b, 0xf7, 0x2b, 0x0c, 0x86, 0x5e, 0xf7, + 0x2d, 0x48, 0x2b, 0x0c, 0x2d, 0x0c, 0x32, 0x0c, 0x30, 0x0c, 0x2d, 0x18, 0x30, 0x24, 0x86, 0x9b, + 0xf7, 0x30, 0x0c, 0x86, 0x5e, 0xf7, 0x32, 0x48, 0x32, 0x0c, 0x37, 0x0c, 0x36, 0x0c, 0x32, 0x0c, + 0x2d, 0x18, 0x2b, 0x24, 0x86, 0x9b, 0xf7, 0x2b, 0x0c, 0x86, 0x5e, 0xf7, 0x2d, 0x48, 0x32, 0x0c, + 0x86, 0x97, 0xf7, 0x32, 0x0c, 0x86, 0x5e, 0xf7, 0x32, 0x0c, 0x30, 0x0c, 0x2d, 0x18, 0x32, 0x24, + 0x86, 0x97, 0xf7, 0x32, 0x0c, 0x86, 0x5e, 0xf7, 0x30, 0x48, 0x32, 0x0c, 0x34, 0x0c, 0x37, 0x0c, + 0x36, 0x0c, 0x37, 0x0c, 0x39, 0x0c, 0x37, 0x24, 0x86, 0x9b, 0xf7, 0x37, 0x0c, 0x86, 0x5e, 0xf7, + 0x39, 0x30, 0x37, 0x06, 0x39, 0x0c, 0x37, 0x06, 0x39, 0x0c, 0x37, 0x06, 0x36, 0x0c, 0x37, 0x06, + 0x36, 0x0c, 0x34, 0x0c, 0x32, 0x0c, 0x30, 0x06, 0x32, 0x0c, 0x30, 0x06, 0x32, 0x0c, 0x30, 0x06, + 0x2f, 0x0c, 0x30, 0x06, 0x2f, 0x0c, 0x2d, 0x0c, 0x2b, 0x0c, 0x2d, 0x06, 0x2d, 0x0c, 0x2b, 0x06, + 0x2d, 0x0c, 0x2f, 0x06, 0x30, 0x0c, 0x32, 0x06, 0x34, 0x0c, 0x36, 0x0c, 0x37, 0x0c, 0x3b, 0x24, + 0x86, 0x97, 0xf7, 0x3b, 0x0c, 0x86, 0x5e, 0xf7, 0x39, 0x48, 0x37, 0x0c, 0x39, 0x0c, 0x3c, 0x06, + 0x3c, 0x06, 0x39, 0x0c, 0x3e, 0x0c, 0x3c, 0x0c, 0x40, 0x24, 0x86, 0x97, 0xf7, 0x40, 0x0c, 0x86, + 0x5e, 0xf7, 0x3e, 0x60, 0x00, 0x06, 0x3e, 0x06, 0x3e, 0x06, 0x40, 0x06, 0x41, 0x06, 0x40, 0x06, + 0x3c, 0x06, 0x3e, 0x07, 0x81, 0xd9, 0xf1, 0x81, 0x11, 0xf2, 0x82, 0x02, 0x3d, 0x0b, 0x36, 0x0c, + 0x3b, 0x0c, 0x36, 0x06, 0x3a, 0x0c, 0x36, 0x06, 0x38, 0x0c, 0x36, 0x06, 0x36, 0x06, 0x3b, 0x06, + 0x3d, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x36, 0x0c, 0x3b, 0x0c, 0x36, 0x06, 0x3a, 0x0c, 0x36, 0x06, + 0x38, 0x0c, 0x36, 0x06, 0x36, 0x06, 0x3b, 0x06, 0x3d, 0x07, 0x83, 0x81, 0x6a, 0xf2, 0x81, 0x49, + 0xf2, 0x81, 0xc3, 0xf1, 0x81, 0x1a, 0xf3, 0x81, 0x4c, 0xf2, 0x81, 0xba, 0xf2, 0x81, 0x4c, 0xf2, + 0x88, 0x02, 0x81, 0xba, 0xf2, 0x88, 0x00, 0x81, 0xef, 0xf2, 0x81, 0x4c, 0xf2, 0x81, 0xc3, 0xf1, + 0x80, 0x3b, 0xf0, 0x87, 0xff, 0xf6, 0x46, 0x18, 0x00, 0x30, 0x87, 0x33, 0xf7, 0x86, 0x1b, 0xf7, + 0x28, 0x0b, 0x00, 0x01, 0x28, 0x0b, 0x00, 0x01, 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x82, + 0x02, 0x40, 0x0b, 0x39, 0x0c, 0x3e, 0x0c, 0x39, 0x06, 0x3c, 0x0c, 0x39, 0x06, 0x3b, 0x0c, 0x3c, + 0x06, 0x39, 0x06, 0x3c, 0x06, 0x3e, 0x06, 0x40, 0x06, 0x40, 0x06, 0x39, 0x0c, 0x3e, 0x0c, 0x39, + 0x06, 0x3c, 0x0c, 0x39, 0x06, 0x3b, 0x0c, 0x3c, 0x06, 0x39, 0x06, 0x3c, 0x06, 0x3e, 0x07, 0x83, + 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x82, 0x02, 0x40, 0x0b, 0x39, 0x0c, 0x3f, 0x0c, 0x39, + 0x06, 0x3d, 0x0c, 0x3d, 0x06, 0x3b, 0x0c, 0x3d, 0x06, 0x39, 0x06, 0x3d, 0x06, 0x3f, 0x06, 0x40, + 0x06, 0x40, 0x06, 0x39, 0x0c, 0x3f, 0x0c, 0x39, 0x06, 0x3d, 0x0c, 0x3d, 0x06, 0x3b, 0x0c, 0x3d, + 0x06, 0x39, 0x06, 0x3d, 0x06, 0x3f, 0x07, 0x83, 0x89, 0x81, 0x4c, 0xf2, 0x86, 0x45, 0xf7, 0x87, + 0x9f, 0xf7, 0x82, 0x04, 0x46, 0x06, 0x83, 0x46, 0x0c, 0x44, 0x06, 0x46, 0x0a, 0x00, 0x02, 0x44, + 0x06, 0x46, 0x0a, 0x00, 0x02, 0x46, 0x0c, 0x44, 0x0c, 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, + 0x36, 0x23, 0x86, 0x97, 0xf7, 0x36, 0x0c, 0x86, 0x5e, 0xf7, 0x34, 0x18, 0x31, 0x0c, 0x2f, 0x18, + 0x2e, 0x0c, 0x2e, 0x0c, 0x81, 0xef, 0xf3, 0x8e, 0xfd, 0x81, 0xef, 0xf3, 0x8e, 0x03, 0x87, 0x4f, + 0xf7, 0x86, 0x5e, 0xf7, 0x2e, 0x0c, 0x2f, 0x0c, 0x31, 0x0c, 0x31, 0x12, 0x31, 0x12, 0x31, 0x0c, + 0x34, 0x12, 0x31, 0x12, 0x2f, 0x0c, 0x31, 0x18, 0x86, 0x97, 0xf7, 0x31, 0x30, 0x81, 0xef, 0xf3, + 0x8e, 0xfd, 0x81, 0xef, 0xf3, 0x00, 0x01, 0x8e, 0x03, 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, + 0x32, 0x18, 0x81, 0xef, 0xf3, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x32, 0x06, 0x32, 0x06, 0x34, + 0x12, 0x32, 0x12, 0x30, 0x0c, 0x32, 0x18, 0x81, 0xef, 0xf3, 0x8e, 0xfb, 0x81, 0xef, 0xf3, 0x81, + 0x87, 0xf5, 0x86, 0x1b, 0xf7, 0x87, 0x33, 0xf7, 0x8e, 0x05, 0x2b, 0x0c, 0x28, 0x0c, 0x89, 0x87, + 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x34, 0x18, 0x81, 0xef, 0xf3, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, + 0x34, 0x06, 0x34, 0x06, 0x36, 0x12, 0x34, 0x12, 0x32, 0x0c, 0x34, 0x12, 0x36, 0x12, 0x38, 0x0c, + 0x36, 0x12, 0x38, 0x12, 0x39, 0x0c, 0x89, 0x81, 0x1a, 0xf3, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, + 0x82, 0x02, 0x82, 0x02, 0x30, 0x06, 0x2f, 0x0c, 0x2e, 0x06, 0x2d, 0x0c, 0x83, 0x30, 0x0c, 0x32, + 0x0c, 0x83, 0x89, 0x87, 0x4f, 0xf7, 0x86, 0x7c, 0xf7, 0x82, 0x04, 0x30, 0x06, 0x2f, 0x0c, 0x2e, + 0x06, 0x2d, 0x0c, 0x83, 0x30, 0x06, 0x2f, 0x0c, 0x2d, 0x06, 0x35, 0x0c, 0x34, 0x0c, 0x82, 0x02, + 0x35, 0x06, 0x34, 0x0c, 0x33, 0x06, 0x32, 0x0c, 0x83, 0x35, 0x0c, 0x34, 0x0c, 0x82, 0x02, 0x2f, + 0x06, 0x2e, 0x0c, 0x2d, 0x06, 0x2c, 0x0c, 0x83, 0x30, 0x0c, 0x2f, 0x0c, 0x89, 0x87, 0x4f, 0xf7, + 0x86, 0x5e, 0xf7, 0x88, 0x00, 0x00, 0x05, 0x2d, 0x06, 0x2d, 0x0c, 0x2d, 0x0c, 0x2d, 0x0c, 0x2d, + 0x18, 0x2d, 0x1e, 0x2d, 0x06, 0x34, 0x0c, 0x34, 0x0c, 0x34, 0x0c, 0x34, 0x18, 0x81, 0xef, 0xf3, + 0x00, 0x0c, 0x35, 0x18, 0x34, 0x18, 0x35, 0x18, 0x34, 0x0c, 0x32, 0x12, 0x2f, 0x06, 0x2f, 0x0c, + 0x2f, 0x0c, 0x2f, 0x0c, 0x2f, 0x18, 0x81, 0xef, 0xf3, 0x00, 0x0c, 0x00, 0x06, 0x30, 0x06, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x18, 0x30, 0x1e, 0x30, 0x06, 0x37, 0x0c, 0x37, 0x0c, 0x37, + 0x0c, 0x37, 0x18, 0x81, 0xef, 0xf3, 0x00, 0x0c, 0x38, 0x18, 0x37, 0x18, 0x38, 0x18, 0x37, 0x0c, + 0x35, 0x12, 0x32, 0x06, 0x32, 0x0c, 0x32, 0x0c, 0x32, 0x0c, 0x32, 0x18, 0x87, 0x33, 0xf7, 0x86, + 0x1b, 0xf7, 0x00, 0x01, 0x2d, 0x0b, 0x00, 0x01, 0x28, 0x05, 0x00, 0x01, 0x28, 0x06, 0x89, 0x87, + 0x33, 0xf7, 0x86, 0x1b, 0xf7, 0x00, 0x01, 0x2b, 0x0b, 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x89, + 0x87, 0x69, 0xf7, 0x84, 0x1f, 0x85, 0x01, 0x82, 0x04, 0x07, 0x06, 0x8d, 0xfe, 0x83, 0x85, 0x08, + 0x87, 0x4f, 0xf7, 0x86, 0x5e, 0xf7, 0x2d, 0x0b, 0x2d, 0x0c, 0x2d, 0x06, 0x2d, 0x06, 0x2d, 0x0c, + 0x2f, 0x0c, 0x30, 0x0c, 0x32, 0x06, 0x32, 0x06, 0x32, 0x0c, 0x32, 0x0c, 0x32, 0x0c, 0x32, 0x18, + 0x81, 0xef, 0xf3, 0x32, 0x06, 0x34, 0x06, 0x35, 0x06, 0x35, 0x06, 0x34, 0x0c, 0x32, 0x0c, 0x30, + 0x0c, 0x2f, 0x0c, 0x2d, 0x0c, 0x2c, 0x1e, 0x2d, 0x06, 0x2d, 0x0c, 0x2d, 0x0c, 0x2f, 0x0c, 0x2d, + 0x18, 0x81, 0xef, 0xf3, 0x00, 0x0d, 0x89, 0x88, 0x07, 0x87, 0x3e, 0xf7, 0x86, 0x45, 0xf7, 0x45, + 0x04, 0x00, 0x02, 0x45, 0x04, 0x00, 0x02, 0x39, 0x0c, 0x87, 0x1f, 0xf7, 0x86, 0x38, 0xf7, 0x2d, + 0x0b, 0x2d, 0x0c, 0x2e, 0x12, 0x2d, 0x12, 0x2b, 0x0c, 0x2b, 0x06, 0x2d, 0x06, 0x2d, 0x06, 0x2d, + 0x06, 0x2d, 0x0c, 0x2b, 0x0c, 0x2d, 0x06, 0x2b, 0x06, 0x2d, 0x0d, 0x87, 0x3e, 0xf7, 0x86, 0x45, + 0xf7, 0x40, 0x0c, 0x39, 0x0c, 0x89, 0x82, 0x04, 0x81, 0x6e, 0xf5, 0x83, 0x81, 0x60, 0xf5, 0x81, + 0x60, 0xf5, 0x82, 0x06, 0x81, 0x6e, 0xf5, 0x83, 0x81, 0x2f, 0xf5, 0x81, 0x2f, 0xf5, 0x8e, 0xf8, + 0x81, 0x2c, 0xf5, 0x81, 0x2c, 0xf5, 0x8e, 0x01, 0x81, 0x2c, 0xf5, 0x8e, 0x02, 0x81, 0x2c, 0xf5, + 0x8e, 0x05, 0x88, 0xf4, 0x82, 0x04, 0x81, 0x49, 0xf2, 0x83, 0x88, 0x00, 0x81, 0x2f, 0xf5, 0x81, + 0x2f, 0xf5, 0x88, 0xf4, 0x81, 0x49, 0xf2, 0x81, 0x49, 0xf2, 0x88, 0x00, 0x81, 0x2f, 0xf5, 0x88, + 0xf4, 0x81, 0x49, 0xf2, 0x88, 0x00, 0x82, 0x08, 0x81, 0x2f, 0xf5, 0x83, 0x81, 0x2c, 0xf5, 0x88, + 0xf8, 0x81, 0x2c, 0xf5, 0x81, 0x2c, 0xf5, 0x81, 0x2c, 0xf5, 0x88, 0xf4, 0x81, 0x49, 0xf2, 0x88, + 0xff, 0x81, 0xc3, 0xf1, 0x88, 0x00, 0x81, 0x2f, 0xf5, 0x81, 0x24, 0xf5, 0x88, 0xf9, 0x81, 0x2f, + 0xf5, 0x81, 0x24, 0xf5, 0x88, 0xfb, 0x81, 0x2c, 0xf5, 0x81, 0x24, 0xf5, 0x81, 0xc3, 0xf1, 0x88, + 0x00, 0x80, 0x96, 0xf4, 0x88, 0xf4, 0x81, 0x4c, 0xf2, 0x88, 0xff, 0x89, 0x81, 0x2f, 0xf5, 0x87, + 0x8c, 0xf7, 0x86, 0x38, 0xf7, 0x43, 0x0c, 0x45, 0x0c, 0x86, 0x84, 0xf7, 0x8e, 0x0c, 0x2d, 0x0c, + 0x2d, 0x0c, 0x2d, 0x24, 0x2b, 0x06, 0x2b, 0x0c, 0x2d, 0x06, 0x2d, 0x0c, 0x2d, 0x0c, 0x2b, 0x0c, + 0x2d, 0x06, 0x2b, 0x06, 0x2d, 0x0c, 0x8e, 0xf4, 0x86, 0x38, 0xf7, 0x43, 0x0c, 0x45, 0x0c, 0x89, + 0x81, 0x6b, 0xf5, 0x8e, 0x03, 0x81, 0x6b, 0xf5, 0x8e, 0xfd, 0x89, 0x81, 0x6e, 0xf5, 0x81, 0x87, + 0xf5, 0x34, 0x0c, 0x34, 0x0c, 0x35, 0x12, 0x34, 0x12, 0x32, 0x0c, 0x81, 0x87, 0xf5, 0x34, 0x0c, + 0x34, 0x0c, 0x34, 0x18, 0x00, 0x18, 0x89, 0x84, 0x0a, 0x85, 0x01, 0x87, 0x69, 0xf7, 0x86, 0x2b, + 0xf7, 0x07, 0x18, 0x85, 0x08, 0x87, 0x1f, 0xf7, 0x89, 0x81, 0xa0, 0xf6, 0x81, 0xa0, 0xf6, 0x81, + 0x95, 0xf6, 0x81, 0x95, 0xf6, 0x81, 0xb6, 0xf6, 0x88, 0x05, 0x81, 0xb6, 0xf6, 0x88, 0xfb, 0x81, + 0xb6, 0xf6, 0x88, 0x00, 0x81, 0xb6, 0xf6, 0x81, 0xa0, 0xf6, 0x81, 0xa0, 0xf6, 0x81, 0xb3, 0xf6, + 0x88, 0x05, 0x81, 0xb3, 0xf6, 0x88, 0xfd, 0x81, 0x8a, 0xf6, 0x81, 0x8a, 0xf6, 0x88, 0x05, 0x81, + 0xb0, 0xf6, 0x88, 0xfb, 0x81, 0xb0, 0xf6, 0x88, 0x00, 0x82, 0x04, 0x81, 0x55, 0xf6, 0x83, 0x81, + 0xb3, 0xf6, 0x88, 0x05, 0x81, 0xb3, 0xf6, 0x88, 0x00, 0x81, 0x55, 0xf6, 0x81, 0x55, 0xf6, 0x81, + 0xb6, 0xf6, 0x88, 0x05, 0x81, 0xb6, 0xf6, 0x88, 0x00, 0x81, 0x55, 0xf6, 0x82, 0x04, 0x88, 0x00, + 0x81, 0xb3, 0xf6, 0x88, 0x05, 0x81, 0xb3, 0xf6, 0x83, 0x88, 0x00, 0x81, 0xb3, 0xf6, 0x88, 0x05, + 0x81, 0xb3, 0xf6, 0x88, 0x04, 0x81, 0xb3, 0xf6, 0x88, 0x00, 0x81, 0xb3, 0xf6, 0x88, 0xfd, 0x81, + 0xb3, 0xf6, 0x88, 0x04, 0x81, 0xb3, 0xf6, 0x88, 0xfd, 0x81, 0x8a, 0xf6, 0x88, 0x00, 0x81, 0x55, + 0xf6, 0x00, 0x60, 0x81, 0xb6, 0xf6, 0x88, 0x05, 0x81, 0xb6, 0xf6, 0x81, 0x58, 0xf6, 0x88, 0x05, + 0x81, 0xb3, 0xf6, 0x81, 0x58, 0xf6, 0x88, 0xfb, 0x81, 0xb0, 0xf6, 0x81, 0x58, 0xf6, 0x00, 0x60, + 0x88, 0x00, 0x80, 0x99, 0xf5, 0x81, 0x58, 0xf6, 0x88, 0x00, 0x86, 0x1b, 0xf7, 0x82, 0x04, 0x81, + 0x76, 0xf6, 0x83, 0x2b, 0x09, 0x00, 0x03, 0x82, 0x06, 0x81, 0x76, 0xf6, 0x83, 0x2b, 0x09, 0x00, + 0x03, 0x27, 0x09, 0x00, 0x03, 0x89, 0x87, 0x33, 0xf7, 0x85, 0x01, 0x84, 0x1c, 0x07, 0x01, 0x00, + 0x01, 0x84, 0x02, 0x07, 0x01, 0x00, 0x03, 0x85, 0x08, 0x89, 0x81, 0xb3, 0xf6, 0x8e, 0x07, 0x81, + 0xb3, 0xf6, 0x8e, 0xf9, 0x89, 0x81, 0xa0, 0xf6, 0x8e, 0x03, 0x81, 0xa0, 0xf6, 0x8e, 0xfd, 0x89, + 0x81, 0xb3, 0xf6, 0x8e, 0x01, 0x81, 0xb6, 0xf6, 0x8e, 0xfa, 0x81, 0xb6, 0xf6, 0x8e, 0x05, 0x89, + 0x81, 0xb3, 0xf6, 0x81, 0xb6, 0xf6, 0x87, 0xeb, 0xf6, 0x86, 0xf4, 0xf6, 0x15, 0x0c, 0x15, 0x06, + 0x21, 0x06, 0x81, 0xd1, 0xf6, 0x15, 0x0c, 0x15, 0x18, 0x81, 0xd1, 0xf6, 0x1f, 0x06, 0x21, 0x06, + 0x89, 0x87, 0xff, 0xf6, 0x84, 0x1f, 0x85, 0x01, 0x8a, 0x82, 0x06, 0x07, 0x01, 0x8d, 0xf0, 0x07, + 0x01, 0x8d, 0x0e, 0x83, 0x8b, 0x85, 0x08, 0x87, 0xeb, 0xf6, 0x89, 0x0f, 0x01, 0x40, 0x3f, 0x0c, + 0xff, 0x80, 0xeb, 0xf6, 0x00, 0x00, 0x05, 0x05, 0x03, 0xfd, 0xfb, 0xfb, 0x80, 0xf4, 0xf6, 0x0f, + 0x01, 0x40, 0x3b, 0x3f, 0x3e, 0x3b, 0x3d, 0x3c, 0x3b, 0x3b, 0x3a, 0x3b, 0x39, 0x38, 0x3b, 0x37, + 0x36, 0x3b, 0x35, 0x34, 0x3b, 0x33, 0x00, 0xff, 0x80, 0xff, 0xf6, 0x32, 0x80, 0x1b, 0xf7, 0x0f, + 0x01, 0x40, 0x3f, 0x3e, 0x3d, 0x3a, 0x0b, 0x02, 0x80, 0x25, 0xf7, 0x82, 0x0c, 0xf4, 0x07, 0xf9, + 0x80, 0x2b, 0xf7, 0x0f, 0xff, 0x80, 0x33, 0xf7, 0x82, 0x0c, 0xf4, 0x80, 0x38, 0xf7, 0x0f, 0x01, + 0x3f, 0x3a, 0x80, 0x3e, 0xf7, 0x82, 0x05, 0x03, 0xf8, 0x05, 0x07, 0xf4, 0x80, 0x45, 0xf7, 0x00, + 0x01, 0x0f, 0x03, 0x0e, 0x04, 0x0d, 0x04, 0x0c, 0x04, 0x0b, 0xff, 0x80, 0x4f, 0xf7, 0x02, 0x01, + 0xff, 0xfe, 0xfe, 0xff, 0x01, 0x02, 0x80, 0x5e, 0xf7, 0x0f, 0x02, 0x00, 0x04, 0x0e, 0x02, 0x00, + 0x04, 0x0d, 0x03, 0x00, 0x03, 0x0c, 0x03, 0x00, 0x03, 0x80, 0x69, 0xf7, 0x83, 0x05, 0x82, 0x18, + 0xe8, 0x80, 0x7c, 0xf7, 0x82, 0x00, 0x0c, 0xf4, 0x00, 0x80, 0x84, 0xf7, 0x0e, 0x01, 0x3f, 0x3e, + 0x0b, 0x02, 0x0a, 0x02, 0x80, 0x90, 0xf7, 0x02, 0x80, 0x97, 0xf7, 0xfe, 0x80, 0x9b, 0xf7, 0x0e, + 0x01, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x80, 0x9f, 0xf7, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; -TRDOS_DIR_ELEMENT sbootdir = { {'b','o','o','t',' ',' ',' ',' '}, 'B', 0xB4, 0xB4, (sizeof(sbootimage)+255)/256, 0, 0 }; +TRDOS_DIR_ELEMENT sbootdir = { {'b','o','o','t',' ',' ',' ',' '}, 'B', 0xB4, 0xB4, (sizeof(sbootimage)+255)/256, 0, 0 }; long CalcCRC32(long CRC, unsigned char Symbol) { @@ -3002,31 +3002,31 @@ unsigned unpack_lzh(unsigned char *src, unsigned size, unsigned char *buf) } //-------------------------------------------------------------------------- -int x2trd(const char *name, fileTYPE *f) -{ - TDiskImage *img = new TDiskImage; - img->Open(getFullPath(name), true); - - if (!FileOpenEx(f, "vtrd", -1)) - { - delete img; - printf("ERROR: fail to create /vtrd\n"); - return 0; - } - - img->writeTRD(f); - delete(img); - - f->size = FileGetSize(f); - FileSeekLBA(f, 0); - printf("x2trd: vtrd size=%llu.\n", f->size); - - return 1; -} - -int x2trd_ext_supp(const char *name) -{ - const char *ext = ""; - if (strlen(name) > 4) ext = name + strlen(name) - 4; - return (!strcasecmp(ext, ".scl") || !strcasecmp(ext, ".fdi") || !strcasecmp(ext, ".udi")); -} +int x2trd(const char *name, fileTYPE *f) +{ + TDiskImage *img = new TDiskImage; + img->Open(getFullPath(name), true); + + if (!FileOpenEx(f, "vtrd", -1)) + { + delete img; + printf("ERROR: fail to create /vtrd\n"); + return 0; + } + + img->writeTRD(f); + delete(img); + + f->size = FileGetSize(f); + FileSeekLBA(f, 0); + printf("x2trd: vtrd size=%llu.\n", f->size); + + return 1; +} + +int x2trd_ext_supp(const char *name) +{ + const char *ext = ""; + if (strlen(name) > 4) ext = name + strlen(name) - 4; + return (!strcasecmp(ext, ".scl") || !strcasecmp(ext, ".fdi") || !strcasecmp(ext, ".udi")); +} diff --git a/DiskImage.h b/DiskImage.h index b3cd9fd..8feae4a 100644 --- a/DiskImage.h +++ b/DiskImage.h @@ -1,192 +1,192 @@ -#ifndef __DISKIMAGE_H -#define __DISKIMAGE_H - -//----------------------------------------------------------------------------- - -#include "file_io.h" - -enum TDiskImageType { DIT_UNK, DIT_SCL, DIT_FDI, DIT_TD0, DIT_UDI, DIT_HOB, DIT_FDD }; - -struct VGFIND_TRACK -{ - unsigned char *TrackPointer; - unsigned char *ClkPointer; - unsigned int TrackLength; - bool FoundTrack; -}; - - -struct VGFIND_ADM -{ - unsigned char* TrackPointer; - unsigned char* ClkPointer; - unsigned int TrackLength; - - unsigned char *ADMPointer; - unsigned int ADMLength; - - unsigned int MarkedOffsetADM; - unsigned int OffsetADM; - unsigned int OffsetEndADM; - bool FoundADM; - bool CRCOK; -}; - - -struct VGFIND_SECTOR -{ - VGFIND_ADM vgfa; - - unsigned char *SectorPointer; - unsigned int SectorLength; - - unsigned int MarkedOffsetSector; - unsigned int OffsetSector; - unsigned int OffsetEndSector; - bool FoundDATA; - bool CRCOK; - unsigned char DataMarker; -}; - - -class TDiskImage -{ - unsigned int FTrackLength[256][256]; - unsigned char* FTracksPtr[256][256][2]; - - TDiskImageType FType; - - unsigned short MakeVGCRC(unsigned char *data, unsigned long length); -public: - bool Changed; - - bool ReadOnly; - bool DiskPresent; - unsigned char MaxTrack; - unsigned char MaxSide; - - TDiskImage(); - ~TDiskImage(); - - bool FindTrack(unsigned char CYL, unsigned char SIDE, VGFIND_TRACK *vgft); - bool FindADMark(unsigned char CYL, unsigned char SIDE, - unsigned int FromOffset, - VGFIND_ADM *vgfa); - bool FindSector(unsigned char CYL, unsigned char SIDE, - unsigned char SECT, - VGFIND_SECTOR *vgfs, unsigned int FromOffset=0); - void ApplySectorCRC(VGFIND_SECTOR vgfs); - - - void Open(const char *filename, bool ReadOnly); - - void writeTRD(fileTYPE *hfile); - - void readSCL(int hfile, bool readonly); - void readFDI(int hfile, bool readonly); - void readUDI(int hfile, bool readonly); - void readTD0(int hfile, bool readonly); - void readFDD(int hfile, bool readonly); - void readHOB(int hfile); - - void formatTRDOS(unsigned int tracks, unsigned int sides); - - void ShowError(const char *str); -}; - -#pragma pack(1) -struct UDI_HEADER // 16 bytes -{ - unsigned char ID[4]; - unsigned long UnpackedLength; - unsigned char Version; - unsigned char MaxCylinder; - unsigned char MaxSide; - unsigned char _zero; - unsigned long ExtHdrLength; -}; - -struct TD0_MAIN_HEADER // 12 bytes -{ - char ID[2]; // +0: "TD" - 'Normal'; "td" - packed LZH ('New Advanced data compression') - unsigned char __t; // +2: = 0x00 - unsigned char __1; // +3: ??? - unsigned char Ver; // +4: Source version (1.0 -> 10, ..., 2.1 -> 21) - unsigned char __2; // +5: ??? - unsigned char DiskType; // +6: Source disk type - unsigned char Info; // +7: D7-­ «¨ç¨¥ image info - unsigned char DataDOS; // +8: if(=0)'All sectors were copied', else'DOS Allocated sectors were copied' - unsigned char ChkdSides; // +9: if(=1)'One side was checked', else'Both sides were checked' - unsigned short CRC; // +A: CRC 娤¥à  TD0_MAIN_HEADER (ªà®¬¥ ¡ ©â á CRC) -}; - -struct TD0_INFO_DATA // 10 ¡ ©â ¡¥§ áâப¨ ª®¬¥­â à¨ï... -{ - unsigned short CRC; // +0: CRC ¤«ï áâàãªâãàë COMMENT_DATA (¡¥§ ¡ ©â®¢ CRC) - unsigned short strLen; // +2: „«¨­  áâப¨ ª®¬¥­â à¨ï - unsigned char Year; // +4: „ â  á®§¤ ­¨ï - £®¤ (1900 + X) - unsigned char Month; // +5: „ â  á®§¤ ­¨ï - ¬¥áïæ (Ÿ­¢ àì=0, ”¥¢à «ì=1,...) - unsigned char Day; // +6: „ â  á®§¤ ­¨ï - ç¨á«® - unsigned char Hours; // +7: ‚६ï á®§¤ ­¨ï - ç áë - unsigned char Minutes; // +8: ‚६ï á®§¤ ­¨ï - ¬¨­ãâë - unsigned char Seconds; // +9: ‚६ï á®§¤ ­¨ï - ᥪ㭤ë -}; - -struct TD0_TRACK_HEADER // 4 bytes -{ - unsigned char SectorCount; - unsigned char Track; - unsigned char Side; - unsigned char CRCL; -}; - -struct TD0_SECT_HEADER // 8 bytes -{ - unsigned char ADRM[6]; - unsigned short DataLength; -}; - -struct FDD_MAIN_HEADER -{ - char ID[30]; /* ᨣ­ âãà  */ - unsigned char MaxTracks; /* ç¨á«® â४®¢ (樫¨­¤à®¢) */ - unsigned char MaxHeads; /* ç¨á«® £®«®¢®ª (1 ¨«¨ 2) */ - long diskIndex; /* unused */ - long DataOffset[512*2]; /* ᬥ饭¨¥ ¢ ä ©«¥ ª áâàãªâãà ¬ § £®«®¢ª®¢ */ - /* â४®¢ */ -}; - -struct FDD_TRACK_HEADER -{ - unsigned char trkType; /* unused */ - unsigned char SectNum; /* ç¨á«® ᥪâ®à®¢ ­  â४¥ */ - struct - { - /* § £®«®¢®ª ᥪâ®à  */ - unsigned char trk; /* ­®¬¥à â४  */ - unsigned char side; /* ­®¬¥à áâ®à®­ë */ - /* 7 ¡¨â í⮣® ¡ ©â  㪠§ë¢ ¥â ¡¨â a */ - unsigned char sect; /* ­®¬¥à ᥪâ®à  */ - unsigned char size; /* à §¬¥à ᥪâ®à  (ª®¤) */ - long SectPos; /* ᬥ饭¨¥ ¢ ä ©«¥ ª ¤ ­­ë¬ ᥪâ®à  */ - } sect[256]; -}; - - -struct TRDOS_DIR_ELEMENT // 16 bytes -{ - char FileName[8]; - char Type; - unsigned short Start; - unsigned short Length; - unsigned char SecLen; - unsigned char FirstSec; - unsigned char FirstTrk; -}; -#pragma pack() - -int x2trd(const char *name, fileTYPE *f); -int x2trd_ext_supp(const char *name); - -//----------------------------------------------------------------------------- -#endif +#ifndef __DISKIMAGE_H +#define __DISKIMAGE_H + +//----------------------------------------------------------------------------- + +#include "file_io.h" + +enum TDiskImageType { DIT_UNK, DIT_SCL, DIT_FDI, DIT_TD0, DIT_UDI, DIT_HOB, DIT_FDD }; + +struct VGFIND_TRACK +{ + unsigned char *TrackPointer; + unsigned char *ClkPointer; + unsigned int TrackLength; + bool FoundTrack; +}; + + +struct VGFIND_ADM +{ + unsigned char* TrackPointer; + unsigned char* ClkPointer; + unsigned int TrackLength; + + unsigned char *ADMPointer; + unsigned int ADMLength; + + unsigned int MarkedOffsetADM; + unsigned int OffsetADM; + unsigned int OffsetEndADM; + bool FoundADM; + bool CRCOK; +}; + + +struct VGFIND_SECTOR +{ + VGFIND_ADM vgfa; + + unsigned char *SectorPointer; + unsigned int SectorLength; + + unsigned int MarkedOffsetSector; + unsigned int OffsetSector; + unsigned int OffsetEndSector; + bool FoundDATA; + bool CRCOK; + unsigned char DataMarker; +}; + + +class TDiskImage +{ + unsigned int FTrackLength[256][256]; + unsigned char* FTracksPtr[256][256][2]; + + TDiskImageType FType; + + unsigned short MakeVGCRC(unsigned char *data, unsigned long length); +public: + bool Changed; + + bool ReadOnly; + bool DiskPresent; + unsigned char MaxTrack; + unsigned char MaxSide; + + TDiskImage(); + ~TDiskImage(); + + bool FindTrack(unsigned char CYL, unsigned char SIDE, VGFIND_TRACK *vgft); + bool FindADMark(unsigned char CYL, unsigned char SIDE, + unsigned int FromOffset, + VGFIND_ADM *vgfa); + bool FindSector(unsigned char CYL, unsigned char SIDE, + unsigned char SECT, + VGFIND_SECTOR *vgfs, unsigned int FromOffset=0); + void ApplySectorCRC(VGFIND_SECTOR vgfs); + + + void Open(const char *filename, bool ReadOnly); + + void writeTRD(fileTYPE *hfile); + + void readSCL(int hfile, bool readonly); + void readFDI(int hfile, bool readonly); + void readUDI(int hfile, bool readonly); + void readTD0(int hfile, bool readonly); + void readFDD(int hfile, bool readonly); + void readHOB(int hfile); + + void formatTRDOS(unsigned int tracks, unsigned int sides); + + void ShowError(const char *str); +}; + +#pragma pack(1) +struct UDI_HEADER // 16 bytes +{ + unsigned char ID[4]; + unsigned long UnpackedLength; + unsigned char Version; + unsigned char MaxCylinder; + unsigned char MaxSide; + unsigned char _zero; + unsigned long ExtHdrLength; +}; + +struct TD0_MAIN_HEADER // 12 bytes +{ + char ID[2]; // +0: "TD" - 'Normal'; "td" - packed LZH ('New Advanced data compression') + unsigned char __t; // +2: = 0x00 + unsigned char __1; // +3: ??? + unsigned char Ver; // +4: Source version (1.0 -> 10, ..., 2.1 -> 21) + unsigned char __2; // +5: ??? + unsigned char DiskType; // +6: Source disk type + unsigned char Info; // +7: D7-­ «¨ç¨¥ image info + unsigned char DataDOS; // +8: if(=0)'All sectors were copied', else'DOS Allocated sectors were copied' + unsigned char ChkdSides; // +9: if(=1)'One side was checked', else'Both sides were checked' + unsigned short CRC; // +A: CRC 娤¥à  TD0_MAIN_HEADER (ªà®¬¥ ¡ ©â á CRC) +}; + +struct TD0_INFO_DATA // 10 ¡ ©â ¡¥§ áâப¨ ª®¬¥­â à¨ï... +{ + unsigned short CRC; // +0: CRC ¤«ï áâàãªâãàë COMMENT_DATA (¡¥§ ¡ ©â®¢ CRC) + unsigned short strLen; // +2: „«¨­  áâப¨ ª®¬¥­â à¨ï + unsigned char Year; // +4: „ â  á®§¤ ­¨ï - £®¤ (1900 + X) + unsigned char Month; // +5: „ â  á®§¤ ­¨ï - ¬¥áïæ (Ÿ­¢ àì=0, ”¥¢à «ì=1,...) + unsigned char Day; // +6: „ â  á®§¤ ­¨ï - ç¨á«® + unsigned char Hours; // +7: ‚६ï á®§¤ ­¨ï - ç áë + unsigned char Minutes; // +8: ‚६ï á®§¤ ­¨ï - ¬¨­ãâë + unsigned char Seconds; // +9: ‚६ï á®§¤ ­¨ï - ᥪ㭤ë +}; + +struct TD0_TRACK_HEADER // 4 bytes +{ + unsigned char SectorCount; + unsigned char Track; + unsigned char Side; + unsigned char CRCL; +}; + +struct TD0_SECT_HEADER // 8 bytes +{ + unsigned char ADRM[6]; + unsigned short DataLength; +}; + +struct FDD_MAIN_HEADER +{ + char ID[30]; /* ᨣ­ âãà  */ + unsigned char MaxTracks; /* ç¨á«® â४®¢ (樫¨­¤à®¢) */ + unsigned char MaxHeads; /* ç¨á«® £®«®¢®ª (1 ¨«¨ 2) */ + long diskIndex; /* unused */ + long DataOffset[512*2]; /* ᬥ饭¨¥ ¢ ä ©«¥ ª áâàãªâãà ¬ § £®«®¢ª®¢ */ + /* â४®¢ */ +}; + +struct FDD_TRACK_HEADER +{ + unsigned char trkType; /* unused */ + unsigned char SectNum; /* ç¨á«® ᥪâ®à®¢ ­  â४¥ */ + struct + { + /* § £®«®¢®ª ᥪâ®à  */ + unsigned char trk; /* ­®¬¥à â४  */ + unsigned char side; /* ­®¬¥à áâ®à®­ë */ + /* 7 ¡¨â í⮣® ¡ ©â  㪠§ë¢ ¥â ¡¨â a */ + unsigned char sect; /* ­®¬¥à ᥪâ®à  */ + unsigned char size; /* à §¬¥à ᥪâ®à  (ª®¤) */ + long SectPos; /* ᬥ饭¨¥ ¢ ä ©«¥ ª ¤ ­­ë¬ ᥪâ®à  */ + } sect[256]; +}; + + +struct TRDOS_DIR_ELEMENT // 16 bytes +{ + char FileName[8]; + char Type; + unsigned short Start; + unsigned short Length; + unsigned char SecLen; + unsigned char FirstSec; + unsigned char FirstTrk; +}; +#pragma pack() + +int x2trd(const char *name, fileTYPE *f); +int x2trd_ext_supp(const char *name); + +//----------------------------------------------------------------------------- +#endif diff --git a/battery.cpp b/battery.cpp index 43272e0..8041aa8 100644 --- a/battery.cpp +++ b/battery.cpp @@ -95,10 +95,10 @@ static int i2c_smbus_access (int fd, char rw, uint8_t command, int size, union i return ioctl (fd, I2C_SMBUS, &args); } -static int i2c_smbus_write_quick(int fd, uint8_t value) -{ - return i2c_smbus_access(fd, value, 0, I2C_SMBUS_QUICK, NULL); -} +static int i2c_smbus_write_quick(int fd, uint8_t value) +{ + return i2c_smbus_access(fd, value, 0, I2C_SMBUS_QUICK, NULL); +} /////////////////////////////////////////////////////////////////////// @@ -123,12 +123,12 @@ static int smbus_open(int dev_address) return 0; } - if (i2c_smbus_write_quick(fd, I2C_SMBUS_WRITE) < 0) - { + if (i2c_smbus_write_quick(fd, I2C_SMBUS_WRITE) < 0) + { printf("Unable to detect SMBUS device: %s\n", strerror(errno)); close(fd); return 0; - } + } i2c_handle = fd; } diff --git a/bootcore.cpp b/bootcore.cpp index a1a93fc..8cc5d29 100644 --- a/bootcore.cpp +++ b/bootcore.cpp @@ -1,234 +1,234 @@ // bootcore.cpp // 2019, Aitor Gomez Garcia (spark2k06@gmail.com) // Thanks to Sorgelig and BBond007 for their help and advice in the development of this feature. - -#include "file_io.h" -#include "cfg.h" -#include "fpga_io.h" -#include -#include -#include -#include - -int16_t btimeout; -char bootcoretype[64]; - -bool isExactcoreName(char *path) -{ - char *spl = strrchr(path, '.'); - return (spl && !strcmp(spl, ".rbf")); -} - -char *getcoreName(char *path) -{ - char *spl = strrchr(path, '.'); - if (spl && !strcmp(spl, ".rbf")) - { - *spl = '\0'; - } - else - { - return NULL; - } - if ((spl = strrchr(path, '/')) != NULL) - { - path = spl + 1; - } - if ((spl = strrchr(path, '_')) != NULL) - { - *spl = 0; - } - - return path; -} - -char *getcoreExactName(char *path) -{ - char *spl; - if ((spl = strrchr(path, '/')) != NULL) - { - path = spl + 1; - } - - return path; -} - -char *replaceStr(const char *str, const char *oldstr, const char *newstr) -{ - char *result; - int i, cnt = 0; - int newstrlen = strlen(newstr); - int oldstrlen = strlen(oldstr); - - for (i = 0; str[i] != '\0'; i++) - { - if (strstr(&str[i], oldstr) == &str[i]) - { - cnt++; - i += oldstrlen - 1; - } - } - - result = new char[i + cnt * (newstrlen - oldstrlen) + 1]; - - i = 0; - while (*str) - { - if (strstr(str, oldstr) == str) - { - strcpy(&result[i], newstr); - i += newstrlen; - str += oldstrlen; - } - else - result[i++] = *str++; - } - - result[i] = '\0'; - return result; -} - -char* loadLastcore() -{ - char full_path[2100]; - char path[256] = { CONFIG_DIR"/" }; - strcat(path, "lastcore.dat"); - sprintf(full_path, "%s/%s", getRootDir(), path); - FILE *fd = fopen(full_path, "r"); - if (!fd) - { - return NULL; - } - fseek(fd, 0L, SEEK_END); - long size = ftell(fd); - - fseek(fd, 0L, SEEK_SET); - char *lastcore = new char[size + 1]; - - int ret = fread(lastcore, sizeof(char), size, fd); - fclose(fd); - if (ret == size) - { - return lastcore; - } - delete[] lastcore; - return NULL; - -} - -char *findCore(const char *name, char *coreName, int indent) -{ - char *spl; - DIR *dir; - struct dirent *entry; - - if (!(dir = opendir(name))) - { - return NULL; - } - - - char *indir; - char* path = new char[256]; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type == DT_DIR) { - if (entry->d_name[0] != '_') - continue; - snprintf(path, 256, "%s/%s", name, entry->d_name); - indir = findCore(path, coreName, indent + 2); - if (indir != NULL) - { - closedir(dir); - delete[] path; - return indir; - } - } - else { - snprintf(path, 256, "%s/%s", name, entry->d_name); - if (strstr(path, coreName) != NULL) { - spl = strrchr(path, '.'); - if (spl && !strcmp(spl, ".rbf")) - { - closedir(dir); - return path; - } - } - } - } - closedir(dir); - delete[] path; - return NULL; -} - -void bootcore_init(const char *path) -{ - char *auxpointer; - char auxstr[256]; - char bootcore[256]; - bool is_lastcore; - const char *rootdir = getRootDir(); - cfg.bootcore_timeout = cfg.bootcore_timeout * 10; - btimeout = cfg.bootcore_timeout; - strcpy(bootcore, cfg.bootcore); - - is_lastcore = (!strcmp(cfg.bootcore, "lastcore") || !strcmp(cfg.bootcore, "lastexactcore")); - - if (is_lastcore) - { - strcpy(bootcoretype, cfg.bootcore); - auxpointer = loadLastcore(); - if (auxpointer != NULL) - { - strcpy(bootcore, auxpointer); - delete[] auxpointer; - } - } - else - { - strcpy(bootcoretype, isExactcoreName(cfg.bootcore) ? "exactcorename" : "corename"); - } - - auxpointer = findCore(rootdir, bootcore, 0); - if (auxpointer != NULL) - { - strcpy(bootcore, auxpointer); - delete[] auxpointer; - - sprintf(auxstr, "%s/", rootdir); - auxpointer = replaceStr(bootcore, auxstr, ""); - if (auxpointer != NULL) - { - strcpy(bootcore, auxpointer); - delete[] auxpointer; - - if (path[0] == '\0') - { - if (!cfg.bootcore_timeout) - { - fpga_load_rbf(bootcore); - } - - strcpy(cfg.bootcore, strcmp(bootcore, "menu.rbf") ? bootcore : ""); - return; - } - } - } - - if (is_lastcore && path[0] != '\0') - { - - strcpy(auxstr, path); - auxpointer = !strcmp(cfg.bootcore, "lastexactcore") ? getcoreExactName(auxstr) : getcoreName(auxstr); - - if (auxpointer != NULL) - { - if (strcmp(bootcore, auxpointer)) - { - FileSaveConfig("lastcore.dat", (char*)auxpointer, strlen(auxpointer)); - } - } - } - strcpy(cfg.bootcore, ""); - -} - + +#include "file_io.h" +#include "cfg.h" +#include "fpga_io.h" +#include +#include +#include +#include + +int16_t btimeout; +char bootcoretype[64]; + +bool isExactcoreName(char *path) +{ + char *spl = strrchr(path, '.'); + return (spl && !strcmp(spl, ".rbf")); +} + +char *getcoreName(char *path) +{ + char *spl = strrchr(path, '.'); + if (spl && !strcmp(spl, ".rbf")) + { + *spl = '\0'; + } + else + { + return NULL; + } + if ((spl = strrchr(path, '/')) != NULL) + { + path = spl + 1; + } + if ((spl = strrchr(path, '_')) != NULL) + { + *spl = 0; + } + + return path; +} + +char *getcoreExactName(char *path) +{ + char *spl; + if ((spl = strrchr(path, '/')) != NULL) + { + path = spl + 1; + } + + return path; +} + +char *replaceStr(const char *str, const char *oldstr, const char *newstr) +{ + char *result; + int i, cnt = 0; + int newstrlen = strlen(newstr); + int oldstrlen = strlen(oldstr); + + for (i = 0; str[i] != '\0'; i++) + { + if (strstr(&str[i], oldstr) == &str[i]) + { + cnt++; + i += oldstrlen - 1; + } + } + + result = new char[i + cnt * (newstrlen - oldstrlen) + 1]; + + i = 0; + while (*str) + { + if (strstr(str, oldstr) == str) + { + strcpy(&result[i], newstr); + i += newstrlen; + str += oldstrlen; + } + else + result[i++] = *str++; + } + + result[i] = '\0'; + return result; +} + +char* loadLastcore() +{ + char full_path[2100]; + char path[256] = { CONFIG_DIR"/" }; + strcat(path, "lastcore.dat"); + sprintf(full_path, "%s/%s", getRootDir(), path); + FILE *fd = fopen(full_path, "r"); + if (!fd) + { + return NULL; + } + fseek(fd, 0L, SEEK_END); + long size = ftell(fd); + + fseek(fd, 0L, SEEK_SET); + char *lastcore = new char[size + 1]; + + int ret = fread(lastcore, sizeof(char), size, fd); + fclose(fd); + if (ret == size) + { + return lastcore; + } + delete[] lastcore; + return NULL; + +} + +char *findCore(const char *name, char *coreName, int indent) +{ + char *spl; + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(name))) + { + return NULL; + } + + + char *indir; + char* path = new char[256]; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_DIR) { + if (entry->d_name[0] != '_') + continue; + snprintf(path, 256, "%s/%s", name, entry->d_name); + indir = findCore(path, coreName, indent + 2); + if (indir != NULL) + { + closedir(dir); + delete[] path; + return indir; + } + } + else { + snprintf(path, 256, "%s/%s", name, entry->d_name); + if (strstr(path, coreName) != NULL) { + spl = strrchr(path, '.'); + if (spl && !strcmp(spl, ".rbf")) + { + closedir(dir); + return path; + } + } + } + } + closedir(dir); + delete[] path; + return NULL; +} + +void bootcore_init(const char *path) +{ + char *auxpointer; + char auxstr[256]; + char bootcore[256]; + bool is_lastcore; + const char *rootdir = getRootDir(); + cfg.bootcore_timeout = cfg.bootcore_timeout * 10; + btimeout = cfg.bootcore_timeout; + strcpy(bootcore, cfg.bootcore); + + is_lastcore = (!strcmp(cfg.bootcore, "lastcore") || !strcmp(cfg.bootcore, "lastexactcore")); + + if (is_lastcore) + { + strcpy(bootcoretype, cfg.bootcore); + auxpointer = loadLastcore(); + if (auxpointer != NULL) + { + strcpy(bootcore, auxpointer); + delete[] auxpointer; + } + } + else + { + strcpy(bootcoretype, isExactcoreName(cfg.bootcore) ? "exactcorename" : "corename"); + } + + auxpointer = findCore(rootdir, bootcore, 0); + if (auxpointer != NULL) + { + strcpy(bootcore, auxpointer); + delete[] auxpointer; + + sprintf(auxstr, "%s/", rootdir); + auxpointer = replaceStr(bootcore, auxstr, ""); + if (auxpointer != NULL) + { + strcpy(bootcore, auxpointer); + delete[] auxpointer; + + if (path[0] == '\0') + { + if (!cfg.bootcore_timeout) + { + fpga_load_rbf(bootcore); + } + + strcpy(cfg.bootcore, strcmp(bootcore, "menu.rbf") ? bootcore : ""); + return; + } + } + } + + if (is_lastcore && path[0] != '\0') + { + + strcpy(auxstr, path); + auxpointer = !strcmp(cfg.bootcore, "lastexactcore") ? getcoreExactName(auxstr) : getcoreName(auxstr); + + if (auxpointer != NULL) + { + if (strcmp(bootcore, auxpointer)) + { + FileSaveConfig("lastcore.dat", (char*)auxpointer, strlen(auxpointer)); + } + } + } + strcpy(cfg.bootcore, ""); + +} + diff --git a/file_io.cpp b/file_io.cpp index 3a8ec2b..5432a1e 100644 --- a/file_io.cpp +++ b/file_io.cpp @@ -1,1253 +1,1253 @@ -#include "file_io.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "osd.h" -#include "fpga_io.h" -#include "menu.h" -#include "errno.h" -#include "DiskImage.h" -#include "user_io.h" -#include "cfg.h" -#include "input.h" -#include "miniz_zip.h" -#include "scheduler.h" - -#define MIN(a,b) (((a)<(b)) ? (a) : (b)) - -typedef std::vector DirentVector; - -static const size_t YieldIterations = 128; - -DirentVector DirItem; -int iSelectedEntry = 0; // selected entry index -int iFirstEntry = 0; - -static char full_path[2100]; - -struct fileZipArchive -{ - mz_zip_archive archive; - int index; - mz_zip_reader_extract_iter_state* iter; - __off64_t offset; -}; - -static bool FileIsZipped(char* path, char** zip_path, char** file_path) -{ - char* z = strcasestr(path, ".zip"); - if (z) - { - z += 4; - if (!z[0]) z[1] = 0; - *z++ = 0; - - if (zip_path) *zip_path = path; - if (file_path) *file_path = z; - return true; - } - return false; -} - -static char* make_fullpath(const char *path, int mode = 0) -{ - const char *root = getRootDir(); - if (strncasecmp(getRootDir(), path, strlen(root))) - { - sprintf(full_path, "%s/%s", (mode == -1) ? "" : root, path); - } - else - { - sprintf(full_path, path); - } - - return full_path; -} - -static int get_stmode(const char *path) -{ - struct stat64 st; - return (stat64(path, &st) < 0) ? 0 : st.st_mode; -} - -static bool isPathDirectory(char *path) -{ - make_fullpath(path); - - char *zip_path, *file_path; - if (FileIsZipped(full_path, &zip_path, &file_path)) - { - mz_zip_archive z{}; - if (!mz_zip_reader_init_file(&z, zip_path, 0)) - { - printf("isPathDirectory(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path, - mz_zip_get_error_string(mz_zip_get_last_error(&z))); - return false; - } - - if (!*file_path) - { - mz_zip_reader_end(&z); - return true; - } - - // Folder names always end with a slash in the zip - // file central directory. - strcat(file_path, "/"); - const int file_index = mz_zip_reader_locate_file(&z, file_path, NULL, 0); - if (file_index < 0) - { - printf("isPathDirectory(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n", - zip_path, file_path, - mz_zip_get_error_string(mz_zip_get_last_error(&z))); - mz_zip_reader_end(&z); - return false; - } - - if (mz_zip_reader_is_file_a_directory(&z, file_index)) - { - mz_zip_reader_end(&z); - return true; - } - mz_zip_reader_end(&z); - } - else - { - int stmode = get_stmode(full_path); - if (!stmode) - { - printf("isPathDirectory(stat) path:%s, error:%s.\n", full_path, strerror(errno)); - return false; - } - - if (stmode & S_IFDIR) return true; - } - - return false; -} - -static bool isPathRegularFile(char *path) -{ - make_fullpath(path); - - char *zip_path, *file_path; - if (FileIsZipped(full_path, &zip_path, &file_path)) - { - mz_zip_archive z{}; - if (!mz_zip_reader_init_file(&z, zip_path, 0)) - { - printf("isPathRegularFile(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path, - mz_zip_get_error_string(mz_zip_get_last_error(&z))); - return false; - } - - if (!*file_path) - { - mz_zip_reader_end(&z); - return false; - } - - const int file_index = mz_zip_reader_locate_file(&z, file_path, NULL, 0); - if (file_index < 0) - { - printf("isPathRegularFile(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n", - zip_path, file_path, - mz_zip_get_error_string(mz_zip_get_last_error(&z))); - mz_zip_reader_end(&z); - return false; - } - - if (!mz_zip_reader_is_file_a_directory(&z, file_index) && mz_zip_reader_is_file_supported(&z, file_index)) - { - mz_zip_reader_end(&z); - return true; - } - mz_zip_reader_end(&z); - } - else - { - int stmode = get_stmode(full_path); - if (!stmode) - { - printf("isPathRegularFile(stat) path:%s, error:%s.\n", full_path, strerror(errno)); - return false; - } - - if (stmode & S_IFREG) return true; - } - - return false; -} - -void FileClose(fileTYPE *file) -{ - if (file->zip) - { - if (file->zip->iter) - { - mz_zip_reader_extract_iter_free(file->zip->iter); - } - mz_zip_reader_end(&file->zip->archive); - - delete file->zip; - file->zip = nullptr; - } - - if (file->filp) - { - //printf("closing %p\n", file->filp); - fclose(file->filp); - if (file->type == 1) - { - if (file->name[0] == '/') - { - shm_unlink(file->name); - } - file->type = 0; - } - } - file->filp = nullptr; -} - -int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute) -{ - make_fullpath((char*)name, mode); - - FileClose(file); - file->mode = 0; - file->type = 0; - - char *p = strrchr(full_path, '/'); - strcpy(file->name, (mode == -1) ? full_path : p + 1); - - char *zip_path, *file_path; - if ((mode != -1) && FileIsZipped(full_path, &zip_path, &file_path)) - { - if (mode & O_RDWR || mode & O_WRONLY) - { - if(!mute) printf("FileOpenEx(mode) Zip:%s, writing to zipped files is not supported.\n", - full_path); - return 0; - } - - file->zip = new fileZipArchive{}; - if (!mz_zip_reader_init_file(&file->zip->archive, zip_path, 0)) - { - if(!mute) printf("FileOpenEx(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path, - mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); - return 0; - } - - file->zip->index = mz_zip_reader_locate_file(&file->zip->archive, file_path, NULL, 0); - if (file->zip->index < 0) - { - if(!mute) printf("FileOpenEx(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n", - zip_path, file_path, - mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); - FileClose(file); - return 0; - } - - mz_zip_archive_file_stat s; - if (!mz_zip_reader_file_stat(&file->zip->archive, file->zip->index, &s)) - { - if(!mute) printf("FileOpenEx(mz_zip_reader_file_stat) Zip:%s, file:%s, error:%s\n", - zip_path, file_path, - mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); - FileClose(file); - return 0; - } - file->size = s.m_uncomp_size; - - file->zip->iter = mz_zip_reader_extract_iter_new(&file->zip->archive, file->zip->index, 0); - if (!file->zip->iter) - { - if(!mute) printf("FileOpenEx(mz_zip_reader_extract_iter_new) Zip:%s, file:%s, error:%s\n", - zip_path, file_path, - mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); - FileClose(file); - return 0; - } - file->zip->offset = 0; - file->offset = 0; - file->mode = mode; - } - else - { - int fd = (mode == -1) ? shm_open("/vtrd", O_CREAT | O_RDWR | O_TRUNC, 0777) : open(full_path, mode, 0777); - if (fd <= 0) - { - if(!mute) printf("FileOpenEx(open) File:%s, error: %s.\n", full_path, strerror(errno)); - return 0; - } - const char *fmode = mode & O_RDWR ? "w+" : "r"; - file->filp = fdopen(fd, fmode); - if (!file->filp) - { - if(!mute) printf("FileOpenEx(fdopen) File:%s, error: %s.\n", full_path, strerror(errno)); - close(fd); - return 0; - } - - if (mode == -1) - { - file->type = 1; - file->size = 0; - file->offset = 0; - file->mode = O_CREAT | O_RDWR | O_TRUNC; - } - else - { - struct stat64 st; - int ret = fstat64(fileno(file->filp), &st); - if (ret < 0) - { - if (!mute) printf("FileOpenEx(fstat) File:%s, error: %d.\n", full_path, ret); - FileClose(file); - return 0; - } - - file->size = st.st_size; - file->offset = 0; - file->mode = mode; - } - } - - //printf("opened %s, size %lu\n", full_path, file->size); - return 1; -} - -__off64_t FileGetSize(fileTYPE *file) -{ - if (file->filp) - { - struct stat64 st; - int ret = fstat64(fileno(file->filp), &st); - return (ret < 0) ? 0 : st.st_size; - } - else if (file->zip) - { - return file->size; - } - return 0; -} - -int FileOpen(fileTYPE *file, const char *name, char mute) -{ - return FileOpenEx(file, name, O_RDONLY, mute); -} - -int FileSeek(fileTYPE *file, __off64_t offset, int origin) -{ - if (file->filp) - { - offset = fseeko64(file->filp, offset, origin); - if(offset<0) - { - printf("Fail to seek the file.\n"); - return 0; - } - } - else if (file->zip) - { - if (origin == SEEK_CUR) - { - offset = file->zip->offset + offset; - } - else if (origin == SEEK_END) - { - offset = file->size - offset; - } - - if (offset < file->zip->offset) - { - mz_zip_reader_extract_iter_state *iter = mz_zip_reader_extract_iter_new(&file->zip->archive, file->zip->index, 0); - if (!iter) - { - printf("FileSeek(mz_zip_reader_extract_iter_new) Failed to rewind iterator, error:%s\n", - mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); - return 0; - } - - mz_zip_reader_extract_iter_free(file->zip->iter); - file->zip->iter = iter; - file->zip->offset = 0; - } - - char buf[512]; - while (file->zip->offset < offset) - { - const size_t want_len = MIN((__off64_t)sizeof(buf), offset - file->zip->offset); - const size_t read_len = mz_zip_reader_extract_iter_read(file->zip->iter, buf, want_len); - file->zip->offset += read_len; - if (read_len < want_len) - { - printf("FileSeek(mz_zip_reader_extract_iter_read) Failed to advance iterator, error:%s\n", - mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); - return 0; - } - } - } - else - { - return 0; - } - - file->offset = offset; - return 1; -} - -int FileSeekLBA(fileTYPE *file, uint32_t offset) -{ - __off64_t off64 = offset; - off64 <<= 9; - return FileSeek(file, off64, SEEK_SET); -} - -// Read with offset advancing -int FileReadAdv(fileTYPE *file, void *pBuffer, int length) -{ - ssize_t ret = 0; - - if (file->filp) - { - ret = fread(pBuffer, 1, length, file->filp); - if (ret < 0) - { - printf("FileReadAdv error(%d).\n", ret); - return 0; - } - } - else if (file->zip) - { - ret = mz_zip_reader_extract_iter_read(file->zip->iter, pBuffer, length); - if (!ret) - { - printf("FileReadEx(mz_zip_reader_extract_iter_read) Failed to read, error:%s\n", - mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); - return 0; - } - file->zip->offset += ret; - } - else - { - printf("FileReadAdv error(unknown file type).\n"); - return -1; - } - - file->offset += ret; - return ret; -} - -int FileReadSec(fileTYPE *file, void *pBuffer) -{ - return FileReadAdv(file, pBuffer, 512); -} - -// Write with offset advancing -int FileWriteAdv(fileTYPE *file, void *pBuffer, int length) -{ - int ret; - - if (file->filp) - { - ret = fwrite(pBuffer, 1, length, file->filp); - fflush(file->filp); - - if (ret < 0) - { - printf("FileWriteAdv error(%d).\n", ret); - return 0; - } - } - else if (file->zip) - { - printf("FileWriteAdv error(not supported for zip).\n"); - return 0; - } - else - { - printf("FileWriteAdv error(unknown file type).\n"); - return 0; - } - - file->offset += ret; - return ret; -} - -int FileWriteSec(fileTYPE *file, void *pBuffer) -{ - return FileWriteAdv(file, pBuffer, 512); -} - -int FileSave(const char *name, void *pBuffer, int size) -{ - sprintf(full_path, "%s/%s", getRootDir(), name); - int fd = open(full_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRWXU | S_IRWXG | S_IRWXO); - if (fd < 0) - { - printf("FileSave(open) File:%s, error: %d.\n", full_path, fd); - return 0; - } - - int ret = write(fd, pBuffer, size); - close(fd); - - if (ret < 0) - { - printf("FileSave(write) File:%s, error: %d.\n", full_path, ret); - return 0; - } - - return ret; -} - -int FileSaveConfig(const char *name, void *pBuffer, int size) -{ - char path[256] = { CONFIG_DIR"/" }; - strcat(path, name); - return FileSave(path, pBuffer, size); -} - -int FileLoad(const char *name, void *pBuffer, int size) -{ - sprintf(full_path, "%s/%s", getRootDir(), name); - int fd = open(full_path, O_RDONLY); - if (fd < 0) - { - printf("FileLoad(open) File:%s, error: %d.\n", full_path, fd); - return 0; - } - - struct stat64 st; - int ret = fstat64(fd, &st); - if (ret < 0) - { - printf("FileLoad(fstat) File:%s, error: %d.\n", full_path, ret); - close(fd); - return 0; - } - - if (!pBuffer) - { - close(fd); - return (int)st.st_size; - } - - ret = read(fd, pBuffer, size ? size : st.st_size); - close(fd); - - if (ret < 0) - { - printf("FileLoad(read) File:%s, error: %d.\n", full_path, ret); - return 0; - } - - return ret; -} - -int FileLoadConfig(const char *name, void *pBuffer, int size) -{ - char path[256] = { CONFIG_DIR"/" }; - strcat(path, name); - return FileLoad(path, pBuffer, size); -} - -int FileCanWrite(const char *name) -{ - sprintf(full_path, "%s/%s", getRootDir(), name); - - if (FileIsZipped(full_path, nullptr, nullptr)) - { - return 0; - } - - struct stat64 st; - int ret = stat64(full_path, &st); - if (ret < 0) - { - printf("FileCanWrite(stat) File:%s, error: %d.\n", full_path, ret); - return 0; - } - - //printf("FileCanWrite: mode=%04o.\n", st.st_mode); - return ((st.st_mode & S_IWUSR) != 0); -} - -void FileGenerateSavePath(const char *name, char* out_name) -{ - make_fullpath(SAVE_DIR); - mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO); - strcat(full_path, "/"); - strcat(full_path, HomeDir); - mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO); - - sprintf(out_name, "%s/%s/", SAVE_DIR, HomeDir); - char *fname = out_name + strlen(out_name); - - const char *p = strrchr(name, '/'); - if (p) - { - strcat(fname, p+1); - } - else - { - strcat(fname, name); - } - - char *e = strrchr(fname, '.'); - if (e) - { - strcpy(e,".sav"); - } - else - { - strcat(fname, ".sav"); - } - - printf("SavePath=%s\n", out_name); -} - -uint32_t getFileType(const char *name) -{ - sprintf(full_path, "%s/%s", getRootDir(), name); - - struct stat64 st; - if (stat64(full_path, &st)) return 0; - - return st.st_mode; -} - -static int device = 0; -static int usbnum = 0; -const char *getStorageDir(int dev) -{ - static char path[32]; - if (!dev) return "/media/fat"; - sprintf(path, "/media/usb%d", usbnum); - return path; -} - -const char *getRootDir() -{ - return getStorageDir(device); -} - -const char *getFullPath(const char *name) -{ - sprintf(full_path, "%s/%s", getRootDir(), name); - return full_path; -} - -void setStorage(int dev) -{ - device = 0; - FileSave(CONFIG_DIR"/device.bin", &dev, sizeof(int)); - fpga_load_rbf("menu.rbf"); -} - -static int orig_device = 0; -int getStorage(int from_setting) -{ - return from_setting ? orig_device : device; -} - -int isPathMounted(int n) -{ - char path[32]; - sprintf(path, "/media/usb%d", n); - - struct stat file_stat; - struct stat parent_stat; - - if (-1 == stat(path, &file_stat)) - { - printf("failed to stat %s\n", path); - return 0; - } - - if (!(file_stat.st_mode & S_IFDIR)) - { - printf("%s is not a directory.\n", path); - return 0; - } - - if (-1 == stat("/media", &parent_stat)) - { - printf("failed to stat /media\n"); - return 0; - } - - if (file_stat.st_dev != parent_stat.st_dev || - (file_stat.st_dev == parent_stat.st_dev && - file_stat.st_ino == parent_stat.st_ino)) - { - printf("%s IS a mountpoint.\n", path); - struct statfs fs_stat; - if (!statfs(path, &fs_stat)) - { - printf("%s is FS: 0x%08X\n", path, fs_stat.f_type); - if (fs_stat.f_type != EXT4_SUPER_MAGIC) - { - printf("%s is not EXT2/3/4.\n", path); - return 1; - } - } - } - - printf("%s is NOT a VFAT mountpoint.\n", path); - return 0; -} - -int isUSBMounted() -{ - for (int i = 0; i < 4; i++) - { - if (isPathMounted(i)) - { - usbnum = i; - return 1; - } - } - return 0; -} - -void FindStorage(void) -{ - char str[128]; - printf("Looking for root device...\n"); - device = 0; - FileLoad(CONFIG_DIR"/device.bin", &device, sizeof(int)); - orig_device = device; - - if(device && !isUSBMounted()) - { - int saveddev = device; - device = 0; - MiSTer_ini_parse(); - device = saveddev; - parse_video_mode(); - user_io_send_buttons(1); - - printf("Waiting for USB...\n"); - int btn = 0; - int done = 0; - for (int i = 30; i >= 0; i--) - { - sprintf(str, "\n Waiting for USB...\n\n %d \n\n\n OSD/USER or ESC to cancel", i); - InfoMessage(str); - if (isUSBMounted()) - { - done = 1; - break; - } - - for (int i = 0; i < 10; i++) - { - btn = fpga_get_buttons(); - if (!btn) btn = input_poll(1); - if (btn) - { - printf("Button has been pressed %d\n", btn); - InfoMessage("\n\n Canceled!\n"); - usleep(500000); - setStorage(0); - break; - } - usleep(100000); - } - if (done) break; - } - - if (!done) - { - InfoMessage("\n\n No USB storage found\n Falling back to SD card\n"); - usleep(2000000); - setStorage(0); - } - } - - if (device) - { - printf("Using USB as a root device\n"); - } - else - { - printf("Using SD card as a root device\n"); - } - - sprintf(full_path, "%s/" CONFIG_DIR, getRootDir()); - DIR* dir = opendir(full_path); - if (dir) closedir(dir); - else if (ENOENT == errno) mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO); -} - -struct DirentComp -{ - bool operator()(const dirent& de1, const dirent& de2) - { - if (++iterations % YieldIterations == 0) - { - scheduler_yield(); - } - - if ((de1.d_type == DT_DIR) && !strcmp(de1.d_name, "..")) return true; - if ((de2.d_type == DT_DIR) && !strcmp(de2.d_name, "..")) return false; - - if ((de1.d_type == DT_DIR) && (de2.d_type == DT_REG)) return true; - if ((de1.d_type == DT_REG) && (de2.d_type == DT_DIR)) return false; - - if ((de1.d_type == DT_REG) && (de2.d_type == DT_REG)) - { - int len1 = strlen(de1.d_name); - int len2 = strlen(de2.d_name); - if ((len1 > 4) && (de1.d_name[len1 - 4] == '.')) len1 -= 4; - if ((len2 > 4) && (de2.d_name[len2 - 4] == '.')) len2 -= 4; - - int len = (len1 < len2) ? len1 : len2; - int ret = strncasecmp(de1.d_name, de2.d_name, len); - if (!ret) - { - return len1 < len2; - } - - return ret < 0; - } - - return strcasecmp(de1.d_name, de2.d_name) < 0; - } - - size_t iterations = 0; -}; - -void AdjustDirectory(char *path) -{ - if (isPathDirectory(path)) return; - - char *p = strrchr(path, '/'); - if (p) - { - *p = 0; - } - else - { - path[0] = 0; - } -} - -static bool IsInSameFolder(const char *folder, const char *path) -{ - if (strcasestr(path, folder) == path) - { - const char *subpath = path + strlen(folder) + 1; - if (*subpath != '\0') - { - const char *slash = strchr(subpath, '/'); - return !slash || *(slash + 1) == '\0'; - } - } - return false; -} - -int ScanDirectory(char* path, int mode, const char *extension, int options, const char *prefix) -{ - static char file_name[1024]; - - int has_trd = 0; - const char *ext = extension; - while (*ext) - { - if (!strncasecmp(ext, "TRD", 3)) has_trd = 1; - ext += 3; - } - - const char* is_zipped = strcasestr(path, ".zip"); - if (is_zipped && strcasestr(is_zipped + 4, ".zip")) - { - printf("Nested zip-files are not supported: %s\n", path); - return 0; - } - int extlen = strlen(extension); - - //printf("scan dir\n"); - - if (mode == SCANF_INIT) - { - file_name[0] = 0; - if (!isPathDirectory(path)) - { - bool isfile = isPathRegularFile(path); - char *p = strrchr(path, '/'); - if (p) - { - if (isfile) strcpy(file_name, p + 1); - *p = 0; - } - else - { - if (isfile) strcpy(file_name, path); - path[0] = 0; - } - } - - if (!isPathDirectory(path)) - { - path[0] = 0; - file_name[0] = 0; - } - - sprintf(full_path, "%s/%s", getRootDir(), path); - int path_len = strlen(full_path); - - printf("Start to scan %sdir: %s\n", is_zipped ? "zipped " : "", full_path); - - char *zip_path, *file_path_in_zip = (char*)""; - FileIsZipped(full_path, &zip_path, &file_path_in_zip); - - iFirstEntry = 0; - iSelectedEntry = 0; - DirItem.clear(); - - DIR *d = nullptr; - mz_zip_archive *z = nullptr; - if (is_zipped) - { - mz_zip_archive _z = {}; - if (!mz_zip_reader_init_file(&_z, zip_path, 0)) - { - printf("Couldn't open zip file %s: %s\n", full_path, mz_zip_get_error_string(mz_zip_get_last_error(z))); - return 0; - } - z = new mz_zip_archive(_z); - } - else - { - d = opendir(full_path); - if (!d) - { - printf("Couldn't open dir: %s\n", full_path); - return 0; - } - } - - struct dirent *de = nullptr; - for (size_t i = 0; (d && (de = readdir(d))) - || (z && i < mz_zip_reader_get_num_files(z)); i++) - { - if (0 < i && i % YieldIterations == 0) - { - scheduler_yield(); - } - - struct dirent _de = {}; - if (z) { - mz_zip_reader_get_filename(z, i, &_de.d_name[0], sizeof(_de.d_name)); - if (!IsInSameFolder(file_path_in_zip, _de.d_name)) - { - continue; - } - // Remove leading folders. - const char* subpath = _de.d_name + strlen(file_path_in_zip); - if (*subpath == '/') - { - subpath++; - } - strcpy(_de.d_name, subpath); - - _de.d_type = mz_zip_reader_is_file_a_directory(z, i) ? DT_DIR : DT_REG; - if (_de.d_type == DT_DIR) - { - // Remove trailing slash. - _de.d_name[strlen(_de.d_name) - 1] = '\0'; - } - de = &_de; - } - else - // Handle (possible) symbolic link type in the directory entry - if (de->d_type == DT_LNK || de->d_type == DT_REG) - { - sprintf(full_path+path_len, "/%s", de->d_name); - - struct stat entrystat; - - if (!stat(full_path, &entrystat)) - { - if (S_ISREG(entrystat.st_mode)) - { - de->d_type = DT_REG; - } - else if (S_ISDIR(entrystat.st_mode)) - { - de->d_type = DT_DIR; - } - } - } - - if (de->d_type == DT_DIR) - { - if (!strcmp(de->d_name, ".")) continue; - if (!strcmp(de->d_name, "..")) - { - if(!strlen(path)) continue; - } - - if (!(options & SCANO_DIR)) - { - if (de->d_name[0] != '_' && strcmp(de->d_name, "..")) continue; - if (!(options & SCANO_CORES)) continue; - } - } - else if (de->d_type == DT_REG) - { - //skip non-selectable files - if (!strcasecmp(de->d_name, "menu.rbf")) continue; - if (!strncasecmp(de->d_name, "menu_20",7)) continue; - if (!strcasecmp(de->d_name, "boot.rom")) continue; - - //check the prefix if given - if (prefix && strncasecmp(prefix, de->d_name, strlen(prefix))) continue; - - if (extlen > 0) - { - const char *ext = extension; - int found = (has_trd && x2trd_ext_supp(de->d_name)); - if (!found && !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".zip")) - { - // Fake that zip-file is a directory. - de->d_type = DT_DIR; - found = 1; - } - if (!found && is_minimig() && !memcmp(extension, "HDF", 3)) - { - found = !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".iso"); - } - - char *fext = strrchr(de->d_name, '.'); - while(!found && *ext && fext) - { - char e[4]; - memcpy(e, ext, 3); - if (e[2] == ' ') - { - e[2] = 0; - if (e[1] == ' ') e[1] = 0; - } - e[3] = 0; - found = 1; - for (int i = 0; i < 3; i++) - { - if (e[i] == '*') break; - if (e[i] == '?' && fext[i+1]) continue; - if (tolower(e[i]) != tolower(fext[i + 1])) found = 0; - } - if (found) break; - - if (strlen(ext) < 3) break; - ext += 3; - } - if (!found) continue; - } - } - else - { - continue; - } - DirItem.push_back(*de); - } - if (z) - { - // Since zip files aren't actually folders the entry to - // exit the zip file must be added manually. - dirent up; - up.d_type = DT_DIR; - strcpy(up.d_name, ".."); - DirItem.push_back(up); - - mz_zip_reader_end(z); - delete z; - } - if (d) - { - closedir(d); - } - - printf("Got %d dir entries\n", flist_nDirEntries()); - if (!flist_nDirEntries()) return 0; - - std::sort(DirItem.begin(), DirItem.end(), DirentComp()); - if (file_name[0]) - { - for (int i = 0; i < flist_nDirEntries(); i++) - { - if (!strcmp(file_name, DirItem[i].d_name)) - { - iSelectedEntry = i; - if (iSelectedEntry + (OsdGetSize() / 2) - 1 >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize(); - else iFirstEntry = iSelectedEntry - (OsdGetSize() / 2) + 1; - if (iFirstEntry < 0) iFirstEntry = 0; - break; - } - } - } - return flist_nDirEntries(); - } - else - { - if (flist_nDirEntries() == 0) // directory is empty so there is no point in searching for any entry - return 0; - - if (mode == SCANF_END) - { - iSelectedEntry = flist_nDirEntries() - 1; - iFirstEntry = iSelectedEntry - OsdGetSize() + 1; - if (iFirstEntry < 0) iFirstEntry = 0; - return 0; - } - else if (mode == SCANF_NEXT) - { - if(iSelectedEntry + 1 < flist_nDirEntries()) // scroll within visible items - { - iSelectedEntry++; - if (iSelectedEntry > iFirstEntry + OsdGetSize() - 1) iFirstEntry = iSelectedEntry - OsdGetSize() + 1; - } - return 0; - } - else if (mode == SCANF_PREV) - { - if (iSelectedEntry > 0) // scroll within visible items - { - iSelectedEntry--; - if (iSelectedEntry < iFirstEntry) iFirstEntry = iSelectedEntry; - } - return 0; - } - else if (mode == SCANF_NEXT_PAGE) - { - if (iSelectedEntry < iFirstEntry + OsdGetSize() - 1) - { - iSelectedEntry = iFirstEntry + OsdGetSize() - 1; - if (iSelectedEntry >= flist_nDirEntries()) iSelectedEntry = flist_nDirEntries() - 1; - } - else - { - iSelectedEntry += OsdGetSize(); - iFirstEntry += OsdGetSize(); - if (iSelectedEntry >= flist_nDirEntries()) - { - iSelectedEntry = flist_nDirEntries() - 1; - iFirstEntry = iSelectedEntry - OsdGetSize() + 1; - if (iFirstEntry < 0) iFirstEntry = 0; - } - else if (iFirstEntry + OsdGetSize() > flist_nDirEntries()) - { - iFirstEntry = flist_nDirEntries() - OsdGetSize(); - } - } - return 0; - } - else if (mode == SCANF_PREV_PAGE) - { - if(iSelectedEntry != iFirstEntry) - { - iSelectedEntry = iFirstEntry; - } - else - { - iFirstEntry -= OsdGetSize(); - if (iFirstEntry < 0) iFirstEntry = 0; - iSelectedEntry = iFirstEntry; - } - } - else if (mode == SCANF_SET_ITEM) - { - for (int i = 0; i < flist_nDirEntries(); i++) - { - if((DirItem[i].d_type == DT_DIR) && !strcmp(DirItem[i].d_name, extension)) - { - iSelectedEntry = i; - if (iSelectedEntry + (OsdGetSize() / 2) - 1 >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize(); - else iFirstEntry = iSelectedEntry - (OsdGetSize() / 2) + 1; - if (iFirstEntry < 0) iFirstEntry = 0; - break; - } - } - } - else - { - //printf("dir scan for key: %x/%c\n", mode, mode); - mode = toupper(mode); - if ((mode >= '0' && mode <= '9') || (mode >= 'A' && mode <= 'Z')) - { - int found = -1; - for (int i = iSelectedEntry+1; i < flist_nDirEntries(); i++) - { - if (toupper(DirItem[i].d_name[0]) == mode) - { - found = i; - break; - } - } - - if (found < 0) - { - for (int i = 0; i < flist_nDirEntries(); i++) - { - if (toupper(DirItem[i].d_name[0]) == mode) - { - found = i; - break; - } - } - } - - if (found >= 0) - { - iSelectedEntry = found; - if (iSelectedEntry + (OsdGetSize() / 2) >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize(); - else iFirstEntry = iSelectedEntry - (OsdGetSize()/2) + 1; - if (iFirstEntry < 0) iFirstEntry = 0; - } - } - } - } - - return 0; -} - -int flist_nDirEntries() -{ - return DirItem.size(); -} - -int flist_iFirstEntry() -{ - return iFirstEntry; -} - -int flist_iSelectedEntry() -{ - return iSelectedEntry; -} - -dirent* flist_DirItem(int n) -{ - return &DirItem[n]; -} - -dirent* flist_SelectedItem() -{ - return &DirItem[iSelectedEntry]; -} +#include "file_io.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "osd.h" +#include "fpga_io.h" +#include "menu.h" +#include "errno.h" +#include "DiskImage.h" +#include "user_io.h" +#include "cfg.h" +#include "input.h" +#include "miniz_zip.h" +#include "scheduler.h" + +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) + +typedef std::vector DirentVector; + +static const size_t YieldIterations = 128; + +DirentVector DirItem; +int iSelectedEntry = 0; // selected entry index +int iFirstEntry = 0; + +static char full_path[2100]; + +struct fileZipArchive +{ + mz_zip_archive archive; + int index; + mz_zip_reader_extract_iter_state* iter; + __off64_t offset; +}; + +static bool FileIsZipped(char* path, char** zip_path, char** file_path) +{ + char* z = strcasestr(path, ".zip"); + if (z) + { + z += 4; + if (!z[0]) z[1] = 0; + *z++ = 0; + + if (zip_path) *zip_path = path; + if (file_path) *file_path = z; + return true; + } + return false; +} + +static char* make_fullpath(const char *path, int mode = 0) +{ + const char *root = getRootDir(); + if (strncasecmp(getRootDir(), path, strlen(root))) + { + sprintf(full_path, "%s/%s", (mode == -1) ? "" : root, path); + } + else + { + sprintf(full_path, path); + } + + return full_path; +} + +static int get_stmode(const char *path) +{ + struct stat64 st; + return (stat64(path, &st) < 0) ? 0 : st.st_mode; +} + +static bool isPathDirectory(char *path) +{ + make_fullpath(path); + + char *zip_path, *file_path; + if (FileIsZipped(full_path, &zip_path, &file_path)) + { + mz_zip_archive z{}; + if (!mz_zip_reader_init_file(&z, zip_path, 0)) + { + printf("isPathDirectory(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path, + mz_zip_get_error_string(mz_zip_get_last_error(&z))); + return false; + } + + if (!*file_path) + { + mz_zip_reader_end(&z); + return true; + } + + // Folder names always end with a slash in the zip + // file central directory. + strcat(file_path, "/"); + const int file_index = mz_zip_reader_locate_file(&z, file_path, NULL, 0); + if (file_index < 0) + { + printf("isPathDirectory(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n", + zip_path, file_path, + mz_zip_get_error_string(mz_zip_get_last_error(&z))); + mz_zip_reader_end(&z); + return false; + } + + if (mz_zip_reader_is_file_a_directory(&z, file_index)) + { + mz_zip_reader_end(&z); + return true; + } + mz_zip_reader_end(&z); + } + else + { + int stmode = get_stmode(full_path); + if (!stmode) + { + printf("isPathDirectory(stat) path:%s, error:%s.\n", full_path, strerror(errno)); + return false; + } + + if (stmode & S_IFDIR) return true; + } + + return false; +} + +static bool isPathRegularFile(char *path) +{ + make_fullpath(path); + + char *zip_path, *file_path; + if (FileIsZipped(full_path, &zip_path, &file_path)) + { + mz_zip_archive z{}; + if (!mz_zip_reader_init_file(&z, zip_path, 0)) + { + printf("isPathRegularFile(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path, + mz_zip_get_error_string(mz_zip_get_last_error(&z))); + return false; + } + + if (!*file_path) + { + mz_zip_reader_end(&z); + return false; + } + + const int file_index = mz_zip_reader_locate_file(&z, file_path, NULL, 0); + if (file_index < 0) + { + printf("isPathRegularFile(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n", + zip_path, file_path, + mz_zip_get_error_string(mz_zip_get_last_error(&z))); + mz_zip_reader_end(&z); + return false; + } + + if (!mz_zip_reader_is_file_a_directory(&z, file_index) && mz_zip_reader_is_file_supported(&z, file_index)) + { + mz_zip_reader_end(&z); + return true; + } + mz_zip_reader_end(&z); + } + else + { + int stmode = get_stmode(full_path); + if (!stmode) + { + printf("isPathRegularFile(stat) path:%s, error:%s.\n", full_path, strerror(errno)); + return false; + } + + if (stmode & S_IFREG) return true; + } + + return false; +} + +void FileClose(fileTYPE *file) +{ + if (file->zip) + { + if (file->zip->iter) + { + mz_zip_reader_extract_iter_free(file->zip->iter); + } + mz_zip_reader_end(&file->zip->archive); + + delete file->zip; + file->zip = nullptr; + } + + if (file->filp) + { + //printf("closing %p\n", file->filp); + fclose(file->filp); + if (file->type == 1) + { + if (file->name[0] == '/') + { + shm_unlink(file->name); + } + file->type = 0; + } + } + file->filp = nullptr; +} + +int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute) +{ + make_fullpath((char*)name, mode); + + FileClose(file); + file->mode = 0; + file->type = 0; + + char *p = strrchr(full_path, '/'); + strcpy(file->name, (mode == -1) ? full_path : p + 1); + + char *zip_path, *file_path; + if ((mode != -1) && FileIsZipped(full_path, &zip_path, &file_path)) + { + if (mode & O_RDWR || mode & O_WRONLY) + { + if(!mute) printf("FileOpenEx(mode) Zip:%s, writing to zipped files is not supported.\n", + full_path); + return 0; + } + + file->zip = new fileZipArchive{}; + if (!mz_zip_reader_init_file(&file->zip->archive, zip_path, 0)) + { + if(!mute) printf("FileOpenEx(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path, + mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); + return 0; + } + + file->zip->index = mz_zip_reader_locate_file(&file->zip->archive, file_path, NULL, 0); + if (file->zip->index < 0) + { + if(!mute) printf("FileOpenEx(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n", + zip_path, file_path, + mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); + FileClose(file); + return 0; + } + + mz_zip_archive_file_stat s; + if (!mz_zip_reader_file_stat(&file->zip->archive, file->zip->index, &s)) + { + if(!mute) printf("FileOpenEx(mz_zip_reader_file_stat) Zip:%s, file:%s, error:%s\n", + zip_path, file_path, + mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); + FileClose(file); + return 0; + } + file->size = s.m_uncomp_size; + + file->zip->iter = mz_zip_reader_extract_iter_new(&file->zip->archive, file->zip->index, 0); + if (!file->zip->iter) + { + if(!mute) printf("FileOpenEx(mz_zip_reader_extract_iter_new) Zip:%s, file:%s, error:%s\n", + zip_path, file_path, + mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); + FileClose(file); + return 0; + } + file->zip->offset = 0; + file->offset = 0; + file->mode = mode; + } + else + { + int fd = (mode == -1) ? shm_open("/vtrd", O_CREAT | O_RDWR | O_TRUNC, 0777) : open(full_path, mode, 0777); + if (fd <= 0) + { + if(!mute) printf("FileOpenEx(open) File:%s, error: %s.\n", full_path, strerror(errno)); + return 0; + } + const char *fmode = mode & O_RDWR ? "w+" : "r"; + file->filp = fdopen(fd, fmode); + if (!file->filp) + { + if(!mute) printf("FileOpenEx(fdopen) File:%s, error: %s.\n", full_path, strerror(errno)); + close(fd); + return 0; + } + + if (mode == -1) + { + file->type = 1; + file->size = 0; + file->offset = 0; + file->mode = O_CREAT | O_RDWR | O_TRUNC; + } + else + { + struct stat64 st; + int ret = fstat64(fileno(file->filp), &st); + if (ret < 0) + { + if (!mute) printf("FileOpenEx(fstat) File:%s, error: %d.\n", full_path, ret); + FileClose(file); + return 0; + } + + file->size = st.st_size; + file->offset = 0; + file->mode = mode; + } + } + + //printf("opened %s, size %lu\n", full_path, file->size); + return 1; +} + +__off64_t FileGetSize(fileTYPE *file) +{ + if (file->filp) + { + struct stat64 st; + int ret = fstat64(fileno(file->filp), &st); + return (ret < 0) ? 0 : st.st_size; + } + else if (file->zip) + { + return file->size; + } + return 0; +} + +int FileOpen(fileTYPE *file, const char *name, char mute) +{ + return FileOpenEx(file, name, O_RDONLY, mute); +} + +int FileSeek(fileTYPE *file, __off64_t offset, int origin) +{ + if (file->filp) + { + offset = fseeko64(file->filp, offset, origin); + if(offset<0) + { + printf("Fail to seek the file.\n"); + return 0; + } + } + else if (file->zip) + { + if (origin == SEEK_CUR) + { + offset = file->zip->offset + offset; + } + else if (origin == SEEK_END) + { + offset = file->size - offset; + } + + if (offset < file->zip->offset) + { + mz_zip_reader_extract_iter_state *iter = mz_zip_reader_extract_iter_new(&file->zip->archive, file->zip->index, 0); + if (!iter) + { + printf("FileSeek(mz_zip_reader_extract_iter_new) Failed to rewind iterator, error:%s\n", + mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); + return 0; + } + + mz_zip_reader_extract_iter_free(file->zip->iter); + file->zip->iter = iter; + file->zip->offset = 0; + } + + char buf[512]; + while (file->zip->offset < offset) + { + const size_t want_len = MIN((__off64_t)sizeof(buf), offset - file->zip->offset); + const size_t read_len = mz_zip_reader_extract_iter_read(file->zip->iter, buf, want_len); + file->zip->offset += read_len; + if (read_len < want_len) + { + printf("FileSeek(mz_zip_reader_extract_iter_read) Failed to advance iterator, error:%s\n", + mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); + return 0; + } + } + } + else + { + return 0; + } + + file->offset = offset; + return 1; +} + +int FileSeekLBA(fileTYPE *file, uint32_t offset) +{ + __off64_t off64 = offset; + off64 <<= 9; + return FileSeek(file, off64, SEEK_SET); +} + +// Read with offset advancing +int FileReadAdv(fileTYPE *file, void *pBuffer, int length) +{ + ssize_t ret = 0; + + if (file->filp) + { + ret = fread(pBuffer, 1, length, file->filp); + if (ret < 0) + { + printf("FileReadAdv error(%d).\n", ret); + return 0; + } + } + else if (file->zip) + { + ret = mz_zip_reader_extract_iter_read(file->zip->iter, pBuffer, length); + if (!ret) + { + printf("FileReadEx(mz_zip_reader_extract_iter_read) Failed to read, error:%s\n", + mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive))); + return 0; + } + file->zip->offset += ret; + } + else + { + printf("FileReadAdv error(unknown file type).\n"); + return -1; + } + + file->offset += ret; + return ret; +} + +int FileReadSec(fileTYPE *file, void *pBuffer) +{ + return FileReadAdv(file, pBuffer, 512); +} + +// Write with offset advancing +int FileWriteAdv(fileTYPE *file, void *pBuffer, int length) +{ + int ret; + + if (file->filp) + { + ret = fwrite(pBuffer, 1, length, file->filp); + fflush(file->filp); + + if (ret < 0) + { + printf("FileWriteAdv error(%d).\n", ret); + return 0; + } + } + else if (file->zip) + { + printf("FileWriteAdv error(not supported for zip).\n"); + return 0; + } + else + { + printf("FileWriteAdv error(unknown file type).\n"); + return 0; + } + + file->offset += ret; + return ret; +} + +int FileWriteSec(fileTYPE *file, void *pBuffer) +{ + return FileWriteAdv(file, pBuffer, 512); +} + +int FileSave(const char *name, void *pBuffer, int size) +{ + sprintf(full_path, "%s/%s", getRootDir(), name); + int fd = open(full_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRWXU | S_IRWXG | S_IRWXO); + if (fd < 0) + { + printf("FileSave(open) File:%s, error: %d.\n", full_path, fd); + return 0; + } + + int ret = write(fd, pBuffer, size); + close(fd); + + if (ret < 0) + { + printf("FileSave(write) File:%s, error: %d.\n", full_path, ret); + return 0; + } + + return ret; +} + +int FileSaveConfig(const char *name, void *pBuffer, int size) +{ + char path[256] = { CONFIG_DIR"/" }; + strcat(path, name); + return FileSave(path, pBuffer, size); +} + +int FileLoad(const char *name, void *pBuffer, int size) +{ + sprintf(full_path, "%s/%s", getRootDir(), name); + int fd = open(full_path, O_RDONLY); + if (fd < 0) + { + printf("FileLoad(open) File:%s, error: %d.\n", full_path, fd); + return 0; + } + + struct stat64 st; + int ret = fstat64(fd, &st); + if (ret < 0) + { + printf("FileLoad(fstat) File:%s, error: %d.\n", full_path, ret); + close(fd); + return 0; + } + + if (!pBuffer) + { + close(fd); + return (int)st.st_size; + } + + ret = read(fd, pBuffer, size ? size : st.st_size); + close(fd); + + if (ret < 0) + { + printf("FileLoad(read) File:%s, error: %d.\n", full_path, ret); + return 0; + } + + return ret; +} + +int FileLoadConfig(const char *name, void *pBuffer, int size) +{ + char path[256] = { CONFIG_DIR"/" }; + strcat(path, name); + return FileLoad(path, pBuffer, size); +} + +int FileCanWrite(const char *name) +{ + sprintf(full_path, "%s/%s", getRootDir(), name); + + if (FileIsZipped(full_path, nullptr, nullptr)) + { + return 0; + } + + struct stat64 st; + int ret = stat64(full_path, &st); + if (ret < 0) + { + printf("FileCanWrite(stat) File:%s, error: %d.\n", full_path, ret); + return 0; + } + + //printf("FileCanWrite: mode=%04o.\n", st.st_mode); + return ((st.st_mode & S_IWUSR) != 0); +} + +void FileGenerateSavePath(const char *name, char* out_name) +{ + make_fullpath(SAVE_DIR); + mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO); + strcat(full_path, "/"); + strcat(full_path, HomeDir); + mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO); + + sprintf(out_name, "%s/%s/", SAVE_DIR, HomeDir); + char *fname = out_name + strlen(out_name); + + const char *p = strrchr(name, '/'); + if (p) + { + strcat(fname, p+1); + } + else + { + strcat(fname, name); + } + + char *e = strrchr(fname, '.'); + if (e) + { + strcpy(e,".sav"); + } + else + { + strcat(fname, ".sav"); + } + + printf("SavePath=%s\n", out_name); +} + +uint32_t getFileType(const char *name) +{ + sprintf(full_path, "%s/%s", getRootDir(), name); + + struct stat64 st; + if (stat64(full_path, &st)) return 0; + + return st.st_mode; +} + +static int device = 0; +static int usbnum = 0; +const char *getStorageDir(int dev) +{ + static char path[32]; + if (!dev) return "/media/fat"; + sprintf(path, "/media/usb%d", usbnum); + return path; +} + +const char *getRootDir() +{ + return getStorageDir(device); +} + +const char *getFullPath(const char *name) +{ + sprintf(full_path, "%s/%s", getRootDir(), name); + return full_path; +} + +void setStorage(int dev) +{ + device = 0; + FileSave(CONFIG_DIR"/device.bin", &dev, sizeof(int)); + fpga_load_rbf("menu.rbf"); +} + +static int orig_device = 0; +int getStorage(int from_setting) +{ + return from_setting ? orig_device : device; +} + +int isPathMounted(int n) +{ + char path[32]; + sprintf(path, "/media/usb%d", n); + + struct stat file_stat; + struct stat parent_stat; + + if (-1 == stat(path, &file_stat)) + { + printf("failed to stat %s\n", path); + return 0; + } + + if (!(file_stat.st_mode & S_IFDIR)) + { + printf("%s is not a directory.\n", path); + return 0; + } + + if (-1 == stat("/media", &parent_stat)) + { + printf("failed to stat /media\n"); + return 0; + } + + if (file_stat.st_dev != parent_stat.st_dev || + (file_stat.st_dev == parent_stat.st_dev && + file_stat.st_ino == parent_stat.st_ino)) + { + printf("%s IS a mountpoint.\n", path); + struct statfs fs_stat; + if (!statfs(path, &fs_stat)) + { + printf("%s is FS: 0x%08X\n", path, fs_stat.f_type); + if (fs_stat.f_type != EXT4_SUPER_MAGIC) + { + printf("%s is not EXT2/3/4.\n", path); + return 1; + } + } + } + + printf("%s is NOT a VFAT mountpoint.\n", path); + return 0; +} + +int isUSBMounted() +{ + for (int i = 0; i < 4; i++) + { + if (isPathMounted(i)) + { + usbnum = i; + return 1; + } + } + return 0; +} + +void FindStorage(void) +{ + char str[128]; + printf("Looking for root device...\n"); + device = 0; + FileLoad(CONFIG_DIR"/device.bin", &device, sizeof(int)); + orig_device = device; + + if(device && !isUSBMounted()) + { + int saveddev = device; + device = 0; + MiSTer_ini_parse(); + device = saveddev; + parse_video_mode(); + user_io_send_buttons(1); + + printf("Waiting for USB...\n"); + int btn = 0; + int done = 0; + for (int i = 30; i >= 0; i--) + { + sprintf(str, "\n Waiting for USB...\n\n %d \n\n\n OSD/USER or ESC to cancel", i); + InfoMessage(str); + if (isUSBMounted()) + { + done = 1; + break; + } + + for (int i = 0; i < 10; i++) + { + btn = fpga_get_buttons(); + if (!btn) btn = input_poll(1); + if (btn) + { + printf("Button has been pressed %d\n", btn); + InfoMessage("\n\n Canceled!\n"); + usleep(500000); + setStorage(0); + break; + } + usleep(100000); + } + if (done) break; + } + + if (!done) + { + InfoMessage("\n\n No USB storage found\n Falling back to SD card\n"); + usleep(2000000); + setStorage(0); + } + } + + if (device) + { + printf("Using USB as a root device\n"); + } + else + { + printf("Using SD card as a root device\n"); + } + + sprintf(full_path, "%s/" CONFIG_DIR, getRootDir()); + DIR* dir = opendir(full_path); + if (dir) closedir(dir); + else if (ENOENT == errno) mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO); +} + +struct DirentComp +{ + bool operator()(const dirent& de1, const dirent& de2) + { + if (++iterations % YieldIterations == 0) + { + scheduler_yield(); + } + + if ((de1.d_type == DT_DIR) && !strcmp(de1.d_name, "..")) return true; + if ((de2.d_type == DT_DIR) && !strcmp(de2.d_name, "..")) return false; + + if ((de1.d_type == DT_DIR) && (de2.d_type == DT_REG)) return true; + if ((de1.d_type == DT_REG) && (de2.d_type == DT_DIR)) return false; + + if ((de1.d_type == DT_REG) && (de2.d_type == DT_REG)) + { + int len1 = strlen(de1.d_name); + int len2 = strlen(de2.d_name); + if ((len1 > 4) && (de1.d_name[len1 - 4] == '.')) len1 -= 4; + if ((len2 > 4) && (de2.d_name[len2 - 4] == '.')) len2 -= 4; + + int len = (len1 < len2) ? len1 : len2; + int ret = strncasecmp(de1.d_name, de2.d_name, len); + if (!ret) + { + return len1 < len2; + } + + return ret < 0; + } + + return strcasecmp(de1.d_name, de2.d_name) < 0; + } + + size_t iterations = 0; +}; + +void AdjustDirectory(char *path) +{ + if (isPathDirectory(path)) return; + + char *p = strrchr(path, '/'); + if (p) + { + *p = 0; + } + else + { + path[0] = 0; + } +} + +static bool IsInSameFolder(const char *folder, const char *path) +{ + if (strcasestr(path, folder) == path) + { + const char *subpath = path + strlen(folder) + 1; + if (*subpath != '\0') + { + const char *slash = strchr(subpath, '/'); + return !slash || *(slash + 1) == '\0'; + } + } + return false; +} + +int ScanDirectory(char* path, int mode, const char *extension, int options, const char *prefix) +{ + static char file_name[1024]; + + int has_trd = 0; + const char *ext = extension; + while (*ext) + { + if (!strncasecmp(ext, "TRD", 3)) has_trd = 1; + ext += 3; + } + + const char* is_zipped = strcasestr(path, ".zip"); + if (is_zipped && strcasestr(is_zipped + 4, ".zip")) + { + printf("Nested zip-files are not supported: %s\n", path); + return 0; + } + int extlen = strlen(extension); + + //printf("scan dir\n"); + + if (mode == SCANF_INIT) + { + file_name[0] = 0; + if (!isPathDirectory(path)) + { + bool isfile = isPathRegularFile(path); + char *p = strrchr(path, '/'); + if (p) + { + if (isfile) strcpy(file_name, p + 1); + *p = 0; + } + else + { + if (isfile) strcpy(file_name, path); + path[0] = 0; + } + } + + if (!isPathDirectory(path)) + { + path[0] = 0; + file_name[0] = 0; + } + + sprintf(full_path, "%s/%s", getRootDir(), path); + int path_len = strlen(full_path); + + printf("Start to scan %sdir: %s\n", is_zipped ? "zipped " : "", full_path); + + char *zip_path, *file_path_in_zip = (char*)""; + FileIsZipped(full_path, &zip_path, &file_path_in_zip); + + iFirstEntry = 0; + iSelectedEntry = 0; + DirItem.clear(); + + DIR *d = nullptr; + mz_zip_archive *z = nullptr; + if (is_zipped) + { + mz_zip_archive _z = {}; + if (!mz_zip_reader_init_file(&_z, zip_path, 0)) + { + printf("Couldn't open zip file %s: %s\n", full_path, mz_zip_get_error_string(mz_zip_get_last_error(z))); + return 0; + } + z = new mz_zip_archive(_z); + } + else + { + d = opendir(full_path); + if (!d) + { + printf("Couldn't open dir: %s\n", full_path); + return 0; + } + } + + struct dirent *de = nullptr; + for (size_t i = 0; (d && (de = readdir(d))) + || (z && i < mz_zip_reader_get_num_files(z)); i++) + { + if (0 < i && i % YieldIterations == 0) + { + scheduler_yield(); + } + + struct dirent _de = {}; + if (z) { + mz_zip_reader_get_filename(z, i, &_de.d_name[0], sizeof(_de.d_name)); + if (!IsInSameFolder(file_path_in_zip, _de.d_name)) + { + continue; + } + // Remove leading folders. + const char* subpath = _de.d_name + strlen(file_path_in_zip); + if (*subpath == '/') + { + subpath++; + } + strcpy(_de.d_name, subpath); + + _de.d_type = mz_zip_reader_is_file_a_directory(z, i) ? DT_DIR : DT_REG; + if (_de.d_type == DT_DIR) + { + // Remove trailing slash. + _de.d_name[strlen(_de.d_name) - 1] = '\0'; + } + de = &_de; + } + else + // Handle (possible) symbolic link type in the directory entry + if (de->d_type == DT_LNK || de->d_type == DT_REG) + { + sprintf(full_path+path_len, "/%s", de->d_name); + + struct stat entrystat; + + if (!stat(full_path, &entrystat)) + { + if (S_ISREG(entrystat.st_mode)) + { + de->d_type = DT_REG; + } + else if (S_ISDIR(entrystat.st_mode)) + { + de->d_type = DT_DIR; + } + } + } + + if (de->d_type == DT_DIR) + { + if (!strcmp(de->d_name, ".")) continue; + if (!strcmp(de->d_name, "..")) + { + if(!strlen(path)) continue; + } + + if (!(options & SCANO_DIR)) + { + if (de->d_name[0] != '_' && strcmp(de->d_name, "..")) continue; + if (!(options & SCANO_CORES)) continue; + } + } + else if (de->d_type == DT_REG) + { + //skip non-selectable files + if (!strcasecmp(de->d_name, "menu.rbf")) continue; + if (!strncasecmp(de->d_name, "menu_20",7)) continue; + if (!strcasecmp(de->d_name, "boot.rom")) continue; + + //check the prefix if given + if (prefix && strncasecmp(prefix, de->d_name, strlen(prefix))) continue; + + if (extlen > 0) + { + const char *ext = extension; + int found = (has_trd && x2trd_ext_supp(de->d_name)); + if (!found && !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".zip")) + { + // Fake that zip-file is a directory. + de->d_type = DT_DIR; + found = 1; + } + if (!found && is_minimig() && !memcmp(extension, "HDF", 3)) + { + found = !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".iso"); + } + + char *fext = strrchr(de->d_name, '.'); + while(!found && *ext && fext) + { + char e[4]; + memcpy(e, ext, 3); + if (e[2] == ' ') + { + e[2] = 0; + if (e[1] == ' ') e[1] = 0; + } + e[3] = 0; + found = 1; + for (int i = 0; i < 3; i++) + { + if (e[i] == '*') break; + if (e[i] == '?' && fext[i+1]) continue; + if (tolower(e[i]) != tolower(fext[i + 1])) found = 0; + } + if (found) break; + + if (strlen(ext) < 3) break; + ext += 3; + } + if (!found) continue; + } + } + else + { + continue; + } + DirItem.push_back(*de); + } + if (z) + { + // Since zip files aren't actually folders the entry to + // exit the zip file must be added manually. + dirent up; + up.d_type = DT_DIR; + strcpy(up.d_name, ".."); + DirItem.push_back(up); + + mz_zip_reader_end(z); + delete z; + } + if (d) + { + closedir(d); + } + + printf("Got %d dir entries\n", flist_nDirEntries()); + if (!flist_nDirEntries()) return 0; + + std::sort(DirItem.begin(), DirItem.end(), DirentComp()); + if (file_name[0]) + { + for (int i = 0; i < flist_nDirEntries(); i++) + { + if (!strcmp(file_name, DirItem[i].d_name)) + { + iSelectedEntry = i; + if (iSelectedEntry + (OsdGetSize() / 2) - 1 >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize(); + else iFirstEntry = iSelectedEntry - (OsdGetSize() / 2) + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + break; + } + } + } + return flist_nDirEntries(); + } + else + { + if (flist_nDirEntries() == 0) // directory is empty so there is no point in searching for any entry + return 0; + + if (mode == SCANF_END) + { + iSelectedEntry = flist_nDirEntries() - 1; + iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + return 0; + } + else if (mode == SCANF_NEXT) + { + if(iSelectedEntry + 1 < flist_nDirEntries()) // scroll within visible items + { + iSelectedEntry++; + if (iSelectedEntry > iFirstEntry + OsdGetSize() - 1) iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + } + return 0; + } + else if (mode == SCANF_PREV) + { + if (iSelectedEntry > 0) // scroll within visible items + { + iSelectedEntry--; + if (iSelectedEntry < iFirstEntry) iFirstEntry = iSelectedEntry; + } + return 0; + } + else if (mode == SCANF_NEXT_PAGE) + { + if (iSelectedEntry < iFirstEntry + OsdGetSize() - 1) + { + iSelectedEntry = iFirstEntry + OsdGetSize() - 1; + if (iSelectedEntry >= flist_nDirEntries()) iSelectedEntry = flist_nDirEntries() - 1; + } + else + { + iSelectedEntry += OsdGetSize(); + iFirstEntry += OsdGetSize(); + if (iSelectedEntry >= flist_nDirEntries()) + { + iSelectedEntry = flist_nDirEntries() - 1; + iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + } + else if (iFirstEntry + OsdGetSize() > flist_nDirEntries()) + { + iFirstEntry = flist_nDirEntries() - OsdGetSize(); + } + } + return 0; + } + else if (mode == SCANF_PREV_PAGE) + { + if(iSelectedEntry != iFirstEntry) + { + iSelectedEntry = iFirstEntry; + } + else + { + iFirstEntry -= OsdGetSize(); + if (iFirstEntry < 0) iFirstEntry = 0; + iSelectedEntry = iFirstEntry; + } + } + else if (mode == SCANF_SET_ITEM) + { + for (int i = 0; i < flist_nDirEntries(); i++) + { + if((DirItem[i].d_type == DT_DIR) && !strcmp(DirItem[i].d_name, extension)) + { + iSelectedEntry = i; + if (iSelectedEntry + (OsdGetSize() / 2) - 1 >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize(); + else iFirstEntry = iSelectedEntry - (OsdGetSize() / 2) + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + break; + } + } + } + else + { + //printf("dir scan for key: %x/%c\n", mode, mode); + mode = toupper(mode); + if ((mode >= '0' && mode <= '9') || (mode >= 'A' && mode <= 'Z')) + { + int found = -1; + for (int i = iSelectedEntry+1; i < flist_nDirEntries(); i++) + { + if (toupper(DirItem[i].d_name[0]) == mode) + { + found = i; + break; + } + } + + if (found < 0) + { + for (int i = 0; i < flist_nDirEntries(); i++) + { + if (toupper(DirItem[i].d_name[0]) == mode) + { + found = i; + break; + } + } + } + + if (found >= 0) + { + iSelectedEntry = found; + if (iSelectedEntry + (OsdGetSize() / 2) >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize(); + else iFirstEntry = iSelectedEntry - (OsdGetSize()/2) + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + } + } + } + } + + return 0; +} + +int flist_nDirEntries() +{ + return DirItem.size(); +} + +int flist_iFirstEntry() +{ + return iFirstEntry; +} + +int flist_iSelectedEntry() +{ + return iSelectedEntry; +} + +dirent* flist_DirItem(int n) +{ + return &DirItem[n]; +} + +dirent* flist_SelectedItem() +{ + return &DirItem[iSelectedEntry]; +} diff --git a/file_io.h b/file_io.h index 406346c..3031aa8 100644 --- a/file_io.h +++ b/file_io.h @@ -1,88 +1,88 @@ -#ifndef _FAT16_H_INCLUDED -#define _FAT16_H_INCLUDED - -#include -#include -#include -#include -#include "spi.h" - -struct fileZipArchive; - -typedef struct -{ - FILE *filp; - int mode; - int type; - fileZipArchive *zip; - __off64_t size; - __off64_t offset; - char path[1024]; - char name[261]; -} fileTYPE; - -int flist_nDirEntries(); -int flist_iFirstEntry(); -int flist_iSelectedEntry(); -dirent* flist_DirItem(int n); -dirent* flist_SelectedItem(); - -// scanning flags -#define SCANF_INIT 0 // start search from beginning of directory -#define SCANF_NEXT 1 // find next file in directory -#define SCANF_PREV -1 // find previous file in directory -#define SCANF_NEXT_PAGE 2 // find next 16 files in directory -#define SCANF_PREV_PAGE -2 // find previous 16 files in directory -#define SCANF_SET_ITEM 3 // find exact item -#define SCANF_END 4 // find last file in directory - -// options flags -#define SCANO_DIR 1 // include subdirectories -#define SCANO_UMOUNT 2 // allow backspace key -#define SCANO_CORES 4 // only include subdirectories with prefix '_' -#define SCANO_COEFF 8 - -void FindStorage(); -int getStorage(int from_setting); -void setStorage(int dev); -int isUSBMounted(); - -int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute = 0); -int FileOpen(fileTYPE *file, const char *name, char mute = 0); -void FileClose(fileTYPE *file); - -__off64_t FileGetSize(fileTYPE *file); - -int FileSeek(fileTYPE *file, __off64_t offset, int origin); -int FileSeekLBA(fileTYPE *file, uint32_t offset); - -int FileReadAdv(fileTYPE *file, void *pBuffer, int length); -int FileReadSec(fileTYPE *file, void *pBuffer); -int FileWriteAdv(fileTYPE *file, void *pBuffer, int length); -int FileWriteSec(fileTYPE *file, void *pBuffer); - -int FileCanWrite(const char *name); - -#define SAVE_DIR "saves" -void FileGenerateSavePath(const char *name, char* out_name); - -int FileSave(const char *name, void *pBuffer, int size); -int FileLoad(const char *name, void *pBuffer, int size); // supply pBuffer = 0 to get the file size without loading - -//save/load from config dir -#define CONFIG_DIR "config" -int FileSaveConfig(const char *name, void *pBuffer, int size); -int FileLoadConfig(const char *name, void *pBuffer, int size); // supply pBuffer = 0 to get the file size without loading - -void AdjustDirectory(char *path); -int ScanDirectory(char* path, int mode, const char *extension, int options, const char *prefix = NULL); - -const char *getStorageDir(int dev); -const char *getRootDir(); -const char *getFullPath(const char *name); - -uint32_t getFileType(const char *name); - -#define COEFF_DIR "filters" - -#endif +#ifndef _FAT16_H_INCLUDED +#define _FAT16_H_INCLUDED + +#include +#include +#include +#include +#include "spi.h" + +struct fileZipArchive; + +typedef struct +{ + FILE *filp; + int mode; + int type; + fileZipArchive *zip; + __off64_t size; + __off64_t offset; + char path[1024]; + char name[261]; +} fileTYPE; + +int flist_nDirEntries(); +int flist_iFirstEntry(); +int flist_iSelectedEntry(); +dirent* flist_DirItem(int n); +dirent* flist_SelectedItem(); + +// scanning flags +#define SCANF_INIT 0 // start search from beginning of directory +#define SCANF_NEXT 1 // find next file in directory +#define SCANF_PREV -1 // find previous file in directory +#define SCANF_NEXT_PAGE 2 // find next 16 files in directory +#define SCANF_PREV_PAGE -2 // find previous 16 files in directory +#define SCANF_SET_ITEM 3 // find exact item +#define SCANF_END 4 // find last file in directory + +// options flags +#define SCANO_DIR 1 // include subdirectories +#define SCANO_UMOUNT 2 // allow backspace key +#define SCANO_CORES 4 // only include subdirectories with prefix '_' +#define SCANO_COEFF 8 + +void FindStorage(); +int getStorage(int from_setting); +void setStorage(int dev); +int isUSBMounted(); + +int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute = 0); +int FileOpen(fileTYPE *file, const char *name, char mute = 0); +void FileClose(fileTYPE *file); + +__off64_t FileGetSize(fileTYPE *file); + +int FileSeek(fileTYPE *file, __off64_t offset, int origin); +int FileSeekLBA(fileTYPE *file, uint32_t offset); + +int FileReadAdv(fileTYPE *file, void *pBuffer, int length); +int FileReadSec(fileTYPE *file, void *pBuffer); +int FileWriteAdv(fileTYPE *file, void *pBuffer, int length); +int FileWriteSec(fileTYPE *file, void *pBuffer); + +int FileCanWrite(const char *name); + +#define SAVE_DIR "saves" +void FileGenerateSavePath(const char *name, char* out_name); + +int FileSave(const char *name, void *pBuffer, int size); +int FileLoad(const char *name, void *pBuffer, int size); // supply pBuffer = 0 to get the file size without loading + +//save/load from config dir +#define CONFIG_DIR "config" +int FileSaveConfig(const char *name, void *pBuffer, int size); +int FileLoadConfig(const char *name, void *pBuffer, int size); // supply pBuffer = 0 to get the file size without loading + +void AdjustDirectory(char *path); +int ScanDirectory(char* path, int mode, const char *extension, int options, const char *prefix = NULL); + +const char *getStorageDir(int dev); +const char *getRootDir(); +const char *getFullPath(const char *name); + +uint32_t getFileType(const char *name); + +#define COEFF_DIR "filters" + +#endif diff --git a/fpga_io.cpp b/fpga_io.cpp index 0e48815..a3ac44e 100644 --- a/fpga_io.cpp +++ b/fpga_io.cpp @@ -1,653 +1,653 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "fpga_io.h" -#include "file_io.h" - -#include "fpga_base_addr_ac5.h" -#include "fpga_manager.h" -#include "fpga_system_manager.h" -#include "fpga_reset_manager.h" -#include "fpga_nic301.h" - -#define FPGA_REG_BASE 0xFF000000 -#define FPGA_REG_SIZE 0x01000000 - -#define MAP_ADDR(x) (volatile uint32_t*)(&map_base[(((uint32_t)(x)) & 0xFFFFFF)>>2]) -#define IS_REG(x) (((((uint32_t)(x))-1)>=(FPGA_REG_BASE - 1)) && ((((uint32_t)(x))-1)<(FPGA_REG_BASE + FPGA_REG_SIZE - 1))) - -#define fatal(x) munmap((void*)map_base, FPGA_REG_SIZE); close(fd); exit(x) - -static struct socfpga_reset_manager *reset_regs = (socfpga_reset_manager *)SOCFPGA_RSTMGR_ADDRESS; -static struct socfpga_fpga_manager *fpgamgr_regs = (socfpga_fpga_manager *)SOCFPGA_FPGAMGRREGS_ADDRESS; -static struct socfpga_system_manager *sysmgr_regs = (socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS; -static struct nic301_registers *nic301_regs = (nic301_registers *)SOCFPGA_L3REGS_ADDRESS; - -static uint32_t *map_base; -static int fd; - -static __inline void writel(uint32_t val, const void* reg) -{ -/* - if(!IS_REG(reg)) - { - printf("ERROR: Trying to write undefined address: %p\n.", reg); - fatal(-1); - } -*/ - *MAP_ADDR(reg) = val; -} - -static __inline uint32_t readl(const void* reg) -{ -/* - if (!IS_REG(reg)) - { - printf("ERROR: Trying to read undefined address: %p\n.", reg); - fatal(-1); - } -*/ - return *MAP_ADDR(reg); -} - -#define clrsetbits_le32(addr, clear, set) writel((readl(addr) & ~(clear)) | (set), addr) -#define setbits_le32(addr, set) writel( readl(addr) | (set), addr) -#define clrbits_le32(addr, clear) writel( readl(addr) & ~(clear), addr) - -/* Timeout count */ -#define FPGA_TIMEOUT_CNT 0x1000000 - -/* Set CD ratio */ -static void fpgamgr_set_cd_ratio(unsigned long ratio) -{ - clrsetbits_le32(&fpgamgr_regs->ctrl, - 0x3 << FPGAMGRREGS_CTRL_CDRATIO_LSB, - (ratio & 0x3) << FPGAMGRREGS_CTRL_CDRATIO_LSB); -} - -static int fpgamgr_dclkcnt_set(unsigned long cnt) -{ - unsigned long i; - - /* Clear any existing done status */ - if (readl(&fpgamgr_regs->dclkstat)) - writel(0x1, &fpgamgr_regs->dclkstat); - - /* Write the dclkcnt */ - writel(cnt, &fpgamgr_regs->dclkcnt); - - /* Wait till the dclkcnt done */ - for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { - if (!readl(&fpgamgr_regs->dclkstat)) - continue; - - writel(0x1, &fpgamgr_regs->dclkstat); - return 0; - } - - return -ETIMEDOUT; -} - -/* Check whether FPGA Init_Done signal is high */ -static int is_fpgamgr_initdone_high(void) -{ - unsigned long val; - - val = readl(&fpgamgr_regs->gpio_ext_porta); - return val & FPGAMGRREGS_MON_GPIO_EXT_PORTA_ID_MASK; -} - -/* Get the FPGA mode */ -static int fpgamgr_get_mode(void) -{ - unsigned long val; - - val = readl(&fpgamgr_regs->stat); - return val & FPGAMGRREGS_STAT_MODE_MASK; -} - -/* Check whether FPGA is ready to be accessed */ -static int fpgamgr_test_fpga_ready(void) -{ - /* Check for init done signal */ - if (!is_fpgamgr_initdone_high()) - return 0; - - /* Check again to avoid false glitches */ - if (!is_fpgamgr_initdone_high()) - return 0; - - if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_USERMODE) - return 0; - - return 1; -} - -/* -// Poll until FPGA is ready to be accessed or timeout occurred -static int fpgamgr_poll_fpga_ready(void) -{ - unsigned long i; - - // If FPGA is blank, wait till WD invoke warm reset - for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { - // check for init done signal - if (!is_fpgamgr_initdone_high()) - continue; - // check again to avoid false glitches - if (!is_fpgamgr_initdone_high()) - continue; - return 1; - } - - return 0; -} -*/ - -/* Start the FPGA programming by initialize the FPGA Manager */ -static int fpgamgr_program_init(void) -{ - unsigned long msel, i; - - /* Get the MSEL value */ - msel = readl(&fpgamgr_regs->stat); - msel &= FPGAMGRREGS_STAT_MSEL_MASK; - msel >>= FPGAMGRREGS_STAT_MSEL_LSB; - - /* - * Set the cfg width - * If MSEL[3] = 1, cfg width = 32 bit - */ - if (msel & 0x8) { - setbits_le32(&fpgamgr_regs->ctrl, - FPGAMGRREGS_CTRL_CFGWDTH_MASK); - - /* To determine the CD ratio */ - /* MSEL[1:0] = 0, CD Ratio = 1 */ - if ((msel & 0x3) == 0x0) - fpgamgr_set_cd_ratio(CDRATIO_x1); - /* MSEL[1:0] = 1, CD Ratio = 4 */ - else if ((msel & 0x3) == 0x1) - fpgamgr_set_cd_ratio(CDRATIO_x4); - /* MSEL[1:0] = 2, CD Ratio = 8 */ - else if ((msel & 0x3) == 0x2) - fpgamgr_set_cd_ratio(CDRATIO_x8); - - } - else { /* MSEL[3] = 0 */ - clrbits_le32(&fpgamgr_regs->ctrl, - FPGAMGRREGS_CTRL_CFGWDTH_MASK); - - /* To determine the CD ratio */ - /* MSEL[1:0] = 0, CD Ratio = 1 */ - if ((msel & 0x3) == 0x0) - fpgamgr_set_cd_ratio(CDRATIO_x1); - /* MSEL[1:0] = 1, CD Ratio = 2 */ - else if ((msel & 0x3) == 0x1) - fpgamgr_set_cd_ratio(CDRATIO_x2); - /* MSEL[1:0] = 2, CD Ratio = 4 */ - else if ((msel & 0x3) == 0x2) - fpgamgr_set_cd_ratio(CDRATIO_x4); - } - - /* To enable FPGA Manager configuration */ - clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCE_MASK); - - /* To enable FPGA Manager drive over configuration line */ - setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_EN_MASK); - - /* Put FPGA into reset phase */ - setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCONFIGPULL_MASK); - - /* (1) wait until FPGA enter reset phase */ - for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { - if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_RESETPHASE) - break; - } - - /* If not in reset state, return error */ - if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_RESETPHASE) { - puts("FPGA: Could not reset\n"); - return -1; - } - - /* Release FPGA from reset phase */ - clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCONFIGPULL_MASK); - - /* (2) wait until FPGA enter configuration phase */ - for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { - if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_CFGPHASE) - break; - } - - /* If not in configuration state, return error */ - if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_CFGPHASE) { - puts("FPGA: Could not configure\n"); - return -2; - } - - /* Clear all interrupts in CB Monitor */ - writel(0xFFF, &fpgamgr_regs->gpio_porta_eoi); - - /* Enable AXI configuration */ - setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_AXICFGEN_MASK); - - return 0; -} - -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) - -/* Write the RBF data to FPGA Manager */ -static void fpgamgr_program_write(const void *rbf_data, unsigned long rbf_size) -{ - uint32_t src = (uint32_t)rbf_data; - uint32_t dst = (uint32_t)MAP_ADDR(SOCFPGA_FPGAMGRDATA_ADDRESS); - - /* Number of loops for 32-byte long copying. */ - uint32_t loops32 = rbf_size / 32; - /* Number of loops for 4-byte long copying + trailing bytes */ - uint32_t loops4 = DIV_ROUND_UP(rbf_size % 32, 4); - - __asm volatile( - "1: ldmia %0!,{r0-r7} \n" - " stmia %1!,{r0-r7} \n" - " sub %1, #32 \n" - " subs %2, #1 \n" - " bne 1b \n" - " cmp %3, #0 \n" - " beq 3f \n" - "2: ldr %2, [%0], #4 \n" - " str %2, [%1] \n" - " subs %3, #1 \n" - " bne 2b \n" - "3: nop \n" - : "+r"(src), "+r"(dst), "+r"(loops32), "+r"(loops4) : - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "cc"); -} - -/* Ensure the FPGA entering config done */ -static int fpgamgr_program_poll_cd(void) -{ - const uint32_t mask = FPGAMGRREGS_MON_GPIO_EXT_PORTA_NS_MASK | - FPGAMGRREGS_MON_GPIO_EXT_PORTA_CD_MASK; - unsigned long reg, i; - - /* (3) wait until full config done */ - for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { - reg = readl(&fpgamgr_regs->gpio_ext_porta); - - /* Config error */ - if (!(reg & mask)) { - printf("FPGA: Configuration error.\n"); - return -3; - } - - /* Config done without error */ - if (reg & mask) - break; - } - - /* Timeout happened, return error */ - if (i == FPGA_TIMEOUT_CNT) { - printf("FPGA: Timeout waiting for program.\n"); - return -4; - } - - /* Disable AXI configuration */ - clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_AXICFGEN_MASK); - - return 0; -} - -/* Ensure the FPGA entering init phase */ -static int fpgamgr_program_poll_initphase(void) -{ - unsigned long i; - - /* Additional clocks for the CB to enter initialization phase */ - if (fpgamgr_dclkcnt_set(0x4)) - return -5; - - /* (4) wait until FPGA enter init phase or user mode */ - for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { - if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_INITPHASE) - break; - if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_USERMODE) - break; - } - - /* If not in configuration state, return error */ - if (i == FPGA_TIMEOUT_CNT) - return -6; - - return 0; -} - -/* Ensure the FPGA entering user mode */ -static int fpgamgr_program_poll_usermode(void) -{ - unsigned long i; - - /* Additional clocks for the CB to exit initialization phase */ - if (fpgamgr_dclkcnt_set(0x5000)) - return -7; - - /* (5) wait until FPGA enter user mode */ - for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { - if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_USERMODE) - break; - } - /* If not in configuration state, return error */ - if (i == FPGA_TIMEOUT_CNT) - return -8; - - /* To release FPGA Manager drive over configuration line */ - clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_EN_MASK); - - return 0; -} - -/* -* FPGA Manager to program the FPGA. This is the interface used by FPGA driver. -* Return 0 for sucess, non-zero for error. -*/ -static int socfpga_load(const void *rbf_data, size_t rbf_size) -{ - unsigned long status; - - if ((uint32_t)rbf_data & 0x3) { - printf("FPGA: Unaligned data, realign to 32bit boundary.\n"); - return -EINVAL; - } - - /* Initialize the FPGA Manager */ - status = fpgamgr_program_init(); - if (status) - return status; - - /* Write the RBF data to FPGA Manager */ - fpgamgr_program_write(rbf_data, rbf_size); - - /* Ensure the FPGA entering config done */ - status = fpgamgr_program_poll_cd(); - if (status) - return status; - - /* Ensure the FPGA entering init phase */ - status = fpgamgr_program_poll_initphase(); - if (status) - return status; - - /* Ensure the FPGA entering user mode */ - return fpgamgr_program_poll_usermode(); -} - -static void do_bridge(uint32_t enable) -{ - if (enable) - { - writel(0x00003FFF, (void*)(SOCFPGA_SDR_ADDRESS + 0x5080)); - writel(0x00000000, &reset_regs->brg_mod_reset); - writel(0x00000019, &nic301_regs->remap); - } - else - { - writel(0, &sysmgr_regs->fpgaintfgrp_module); - writel(0, (void*)(SOCFPGA_SDR_ADDRESS + 0x5080)); - writel(7, &reset_regs->brg_mod_reset); - writel(1, &nic301_regs->remap); - } -} - -static int make_env(const char *name, const char *cfg) -{ - if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) return -1; - - void* buf = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x1FFFF000); - if (buf == (void *)-1) - { - printf("Unable to mmap(/dev/mem)\n"); - close(fd); - return -1; - } - - volatile char* str = (volatile char*)buf; - memset((void*)str, 0, 0x1000); - - *str++ = 0x21; - *str++ = 0x43; - *str++ = 0x65; - *str++ = 0x87; - *str++ = 'c'; - *str++ = 'o'; - *str++ = 'r'; - *str++ = 'e'; - *str++ = '='; - *str++ = '"'; - - for (uint32_t i = 0; i < strlen(name); i++) - { - *str++ = name[i]; - } - - *str++ = '"'; - *str++ = '\n'; - FileLoad(cfg, (void*)str, 0); - munmap(buf, 0x1000); - return 0; -} - -int fpga_load_rbf(const char *name, const char *cfg) -{ - static char path[1024]; - int ret = 0; - - if(cfg) - { - make_env(name, cfg); - do_bridge(0); - reboot(0); - } - - printf("Loading RBF: %s\n", name); - sprintf(path, "%s/%s", !strcasecmp(name, "menu.rbf") ? getStorageDir(0) : getRootDir(), name); - - int rbf = open(path, O_RDONLY); - if (rbf < 0) - { - printf("Couldn't open file %s\n", path); - return -1; - } - else - { - struct stat64 st; - if (fstat64(rbf, &st)<0) - { - printf("Couldn't get info of file %s\n", path); - ret = -1; - } - else - { - printf("Bitstream size: %lld bytes\n", st.st_size); - - void *buf = malloc(st.st_size); - if (!buf) - { - printf("Couldn't allocate %llu bytes.\n", st.st_size); - ret = -1; - } - else - { - if (read(rbf, buf, st.st_size)> 8) != 0x5CA623) return -1; - return coretype & 0xFF; -} - -int fpga_get_fio_size() -{ - return (fpga_gpi_read()>>16) & 1; -} - -int fpga_get_io_version() -{ - return (fpga_gpi_read() >> 18) & 3; -} - -void fpga_set_led(uint32_t on) -{ - uint32_t gpo = fpga_gpo_read(); - fpga_gpo_write(on ? gpo | 0x20000000 : gpo & ~0x20000000); -} - -int fpga_get_buttons() -{ - fpga_gpo_write(fpga_gpo_read() | 0x80000000); - int gpi = fpga_gpi_read(); - if (gpi < 0) gpi = 0; // FPGA is not in user mode. Ignore the data; - return (gpi >> 29) & 3; -} - -void reboot(int cold) -{ - sync(); - fpga_core_reset(1); - - usleep(500000); - - writel(cold ? 0 : 0x1, &reset_regs->tstscratch); - writel(2, &reset_regs->ctrl); - while (1); -} - -char *getappname() -{ - static char dest[PATH_MAX]; - memset(dest, 0, sizeof(dest)); - - char path[64]; - sprintf(path, "/proc/%d/exe", getpid()); - readlink(path, dest, PATH_MAX); - - return dest; -} - -void app_restart(const char *path) -{ - sync(); - fpga_core_reset(1); - - char *appname = getappname(); - printf("restarting the %s\n", appname); - execl(appname, appname, path, NULL); - - printf("Something went wrong. Rebooting...\n"); - reboot(0); -} - -void fpga_core_reset(int reset) -{ - uint32_t gpo = fpga_gpo_read() & ~0xC0000000; - fpga_gpo_write(reset ? gpo | 0x40000000 : gpo | 0x80000000); -} - -int is_fpga_ready(int quick) -{ - if (quick) - { - return (fpga_gpi_read() >= 0); - } - - return fpgamgr_test_fpga_ready(); -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fpga_io.h" +#include "file_io.h" + +#include "fpga_base_addr_ac5.h" +#include "fpga_manager.h" +#include "fpga_system_manager.h" +#include "fpga_reset_manager.h" +#include "fpga_nic301.h" + +#define FPGA_REG_BASE 0xFF000000 +#define FPGA_REG_SIZE 0x01000000 + +#define MAP_ADDR(x) (volatile uint32_t*)(&map_base[(((uint32_t)(x)) & 0xFFFFFF)>>2]) +#define IS_REG(x) (((((uint32_t)(x))-1)>=(FPGA_REG_BASE - 1)) && ((((uint32_t)(x))-1)<(FPGA_REG_BASE + FPGA_REG_SIZE - 1))) + +#define fatal(x) munmap((void*)map_base, FPGA_REG_SIZE); close(fd); exit(x) + +static struct socfpga_reset_manager *reset_regs = (socfpga_reset_manager *)SOCFPGA_RSTMGR_ADDRESS; +static struct socfpga_fpga_manager *fpgamgr_regs = (socfpga_fpga_manager *)SOCFPGA_FPGAMGRREGS_ADDRESS; +static struct socfpga_system_manager *sysmgr_regs = (socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS; +static struct nic301_registers *nic301_regs = (nic301_registers *)SOCFPGA_L3REGS_ADDRESS; + +static uint32_t *map_base; +static int fd; + +static __inline void writel(uint32_t val, const void* reg) +{ +/* + if(!IS_REG(reg)) + { + printf("ERROR: Trying to write undefined address: %p\n.", reg); + fatal(-1); + } +*/ + *MAP_ADDR(reg) = val; +} + +static __inline uint32_t readl(const void* reg) +{ +/* + if (!IS_REG(reg)) + { + printf("ERROR: Trying to read undefined address: %p\n.", reg); + fatal(-1); + } +*/ + return *MAP_ADDR(reg); +} + +#define clrsetbits_le32(addr, clear, set) writel((readl(addr) & ~(clear)) | (set), addr) +#define setbits_le32(addr, set) writel( readl(addr) | (set), addr) +#define clrbits_le32(addr, clear) writel( readl(addr) & ~(clear), addr) + +/* Timeout count */ +#define FPGA_TIMEOUT_CNT 0x1000000 + +/* Set CD ratio */ +static void fpgamgr_set_cd_ratio(unsigned long ratio) +{ + clrsetbits_le32(&fpgamgr_regs->ctrl, + 0x3 << FPGAMGRREGS_CTRL_CDRATIO_LSB, + (ratio & 0x3) << FPGAMGRREGS_CTRL_CDRATIO_LSB); +} + +static int fpgamgr_dclkcnt_set(unsigned long cnt) +{ + unsigned long i; + + /* Clear any existing done status */ + if (readl(&fpgamgr_regs->dclkstat)) + writel(0x1, &fpgamgr_regs->dclkstat); + + /* Write the dclkcnt */ + writel(cnt, &fpgamgr_regs->dclkcnt); + + /* Wait till the dclkcnt done */ + for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { + if (!readl(&fpgamgr_regs->dclkstat)) + continue; + + writel(0x1, &fpgamgr_regs->dclkstat); + return 0; + } + + return -ETIMEDOUT; +} + +/* Check whether FPGA Init_Done signal is high */ +static int is_fpgamgr_initdone_high(void) +{ + unsigned long val; + + val = readl(&fpgamgr_regs->gpio_ext_porta); + return val & FPGAMGRREGS_MON_GPIO_EXT_PORTA_ID_MASK; +} + +/* Get the FPGA mode */ +static int fpgamgr_get_mode(void) +{ + unsigned long val; + + val = readl(&fpgamgr_regs->stat); + return val & FPGAMGRREGS_STAT_MODE_MASK; +} + +/* Check whether FPGA is ready to be accessed */ +static int fpgamgr_test_fpga_ready(void) +{ + /* Check for init done signal */ + if (!is_fpgamgr_initdone_high()) + return 0; + + /* Check again to avoid false glitches */ + if (!is_fpgamgr_initdone_high()) + return 0; + + if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_USERMODE) + return 0; + + return 1; +} + +/* +// Poll until FPGA is ready to be accessed or timeout occurred +static int fpgamgr_poll_fpga_ready(void) +{ + unsigned long i; + + // If FPGA is blank, wait till WD invoke warm reset + for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { + // check for init done signal + if (!is_fpgamgr_initdone_high()) + continue; + // check again to avoid false glitches + if (!is_fpgamgr_initdone_high()) + continue; + return 1; + } + + return 0; +} +*/ + +/* Start the FPGA programming by initialize the FPGA Manager */ +static int fpgamgr_program_init(void) +{ + unsigned long msel, i; + + /* Get the MSEL value */ + msel = readl(&fpgamgr_regs->stat); + msel &= FPGAMGRREGS_STAT_MSEL_MASK; + msel >>= FPGAMGRREGS_STAT_MSEL_LSB; + + /* + * Set the cfg width + * If MSEL[3] = 1, cfg width = 32 bit + */ + if (msel & 0x8) { + setbits_le32(&fpgamgr_regs->ctrl, + FPGAMGRREGS_CTRL_CFGWDTH_MASK); + + /* To determine the CD ratio */ + /* MSEL[1:0] = 0, CD Ratio = 1 */ + if ((msel & 0x3) == 0x0) + fpgamgr_set_cd_ratio(CDRATIO_x1); + /* MSEL[1:0] = 1, CD Ratio = 4 */ + else if ((msel & 0x3) == 0x1) + fpgamgr_set_cd_ratio(CDRATIO_x4); + /* MSEL[1:0] = 2, CD Ratio = 8 */ + else if ((msel & 0x3) == 0x2) + fpgamgr_set_cd_ratio(CDRATIO_x8); + + } + else { /* MSEL[3] = 0 */ + clrbits_le32(&fpgamgr_regs->ctrl, + FPGAMGRREGS_CTRL_CFGWDTH_MASK); + + /* To determine the CD ratio */ + /* MSEL[1:0] = 0, CD Ratio = 1 */ + if ((msel & 0x3) == 0x0) + fpgamgr_set_cd_ratio(CDRATIO_x1); + /* MSEL[1:0] = 1, CD Ratio = 2 */ + else if ((msel & 0x3) == 0x1) + fpgamgr_set_cd_ratio(CDRATIO_x2); + /* MSEL[1:0] = 2, CD Ratio = 4 */ + else if ((msel & 0x3) == 0x2) + fpgamgr_set_cd_ratio(CDRATIO_x4); + } + + /* To enable FPGA Manager configuration */ + clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCE_MASK); + + /* To enable FPGA Manager drive over configuration line */ + setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_EN_MASK); + + /* Put FPGA into reset phase */ + setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCONFIGPULL_MASK); + + /* (1) wait until FPGA enter reset phase */ + for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { + if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_RESETPHASE) + break; + } + + /* If not in reset state, return error */ + if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_RESETPHASE) { + puts("FPGA: Could not reset\n"); + return -1; + } + + /* Release FPGA from reset phase */ + clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCONFIGPULL_MASK); + + /* (2) wait until FPGA enter configuration phase */ + for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { + if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_CFGPHASE) + break; + } + + /* If not in configuration state, return error */ + if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_CFGPHASE) { + puts("FPGA: Could not configure\n"); + return -2; + } + + /* Clear all interrupts in CB Monitor */ + writel(0xFFF, &fpgamgr_regs->gpio_porta_eoi); + + /* Enable AXI configuration */ + setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_AXICFGEN_MASK); + + return 0; +} + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +/* Write the RBF data to FPGA Manager */ +static void fpgamgr_program_write(const void *rbf_data, unsigned long rbf_size) +{ + uint32_t src = (uint32_t)rbf_data; + uint32_t dst = (uint32_t)MAP_ADDR(SOCFPGA_FPGAMGRDATA_ADDRESS); + + /* Number of loops for 32-byte long copying. */ + uint32_t loops32 = rbf_size / 32; + /* Number of loops for 4-byte long copying + trailing bytes */ + uint32_t loops4 = DIV_ROUND_UP(rbf_size % 32, 4); + + __asm volatile( + "1: ldmia %0!,{r0-r7} \n" + " stmia %1!,{r0-r7} \n" + " sub %1, #32 \n" + " subs %2, #1 \n" + " bne 1b \n" + " cmp %3, #0 \n" + " beq 3f \n" + "2: ldr %2, [%0], #4 \n" + " str %2, [%1] \n" + " subs %3, #1 \n" + " bne 2b \n" + "3: nop \n" + : "+r"(src), "+r"(dst), "+r"(loops32), "+r"(loops4) : + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "cc"); +} + +/* Ensure the FPGA entering config done */ +static int fpgamgr_program_poll_cd(void) +{ + const uint32_t mask = FPGAMGRREGS_MON_GPIO_EXT_PORTA_NS_MASK | + FPGAMGRREGS_MON_GPIO_EXT_PORTA_CD_MASK; + unsigned long reg, i; + + /* (3) wait until full config done */ + for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { + reg = readl(&fpgamgr_regs->gpio_ext_porta); + + /* Config error */ + if (!(reg & mask)) { + printf("FPGA: Configuration error.\n"); + return -3; + } + + /* Config done without error */ + if (reg & mask) + break; + } + + /* Timeout happened, return error */ + if (i == FPGA_TIMEOUT_CNT) { + printf("FPGA: Timeout waiting for program.\n"); + return -4; + } + + /* Disable AXI configuration */ + clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_AXICFGEN_MASK); + + return 0; +} + +/* Ensure the FPGA entering init phase */ +static int fpgamgr_program_poll_initphase(void) +{ + unsigned long i; + + /* Additional clocks for the CB to enter initialization phase */ + if (fpgamgr_dclkcnt_set(0x4)) + return -5; + + /* (4) wait until FPGA enter init phase or user mode */ + for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { + if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_INITPHASE) + break; + if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_USERMODE) + break; + } + + /* If not in configuration state, return error */ + if (i == FPGA_TIMEOUT_CNT) + return -6; + + return 0; +} + +/* Ensure the FPGA entering user mode */ +static int fpgamgr_program_poll_usermode(void) +{ + unsigned long i; + + /* Additional clocks for the CB to exit initialization phase */ + if (fpgamgr_dclkcnt_set(0x5000)) + return -7; + + /* (5) wait until FPGA enter user mode */ + for (i = 0; i < FPGA_TIMEOUT_CNT; i++) { + if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_USERMODE) + break; + } + /* If not in configuration state, return error */ + if (i == FPGA_TIMEOUT_CNT) + return -8; + + /* To release FPGA Manager drive over configuration line */ + clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_EN_MASK); + + return 0; +} + +/* +* FPGA Manager to program the FPGA. This is the interface used by FPGA driver. +* Return 0 for sucess, non-zero for error. +*/ +static int socfpga_load(const void *rbf_data, size_t rbf_size) +{ + unsigned long status; + + if ((uint32_t)rbf_data & 0x3) { + printf("FPGA: Unaligned data, realign to 32bit boundary.\n"); + return -EINVAL; + } + + /* Initialize the FPGA Manager */ + status = fpgamgr_program_init(); + if (status) + return status; + + /* Write the RBF data to FPGA Manager */ + fpgamgr_program_write(rbf_data, rbf_size); + + /* Ensure the FPGA entering config done */ + status = fpgamgr_program_poll_cd(); + if (status) + return status; + + /* Ensure the FPGA entering init phase */ + status = fpgamgr_program_poll_initphase(); + if (status) + return status; + + /* Ensure the FPGA entering user mode */ + return fpgamgr_program_poll_usermode(); +} + +static void do_bridge(uint32_t enable) +{ + if (enable) + { + writel(0x00003FFF, (void*)(SOCFPGA_SDR_ADDRESS + 0x5080)); + writel(0x00000000, &reset_regs->brg_mod_reset); + writel(0x00000019, &nic301_regs->remap); + } + else + { + writel(0, &sysmgr_regs->fpgaintfgrp_module); + writel(0, (void*)(SOCFPGA_SDR_ADDRESS + 0x5080)); + writel(7, &reset_regs->brg_mod_reset); + writel(1, &nic301_regs->remap); + } +} + +static int make_env(const char *name, const char *cfg) +{ + if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) return -1; + + void* buf = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x1FFFF000); + if (buf == (void *)-1) + { + printf("Unable to mmap(/dev/mem)\n"); + close(fd); + return -1; + } + + volatile char* str = (volatile char*)buf; + memset((void*)str, 0, 0x1000); + + *str++ = 0x21; + *str++ = 0x43; + *str++ = 0x65; + *str++ = 0x87; + *str++ = 'c'; + *str++ = 'o'; + *str++ = 'r'; + *str++ = 'e'; + *str++ = '='; + *str++ = '"'; + + for (uint32_t i = 0; i < strlen(name); i++) + { + *str++ = name[i]; + } + + *str++ = '"'; + *str++ = '\n'; + FileLoad(cfg, (void*)str, 0); + munmap(buf, 0x1000); + return 0; +} + +int fpga_load_rbf(const char *name, const char *cfg) +{ + static char path[1024]; + int ret = 0; + + if(cfg) + { + make_env(name, cfg); + do_bridge(0); + reboot(0); + } + + printf("Loading RBF: %s\n", name); + sprintf(path, "%s/%s", !strcasecmp(name, "menu.rbf") ? getStorageDir(0) : getRootDir(), name); + + int rbf = open(path, O_RDONLY); + if (rbf < 0) + { + printf("Couldn't open file %s\n", path); + return -1; + } + else + { + struct stat64 st; + if (fstat64(rbf, &st)<0) + { + printf("Couldn't get info of file %s\n", path); + ret = -1; + } + else + { + printf("Bitstream size: %lld bytes\n", st.st_size); + + void *buf = malloc(st.st_size); + if (!buf) + { + printf("Couldn't allocate %llu bytes.\n", st.st_size); + ret = -1; + } + else + { + if (read(rbf, buf, st.st_size)> 8) != 0x5CA623) return -1; + return coretype & 0xFF; +} + +int fpga_get_fio_size() +{ + return (fpga_gpi_read()>>16) & 1; +} + +int fpga_get_io_version() +{ + return (fpga_gpi_read() >> 18) & 3; +} + +void fpga_set_led(uint32_t on) +{ + uint32_t gpo = fpga_gpo_read(); + fpga_gpo_write(on ? gpo | 0x20000000 : gpo & ~0x20000000); +} + +int fpga_get_buttons() +{ + fpga_gpo_write(fpga_gpo_read() | 0x80000000); + int gpi = fpga_gpi_read(); + if (gpi < 0) gpi = 0; // FPGA is not in user mode. Ignore the data; + return (gpi >> 29) & 3; +} + +void reboot(int cold) +{ + sync(); + fpga_core_reset(1); + + usleep(500000); + + writel(cold ? 0 : 0x1, &reset_regs->tstscratch); + writel(2, &reset_regs->ctrl); + while (1); +} + +char *getappname() +{ + static char dest[PATH_MAX]; + memset(dest, 0, sizeof(dest)); + + char path[64]; + sprintf(path, "/proc/%d/exe", getpid()); + readlink(path, dest, PATH_MAX); + + return dest; +} + +void app_restart(const char *path) +{ + sync(); + fpga_core_reset(1); + + char *appname = getappname(); + printf("restarting the %s\n", appname); + execl(appname, appname, path, NULL); + + printf("Something went wrong. Rebooting...\n"); + reboot(0); +} + +void fpga_core_reset(int reset) +{ + uint32_t gpo = fpga_gpo_read() & ~0xC0000000; + fpga_gpo_write(reset ? gpo | 0x40000000 : gpo | 0x80000000); +} + +int is_fpga_ready(int quick) +{ + if (quick) + { + return (fpga_gpi_read() >= 0); + } + + return fpgamgr_test_fpga_ready(); +} diff --git a/fpga_io.h b/fpga_io.h index dfc3cdb..36127c2 100644 --- a/fpga_io.h +++ b/fpga_io.h @@ -1,37 +1,37 @@ - -#include - -#ifndef FPGAIO_H -#define FPGAIO_H - -#define DISKLED_ON fpga_set_led(1) -#define DISKLED_OFF fpga_set_led(0) - -#define BUTTON_OSD 1 -#define BUTTON_USR 2 - -int fpga_io_init(); - -void fpga_gpo_write(uint32_t value); -uint32_t fpga_gpo_read(); -int fpga_gpi_read(); - -void fpga_set_led(uint32_t on); -int fpga_get_buttons(); - -void fpga_core_reset(int reset); -void fpga_core_write(uint32_t offset, uint32_t value); -uint32_t fpga_core_read(uint32_t offset); -int fpga_core_id(); -int is_fpga_ready(int quick); - -int fpga_get_fio_size(); -int fpga_get_io_version(); - -int fpga_load_rbf(const char *name, const char *cfg = NULL); - -void reboot(int cold); -void app_restart(const char *path); -char *getappname(); - -#endif + +#include + +#ifndef FPGAIO_H +#define FPGAIO_H + +#define DISKLED_ON fpga_set_led(1) +#define DISKLED_OFF fpga_set_led(0) + +#define BUTTON_OSD 1 +#define BUTTON_USR 2 + +int fpga_io_init(); + +void fpga_gpo_write(uint32_t value); +uint32_t fpga_gpo_read(); +int fpga_gpi_read(); + +void fpga_set_led(uint32_t on); +int fpga_get_buttons(); + +void fpga_core_reset(int reset); +void fpga_core_write(uint32_t offset, uint32_t value); +uint32_t fpga_core_read(uint32_t offset); +int fpga_core_id(); +int is_fpga_ready(int quick); + +int fpga_get_fio_size(); +int fpga_get_io_version(); + +int fpga_load_rbf(const char *name, const char *cfg = NULL); + +void reboot(int cold); +void app_restart(const char *path); +char *getappname(); + +#endif diff --git a/hardware.cpp b/hardware.cpp index 8aadfe6..2138994 100644 --- a/hardware.cpp +++ b/hardware.cpp @@ -1,77 +1,77 @@ -/* -Copyright 2008, 2009 Jakub Bednarski - -This file is part of Minimig - -Minimig is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -Minimig 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include -#include "hardware.h" -#include "user_io.h" - -uint8_t rstval = 0; - -void hexdump(void *data, uint16_t size, uint16_t offset) -{ - uint8_t i, b2c; - uint16_t n = 0; - char *ptr = (char*)data; - - if (!size) return; - - while (size>0) { - printf("%04x: ", n + offset); - - b2c = (size>16) ? 16 : size; - for (i = 0; i= time; -} - -void WaitTimer(unsigned long time) -{ - time = GetTimer(time); - while (!CheckTimer(time)); -} +/* +Copyright 2008, 2009 Jakub Bednarski + +This file is part of Minimig + +Minimig is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Minimig 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include "hardware.h" +#include "user_io.h" + +uint8_t rstval = 0; + +void hexdump(void *data, uint16_t size, uint16_t offset) +{ + uint8_t i, b2c; + uint16_t n = 0; + char *ptr = (char*)data; + + if (!size) return; + + while (size>0) { + printf("%04x: ", n + offset); + + b2c = (size>16) ? 16 : size; + for (i = 0; i= time; +} + +void WaitTimer(unsigned long time) +{ + time = GetTimer(time); + while (!CheckTimer(time)); +} diff --git a/hardware.h b/hardware.h index 7287e43..474f32b 100644 --- a/hardware.h +++ b/hardware.h @@ -1,19 +1,19 @@ -#ifndef HARDWARE_H -#define HARDWARE_H - -#include -#include - -unsigned long GetTimer(unsigned long offset); -unsigned long CheckTimer(unsigned long t); -void WaitTimer(unsigned long time); - -void hexdump(void *data, uint16_t size, uint16_t offset = 0); - -// minimig reset stuff -#define SPI_RST_USR 0x1 -#define SPI_RST_CPU 0x2 -#define SPI_CPU_HLT 0x4 -extern uint8_t rstval; - -#endif // HARDWARE_H +#ifndef HARDWARE_H +#define HARDWARE_H + +#include +#include + +unsigned long GetTimer(unsigned long offset); +unsigned long CheckTimer(unsigned long t); +void WaitTimer(unsigned long time); + +void hexdump(void *data, uint16_t size, uint16_t offset = 0); + +// minimig reset stuff +#define SPI_RST_USR 0x1 +#define SPI_RST_CPU 0x2 +#define SPI_CPU_HLT 0x4 +extern uint8_t rstval; + +#endif // HARDWARE_H diff --git a/input.cpp b/input.cpp index 6436948..682adec 100644 --- a/input.cpp +++ b/input.cpp @@ -1,2501 +1,2501 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "input.h" -#include "user_io.h" -#include "menu.h" -#include "hardware.h" -#include "cfg.h" -#include "fpga_io.h" -#include "osd.h" -#include "errno.h" - -#define NUMDEV 10 -#define NUMPLAYERS 6 - -static int ev2amiga[] = -{ - NONE, //0 KEY_RESERVED - 0x45, //1 KEY_ESC - 0x01, //2 KEY_1 - 0x02, //3 KEY_2 - 0x03, //4 KEY_3 - 0x04, //5 KEY_4 - 0x05, //6 KEY_5 - 0x06, //7 KEY_6 - 0x07, //8 KEY_7 - 0x08, //9 KEY_8 - 0x09, //10 KEY_9 - 0x0a, //11 KEY_0 - 0x0b, //12 KEY_MINUS - 0x0c, //13 KEY_EQUAL - 0x41, //14 KEY_BACKSPACE - 0x42, //15 KEY_TAB - 0x10, //16 KEY_Q - 0x11, //17 KEY_W - 0x12, //18 KEY_E - 0x13, //19 KEY_R - 0x14, //20 KEY_T - 0x15, //21 KEY_Y - 0x16, //22 KEY_U - 0x17, //23 KEY_I - 0x18, //24 KEY_O - 0x19, //25 KEY_P - 0x1a, //26 KEY_LEFTBRACE - 0x1b, //27 KEY_RIGHTBRACE - 0x44, //28 KEY_ENTER - 0x63, //29 KEY_LEFTCTRL - 0x20, //30 KEY_A - 0x21, //31 KEY_S - 0x22, //32 KEY_D - 0x23, //33 KEY_F - 0x24, //34 KEY_G - 0x25, //35 KEY_H - 0x26, //36 KEY_J - 0x27, //37 KEY_K - 0x28, //38 KEY_L - 0x29, //39 KEY_SEMICOLON - 0x2a, //40 KEY_APOSTROPHE - 0x00, //41 KEY_GRAVE - 0x60, //42 KEY_LEFTSHIFT - 0x0d, //43 KEY_BACKSLASH - 0x31, //44 KEY_Z - 0x32, //45 KEY_X - 0x33, //46 KEY_C - 0x34, //47 KEY_V - 0x35, //48 KEY_B - 0x36, //49 KEY_N - 0x37, //50 KEY_M - 0x38, //51 KEY_COMMA - 0x39, //52 KEY_DOT - 0x3a, //53 KEY_SLASH - 0x61, //54 KEY_RIGHTSHIFT - 0x5d, //55 KEY_KPASTERISK - 0x64, //56 KEY_LEFTALT - 0x40, //57 KEY_SPACE - 0x62 | CAPS_TOGGLE, //58 KEY_CAPSLOCK - 0x50, //59 KEY_F1 - 0x51, //60 KEY_F2 - 0x52, //61 KEY_F3 - 0x53, //62 KEY_F4 - 0x54, //63 KEY_F5 - 0x55, //64 KEY_F6 - 0x56, //65 KEY_F7 - 0x57, //66 KEY_F8 - 0x58, //67 KEY_F9 - 0x59, //68 KEY_F10 - NONE, //69 KEY_NUMLOCK - NONE, //70 KEY_SCROLLLOCK - 0x3d, //71 KEY_KP7 - 0x3e, //72 KEY_KP8 - 0x3f, //73 KEY_KP9 - 0x4a, //74 KEY_KPMINUS - 0x2d, //75 KEY_KP4 - 0x2e, //76 KEY_KP5 - 0x2f, //77 KEY_KP6 - 0x5e, //78 KEY_KPPLUS - 0x1d, //79 KEY_KP1 - 0x1e, //80 KEY_KP2 - 0x1f, //81 KEY_KP3 - 0x0f, //82 KEY_KP0 - 0x3c, //83 KEY_KPDOT - NONE, //84 ??? - NONE, //85 KEY_ZENKAKU - NONE, //86 KEY_102ND - 0x5f, //87 KEY_F11 - NONE, //88 KEY_F12 - NONE, //89 KEY_RO - NONE, //90 KEY_KATAKANA - NONE, //91 KEY_HIRAGANA - NONE, //92 KEY_HENKAN - NONE, //93 KEY_KATAKANA - NONE, //94 KEY_MUHENKAN - NONE, //95 KEY_KPJPCOMMA - 0x43, //96 KEY_KPENTER - 0x63, //97 KEY_RIGHTCTRL - 0x5c, //98 KEY_KPSLASH - NONE, //99 KEY_SYSRQ - 0x65, //100 KEY_RIGHTALT - NONE, //101 KEY_LINEFEED - 0x6a, //102 KEY_HOME - 0x4c, //103 KEY_UP - NONE, //104 KEY_PAGEUP - 0x4f, //105 KEY_LEFT - 0x4e, //106 KEY_RIGHT - NONE, //107 KEY_END - 0x4d, //108 KEY_DOWN - NONE, //109 KEY_PAGEDOWN - 0x0d, //110 KEY_INSERT - 0x46, //111 KEY_DELETE - NONE, //112 KEY_MACRO - NONE, //113 KEY_MUTE - NONE, //114 KEY_VOLUMEDOWN - NONE, //115 KEY_VOLUMEUP - NONE, //116 KEY_POWER - NONE, //117 KEY_KPEQUAL - NONE, //118 KEY_KPPLUSMINUS - NONE, //119 KEY_PAUSE - NONE, //120 KEY_SCALE - NONE, //121 KEY_KPCOMMA - NONE, //122 KEY_HANGEUL - NONE, //123 KEY_HANJA - NONE, //124 KEY_YEN - 0x66, //125 KEY_LEFTMETA - 0x67, //126 KEY_RIGHTMETA - NONE, //127 KEY_COMPOSE - NONE, //128 KEY_STOP - NONE, //129 KEY_AGAIN - NONE, //130 KEY_PROPS - NONE, //131 KEY_UNDO - NONE, //132 KEY_FRONT - NONE, //133 KEY_COPY - NONE, //134 KEY_OPEN - NONE, //135 KEY_PASTE - NONE, //136 KEY_FIND - NONE, //137 KEY_CUT - NONE, //138 KEY_HELP - NONE, //139 KEY_MENU - NONE, //140 KEY_CALC - NONE, //141 KEY_SETUP - NONE, //142 KEY_SLEEP - NONE, //143 KEY_WAKEUP - NONE, //144 KEY_FILE - NONE, //145 KEY_SENDFILE - NONE, //146 KEY_DELETEFILE - NONE, //147 KEY_XFER - NONE, //148 KEY_PROG1 - NONE, //149 KEY_PROG2 - NONE, //150 KEY_WWW - NONE, //151 KEY_MSDOS - NONE, //152 KEY_SCREENLOCK - NONE, //153 KEY_DIRECTION - NONE, //154 KEY_CYCLEWINDOWS - NONE, //155 KEY_MAIL - NONE, //156 KEY_BOOKMARKS - NONE, //157 KEY_COMPUTER - NONE, //158 KEY_BACK - NONE, //159 KEY_FORWARD - NONE, //160 KEY_CLOSECD - NONE, //161 KEY_EJECTCD - NONE, //162 KEY_EJECTCLOSECD - NONE, //163 KEY_NEXTSONG - NONE, //164 KEY_PLAYPAUSE - NONE, //165 KEY_PREVIOUSSONG - NONE, //166 KEY_STOPCD - NONE, //167 KEY_RECORD - NONE, //168 KEY_REWIND - NONE, //169 KEY_PHONE - NONE, //170 KEY_ISO - NONE, //171 KEY_CONFIG - NONE, //172 KEY_HOMEPAGE - NONE, //173 KEY_REFRESH - NONE, //174 KEY_EXIT - NONE, //175 KEY_MOVE - NONE, //176 KEY_EDIT - NONE, //177 KEY_SCROLLUP - NONE, //178 KEY_SCROLLDOWN - NONE, //179 KEY_KPLEFTPAREN - NONE, //180 KEY_KPRIGHTPAREN - NONE, //181 KEY_NEW - NONE, //182 KEY_REDO - 0x5a, //183 KEY_F13 - 0x5b, //184 KEY_F14 - NONE, //185 KEY_F15 - 0x5f, //186 KEY_F16 - NONE, //187 KEY_F17 - NONE, //188 KEY_F18 - NONE, //189 KEY_F19 - NONE, //190 KEY_F20 - NONE, //191 KEY_F21 - NONE, //192 KEY_F22 - NONE, //193 KEY_F23 - 0x63, //194 KEY_F24 - NONE, //195 ??? - NONE, //196 ??? - NONE, //197 ??? - NONE, //198 ??? - NONE, //199 ??? - NONE, //200 KEY_PLAYCD - NONE, //201 KEY_PAUSECD - NONE, //202 KEY_PROG3 - NONE, //203 KEY_PROG4 - NONE, //204 KEY_DASHBOARD - NONE, //205 KEY_SUSPEND - NONE, //206 KEY_CLOSE - NONE, //207 KEY_PLAY - NONE, //208 KEY_FASTFORWARD - NONE, //209 KEY_BASSBOOST - NONE, //210 KEY_PRINT - NONE, //211 KEY_HP - NONE, //212 KEY_CAMERA - NONE, //213 KEY_SOUND - NONE, //214 KEY_QUESTION - NONE, //215 KEY_EMAIL - NONE, //216 KEY_CHAT - NONE, //217 KEY_SEARCH - NONE, //218 KEY_CONNECT - NONE, //219 KEY_FINANCE - NONE, //220 KEY_SPORT - NONE, //221 KEY_SHOP - NONE, //222 KEY_ALTERASE - NONE, //223 KEY_CANCEL - NONE, //224 KEY_BRIGHT_DOWN - NONE, //225 KEY_BRIGHT_UP - NONE, //226 KEY_MEDIA - NONE, //227 KEY_SWITCHVIDEO - NONE, //228 KEY_DILLUMTOGGLE - NONE, //229 KEY_DILLUMDOWN - NONE, //230 KEY_DILLUMUP - NONE, //231 KEY_SEND - NONE, //232 KEY_REPLY - NONE, //233 KEY_FORWARDMAIL - NONE, //234 KEY_SAVE - NONE, //235 KEY_DOCUMENTS - NONE, //236 KEY_BATTERY - NONE, //237 KEY_BLUETOOTH - NONE, //238 KEY_WLAN - NONE, //239 KEY_UWB - NONE, //240 KEY_UNKNOWN - NONE, //241 KEY_VIDEO_NEXT - NONE, //242 KEY_VIDEO_PREV - NONE, //243 KEY_BRIGHT_CYCLE - NONE, //244 KEY_BRIGHT_AUTO - NONE, //245 KEY_DISPLAY_OFF - NONE, //246 KEY_WWAN - NONE, //247 KEY_RFKILL - NONE, //248 KEY_MICMUTE - NONE, //249 ??? - NONE, //250 ??? - NONE, //251 ??? - NONE, //252 ??? - NONE, //253 ??? - NONE, //254 ??? - NONE //255 ??? -}; - - -static const int ev2ps2[] = -{ - NONE, //0 KEY_RESERVED - 0x76, //1 KEY_ESC - 0x16, //2 KEY_1 - 0x1e, //3 KEY_2 - 0x26, //4 KEY_3 - 0x25, //5 KEY_4 - 0x2e, //6 KEY_5 - 0x36, //7 KEY_6 - 0x3d, //8 KEY_7 - 0x3e, //9 KEY_8 - 0x46, //10 KEY_9 - 0x45, //11 KEY_0 - 0x4e, //12 KEY_MINUS - 0x55, //13 KEY_EQUAL - 0x66, //14 KEY_BACKSPACE - 0x0d, //15 KEY_TAB - 0x15, //16 KEY_Q - 0x1d, //17 KEY_W - 0x24, //18 KEY_E - 0x2d, //19 KEY_R - 0x2c, //20 KEY_T - 0x35, //21 KEY_Y - 0x3c, //22 KEY_U - 0x43, //23 KEY_I - 0x44, //24 KEY_O - 0x4d, //25 KEY_P - 0x54, //26 KEY_LEFTBRACE - 0x5b, //27 KEY_RIGHTBRACE - 0x5a, //28 KEY_ENTER - LCTRL | 0x14, //29 KEY_LEFTCTRL - 0x1c, //30 KEY_A - 0x1b, //31 KEY_S - 0x23, //32 KEY_D - 0x2b, //33 KEY_F - 0x34, //34 KEY_G - 0x33, //35 KEY_H - 0x3b, //36 KEY_J - 0x42, //37 KEY_K - 0x4b, //38 KEY_L - 0x4c, //39 KEY_SEMICOLON - 0x52, //40 KEY_APOSTROPHE - 0x0e, //41 KEY_GRAVE - LSHIFT | 0x12, //42 KEY_LEFTSHIFT - 0x5d, //43 KEY_BACKSLASH - 0x1a, //44 KEY_Z - 0x22, //45 KEY_X - 0x21, //46 KEY_C - 0x2a, //47 KEY_V - 0x32, //48 KEY_B - 0x31, //49 KEY_N - 0x3a, //50 KEY_M - 0x41, //51 KEY_COMMA - 0x49, //52 KEY_DOT - 0x4a, //53 KEY_SLASH - RSHIFT | 0x59, //54 KEY_RIGHTSHIFT - 0x7c, //55 KEY_KPASTERISK - LALT | 0x11, //56 KEY_LEFTALT - 0x29, //57 KEY_SPACE - 0x58, //58 KEY_CAPSLOCK - 0x05, //59 KEY_F1 - 0x06, //60 KEY_F2 - 0x04, //61 KEY_F3 - 0x0c, //62 KEY_F4 - 0x03, //63 KEY_F5 - 0x0b, //64 KEY_F6 - 0x83, //65 KEY_F7 - 0x0a, //66 KEY_F8 - 0x01, //67 KEY_F9 - 0x09, //68 KEY_F10 - EMU_SWITCH_2 | 0x77, //69 KEY_NUMLOCK - EMU_SWITCH_1 | 0x7E, //70 KEY_SCROLLLOCK - 0x6c, //71 KEY_KP7 - 0x75, //72 KEY_KP8 - 0x7d, //73 KEY_KP9 - 0x7b, //74 KEY_KPMINUS - 0x6b, //75 KEY_KP4 - 0x73, //76 KEY_KP5 - 0x74, //77 KEY_KP6 - 0x79, //78 KEY_KPPLUS - 0x69, //79 KEY_KP1 - 0x72, //80 KEY_KP2 - 0x7a, //81 KEY_KP3 - 0x70, //82 KEY_KP0 - 0x71, //83 KEY_KPDOT - NONE, //84 ??? - NONE, //85 KEY_ZENKAKU - NONE, //86 KEY_102ND - 0x78, //87 KEY_F11 - 0x07, //88 KEY_F12 - NONE, //89 KEY_RO - NONE, //90 KEY_KATAKANA - NONE, //91 KEY_HIRAGANA - NONE, //92 KEY_HENKAN - NONE, //93 KEY_KATAKANA - NONE, //94 KEY_MUHENKAN - NONE, //95 KEY_KPJPCOMMA - EXT | 0x5a, //96 KEY_KPENTER - RCTRL | EXT | 0x14, //97 KEY_RIGHTCTRL - EXT | 0x4a, //98 KEY_KPSLASH - 0xE2, //99 KEY_SYSRQ - RALT | EXT | 0x11, //100 KEY_RIGHTALT - NONE, //101 KEY_LINEFEED - EXT | 0x6c, //102 KEY_HOME - EXT | 0x75, //103 KEY_UP - EXT | 0x7d, //104 KEY_PAGEUP - EXT | 0x6b, //105 KEY_LEFT - EXT | 0x74, //106 KEY_RIGHT - EXT | 0x69, //107 KEY_END - EXT | 0x72, //108 KEY_DOWN - EXT | 0x7a, //109 KEY_PAGEDOWN - EXT | 0x70, //110 KEY_INSERT - EXT | 0x71, //111 KEY_DELETE - NONE, //112 KEY_MACRO - NONE, //113 KEY_MUTE - NONE, //114 KEY_VOLUMEDOWN - NONE, //115 KEY_VOLUMEUP - NONE, //116 KEY_POWER - NONE, //117 KEY_KPEQUAL - NONE, //118 KEY_KPPLUSMINUS - 0xE1, //119 KEY_PAUSE - NONE, //120 KEY_SCALE - NONE, //121 KEY_KPCOMMA - NONE, //122 KEY_HANGEUL - NONE, //123 KEY_HANJA - NONE, //124 KEY_YEN - LGUI | EXT | 0x1f, //125 KEY_LEFTMETA - RGUI | EXT | 0x27, //126 KEY_RIGHTMETA - NONE, //127 KEY_COMPOSE - NONE, //128 KEY_STOP - NONE, //129 KEY_AGAIN - NONE, //130 KEY_PROPS - NONE, //131 KEY_UNDO - NONE, //132 KEY_FRONT - NONE, //133 KEY_COPY - NONE, //134 KEY_OPEN - NONE, //135 KEY_PASTE - NONE, //136 KEY_FIND - NONE, //137 KEY_CUT - NONE, //138 KEY_HELP - NONE, //139 KEY_MENU - NONE, //140 KEY_CALC - NONE, //141 KEY_SETUP - NONE, //142 KEY_SLEEP - NONE, //143 KEY_WAKEUP - NONE, //144 KEY_FILE - NONE, //145 KEY_SENDFILE - NONE, //146 KEY_DELETEFILE - NONE, //147 KEY_XFER - NONE, //148 KEY_PROG1 - NONE, //149 KEY_PROG2 - NONE, //150 KEY_WWW - NONE, //151 KEY_MSDOS - NONE, //152 KEY_SCREENLOCK - NONE, //153 KEY_DIRECTION - NONE, //154 KEY_CYCLEWINDOWS - NONE, //155 KEY_MAIL - NONE, //156 KEY_BOOKMARKS - NONE, //157 KEY_COMPUTER - NONE, //158 KEY_BACK - NONE, //159 KEY_FORWARD - NONE, //160 KEY_CLOSECD - NONE, //161 KEY_EJECTCD - NONE, //162 KEY_EJECTCLOSECD - NONE, //163 KEY_NEXTSONG - NONE, //164 KEY_PLAYPAUSE - NONE, //165 KEY_PREVIOUSSONG - NONE, //166 KEY_STOPCD - NONE, //167 KEY_RECORD - NONE, //168 KEY_REWIND - NONE, //169 KEY_PHONE - NONE, //170 KEY_ISO - NONE, //171 KEY_CONFIG - NONE, //172 KEY_HOMEPAGE - NONE, //173 KEY_REFRESH - NONE, //174 KEY_EXIT - NONE, //175 KEY_MOVE - NONE, //176 KEY_EDIT - NONE, //177 KEY_SCROLLUP - NONE, //178 KEY_SCROLLDOWN - NONE, //179 KEY_KPLEFTPAREN - NONE, //180 KEY_KPRIGHTPAREN - NONE, //181 KEY_NEW - NONE, //182 KEY_REDO - NONE, //183 KEY_F13 - NONE, //184 KEY_F14 - NONE, //185 KEY_F15 - NONE, //186 KEY_F16 - EMU_SWITCH_1 | 1, //187 KEY_F17 - EMU_SWITCH_1 | 2, //188 KEY_F18 - EMU_SWITCH_1 | 3, //189 KEY_F19 - EMU_SWITCH_1 | 4, //190 KEY_F20 - NONE, //191 KEY_F21 - NONE, //192 KEY_F22 - NONE, //193 KEY_F23 - 0x5D, //194 U-mlaut on DE mapped to backslash - NONE, //195 ??? - NONE, //196 ??? - NONE, //197 ??? - NONE, //198 ??? - NONE, //199 ??? - NONE, //200 KEY_PLAYCD - NONE, //201 KEY_PAUSECD - NONE, //202 KEY_PROG3 - NONE, //203 KEY_PROG4 - NONE, //204 KEY_DASHBOARD - NONE, //205 KEY_SUSPEND - NONE, //206 KEY_CLOSE - NONE, //207 KEY_PLAY - NONE, //208 KEY_FASTFORWARD - NONE, //209 KEY_BASSBOOST - NONE, //210 KEY_PRINT - NONE, //211 KEY_HP - NONE, //212 KEY_CAMERA - NONE, //213 KEY_SOUND - NONE, //214 KEY_QUESTION - NONE, //215 KEY_EMAIL - NONE, //216 KEY_CHAT - NONE, //217 KEY_SEARCH - NONE, //218 KEY_CONNECT - NONE, //219 KEY_FINANCE - NONE, //220 KEY_SPORT - NONE, //221 KEY_SHOP - NONE, //222 KEY_ALTERASE - NONE, //223 KEY_CANCEL - NONE, //224 KEY_BRIGHT_DOWN - NONE, //225 KEY_BRIGHT_UP - NONE, //226 KEY_MEDIA - NONE, //227 KEY_SWITCHVIDEO - NONE, //228 KEY_DILLUMTOGGLE - NONE, //229 KEY_DILLUMDOWN - NONE, //230 KEY_DILLUMUP - NONE, //231 KEY_SEND - NONE, //232 KEY_REPLY - NONE, //233 KEY_FORWARDMAIL - NONE, //234 KEY_SAVE - NONE, //235 KEY_DOCUMENTS - NONE, //236 KEY_BATTERY - NONE, //237 KEY_BLUETOOTH - NONE, //238 KEY_WLAN - NONE, //239 KEY_UWB - NONE, //240 KEY_UNKNOWN - NONE, //241 KEY_VIDEO_NEXT - NONE, //242 KEY_VIDEO_PREV - NONE, //243 KEY_BRIGHT_CYCLE - NONE, //244 KEY_BRIGHT_AUTO - NONE, //245 KEY_DISPLAY_OFF - NONE, //246 KEY_WWAN - NONE, //247 KEY_RFKILL - NONE, //248 KEY_MICMUTE - NONE, //249 ??? - NONE, //250 ??? - NONE, //251 ??? - NONE, //252 ??? - NONE, //253 ??? - NONE, //254 ??? - NONE //255 ??? -}; - -static int ev2archie[] = -{ - NONE, //0 KEY_RESERVED - 0x00, //1 KEY_ESC - 0x11, //2 KEY_1 - 0x12, //3 KEY_2 - 0x13, //4 KEY_3 - 0x14, //5 KEY_4 - 0x15, //6 KEY_5 - 0x16, //7 KEY_6 - 0x17, //8 KEY_7 - 0x18, //9 KEY_8 - 0x19, //10 KEY_9 - 0x1a, //11 KEY_0 - 0x1b, //12 KEY_MINUS - 0x1c, //13 KEY_EQUAL - 0x1e, //14 KEY_BACKSPACE - 0x26, //15 KEY_TAB - 0x27, //16 KEY_Q - 0x28, //17 KEY_W - 0x29, //18 KEY_E - 0x2a, //19 KEY_R - 0x2b, //20 KEY_T - 0x2c, //21 KEY_Y - 0x2d, //22 KEY_U - 0x2e, //23 KEY_I - 0x2f, //24 KEY_O - 0x30, //25 KEY_P - 0x31, //26 KEY_LEFTBRACE - 0x32, //27 KEY_RIGHTBRACE - 0x47, //28 KEY_ENTER - 0x3b, //29 KEY_LEFTCTRL - 0x3c, //30 KEY_A - 0x3d, //31 KEY_S - 0x3e, //32 KEY_D - 0x3f, //33 KEY_F - 0x40, //34 KEY_G - 0x41, //35 KEY_H - 0x42, //36 KEY_J - 0x43, //37 KEY_K - 0x44, //38 KEY_L - 0x45, //39 KEY_SEMICOLON - 0x46, //40 KEY_APOSTROPHE - 0x10, //41 KEY_GRAVE - 0x4c, //42 KEY_LEFTSHIFT - 0x33, //43 KEY_BACKSLASH - 0x4e, //44 KEY_Z - 0x4f, //45 KEY_X - 0x50, //46 KEY_C - 0x51, //47 KEY_V - 0x52, //48 KEY_B - 0x53, //49 KEY_N - 0x54, //50 KEY_M - 0x55, //51 KEY_COMMA - 0x56, //52 KEY_DOT - 0x57, //53 KEY_SLASH - 0x58, //54 KEY_RIGHTSHIFT - 0x24, //55 KEY_KPASTERISK - 0x5e, //56 KEY_LEFTALT - 0x5f, //57 KEY_SPACE - 0x5d, //58 KEY_CAPSLOCK - 0x01, //59 KEY_F1 - 0x02, //60 KEY_F2 - 0x03, //61 KEY_F3 - 0x04, //62 KEY_F4 - 0x05, //63 KEY_F5 - 0x06, //64 KEY_F6 - 0x07, //65 KEY_F7 - 0x08, //66 KEY_F8 - 0x09, //67 KEY_F9 - 0x0a, //68 KEY_F10 - 0x22, //69 KEY_NUMLOCK - NONE, //70 KEY_SCROLLLOCK - 0x37, //71 KEY_KP7 - 0x38, //72 KEY_KP8 - 0x39, //73 KEY_KP9 - 0x3a, //74 KEY_KPMINUS - 0x48, //75 KEY_KP4 - 0x49, //76 KEY_KP5 - 0x4a, //77 KEY_KP6 - 0x4b, //78 KEY_KPPLUS - 0x5a, //79 KEY_KP1 - 0x5b, //80 KEY_KP2 - 0x5c, //81 KEY_KP3 - 0x65, //82 KEY_KP0 - 0x66, //83 KEY_KPDOT - NONE, //84 ??? - NONE, //85 KEY_ZENKAKU - NONE, //86 KEY_102ND - 0x0b, //87 KEY_F11 - 0x0c, //88 KEY_F12 - NONE, //89 KEY_RO - NONE, //90 KEY_KATAKANA - NONE, //91 KEY_HIRAGANA - NONE, //92 KEY_HENKAN - NONE, //93 KEY_KATAKANA - NONE, //94 KEY_MUHENKAN - NONE, //95 KEY_KPJPCOMMA - 0x67, //96 KEY_KPENTER - 0x61, //97 KEY_RIGHTCTRL - 0x23, //98 KEY_KPSLASH - 0x0D, //99 KEY_SYSRQ - 0x60, //100 KEY_RIGHTALT - NONE, //101 KEY_LINEFEED - 0x20, //102 KEY_HOME - 0x59, //103 KEY_UP - 0x21, //104 KEY_PAGEUP - 0x62, //105 KEY_LEFT - 0x64, //106 KEY_RIGHT - 0x35, //107 KEY_END - 0x63, //108 KEY_DOWN - 0x36, //109 KEY_PAGEDOWN - 0x1f, //110 KEY_INSERT - 0x34, //111 KEY_DELETE - NONE, //112 KEY_MACRO - NONE, //113 KEY_MUTE - NONE, //114 KEY_VOLUMEDOWN - NONE, //115 KEY_VOLUMEUP - NONE, //116 KEY_POWER - NONE, //117 KEY_KPEQUAL - NONE, //118 KEY_KPPLUSMINUS - 0x0f, //119 KEY_PAUSE - NONE, //120 KEY_SCALE - NONE, //121 KEY_KPCOMMA - NONE, //122 KEY_HANGEUL - NONE, //123 KEY_HANJA - NONE, //124 KEY_YEN - NONE, //125 KEY_LEFTMETA - NONE, //126 KEY_RIGHTMETA - 0x71, //127 KEY_COMPOSE - NONE, //128 KEY_STOP - NONE, //129 KEY_AGAIN - NONE, //130 KEY_PROPS - NONE, //131 KEY_UNDO - NONE, //132 KEY_FRONT - NONE, //133 KEY_COPY - NONE, //134 KEY_OPEN - NONE, //135 KEY_PASTE - NONE, //136 KEY_FIND - NONE, //137 KEY_CUT - NONE, //138 KEY_HELP - NONE, //139 KEY_MENU - NONE, //140 KEY_CALC - NONE, //141 KEY_SETUP - NONE, //142 KEY_SLEEP - NONE, //143 KEY_WAKEUP - NONE, //144 KEY_FILE - NONE, //145 KEY_SENDFILE - NONE, //146 KEY_DELETEFILE - NONE, //147 KEY_XFER - NONE, //148 KEY_PROG1 - NONE, //149 KEY_PROG2 - NONE, //150 KEY_WWW - NONE, //151 KEY_MSDOS - NONE, //152 KEY_SCREENLOCK - NONE, //153 KEY_DIRECTION - NONE, //154 KEY_CYCLEWINDOWS - NONE, //155 KEY_MAIL - NONE, //156 KEY_BOOKMARKS - NONE, //157 KEY_COMPUTER - NONE, //158 KEY_BACK - NONE, //159 KEY_FORWARD - NONE, //160 KEY_CLOSECD - NONE, //161 KEY_EJECTCD - NONE, //162 KEY_EJECTCLOSECD - NONE, //163 KEY_NEXTSONG - NONE, //164 KEY_PLAYPAUSE - NONE, //165 KEY_PREVIOUSSONG - NONE, //166 KEY_STOPCD - NONE, //167 KEY_RECORD - NONE, //168 KEY_REWIND - NONE, //169 KEY_PHONE - NONE, //170 KEY_ISO - NONE, //171 KEY_CONFIG - NONE, //172 KEY_HOMEPAGE - NONE, //173 KEY_REFRESH - NONE, //174 KEY_EXIT - NONE, //175 KEY_MOVE - NONE, //176 KEY_EDIT - NONE, //177 KEY_SCROLLUP - NONE, //178 KEY_SCROLLDOWN - NONE, //179 KEY_KPLEFTPAREN - NONE, //180 KEY_KPRIGHTPAREN - NONE, //181 KEY_NEW - NONE, //182 KEY_REDO - NONE, //183 KEY_F13 - NONE, //184 KEY_F14 - NONE, //185 KEY_F15 - NONE, //186 KEY_F16 - NONE, //187 KEY_F17 - NONE, //188 KEY_F18 - NONE, //189 KEY_F19 - NONE, //190 KEY_F20 - NONE, //191 KEY_F21 - NONE, //192 KEY_F22 - NONE, //193 KEY_F23 - NONE, //194 KEY_F24 - NONE, //195 ??? - NONE, //196 ??? - NONE, //197 ??? - NONE, //198 ??? - NONE, //199 ??? - NONE, //200 KEY_PLAYCD - NONE, //201 KEY_PAUSECD - NONE, //202 KEY_PROG3 - NONE, //203 KEY_PROG4 - NONE, //204 KEY_DASHBOARD - NONE, //205 KEY_SUSPEND - NONE, //206 KEY_CLOSE - NONE, //207 KEY_PLAY - NONE, //208 KEY_FASTFORWARD - NONE, //209 KEY_BASSBOOST - NONE, //210 KEY_PRINT - NONE, //211 KEY_HP - NONE, //212 KEY_CAMERA - NONE, //213 KEY_SOUND - NONE, //214 KEY_QUESTION - NONE, //215 KEY_EMAIL - NONE, //216 KEY_CHAT - NONE, //217 KEY_SEARCH - NONE, //218 KEY_CONNECT - NONE, //219 KEY_FINANCE - NONE, //220 KEY_SPORT - NONE, //221 KEY_SHOP - NONE, //222 KEY_ALTERASE - NONE, //223 KEY_CANCEL - NONE, //224 KEY_BRIGHT_DOWN - NONE, //225 KEY_BRIGHT_UP - NONE, //226 KEY_MEDIA - NONE, //227 KEY_SWITCHVIDEO - NONE, //228 KEY_DILLUMTOGGLE - NONE, //229 KEY_DILLUMDOWN - NONE, //230 KEY_DILLUMUP - NONE, //231 KEY_SEND - NONE, //232 KEY_REPLY - NONE, //233 KEY_FORWARDMAIL - NONE, //234 KEY_SAVE - NONE, //235 KEY_DOCUMENTS - NONE, //236 KEY_BATTERY - NONE, //237 KEY_BLUETOOTH - NONE, //238 KEY_WLAN - NONE, //239 KEY_UWB - NONE, //240 KEY_UNKNOWN - NONE, //241 KEY_VIDEO_NEXT - NONE, //242 KEY_VIDEO_PREV - NONE, //243 KEY_BRIGHT_CYCLE - NONE, //244 KEY_BRIGHT_AUTO - NONE, //245 KEY_DISPLAY_OFF - NONE, //246 KEY_WWAN - NONE, //247 KEY_RFKILL - NONE, //248 KEY_MICMUTE - NONE, //249 ??? - NONE, //250 ??? - NONE, //251 ??? - NONE, //252 ??? - NONE, //253 ??? - NONE, //254 ??? - NONE //255 ??? -}; - -/* - -// unmapped atari keys: -// 0x63 KP ( -// 0x64 KP ) - -// keycode translation table for atari -const unsigned short usb2atari[] = { -MISS, // 00: NoEvent -MISS, // 01: Overrun Error -MISS, // 02: POST fail -MISS, // 03: ErrorUndefined -0x1e, // 04: a -0x30, // 05: b -0x2e, // 06: c -0x20, // 07: d -0x12, // 08: e -0x21, // 09: f -0x22, // 0a: g -0x23, // 0b: h -0x17, // 0c: i -0x24, // 0d: j -0x25, // 0e: k -0x26, // 0f: l -0x32, // 10: m -0x31, // 11: n -0x18, // 12: o -0x19, // 13: p -0x10, // 14: q -0x13, // 15: r -0x1f, // 16: s -0x14, // 17: t -0x16, // 18: u -0x2f, // 19: v -0x11, // 1a: w -0x2d, // 1b: x -0x15, // 1c: y -0x2c, // 1d: z -0x02, // 1e: 1 -0x03, // 1f: 2 -0x04, // 20: 3 -0x05, // 21: 4 -0x06, // 22: 5 -0x07, // 23: 6 -0x08, // 24: 7 -0x09, // 25: 8 -0x0a, // 26: 9 -0x0b, // 27: 0 -0x1c, // 28: Return -0x01, // 29: Escape -0x0e, // 2a: Backspace -0x0f, // 2b: Tab -0x39, // 2c: Space -0x0c, // 2d: - -0x0d, // 2e: = -0x1a, // 2f: [ -0x1b, // 30: ] -0x29, // 31: backslash, only on us keyboard -0x29, // 32: Europe 1, only on int. keyboard -0x27, // 33: ; -0x28, // 34: ' -0x2b, // 35: ` -0x33, // 36: , -0x34, // 37: . -0x35, // 38: / -0x3a | CAPS_LOCK_TOGGLE, // 39: Caps Lock -0x3b, // 3a: F1 -0x3c, // 3b: F2 -0x3d, // 3c: F3 -0x3e, // 3d: F4 -0x3f, // 3e: F5 -0x40, // 3f: F6 -0x41, // 40: F7 -0x42, // 41: F8 -0x43, // 42: F9 -0x44, // 43: F10 -MISS, // 44: F11 -OSD_OPEN, // 45: F12 -MISS, // 46: Print Screen -NUM_LOCK_TOGGLE, // 47: Scroll Lock -MISS, // 48: Pause -0x52, // 49: Insert -0x47, // 4a: Home -0x62, // 4b: Page Up -0x53, // 4c: Delete -MISS, // 4d: End -0x61, // 4e: Page Down -0x4d, // 4f: Right Arrow -0x4b, // 50: Left Arrow -0x50, // 51: Down Arrow -0x48, // 52: Up Arrow -NUM_LOCK_TOGGLE, // 53: Num Lock -0x65, // 54: KP / -0x66, // 55: KP * -0x4a, // 56: KP - -0x4e, // 57: KP + -0x72, // 58: KP Enter -0x6d, // 59: KP 1 -0x6e, // 5a: KP 2 -0x6f, // 5b: KP 3 -0x6a, // 5c: KP 4 -0x6b, // 5d: KP 5 -0x6c, // 5e: KP 6 -0x67, // 5f: KP 7 -0x68, // 60: KP 8 -0x69, // 61: KP 9 -0x70, // 62: KP 0 -0x71, // 63: KP . -0x60, // 64: Europe 2 -OSD_OPEN, // 65: App -MISS, // 66: Power -MISS, // 67: KP = -MISS, // 68: F13 -MISS, // 69: F14 -MISS, // 6a: F15 -0x52, // 6b: insert (for keyrah) -NUM_LOCK_TOGGLE | 1, // 6c: F17 -NUM_LOCK_TOGGLE | 2, // 6d: F18 -NUM_LOCK_TOGGLE | 3, // 6e: F19 -NUM_LOCK_TOGGLE | 4 // 6f: F20 -}; - -unsigned short modifier_keycode(unsigned char index) -{ -// usb modifer bits: -//0 1 2 3 4 5 6 7 -//LCTRL LSHIFT LALT LGUI RCTRL RSHIFT RALT RGUI - -if (core_type == CORE_TYPE_MIST) -{ -static const unsigned short atari_modifier[] = { 0x1d, 0x2a, 0x38, MISS, 0x1d, 0x36, 0x38, MISS }; -return atari_modifier[index]; -} -} -*/ - - -uint32_t get_ps2_code(uint16_t key) -{ - if (key > 255) return NONE; - return ev2ps2[key]; -} - -uint32_t get_amiga_code(uint16_t key) -{ - if (key > 255) return NONE; - return ev2amiga[key]; -} - -uint32_t get_atari_code(uint16_t key) -{ - if (key > 255) return NONE; - return 0; // ev2atari[key]; -} - -uint32_t get_archie_code(uint16_t key) -{ - if (key > 255) return NONE; - return ev2archie[key]; -} - -static uint32_t modifier = 0; - -uint32_t get_key_mod() -{ - return modifier & MODMASK; -} - -typedef struct -{ - uint16_t vid, pid; - uint8_t led; - uint8_t axis_state[256]; - - uint8_t num; - uint8_t has_map; - uint32_t map[32]; - - uint8_t has_mmap; - uint32_t mmap[32]; - uint16_t jkmap[1024]; - - uint8_t has_kbdmap; - uint8_t kbdmap[256]; - - int accx, accy; -} devInput; - -static devInput input[NUMDEV] = {}; - -#define BTN_NUM (sizeof(devInput::map) / sizeof(devInput::map[0])) - -int mfd = -1; -int mwd = -1; - -static int set_watch() -{ - mwd = -1; - mfd = inotify_init(); - if (mfd < 0) - { - printf("ERR: inotify_init"); - return -1; - } - - mwd = inotify_add_watch(mfd, "/dev/input", IN_MODIFY | IN_CREATE | IN_DELETE); - - if (mwd < 0) - { - printf("ERR: inotify_add_watch"); - return -1; - } - - return mfd; -} - -#define EVENT_SIZE ( sizeof (struct inotify_event) ) -#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) ) - -static int check_devs() -{ - int result = 0; - int length, i = 0; - char buffer[BUF_LEN]; - length = read(mfd, buffer, BUF_LEN); - - if (length < 0) - { - printf("ERR: read\n"); - return 0; - } - - while (ilen) - { - if (event->mask & IN_CREATE) - { - result = 1; - if (event->mask & IN_ISDIR) - { - printf("The directory %s was created.\n", event->name); - } - else - { - printf("The file %s was created.\n", event->name); - } - } - else if (event->mask & IN_DELETE) - { - result = 1; - if (event->mask & IN_ISDIR) - { - printf("The directory %s was deleted.\n", event->name); - } - else - { - printf("The file %s was deleted.\n", event->name); - } - } - /* - else if ( event->mask & IN_MODIFY ) - { - result = 1; - if ( event->mask & IN_ISDIR ) - { - printf( "The directory %s was modified.\n", event->name ); - } - else - { - printf( "The file %s was modified.\n", event->name ); - } - } - */ - } - i += EVENT_SIZE + event->len; - } - - return result; -} - -static void INThandler(int code) -{ - (void)code; - - printf("\nExiting...\n"); - - if (mwd >= 0) inotify_rm_watch(mfd, mwd); - if (mfd >= 0) close(mfd); - - exit(0); -} - -#define test_bit(bit, array) (array [bit / 8] & (1 << (bit % 8))) - -static char has_led(int fd) -{ - unsigned char evtype_b[(EV_MAX + 7) / 8]; - if (fd<0) return 0; - - memset(&evtype_b, 0, sizeof(evtype_b)); - if (ioctl(fd, EVIOCGBIT(0, sizeof(evtype_b)), evtype_b) < 0) - { - printf("ERR: evdev ioctl.\n"); - return 0; - } - - if (test_bit(EV_LED, evtype_b)) - { - printf("has LEDs.\n"); - return 1; - } - - return 0; -} - -static char leds_state = 0; -void set_kbdled(int mask, int state) -{ - leds_state = state ? leds_state | (mask&HID_LED_MASK) : leds_state & ~(mask&HID_LED_MASK); -} - -int get_kbdled(int mask) -{ - return (leds_state & (mask&HID_LED_MASK)) ? 1 : 0; -} - -int toggle_kbdled(int mask) -{ - int state = !get_kbdled(mask); - set_kbdled(mask, state); - return state; -} - -static int mapping = 0; -static int mapping_button; -static int mapping_dev; -static int mapping_type; -static int mapping_count; -static uint8_t mapping_key; - -static uint32_t tmp_axis[4]; -static int tmp_axis_n = 0; - -void start_map_setting(int cnt) -{ - mapping_button = 0; - mapping = 1; - mapping_dev = -1; - mapping_type = (cnt<0) ? 3 : cnt ? 1 : 2; - mapping_count = cnt; - tmp_axis_n = 0; - - if (mapping_type <= 1 && is_menu_core()) mapping_button = -6; - memset(tmp_axis, 0, sizeof(tmp_axis)); -} - -int get_map_button() -{ - return (mapping_type == 2) ? mapping_key : mapping_button; -} - -int get_map_type() -{ - return mapping_type; -} - -static char *get_map_name(int dev, int def) -{ - static char name[128]; - if (def || is_menu_core()) sprintf(name, "input_%04x_%04x_v2.map", input[dev].vid, input[dev].pid); - else sprintf(name, "%s_input_%04x_%04x_v2.map", user_io_get_core_name_ex(), input[dev].vid, input[dev].pid); - return name; -} - -static char *get_kbdmap_name(int dev) -{ - static char name[128]; - sprintf(name, "kbd_%04x_%04x.map", input[dev].vid, input[dev].pid); - return name; -} - -void finish_map_setting(int dismiss) -{ - mapping = 0; - if (mapping_dev<0) return; - - if (mapping_type == 2) - { - if (dismiss) input[mapping_dev].has_kbdmap = 0; - else FileSaveConfig(get_kbdmap_name(mapping_dev), &input[mapping_dev].kbdmap, sizeof(input[mapping_dev].kbdmap)); - } - else if (mapping_type == 3) - { - if (dismiss) memset(input[mapping_dev].jkmap, 0, sizeof(input[mapping_dev].jkmap)); - } - else - { - for (int i = 0; i < NUMDEV; i++) input[i].has_map = 0; - - if (mapping_button < 0) mapping_button = 0; - if (!is_menu_core()) for (uint i = mapping_button; i < BTN_NUM; i++) input[mapping_dev].map[i] = 0; - - if (!dismiss) FileSaveConfig(get_map_name(mapping_dev, 0), &input[mapping_dev].map, sizeof(input[mapping_dev].map)); - if (is_menu_core()) input[mapping_dev].has_mmap = 0; - } -} - -uint16_t get_map_vid() -{ - return (mapping && mapping_dev >= 0) ? input[mapping_dev].vid : 0; -} - -uint16_t get_map_pid() -{ - return (mapping && mapping_dev >= 0) ? input[mapping_dev].pid : 0; -} - -int has_default_map() -{ - return (mapping_dev >= 0) ? (input[mapping_dev].has_mmap == 1) : 0; -} - -static char kr_fn_table[] = -{ - KEY_KPSLASH, KEY_PAUSE, - KEY_KPASTERISK, KEY_PRINT, - KEY_LEFT, KEY_HOME, - KEY_RIGHT, KEY_END, - KEY_UP, KEY_PAGEUP, - KEY_DOWN, KEY_PAGEDOWN, - KEY_F1, KEY_F11, - KEY_F2, KEY_F12, - - KEY_F3, KEY_F17, // EMU_MOUSE - KEY_F4, KEY_F18, // EMU_JOY0 - KEY_F5, KEY_F19, // EMU_JOY1 - KEY_F6, KEY_F20, // EMU_NONE - - //Emulate keypad for A600 - KEY_1, KEY_KP1, - KEY_2, KEY_KP2, - KEY_3, KEY_KP3, - KEY_4, KEY_KP4, - KEY_5, KEY_KP5, - KEY_6, KEY_KP6, - KEY_7, KEY_KP7, - KEY_8, KEY_KP8, - KEY_9, KEY_KP9, - KEY_0, KEY_KP0, - KEY_MINUS, KEY_KPMINUS, - KEY_EQUAL, KEY_KPPLUS, - KEY_BACKSLASH, KEY_KPASTERISK, - KEY_LEFTBRACE, KEY_F13, //KP( - KEY_RIGHTBRACE, KEY_F14, //KP) - KEY_DOT, KEY_KPDOT, - KEY_ENTER, KEY_KPENTER -}; - -static int keyrah_trans(int key, int press) -{ - static int fn = 0; - - if (key == KEY_NUMLOCK) return KEY_F13; // numlock -> f13 - if (key == KEY_SCROLLLOCK) return KEY_F14; // scrlock -> f14 - if (key == KEY_INSERT) return KEY_F16; // insert -> f16. workaround! - - if (key == KEY_102ND) - { - if (!press && fn == 1) menu_key_set(KEY_MENU); - fn = press ? 1 : 0; - return 0; - } - else if (fn) - { - fn |= 2; - for (uint32_t n = 0; n<(sizeof(kr_fn_table) / (2 * sizeof(kr_fn_table[0]))); n++) - { - if ((key&255) == kr_fn_table[n * 2]) return kr_fn_table[(n * 2) + 1]; - } - } - - return key; -} - -#define KEY_EMU (KEY_MAX+1) - -#define AXIS1_X 24 -#define AXIS1_Y 25 -#define AXIS2_X 26 -#define AXIS2_Y 27 -#define AXIS_X 28 -#define AXIS_Y 29 -#define AXIS_MX 30 -#define AXIS_MY 31 - -static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int dev); - -static int kbd_toggle = 0; -static uint32_t joy[NUMPLAYERS] = {}, joy_prev[NUMPLAYERS] = {}; -static uint32_t autofire[NUMPLAYERS] = {}; -static uint32_t autofirecodes[NUMPLAYERS][BTN_NUM] = {}; -static int af_delay[NUMPLAYERS] = {}; - -static unsigned char mouse_btn = 0; -static int mouse_emu = 0; -static int kbd_mouse_emu = 0; -static int mouse_sniper = 0; -static int mouse_emu_x = 0; -static int mouse_emu_y = 0; - -static uint32_t mouse_timer = 0; - -#define BTN_TGL 100 -#define BTN_OSD 101 - -static void joy_digital(int jnum, uint32_t mask, uint32_t code, char press, int bnum) -{ - static char str[128]; - static uint32_t lastcode[NUMPLAYERS], lastmask[NUMPLAYERS]; - int num = jnum - 1; - - if (num < NUMPLAYERS) - { - if (jnum) - { - if (bnum != BTN_OSD && bnum != BTN_TGL) - { - if (!(mask & 0xF)) - { - if (press) - { - lastcode[num] = code; - lastmask[num] = mask; - } - else - { - lastcode[num] = 0; - lastmask[num] = 0; - } - } - } - else if (!user_io_osd_is_visible()) - { - if (lastcode[num]) - { - if (press) - { - int found = 0; - int zero = -1; - for (uint i = 0; i < BTN_NUM; i++) - { - if (!autofirecodes[num][i]) zero = i; - if (autofirecodes[num][i] == lastcode[num]) - { - found = 1; - autofirecodes[num][i] = 0; - break; - } - } - - if (!found && zero >= 0) autofirecodes[num][zero] = lastcode[num]; - autofire[num] = !found ? autofire[num] | lastmask[num] : autofire[num] & ~lastmask[num]; - - if (hasAPI1_5()) - { - if (!found) sprintf(str, "Auto fire: %d ms", af_delay[num] * 2); - else sprintf(str, "Auto fire: OFF"); - Info(str); - } - else InfoMessage((!found) ? "\n\n Auto fire\n ON" : - "\n\n Auto fire\n OFF"); - } - return; - } - else if (joy[num] & 0xF) - { - if (press) - { - if (joy[num] & 9) - { - af_delay[num] += 25 << ((joy[num] & 1) ? 1 : 0); - if (af_delay[num] > 500) af_delay[num] = 500; - } - else - { - af_delay[num] -= 25 << ((joy[num] & 2) ? 1 : 0); - if (af_delay[num] < 25) af_delay[num] = 25; - } - - static char str[256]; - - if (hasAPI1_5()) - { - sprintf(str, "Auto fire period: %d ms", af_delay[num] * 2); - Info(str); - } - else - { - sprintf(str, "\n\n Auto fire period\n %dms", af_delay[num] * 2); - InfoMessage(str); - } - } - return; - } - } - } - - if (bnum == BTN_TGL) - { - if(press) kbd_toggle = !kbd_toggle; - return; - } - - if (!user_io_osd_is_visible() && (bnum == BTN_OSD) && (mouse_emu & 1)) - { - if (press) - { - mouse_sniper = 0; - mouse_timer = 0; - mouse_btn = 0; - mouse_emu_x = 0; - mouse_emu_y = 0; - user_io_mouse(0, 0, 0); - - mouse_emu ^= 2; - if (hasAPI1_5()) Info((mouse_emu & 2) ? "Mouse mode ON" : "Mouse mode OFF"); - else InfoMessage((mouse_emu & 2) ? "\n\n Mouse mode lock\n ON" : - "\n\n Mouse mode lock\n OFF"); - } - return; - } - - if (user_io_osd_is_visible() || (bnum == BTN_OSD)) - { - memset(joy, 0, sizeof(joy)); - struct input_event ev; - ev.type = EV_KEY; - ev.value = press; - switch (mask) - { - case JOY_RIGHT: - ev.code = KEY_RIGHT; - break; - - case JOY_LEFT: - ev.code = KEY_LEFT; - break; - - case JOY_UP: - ev.code = KEY_UP; - break; - - case JOY_DOWN: - ev.code = KEY_DOWN; - break; - - case JOY_BTN1: - ev.code = KEY_ENTER; - break; - - case JOY_BTN2: - ev.code = KEY_ESC; - break; - - case JOY_BTN3: - ev.code = KEY_BACKSPACE; - break; - - default: - ev.code = (bnum == BTN_OSD) ? KEY_MENU : 0; - } - - input_cb(&ev, 0, 0); - } - else if(jnum) - { - if (press) joy[num] |= mask; - else joy[num] &= ~mask; - //user_io_digital_joystick(num, joy[num]); - - if (code) - { - int found = 0; - for (uint i = 0; i < BTN_NUM; i++) if (autofirecodes[num][i] == code) found = 1; - if (found) autofire[num] = press ? autofire[num] | mask : autofire[num] & ~mask; - } - } - } -} - -static void joy_analog(int num, int axis, int offset) -{ - static int pos[NUMPLAYERS][2] = {}; - - if (num > 0 && num < NUMPLAYERS+1) - { - num--; - pos[num][axis] = offset; - user_io_analog_joystick(num, (char)(pos[num][0]), (char)(pos[num][1])); - } -} - -static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int dev) -{ - static int key_mapped = 0; - - if (ev->type == EV_KEY && mapping && mapping_type == 3 && ev->code == input[dev].mmap[17]) ev->code = KEY_ENTER; - - int map_skip = (ev->type == EV_KEY && ev->code == KEY_SPACE && ((mapping_dev >= 0 && mapping_type==1) || mapping_button<0)); - int cancel = (ev->type == EV_KEY && ev->code == KEY_ESC); - int enter = (ev->type == EV_KEY && ev->code == KEY_ENTER); - int origcode = ev->code; - - //mouse - switch (ev->type) - { - case EV_KEY: - if (ev->code >= 272 && ev->code <= 279) - { - if (ev->value <= 1) - { - unsigned char mask = 1 << (ev->code - 272); - mouse_btn = (ev->value) ? mouse_btn | mask : mouse_btn & ~mask; - user_io_mouse(mouse_btn, 0, 0); - } - return; - } - break; - - case EV_REL: - { - int msval; - if (!cfg.mouse_throttle) cfg.mouse_throttle = 1; - - switch (ev->code) - { - case 0: - input[dev].accx += ev->value; - msval = input[dev].accx / cfg.mouse_throttle; - input[dev].accx -= msval * cfg.mouse_throttle; - - //printf("Mouse PosX: %d\n", msval); - user_io_mouse(mouse_btn, msval, 0); - return; - case 1: - input[dev].accy += ev->value; - msval = input[dev].accy / cfg.mouse_throttle; - input[dev].accy -= msval * cfg.mouse_throttle; - - //printf("Mouse PosY: %d\n", msval); - user_io_mouse(mouse_btn, 0, msval); - return; - } - } - break; - } - - if (!input[dev].has_map) - { - if (!FileLoadConfig(get_map_name(dev, 0), &input[dev].map, sizeof(input[dev].map))) - { - if (is_menu_core() || !FileLoadConfig(get_map_name(dev, 1), &input[dev].map, sizeof(input[dev].map))) - { - memset(input[dev].map, 0, sizeof(input[dev].map)); - input[dev].has_map++; - } - - //remove system controls from default map - for (uint i = 8; i < sizeof(input[0].map) / sizeof(input[0].map[0]); i++) input[dev].map[i] = 0; - input[dev].has_map++; - } - input[dev].has_map++; - } - - if (!input[dev].has_mmap) - { - if (!FileLoadConfig(get_map_name(dev, 1), &input[dev].mmap, sizeof(input[dev].mmap))) - { - memset(input[dev].mmap, 0, sizeof(input[dev].mmap)); - input[dev].has_mmap++; - } - input[dev].has_mmap++; - } - - //mapping - if (mapping && (mapping_dev >=0 || ev->value) && !cancel && !enter) - { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "input.h" +#include "user_io.h" +#include "menu.h" +#include "hardware.h" +#include "cfg.h" +#include "fpga_io.h" +#include "osd.h" +#include "errno.h" + +#define NUMDEV 10 +#define NUMPLAYERS 6 + +static int ev2amiga[] = +{ + NONE, //0 KEY_RESERVED + 0x45, //1 KEY_ESC + 0x01, //2 KEY_1 + 0x02, //3 KEY_2 + 0x03, //4 KEY_3 + 0x04, //5 KEY_4 + 0x05, //6 KEY_5 + 0x06, //7 KEY_6 + 0x07, //8 KEY_7 + 0x08, //9 KEY_8 + 0x09, //10 KEY_9 + 0x0a, //11 KEY_0 + 0x0b, //12 KEY_MINUS + 0x0c, //13 KEY_EQUAL + 0x41, //14 KEY_BACKSPACE + 0x42, //15 KEY_TAB + 0x10, //16 KEY_Q + 0x11, //17 KEY_W + 0x12, //18 KEY_E + 0x13, //19 KEY_R + 0x14, //20 KEY_T + 0x15, //21 KEY_Y + 0x16, //22 KEY_U + 0x17, //23 KEY_I + 0x18, //24 KEY_O + 0x19, //25 KEY_P + 0x1a, //26 KEY_LEFTBRACE + 0x1b, //27 KEY_RIGHTBRACE + 0x44, //28 KEY_ENTER + 0x63, //29 KEY_LEFTCTRL + 0x20, //30 KEY_A + 0x21, //31 KEY_S + 0x22, //32 KEY_D + 0x23, //33 KEY_F + 0x24, //34 KEY_G + 0x25, //35 KEY_H + 0x26, //36 KEY_J + 0x27, //37 KEY_K + 0x28, //38 KEY_L + 0x29, //39 KEY_SEMICOLON + 0x2a, //40 KEY_APOSTROPHE + 0x00, //41 KEY_GRAVE + 0x60, //42 KEY_LEFTSHIFT + 0x0d, //43 KEY_BACKSLASH + 0x31, //44 KEY_Z + 0x32, //45 KEY_X + 0x33, //46 KEY_C + 0x34, //47 KEY_V + 0x35, //48 KEY_B + 0x36, //49 KEY_N + 0x37, //50 KEY_M + 0x38, //51 KEY_COMMA + 0x39, //52 KEY_DOT + 0x3a, //53 KEY_SLASH + 0x61, //54 KEY_RIGHTSHIFT + 0x5d, //55 KEY_KPASTERISK + 0x64, //56 KEY_LEFTALT + 0x40, //57 KEY_SPACE + 0x62 | CAPS_TOGGLE, //58 KEY_CAPSLOCK + 0x50, //59 KEY_F1 + 0x51, //60 KEY_F2 + 0x52, //61 KEY_F3 + 0x53, //62 KEY_F4 + 0x54, //63 KEY_F5 + 0x55, //64 KEY_F6 + 0x56, //65 KEY_F7 + 0x57, //66 KEY_F8 + 0x58, //67 KEY_F9 + 0x59, //68 KEY_F10 + NONE, //69 KEY_NUMLOCK + NONE, //70 KEY_SCROLLLOCK + 0x3d, //71 KEY_KP7 + 0x3e, //72 KEY_KP8 + 0x3f, //73 KEY_KP9 + 0x4a, //74 KEY_KPMINUS + 0x2d, //75 KEY_KP4 + 0x2e, //76 KEY_KP5 + 0x2f, //77 KEY_KP6 + 0x5e, //78 KEY_KPPLUS + 0x1d, //79 KEY_KP1 + 0x1e, //80 KEY_KP2 + 0x1f, //81 KEY_KP3 + 0x0f, //82 KEY_KP0 + 0x3c, //83 KEY_KPDOT + NONE, //84 ??? + NONE, //85 KEY_ZENKAKU + NONE, //86 KEY_102ND + 0x5f, //87 KEY_F11 + NONE, //88 KEY_F12 + NONE, //89 KEY_RO + NONE, //90 KEY_KATAKANA + NONE, //91 KEY_HIRAGANA + NONE, //92 KEY_HENKAN + NONE, //93 KEY_KATAKANA + NONE, //94 KEY_MUHENKAN + NONE, //95 KEY_KPJPCOMMA + 0x43, //96 KEY_KPENTER + 0x63, //97 KEY_RIGHTCTRL + 0x5c, //98 KEY_KPSLASH + NONE, //99 KEY_SYSRQ + 0x65, //100 KEY_RIGHTALT + NONE, //101 KEY_LINEFEED + 0x6a, //102 KEY_HOME + 0x4c, //103 KEY_UP + NONE, //104 KEY_PAGEUP + 0x4f, //105 KEY_LEFT + 0x4e, //106 KEY_RIGHT + NONE, //107 KEY_END + 0x4d, //108 KEY_DOWN + NONE, //109 KEY_PAGEDOWN + 0x0d, //110 KEY_INSERT + 0x46, //111 KEY_DELETE + NONE, //112 KEY_MACRO + NONE, //113 KEY_MUTE + NONE, //114 KEY_VOLUMEDOWN + NONE, //115 KEY_VOLUMEUP + NONE, //116 KEY_POWER + NONE, //117 KEY_KPEQUAL + NONE, //118 KEY_KPPLUSMINUS + NONE, //119 KEY_PAUSE + NONE, //120 KEY_SCALE + NONE, //121 KEY_KPCOMMA + NONE, //122 KEY_HANGEUL + NONE, //123 KEY_HANJA + NONE, //124 KEY_YEN + 0x66, //125 KEY_LEFTMETA + 0x67, //126 KEY_RIGHTMETA + NONE, //127 KEY_COMPOSE + NONE, //128 KEY_STOP + NONE, //129 KEY_AGAIN + NONE, //130 KEY_PROPS + NONE, //131 KEY_UNDO + NONE, //132 KEY_FRONT + NONE, //133 KEY_COPY + NONE, //134 KEY_OPEN + NONE, //135 KEY_PASTE + NONE, //136 KEY_FIND + NONE, //137 KEY_CUT + NONE, //138 KEY_HELP + NONE, //139 KEY_MENU + NONE, //140 KEY_CALC + NONE, //141 KEY_SETUP + NONE, //142 KEY_SLEEP + NONE, //143 KEY_WAKEUP + NONE, //144 KEY_FILE + NONE, //145 KEY_SENDFILE + NONE, //146 KEY_DELETEFILE + NONE, //147 KEY_XFER + NONE, //148 KEY_PROG1 + NONE, //149 KEY_PROG2 + NONE, //150 KEY_WWW + NONE, //151 KEY_MSDOS + NONE, //152 KEY_SCREENLOCK + NONE, //153 KEY_DIRECTION + NONE, //154 KEY_CYCLEWINDOWS + NONE, //155 KEY_MAIL + NONE, //156 KEY_BOOKMARKS + NONE, //157 KEY_COMPUTER + NONE, //158 KEY_BACK + NONE, //159 KEY_FORWARD + NONE, //160 KEY_CLOSECD + NONE, //161 KEY_EJECTCD + NONE, //162 KEY_EJECTCLOSECD + NONE, //163 KEY_NEXTSONG + NONE, //164 KEY_PLAYPAUSE + NONE, //165 KEY_PREVIOUSSONG + NONE, //166 KEY_STOPCD + NONE, //167 KEY_RECORD + NONE, //168 KEY_REWIND + NONE, //169 KEY_PHONE + NONE, //170 KEY_ISO + NONE, //171 KEY_CONFIG + NONE, //172 KEY_HOMEPAGE + NONE, //173 KEY_REFRESH + NONE, //174 KEY_EXIT + NONE, //175 KEY_MOVE + NONE, //176 KEY_EDIT + NONE, //177 KEY_SCROLLUP + NONE, //178 KEY_SCROLLDOWN + NONE, //179 KEY_KPLEFTPAREN + NONE, //180 KEY_KPRIGHTPAREN + NONE, //181 KEY_NEW + NONE, //182 KEY_REDO + 0x5a, //183 KEY_F13 + 0x5b, //184 KEY_F14 + NONE, //185 KEY_F15 + 0x5f, //186 KEY_F16 + NONE, //187 KEY_F17 + NONE, //188 KEY_F18 + NONE, //189 KEY_F19 + NONE, //190 KEY_F20 + NONE, //191 KEY_F21 + NONE, //192 KEY_F22 + NONE, //193 KEY_F23 + 0x63, //194 KEY_F24 + NONE, //195 ??? + NONE, //196 ??? + NONE, //197 ??? + NONE, //198 ??? + NONE, //199 ??? + NONE, //200 KEY_PLAYCD + NONE, //201 KEY_PAUSECD + NONE, //202 KEY_PROG3 + NONE, //203 KEY_PROG4 + NONE, //204 KEY_DASHBOARD + NONE, //205 KEY_SUSPEND + NONE, //206 KEY_CLOSE + NONE, //207 KEY_PLAY + NONE, //208 KEY_FASTFORWARD + NONE, //209 KEY_BASSBOOST + NONE, //210 KEY_PRINT + NONE, //211 KEY_HP + NONE, //212 KEY_CAMERA + NONE, //213 KEY_SOUND + NONE, //214 KEY_QUESTION + NONE, //215 KEY_EMAIL + NONE, //216 KEY_CHAT + NONE, //217 KEY_SEARCH + NONE, //218 KEY_CONNECT + NONE, //219 KEY_FINANCE + NONE, //220 KEY_SPORT + NONE, //221 KEY_SHOP + NONE, //222 KEY_ALTERASE + NONE, //223 KEY_CANCEL + NONE, //224 KEY_BRIGHT_DOWN + NONE, //225 KEY_BRIGHT_UP + NONE, //226 KEY_MEDIA + NONE, //227 KEY_SWITCHVIDEO + NONE, //228 KEY_DILLUMTOGGLE + NONE, //229 KEY_DILLUMDOWN + NONE, //230 KEY_DILLUMUP + NONE, //231 KEY_SEND + NONE, //232 KEY_REPLY + NONE, //233 KEY_FORWARDMAIL + NONE, //234 KEY_SAVE + NONE, //235 KEY_DOCUMENTS + NONE, //236 KEY_BATTERY + NONE, //237 KEY_BLUETOOTH + NONE, //238 KEY_WLAN + NONE, //239 KEY_UWB + NONE, //240 KEY_UNKNOWN + NONE, //241 KEY_VIDEO_NEXT + NONE, //242 KEY_VIDEO_PREV + NONE, //243 KEY_BRIGHT_CYCLE + NONE, //244 KEY_BRIGHT_AUTO + NONE, //245 KEY_DISPLAY_OFF + NONE, //246 KEY_WWAN + NONE, //247 KEY_RFKILL + NONE, //248 KEY_MICMUTE + NONE, //249 ??? + NONE, //250 ??? + NONE, //251 ??? + NONE, //252 ??? + NONE, //253 ??? + NONE, //254 ??? + NONE //255 ??? +}; + + +static const int ev2ps2[] = +{ + NONE, //0 KEY_RESERVED + 0x76, //1 KEY_ESC + 0x16, //2 KEY_1 + 0x1e, //3 KEY_2 + 0x26, //4 KEY_3 + 0x25, //5 KEY_4 + 0x2e, //6 KEY_5 + 0x36, //7 KEY_6 + 0x3d, //8 KEY_7 + 0x3e, //9 KEY_8 + 0x46, //10 KEY_9 + 0x45, //11 KEY_0 + 0x4e, //12 KEY_MINUS + 0x55, //13 KEY_EQUAL + 0x66, //14 KEY_BACKSPACE + 0x0d, //15 KEY_TAB + 0x15, //16 KEY_Q + 0x1d, //17 KEY_W + 0x24, //18 KEY_E + 0x2d, //19 KEY_R + 0x2c, //20 KEY_T + 0x35, //21 KEY_Y + 0x3c, //22 KEY_U + 0x43, //23 KEY_I + 0x44, //24 KEY_O + 0x4d, //25 KEY_P + 0x54, //26 KEY_LEFTBRACE + 0x5b, //27 KEY_RIGHTBRACE + 0x5a, //28 KEY_ENTER + LCTRL | 0x14, //29 KEY_LEFTCTRL + 0x1c, //30 KEY_A + 0x1b, //31 KEY_S + 0x23, //32 KEY_D + 0x2b, //33 KEY_F + 0x34, //34 KEY_G + 0x33, //35 KEY_H + 0x3b, //36 KEY_J + 0x42, //37 KEY_K + 0x4b, //38 KEY_L + 0x4c, //39 KEY_SEMICOLON + 0x52, //40 KEY_APOSTROPHE + 0x0e, //41 KEY_GRAVE + LSHIFT | 0x12, //42 KEY_LEFTSHIFT + 0x5d, //43 KEY_BACKSLASH + 0x1a, //44 KEY_Z + 0x22, //45 KEY_X + 0x21, //46 KEY_C + 0x2a, //47 KEY_V + 0x32, //48 KEY_B + 0x31, //49 KEY_N + 0x3a, //50 KEY_M + 0x41, //51 KEY_COMMA + 0x49, //52 KEY_DOT + 0x4a, //53 KEY_SLASH + RSHIFT | 0x59, //54 KEY_RIGHTSHIFT + 0x7c, //55 KEY_KPASTERISK + LALT | 0x11, //56 KEY_LEFTALT + 0x29, //57 KEY_SPACE + 0x58, //58 KEY_CAPSLOCK + 0x05, //59 KEY_F1 + 0x06, //60 KEY_F2 + 0x04, //61 KEY_F3 + 0x0c, //62 KEY_F4 + 0x03, //63 KEY_F5 + 0x0b, //64 KEY_F6 + 0x83, //65 KEY_F7 + 0x0a, //66 KEY_F8 + 0x01, //67 KEY_F9 + 0x09, //68 KEY_F10 + EMU_SWITCH_2 | 0x77, //69 KEY_NUMLOCK + EMU_SWITCH_1 | 0x7E, //70 KEY_SCROLLLOCK + 0x6c, //71 KEY_KP7 + 0x75, //72 KEY_KP8 + 0x7d, //73 KEY_KP9 + 0x7b, //74 KEY_KPMINUS + 0x6b, //75 KEY_KP4 + 0x73, //76 KEY_KP5 + 0x74, //77 KEY_KP6 + 0x79, //78 KEY_KPPLUS + 0x69, //79 KEY_KP1 + 0x72, //80 KEY_KP2 + 0x7a, //81 KEY_KP3 + 0x70, //82 KEY_KP0 + 0x71, //83 KEY_KPDOT + NONE, //84 ??? + NONE, //85 KEY_ZENKAKU + NONE, //86 KEY_102ND + 0x78, //87 KEY_F11 + 0x07, //88 KEY_F12 + NONE, //89 KEY_RO + NONE, //90 KEY_KATAKANA + NONE, //91 KEY_HIRAGANA + NONE, //92 KEY_HENKAN + NONE, //93 KEY_KATAKANA + NONE, //94 KEY_MUHENKAN + NONE, //95 KEY_KPJPCOMMA + EXT | 0x5a, //96 KEY_KPENTER + RCTRL | EXT | 0x14, //97 KEY_RIGHTCTRL + EXT | 0x4a, //98 KEY_KPSLASH + 0xE2, //99 KEY_SYSRQ + RALT | EXT | 0x11, //100 KEY_RIGHTALT + NONE, //101 KEY_LINEFEED + EXT | 0x6c, //102 KEY_HOME + EXT | 0x75, //103 KEY_UP + EXT | 0x7d, //104 KEY_PAGEUP + EXT | 0x6b, //105 KEY_LEFT + EXT | 0x74, //106 KEY_RIGHT + EXT | 0x69, //107 KEY_END + EXT | 0x72, //108 KEY_DOWN + EXT | 0x7a, //109 KEY_PAGEDOWN + EXT | 0x70, //110 KEY_INSERT + EXT | 0x71, //111 KEY_DELETE + NONE, //112 KEY_MACRO + NONE, //113 KEY_MUTE + NONE, //114 KEY_VOLUMEDOWN + NONE, //115 KEY_VOLUMEUP + NONE, //116 KEY_POWER + NONE, //117 KEY_KPEQUAL + NONE, //118 KEY_KPPLUSMINUS + 0xE1, //119 KEY_PAUSE + NONE, //120 KEY_SCALE + NONE, //121 KEY_KPCOMMA + NONE, //122 KEY_HANGEUL + NONE, //123 KEY_HANJA + NONE, //124 KEY_YEN + LGUI | EXT | 0x1f, //125 KEY_LEFTMETA + RGUI | EXT | 0x27, //126 KEY_RIGHTMETA + NONE, //127 KEY_COMPOSE + NONE, //128 KEY_STOP + NONE, //129 KEY_AGAIN + NONE, //130 KEY_PROPS + NONE, //131 KEY_UNDO + NONE, //132 KEY_FRONT + NONE, //133 KEY_COPY + NONE, //134 KEY_OPEN + NONE, //135 KEY_PASTE + NONE, //136 KEY_FIND + NONE, //137 KEY_CUT + NONE, //138 KEY_HELP + NONE, //139 KEY_MENU + NONE, //140 KEY_CALC + NONE, //141 KEY_SETUP + NONE, //142 KEY_SLEEP + NONE, //143 KEY_WAKEUP + NONE, //144 KEY_FILE + NONE, //145 KEY_SENDFILE + NONE, //146 KEY_DELETEFILE + NONE, //147 KEY_XFER + NONE, //148 KEY_PROG1 + NONE, //149 KEY_PROG2 + NONE, //150 KEY_WWW + NONE, //151 KEY_MSDOS + NONE, //152 KEY_SCREENLOCK + NONE, //153 KEY_DIRECTION + NONE, //154 KEY_CYCLEWINDOWS + NONE, //155 KEY_MAIL + NONE, //156 KEY_BOOKMARKS + NONE, //157 KEY_COMPUTER + NONE, //158 KEY_BACK + NONE, //159 KEY_FORWARD + NONE, //160 KEY_CLOSECD + NONE, //161 KEY_EJECTCD + NONE, //162 KEY_EJECTCLOSECD + NONE, //163 KEY_NEXTSONG + NONE, //164 KEY_PLAYPAUSE + NONE, //165 KEY_PREVIOUSSONG + NONE, //166 KEY_STOPCD + NONE, //167 KEY_RECORD + NONE, //168 KEY_REWIND + NONE, //169 KEY_PHONE + NONE, //170 KEY_ISO + NONE, //171 KEY_CONFIG + NONE, //172 KEY_HOMEPAGE + NONE, //173 KEY_REFRESH + NONE, //174 KEY_EXIT + NONE, //175 KEY_MOVE + NONE, //176 KEY_EDIT + NONE, //177 KEY_SCROLLUP + NONE, //178 KEY_SCROLLDOWN + NONE, //179 KEY_KPLEFTPAREN + NONE, //180 KEY_KPRIGHTPAREN + NONE, //181 KEY_NEW + NONE, //182 KEY_REDO + NONE, //183 KEY_F13 + NONE, //184 KEY_F14 + NONE, //185 KEY_F15 + NONE, //186 KEY_F16 + EMU_SWITCH_1 | 1, //187 KEY_F17 + EMU_SWITCH_1 | 2, //188 KEY_F18 + EMU_SWITCH_1 | 3, //189 KEY_F19 + EMU_SWITCH_1 | 4, //190 KEY_F20 + NONE, //191 KEY_F21 + NONE, //192 KEY_F22 + NONE, //193 KEY_F23 + 0x5D, //194 U-mlaut on DE mapped to backslash + NONE, //195 ??? + NONE, //196 ??? + NONE, //197 ??? + NONE, //198 ??? + NONE, //199 ??? + NONE, //200 KEY_PLAYCD + NONE, //201 KEY_PAUSECD + NONE, //202 KEY_PROG3 + NONE, //203 KEY_PROG4 + NONE, //204 KEY_DASHBOARD + NONE, //205 KEY_SUSPEND + NONE, //206 KEY_CLOSE + NONE, //207 KEY_PLAY + NONE, //208 KEY_FASTFORWARD + NONE, //209 KEY_BASSBOOST + NONE, //210 KEY_PRINT + NONE, //211 KEY_HP + NONE, //212 KEY_CAMERA + NONE, //213 KEY_SOUND + NONE, //214 KEY_QUESTION + NONE, //215 KEY_EMAIL + NONE, //216 KEY_CHAT + NONE, //217 KEY_SEARCH + NONE, //218 KEY_CONNECT + NONE, //219 KEY_FINANCE + NONE, //220 KEY_SPORT + NONE, //221 KEY_SHOP + NONE, //222 KEY_ALTERASE + NONE, //223 KEY_CANCEL + NONE, //224 KEY_BRIGHT_DOWN + NONE, //225 KEY_BRIGHT_UP + NONE, //226 KEY_MEDIA + NONE, //227 KEY_SWITCHVIDEO + NONE, //228 KEY_DILLUMTOGGLE + NONE, //229 KEY_DILLUMDOWN + NONE, //230 KEY_DILLUMUP + NONE, //231 KEY_SEND + NONE, //232 KEY_REPLY + NONE, //233 KEY_FORWARDMAIL + NONE, //234 KEY_SAVE + NONE, //235 KEY_DOCUMENTS + NONE, //236 KEY_BATTERY + NONE, //237 KEY_BLUETOOTH + NONE, //238 KEY_WLAN + NONE, //239 KEY_UWB + NONE, //240 KEY_UNKNOWN + NONE, //241 KEY_VIDEO_NEXT + NONE, //242 KEY_VIDEO_PREV + NONE, //243 KEY_BRIGHT_CYCLE + NONE, //244 KEY_BRIGHT_AUTO + NONE, //245 KEY_DISPLAY_OFF + NONE, //246 KEY_WWAN + NONE, //247 KEY_RFKILL + NONE, //248 KEY_MICMUTE + NONE, //249 ??? + NONE, //250 ??? + NONE, //251 ??? + NONE, //252 ??? + NONE, //253 ??? + NONE, //254 ??? + NONE //255 ??? +}; + +static int ev2archie[] = +{ + NONE, //0 KEY_RESERVED + 0x00, //1 KEY_ESC + 0x11, //2 KEY_1 + 0x12, //3 KEY_2 + 0x13, //4 KEY_3 + 0x14, //5 KEY_4 + 0x15, //6 KEY_5 + 0x16, //7 KEY_6 + 0x17, //8 KEY_7 + 0x18, //9 KEY_8 + 0x19, //10 KEY_9 + 0x1a, //11 KEY_0 + 0x1b, //12 KEY_MINUS + 0x1c, //13 KEY_EQUAL + 0x1e, //14 KEY_BACKSPACE + 0x26, //15 KEY_TAB + 0x27, //16 KEY_Q + 0x28, //17 KEY_W + 0x29, //18 KEY_E + 0x2a, //19 KEY_R + 0x2b, //20 KEY_T + 0x2c, //21 KEY_Y + 0x2d, //22 KEY_U + 0x2e, //23 KEY_I + 0x2f, //24 KEY_O + 0x30, //25 KEY_P + 0x31, //26 KEY_LEFTBRACE + 0x32, //27 KEY_RIGHTBRACE + 0x47, //28 KEY_ENTER + 0x3b, //29 KEY_LEFTCTRL + 0x3c, //30 KEY_A + 0x3d, //31 KEY_S + 0x3e, //32 KEY_D + 0x3f, //33 KEY_F + 0x40, //34 KEY_G + 0x41, //35 KEY_H + 0x42, //36 KEY_J + 0x43, //37 KEY_K + 0x44, //38 KEY_L + 0x45, //39 KEY_SEMICOLON + 0x46, //40 KEY_APOSTROPHE + 0x10, //41 KEY_GRAVE + 0x4c, //42 KEY_LEFTSHIFT + 0x33, //43 KEY_BACKSLASH + 0x4e, //44 KEY_Z + 0x4f, //45 KEY_X + 0x50, //46 KEY_C + 0x51, //47 KEY_V + 0x52, //48 KEY_B + 0x53, //49 KEY_N + 0x54, //50 KEY_M + 0x55, //51 KEY_COMMA + 0x56, //52 KEY_DOT + 0x57, //53 KEY_SLASH + 0x58, //54 KEY_RIGHTSHIFT + 0x24, //55 KEY_KPASTERISK + 0x5e, //56 KEY_LEFTALT + 0x5f, //57 KEY_SPACE + 0x5d, //58 KEY_CAPSLOCK + 0x01, //59 KEY_F1 + 0x02, //60 KEY_F2 + 0x03, //61 KEY_F3 + 0x04, //62 KEY_F4 + 0x05, //63 KEY_F5 + 0x06, //64 KEY_F6 + 0x07, //65 KEY_F7 + 0x08, //66 KEY_F8 + 0x09, //67 KEY_F9 + 0x0a, //68 KEY_F10 + 0x22, //69 KEY_NUMLOCK + NONE, //70 KEY_SCROLLLOCK + 0x37, //71 KEY_KP7 + 0x38, //72 KEY_KP8 + 0x39, //73 KEY_KP9 + 0x3a, //74 KEY_KPMINUS + 0x48, //75 KEY_KP4 + 0x49, //76 KEY_KP5 + 0x4a, //77 KEY_KP6 + 0x4b, //78 KEY_KPPLUS + 0x5a, //79 KEY_KP1 + 0x5b, //80 KEY_KP2 + 0x5c, //81 KEY_KP3 + 0x65, //82 KEY_KP0 + 0x66, //83 KEY_KPDOT + NONE, //84 ??? + NONE, //85 KEY_ZENKAKU + NONE, //86 KEY_102ND + 0x0b, //87 KEY_F11 + 0x0c, //88 KEY_F12 + NONE, //89 KEY_RO + NONE, //90 KEY_KATAKANA + NONE, //91 KEY_HIRAGANA + NONE, //92 KEY_HENKAN + NONE, //93 KEY_KATAKANA + NONE, //94 KEY_MUHENKAN + NONE, //95 KEY_KPJPCOMMA + 0x67, //96 KEY_KPENTER + 0x61, //97 KEY_RIGHTCTRL + 0x23, //98 KEY_KPSLASH + 0x0D, //99 KEY_SYSRQ + 0x60, //100 KEY_RIGHTALT + NONE, //101 KEY_LINEFEED + 0x20, //102 KEY_HOME + 0x59, //103 KEY_UP + 0x21, //104 KEY_PAGEUP + 0x62, //105 KEY_LEFT + 0x64, //106 KEY_RIGHT + 0x35, //107 KEY_END + 0x63, //108 KEY_DOWN + 0x36, //109 KEY_PAGEDOWN + 0x1f, //110 KEY_INSERT + 0x34, //111 KEY_DELETE + NONE, //112 KEY_MACRO + NONE, //113 KEY_MUTE + NONE, //114 KEY_VOLUMEDOWN + NONE, //115 KEY_VOLUMEUP + NONE, //116 KEY_POWER + NONE, //117 KEY_KPEQUAL + NONE, //118 KEY_KPPLUSMINUS + 0x0f, //119 KEY_PAUSE + NONE, //120 KEY_SCALE + NONE, //121 KEY_KPCOMMA + NONE, //122 KEY_HANGEUL + NONE, //123 KEY_HANJA + NONE, //124 KEY_YEN + NONE, //125 KEY_LEFTMETA + NONE, //126 KEY_RIGHTMETA + 0x71, //127 KEY_COMPOSE + NONE, //128 KEY_STOP + NONE, //129 KEY_AGAIN + NONE, //130 KEY_PROPS + NONE, //131 KEY_UNDO + NONE, //132 KEY_FRONT + NONE, //133 KEY_COPY + NONE, //134 KEY_OPEN + NONE, //135 KEY_PASTE + NONE, //136 KEY_FIND + NONE, //137 KEY_CUT + NONE, //138 KEY_HELP + NONE, //139 KEY_MENU + NONE, //140 KEY_CALC + NONE, //141 KEY_SETUP + NONE, //142 KEY_SLEEP + NONE, //143 KEY_WAKEUP + NONE, //144 KEY_FILE + NONE, //145 KEY_SENDFILE + NONE, //146 KEY_DELETEFILE + NONE, //147 KEY_XFER + NONE, //148 KEY_PROG1 + NONE, //149 KEY_PROG2 + NONE, //150 KEY_WWW + NONE, //151 KEY_MSDOS + NONE, //152 KEY_SCREENLOCK + NONE, //153 KEY_DIRECTION + NONE, //154 KEY_CYCLEWINDOWS + NONE, //155 KEY_MAIL + NONE, //156 KEY_BOOKMARKS + NONE, //157 KEY_COMPUTER + NONE, //158 KEY_BACK + NONE, //159 KEY_FORWARD + NONE, //160 KEY_CLOSECD + NONE, //161 KEY_EJECTCD + NONE, //162 KEY_EJECTCLOSECD + NONE, //163 KEY_NEXTSONG + NONE, //164 KEY_PLAYPAUSE + NONE, //165 KEY_PREVIOUSSONG + NONE, //166 KEY_STOPCD + NONE, //167 KEY_RECORD + NONE, //168 KEY_REWIND + NONE, //169 KEY_PHONE + NONE, //170 KEY_ISO + NONE, //171 KEY_CONFIG + NONE, //172 KEY_HOMEPAGE + NONE, //173 KEY_REFRESH + NONE, //174 KEY_EXIT + NONE, //175 KEY_MOVE + NONE, //176 KEY_EDIT + NONE, //177 KEY_SCROLLUP + NONE, //178 KEY_SCROLLDOWN + NONE, //179 KEY_KPLEFTPAREN + NONE, //180 KEY_KPRIGHTPAREN + NONE, //181 KEY_NEW + NONE, //182 KEY_REDO + NONE, //183 KEY_F13 + NONE, //184 KEY_F14 + NONE, //185 KEY_F15 + NONE, //186 KEY_F16 + NONE, //187 KEY_F17 + NONE, //188 KEY_F18 + NONE, //189 KEY_F19 + NONE, //190 KEY_F20 + NONE, //191 KEY_F21 + NONE, //192 KEY_F22 + NONE, //193 KEY_F23 + NONE, //194 KEY_F24 + NONE, //195 ??? + NONE, //196 ??? + NONE, //197 ??? + NONE, //198 ??? + NONE, //199 ??? + NONE, //200 KEY_PLAYCD + NONE, //201 KEY_PAUSECD + NONE, //202 KEY_PROG3 + NONE, //203 KEY_PROG4 + NONE, //204 KEY_DASHBOARD + NONE, //205 KEY_SUSPEND + NONE, //206 KEY_CLOSE + NONE, //207 KEY_PLAY + NONE, //208 KEY_FASTFORWARD + NONE, //209 KEY_BASSBOOST + NONE, //210 KEY_PRINT + NONE, //211 KEY_HP + NONE, //212 KEY_CAMERA + NONE, //213 KEY_SOUND + NONE, //214 KEY_QUESTION + NONE, //215 KEY_EMAIL + NONE, //216 KEY_CHAT + NONE, //217 KEY_SEARCH + NONE, //218 KEY_CONNECT + NONE, //219 KEY_FINANCE + NONE, //220 KEY_SPORT + NONE, //221 KEY_SHOP + NONE, //222 KEY_ALTERASE + NONE, //223 KEY_CANCEL + NONE, //224 KEY_BRIGHT_DOWN + NONE, //225 KEY_BRIGHT_UP + NONE, //226 KEY_MEDIA + NONE, //227 KEY_SWITCHVIDEO + NONE, //228 KEY_DILLUMTOGGLE + NONE, //229 KEY_DILLUMDOWN + NONE, //230 KEY_DILLUMUP + NONE, //231 KEY_SEND + NONE, //232 KEY_REPLY + NONE, //233 KEY_FORWARDMAIL + NONE, //234 KEY_SAVE + NONE, //235 KEY_DOCUMENTS + NONE, //236 KEY_BATTERY + NONE, //237 KEY_BLUETOOTH + NONE, //238 KEY_WLAN + NONE, //239 KEY_UWB + NONE, //240 KEY_UNKNOWN + NONE, //241 KEY_VIDEO_NEXT + NONE, //242 KEY_VIDEO_PREV + NONE, //243 KEY_BRIGHT_CYCLE + NONE, //244 KEY_BRIGHT_AUTO + NONE, //245 KEY_DISPLAY_OFF + NONE, //246 KEY_WWAN + NONE, //247 KEY_RFKILL + NONE, //248 KEY_MICMUTE + NONE, //249 ??? + NONE, //250 ??? + NONE, //251 ??? + NONE, //252 ??? + NONE, //253 ??? + NONE, //254 ??? + NONE //255 ??? +}; + +/* + +// unmapped atari keys: +// 0x63 KP ( +// 0x64 KP ) + +// keycode translation table for atari +const unsigned short usb2atari[] = { +MISS, // 00: NoEvent +MISS, // 01: Overrun Error +MISS, // 02: POST fail +MISS, // 03: ErrorUndefined +0x1e, // 04: a +0x30, // 05: b +0x2e, // 06: c +0x20, // 07: d +0x12, // 08: e +0x21, // 09: f +0x22, // 0a: g +0x23, // 0b: h +0x17, // 0c: i +0x24, // 0d: j +0x25, // 0e: k +0x26, // 0f: l +0x32, // 10: m +0x31, // 11: n +0x18, // 12: o +0x19, // 13: p +0x10, // 14: q +0x13, // 15: r +0x1f, // 16: s +0x14, // 17: t +0x16, // 18: u +0x2f, // 19: v +0x11, // 1a: w +0x2d, // 1b: x +0x15, // 1c: y +0x2c, // 1d: z +0x02, // 1e: 1 +0x03, // 1f: 2 +0x04, // 20: 3 +0x05, // 21: 4 +0x06, // 22: 5 +0x07, // 23: 6 +0x08, // 24: 7 +0x09, // 25: 8 +0x0a, // 26: 9 +0x0b, // 27: 0 +0x1c, // 28: Return +0x01, // 29: Escape +0x0e, // 2a: Backspace +0x0f, // 2b: Tab +0x39, // 2c: Space +0x0c, // 2d: - +0x0d, // 2e: = +0x1a, // 2f: [ +0x1b, // 30: ] +0x29, // 31: backslash, only on us keyboard +0x29, // 32: Europe 1, only on int. keyboard +0x27, // 33: ; +0x28, // 34: ' +0x2b, // 35: ` +0x33, // 36: , +0x34, // 37: . +0x35, // 38: / +0x3a | CAPS_LOCK_TOGGLE, // 39: Caps Lock +0x3b, // 3a: F1 +0x3c, // 3b: F2 +0x3d, // 3c: F3 +0x3e, // 3d: F4 +0x3f, // 3e: F5 +0x40, // 3f: F6 +0x41, // 40: F7 +0x42, // 41: F8 +0x43, // 42: F9 +0x44, // 43: F10 +MISS, // 44: F11 +OSD_OPEN, // 45: F12 +MISS, // 46: Print Screen +NUM_LOCK_TOGGLE, // 47: Scroll Lock +MISS, // 48: Pause +0x52, // 49: Insert +0x47, // 4a: Home +0x62, // 4b: Page Up +0x53, // 4c: Delete +MISS, // 4d: End +0x61, // 4e: Page Down +0x4d, // 4f: Right Arrow +0x4b, // 50: Left Arrow +0x50, // 51: Down Arrow +0x48, // 52: Up Arrow +NUM_LOCK_TOGGLE, // 53: Num Lock +0x65, // 54: KP / +0x66, // 55: KP * +0x4a, // 56: KP - +0x4e, // 57: KP + +0x72, // 58: KP Enter +0x6d, // 59: KP 1 +0x6e, // 5a: KP 2 +0x6f, // 5b: KP 3 +0x6a, // 5c: KP 4 +0x6b, // 5d: KP 5 +0x6c, // 5e: KP 6 +0x67, // 5f: KP 7 +0x68, // 60: KP 8 +0x69, // 61: KP 9 +0x70, // 62: KP 0 +0x71, // 63: KP . +0x60, // 64: Europe 2 +OSD_OPEN, // 65: App +MISS, // 66: Power +MISS, // 67: KP = +MISS, // 68: F13 +MISS, // 69: F14 +MISS, // 6a: F15 +0x52, // 6b: insert (for keyrah) +NUM_LOCK_TOGGLE | 1, // 6c: F17 +NUM_LOCK_TOGGLE | 2, // 6d: F18 +NUM_LOCK_TOGGLE | 3, // 6e: F19 +NUM_LOCK_TOGGLE | 4 // 6f: F20 +}; + +unsigned short modifier_keycode(unsigned char index) +{ +// usb modifer bits: +//0 1 2 3 4 5 6 7 +//LCTRL LSHIFT LALT LGUI RCTRL RSHIFT RALT RGUI + +if (core_type == CORE_TYPE_MIST) +{ +static const unsigned short atari_modifier[] = { 0x1d, 0x2a, 0x38, MISS, 0x1d, 0x36, 0x38, MISS }; +return atari_modifier[index]; +} +} +*/ + + +uint32_t get_ps2_code(uint16_t key) +{ + if (key > 255) return NONE; + return ev2ps2[key]; +} + +uint32_t get_amiga_code(uint16_t key) +{ + if (key > 255) return NONE; + return ev2amiga[key]; +} + +uint32_t get_atari_code(uint16_t key) +{ + if (key > 255) return NONE; + return 0; // ev2atari[key]; +} + +uint32_t get_archie_code(uint16_t key) +{ + if (key > 255) return NONE; + return ev2archie[key]; +} + +static uint32_t modifier = 0; + +uint32_t get_key_mod() +{ + return modifier & MODMASK; +} + +typedef struct +{ + uint16_t vid, pid; + uint8_t led; + uint8_t axis_state[256]; + + uint8_t num; + uint8_t has_map; + uint32_t map[32]; + + uint8_t has_mmap; + uint32_t mmap[32]; + uint16_t jkmap[1024]; + + uint8_t has_kbdmap; + uint8_t kbdmap[256]; + + int accx, accy; +} devInput; + +static devInput input[NUMDEV] = {}; + +#define BTN_NUM (sizeof(devInput::map) / sizeof(devInput::map[0])) + +int mfd = -1; +int mwd = -1; + +static int set_watch() +{ + mwd = -1; + mfd = inotify_init(); + if (mfd < 0) + { + printf("ERR: inotify_init"); + return -1; + } + + mwd = inotify_add_watch(mfd, "/dev/input", IN_MODIFY | IN_CREATE | IN_DELETE); + + if (mwd < 0) + { + printf("ERR: inotify_add_watch"); + return -1; + } + + return mfd; +} + +#define EVENT_SIZE ( sizeof (struct inotify_event) ) +#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) ) + +static int check_devs() +{ + int result = 0; + int length, i = 0; + char buffer[BUF_LEN]; + length = read(mfd, buffer, BUF_LEN); + + if (length < 0) + { + printf("ERR: read\n"); + return 0; + } + + while (ilen) + { + if (event->mask & IN_CREATE) + { + result = 1; + if (event->mask & IN_ISDIR) + { + printf("The directory %s was created.\n", event->name); + } + else + { + printf("The file %s was created.\n", event->name); + } + } + else if (event->mask & IN_DELETE) + { + result = 1; + if (event->mask & IN_ISDIR) + { + printf("The directory %s was deleted.\n", event->name); + } + else + { + printf("The file %s was deleted.\n", event->name); + } + } + /* + else if ( event->mask & IN_MODIFY ) + { + result = 1; + if ( event->mask & IN_ISDIR ) + { + printf( "The directory %s was modified.\n", event->name ); + } + else + { + printf( "The file %s was modified.\n", event->name ); + } + } + */ + } + i += EVENT_SIZE + event->len; + } + + return result; +} + +static void INThandler(int code) +{ + (void)code; + + printf("\nExiting...\n"); + + if (mwd >= 0) inotify_rm_watch(mfd, mwd); + if (mfd >= 0) close(mfd); + + exit(0); +} + +#define test_bit(bit, array) (array [bit / 8] & (1 << (bit % 8))) + +static char has_led(int fd) +{ + unsigned char evtype_b[(EV_MAX + 7) / 8]; + if (fd<0) return 0; + + memset(&evtype_b, 0, sizeof(evtype_b)); + if (ioctl(fd, EVIOCGBIT(0, sizeof(evtype_b)), evtype_b) < 0) + { + printf("ERR: evdev ioctl.\n"); + return 0; + } + + if (test_bit(EV_LED, evtype_b)) + { + printf("has LEDs.\n"); + return 1; + } + + return 0; +} + +static char leds_state = 0; +void set_kbdled(int mask, int state) +{ + leds_state = state ? leds_state | (mask&HID_LED_MASK) : leds_state & ~(mask&HID_LED_MASK); +} + +int get_kbdled(int mask) +{ + return (leds_state & (mask&HID_LED_MASK)) ? 1 : 0; +} + +int toggle_kbdled(int mask) +{ + int state = !get_kbdled(mask); + set_kbdled(mask, state); + return state; +} + +static int mapping = 0; +static int mapping_button; +static int mapping_dev; +static int mapping_type; +static int mapping_count; +static uint8_t mapping_key; + +static uint32_t tmp_axis[4]; +static int tmp_axis_n = 0; + +void start_map_setting(int cnt) +{ + mapping_button = 0; + mapping = 1; + mapping_dev = -1; + mapping_type = (cnt<0) ? 3 : cnt ? 1 : 2; + mapping_count = cnt; + tmp_axis_n = 0; + + if (mapping_type <= 1 && is_menu_core()) mapping_button = -6; + memset(tmp_axis, 0, sizeof(tmp_axis)); +} + +int get_map_button() +{ + return (mapping_type == 2) ? mapping_key : mapping_button; +} + +int get_map_type() +{ + return mapping_type; +} + +static char *get_map_name(int dev, int def) +{ + static char name[128]; + if (def || is_menu_core()) sprintf(name, "input_%04x_%04x_v2.map", input[dev].vid, input[dev].pid); + else sprintf(name, "%s_input_%04x_%04x_v2.map", user_io_get_core_name_ex(), input[dev].vid, input[dev].pid); + return name; +} + +static char *get_kbdmap_name(int dev) +{ + static char name[128]; + sprintf(name, "kbd_%04x_%04x.map", input[dev].vid, input[dev].pid); + return name; +} + +void finish_map_setting(int dismiss) +{ + mapping = 0; + if (mapping_dev<0) return; + + if (mapping_type == 2) + { + if (dismiss) input[mapping_dev].has_kbdmap = 0; + else FileSaveConfig(get_kbdmap_name(mapping_dev), &input[mapping_dev].kbdmap, sizeof(input[mapping_dev].kbdmap)); + } + else if (mapping_type == 3) + { + if (dismiss) memset(input[mapping_dev].jkmap, 0, sizeof(input[mapping_dev].jkmap)); + } + else + { + for (int i = 0; i < NUMDEV; i++) input[i].has_map = 0; + + if (mapping_button < 0) mapping_button = 0; + if (!is_menu_core()) for (uint i = mapping_button; i < BTN_NUM; i++) input[mapping_dev].map[i] = 0; + + if (!dismiss) FileSaveConfig(get_map_name(mapping_dev, 0), &input[mapping_dev].map, sizeof(input[mapping_dev].map)); + if (is_menu_core()) input[mapping_dev].has_mmap = 0; + } +} + +uint16_t get_map_vid() +{ + return (mapping && mapping_dev >= 0) ? input[mapping_dev].vid : 0; +} + +uint16_t get_map_pid() +{ + return (mapping && mapping_dev >= 0) ? input[mapping_dev].pid : 0; +} + +int has_default_map() +{ + return (mapping_dev >= 0) ? (input[mapping_dev].has_mmap == 1) : 0; +} + +static char kr_fn_table[] = +{ + KEY_KPSLASH, KEY_PAUSE, + KEY_KPASTERISK, KEY_PRINT, + KEY_LEFT, KEY_HOME, + KEY_RIGHT, KEY_END, + KEY_UP, KEY_PAGEUP, + KEY_DOWN, KEY_PAGEDOWN, + KEY_F1, KEY_F11, + KEY_F2, KEY_F12, + + KEY_F3, KEY_F17, // EMU_MOUSE + KEY_F4, KEY_F18, // EMU_JOY0 + KEY_F5, KEY_F19, // EMU_JOY1 + KEY_F6, KEY_F20, // EMU_NONE + + //Emulate keypad for A600 + KEY_1, KEY_KP1, + KEY_2, KEY_KP2, + KEY_3, KEY_KP3, + KEY_4, KEY_KP4, + KEY_5, KEY_KP5, + KEY_6, KEY_KP6, + KEY_7, KEY_KP7, + KEY_8, KEY_KP8, + KEY_9, KEY_KP9, + KEY_0, KEY_KP0, + KEY_MINUS, KEY_KPMINUS, + KEY_EQUAL, KEY_KPPLUS, + KEY_BACKSLASH, KEY_KPASTERISK, + KEY_LEFTBRACE, KEY_F13, //KP( + KEY_RIGHTBRACE, KEY_F14, //KP) + KEY_DOT, KEY_KPDOT, + KEY_ENTER, KEY_KPENTER +}; + +static int keyrah_trans(int key, int press) +{ + static int fn = 0; + + if (key == KEY_NUMLOCK) return KEY_F13; // numlock -> f13 + if (key == KEY_SCROLLLOCK) return KEY_F14; // scrlock -> f14 + if (key == KEY_INSERT) return KEY_F16; // insert -> f16. workaround! + + if (key == KEY_102ND) + { + if (!press && fn == 1) menu_key_set(KEY_MENU); + fn = press ? 1 : 0; + return 0; + } + else if (fn) + { + fn |= 2; + for (uint32_t n = 0; n<(sizeof(kr_fn_table) / (2 * sizeof(kr_fn_table[0]))); n++) + { + if ((key&255) == kr_fn_table[n * 2]) return kr_fn_table[(n * 2) + 1]; + } + } + + return key; +} + +#define KEY_EMU (KEY_MAX+1) + +#define AXIS1_X 24 +#define AXIS1_Y 25 +#define AXIS2_X 26 +#define AXIS2_Y 27 +#define AXIS_X 28 +#define AXIS_Y 29 +#define AXIS_MX 30 +#define AXIS_MY 31 + +static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int dev); + +static int kbd_toggle = 0; +static uint32_t joy[NUMPLAYERS] = {}, joy_prev[NUMPLAYERS] = {}; +static uint32_t autofire[NUMPLAYERS] = {}; +static uint32_t autofirecodes[NUMPLAYERS][BTN_NUM] = {}; +static int af_delay[NUMPLAYERS] = {}; + +static unsigned char mouse_btn = 0; +static int mouse_emu = 0; +static int kbd_mouse_emu = 0; +static int mouse_sniper = 0; +static int mouse_emu_x = 0; +static int mouse_emu_y = 0; + +static uint32_t mouse_timer = 0; + +#define BTN_TGL 100 +#define BTN_OSD 101 + +static void joy_digital(int jnum, uint32_t mask, uint32_t code, char press, int bnum) +{ + static char str[128]; + static uint32_t lastcode[NUMPLAYERS], lastmask[NUMPLAYERS]; + int num = jnum - 1; + + if (num < NUMPLAYERS) + { + if (jnum) + { + if (bnum != BTN_OSD && bnum != BTN_TGL) + { + if (!(mask & 0xF)) + { + if (press) + { + lastcode[num] = code; + lastmask[num] = mask; + } + else + { + lastcode[num] = 0; + lastmask[num] = 0; + } + } + } + else if (!user_io_osd_is_visible()) + { + if (lastcode[num]) + { + if (press) + { + int found = 0; + int zero = -1; + for (uint i = 0; i < BTN_NUM; i++) + { + if (!autofirecodes[num][i]) zero = i; + if (autofirecodes[num][i] == lastcode[num]) + { + found = 1; + autofirecodes[num][i] = 0; + break; + } + } + + if (!found && zero >= 0) autofirecodes[num][zero] = lastcode[num]; + autofire[num] = !found ? autofire[num] | lastmask[num] : autofire[num] & ~lastmask[num]; + + if (hasAPI1_5()) + { + if (!found) sprintf(str, "Auto fire: %d ms", af_delay[num] * 2); + else sprintf(str, "Auto fire: OFF"); + Info(str); + } + else InfoMessage((!found) ? "\n\n Auto fire\n ON" : + "\n\n Auto fire\n OFF"); + } + return; + } + else if (joy[num] & 0xF) + { + if (press) + { + if (joy[num] & 9) + { + af_delay[num] += 25 << ((joy[num] & 1) ? 1 : 0); + if (af_delay[num] > 500) af_delay[num] = 500; + } + else + { + af_delay[num] -= 25 << ((joy[num] & 2) ? 1 : 0); + if (af_delay[num] < 25) af_delay[num] = 25; + } + + static char str[256]; + + if (hasAPI1_5()) + { + sprintf(str, "Auto fire period: %d ms", af_delay[num] * 2); + Info(str); + } + else + { + sprintf(str, "\n\n Auto fire period\n %dms", af_delay[num] * 2); + InfoMessage(str); + } + } + return; + } + } + } + + if (bnum == BTN_TGL) + { + if(press) kbd_toggle = !kbd_toggle; + return; + } + + if (!user_io_osd_is_visible() && (bnum == BTN_OSD) && (mouse_emu & 1)) + { + if (press) + { + mouse_sniper = 0; + mouse_timer = 0; + mouse_btn = 0; + mouse_emu_x = 0; + mouse_emu_y = 0; + user_io_mouse(0, 0, 0); + + mouse_emu ^= 2; + if (hasAPI1_5()) Info((mouse_emu & 2) ? "Mouse mode ON" : "Mouse mode OFF"); + else InfoMessage((mouse_emu & 2) ? "\n\n Mouse mode lock\n ON" : + "\n\n Mouse mode lock\n OFF"); + } + return; + } + + if (user_io_osd_is_visible() || (bnum == BTN_OSD)) + { + memset(joy, 0, sizeof(joy)); + struct input_event ev; + ev.type = EV_KEY; + ev.value = press; + switch (mask) + { + case JOY_RIGHT: + ev.code = KEY_RIGHT; + break; + + case JOY_LEFT: + ev.code = KEY_LEFT; + break; + + case JOY_UP: + ev.code = KEY_UP; + break; + + case JOY_DOWN: + ev.code = KEY_DOWN; + break; + + case JOY_BTN1: + ev.code = KEY_ENTER; + break; + + case JOY_BTN2: + ev.code = KEY_ESC; + break; + + case JOY_BTN3: + ev.code = KEY_BACKSPACE; + break; + + default: + ev.code = (bnum == BTN_OSD) ? KEY_MENU : 0; + } + + input_cb(&ev, 0, 0); + } + else if(jnum) + { + if (press) joy[num] |= mask; + else joy[num] &= ~mask; + //user_io_digital_joystick(num, joy[num]); + + if (code) + { + int found = 0; + for (uint i = 0; i < BTN_NUM; i++) if (autofirecodes[num][i] == code) found = 1; + if (found) autofire[num] = press ? autofire[num] | mask : autofire[num] & ~mask; + } + } + } +} + +static void joy_analog(int num, int axis, int offset) +{ + static int pos[NUMPLAYERS][2] = {}; + + if (num > 0 && num < NUMPLAYERS+1) + { + num--; + pos[num][axis] = offset; + user_io_analog_joystick(num, (char)(pos[num][0]), (char)(pos[num][1])); + } +} + +static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int dev) +{ + static int key_mapped = 0; + + if (ev->type == EV_KEY && mapping && mapping_type == 3 && ev->code == input[dev].mmap[17]) ev->code = KEY_ENTER; + + int map_skip = (ev->type == EV_KEY && ev->code == KEY_SPACE && ((mapping_dev >= 0 && mapping_type==1) || mapping_button<0)); + int cancel = (ev->type == EV_KEY && ev->code == KEY_ESC); + int enter = (ev->type == EV_KEY && ev->code == KEY_ENTER); + int origcode = ev->code; + + //mouse + switch (ev->type) + { + case EV_KEY: + if (ev->code >= 272 && ev->code <= 279) + { + if (ev->value <= 1) + { + unsigned char mask = 1 << (ev->code - 272); + mouse_btn = (ev->value) ? mouse_btn | mask : mouse_btn & ~mask; + user_io_mouse(mouse_btn, 0, 0); + } + return; + } + break; + + case EV_REL: + { + int msval; + if (!cfg.mouse_throttle) cfg.mouse_throttle = 1; + + switch (ev->code) + { + case 0: + input[dev].accx += ev->value; + msval = input[dev].accx / cfg.mouse_throttle; + input[dev].accx -= msval * cfg.mouse_throttle; + + //printf("Mouse PosX: %d\n", msval); + user_io_mouse(mouse_btn, msval, 0); + return; + case 1: + input[dev].accy += ev->value; + msval = input[dev].accy / cfg.mouse_throttle; + input[dev].accy -= msval * cfg.mouse_throttle; + + //printf("Mouse PosY: %d\n", msval); + user_io_mouse(mouse_btn, 0, msval); + return; + } + } + break; + } + + if (!input[dev].has_map) + { + if (!FileLoadConfig(get_map_name(dev, 0), &input[dev].map, sizeof(input[dev].map))) + { + if (is_menu_core() || !FileLoadConfig(get_map_name(dev, 1), &input[dev].map, sizeof(input[dev].map))) + { + memset(input[dev].map, 0, sizeof(input[dev].map)); + input[dev].has_map++; + } + + //remove system controls from default map + for (uint i = 8; i < sizeof(input[0].map) / sizeof(input[0].map[0]); i++) input[dev].map[i] = 0; + input[dev].has_map++; + } + input[dev].has_map++; + } + + if (!input[dev].has_mmap) + { + if (!FileLoadConfig(get_map_name(dev, 1), &input[dev].mmap, sizeof(input[dev].mmap))) + { + memset(input[dev].mmap, 0, sizeof(input[dev].mmap)); + input[dev].has_mmap++; + } + input[dev].has_mmap++; + } + + //mapping + if (mapping && (mapping_dev >=0 || ev->value) && !cancel && !enter) + { if (is_menu_core()) spi_uio_cmd(UIO_KEYBOARD); //ping the Menu core to wakeup - - if (ev->type == EV_KEY && mapping_button>=0) - { - if (mapping_type == 2) - { - if (ev->value == 1 && ev->code < 256) - { - if(mapping_dev < 0) - { - mapping_dev = dev; - mapping_key = 0; - } - - if (!mapping_key) mapping_key = ev->code; - else - { - input[mapping_dev].kbdmap[mapping_key] = ev->code; - mapping_key = 0; - } - } - return; - } - else if (mapping_type == 3) - { - if (ev->value == 1) - { - if (!mapping_button) - { - if (mapping_dev < 0) mapping_dev = dev; - if (mapping_dev == dev && ev->code < 1024) mapping_button = ev->code; - } - else if (mapping_dev >= 0 && (ev->code<256 || mapping_dev == dev)) - { - // Technically it's hard to map the key to button as keyboards - // are all the same while joysticks are personalized and numbered. - input[mapping_dev].jkmap[mapping_button] = ev->code; - mapping_button = 0; - } - } - } - else if ((mapping_dev < 0) || ((ev->code >= 256) ? mapping_type : !mapping_type)) - { - if (mapping_dev < 0) - { - mapping_dev = dev; - mapping_type = (ev->code >= 256) ? 1 : 0; - } - - if (mapping_dev == dev && mapping_button < (is_menu_core() ? 17 : mapping_count)) - { - if (ev->value == 1) - { - if (!mapping_button) memset(input[dev].map, 0, sizeof(input[dev].map)); - - int found = 0; - for (int i = (is_menu_core() && mapping_button >= 8) ? 8 : 0; i < mapping_button; i++) if (input[dev].map[i] == ev->code) found = 1; - - if (!found) - { - input[dev].map[(mapping_button == 16 && is_menu_core()) ? 16 + mapping_type : mapping_button] = ev->code; - key_mapped = ev->code; - } - } - else if(ev->value == 0 && key_mapped == ev->code) - { - mapping_button++; - key_mapped = 0; - } - } - - return; - } - } - - //Define min-0-max analogs - int idx = 0; - if (is_menu_core()) - { - switch (mapping_button) - { - case 17: idx = AXIS_X; break; - case 18: idx = AXIS_Y; break; - case 19: idx = AXIS_MX; break; - case 20: idx = AXIS_MY; break; - case -4: idx = AXIS1_X; break; - case -3: idx = AXIS1_Y; break; - case -2: idx = AXIS2_X; break; - case -1: idx = AXIS2_Y; break; - } - - if (mapping_dev == dev || (mapping_dev < 0 && mapping_button < 0)) - { - int max = 0, min=0; - - if (ev->type == EV_ABS) - { - max = (ev->value == absinfo->maximum); - min = (ev->value == absinfo->minimum); - printf("min=%d,max=%d\n", min, max); - } - - //check DPAD horz - if (mapping_button == -6) - { - if (ev->type == EV_ABS && max) - { - if (mapping_dev < 0) mapping_dev = dev; - mapping_type = 1; - - if (absinfo->maximum > 2) - { - tmp_axis[tmp_axis_n++] = ev->code | 0x20000; - mapping_button++; - } - else - { - //Standard DPAD event - mapping_button += 2; - } - } - else if (ev->type == EV_KEY && ev->value == 1) - { - //DPAD uses simple button events - if (!map_skip) - { - mapping_button += 2; - if (mapping_dev < 0) mapping_dev = dev; - if (ev->code < 256) - { - // keyboard, skip stick 1/2 - mapping_button += 4; - mapping_type = 0; - } - } - } - } - //check DPAD vert - else if (mapping_button == -5) - { - if (ev->type == EV_ABS && max && absinfo->maximum > 1 && ev->code != (tmp_axis[0] & 0xFFFF)) - { - tmp_axis[tmp_axis_n++] = ev->code | 0x20000; - mapping_button++; - } - } - //Sticks - else if (ev->type == EV_ABS && idx) - { - if (mapping_dev < 0) mapping_dev = dev; - - if (idx && max && absinfo->maximum > 2) - { - if (mapping_button < 0) - { - int found = 0; - for (int i = 0; i < tmp_axis_n; i++) if (ev->code == (tmp_axis[i] & 0xFFFF)) found = 1; - if (!found) - { - mapping_type = 1; - tmp_axis[tmp_axis_n++] = ev->code | 0x20000; - //if (min) tmp_axis[idx - AXIS1_X] |= 0x10000; - mapping_button++; - if (tmp_axis_n >= 4) mapping_button = 0; - } - } - else - { - if (idx == AXIS_X || ev->code != (input[dev].map[idx - 1] & 0xFFFF)) - { - input[dev].map[idx] = ev->code | 0x20000; - //if (min) input[dev].map[idx] |= 0x10000; - mapping_button++; - } - } - } - } - } - } - - if (mapping_type <= 1 && map_skip && mapping_button < mapping_count) - { - if (ev->value == 1) - { - if (idx && mapping_dev >= 0) input[mapping_dev].map[idx] = 0; - mapping_button++; - if (mapping_button < 0 && (mapping_button&1)) mapping_button++; - } - } - - if (is_menu_core() && mapping_type <= 1 && mapping_dev >= 0) - { - memcpy(&input[mapping_dev].mmap[AXIS1_X], tmp_axis, sizeof(tmp_axis)); - memcpy(&input[mapping_dev].map[AXIS1_X], tmp_axis, sizeof(tmp_axis)); - } - } - else - { - key_mapped = 0; - switch (ev->type) - { - case EV_KEY: - if (ev->code < 1024 && input[dev].jkmap[ev->code] && !user_io_osd_is_visible()) ev->code = input[dev].jkmap[ev->code]; - - //joystick buttons, digital directions - if (ev->code >= 256) - { - if (!input[dev].num) - { - for (uint8_t num = 1; num < NUMDEV + 1; num++) - { - int found = 0; - for (int i = 0; i < NUMDEV; i++) - { - found = (input[i].num == num); - if (found) break; - } - - if (!found) - { - input[dev].num = num; - break; - } - } - } - - if (ev->code == input[dev].mmap[17]) - { - if (ev->value <= 1) joy_digital(input[dev].num, 0, 0, ev->value, BTN_OSD); - return; - } - - if (user_io_osd_is_visible()) - { - if (ev->value <= 1) - { - for (int i = 0; i <= 11; i++) - { - if (ev->code == input[dev].mmap[i]) - { - int n = i; - if (n >= 8) n -= 8; - joy_digital(0, 1 << n, 0, ev->value, n); - return; - } - } - - if (ev->code == input[dev].mmap[17]) - { - joy_digital(0, 0, 0, ev->value, BTN_OSD); - return; - } - } - } - else - { - if (mouse_emu) - { - int use_analog = (input[dev].mmap[AXIS_MX] || input[dev].mmap[AXIS_MY]); - - for (int i = (use_analog ? 12 : 8); i <= 14; i++) - { - if (ev->code == input[dev].mmap[i]) - { - switch (i) - { - case 8: - mouse_emu_x = ev->value ? 10 : 0; - break; - case 9: - mouse_emu_x = ev->value ? -10 : 0; - break; - case 10: - mouse_emu_y = ev->value ? 10 : 0; - break; - case 11: - mouse_emu_y = ev->value ? -10 : 0; - break; - - default: - mouse_btn = ev->value ? mouse_btn | 1 << (i - 12) : mouse_btn & ~(1 << (i - 12)); - user_io_mouse(mouse_btn, 0, 0); - break; - } - return; - } - } - - for (uint i = 0; i < BTN_NUM; i++) - { - if (ev->code == input[dev].map[i]) - { - if (ev->value <= 1) joy_digital(input[dev].num, 1 << i, 0, ev->value, i); - return; - } - } - } - else - { - if (input[dev].has_map == 2) - { - Info("This joystick is not defined\n Using default map"); - input[dev].has_map = 1; - } - - if (input[dev].has_map == 3) - { - Info("This joystick is not defined"); - input[dev].has_map = 1; - } - - for (uint i = 0; i < BTN_NUM; i++) - { - if (ev->code == input[dev].map[i]) - { - if (i <= 3 && origcode == ev->code) origcode = 0; // prevent autofire for original dpad - if (ev->value <= 1) joy_digital(input[dev].num, 1 << i, origcode, ev->value, i); - return; - } - } - - for (int i = 8; i <= 11; i++) - { - if (ev->code == input[dev].mmap[i]) - { - if (origcode == ev->code) origcode = 0; // prevent autofire for original dpad - if (ev->value <= 1) joy_digital(input[dev].num, 1 << (i - 8), origcode, ev->value, i - 8); - return; - } - } - } - - if (ev->code == input[dev].mmap[15] && (ev->value <= 1) && ((!(mouse_emu & 1)) ^ (!ev->value))) - { - mouse_emu = ev->value ? mouse_emu | 1 : mouse_emu & ~1; - printf("mouse_emu = %d\n", mouse_emu); - if (mouse_emu & 2) - { - mouse_sniper = ev->value; - } - else - { - mouse_timer = 0; - mouse_btn = 0; - mouse_emu_x = 0; - mouse_emu_y = 0; - user_io_mouse(0, 0, 0); - } - } - } - return; - } - // keyboard - else - { - if (!input[dev].has_kbdmap) - { - if (!FileLoadConfig(get_kbdmap_name(dev), &input[dev].kbdmap, sizeof(input[dev].kbdmap))) - { - memset(input[dev].kbdmap, 0, sizeof(input[dev].kbdmap)); - } - input[dev].has_kbdmap = 1; - } - - uint16_t code = ev->code; - if (code < 256 && input[dev].kbdmap[code]) code = input[dev].kbdmap[code]; - - // replace MENU key by RGUI to allow using Right Amiga on reduced keyboards - // (it also disables the use of Menu for OSD) - if (cfg.key_menu_as_rgui && code == 139) code = 126; - - //Keyrah v2: USB\VID_18D8&PID_0002\A600/A1200_MULTIMEDIA_EXTENSION_VERSION - int keyrah = (cfg.keyrah_mode && (((((uint32_t)input[dev].vid) << 16) | input[dev].pid) == cfg.keyrah_mode)); - if (keyrah) code = keyrah_trans(code, ev->value); - - uint32_t ps2code = get_ps2_code(code); - if (ev->value) modifier |= ps2code; - else modifier &= ~ps2code; - - uint16_t reset_m = (modifier & MODMASK) >> 8; - if (code == 111) reset_m |= 0x100; - user_io_check_reset(reset_m, (keyrah && !cfg.reset_combo) ? 1 : cfg.reset_combo); - - if(!user_io_osd_is_visible() && ((user_io_get_kbdemu() == EMU_JOY0) || (user_io_get_kbdemu() == EMU_JOY1))) - { - if (!kbd_toggle) - { - for (uint i = 0; i < BTN_NUM; i++) - { - if (ev->code == input[dev].map[i]) - { - if (i <= 3 && origcode == ev->code) origcode = 0; // prevent autofire for original dpad - if (ev->value <= 1) joy_digital((user_io_get_kbdemu() == EMU_JOY0) ? 1 : 2, 1 << i, origcode, ev->value, i); - return; - } - } - } - - if (ev->code == input[dev].mmap[16]) - { - if (ev->value <= 1) joy_digital((user_io_get_kbdemu() == EMU_JOY0) ? 1 : 2, 0, 0, ev->value, BTN_TGL); - return; - } - } - else - { - kbd_toggle = 0; - } - - if (!user_io_osd_is_visible() && (user_io_get_kbdemu() == EMU_MOUSE)) - { - if (kbd_mouse_emu) - { - for (int i = 8; i <= 14; i++) - { - if (ev->code == input[dev].mmap[i]) - { - switch (i) - { - case 8: - mouse_emu_x = ev->value ? 10 : 0; - break; - case 9: - mouse_emu_x = ev->value ? -10 : 0; - break; - case 10: - mouse_emu_y = ev->value ? 10 : 0; - break; - case 11: - mouse_emu_y = ev->value ? -10 : 0; - break; - - default: - mouse_btn = ev->value ? mouse_btn | 1 << (i - 12) : mouse_btn & ~(1 << (i - 12)); - user_io_mouse(mouse_btn, 0, 0); - break; - } - return; - } - } - - if (ev->code == input[dev].mmap[15]) - { - if (ev->value <= 1) mouse_sniper = ev->value; - return; - } - } - - if (ev->code == input[dev].mmap[16]) - { - if (ev->value == 1) - { - kbd_mouse_emu = !kbd_mouse_emu; - printf("kbd_mouse_emu = %d\n", kbd_mouse_emu); - - mouse_timer = 0; - mouse_btn = 0; - mouse_emu_x = 0; - mouse_emu_y = 0; - user_io_mouse(0, 0, 0); - } - return; - } - } - - user_io_kbd(code, ev->value); - return; - } - break; - - //analog joystick - case EV_ABS: - if (!user_io_osd_is_visible()) - { - // TODO: implement inversion - - //convert to 0..255 range - int value = ((ev->value - absinfo->minimum) * 256) / (absinfo->maximum - absinfo->minimum + 1); - value = (value < 127 || value>129) ? value - 128 : 0; - if (value < -127) value = -127; - //printf("ABS: axis %d = %d -> %d\n", ev->code, ev->value, value); - - else if (ev->code == (input[dev].mmap[AXIS_MX] & 0xFFFF) && mouse_emu) - { - mouse_emu_x = 0; - if (value < -1 || value>1) mouse_emu_x = value; - mouse_emu_x /= 12; - return; - } - else if (ev->code == (input[dev].mmap[AXIS_MY] & 0xFFFF) && mouse_emu) - { - mouse_emu_y = 0; - if (value < -1 || value>1) mouse_emu_y = value; - mouse_emu_y /= 12; - return; - } - else if (ev->code == (input[dev].mmap[AXIS_X] & 0xFFFF)) - { - // skip if first joystick is not defined. - if (!input[dev].num) break; - - int offset = 0; - if (value < -1 || value>1) offset = value; - //printf("analog_x = %d\n", offset); - joy_analog(input[dev].num, 0, offset); - return; - } - else if (ev->code == (input[dev].mmap[AXIS_Y] & 0xFFFF)) - { - // skip if first joystick is not defined. - if (!input[dev].num) break; - - int offset = 0; - if (value < -1 || value>1) offset = value; - //printf("analog_y = %d\n", offset); - joy_analog(input[dev].num, 1, offset); - return; - } - } - break; - } - } -} - -static uint16_t read_hex(char *filename) -{ - FILE *in; - unsigned int value; - - in = fopen(filename, "rb"); - if (!in) return 0; - - if (fscanf(in, "%x", &value) == 1) - { - fclose(in); - return (uint16_t)value; - } - fclose(in); - return 0; -} - -static void getVidPid(int num, uint16_t* vid, uint16_t* pid) -{ - char name[256]; - sprintf(name, "/sys/class/input/event%d/device/id/vendor", num); - *vid = read_hex(name); - sprintf(name, "/sys/class/input/event%d/device/id/product", num); - *pid = read_hex(name); -} - -static struct pollfd pool[NUMDEV + 1]; - -int input_test(int getchar) -{ - static char cur_leds = 0; - static int state = 0; - struct input_absinfo absinfo; - - char devname[20]; - struct input_event ev; - - if (state == 0) - { - signal(SIGINT, INThandler); - pool[NUMDEV].fd = set_watch(); - pool[NUMDEV].events = POLLIN; - state++; - } - - if (state == 1) - { - printf("Open up to %d input devices.\n", NUMDEV); - for (int i = 0; i 0) getVidPid(i, &input[i].vid, &input[i].pid); - if (pool[i].fd > 0) printf("opened %s (%04x:%04x)\n", devname, input[i].vid, input[i].pid); - } - - cur_leds |= 0x80; - state++; - } - - if (state == 2) - { - int return_value = poll(pool, NUMDEV + 1, 0); - if (return_value < 0) - { - printf("ERR: poll\n"); - } - else if (return_value > 0) - { - if ((pool[NUMDEV].revents & POLLIN) && check_devs()) - { - printf("Close all devices.\n"); - for (int i = 0; i= 0) close(pool[i].fd); - } - state = 1; - return 0; - } - - for (int i = 0; i= 0) && (pool[i].revents & POLLIN)) - { - memset(&ev, 0, sizeof(ev)); - if (read(pool[i].fd, &ev, sizeof(ev)) == sizeof(ev)) - { - if (getchar) - { - if (ev.type == EV_KEY && ev.value >= 1) - { - return ev.code; - } - } - else if(ev.type) - { - if (ev.type == EV_ABS) - { - int len = ioctl(pool[i].fd, EVIOCGABS(ev.code), &absinfo); - if (len < 0) - { - memset(&absinfo, 0, sizeof(absinfo)); - } - } - - if (is_menu_core()) - { - /* - if (mapping && mapping_type <= 1 && !(ev.type==EV_KEY && ev.value>1)) - { - static char str[64], str2[64]; - OsdWrite(12, "\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81"); - sprintf(str, " VID=%04X PID=%04X", input[i].vid, input[i].pid); - OsdWrite(13, str); - - sprintf(str, "Type=%d Code=%d Value=%d", ev.type, ev.code, ev.value); + + if (ev->type == EV_KEY && mapping_button>=0) + { + if (mapping_type == 2) + { + if (ev->value == 1 && ev->code < 256) + { + if(mapping_dev < 0) + { + mapping_dev = dev; + mapping_key = 0; + } + + if (!mapping_key) mapping_key = ev->code; + else + { + input[mapping_dev].kbdmap[mapping_key] = ev->code; + mapping_key = 0; + } + } + return; + } + else if (mapping_type == 3) + { + if (ev->value == 1) + { + if (!mapping_button) + { + if (mapping_dev < 0) mapping_dev = dev; + if (mapping_dev == dev && ev->code < 1024) mapping_button = ev->code; + } + else if (mapping_dev >= 0 && (ev->code<256 || mapping_dev == dev)) + { + // Technically it's hard to map the key to button as keyboards + // are all the same while joysticks are personalized and numbered. + input[mapping_dev].jkmap[mapping_button] = ev->code; + mapping_button = 0; + } + } + } + else if ((mapping_dev < 0) || ((ev->code >= 256) ? mapping_type : !mapping_type)) + { + if (mapping_dev < 0) + { + mapping_dev = dev; + mapping_type = (ev->code >= 256) ? 1 : 0; + } + + if (mapping_dev == dev && mapping_button < (is_menu_core() ? 17 : mapping_count)) + { + if (ev->value == 1) + { + if (!mapping_button) memset(input[dev].map, 0, sizeof(input[dev].map)); + + int found = 0; + for (int i = (is_menu_core() && mapping_button >= 8) ? 8 : 0; i < mapping_button; i++) if (input[dev].map[i] == ev->code) found = 1; + + if (!found) + { + input[dev].map[(mapping_button == 16 && is_menu_core()) ? 16 + mapping_type : mapping_button] = ev->code; + key_mapped = ev->code; + } + } + else if(ev->value == 0 && key_mapped == ev->code) + { + mapping_button++; + key_mapped = 0; + } + } + + return; + } + } + + //Define min-0-max analogs + int idx = 0; + if (is_menu_core()) + { + switch (mapping_button) + { + case 17: idx = AXIS_X; break; + case 18: idx = AXIS_Y; break; + case 19: idx = AXIS_MX; break; + case 20: idx = AXIS_MY; break; + case -4: idx = AXIS1_X; break; + case -3: idx = AXIS1_Y; break; + case -2: idx = AXIS2_X; break; + case -1: idx = AXIS2_Y; break; + } + + if (mapping_dev == dev || (mapping_dev < 0 && mapping_button < 0)) + { + int max = 0, min=0; + + if (ev->type == EV_ABS) + { + max = (ev->value == absinfo->maximum); + min = (ev->value == absinfo->minimum); + printf("min=%d,max=%d\n", min, max); + } + + //check DPAD horz + if (mapping_button == -6) + { + if (ev->type == EV_ABS && max) + { + if (mapping_dev < 0) mapping_dev = dev; + mapping_type = 1; + + if (absinfo->maximum > 2) + { + tmp_axis[tmp_axis_n++] = ev->code | 0x20000; + mapping_button++; + } + else + { + //Standard DPAD event + mapping_button += 2; + } + } + else if (ev->type == EV_KEY && ev->value == 1) + { + //DPAD uses simple button events + if (!map_skip) + { + mapping_button += 2; + if (mapping_dev < 0) mapping_dev = dev; + if (ev->code < 256) + { + // keyboard, skip stick 1/2 + mapping_button += 4; + mapping_type = 0; + } + } + } + } + //check DPAD vert + else if (mapping_button == -5) + { + if (ev->type == EV_ABS && max && absinfo->maximum > 1 && ev->code != (tmp_axis[0] & 0xFFFF)) + { + tmp_axis[tmp_axis_n++] = ev->code | 0x20000; + mapping_button++; + } + } + //Sticks + else if (ev->type == EV_ABS && idx) + { + if (mapping_dev < 0) mapping_dev = dev; + + if (idx && max && absinfo->maximum > 2) + { + if (mapping_button < 0) + { + int found = 0; + for (int i = 0; i < tmp_axis_n; i++) if (ev->code == (tmp_axis[i] & 0xFFFF)) found = 1; + if (!found) + { + mapping_type = 1; + tmp_axis[tmp_axis_n++] = ev->code | 0x20000; + //if (min) tmp_axis[idx - AXIS1_X] |= 0x10000; + mapping_button++; + if (tmp_axis_n >= 4) mapping_button = 0; + } + } + else + { + if (idx == AXIS_X || ev->code != (input[dev].map[idx - 1] & 0xFFFF)) + { + input[dev].map[idx] = ev->code | 0x20000; + //if (min) input[dev].map[idx] |= 0x10000; + mapping_button++; + } + } + } + } + } + } + + if (mapping_type <= 1 && map_skip && mapping_button < mapping_count) + { + if (ev->value == 1) + { + if (idx && mapping_dev >= 0) input[mapping_dev].map[idx] = 0; + mapping_button++; + if (mapping_button < 0 && (mapping_button&1)) mapping_button++; + } + } + + if (is_menu_core() && mapping_type <= 1 && mapping_dev >= 0) + { + memcpy(&input[mapping_dev].mmap[AXIS1_X], tmp_axis, sizeof(tmp_axis)); + memcpy(&input[mapping_dev].map[AXIS1_X], tmp_axis, sizeof(tmp_axis)); + } + } + else + { + key_mapped = 0; + switch (ev->type) + { + case EV_KEY: + if (ev->code < 1024 && input[dev].jkmap[ev->code] && !user_io_osd_is_visible()) ev->code = input[dev].jkmap[ev->code]; + + //joystick buttons, digital directions + if (ev->code >= 256) + { + if (!input[dev].num) + { + for (uint8_t num = 1; num < NUMDEV + 1; num++) + { + int found = 0; + for (int i = 0; i < NUMDEV; i++) + { + found = (input[i].num == num); + if (found) break; + } + + if (!found) + { + input[dev].num = num; + break; + } + } + } + + if (ev->code == input[dev].mmap[17]) + { + if (ev->value <= 1) joy_digital(input[dev].num, 0, 0, ev->value, BTN_OSD); + return; + } + + if (user_io_osd_is_visible()) + { + if (ev->value <= 1) + { + for (int i = 0; i <= 11; i++) + { + if (ev->code == input[dev].mmap[i]) + { + int n = i; + if (n >= 8) n -= 8; + joy_digital(0, 1 << n, 0, ev->value, n); + return; + } + } + + if (ev->code == input[dev].mmap[17]) + { + joy_digital(0, 0, 0, ev->value, BTN_OSD); + return; + } + } + } + else + { + if (mouse_emu) + { + int use_analog = (input[dev].mmap[AXIS_MX] || input[dev].mmap[AXIS_MY]); + + for (int i = (use_analog ? 12 : 8); i <= 14; i++) + { + if (ev->code == input[dev].mmap[i]) + { + switch (i) + { + case 8: + mouse_emu_x = ev->value ? 10 : 0; + break; + case 9: + mouse_emu_x = ev->value ? -10 : 0; + break; + case 10: + mouse_emu_y = ev->value ? 10 : 0; + break; + case 11: + mouse_emu_y = ev->value ? -10 : 0; + break; + + default: + mouse_btn = ev->value ? mouse_btn | 1 << (i - 12) : mouse_btn & ~(1 << (i - 12)); + user_io_mouse(mouse_btn, 0, 0); + break; + } + return; + } + } + + for (uint i = 0; i < BTN_NUM; i++) + { + if (ev->code == input[dev].map[i]) + { + if (ev->value <= 1) joy_digital(input[dev].num, 1 << i, 0, ev->value, i); + return; + } + } + } + else + { + if (input[dev].has_map == 2) + { + Info("This joystick is not defined\n Using default map"); + input[dev].has_map = 1; + } + + if (input[dev].has_map == 3) + { + Info("This joystick is not defined"); + input[dev].has_map = 1; + } + + for (uint i = 0; i < BTN_NUM; i++) + { + if (ev->code == input[dev].map[i]) + { + if (i <= 3 && origcode == ev->code) origcode = 0; // prevent autofire for original dpad + if (ev->value <= 1) joy_digital(input[dev].num, 1 << i, origcode, ev->value, i); + return; + } + } + + for (int i = 8; i <= 11; i++) + { + if (ev->code == input[dev].mmap[i]) + { + if (origcode == ev->code) origcode = 0; // prevent autofire for original dpad + if (ev->value <= 1) joy_digital(input[dev].num, 1 << (i - 8), origcode, ev->value, i - 8); + return; + } + } + } + + if (ev->code == input[dev].mmap[15] && (ev->value <= 1) && ((!(mouse_emu & 1)) ^ (!ev->value))) + { + mouse_emu = ev->value ? mouse_emu | 1 : mouse_emu & ~1; + printf("mouse_emu = %d\n", mouse_emu); + if (mouse_emu & 2) + { + mouse_sniper = ev->value; + } + else + { + mouse_timer = 0; + mouse_btn = 0; + mouse_emu_x = 0; + mouse_emu_y = 0; + user_io_mouse(0, 0, 0); + } + } + } + return; + } + // keyboard + else + { + if (!input[dev].has_kbdmap) + { + if (!FileLoadConfig(get_kbdmap_name(dev), &input[dev].kbdmap, sizeof(input[dev].kbdmap))) + { + memset(input[dev].kbdmap, 0, sizeof(input[dev].kbdmap)); + } + input[dev].has_kbdmap = 1; + } + + uint16_t code = ev->code; + if (code < 256 && input[dev].kbdmap[code]) code = input[dev].kbdmap[code]; + + // replace MENU key by RGUI to allow using Right Amiga on reduced keyboards + // (it also disables the use of Menu for OSD) + if (cfg.key_menu_as_rgui && code == 139) code = 126; + + //Keyrah v2: USB\VID_18D8&PID_0002\A600/A1200_MULTIMEDIA_EXTENSION_VERSION + int keyrah = (cfg.keyrah_mode && (((((uint32_t)input[dev].vid) << 16) | input[dev].pid) == cfg.keyrah_mode)); + if (keyrah) code = keyrah_trans(code, ev->value); + + uint32_t ps2code = get_ps2_code(code); + if (ev->value) modifier |= ps2code; + else modifier &= ~ps2code; + + uint16_t reset_m = (modifier & MODMASK) >> 8; + if (code == 111) reset_m |= 0x100; + user_io_check_reset(reset_m, (keyrah && !cfg.reset_combo) ? 1 : cfg.reset_combo); + + if(!user_io_osd_is_visible() && ((user_io_get_kbdemu() == EMU_JOY0) || (user_io_get_kbdemu() == EMU_JOY1))) + { + if (!kbd_toggle) + { + for (uint i = 0; i < BTN_NUM; i++) + { + if (ev->code == input[dev].map[i]) + { + if (i <= 3 && origcode == ev->code) origcode = 0; // prevent autofire for original dpad + if (ev->value <= 1) joy_digital((user_io_get_kbdemu() == EMU_JOY0) ? 1 : 2, 1 << i, origcode, ev->value, i); + return; + } + } + } + + if (ev->code == input[dev].mmap[16]) + { + if (ev->value <= 1) joy_digital((user_io_get_kbdemu() == EMU_JOY0) ? 1 : 2, 0, 0, ev->value, BTN_TGL); + return; + } + } + else + { + kbd_toggle = 0; + } + + if (!user_io_osd_is_visible() && (user_io_get_kbdemu() == EMU_MOUSE)) + { + if (kbd_mouse_emu) + { + for (int i = 8; i <= 14; i++) + { + if (ev->code == input[dev].mmap[i]) + { + switch (i) + { + case 8: + mouse_emu_x = ev->value ? 10 : 0; + break; + case 9: + mouse_emu_x = ev->value ? -10 : 0; + break; + case 10: + mouse_emu_y = ev->value ? 10 : 0; + break; + case 11: + mouse_emu_y = ev->value ? -10 : 0; + break; + + default: + mouse_btn = ev->value ? mouse_btn | 1 << (i - 12) : mouse_btn & ~(1 << (i - 12)); + user_io_mouse(mouse_btn, 0, 0); + break; + } + return; + } + } + + if (ev->code == input[dev].mmap[15]) + { + if (ev->value <= 1) mouse_sniper = ev->value; + return; + } + } + + if (ev->code == input[dev].mmap[16]) + { + if (ev->value == 1) + { + kbd_mouse_emu = !kbd_mouse_emu; + printf("kbd_mouse_emu = %d\n", kbd_mouse_emu); + + mouse_timer = 0; + mouse_btn = 0; + mouse_emu_x = 0; + mouse_emu_y = 0; + user_io_mouse(0, 0, 0); + } + return; + } + } + + user_io_kbd(code, ev->value); + return; + } + break; + + //analog joystick + case EV_ABS: + if (!user_io_osd_is_visible()) + { + // TODO: implement inversion + + //convert to 0..255 range + int value = ((ev->value - absinfo->minimum) * 256) / (absinfo->maximum - absinfo->minimum + 1); + value = (value < 127 || value>129) ? value - 128 : 0; + if (value < -127) value = -127; + //printf("ABS: axis %d = %d -> %d\n", ev->code, ev->value, value); + + else if (ev->code == (input[dev].mmap[AXIS_MX] & 0xFFFF) && mouse_emu) + { + mouse_emu_x = 0; + if (value < -1 || value>1) mouse_emu_x = value; + mouse_emu_x /= 12; + return; + } + else if (ev->code == (input[dev].mmap[AXIS_MY] & 0xFFFF) && mouse_emu) + { + mouse_emu_y = 0; + if (value < -1 || value>1) mouse_emu_y = value; + mouse_emu_y /= 12; + return; + } + else if (ev->code == (input[dev].mmap[AXIS_X] & 0xFFFF)) + { + // skip if first joystick is not defined. + if (!input[dev].num) break; + + int offset = 0; + if (value < -1 || value>1) offset = value; + //printf("analog_x = %d\n", offset); + joy_analog(input[dev].num, 0, offset); + return; + } + else if (ev->code == (input[dev].mmap[AXIS_Y] & 0xFFFF)) + { + // skip if first joystick is not defined. + if (!input[dev].num) break; + + int offset = 0; + if (value < -1 || value>1) offset = value; + //printf("analog_y = %d\n", offset); + joy_analog(input[dev].num, 1, offset); + return; + } + } + break; + } + } +} + +static uint16_t read_hex(char *filename) +{ + FILE *in; + unsigned int value; + + in = fopen(filename, "rb"); + if (!in) return 0; + + if (fscanf(in, "%x", &value) == 1) + { + fclose(in); + return (uint16_t)value; + } + fclose(in); + return 0; +} + +static void getVidPid(int num, uint16_t* vid, uint16_t* pid) +{ + char name[256]; + sprintf(name, "/sys/class/input/event%d/device/id/vendor", num); + *vid = read_hex(name); + sprintf(name, "/sys/class/input/event%d/device/id/product", num); + *pid = read_hex(name); +} + +static struct pollfd pool[NUMDEV + 1]; + +int input_test(int getchar) +{ + static char cur_leds = 0; + static int state = 0; + struct input_absinfo absinfo; + + char devname[20]; + struct input_event ev; + + if (state == 0) + { + signal(SIGINT, INThandler); + pool[NUMDEV].fd = set_watch(); + pool[NUMDEV].events = POLLIN; + state++; + } + + if (state == 1) + { + printf("Open up to %d input devices.\n", NUMDEV); + for (int i = 0; i 0) getVidPid(i, &input[i].vid, &input[i].pid); + if (pool[i].fd > 0) printf("opened %s (%04x:%04x)\n", devname, input[i].vid, input[i].pid); + } + + cur_leds |= 0x80; + state++; + } + + if (state == 2) + { + int return_value = poll(pool, NUMDEV + 1, 0); + if (return_value < 0) + { + printf("ERR: poll\n"); + } + else if (return_value > 0) + { + if ((pool[NUMDEV].revents & POLLIN) && check_devs()) + { + printf("Close all devices.\n"); + for (int i = 0; i= 0) close(pool[i].fd); + } + state = 1; + return 0; + } + + for (int i = 0; i= 0) && (pool[i].revents & POLLIN)) + { + memset(&ev, 0, sizeof(ev)); + if (read(pool[i].fd, &ev, sizeof(ev)) == sizeof(ev)) + { + if (getchar) + { + if (ev.type == EV_KEY && ev.value >= 1) + { + return ev.code; + } + } + else if(ev.type) + { + if (ev.type == EV_ABS) + { + int len = ioctl(pool[i].fd, EVIOCGABS(ev.code), &absinfo); + if (len < 0) + { + memset(&absinfo, 0, sizeof(absinfo)); + } + } + + if (is_menu_core()) + { + /* + if (mapping && mapping_type <= 1 && !(ev.type==EV_KEY && ev.value>1)) + { + static char str[64], str2[64]; + OsdWrite(12, "\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81"); + sprintf(str, " VID=%04X PID=%04X", input[i].vid, input[i].pid); + OsdWrite(13, str); + + sprintf(str, "Type=%d Code=%d Value=%d", ev.type, ev.code, ev.value); str2[0] = 0; int len = (29 - (strlen(str))) / 2; while (len-- > 0) strcat(str2, " "); - strcat(str2, str); - OsdWrite(14, str2); - + strcat(str2, str); + OsdWrite(14, str2); + str2[0] = 0; - if (ev.type == EV_ABS) - { - sprintf(str, "Min=%d Max=%d", absinfo.minimum, absinfo.maximum); + if (ev.type == EV_ABS) + { + sprintf(str, "Min=%d Max=%d", absinfo.minimum, absinfo.maximum); int len = (29 - (strlen(str))) / 2; while (len-- > 0) strcat(str2, " "); - strcat(str2, str); - } - OsdWrite(15, str2); - } - */ - - switch (ev.type) - { - //keyboard, buttons - case EV_KEY: - printf("Input event: type=EV_KEY, code=%d(0x%x), value=%d, jnum=%d, ID:%04x:%04x\n", ev.code, ev.code, ev.value, input[i].num, input[i].vid, input[i].pid); - break; - - //mouse - case EV_REL: - printf("Input event: type=EV_REL, Axis=%d, Offset:=%d, jnum=%d, ID:%04x:%04x\n", ev.code, ev.value, input[i].num, input[i].vid, input[i].pid); - break; - - case EV_SYN: - case EV_MSC: - break; - - //analog joystick - case EV_ABS: - if (ev.code == 62) break; - if (ev.code == 61) break; //ps3 accel axis - if (ev.code == 60) break; //ps3 accel axis - if (ev.code == 59) break; //ps3 accel axis - - //reduce flood from PS3 gamepad - if (input[i].vid == 0x054c && input[i].pid == 0x0268) - { if (ev.code <= 5 && ev.value > 118 && ev.value < 138) break; } - - //aliexpress USB encoder floods messages - if (input[i].vid == 0x0079 && input[i].pid == 0x0006) - { if (ev.code == 2) break; } - - printf("Input event: type=EV_ABS, Axis=%d, Offset:=%d, jnum=%d, ID:%04x:%04x.", ev.code, ev.value, input[i].num, input[i].vid, input[i].pid); - printf(" ABS_INFO: min = %d max = %d", absinfo.minimum, absinfo.maximum); - if (absinfo.fuzz) printf(" fuzz = %d", absinfo.fuzz); - if (absinfo.resolution) printf(" res = %d", absinfo.resolution); - printf("\n"); - break; - - default: - printf("Input event: type=%d, code=%d(0x%x), value=%d(0x%x), jnum=%d, ID:%04x:%04x\n", ev.type, ev.code, ev.code, ev.value, ev.value, input[i].num, input[i].vid, input[i].pid); - } - } - - //Menu combo on 8BitDo receiver in PSC mode - if (input[i].vid == 0x054c && input[i].pid == 0x0cda && ev.type == EV_KEY) - { - if (ev.code == 164) ev.code = KEY_MENU; - if (ev.code == 1) ev.code = KEY_MENU; - } - - input_cb(&ev, &absinfo, i); - - //sumulate digital directions from analog - if (ev.type == EV_ABS && !(mapping && mapping_type<=1 && mapping_button<-4)) - { - uint8_t axis_state = 0; - if ((absinfo.maximum == 1 && absinfo.minimum == -1) || (absinfo.maximum == 2 && absinfo.minimum == 0)) - { - if (ev.value == absinfo.minimum) axis_state = 1; - if (ev.value == absinfo.maximum) axis_state = 2; - } - else - { - int range = absinfo.maximum - absinfo.minimum + 1; - int center = absinfo.minimum + (range / 2); - int treshold = range / 4; - - int only_max = 1; - for (int n = 0; n < 4; n++) if (input[i].mmap[AXIS1_X + n] && ((input[i].mmap[AXIS1_X + n] & 0xFFFF) == ev.code)) only_max = 0; - - if (ev.value < center - treshold && !only_max) axis_state = 1; - if (ev.value > center + treshold) axis_state = 2; - } - - uint8_t last_state = input[i].axis_state[ev.code & 255]; - input[i].axis_state[ev.code & 255] = axis_state; - - //printf("last_state=%d, axis_state=%d\n", last_state, axis_state); - if (last_state != axis_state) - { - uint16_t ecode = KEY_EMU + (ev.code << 1) - 1; - ev.type = EV_KEY; - if (last_state) - { - ev.value = 0; - ev.code = ecode + last_state; - input_cb(&ev, 0, i); - } - - if (axis_state) - { - ev.value = 1; - ev.code = ecode + axis_state; - input_cb(&ev, 0, i); - } - } - - // Menu button on 8BitDo Receiver in D-Input mode - if (ev.code == 9 && input[i].vid == 0x2dc8 && (input[i].pid == 0x3100 || input[i].pid == 0x3104)) - { - ev.type = EV_KEY; - ev.code = KEY_EMU + (ev.code << 1); - input_cb(&ev, &absinfo, i); - } - } - } - } - } - } - } - - if (cur_leds != leds_state) - { - cur_leds = leds_state; - for (int i = 0; i 2) dx = 2; - if (dx < -2) dx = -2; - if (dy > 2) dy = 2; - if (dy < -2) dy = -2; - } - - user_io_mouse(mouse_btn, dx, dy); - prev_dx = mouse_emu_x; - prev_dy = mouse_emu_y; - } - } - - if (!mouse_emu_x && !mouse_emu_y) mouse_timer = 0; - - for (int i = 0; i < NUMPLAYERS; i++) - { - if (!af_delay[i]) af_delay[i] = 50; - - if (!time[i]) time[i] = GetTimer(af_delay[i]); - int send = 0; - - int newdir = ((joy[i] & 0xF) != (joy_prev[i] & 0xF)); - if (joy[i] != joy_prev[i]) - { - if ((joy[i] ^ joy_prev[i]) & autofire[i]) - { - time[i] = GetTimer(af_delay[i]); - af[i] = 0; - } - - send = 1; - joy_prev[i] = joy[i]; - } - - if (CheckTimer(time[i])) - { - time[i] = GetTimer(af_delay[i]); - af[i] = !af[i]; - if (joy[i] & autofire[i]) send = 1; - } - - if (send) - { - user_io_digital_joystick(i, af[i] ? joy[i] & ~autofire[i] : joy[i], newdir); - } - } - - return 0; -} - -int is_key_pressed(int key) -{ - unsigned char bits[(KEY_MAX + 7) / 8]; - for (int i = 0; i < NUMDEV; i++) - { - if (pool[i].fd > 0) - { - unsigned long evbit = 0; - if (ioctl(pool[i].fd, EVIOCGBIT(0, sizeof(evbit)), &evbit) >= 0) - { - if (evbit & (1 << EV_KEY)) - { - memset(bits, 0, sizeof(bits)); - if (ioctl(pool[i].fd, EVIOCGKEY(sizeof(bits)), &bits) >= 0) - { - if (bits[key / 8] & (1 << (key % 8))) - { - return 1; - } - } - } - } - } - } - - return 0; -} - -void input_notify_mode() -{ - //reset mouse parameters on any mode switch - kbd_mouse_emu = 1; - mouse_sniper = 0; - mouse_timer = 0; - mouse_btn = 0; - mouse_emu_x = 0; - mouse_emu_y = 0; - user_io_mouse(0, 0, 0); -} + strcat(str2, str); + } + OsdWrite(15, str2); + } + */ + + switch (ev.type) + { + //keyboard, buttons + case EV_KEY: + printf("Input event: type=EV_KEY, code=%d(0x%x), value=%d, jnum=%d, ID:%04x:%04x\n", ev.code, ev.code, ev.value, input[i].num, input[i].vid, input[i].pid); + break; + + //mouse + case EV_REL: + printf("Input event: type=EV_REL, Axis=%d, Offset:=%d, jnum=%d, ID:%04x:%04x\n", ev.code, ev.value, input[i].num, input[i].vid, input[i].pid); + break; + + case EV_SYN: + case EV_MSC: + break; + + //analog joystick + case EV_ABS: + if (ev.code == 62) break; + if (ev.code == 61) break; //ps3 accel axis + if (ev.code == 60) break; //ps3 accel axis + if (ev.code == 59) break; //ps3 accel axis + + //reduce flood from PS3 gamepad + if (input[i].vid == 0x054c && input[i].pid == 0x0268) + { if (ev.code <= 5 && ev.value > 118 && ev.value < 138) break; } + + //aliexpress USB encoder floods messages + if (input[i].vid == 0x0079 && input[i].pid == 0x0006) + { if (ev.code == 2) break; } + + printf("Input event: type=EV_ABS, Axis=%d, Offset:=%d, jnum=%d, ID:%04x:%04x.", ev.code, ev.value, input[i].num, input[i].vid, input[i].pid); + printf(" ABS_INFO: min = %d max = %d", absinfo.minimum, absinfo.maximum); + if (absinfo.fuzz) printf(" fuzz = %d", absinfo.fuzz); + if (absinfo.resolution) printf(" res = %d", absinfo.resolution); + printf("\n"); + break; + + default: + printf("Input event: type=%d, code=%d(0x%x), value=%d(0x%x), jnum=%d, ID:%04x:%04x\n", ev.type, ev.code, ev.code, ev.value, ev.value, input[i].num, input[i].vid, input[i].pid); + } + } + + //Menu combo on 8BitDo receiver in PSC mode + if (input[i].vid == 0x054c && input[i].pid == 0x0cda && ev.type == EV_KEY) + { + if (ev.code == 164) ev.code = KEY_MENU; + if (ev.code == 1) ev.code = KEY_MENU; + } + + input_cb(&ev, &absinfo, i); + + //sumulate digital directions from analog + if (ev.type == EV_ABS && !(mapping && mapping_type<=1 && mapping_button<-4)) + { + uint8_t axis_state = 0; + if ((absinfo.maximum == 1 && absinfo.minimum == -1) || (absinfo.maximum == 2 && absinfo.minimum == 0)) + { + if (ev.value == absinfo.minimum) axis_state = 1; + if (ev.value == absinfo.maximum) axis_state = 2; + } + else + { + int range = absinfo.maximum - absinfo.minimum + 1; + int center = absinfo.minimum + (range / 2); + int treshold = range / 4; + + int only_max = 1; + for (int n = 0; n < 4; n++) if (input[i].mmap[AXIS1_X + n] && ((input[i].mmap[AXIS1_X + n] & 0xFFFF) == ev.code)) only_max = 0; + + if (ev.value < center - treshold && !only_max) axis_state = 1; + if (ev.value > center + treshold) axis_state = 2; + } + + uint8_t last_state = input[i].axis_state[ev.code & 255]; + input[i].axis_state[ev.code & 255] = axis_state; + + //printf("last_state=%d, axis_state=%d\n", last_state, axis_state); + if (last_state != axis_state) + { + uint16_t ecode = KEY_EMU + (ev.code << 1) - 1; + ev.type = EV_KEY; + if (last_state) + { + ev.value = 0; + ev.code = ecode + last_state; + input_cb(&ev, 0, i); + } + + if (axis_state) + { + ev.value = 1; + ev.code = ecode + axis_state; + input_cb(&ev, 0, i); + } + } + + // Menu button on 8BitDo Receiver in D-Input mode + if (ev.code == 9 && input[i].vid == 0x2dc8 && (input[i].pid == 0x3100 || input[i].pid == 0x3104)) + { + ev.type = EV_KEY; + ev.code = KEY_EMU + (ev.code << 1); + input_cb(&ev, &absinfo, i); + } + } + } + } + } + } + } + + if (cur_leds != leds_state) + { + cur_leds = leds_state; + for (int i = 0; i 2) dx = 2; + if (dx < -2) dx = -2; + if (dy > 2) dy = 2; + if (dy < -2) dy = -2; + } + + user_io_mouse(mouse_btn, dx, dy); + prev_dx = mouse_emu_x; + prev_dy = mouse_emu_y; + } + } + + if (!mouse_emu_x && !mouse_emu_y) mouse_timer = 0; + + for (int i = 0; i < NUMPLAYERS; i++) + { + if (!af_delay[i]) af_delay[i] = 50; + + if (!time[i]) time[i] = GetTimer(af_delay[i]); + int send = 0; + + int newdir = ((joy[i] & 0xF) != (joy_prev[i] & 0xF)); + if (joy[i] != joy_prev[i]) + { + if ((joy[i] ^ joy_prev[i]) & autofire[i]) + { + time[i] = GetTimer(af_delay[i]); + af[i] = 0; + } + + send = 1; + joy_prev[i] = joy[i]; + } + + if (CheckTimer(time[i])) + { + time[i] = GetTimer(af_delay[i]); + af[i] = !af[i]; + if (joy[i] & autofire[i]) send = 1; + } + + if (send) + { + user_io_digital_joystick(i, af[i] ? joy[i] & ~autofire[i] : joy[i], newdir); + } + } + + return 0; +} + +int is_key_pressed(int key) +{ + unsigned char bits[(KEY_MAX + 7) / 8]; + for (int i = 0; i < NUMDEV; i++) + { + if (pool[i].fd > 0) + { + unsigned long evbit = 0; + if (ioctl(pool[i].fd, EVIOCGBIT(0, sizeof(evbit)), &evbit) >= 0) + { + if (evbit & (1 << EV_KEY)) + { + memset(bits, 0, sizeof(bits)); + if (ioctl(pool[i].fd, EVIOCGKEY(sizeof(bits)), &bits) >= 0) + { + if (bits[key / 8] & (1 << (key % 8))) + { + return 1; + } + } + } + } + } + } + + return 0; +} + +void input_notify_mode() +{ + //reset mouse parameters on any mode switch + kbd_mouse_emu = 1; + mouse_sniper = 0; + mouse_timer = 0; + mouse_btn = 0; + mouse_emu_x = 0; + mouse_emu_y = 0; + user_io_mouse(0, 0, 0); +} diff --git a/input.h b/input.h index a44beda..6b497b5 100644 --- a/input.h +++ b/input.h @@ -2,24 +2,24 @@ #ifndef EVINPUT_H #define EVINPUT_H -#include +#include #define HID_LED_NUM_LOCK 1 #define HID_LED_CAPS_LOCK 2 #define HID_LED_SCROLL_LOCK 4 #define HID_LED_MASK 7 -#define NONE 0xFF -#define LCTRL 0x000100 -#define LSHIFT 0x000200 -#define LALT 0x000400 -#define LGUI 0x000800 -#define RCTRL 0x001000 -#define RSHIFT 0x002000 -#define RALT 0x004000 -#define RGUI 0x008000 -#define MODMASK 0x00FF00 - +#define NONE 0xFF +#define LCTRL 0x000100 +#define LSHIFT 0x000200 +#define LALT 0x000400 +#define LGUI 0x000800 +#define RCTRL 0x001000 +#define RSHIFT 0x002000 +#define RALT 0x004000 +#define RGUI 0x008000 +#define MODMASK 0x00FF00 + #define OSD 0x010000 // to be used by OSD, not the core itself #define OSD_OPEN 0x020000 // OSD key not forwarded to core, but queued in arm controller #define CAPS_TOGGLE 0x040000 // caps lock toggle behaviour @@ -33,16 +33,16 @@ void set_kbdled(int mask, int state); int get_kbdled(int mask); int toggle_kbdled(int mask); -void input_notify_mode(); +void input_notify_mode(); int input_poll(int getchar); -int is_key_pressed(int key); +int is_key_pressed(int key); -void start_map_setting(int cnt); -int get_map_button(); -int get_map_type(); -void finish_map_setting(int dismiss); -uint16_t get_map_vid(); -uint16_t get_map_pid(); +void start_map_setting(int cnt); +int get_map_button(); +int get_map_type(); +void finish_map_setting(int dismiss); +uint16_t get_map_vid(); +uint16_t get_map_pid(); int has_default_map(); uint32_t get_key_mod(); diff --git a/logo.h b/logo.h index 89a1cdd..cec749c 100644 --- a/logo.h +++ b/logo.h @@ -1,101 +1,101 @@ #ifndef LOGO_H #define LOGO_H const unsigned char logodata[6][227] = { - { - 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, - 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, - 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, - 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, - 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, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, - 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, - 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xE0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x80, 0xC0, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, - 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, - 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, - 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, - 0xE0, 0xE0, 0xE0, 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, 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, 0x80, - 0xE0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFE, - 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x7F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF0, 0xF8, 0xF9, 0xF9, 0xF9, 0xFB, - 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0x19, 0x01, 0x01, 0x00, 0x00, 0x00, 0x3C, 0x7F, 0x7F, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF3, 0xF3, 0xE1, 0xE1, 0xE1, 0xC1, - 0xC1, 0xC3, 0x83, 0x83, 0x03, 0x03, 0x03, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xC3, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x83, 0xC0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0x78, 0x78, 0x38, 0x38, - 0x38, 0x38, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x00, - 0x00, 0x00, 0x80, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xD8, - 0xE0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0x78, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF0, 0xFE, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, - 0x07, 0x01, 0x80, 0xE0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, - 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x7F, 0x1F, 0x03, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0, - 0xE0, 0xE1, 0xC1, 0xC1, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x7F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x7E, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, - 0xCE, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xC7, 0xC7, 0xE7, 0x27, 0x03, 0x03, 0x01, 0x00, 0x00, 0xC0, - 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0x07, 0x03, 0x01, - 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, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xE0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x80, 0xC0, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE0, 0xE0, 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, 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, 0x80, + 0xE0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFE, + 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x7F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF0, 0xF8, 0xF9, 0xF9, 0xF9, 0xFB, + 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0x19, 0x01, 0x01, 0x00, 0x00, 0x00, 0x3C, 0x7F, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF3, 0xF3, 0xE1, 0xE1, 0xE1, 0xC1, + 0xC1, 0xC3, 0x83, 0x83, 0x03, 0x03, 0x03, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xC3, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x83, 0xC0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0x78, 0x78, 0x38, 0x38, + 0x38, 0x38, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xD8, + 0xE0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0x78, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF0, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, + 0x07, 0x01, 0x80, 0xE0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, + 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x7F, 0x1F, 0x03, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0, + 0xE0, 0xE1, 0xC1, 0xC1, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x7F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x7E, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, + 0xCE, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xC7, 0xC7, 0xE7, 0x27, 0x03, 0x03, 0x01, 0x00, 0x00, 0xC0, + 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0x07, 0x03, 0x01, + 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, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 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, 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; #endif diff --git a/main.cpp b/main.cpp index fdcb48f..0ff3202 100644 --- a/main.cpp +++ b/main.cpp @@ -1,74 +1,74 @@ -/* -Copyright 2005, 2006, 2007 Dennis van Weeren -Copyright 2008, 2009 Jakub Bednarski -Copyright 2012 Till Harbaum - -This file is part of Minimig - -Minimig is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -Minimig 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include -#include -#include -#include +/* +Copyright 2005, 2006, 2007 Dennis van Weeren +Copyright 2008, 2009 Jakub Bednarski +Copyright 2012 Till Harbaum + +This file is part of Minimig + +Minimig is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Minimig 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include #include #include -#include -#include "menu.h" -#include "user_io.h" -#include "input.h" -#include "fpga_io.h" -#include "scheduler.h" - -const char *version = "$VER:HPS" VDATE; - -int main(int argc, char *argv[]) -{ +#include +#include "menu.h" +#include "user_io.h" +#include "input.h" +#include "fpga_io.h" +#include "scheduler.h" + +const char *version = "$VER:HPS" VDATE; + +int main(int argc, char *argv[]) +{ // Always pin main worker process to core #1 as core #0 is the // hardware interrupt handler in Linux. This reduces idle latency // in the main loop by about 6-7x. - cpu_set_t set; - CPU_ZERO(&set); - CPU_SET(1, &set); - sched_setaffinity(0, sizeof(set), &set); - - fpga_io_init(); - fpga_gpo_write(0); - - DISKLED_OFF; - - printf("\nMinimig by Dennis van Weeren"); - printf("\nARM Controller by Jakub Bednarski"); - printf("\nMiSTer code by Sorgelig\n\n"); - - printf("Version %s\n\n", version + 5); - - if (argc > 1) printf("Core path: %s\n", argv[1]); - - if (!is_fpga_ready(1)) - { - printf("\nGPI[31]==1. FPGA is uninitialized or incompatible core loaded.\n"); - printf("Quitting. Bye bye...\n"); - exit(0); - } - - FindStorage(); - user_io_init((argc > 1) ? argv[1] : ""); - - scheduler_init(); - scheduler_run(); - - return 0; -} + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(1, &set); + sched_setaffinity(0, sizeof(set), &set); + + fpga_io_init(); + fpga_gpo_write(0); + + DISKLED_OFF; + + printf("\nMinimig by Dennis van Weeren"); + printf("\nARM Controller by Jakub Bednarski"); + printf("\nMiSTer code by Sorgelig\n\n"); + + printf("Version %s\n\n", version + 5); + + if (argc > 1) printf("Core path: %s\n", argv[1]); + + if (!is_fpga_ready(1)) + { + printf("\nGPI[31]==1. FPGA is uninitialized or incompatible core loaded.\n"); + printf("Quitting. Bye bye...\n"); + exit(0); + } + + FindStorage(); + user_io_init((argc > 1) ? argv[1] : ""); + + scheduler_init(); + scheduler_run(); + + return 0; +} diff --git a/menu.cpp b/menu.cpp index 67cc7c6..779d6be 100644 --- a/menu.cpp +++ b/menu.cpp @@ -652,14 +652,14 @@ const char* get_rbf_name_bootcore(char *str) char *p = strrchr(str, '/'); if (!p) return str; - char *spl = strrchr(p + 1, '.'); - if (spl && !strcmp(spl, ".rbf")) - { - *spl = 0; - } - else - { - return NULL; + char *spl = strrchr(p + 1, '.'); + if (spl && !strcmp(spl, ".rbf")) + { + *spl = 0; + } + else + { + return NULL; } return p + 1; diff --git a/menu.h b/menu.h index fb7f0bc..ee00b0e 100644 --- a/menu.h +++ b/menu.h @@ -12,20 +12,20 @@ extern const char *config_hdf_msg[]; extern const char *config_chipset_msg[]; void HandleUI(void); -void menu_key_set(unsigned int c); -void PrintFileName(char *name, int row, int maxinv); +void menu_key_set(unsigned int c); +void PrintFileName(char *name, int row, int maxinv); void PrintDirectory(void); void ScrollLongName(void); void ErrorMessage(const char *message, unsigned char code); -void InfoMessage(const char *message, int timeout = 2000); -void Info(const char *message, int timeout = 2000, int width = 0, int height = 0, int frame = 0); - -uint32_t getStatus(char *opt, uint32_t status); -void substrcpy(char *d, char *s, char idx); - -extern char joy_bnames[32][32]; -extern int joy_bcount; +void InfoMessage(const char *message, int timeout = 2000); +void Info(const char *message, int timeout = 2000, int width = 0, int height = 0, int frame = 0); + +uint32_t getStatus(char *opt, uint32_t status); +void substrcpy(char *d, char *s, char idx); + +extern char joy_bnames[32][32]; +extern int joy_bcount; void open_joystick_setup(); diff --git a/osd.cpp b/osd.cpp index fdbcf2b..89dc669 100644 --- a/osd.cpp +++ b/osd.cpp @@ -1,692 +1,692 @@ -/* -Copyright 2005, 2006, 2007 Dennis van Weeren -Copyright 2008, 2009 Jakub Bednarski - -This file is part of Minimig - -Minimig is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -Minimig 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -This is the Minimig OSD (on-screen-display) handler. - -2012-02-09 - Split character rom out to separate header file, with upper 128 entries -as rotated copies of the first 128 entries. -- AMR - -29-12-2006 - created -30-12-2006 - improved and simplified --- JB -- -2008-10-04 - ARM version -2008-10-26 - added cpu and floppy configuration functions -2008-12-31 - added enable HDD command -2009-02-03 - full keyboard support -2009-06-23 - hires OSD display -2009-08-23 - adapted ConfigIDE() - support for 2 hardfiles -*/ - -#include -#include -#include -#include - -#include "osd.h" -#include "spi.h" - -#include "charrom.h" -#include "logo.h" -#include "user_io.h" -#include "hardware.h" - -#include "support.h" - -static int osd_size = 8; - -void OsdSetSize(int n) -{ - osd_size = n; -} - -int OsdGetSize() -{ - return osd_size; -} - -struct star -{ - int x, y; - int dx, dy; -}; - -struct star stars[64]; - -char framebuffer[16][256]; -void framebuffer_clear() -{ - memset(framebuffer, 0, sizeof(framebuffer)); -} - -void framebuffer_plot(int x, int y) -{ - framebuffer[y / 8][x] |= (1 << (y & 7)); -} - -void StarsInit() -{ - srand(time(NULL)); - for (int i = 0; i<64; ++i) - { - stars[i].x = (rand() % 228) << 4; // X centre - stars[i].y = (rand() % 128) << 4; // Y centre - stars[i].dx = -(rand() & 7) - 3; - stars[i].dy = 0; - } -} - -void StarsUpdate() -{ - framebuffer_clear(); - for (int i = 0; i<64; ++i) - { - stars[i].x += stars[i].dx; - stars[i].y += stars[i].dy; - if ((stars[i].x<0) || (stars[i].x>(228 << 4)) || - (stars[i].y<0) || (stars[i].y>(128 << 4))) - { - stars[i].x = 228 << 4; - stars[i].y = (rand() % 128) << 4; - stars[i].dx = -(rand() & 7) - 3; - stars[i].dy = 0; - } - framebuffer_plot(stars[i].x >> 4, stars[i].y >> 4); - } -} - - -// time delay after which file/dir name starts to scroll -#define SCROLL_DELAY 1000 -#define SCROLL_DELAY2 10 -#define SCROLL_DELAY3 50 - -static unsigned long scroll_offset = 0; // file/dir name scrolling position -static unsigned long scroll_timer = 0; // file/dir name scrolling timer - -static int arrow; -static unsigned char titlebuffer[256]; - -static void rotatechar(unsigned char *in, unsigned char *out) -{ - int a; - int b; - int c; - for (b = 0; b<8; ++b) - { - a = 0; - for (c = 0; c<8; ++c) - { - a <<= 1; - a |= (in[c] >> b) & 1; - } - out[b] = a; - } -} - -#define OSDHEIGHT (uint)(osd_size*8) - -void OsdSetTitle(const char *s, int a) -{ - // Compose the title, condensing character gaps - arrow = a; - int zeros = 0; - uint i = 0, j = 0; - uint outp = 0; - while (1) - { - int c = s[i++]; - if (c && (outpsizeof(titlebuffer)) break; - } - } - else break; - } - for (i = outp; i to the OSD buffer starting at line -void OsdWriteOffset(unsigned char n, const char *s, unsigned char invert, unsigned char stipple, char offset, char leftchar, char usebg, int maxinv) -{ - //printf("OsdWriteOffset(%d)\n", n); - unsigned short i; - unsigned char b; - const unsigned char *p; - unsigned char stipplemask = 0xff; - int linelimit = OSDLINELEN; - int arrowmask = arrow; - if (n == (osd_size-1) && (arrow & OSD_ARROW_RIGHT)) - linelimit -= 22; - - if (stipple) { - stipplemask = 0x55; - stipple = 0xff; - } - else - stipple = 0; - - // select buffer and line to write to - if (!is_minimig()) - spi_osd_cmd_cont(MM1_OSDCMDWRITE | n); - else - spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n); - - if (invert) invert = 255; - - i = 0; - // send all characters in string to OSD - while (1) - { - if (invert && i / 8 >= maxinv) invert = 0; - if (i == 0 && (n < osd_size)) - { // Render sidestripe - unsigned char j; - unsigned char tmp[8]; - - if (leftchar) - { - unsigned char tmp2[8]; - memcpy(tmp2, charfont[(uint)leftchar], 8); - rotatechar(tmp2, tmp); - p = tmp; - } - else - { - p = &titlebuffer[(osd_size - 1 - n) * 8]; - } - - spi16(0xffff); // left white border - - for (j = 0; j < 8; j++) - spi_n(255 ^ *p++, 2); - - spi16(0xffff); // right white border - spi16(0x0000); // blue gap - i += 22; - } - else if (n == (osd_size-1) && (arrowmask & OSD_ARROW_LEFT)) { // Draw initial arrow - unsigned char b; - - spi24((invert<<16)| (invert << 8) |invert); - p = &charfont[0x10][0]; - for (b = 0; b<8; b++) spi8((*p++ << offset) ^ invert); - p = &charfont[0x14][0]; - for (b = 0; b<8; b++) spi8((*p++ << offset) ^ invert); - spi24((invert << 16) | (invert << 8) | invert); - spi_n(invert, 2); - i += 24; - arrowmask &= ~OSD_ARROW_LEFT; - if (*s++ == 0) break; // Skip 3 characters, to keep alignent the same. - if (*s++ == 0) break; - if (*s++ == 0) break; - } - else { - b = *s++; - - if (b == 0) // end of string - break; - - else if (b == 0x0d || b == 0x0a) { // cariage return / linefeed, go to next line - // increment line counter - if (++n >= linelimit) - n = 0; - - // send new line number to OSD - DisableOsd(); - - if (!is_minimig()) - spi_osd_cmd_cont(MM1_OSDCMDWRITE | n); - else - spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n); - } - else if (i<(linelimit - 8)) { // normal character - unsigned char c; - p = &charfont[b][0]; - for (c = 0; c<8; c++) { - char bg = usebg ? framebuffer[n][i+c-22] : 0; - spi8((((*p++ << offset)&stipplemask) ^ invert) | bg); - stipplemask ^= stipple; - } - i += 8; - } - } - } - - for (; i < linelimit; i++) // clear end of line - { - char bg = usebg ? framebuffer[n][i-22] : 0; - spi8(invert | bg); - } - - if (n == (osd_size-1) && (arrowmask & OSD_ARROW_RIGHT)) - { // Draw final arrow if needed - unsigned char c; - spi24((invert << 16) | (invert << 8) | invert); - p = &charfont[0x15][0]; - for (c = 0; c<8; c++) spi8((*p++ << offset) ^ invert); - p = &charfont[0x11][0]; - for (c = 0; c<8; c++) spi8((*p++ << offset) ^ invert); - spi24((invert << 16) | (invert << 8) | invert); - i += 22; - } - - // deselect OSD SPI device - DisableOsd(); -} - -void OsdDrawLogo(int row) -{ - unsigned short i; - const unsigned char *p; - int linelimit = OSDLINELEN; - - int mag = (osd_size / 8); - uint n = row * mag; - - // select buffer and line to write to - if (!is_minimig()) - { - spi_osd_cmd_cont(MM1_OSDCMDWRITE | n); - } - else - { - spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n); - } - - for (int k = 0; k < mag; k++) - { - unsigned char bt = 0; - const unsigned char *lp = logodata[row]; - int bytes = sizeof(logodata[0]); - if ((uint)row >= (sizeof(logodata) / sizeof(logodata[0]))) lp = 0; - - char *bg = framebuffer[n + k]; - - i = 0; - while(i < linelimit) - { - if (i == 0) - { - unsigned char j; - p = &titlebuffer[(osd_size - 1 - n - k) * 8]; - spi16(0xffff); // left white border - for (j = 0; j<8; j++) spi_n(255 ^ *p++, 2); - spi16(0xffff); // right white border - spi16(0x0000); // blue gap - i += 22; - } - - if(lp && bytes) - { - bt = *lp++; - if(mag > 1) - { - if (k) bt >>= 4; - bt = (bt & 1) | ((bt & 1) << 1) | ((bt & 2) << 1) | ((bt & 2) << 2) | ((bt & 4) << 2) | ((bt & 4) << 3) | ((bt & 8) << 3) | ((bt & 8) << 4); - } - bytes--; - } - - spi8(bt | *bg++); - ++i; - } - } - - // deselect OSD SPI device - DisableOsd(); -} - -// write a null-terminated string to the OSD buffer starting at line -void OSD_PrintText(unsigned char line, const char *text, unsigned long start, unsigned long width, unsigned long offset, unsigned char invert) -{ - // line : OSD line number (0-7) - // text : pointer to null-terminated string - // start : start position (in pixels) - // width : printed text length in pixels - // offset : scroll offset in pixels counting from the start of the string (0-7) - // invert : invertion flag - - const unsigned char *p; - int i, j; - - // select buffer and line to write to - if (!is_minimig()) - spi_osd_cmd_cont(MM1_OSDCMDWRITE | line); - else - spi_osd_cmd32_cont(OSD_CMD_OSD_WR, line); - - if (invert) - invert = 0xff; - - p = &titlebuffer[(osd_size - 1 - line) * 8]; - if (start>2) { - spi16(0xffff); - start -= 2; - } - - i = start>16 ? 16 : start; - for (j = 0; j<(i / 2); ++j) - spi_n(255 ^ *p++, 2); - - if (i & 1) - spi8(255 ^ *p); - start -= i; - - if (start>2) { - spi16(0xffff); - start -= 2; - } - - while (start--) - spi8(0x00); - - if (offset) { - width -= 8 - offset; - p = &charfont[(uint)(*text++)][offset]; - for (; offset < 8; offset++) - spi8(*p++^invert); - } - - while (width > 8) { - unsigned char b; - p = &charfont[(uint)(*text++)][0]; - for (b = 0; b<8; b++) spi8(*p++^invert); - width -= 8; - } - - if (width) { - p = &charfont[(uint)(*text++)][0]; - while (width--) - spi8(*p++^invert); - } - - DisableOsd(); -} - -#define INFO_MAXW 32 -#define INFO_MAXH 16 - -void OSD_PrintInfo(const char *message, int *width, int *height, int frame) -{ - static char str[INFO_MAXW * INFO_MAXH]; - memset(str, ' ', sizeof(str)); - - // calc height/width if none provided. Add frame to calculated size. - // no frame will be added if width and height are provided. - int calc = !*width || !*height || frame; - - int maxw = 0; - int x = calc ? 1 : 0; - int y = calc ? 1 : 0; - while (*message) - { - char c = *message++; - if (c == 0xD) continue; - if (c == 0xA) - { - x = calc ? 1 : 0; - y++; - continue; - } - - if (x < INFO_MAXW && y < INFO_MAXH) str[(y*INFO_MAXW) + x] = c; - - x++; - if (x > maxw) maxw = x; - } - - int w = !calc ? *width + 2 : maxw+1; - if (w > INFO_MAXW) w = INFO_MAXW; - *width = w; - - int h = !calc ? *height + 2 : y+2; - if (h > INFO_MAXH) h = INFO_MAXH; - *height = h; - - if (frame) - { - frame = (frame - 1) * 6; - for (x = 1; x < w - 1; x++) - { - str[(0 * INFO_MAXW) + x] = 0x81+frame; - str[((h - 1)*INFO_MAXW) + x] = 0x81 + frame; - } - for (y = 1; y < h - 1; y++) - { - str[(y * INFO_MAXW)] = 0x83 + frame; - str[(y * INFO_MAXW) + w - 1] = 0x83 + frame; - } - str[0] = 0x80 + frame; - str[w - 1] = 0x82 + frame; - str[(h - 1)*INFO_MAXW] = 0x85 + frame; - str[((h - 1)*INFO_MAXW) + w - 1] = 0x84 + frame; - } - - for (y = 0; y < h; y++) - { - if (!is_minimig()) - spi_osd_cmd_cont(MM1_OSDCMDWRITE | y); - else - spi_osd_cmd32_cont(OSD_CMD_OSD_WR, y); - - for (x = 0; x < w; x++) - { - const unsigned char *p = charfont[(uint)str[(y*INFO_MAXW) + x]]; - for (int i = 0; i < 8; i++) spi8(*p++); - } - - DisableOsd(); - } -} - -// clear OSD frame buffer -void OsdClear(void) -{ - // select buffer to write to - if (!is_minimig()) spi_osd_cmd_cont(MM1_OSDCMDWRITE); - else spi_osd_cmd32_cont(OSD_CMD_OSD_WR, 0); - - // clear buffer - spi_n(0x00, OSDLINELEN * OsdGetSize()); - - // deselect OSD SPI device - DisableOsd(); -} - -// enable displaying of OSD -void OsdEnable(unsigned char mode) -{ - user_io_osd_key_enable(mode & DISABLE_KEYBOARD); - - mode &= DISABLE_KEYBOARD; - - if (!is_minimig()) spi_osd_cmd(MM1_OSDCMDENABLE | mode); - else spi_osd_cmd8(OSD_CMD_OSD, 0x01 | mode); -} - -void InfoEnable(int x, int y, int width, int height) -{ - user_io_osd_key_enable(0); - - if (!is_minimig()) - { - spi_osd_cmd_cont(MM1_OSDCMDENABLE | OSD_INFO); - } - else - { - spi_osd_cmd_cont(OSD_CMD_OSD); - spi8(1 | OSD_INFO); - } - - spi_w(x); - spi_w(y); - spi_w(width); - spi_w(height); - DisableOsd(); -} - -// disable displaying of OSD -void OsdDisable(void) -{ - user_io_osd_key_enable(0); - - if (!is_minimig()) - spi_osd_cmd(MM1_OSDCMDDISABLE); - else - spi_osd_cmd8(OSD_CMD_OSD, 0x00); -} - - -void ConfigVideo(unsigned char hires, unsigned char lores, unsigned char scanlines) -{ - spi_osd_cmd16(OSD_CMD_VID, (((scanlines >> 6) & 0x03) << 10) | (((scanlines >> 4) & 0x03) << 8) | (((scanlines >> 2) & 0x03) << 6) | ((hires & 0x03) << 4) | ((lores & 0x03) << 2) | (scanlines & 0x03)); -} - -void ConfigAudio(unsigned char audio) -{ - spi_osd_cmd8(OSD_CMD_AUD, audio); -} - -void ConfigMemory(unsigned char memory) -{ - spi_osd_cmd8(OSD_CMD_MEM, memory); -} - -void ConfigCPU(unsigned char cpu) -{ - spi_osd_cmd8(OSD_CMD_CPU, cpu & 0x0f); -} - -void ConfigChipset(unsigned char chipset) -{ - spi_osd_cmd8(OSD_CMD_CHIP, chipset & 0x1f); -} - -void ConfigFloppy(unsigned char drives, unsigned char speed) -{ - spi_osd_cmd8(OSD_CMD_FLP, ((drives & 0x03) << 2) | (speed & 0x03)); -} - -void ConfigAutofire(unsigned char autofire, unsigned char mask) -{ - uint16_t param = mask; - param = (param << 8) | autofire; - spi_osd_cmd16(OSD_CMD_JOY, param); -} - -void ScrollText(char n, const char *str, int off, int len, int max_len, unsigned char invert) -{ - // this function is called periodically when a string longer than the window is displayed. - -#define BLANKSPACE 10 // number of spaces between the end and start of repeated name - - char s[40]; - long offset; - if (!max_len) max_len = 30; - - if (str && str[0] && CheckTimer(scroll_timer)) // scroll if long name and timer delay elapsed - { - scroll_timer = GetTimer(SCROLL_DELAY2); // reset scroll timer to repeat delay - - scroll_offset++; // increase scroll position (1 pixel unit) - memset(s, ' ', 32); // clear buffer - - if (!len) len = strlen(str); // get name length - - if (off+len > max_len) // scroll name if longer than display size - { - // reset scroll position if it exceeds predefined maximum - if (scroll_offset >= (uint)(len + BLANKSPACE) << 3) scroll_offset = 0; - - offset = scroll_offset >> 3; // get new starting character of the name (scroll_offset is no longer in 2 pixel unit) - len -= offset; // remaining number of characters in the name - if (len>max_len) len = max_len; - if (len > 0) strncpy(s, &str[offset], len); // copy name substring - - if (len < max_len - BLANKSPACE) // file name substring and blank space is shorter than display line size - { - strncpy(s + len + BLANKSPACE, str, max_len - len - BLANKSPACE); // repeat the name after its end and predefined number of blank space - } - - OSD_PrintText(n, s, 22, (max_len - 1) << 3, (scroll_offset & 0x7), invert); // OSD print function with pixel precision - } - } -} - -void ScrollReset() -{ - scroll_timer = GetTimer(SCROLL_DELAY); // set timer to start name scrolling after predefined time delay - scroll_offset = 0; // start scrolling from the start -} - -/* core currently loaded */ -static char lastcorename[261 + 10] = "CORE"; -void OsdCoreNameSet(const char* str) -{ - sprintf(lastcorename, "%s", str); -} - -char* OsdCoreName() -{ - return lastcorename; -} +This file is part of Minimig + +Minimig is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Minimig 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +This is the Minimig OSD (on-screen-display) handler. + +2012-02-09 - Split character rom out to separate header file, with upper 128 entries +as rotated copies of the first 128 entries. -- AMR + +29-12-2006 - created +30-12-2006 - improved and simplified +-- JB -- +2008-10-04 - ARM version +2008-10-26 - added cpu and floppy configuration functions +2008-12-31 - added enable HDD command +2009-02-03 - full keyboard support +2009-06-23 - hires OSD display +2009-08-23 - adapted ConfigIDE() - support for 2 hardfiles +*/ + +#include +#include +#include +#include + +#include "osd.h" +#include "spi.h" + +#include "charrom.h" +#include "logo.h" +#include "user_io.h" +#include "hardware.h" + +#include "support.h" + +static int osd_size = 8; + +void OsdSetSize(int n) +{ + osd_size = n; +} + +int OsdGetSize() +{ + return osd_size; +} + +struct star +{ + int x, y; + int dx, dy; +}; + +struct star stars[64]; + +char framebuffer[16][256]; +void framebuffer_clear() +{ + memset(framebuffer, 0, sizeof(framebuffer)); +} + +void framebuffer_plot(int x, int y) +{ + framebuffer[y / 8][x] |= (1 << (y & 7)); +} + +void StarsInit() +{ + srand(time(NULL)); + for (int i = 0; i<64; ++i) + { + stars[i].x = (rand() % 228) << 4; // X centre + stars[i].y = (rand() % 128) << 4; // Y centre + stars[i].dx = -(rand() & 7) - 3; + stars[i].dy = 0; + } +} + +void StarsUpdate() +{ + framebuffer_clear(); + for (int i = 0; i<64; ++i) + { + stars[i].x += stars[i].dx; + stars[i].y += stars[i].dy; + if ((stars[i].x<0) || (stars[i].x>(228 << 4)) || + (stars[i].y<0) || (stars[i].y>(128 << 4))) + { + stars[i].x = 228 << 4; + stars[i].y = (rand() % 128) << 4; + stars[i].dx = -(rand() & 7) - 3; + stars[i].dy = 0; + } + framebuffer_plot(stars[i].x >> 4, stars[i].y >> 4); + } +} + + +// time delay after which file/dir name starts to scroll +#define SCROLL_DELAY 1000 +#define SCROLL_DELAY2 10 +#define SCROLL_DELAY3 50 + +static unsigned long scroll_offset = 0; // file/dir name scrolling position +static unsigned long scroll_timer = 0; // file/dir name scrolling timer + +static int arrow; +static unsigned char titlebuffer[256]; + +static void rotatechar(unsigned char *in, unsigned char *out) +{ + int a; + int b; + int c; + for (b = 0; b<8; ++b) + { + a = 0; + for (c = 0; c<8; ++c) + { + a <<= 1; + a |= (in[c] >> b) & 1; + } + out[b] = a; + } +} + +#define OSDHEIGHT (uint)(osd_size*8) + +void OsdSetTitle(const char *s, int a) +{ + // Compose the title, condensing character gaps + arrow = a; + int zeros = 0; + uint i = 0, j = 0; + uint outp = 0; + while (1) + { + int c = s[i++]; + if (c && (outpsizeof(titlebuffer)) break; + } + } + else break; + } + for (i = outp; i to the OSD buffer starting at line +void OsdWriteOffset(unsigned char n, const char *s, unsigned char invert, unsigned char stipple, char offset, char leftchar, char usebg, int maxinv) +{ + //printf("OsdWriteOffset(%d)\n", n); + unsigned short i; + unsigned char b; + const unsigned char *p; + unsigned char stipplemask = 0xff; + int linelimit = OSDLINELEN; + int arrowmask = arrow; + if (n == (osd_size-1) && (arrow & OSD_ARROW_RIGHT)) + linelimit -= 22; + + if (stipple) { + stipplemask = 0x55; + stipple = 0xff; + } + else + stipple = 0; + + // select buffer and line to write to + if (!is_minimig()) + spi_osd_cmd_cont(MM1_OSDCMDWRITE | n); + else + spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n); + + if (invert) invert = 255; + + i = 0; + // send all characters in string to OSD + while (1) + { + if (invert && i / 8 >= maxinv) invert = 0; + if (i == 0 && (n < osd_size)) + { // Render sidestripe + unsigned char j; + unsigned char tmp[8]; + + if (leftchar) + { + unsigned char tmp2[8]; + memcpy(tmp2, charfont[(uint)leftchar], 8); + rotatechar(tmp2, tmp); + p = tmp; + } + else + { + p = &titlebuffer[(osd_size - 1 - n) * 8]; + } + + spi16(0xffff); // left white border + + for (j = 0; j < 8; j++) + spi_n(255 ^ *p++, 2); + + spi16(0xffff); // right white border + spi16(0x0000); // blue gap + i += 22; + } + else if (n == (osd_size-1) && (arrowmask & OSD_ARROW_LEFT)) { // Draw initial arrow + unsigned char b; + + spi24((invert<<16)| (invert << 8) |invert); + p = &charfont[0x10][0]; + for (b = 0; b<8; b++) spi8((*p++ << offset) ^ invert); + p = &charfont[0x14][0]; + for (b = 0; b<8; b++) spi8((*p++ << offset) ^ invert); + spi24((invert << 16) | (invert << 8) | invert); + spi_n(invert, 2); + i += 24; + arrowmask &= ~OSD_ARROW_LEFT; + if (*s++ == 0) break; // Skip 3 characters, to keep alignent the same. + if (*s++ == 0) break; + if (*s++ == 0) break; + } + else { + b = *s++; + + if (b == 0) // end of string + break; + + else if (b == 0x0d || b == 0x0a) { // cariage return / linefeed, go to next line + // increment line counter + if (++n >= linelimit) + n = 0; + + // send new line number to OSD + DisableOsd(); + + if (!is_minimig()) + spi_osd_cmd_cont(MM1_OSDCMDWRITE | n); + else + spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n); + } + else if (i<(linelimit - 8)) { // normal character + unsigned char c; + p = &charfont[b][0]; + for (c = 0; c<8; c++) { + char bg = usebg ? framebuffer[n][i+c-22] : 0; + spi8((((*p++ << offset)&stipplemask) ^ invert) | bg); + stipplemask ^= stipple; + } + i += 8; + } + } + } + + for (; i < linelimit; i++) // clear end of line + { + char bg = usebg ? framebuffer[n][i-22] : 0; + spi8(invert | bg); + } + + if (n == (osd_size-1) && (arrowmask & OSD_ARROW_RIGHT)) + { // Draw final arrow if needed + unsigned char c; + spi24((invert << 16) | (invert << 8) | invert); + p = &charfont[0x15][0]; + for (c = 0; c<8; c++) spi8((*p++ << offset) ^ invert); + p = &charfont[0x11][0]; + for (c = 0; c<8; c++) spi8((*p++ << offset) ^ invert); + spi24((invert << 16) | (invert << 8) | invert); + i += 22; + } + + // deselect OSD SPI device + DisableOsd(); +} + +void OsdDrawLogo(int row) +{ + unsigned short i; + const unsigned char *p; + int linelimit = OSDLINELEN; + + int mag = (osd_size / 8); + uint n = row * mag; + + // select buffer and line to write to + if (!is_minimig()) + { + spi_osd_cmd_cont(MM1_OSDCMDWRITE | n); + } + else + { + spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n); + } + + for (int k = 0; k < mag; k++) + { + unsigned char bt = 0; + const unsigned char *lp = logodata[row]; + int bytes = sizeof(logodata[0]); + if ((uint)row >= (sizeof(logodata) / sizeof(logodata[0]))) lp = 0; + + char *bg = framebuffer[n + k]; + + i = 0; + while(i < linelimit) + { + if (i == 0) + { + unsigned char j; + p = &titlebuffer[(osd_size - 1 - n - k) * 8]; + spi16(0xffff); // left white border + for (j = 0; j<8; j++) spi_n(255 ^ *p++, 2); + spi16(0xffff); // right white border + spi16(0x0000); // blue gap + i += 22; + } + + if(lp && bytes) + { + bt = *lp++; + if(mag > 1) + { + if (k) bt >>= 4; + bt = (bt & 1) | ((bt & 1) << 1) | ((bt & 2) << 1) | ((bt & 2) << 2) | ((bt & 4) << 2) | ((bt & 4) << 3) | ((bt & 8) << 3) | ((bt & 8) << 4); + } + bytes--; + } + + spi8(bt | *bg++); + ++i; + } + } + + // deselect OSD SPI device + DisableOsd(); +} + +// write a null-terminated string to the OSD buffer starting at line +void OSD_PrintText(unsigned char line, const char *text, unsigned long start, unsigned long width, unsigned long offset, unsigned char invert) +{ + // line : OSD line number (0-7) + // text : pointer to null-terminated string + // start : start position (in pixels) + // width : printed text length in pixels + // offset : scroll offset in pixels counting from the start of the string (0-7) + // invert : invertion flag + + const unsigned char *p; + int i, j; + + // select buffer and line to write to + if (!is_minimig()) + spi_osd_cmd_cont(MM1_OSDCMDWRITE | line); + else + spi_osd_cmd32_cont(OSD_CMD_OSD_WR, line); + + if (invert) + invert = 0xff; + + p = &titlebuffer[(osd_size - 1 - line) * 8]; + if (start>2) { + spi16(0xffff); + start -= 2; + } + + i = start>16 ? 16 : start; + for (j = 0; j<(i / 2); ++j) + spi_n(255 ^ *p++, 2); + + if (i & 1) + spi8(255 ^ *p); + start -= i; + + if (start>2) { + spi16(0xffff); + start -= 2; + } + + while (start--) + spi8(0x00); + + if (offset) { + width -= 8 - offset; + p = &charfont[(uint)(*text++)][offset]; + for (; offset < 8; offset++) + spi8(*p++^invert); + } + + while (width > 8) { + unsigned char b; + p = &charfont[(uint)(*text++)][0]; + for (b = 0; b<8; b++) spi8(*p++^invert); + width -= 8; + } + + if (width) { + p = &charfont[(uint)(*text++)][0]; + while (width--) + spi8(*p++^invert); + } + + DisableOsd(); +} + +#define INFO_MAXW 32 +#define INFO_MAXH 16 + +void OSD_PrintInfo(const char *message, int *width, int *height, int frame) +{ + static char str[INFO_MAXW * INFO_MAXH]; + memset(str, ' ', sizeof(str)); + + // calc height/width if none provided. Add frame to calculated size. + // no frame will be added if width and height are provided. + int calc = !*width || !*height || frame; + + int maxw = 0; + int x = calc ? 1 : 0; + int y = calc ? 1 : 0; + while (*message) + { + char c = *message++; + if (c == 0xD) continue; + if (c == 0xA) + { + x = calc ? 1 : 0; + y++; + continue; + } + + if (x < INFO_MAXW && y < INFO_MAXH) str[(y*INFO_MAXW) + x] = c; + + x++; + if (x > maxw) maxw = x; + } + + int w = !calc ? *width + 2 : maxw+1; + if (w > INFO_MAXW) w = INFO_MAXW; + *width = w; + + int h = !calc ? *height + 2 : y+2; + if (h > INFO_MAXH) h = INFO_MAXH; + *height = h; + + if (frame) + { + frame = (frame - 1) * 6; + for (x = 1; x < w - 1; x++) + { + str[(0 * INFO_MAXW) + x] = 0x81+frame; + str[((h - 1)*INFO_MAXW) + x] = 0x81 + frame; + } + for (y = 1; y < h - 1; y++) + { + str[(y * INFO_MAXW)] = 0x83 + frame; + str[(y * INFO_MAXW) + w - 1] = 0x83 + frame; + } + str[0] = 0x80 + frame; + str[w - 1] = 0x82 + frame; + str[(h - 1)*INFO_MAXW] = 0x85 + frame; + str[((h - 1)*INFO_MAXW) + w - 1] = 0x84 + frame; + } + + for (y = 0; y < h; y++) + { + if (!is_minimig()) + spi_osd_cmd_cont(MM1_OSDCMDWRITE | y); + else + spi_osd_cmd32_cont(OSD_CMD_OSD_WR, y); + + for (x = 0; x < w; x++) + { + const unsigned char *p = charfont[(uint)str[(y*INFO_MAXW) + x]]; + for (int i = 0; i < 8; i++) spi8(*p++); + } + + DisableOsd(); + } +} + +// clear OSD frame buffer +void OsdClear(void) +{ + // select buffer to write to + if (!is_minimig()) spi_osd_cmd_cont(MM1_OSDCMDWRITE); + else spi_osd_cmd32_cont(OSD_CMD_OSD_WR, 0); + + // clear buffer + spi_n(0x00, OSDLINELEN * OsdGetSize()); + + // deselect OSD SPI device + DisableOsd(); +} + +// enable displaying of OSD +void OsdEnable(unsigned char mode) +{ + user_io_osd_key_enable(mode & DISABLE_KEYBOARD); + + mode &= DISABLE_KEYBOARD; + + if (!is_minimig()) spi_osd_cmd(MM1_OSDCMDENABLE | mode); + else spi_osd_cmd8(OSD_CMD_OSD, 0x01 | mode); +} + +void InfoEnable(int x, int y, int width, int height) +{ + user_io_osd_key_enable(0); + + if (!is_minimig()) + { + spi_osd_cmd_cont(MM1_OSDCMDENABLE | OSD_INFO); + } + else + { + spi_osd_cmd_cont(OSD_CMD_OSD); + spi8(1 | OSD_INFO); + } + + spi_w(x); + spi_w(y); + spi_w(width); + spi_w(height); + DisableOsd(); +} + +// disable displaying of OSD +void OsdDisable(void) +{ + user_io_osd_key_enable(0); + + if (!is_minimig()) + spi_osd_cmd(MM1_OSDCMDDISABLE); + else + spi_osd_cmd8(OSD_CMD_OSD, 0x00); +} + + +void ConfigVideo(unsigned char hires, unsigned char lores, unsigned char scanlines) +{ + spi_osd_cmd16(OSD_CMD_VID, (((scanlines >> 6) & 0x03) << 10) | (((scanlines >> 4) & 0x03) << 8) | (((scanlines >> 2) & 0x03) << 6) | ((hires & 0x03) << 4) | ((lores & 0x03) << 2) | (scanlines & 0x03)); +} + +void ConfigAudio(unsigned char audio) +{ + spi_osd_cmd8(OSD_CMD_AUD, audio); +} + +void ConfigMemory(unsigned char memory) +{ + spi_osd_cmd8(OSD_CMD_MEM, memory); +} + +void ConfigCPU(unsigned char cpu) +{ + spi_osd_cmd8(OSD_CMD_CPU, cpu & 0x0f); +} + +void ConfigChipset(unsigned char chipset) +{ + spi_osd_cmd8(OSD_CMD_CHIP, chipset & 0x1f); +} + +void ConfigFloppy(unsigned char drives, unsigned char speed) +{ + spi_osd_cmd8(OSD_CMD_FLP, ((drives & 0x03) << 2) | (speed & 0x03)); +} + +void ConfigAutofire(unsigned char autofire, unsigned char mask) +{ + uint16_t param = mask; + param = (param << 8) | autofire; + spi_osd_cmd16(OSD_CMD_JOY, param); +} + +void ScrollText(char n, const char *str, int off, int len, int max_len, unsigned char invert) +{ + // this function is called periodically when a string longer than the window is displayed. + +#define BLANKSPACE 10 // number of spaces between the end and start of repeated name + + char s[40]; + long offset; + if (!max_len) max_len = 30; + + if (str && str[0] && CheckTimer(scroll_timer)) // scroll if long name and timer delay elapsed + { + scroll_timer = GetTimer(SCROLL_DELAY2); // reset scroll timer to repeat delay + + scroll_offset++; // increase scroll position (1 pixel unit) + memset(s, ' ', 32); // clear buffer + + if (!len) len = strlen(str); // get name length + + if (off+len > max_len) // scroll name if longer than display size + { + // reset scroll position if it exceeds predefined maximum + if (scroll_offset >= (uint)(len + BLANKSPACE) << 3) scroll_offset = 0; + + offset = scroll_offset >> 3; // get new starting character of the name (scroll_offset is no longer in 2 pixel unit) + len -= offset; // remaining number of characters in the name + if (len>max_len) len = max_len; + if (len > 0) strncpy(s, &str[offset], len); // copy name substring + + if (len < max_len - BLANKSPACE) // file name substring and blank space is shorter than display line size + { + strncpy(s + len + BLANKSPACE, str, max_len - len - BLANKSPACE); // repeat the name after its end and predefined number of blank space + } + + OSD_PrintText(n, s, 22, (max_len - 1) << 3, (scroll_offset & 0x7), invert); // OSD print function with pixel precision + } + } +} + +void ScrollReset() +{ + scroll_timer = GetTimer(SCROLL_DELAY); // set timer to start name scrolling after predefined time delay + scroll_offset = 0; // start scrolling from the start +} + +/* core currently loaded */ +static char lastcorename[261 + 10] = "CORE"; +void OsdCoreNameSet(const char* str) +{ + sprintf(lastcorename, "%s", str); +} + +char* OsdCoreName() +{ + return lastcorename; +} diff --git a/osd.h b/osd.h index d647a1e..f736564 100644 --- a/osd.h +++ b/osd.h @@ -72,7 +72,7 @@ void OsdWrite(unsigned char n, const char *s="", unsigned char inver=0, unsigned void OsdWriteOffset(unsigned char n, const char *s, unsigned char inver, unsigned char stipple, char offset, char leftchar, char usebg = 0, int maxinv = 32); // Used for scrolling "Exit" text downwards... void OsdClear(void); void OsdEnable(unsigned char mode); -void InfoEnable(int x, int y, int width, int height); +void InfoEnable(int x, int y, int width, int height); void OsdDisable(void); void ConfigVideo(unsigned char hires, unsigned char lores, unsigned char scanlines); void ConfigAudio(unsigned char audio); @@ -80,9 +80,9 @@ void ConfigMemory(unsigned char memory); void ConfigCPU(unsigned char cpu); void ConfigChipset(unsigned char chipset); void ConfigFloppy(unsigned char drives, unsigned char speed); -void ConfigAutofire(unsigned char autofire, unsigned char mask); +void ConfigAutofire(unsigned char autofire, unsigned char mask); void OSD_PrintText(unsigned char line, const char *text, unsigned long start, unsigned long width, unsigned long offset, unsigned char invert); -void OSD_PrintInfo(const char *message, int *width, int *height, int frame = 0); +void OSD_PrintInfo(const char *message, int *width, int *height, int frame = 0); void OsdDrawLogo(int row); void ScrollText(char n, const char *str, int off, int len, int max_len, unsigned char invert); void ScrollReset(); diff --git a/spi.cpp b/spi.cpp index 2db8158..ccbd49f 100644 --- a/spi.cpp +++ b/spi.cpp @@ -1,331 +1,331 @@ -#include "spi.h" -#include "hardware.h" -#include "fpga_io.h" - -#define SSPI_STROBE (1<<17) -#define SSPI_ACK SSPI_STROBE - -#define SSPI_FPGA_EN (1<<18) -#define SSPI_OSD_EN (1<<19) -#define SSPI_IO_EN (1<<20) -#define SSPI_DM_EN (1<<21) - -#define SWAPW(a) ((((a)<<8)&0xff00)|(((a)>>8)&0x00ff)) - -static void spi_en(uint32_t mask, uint32_t en) -{ - uint32_t gpo = fpga_gpo_read() | 0x80000000; - fpga_gpo_write(en ? gpo | mask : gpo & ~mask); -} - -uint16_t spi_w(uint16_t word) -{ - uint32_t gpo = (fpga_gpo_read() & ~(0xFFFF | SSPI_STROBE)) | word; - - fpga_gpo_write(gpo); - fpga_gpo_write(gpo | SSPI_STROBE); - - int gpi; - do - { - gpi = fpga_gpi_read(); - if (gpi < 0) - { - printf("GPI[31]==1. FPGA is uninitialized?\n"); - return 0; - } - } while (!(gpi & SSPI_ACK)); - - fpga_gpo_write(gpo); - - do - { - gpi = fpga_gpi_read(); - if (gpi < 0) - { - printf("GPI[31]==1. FPGA is uninitialized?\n"); - return 0; - } - } while (gpi & SSPI_ACK); - - return (uint16_t)gpi; -} - -void spi_init(int enable) -{ - (void)enable; - printf("Init SPI.\n"); -} - -uint8_t spi_b(uint8_t parm) -{ - return (uint8_t)spi_w(parm); -} - -void EnableFpga() -{ - spi_en(SSPI_FPGA_EN, 1); -} - -void DisableFpga() -{ - spi_en(SSPI_FPGA_EN, 0); -} - -void EnableOsd() -{ - spi_en(SSPI_OSD_EN, 1); -} - -void DisableOsd() -{ - spi_en(SSPI_OSD_EN, 0); -} - -void EnableIO() -{ - spi_en(SSPI_IO_EN, 1); -} - -void DisableIO() -{ - spi_en(SSPI_IO_EN, 0); -} - -void EnableDMode() -{ - spi_en(SSPI_DM_EN, 1); -} - -void DisableDMode() -{ - spi_en(SSPI_DM_EN, 0); -} - -uint8_t spi_in() -{ - return spi_b(0); -} - -void spi8(uint8_t parm) -{ - spi_b(parm); -} - -void spi16(uint16_t parm) -{ - spi8(parm >> 8); - spi8(parm >> 0); -} - -void spi24(uint32_t parm) -{ - spi8(parm >> 16); - spi8(parm >> 8); - spi8(parm >> 0); -} - -void spi32(uint32_t parm) -{ - spi8(parm >> 24); - spi8(parm >> 16); - spi8(parm >> 8); - spi8(parm >> 0); -} - -uint32_t spi32w(uint32_t parm) -{ - uint32_t res; - res = spi_w(parm); - res |= (spi_w(parm>>16))<<16; - return res; -} - -// little endian: lsb first -void spi32le(uint32_t parm) -{ - spi8(parm >> 0); - spi8(parm >> 8); - spi8(parm >> 16); - spi8(parm >> 24); -} - -/* OSD related SPI functions */ -void spi_osd_cmd_cont(uint8_t cmd) -{ - EnableOsd(); - spi8(cmd); -} - -void spi_osd_cmd(uint8_t cmd) -{ - spi_osd_cmd_cont(cmd); - DisableOsd(); -} - -void spi_osd_cmd8_cont(uint8_t cmd, uint8_t parm) -{ - EnableOsd(); - spi8(cmd); - spi8(parm); -} - -void spi_osd_cmd8(uint8_t cmd, uint8_t parm) -{ - spi_osd_cmd8_cont(cmd, parm); - DisableOsd(); -} - -void spi_osd_cmd16(uint8_t cmd, uint16_t parm) -{ - EnableOsd(); - spi8(cmd); - spi_w(parm); - DisableOsd(); -} - -void spi_osd_cmd32_cont(uint8_t cmd, uint32_t parm) -{ - EnableOsd(); - spi8(cmd); - spi32(parm); -} - -void spi_osd_cmd32(uint8_t cmd, uint32_t parm) -{ - spi_osd_cmd32_cont(cmd, parm); - DisableOsd(); -} - -void spi_osd_cmd32le_cont(uint8_t cmd, uint32_t parm) -{ - EnableOsd(); - spi8(cmd); - spi32le(parm); -} - -void spi_osd_cmd32le(uint8_t cmd, uint32_t parm) -{ - spi_osd_cmd32le_cont(cmd, parm); - DisableOsd(); -} - -/* User_io related SPI functions */ -uint8_t spi_uio_cmd_cont(uint8_t cmd) -{ - EnableIO(); - return spi_b(cmd); -} - -uint8_t spi_uio_cmd(uint8_t cmd) -{ - uint8_t res = spi_uio_cmd_cont(cmd); - DisableIO(); - return res; -} - -void spi_uio_cmd8_cont(uint8_t cmd, uint8_t parm) -{ - EnableIO(); - spi8(cmd); - spi8(parm); -} - -void spi_uio_cmd8(uint8_t cmd, uint8_t parm) -{ - spi_uio_cmd8_cont(cmd, parm); - DisableIO(); -} - -void spi_uio_cmd16(uint8_t cmd, uint16_t parm) -{ - spi_uio_cmd_cont(cmd); - spi_w(parm); - DisableIO(); -} - -void spi_uio_cmd32(uint8_t cmd, uint32_t parm, int wide) -{ - EnableIO(); - spi8(cmd); - if (wide) - { - spi_w((uint16_t)parm); - spi_w((uint16_t)(parm >> 16)); - } - else - { - spi8(parm); - spi8(parm >> 8); - spi8(parm >> 16); - spi8(parm >> 24); - } - DisableIO(); -} - -void spi_n(uint8_t value, uint16_t cnt) -{ - while (cnt--) spi8(value); -} - -void spi_read(uint8_t *addr, uint16_t len, int wide) -{ - if (wide) - { - uint16_t len16 = len >> 1; - uint16_t *a16 = (uint16_t*)addr; - while (len16--) *a16++ = spi_w(0); - if (len & 1) *((uint8_t*)a16) = spi_w(0); - } - else - { - while (len--) *addr++ = spi_b(0); - } -} - -void spi_write(const uint8_t *addr, uint16_t len, int wide) -{ - if (wide) - { - uint16_t len16 = len >> 1; - uint16_t *a16 = (uint16_t*)addr; - while (len16--) spi_w(*a16++); - if(len & 1) spi_w(*((uint8_t*)a16)); - } - else - { - while (len--) spi8(*addr++); - } -} - -void spi_block_read(uint8_t *addr, int wide) -{ - spi_read(addr, 512, wide); -} - -void spi_block_write(const uint8_t *addr, int wide) -{ - spi_write(addr, 512, wide); -} - -void spi_block_write_16be(const uint16_t *addr) -{ - uint16_t len = 256; - uint16_t tmp; - while (len--) - { - tmp = *addr++; - spi_w(SWAPW(tmp)); - } -} - -void spi_block_read_16be(uint16_t *addr) -{ - uint16_t len = 256; - uint16_t tmp; - while (len--) - { - tmp = spi_w(0xFFFF); - *addr++ = SWAPW(tmp); - } -} +#include "spi.h" +#include "hardware.h" +#include "fpga_io.h" + +#define SSPI_STROBE (1<<17) +#define SSPI_ACK SSPI_STROBE + +#define SSPI_FPGA_EN (1<<18) +#define SSPI_OSD_EN (1<<19) +#define SSPI_IO_EN (1<<20) +#define SSPI_DM_EN (1<<21) + +#define SWAPW(a) ((((a)<<8)&0xff00)|(((a)>>8)&0x00ff)) + +static void spi_en(uint32_t mask, uint32_t en) +{ + uint32_t gpo = fpga_gpo_read() | 0x80000000; + fpga_gpo_write(en ? gpo | mask : gpo & ~mask); +} + +uint16_t spi_w(uint16_t word) +{ + uint32_t gpo = (fpga_gpo_read() & ~(0xFFFF | SSPI_STROBE)) | word; + + fpga_gpo_write(gpo); + fpga_gpo_write(gpo | SSPI_STROBE); + + int gpi; + do + { + gpi = fpga_gpi_read(); + if (gpi < 0) + { + printf("GPI[31]==1. FPGA is uninitialized?\n"); + return 0; + } + } while (!(gpi & SSPI_ACK)); + + fpga_gpo_write(gpo); + + do + { + gpi = fpga_gpi_read(); + if (gpi < 0) + { + printf("GPI[31]==1. FPGA is uninitialized?\n"); + return 0; + } + } while (gpi & SSPI_ACK); + + return (uint16_t)gpi; +} + +void spi_init(int enable) +{ + (void)enable; + printf("Init SPI.\n"); +} + +uint8_t spi_b(uint8_t parm) +{ + return (uint8_t)spi_w(parm); +} + +void EnableFpga() +{ + spi_en(SSPI_FPGA_EN, 1); +} + +void DisableFpga() +{ + spi_en(SSPI_FPGA_EN, 0); +} + +void EnableOsd() +{ + spi_en(SSPI_OSD_EN, 1); +} + +void DisableOsd() +{ + spi_en(SSPI_OSD_EN, 0); +} + +void EnableIO() +{ + spi_en(SSPI_IO_EN, 1); +} + +void DisableIO() +{ + spi_en(SSPI_IO_EN, 0); +} + +void EnableDMode() +{ + spi_en(SSPI_DM_EN, 1); +} + +void DisableDMode() +{ + spi_en(SSPI_DM_EN, 0); +} + +uint8_t spi_in() +{ + return spi_b(0); +} + +void spi8(uint8_t parm) +{ + spi_b(parm); +} + +void spi16(uint16_t parm) +{ + spi8(parm >> 8); + spi8(parm >> 0); +} + +void spi24(uint32_t parm) +{ + spi8(parm >> 16); + spi8(parm >> 8); + spi8(parm >> 0); +} + +void spi32(uint32_t parm) +{ + spi8(parm >> 24); + spi8(parm >> 16); + spi8(parm >> 8); + spi8(parm >> 0); +} + +uint32_t spi32w(uint32_t parm) +{ + uint32_t res; + res = spi_w(parm); + res |= (spi_w(parm>>16))<<16; + return res; +} + +// little endian: lsb first +void spi32le(uint32_t parm) +{ + spi8(parm >> 0); + spi8(parm >> 8); + spi8(parm >> 16); + spi8(parm >> 24); +} + +/* OSD related SPI functions */ +void spi_osd_cmd_cont(uint8_t cmd) +{ + EnableOsd(); + spi8(cmd); +} + +void spi_osd_cmd(uint8_t cmd) +{ + spi_osd_cmd_cont(cmd); + DisableOsd(); +} + +void spi_osd_cmd8_cont(uint8_t cmd, uint8_t parm) +{ + EnableOsd(); + spi8(cmd); + spi8(parm); +} + +void spi_osd_cmd8(uint8_t cmd, uint8_t parm) +{ + spi_osd_cmd8_cont(cmd, parm); + DisableOsd(); +} + +void spi_osd_cmd16(uint8_t cmd, uint16_t parm) +{ + EnableOsd(); + spi8(cmd); + spi_w(parm); + DisableOsd(); +} + +void spi_osd_cmd32_cont(uint8_t cmd, uint32_t parm) +{ + EnableOsd(); + spi8(cmd); + spi32(parm); +} + +void spi_osd_cmd32(uint8_t cmd, uint32_t parm) +{ + spi_osd_cmd32_cont(cmd, parm); + DisableOsd(); +} + +void spi_osd_cmd32le_cont(uint8_t cmd, uint32_t parm) +{ + EnableOsd(); + spi8(cmd); + spi32le(parm); +} + +void spi_osd_cmd32le(uint8_t cmd, uint32_t parm) +{ + spi_osd_cmd32le_cont(cmd, parm); + DisableOsd(); +} + +/* User_io related SPI functions */ +uint8_t spi_uio_cmd_cont(uint8_t cmd) +{ + EnableIO(); + return spi_b(cmd); +} + +uint8_t spi_uio_cmd(uint8_t cmd) +{ + uint8_t res = spi_uio_cmd_cont(cmd); + DisableIO(); + return res; +} + +void spi_uio_cmd8_cont(uint8_t cmd, uint8_t parm) +{ + EnableIO(); + spi8(cmd); + spi8(parm); +} + +void spi_uio_cmd8(uint8_t cmd, uint8_t parm) +{ + spi_uio_cmd8_cont(cmd, parm); + DisableIO(); +} + +void spi_uio_cmd16(uint8_t cmd, uint16_t parm) +{ + spi_uio_cmd_cont(cmd); + spi_w(parm); + DisableIO(); +} + +void spi_uio_cmd32(uint8_t cmd, uint32_t parm, int wide) +{ + EnableIO(); + spi8(cmd); + if (wide) + { + spi_w((uint16_t)parm); + spi_w((uint16_t)(parm >> 16)); + } + else + { + spi8(parm); + spi8(parm >> 8); + spi8(parm >> 16); + spi8(parm >> 24); + } + DisableIO(); +} + +void spi_n(uint8_t value, uint16_t cnt) +{ + while (cnt--) spi8(value); +} + +void spi_read(uint8_t *addr, uint16_t len, int wide) +{ + if (wide) + { + uint16_t len16 = len >> 1; + uint16_t *a16 = (uint16_t*)addr; + while (len16--) *a16++ = spi_w(0); + if (len & 1) *((uint8_t*)a16) = spi_w(0); + } + else + { + while (len--) *addr++ = spi_b(0); + } +} + +void spi_write(const uint8_t *addr, uint16_t len, int wide) +{ + if (wide) + { + uint16_t len16 = len >> 1; + uint16_t *a16 = (uint16_t*)addr; + while (len16--) spi_w(*a16++); + if(len & 1) spi_w(*((uint8_t*)a16)); + } + else + { + while (len--) spi8(*addr++); + } +} + +void spi_block_read(uint8_t *addr, int wide) +{ + spi_read(addr, 512, wide); +} + +void spi_block_write(const uint8_t *addr, int wide) +{ + spi_write(addr, 512, wide); +} + +void spi_block_write_16be(const uint16_t *addr) +{ + uint16_t len = 256; + uint16_t tmp; + while (len--) + { + tmp = *addr++; + spi_w(SWAPW(tmp)); + } +} + +void spi_block_read_16be(uint16_t *addr) +{ + uint16_t len = 256; + uint16_t tmp; + while (len--) + { + tmp = spi_w(0xFFFF); + *addr++ = SWAPW(tmp); + } +} diff --git a/spi.h b/spi.h index d884d1c..9fdd539 100644 --- a/spi.h +++ b/spi.h @@ -1,61 +1,61 @@ -#ifndef SPI_H -#define SPI_H - -#include - -/* main init functions */ -void spi_init(int enable); - -/* chip select functions */ -void EnableFpga(); -void DisableFpga(); -void EnableOsd(); -void DisableOsd(); -void EnableDMode(); -void DisableDMode(); -void EnableIO(); -void DisableIO(); - -// base functions -uint8_t spi_b(uint8_t parm); -uint16_t spi_w(uint16_t word); - -// input only helper -uint8_t spi_in(); - -void spi8(uint8_t parm); -void spi16(uint16_t parm); -void spi24(uint32_t parm); -void spi32(uint32_t parm); -void spi32le(uint32_t parm); -void spi_n(uint8_t value, uint16_t cnt); -uint32_t spi32w(uint32_t parm); - -/* block transfer functions */ -void spi_block_read(uint8_t *addr, int wide); -void spi_read(uint8_t *addr, uint16_t len, int wide); -void spi_block_write(const uint8_t *addr, int wide); -void spi_write(const uint8_t *addr, uint16_t len, int wide); -void spi_block_write_16be(const uint16_t *addr); -void spi_block_read_16be(uint16_t *addr); - -/* OSD related SPI functions */ -void spi_osd_cmd_cont(uint8_t cmd); -void spi_osd_cmd(uint8_t cmd); -void spi_osd_cmd8_cont(uint8_t cmd, uint8_t parm); -void spi_osd_cmd8(uint8_t cmd, uint8_t parm); -void spi_osd_cmd16(uint8_t cmd, uint16_t parm); -void spi_osd_cmd32_cont(uint8_t cmd, uint32_t parm); -void spi_osd_cmd32(uint8_t cmd, uint32_t parm); -void spi_osd_cmd32le_cont(uint8_t cmd, uint32_t parm); -void spi_osd_cmd32le(uint8_t cmd, uint32_t parm); - -/* User_io related SPI functions */ -uint8_t spi_uio_cmd_cont(uint8_t cmd); -uint8_t spi_uio_cmd(uint8_t cmd); -void spi_uio_cmd8(uint8_t cmd, uint8_t parm); -void spi_uio_cmd8_cont(uint8_t cmd, uint8_t parm); -void spi_uio_cmd16(uint8_t cmd, uint16_t parm); -void spi_uio_cmd32(uint8_t cmd, uint32_t parm, int wide); - -#endif // SPI_H +#ifndef SPI_H +#define SPI_H + +#include + +/* main init functions */ +void spi_init(int enable); + +/* chip select functions */ +void EnableFpga(); +void DisableFpga(); +void EnableOsd(); +void DisableOsd(); +void EnableDMode(); +void DisableDMode(); +void EnableIO(); +void DisableIO(); + +// base functions +uint8_t spi_b(uint8_t parm); +uint16_t spi_w(uint16_t word); + +// input only helper +uint8_t spi_in(); + +void spi8(uint8_t parm); +void spi16(uint16_t parm); +void spi24(uint32_t parm); +void spi32(uint32_t parm); +void spi32le(uint32_t parm); +void spi_n(uint8_t value, uint16_t cnt); +uint32_t spi32w(uint32_t parm); + +/* block transfer functions */ +void spi_block_read(uint8_t *addr, int wide); +void spi_read(uint8_t *addr, uint16_t len, int wide); +void spi_block_write(const uint8_t *addr, int wide); +void spi_write(const uint8_t *addr, uint16_t len, int wide); +void spi_block_write_16be(const uint16_t *addr); +void spi_block_read_16be(uint16_t *addr); + +/* OSD related SPI functions */ +void spi_osd_cmd_cont(uint8_t cmd); +void spi_osd_cmd(uint8_t cmd); +void spi_osd_cmd8_cont(uint8_t cmd, uint8_t parm); +void spi_osd_cmd8(uint8_t cmd, uint8_t parm); +void spi_osd_cmd16(uint8_t cmd, uint16_t parm); +void spi_osd_cmd32_cont(uint8_t cmd, uint32_t parm); +void spi_osd_cmd32(uint8_t cmd, uint32_t parm); +void spi_osd_cmd32le_cont(uint8_t cmd, uint32_t parm); +void spi_osd_cmd32le(uint8_t cmd, uint32_t parm); + +/* User_io related SPI functions */ +uint8_t spi_uio_cmd_cont(uint8_t cmd); +uint8_t spi_uio_cmd(uint8_t cmd); +void spi_uio_cmd8(uint8_t cmd, uint8_t parm); +void spi_uio_cmd8_cont(uint8_t cmd, uint8_t parm); +void spi_uio_cmd16(uint8_t cmd, uint16_t parm); +void spi_uio_cmd32(uint8_t cmd, uint32_t parm, int wide); + +#endif // SPI_H diff --git a/sxmlc.c b/sxmlc.c index 04b7c66..f753d4f 100644 --- a/sxmlc.c +++ b/sxmlc.c @@ -1,2291 +1,2291 @@ -/* - Copyright (c) 2010, Matthieu Labas - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY - OF SUCH DAMAGE. - - The views and conclusions contained in the software and documentation are those of the - authors and should not be interpreted as representing official policies, either expressed - or implied, of the FreeBSD Project. -*/ -#if defined(WIN32) || defined(WIN64) -#pragma warning(disable : 4996) -#else -#ifndef strdup -#define _GNU_SOURCE -#endif -#endif - -#include -#include -#include -#include -#include "sxmlc.h" - -/* - Struct defining "special" tags such as "" or "". - These tags are considered having a start and an end with some data in between that will - be stored in the 'tag' member of an XMLNode. - The 'tag_type' member is a constant that is associated to such tag. - All 'len_*' members are basically the "sx_strlen()" of 'start' and 'end' members. - */ -typedef struct _Tag { - TagType tag_type; - SXML_CHAR* start; - int len_start; - SXML_CHAR* end; - int len_end; -} _TAG; - -typedef struct _SpecialTag { - _TAG *tags; - int n_tags; -} SPECIAL_TAG; - -/* - List of "special" tags handled by sxmlc. - NB the "' or ']>'). - */ -static _TAG _spec[] = { - { TAG_INSTR, C2SX(""), 2 }, - { TAG_COMMENT, C2SX(""), 3 }, - { TAG_CDATA, C2SX(""), 3 } -}; -static int NB_SPECIAL_TAGS = (int)(sizeof(_spec) / sizeof(_TAG)); /* Auto computation of number of special tags */ - -/* - User-registered tags. - */ -static SPECIAL_TAG _user_tags = { NULL, 0 }; - -int XML_register_user_tag(TagType tag_type, SXML_CHAR* start, SXML_CHAR* end) -{ - _TAG* p; - int i, n, le; - - if (tag_type < TAG_USER) - return -1; - - if (start == NULL || end == NULL || *start != C2SX('<')) - return -1; - - le = sx_strlen(end); - if (end[le-1] != C2SX('>')) - return -1; - - i = _user_tags.n_tags; - n = i + 1; - p = (_TAG*)__realloc(_user_tags.tags, n * sizeof(_TAG)); - if (p == NULL) - return -1; - - p[i].tag_type = tag_type; - p[i].start = start; - p[i].end = end; - p[i].len_start = sx_strlen(start); - p[i].len_end = le; - _user_tags.tags = p; - _user_tags.n_tags = n; - - return i; -} - -int XML_unregister_user_tag(int i_tag) -{ - _TAG* pt; - - if (i_tag < 0 || i_tag >= _user_tags.n_tags) - return -1; - - if (_user_tags.n_tags == 1) - pt = NULL; - else { - pt = (_TAG*)__malloc((_user_tags.n_tags - 1) * sizeof(_TAG)); - if (pt == NULL) - return -1; - } - - if (pt != NULL) { - memcpy(pt, _user_tags.tags, i_tag * sizeof(_TAG)); - memcpy(&pt[i_tag], &_user_tags.tags[i_tag + 1], (_user_tags.n_tags - i_tag - 1) * sizeof(_TAG)); - } - if (_user_tags.tags != NULL) - __free(_user_tags.tags); - _user_tags.tags = pt; - _user_tags.n_tags--; - - return _user_tags.n_tags; -} - -int XML_get_nb_registered_user_tags(void) -{ - return _user_tags.n_tags; -} - -int XML_get_registered_user_tag(TagType tag_type) -{ - int i; - - for (i = 0; i < _user_tags.n_tags; i++) - if (_user_tags.tags[i].tag_type == tag_type) - return i; - - return -1; -} - -/* --- XMLNode methods --- */ - -/* - Add 'node' to given '*children_array' of '*len_array' elements. - '*len_array' is overwritten with the number of elements in '*children_array' after its reallocation. - Return the index of the newly added 'node' in '*children_array', or '-1' for memory error. - */ -static int _add_node(XMLNode*** children_array, int* len_array, XMLNode* node) -{ - XMLNode** pt = (XMLNode**)__realloc(*children_array, (*len_array+1) * sizeof(XMLNode*)); - - if (pt == NULL) - return -1; - - pt[*len_array] = node; - *children_array = pt; - - return (*len_array)++; -} - -int XMLNode_init(XMLNode* node) -{ - if (node == NULL) - return false; - - if (node->init_value == XML_INIT_DONE) - return true; /*(void)XMLNode_free(node);*/ - - node->tag = NULL; - node->text = NULL; - - node->attributes = NULL; - node->n_attributes = 0; - - node->father = NULL; - node->children = NULL; - node->n_children = 0; - - node->tag_type = TAG_NONE; - node->active = true; - - node->init_value = XML_INIT_DONE; - - return true; -} - -XMLNode* XMLNode_allocN(int n) -{ - int i; - XMLNode* p; - - if (n <= 0) - return NULL; - - p = (XMLNode*)__calloc(n, sizeof(XMLNode)); - if (p == NULL) - return NULL; - - for (i = 0; i < n; i++) - (void)XMLNode_init(&p[i]); - - return p; -} - -XMLNode* XMLNode_dup(const XMLNode* node, int copy_children) -{ - XMLNode* n; - - if (node == NULL) - return NULL; - - n = (XMLNode*)__calloc(1, sizeof(XMLNode)); - if (n == NULL) - return NULL; - - XMLNode_init(n); - if (!XMLNode_copy(n, node, copy_children)) { - XMLNode_free(n); - - return NULL; - } - - return n; -} - -int XMLNode_free(XMLNode* node) -{ - if (node == NULL || node->init_value != XML_INIT_DONE) - return false; - - if (node->tag != NULL) { - __free(node->tag); - node->tag = NULL; - } - - XMLNode_remove_text(node); - XMLNode_remove_all_attributes(node); - XMLNode_remove_children(node); - - node->tag_type = TAG_NONE; - - return true; -} - -int XMLNode_copy(XMLNode* dst, const XMLNode* src, int copy_children) -{ - int i; - - if (dst == NULL || (src != NULL && src->init_value != XML_INIT_DONE)) - return false; - - (void)XMLNode_free(dst); /* 'dst' is freed first */ - - /* NULL 'src' resets 'dst' */ - if (src == NULL) - return true; - - /* Tag */ - if (src->tag != NULL) { - dst->tag = sx_strdup(src->tag); - if (dst->tag == NULL) goto copy_err; - } - - /* Text */ - if (dst->text != NULL) { - dst->text = sx_strdup(src->text); - if (dst->text == NULL) goto copy_err; - } - - /* Attributes */ - if (src->n_attributes > 0) { - dst->attributes = (XMLAttribute*)__calloc(src->n_attributes, sizeof(XMLAttribute)); - if (dst->attributes== NULL) goto copy_err; - dst->n_attributes = src->n_attributes; - for (i = 0; i < src->n_attributes; i++) { - dst->attributes[i].name = sx_strdup(src->attributes[i].name); - dst->attributes[i].value = sx_strdup(src->attributes[i].value); - if (dst->attributes[i].name == NULL || dst->attributes[i].value == NULL) goto copy_err; - dst->attributes[i].active = src->attributes[i].active; - } - } - - dst->tag_type = src->tag_type; - dst->father = src->father; - dst->user = src->user; - dst->active = src->active; - - /* Copy children if required (and there are any) */ - if (copy_children && src->n_children > 0) { - dst->children = (XMLNode**)__calloc(src->n_children, sizeof(XMLNode*)); - if (dst->children == NULL) goto copy_err; - dst->n_children = src->n_children; - for (i = 0; i < src->n_children; i++) { - if (!XMLNode_copy(dst->children[i], src->children[i], true)) goto copy_err; - } - } - - return true; - -copy_err: - (void)XMLNode_free(dst); - - return false; -} - -int XMLNode_set_active(XMLNode* node, int active) -{ - if (node == NULL || node->init_value != XML_INIT_DONE) - return false; - - node->active = active; - - return true; -} - -int XMLNode_set_tag(XMLNode* node, const SXML_CHAR* tag) -{ - SXML_CHAR* newtag; - if (node == NULL || tag == NULL || node->init_value != XML_INIT_DONE) - return false; - - newtag = sx_strdup(tag); - if (newtag == NULL) - return false; - if (node->tag != NULL) __free(node->tag); - node->tag = newtag; - - return true; -} - -int XMLNode_set_type(XMLNode* node, const TagType tag_type) -{ - if (node == NULL || node->init_value != XML_INIT_DONE) - return false; - - switch (tag_type) { - case TAG_ERROR: - case TAG_END: - case TAG_PARTIAL: - case TAG_NONE: - return false; - - default: - node->tag_type = tag_type; - return true; - } -} - -int XMLNode_set_attribute(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR* attr_value) -{ - XMLAttribute* pt; - int i; - - if (node == NULL || attr_name == NULL || attr_name[0] == NULC || node->init_value != XML_INIT_DONE) - return -1; - - i = XMLNode_search_attribute(node, attr_name, 0); - if (i >= 0) { /* Attribute found: update it */ - SXML_CHAR* value = NULL; - if (attr_value != NULL && (value = sx_strdup(attr_value)) == NULL) - return -1; - pt = node->attributes; - if (pt[i].value != NULL) - __free(pt[i].value); - pt[i].value = value; - } else { /* Attribute not found: add it */ - SXML_CHAR* name = sx_strdup(attr_name); - SXML_CHAR* value = (attr_value == NULL ? NULL : sx_strdup(attr_value)); - if (name == NULL || (value == NULL && attr_value != NULL)) { - if (value != NULL) - __free(value); - if (name != NULL) - __free(name); - return -1; - } - i = node->n_attributes; - pt = (XMLAttribute*)__realloc(node->attributes, (i+1) * sizeof(XMLAttribute)); - if (pt == NULL) { - if (value != NULL) - __free(value); - __free(name); - return -1; - } - - pt[i].name = name; - pt[i].value = value; - pt[i].active = true; - node->attributes = pt; - node->n_attributes = i + 1; - } - - return node->n_attributes; -} - -int XMLNode_get_attribute_with_default(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR** attr_value, const SXML_CHAR* default_attr_value) -{ - XMLAttribute* pt; - int i; - - if (node == NULL || attr_name == NULL || attr_name[0] == NULC || attr_value == NULL || node->init_value != XML_INIT_DONE) - return false; - - i = XMLNode_search_attribute(node, attr_name, 0); - if (i >= 0) { - pt = node->attributes; - if (pt[i].value != NULL) { - *attr_value = sx_strdup(pt[i].value); - if (*attr_value == NULL) - return false; - } else - *attr_value = NULL; /* NULL but returns 'true' as 'NULL' is the actual attribute value */ - } else if (default_attr_value != NULL) { - *attr_value = sx_strdup(default_attr_value); - if (*attr_value == NULL) - return false; - } else - *attr_value = NULL; - - return true; -} - -int XMLNode_get_attribute_count(const XMLNode* node) -{ - int i, n; - - if (node == NULL || node->init_value != XML_INIT_DONE) - return -1; - - for (i = n = 0; i < node->n_attributes; i++) - if (node->attributes[i].active) n++; - - return n; -} - -int XMLNode_search_attribute(const XMLNode* node, const SXML_CHAR* attr_name, int i_search) -{ - int i; - - if (node == NULL || attr_name == NULL || attr_name[0] == NULC || i_search < 0 || i_search >= node->n_attributes) - return -1; - - for (i = i_search; i < node->n_attributes; i++) - if (node->attributes[i].active && !sx_strcmp(node->attributes[i].name, attr_name)) - return i; - - return -1; -} - -int XMLNode_remove_attribute(XMLNode* node, int i_attr) -{ - XMLAttribute* pt; - if (node == NULL || node->init_value != XML_INIT_DONE || i_attr < 0 || i_attr >= node->n_attributes) - return -1; - - /* Before modifying first see if we run out of memory */ - if (node->n_attributes == 1) - pt = NULL; - else { - pt = (XMLAttribute*)__malloc((node->n_attributes - 1) * sizeof(XMLAttribute)); - if (pt == NULL) - return -1; - } - - /* Can't fail anymore, free item */ - if (node->attributes[i_attr].name != NULL) __free(node->attributes[i_attr].name); - if (node->attributes[i_attr].value != NULL) __free(node->attributes[i_attr].value); - - if (pt != NULL) { - memcpy(pt, node->attributes, i_attr * sizeof(XMLAttribute)); - memcpy(&pt[i_attr], &node->attributes[i_attr + 1], (node->n_attributes - i_attr - 1) * sizeof(XMLAttribute)); - } - if (node->attributes != NULL) - __free(node->attributes); - node->attributes = pt; - node->n_attributes--; - - return node->n_attributes; -} - -int XMLNode_remove_all_attributes(XMLNode* node) -{ - int i; - - if (node == NULL || node->init_value != XML_INIT_DONE) - return false; - - if (node->attributes != NULL) { - for (i = 0; i < node->n_attributes; i++) { - if (node->attributes[i].name != NULL) - __free(node->attributes[i].name); - if (node->attributes[i].value != NULL) - __free(node->attributes[i].value); - } - __free(node->attributes); - node->attributes = NULL; - } - node->n_attributes = 0; - - return true; -} - -int XMLNode_set_text(XMLNode* node, const SXML_CHAR* text) -{ - SXML_CHAR* p; - if (node == NULL || node->init_value != XML_INIT_DONE) - return false; - - if (text == NULL) { /* We want to remove it => free node text */ - if (node->text != NULL) { - __free(node->text); - node->text = NULL; - } - - return true; - } - - p = (SXML_CHAR*)__realloc(node->text, (sx_strlen(text) + 1)*sizeof(SXML_CHAR)); /* +1 for '\0' */ - if (p == NULL) - return false; - node->text = p; - - sx_strcpy(node->text, text); - - return true; -} - -int XMLNode_add_child(XMLNode* node, XMLNode* child) -{ - if (node == NULL || child == NULL || node->init_value != XML_INIT_DONE || child->init_value != XML_INIT_DONE) - return false; - - if (_add_node(&node->children, &node->n_children, child) >= 0) { - node->tag_type = TAG_FATHER; - child->father = node; - return true; - } else - return false; -} - -int XMLNode_get_children_count(const XMLNode* node) -{ - int i, n; - - if (node == NULL || node->init_value != XML_INIT_DONE) - return -1; - - for (i = n = 0; i < node->n_children; i++) - if (node->children[i]->active) n++; - - return n; -} - -XMLNode* XMLNode_get_child(const XMLNode* node, int i_child) -{ - int i; - - if (node == NULL || node->init_value != XML_INIT_DONE || i_child < 0 || i_child >= node->n_children) - return NULL; - - for (i = 0; i < node->n_children; i++) { - if (!node->children[i]->active) - i_child++; - else if (i == i_child) - return node->children[i]; - } - - return NULL; -} - -int XMLNode_remove_child(XMLNode* node, int i_child, int free_child) -{ - int i; - XMLNode** pt; - - if (node == NULL || node->init_value != XML_INIT_DONE || i_child < 0 || i_child >= node->n_children) - return -1; - - /* Lookup 'i_child'th active child */ - for (i = 0; i < node->n_children; i++) { - if (!node->children[i]->active) - i_child++; - else if (i == i_child) - break; - } - if (i >= node->n_children) - return -1; /* Children is not found */ - - /* Before modifying first see if we run out of memory */ - if (node->n_children == 1) - pt = NULL; - else { - pt = (XMLNode**)__malloc((node->n_children - 1) * sizeof(XMLNode*)); - if (pt == NULL) - return -1; - } - - /* Can't fail anymore, free item */ - (void)XMLNode_free(node->children[i_child]); - if (free_child) - __free(node->children[i_child]); - - if (pt != NULL) { - memcpy(pt, node->children, i_child * sizeof(XMLNode*)); - memcpy(&pt[i_child], &node->children[i_child + 1], (node->n_children - i_child - 1) * sizeof(XMLNode*)); - } - if (node->children != NULL) - __free(node->children); - node->children = pt; - node->n_children--; - if (node->n_children == 0) - node->tag_type = TAG_SELF; - - return node->n_children; -} - -int XMLNode_remove_children(XMLNode* node) -{ - int i; - - if (node == NULL || node->init_value != XML_INIT_DONE) - return false; - - if (node->children != NULL) { - for (i = 0; i < node->n_children; i++) - if (node->children[i] != NULL) { - (void)XMLNode_free(node->children[i]); - __free(node->children[i]); - } - __free(node->children); - node->children = NULL; - } - node->n_children = 0; - - return true; -} - -int XMLNode_equal(const XMLNode* node1, const XMLNode* node2) -{ - int i, j; - - if (node1 == node2) - return true; - - if (node1 == NULL || node2 == NULL || node1->init_value != XML_INIT_DONE || node2->init_value != XML_INIT_DONE) - return false; - - if (sx_strcmp(node1->tag, node2->tag)) - return false; - - /* Test all attributes from 'node1' */ - for (i = 0; i < node1->n_attributes; i++) { - if (!node1->attributes[i].active) - continue; - j = XMLNode_search_attribute(node2, node1->attributes[i].name, 0); - if (j < 0) - return false; - if (sx_strcmp(node1->attributes[i].value, node2->attributes[j].value)) - return false; - } - - /* Test other attributes from 'node2' that might not be in 'node1' */ - for (i = 0; i < node2->n_attributes; i++) { - if (!node2->attributes[i].active) - continue; - j = XMLNode_search_attribute(node1, node2->attributes[i].name, 0); - if (j < 0) - return false; - if (sx_strcmp(node2->attributes[i].name, node1->attributes[j].name)) - return false; - } - - return true; -} - -XMLNode* XMLNode_next_sibling(const XMLNode* node) -{ - int i; - XMLNode* father; - - if (node == NULL || node->init_value != XML_INIT_DONE || node->father == NULL) - return NULL; - - father = node->father; - for (i = 0; i < father->n_children && father->children[i] != node; i++) ; - i++; /* father->children[i] is now 'node' next sibling */ - - return i < father->n_children ? father->children[i] : NULL; -} - -static XMLNode* _XMLNode_next(const XMLNode* node, int in_children) -{ - XMLNode* node2; - - if (node == NULL || node->init_value != XML_INIT_DONE) - return NULL; - - /* Check first child */ - if (in_children && node->n_children > 0) - return node->children[0]; - - /* Check next sibling */ - if ((node2 = XMLNode_next_sibling(node)) != NULL) - return node2; - - /* Check next uncle */ - return _XMLNode_next(node->father, false); -} - -XMLNode* XMLNode_next(const XMLNode* node) -{ - return _XMLNode_next(node, true); -} - -/* --- XMLDoc methods --- */ - -int XMLDoc_init(XMLDoc* doc) -{ - if (doc == NULL) - return false; - - doc->filename[0] = NULC; -#ifdef SXMLC_UNICODE - memset(&doc->bom, 0, sizeof(doc->bom)); -#endif - doc->nodes = NULL; - doc->n_nodes = 0; - doc->i_root = -1; - doc->init_value = XML_INIT_DONE; - - return true; -} - -int XMLDoc_free(XMLDoc* doc) -{ - int i; - - if (doc == NULL || doc->init_value != XML_INIT_DONE) - return false; - - for (i = 0; i < doc->n_nodes; i++) { - (void)XMLNode_free(doc->nodes[i]); - __free(doc->nodes[i]); - } - __free(doc->nodes); - doc->nodes = NULL; - doc->n_nodes = 0; - doc->i_root = -1; - - return true; -} - -int XMLDoc_set_root(XMLDoc* doc, int i_root) -{ - if (doc == NULL || doc->init_value != XML_INIT_DONE || i_root < 0 || i_root >= doc->n_nodes) - return false; - - doc->i_root = i_root; - - return true; -} - -int XMLDoc_add_node(XMLDoc* doc, XMLNode* node) -{ - if (doc == NULL || node == NULL || doc->init_value != XML_INIT_DONE) - return -1; - - if (_add_node(&doc->nodes, &doc->n_nodes, node) < 0) - return -1; - - if (node->tag_type == TAG_FATHER) - doc->i_root = doc->n_nodes - 1; /* Main root node is the last father node */ - - return doc->n_nodes; -} - -int XMLDoc_remove_node(XMLDoc* doc, int i_node, int free_node) -{ - XMLNode** pt; - if (doc == NULL || doc->init_value != XML_INIT_DONE || i_node < 0 || i_node > doc->n_nodes) - return false; - - /* Before modifying first see if we run out of memory */ - if (doc->n_nodes == 1) - pt = NULL; - else { - pt = (XMLNode**)__malloc((doc->n_nodes - 1) * sizeof(XMLNode*)); - if (pt == NULL) - return false; - } - - /* Can't fail anymore, free item */ - (void)XMLNode_free(doc->nodes[i_node]); - if (free_node) __free(doc->nodes[i_node]); - - if (pt != NULL) { - memcpy(pt, &doc->nodes[i_node], i_node * sizeof(XMLNode*)); - memcpy(&pt[i_node], &doc->nodes[i_node + 1], (doc->n_nodes - i_node - 1) * sizeof(XMLNode*)); - } - - if (doc->nodes != NULL) - __free(doc->nodes); - doc->nodes = pt; - doc->n_nodes--; - - return true; -} - -/* - Helper functions to print formatting before a new tag. - Returns the new number of characters in the line. - */ -static int _count_new_char_line(const SXML_CHAR* str, int nb_char_tab, int cur_sz_line) -{ - for (; *str; str++) { - if (*str == C2SX('\n')) - cur_sz_line = 0; - else if (*str == C2SX('\t')) - cur_sz_line += nb_char_tab; - else - cur_sz_line++; - } - - return cur_sz_line; -} -static int _print_formatting(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, int nb_char_tab, int cur_sz_line) -{ - if (tag_sep != NULL) { - sx_fprintf(f, tag_sep); - cur_sz_line = _count_new_char_line(tag_sep, nb_char_tab, cur_sz_line); - } - if (child_sep != NULL) { - for (node = node->father; node != NULL; node = node->father) { - sx_fprintf(f, child_sep); - cur_sz_line = _count_new_char_line(child_sep, nb_char_tab, cur_sz_line); - } - } - - return cur_sz_line; -} - -static int _XMLNode_print_header(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int sz_line, int cur_sz_line, int nb_char_tab) -{ - int i; - SXML_CHAR* p; - - if (node == NULL || f == NULL || !node->active || node->tag == NULL || node->tag[0] == NULC) - return -1; - - /* Special handling of DOCTYPE */ - if (node->tag_type == TAG_DOCTYPE) { - /* Search for an unescaped '[' in the DOCTYPE definition, in which case the end delimiter should be ']>' instead of '>' */ - for (p = sx_strchr(node->tag, C2SX('[')); p != NULL && *(p-1) == C2SX('\\'); p = sx_strchr(p+1, C2SX('['))) ; - cur_sz_line += sx_fprintf(f, C2SX(""), node->tag, p != NULL ? C2SX("]") : C2SX("")); - return cur_sz_line; - } - - /* Check for special tags first */ - for (i = 0; i < NB_SPECIAL_TAGS; i++) { - if (node->tag_type == _spec[i].tag_type) { - sx_fprintf(f, C2SX("%s%s%s"), _spec[i].start, node->tag, _spec[i].end); - cur_sz_line += sx_strlen(_spec[i].start) + sx_strlen(node->tag) + sx_strlen(_spec[i].end); - return cur_sz_line; - } - } - - /* Check for user tags */ - for (i = 0; i < _user_tags.n_tags; i++) { - if (node->tag_type == _user_tags.tags[i].tag_type) { - sx_fprintf(f, C2SX("%s%s%s"), _user_tags.tags[i].start, node->tag, _user_tags.tags[i].end); - cur_sz_line += sx_strlen(_user_tags.tags[i].start) + sx_strlen(node->tag) + sx_strlen(_user_tags.tags[i].end); - return cur_sz_line; - } - } - - /* Print tag name */ - cur_sz_line += sx_fprintf(f, C2SX("<%s"), node->tag); - - /* Print attributes */ - for (i = 0; i < node->n_attributes; i++) { - if (!node->attributes[i].active) - continue; - cur_sz_line += sx_strlen(node->attributes[i].name) + sx_strlen(node->attributes[i].value) + 3; - if (sz_line > 0 && cur_sz_line > sz_line) { - cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line); - /* Add extra separator, as if new line was a child of the previous one */ - if (child_sep != NULL) { - sx_fprintf(f, child_sep); - cur_sz_line = _count_new_char_line(child_sep, nb_char_tab, cur_sz_line); - } - } - /* Attribute name */ - if (attr_sep != NULL) { - sx_fprintf(f, attr_sep); - cur_sz_line = _count_new_char_line(attr_sep, nb_char_tab, cur_sz_line); - sx_fprintf(f, C2SX("%s="), node->attributes[i].name); - } else - sx_fprintf(f, C2SX(" %s="), node->attributes[i].name); - - /* Attribute value */ - (void)sx_fputc(XML_DEFAULT_QUOTE, f); - cur_sz_line += fprintHTML(f, node->attributes[i].value) + 2; - (void)sx_fputc(XML_DEFAULT_QUOTE, f); - } - - /* End the tag if there are no children and no text */ - if (node->n_children == 0 && (node->text == NULL || node->text[0] == NULC)) { - cur_sz_line += sx_fprintf(f, C2SX("/>")); - } else { - (void)sx_fputc(C2SX('>'), f); - cur_sz_line++; - } - - return cur_sz_line; -} - -int XMLNode_print_header(const XMLNode* node, FILE* f, int sz_line, int nb_char_tab) -{ - return _XMLNode_print_header(node, f, NULL, NULL, NULL, sz_line, 0, nb_char_tab) < 0 ? false : true; -} - -static int _XMLNode_print(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int cur_sz_line, int nb_char_tab, int depth) -{ - int i; - SXML_CHAR* p; - - if (node != NULL && node->tag_type==TAG_TEXT) { /* Text has to be printed: check if it is only spaces */ - if (!keep_text_spaces) { - for (p = node->text; *p != NULC && sx_isspace(*p); p++) ; /* 'p' points to first non-space character, or to '\0' if only spaces */ - } else - p = node->text; /* '*p' won't be '\0' */ - if (*p != NULC) - cur_sz_line += fprintHTML(f, node->text); - return cur_sz_line; - } - - if (node == NULL || f == NULL || !node->active || node->tag == NULL || node->tag[0] == NULC) - return -1; - - if (nb_char_tab <= 0) - nb_char_tab = 1; - - /* Print formatting */ - if (depth < 0) /* UGLY HACK: 'depth' forced negative on very first line so we don't print an extra 'tag_sep' (usually "\n" when pretty-printing) */ - depth = 0; - else - cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line); - - _XMLNode_print_header(node, f, tag_sep, child_sep, attr_sep, sz_line, cur_sz_line, nb_char_tab); - - if (node->text != NULL && node->text[0] != NULC) { - /* Text has to be printed: check if it is only spaces */ - if (!keep_text_spaces) { - for (p = node->text; *p != NULC && sx_isspace(*p); p++) ; /* 'p' points to first non-space character, or to '\0' if only spaces */ - } else - p = node->text; /* '*p' won't be '\0' */ - if (*p != NULC) cur_sz_line += fprintHTML(f, node->text); - } else if (node->n_children <= 0) /* Everything has already been printed */ - return cur_sz_line; - - /* Recursively print children */ - for (i = 0; i < node->n_children; i++) - (void)_XMLNode_print(node->children[i], f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, cur_sz_line, nb_char_tab, depth+1); - - /* Print tag end after children */ - /* Print formatting */ - if (node->n_children > 0) - cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line); - cur_sz_line += sx_fprintf(f, C2SX(""), node->tag); - - return cur_sz_line; -} - -int XMLNode_print_attr_sep(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab) -{ - return _XMLNode_print(node, f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, 0, nb_char_tab, 0); -} - -int XMLDoc_print_attr_sep(const XMLDoc* doc, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab) -{ - int i, depth, cur_sz_line; - - if (doc == NULL || f == NULL || doc->init_value != XML_INIT_DONE) - return false; - -#ifdef SXMLC_UNICODE - /* Write BOM if it exist */ - if (doc->sz_bom > 0) fwrite(doc->bom, sizeof(unsigned char), doc->sz_bom, f); -#endif - - depth = -1; /* UGLY HACK: 'depth' forced negative on very first line so we don't print an extra 'tag_sep' (usually "\n") */ - for (i = 0, cur_sz_line = 0; i < doc->n_nodes; i++) { - cur_sz_line = _XMLNode_print(doc->nodes[i], f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, cur_sz_line, nb_char_tab, depth); - depth = 0; - } - /* TODO: Find something more graceful than 'depth=-1', even though everyone knows I probably never will ;) */ - - return true; -} - -/* --- */ - -int XML_parse_attribute_to(const SXML_CHAR* str, int to, XMLAttribute* xmlattr) -{ - const SXML_CHAR *p; - int i, n0, n1, remQ = 0; - int ret = 1; - SXML_CHAR quote = '\0'; - - if (str == NULL || xmlattr == NULL) - return 0; - - if (to < 0) - to = sx_strlen(str) - 1; - - /* Search for the '=' */ - /* 'n0' is where the attribute name stops, 'n1' is where the attribute value starts */ - for (n0 = 0; n0 != to && str[n0] != C2SX('=') && !sx_isspace(str[n0]); n0++) ; /* Search for '=' or a space */ - for (n1 = n0; n1 != to && sx_isspace(str[n1]); n1++) ; /* Search for something not a space */ - if (str[n1] != C2SX('=')) - return 0; /* '=' not found: malformed string */ - for (n1++; n1 != to && sx_isspace(str[n1]); n1++) ; /* Search for something not a space */ - if (isquote(str[n1])) { /* Remove quotes */ - quote = str[n1]; - remQ = 1; - } - - xmlattr->name = (SXML_CHAR*)__malloc((n0+1)*sizeof(SXML_CHAR)); - xmlattr->value = (SXML_CHAR*)__malloc((to+1 - n1 - remQ + 1) * sizeof(SXML_CHAR)); - xmlattr->active = true; - if (xmlattr->name != NULL && xmlattr->value != NULL) { - /* Copy name */ - sx_strncpy(xmlattr->name, str, n0); - xmlattr->name[n0] = NULC; - /* (void)str_unescape(xmlattr->name); do not unescape the name */ - /* Copy value (p starts after the quote (if any) and stops at the end of 'str' - (skipping the quote if any, hence the '*(p+remQ)') */ - for (i = 0, p = str + n1 + remQ; i + n1 + remQ < to && *(p+remQ) != NULC; i++, p++) - xmlattr->value[i] = *p; - xmlattr->value[i] = NULC; - (void)html2str(xmlattr->value, NULL); /* Convert HTML escape sequences, do not str_unescape(xmlattr->value) */ - if (remQ && *p != quote) - ret = 2; /* Quote at the beginning but not at the end: probable presence of '>' inside attribute value, so we need to read more data! */ - } else - ret = 0; - - if (ret == 0) { - if (xmlattr->name != NULL) { - __free(xmlattr->name); - xmlattr->name = NULL; - } - if (xmlattr->value != NULL) { - __free(xmlattr->value); - xmlattr->value = NULL; - } - } - - return ret; -} - -static TagType _parse_special_tag(const SXML_CHAR* str, int len, _TAG* tag, XMLNode* node) -{ - if (sx_strncmp(str, tag->start, tag->len_start)) - return TAG_NONE; - - if (sx_strncmp(str + len - tag->len_end, tag->end, tag->len_end)) /* There probably is a '>' inside the tag */ - return TAG_PARTIAL; - - node->tag = (SXML_CHAR*)__malloc((len - tag->len_start - tag->len_end + 1)*sizeof(SXML_CHAR)); - if (node->tag == NULL) - return TAG_NONE; - sx_strncpy(node->tag, str + tag->len_start, len - tag->len_start - tag->len_end); - node->tag[len - tag->len_start - tag->len_end] = NULC; - node->tag_type = tag->tag_type; - - return node->tag_type; -} - -/* - Reads a string that is supposed to be an xml tag like '' or ''. - Fills the 'xmlnode' structure with the tag name and its attributes. - Returns 'TAG_ERROR' if an error occurred (malformed 'str' or memory). 'TAG_*' when string is recognized. - */ -TagType XML_parse_1string(const SXML_CHAR* str, XMLNode* xmlnode) -{ - SXML_CHAR *p; - XMLAttribute* pt; - int n, nn, len, rc, tag_end = 0; - - if (str == NULL || xmlnode == NULL) - return TAG_ERROR; - len = sx_strlen(str); - - /* Check for malformed string */ - if (str[0] != C2SX('<') || str[len-1] != C2SX('>')) - return TAG_ERROR; - - for (nn = 0; nn < NB_SPECIAL_TAGS; nn++) { - n = (int)_parse_special_tag(str, len, &_spec[nn], xmlnode); - switch (n) { - case TAG_NONE: break; /* Nothing found => do nothing */ - default: return (TagType)n; /* Tag found => return it */ - } - } - - /* "" instead of ">" if a '[' is found inside */ - if (str[1] == C2SX('!')) { - /* DOCTYPE */ - if (!sx_strncmp(str, C2SX("" tag end */ - nn = 0; - if (str[n]) { /* '[' was found */ - if (sx_strncmp(str+len-2, C2SX("]>"), 2)) /* There probably is a '>' inside the DOCTYPE */ - return TAG_PARTIAL; - nn = 1; - } - xmlnode->tag = (SXML_CHAR*)__malloc((len - 9 - nn)*sizeof(SXML_CHAR)); /* 'len' - "" + '\0' */ - if (xmlnode->tag == NULL) - return TAG_ERROR; - sx_strncpy(xmlnode->tag, &str[9], len - 10 - nn); - xmlnode->tag[len - 10 - nn] = NULC; - xmlnode->tag_type = TAG_DOCTYPE; - - return TAG_DOCTYPE; - } - } - - /* Test user tags */ - for (nn = 0; nn < _user_tags.n_tags; nn++) { - n = _parse_special_tag(str, len, &_user_tags.tags[nn], xmlnode); - switch (n) { - case TAG_ERROR: return TAG_NONE; /* Error => exit */ - case TAG_NONE: break; /* Nothing found => do nothing */ - default: return (TagType)n; /* Tag found => return it */ - } - } - - if (str[1] == C2SX('/')) - tag_end = 1; - - /* tag starts at index 1 (or 2 if tag end) and ends at the first space or '/>' */ - for (n = 1 + tag_end; str[n] != NULC && str[n] != C2SX('>') && str[n] != C2SX('/') && !sx_isspace(str[n]); n++) ; - xmlnode->tag = (SXML_CHAR*)__malloc((n - tag_end)*sizeof(SXML_CHAR)); - if (xmlnode->tag == NULL) - return TAG_ERROR; - sx_strncpy(xmlnode->tag, &str[1 + tag_end], n - 1 - tag_end); - xmlnode->tag[n - 1 - tag_end] = NULC; - if (tag_end) { - xmlnode->tag_type = TAG_END; - return TAG_END; - } - - /* Here, 'n' is the position of the first space after tag name */ - while (n < len) { - /* Skips spaces */ - while (sx_isspace(str[n])) n++; - - /* Check for XML end ('>' or '/>') */ - if (str[n] == C2SX('>')) { /* Tag with children */ - int type = (str[n-1] == '/' ? TAG_SELF : TAG_FATHER); // TODO: Find something better to cope with - xmlnode->tag_type = type; - return type; - } - if (!sx_strcmp(str+n, C2SX("/>"))) { /* Tag without children */ - xmlnode->tag_type = TAG_SELF; - return TAG_SELF; - } - - /* New attribute found */ - p = sx_strchr(str+n, C2SX('=')); - if (p == NULL) goto parse_err; - pt = (XMLAttribute*)__realloc(xmlnode->attributes, (xmlnode->n_attributes + 1) * sizeof(XMLAttribute)); - if (pt == NULL) goto parse_err; - - pt[xmlnode->n_attributes].name = NULL; - pt[xmlnode->n_attributes].value = NULL; - pt[xmlnode->n_attributes].active = false; - xmlnode->n_attributes++; - xmlnode->attributes = pt; - while (*p != NULC && sx_isspace(*++p)) ; /* Skip spaces */ - if (isquote(*p)) { /* Attribute value starts with a quote, look for next one, ignoring protected ones with '\' */ - for (nn = p-str+1; str[nn] && str[nn] != *p; nn++) { // CHECK UNICODE "nn = p-str+1" - /* if (str[nn] == C2SX('\\')) nn++; [bugs:#7]: '\' is valid in values */ - } - } else { /* Attribute value stops at first space or end of XML string */ - for (nn = p-str+1; str[nn] != NULC && !sx_isspace(str[nn]) && str[nn] != C2SX('/') && str[nn] != C2SX('>'); nn++) ; /* Go to the end of the attribute value */ // CHECK UNICODE - } - - /* Here 'str[nn]' is the character after value */ - /* the attribute definition ('attrName="attrVal"') is between 'str[n]' and 'str[nn]' */ - rc = XML_parse_attribute_to(&str[n], nn - n, &xmlnode->attributes[xmlnode->n_attributes - 1]); - if (!rc) goto parse_err; - if (rc == 2) { /* Probable presence of '>' inside attribute value, which is legal XML. Remove attribute to re-parse it later */ - XMLNode_remove_attribute(xmlnode, xmlnode->n_attributes - 1); - return TAG_PARTIAL; - } - - n = nn + 1; - } - - sx_fprintf(stderr, C2SX("\nWE SHOULD NOT BE HERE!\n[%s]\n\n"), str); - -parse_err: - (void)XMLNode_free(xmlnode); - - return TAG_ERROR; -} - -static int _parse_data_SAX(void* in, const DataSourceType in_type, const SAX_Callbacks* sax, SAX_Data* sd) -{ - SXML_CHAR *line = NULL, *txt_end, *p; - XMLNode node; - int ret, exit, sz, n0, ncr; - TagType tag_type; - int (*meos)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_beob : (int(*)(void*))sx_feof); - - if (sax->start_doc != NULL && !sax->start_doc(sd)) - return true; - if (sax->all_event != NULL && !sax->all_event(XML_EVENT_START_DOC, NULL, (SXML_CHAR*)sd->name, 0, sd)) - return true; - - ret = true; - exit = false; - sd->line_num = 1; /* Line counter, starts at 1 */ - sz = 0; /* 'line' buffer size */ - node.init_value = 0; - (void)XMLNode_init(&node); - while ((n0 = read_line_alloc(in, in_type, &line, &sz, 0, NULC, C2SX('>'), true, C2SX('\n'), &ncr)) != 0) { - (void)XMLNode_free(&node); - for (p = line; *p != NULC && sx_isspace(*p); p++) ; /* Checks if text is only spaces */ - if (*p == NULC) - break; - sd->line_num += ncr; - - /* Get text for 'father' (i.e. what is before '<') */ - while ((txt_end = sx_strchr(line, C2SX('<'))) == NULL) { /* '<' was not found, indicating a probable '>' inside text (should have been escaped with '>' but we'll handle that ;) */ - int n1 = read_line_alloc(in, in_type, &line, &sz, n0, 0, C2SX('>'), true, C2SX('\n'), &ncr); /* Go on reading the file from current position until next '>' */ - sd->line_num += ncr; - if (n1 <= n0) { - ret = false; - if (sax->on_error == NULL && sax->all_event == NULL) - sx_fprintf(stderr, C2SX("%s:%d: MEMORY ERROR.\n"), sd->name, sd->line_num); - else { - if (sax->on_error != NULL && !sax->on_error(PARSE_ERR_MEMORY, sd->line_num, sd)) - break; - if (sax->all_event != NULL && !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_MEMORY, sd)) - break; - } - break; /* 'txt_end' is still NULL here so we'll display the syntax error below */ - } - n0 = n1; - } - if (txt_end == NULL) { /* Missing tag start */ - ret = false; - if (sax->on_error == NULL && sax->all_event == NULL) - sx_fprintf(stderr, C2SX("%s:%d: ERROR: Unexpected end character '>', without matching '<'!\n"), sd->name, sd->line_num); - else { - if (sax->on_error != NULL && !sax->on_error(PARSE_ERR_UNEXPECTED_TAG_END, sd->line_num, sd)) - break; - if (sax->all_event != NULL && !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_UNEXPECTED_TAG_END, sd)) - break; - } - break; - } - /* First part of 'line' (before '<') is to be added to 'father->text' */ - *txt_end = NULC; /* Have 'line' be the text for 'father' */ - if (*line != NULC && (sax->new_text != NULL || sax->all_event != NULL)) { - if (sax->new_text != NULL && (exit = !sax->new_text(line, sd))) /* no str_unescape(line) */ - break; - if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_TEXT, NULL, line, sd->line_num, sd))) - break; - } - *txt_end = '<'; /* Restores tag start */ - - switch (tag_type = XML_parse_1string(txt_end, &node)) { - case TAG_ERROR: /* Memory error */ - ret = false; - if (sax->on_error == NULL && sax->all_event == NULL) - sx_fprintf(stderr, C2SX("%s:%d: MEMORY ERROR.\n"), sd->name, sd->line_num); - else { - if (sax->on_error != NULL && (exit = !sax->on_error(PARSE_ERR_MEMORY, sd->line_num, sd))) - break; - if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_MEMORY, sd))) - break; - } - break; - - case TAG_NONE: /* Syntax error */ - ret = false; - p = sx_strchr(txt_end, C2SX('\n')); - if (p != NULL) - *p = NULC; - if (sax->on_error == NULL && sax->all_event == NULL) { - sx_fprintf(stderr, C2SX("%s:%d: SYNTAX ERROR (%s%s).\n"), sd->name, sd->line_num, txt_end, p == NULL ? C2SX("") : C2SX("...")); - if (p != NULL) - *p = C2SX('\n'); - } else { - if (sax->on_error != NULL && (exit = !sax->on_error(PARSE_ERR_SYNTAX, sd->line_num, sd))) - break; - if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_SYNTAX, sd))) - break; - } - break; - - case TAG_END: - if (sax->end_node != NULL || sax->all_event != NULL) { - if (sax->end_node != NULL && (exit = !sax->end_node(&node, sd))) - break; - if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_END_NODE, &node, NULL, sd->line_num, sd))) - break; - } - break; - - default: /* Add 'node' to 'father' children */ - /* If the line looks like a comment (or CDATA) but is not properly finished, loop until we find the end. */ - while (tag_type == TAG_PARTIAL) { - int n1 = read_line_alloc(in, in_type, &line, &sz, n0, NULC, C2SX('>'), true, C2SX('\n'), &ncr); /* Go on reading the file from current position until next '>' */ - sd->line_num += ncr; - if (n1 <= n0) { - ret = false; - if (sax->on_error == NULL && sax->all_event == NULL) - sx_fprintf(stderr, C2SX("%s:%d: SYNTAX ERROR.\n"), sd->name, sd->line_num); - else { - if (sax->on_error != NULL && (exit = !sax->on_error(meos(in) ? PARSE_ERR_EOF : PARSE_ERR_MEMORY, sd->line_num, sd))) - break; - if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, meos(in) ? PARSE_ERR_EOF : PARSE_ERR_MEMORY, sd))) - break; - } - break; - } - n0 = n1; - txt_end = sx_strchr(line, C2SX('<')); /* In case 'line' has been moved by the '__realloc' in 'read_line_alloc' */ - tag_type = XML_parse_1string(txt_end, &node); - if (tag_type == TAG_ERROR) { - ret = false; - if (sax->on_error == NULL && sax->all_event == NULL) - sx_fprintf(stderr, C2SX("%s:%d: PARSE ERROR.\n"), sd->name, sd->line_num); - else { - if (sax->on_error != NULL && (exit = !sax->on_error(meos(in) ? PARSE_ERR_EOF : PARSE_ERR_SYNTAX, sd->line_num, sd))) - break; - if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, meos(in) ? PARSE_ERR_EOF : PARSE_ERR_SYNTAX, sd))) - break; - } - break; - } - } - if (ret == false) - break; - if (sax->start_node != NULL && (exit = !sax->start_node(&node, sd))) - break; - if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_START_NODE, &node, NULL, sd->line_num, sd))) - break; - if (node.tag_type != TAG_FATHER && (sax->end_node != NULL || sax->all_event != NULL)) { - if (sax->end_node != NULL && (exit = !sax->end_node(&node, sd))) - break; - if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_END_NODE, &node, NULL, sd->line_num, sd))) - break; - } - break; - } - if (exit == true || ret == false || meos(in)) - break; - } - __free(line); - (void)XMLNode_free(&node); - - if (sax->end_doc != NULL && !sax->end_doc(sd)) - return ret; - if (sax->all_event != NULL) - (void)sax->all_event(XML_EVENT_END_DOC, NULL, (SXML_CHAR*)sd->name, sd->line_num, sd); - - return ret; -} - -int SAX_Callbacks_init(SAX_Callbacks* sax) -{ - if (sax == NULL) - return false; - - sax->start_doc = NULL; - sax->start_node = NULL; - sax->end_node = NULL; - sax->new_text = NULL; - sax->on_error = NULL; - sax->end_doc = NULL; - sax->all_event = NULL; - - return true; -} - -int DOMXMLDoc_doc_start(SAX_Data* sd) -{ - DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; - - dom->current = NULL; - dom->error = PARSE_ERR_NONE; - dom->line_error = 0; - - return true; -} - -int DOMXMLDoc_node_start(const XMLNode* node, SAX_Data* sd) -{ - DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; - XMLNode* new_node; - int i; - - if ((new_node = XMLNode_dup(node, true)) == NULL) goto node_start_err; /* No real need to put 'true' for 'XMLNode_dup', but cleaner */ - - if (dom->current == NULL) { - if ((i = _add_node(&dom->doc->nodes, &dom->doc->n_nodes, new_node)) < 0) goto node_start_err; - - if (dom->doc->i_root < 0 && (node->tag_type == TAG_FATHER || node->tag_type == TAG_SELF)) - dom->doc->i_root = i; - } else { - if (_add_node(&dom->current->children, &dom->current->n_children, new_node) < 0) goto node_start_err; - } - - new_node->father = dom->current; - dom->current = new_node; - - return true; - -node_start_err: - dom->error = PARSE_ERR_MEMORY; - dom->line_error = sd->line_num; - (void)XMLNode_free(new_node); - __free(new_node); - - return false; -} - -int DOMXMLDoc_node_end(const XMLNode* node, SAX_Data* sd) -{ - DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; - - if (dom->current == NULL || sx_strcmp(dom->current->tag, node->tag)) { - sx_fprintf(stderr, C2SX("%s:%d: ERROR - End tag was unexpected"), sd->name, sd->line_num, node->tag); - if (dom->current != NULL) - sx_fprintf(stderr, C2SX(" ( was expected)\n"), dom->current->tag); - else - sx_fprintf(stderr, C2SX(" (no node to end)\n")); - - dom->error = PARSE_ERR_UNEXPECTED_NODE_END; - dom->line_error = sd->line_num; - - return false; - } - - dom->current = dom->current->father; - - return true; -} - -int DOMXMLDoc_node_text(SXML_CHAR* text, SAX_Data* sd) -{ - SXML_CHAR* p = text; - DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; - - /* Keep text, even if it is only spaces */ -#if 0 - while(*p != NULC && sx_isspace(*p++)) ; - if (*p == NULC) return true; /* Only spaces */ -#endif - - /* If there is no current node to add text to, raise an error, except if text is only spaces, in which case it is probably just formatting */ - if (dom->current == NULL) { - while(*p != NULC && sx_isspace(*p++)) ; - if (*p == NULC) /* Only spaces => probably pretty-printing */ - return true; - dom->error = PARSE_ERR_TEXT_OUTSIDE_NODE; - dom->line_error = sd->line_num; - return false; /* There is some "real" text => raise an error */ - } - - if (dom->text_as_nodes) { - XMLNode* new_node = XMLNode_allocN(1); - if (new_node == NULL || (new_node->text = sx_strdup(text)) == NULL - || _add_node(&dom->current->children, &dom->current->n_children, new_node) < 0) { - dom->error = PARSE_ERR_MEMORY; - dom->line_error = sd->line_num; - (void)XMLNode_free(new_node); - __free(new_node); - return false; - } - new_node->tag_type = TAG_TEXT; - new_node->father = dom->current; - //dom->current->tag_type = TAG_FATHER; // OS: should parent field be forced to be TAG_FATHER? now it has at least one TAG_TEXT child. I decided not to enforce this to enforce backward-compatibility related to tag_types - return true; - } else { /* Old behaviour: concatenate text to the previous one */ - /* 'p' will point at the new text */ - if (dom->current->text == NULL) { - p = sx_strdup(text); - } else { - p = (SXML_CHAR*)__realloc(dom->current->text, (sx_strlen(dom->current->text) + sx_strlen(text) + 1)*sizeof(SXML_CHAR)); - if (p != NULL) - sx_strcat(p, text); - } - if (p == NULL) { - dom->error = PARSE_ERR_MEMORY; - dom->line_error = sd->line_num; - return false; - } - - dom->current->text = p; - } - - return true; -} - -int DOMXMLDoc_parse_error(ParseError error_num, int line_number, SAX_Data* sd) -{ - DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; - - dom->error = error_num; - dom->line_error = line_number; - - /* Complete error message will be displayed in 'DOMXMLDoc_doc_end' callback */ - - return false; /* Stop on error */ -} - -int DOMXMLDoc_doc_end(SAX_Data* sd) -{ - DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; - - if (dom->error != PARSE_ERR_NONE) { - SXML_CHAR* msg; - - switch (dom->error) { - case PARSE_ERR_MEMORY: msg = C2SX("MEMORY"); break; - case PARSE_ERR_UNEXPECTED_TAG_END: msg = C2SX("UNEXPECTED_TAG_END"); break; - case PARSE_ERR_SYNTAX: msg = C2SX("SYNTAX"); break; - case PARSE_ERR_EOF: msg = C2SX("UNEXPECTED_END_OF_FILE"); break; - case PARSE_ERR_TEXT_OUTSIDE_NODE: msg = C2SX("TEXT_OUTSIDE_NODE"); break; - case PARSE_ERR_UNEXPECTED_NODE_END: msg = C2SX("UNEXPECTED_NODE_END"); break; - default: msg = C2SX("UNKNOWN"); break; - } - sx_fprintf(stderr, C2SX("%s:%d: An error was found (%s), loading aborted...\n"), sd->name, dom->line_error, msg); - dom->current = NULL; - (void)XMLDoc_free(dom->doc); - dom->doc = NULL; - } - - return true; -} - -int SAX_Callbacks_init_DOM(SAX_Callbacks* sax) -{ - if (sax == NULL) - return false; - - sax->start_doc = DOMXMLDoc_doc_start; - sax->start_node = DOMXMLDoc_node_start; - sax->end_node = DOMXMLDoc_node_end; - sax->new_text = DOMXMLDoc_node_text; - sax->on_error = DOMXMLDoc_parse_error; - sax->end_doc = DOMXMLDoc_doc_end; - sax->all_event = NULL; - - return true; -} - -int XMLDoc_parse_file_SAX(const SXML_CHAR* filename, const SAX_Callbacks* sax, void* user) -{ - FILE* f; - int ret; - SAX_Data sd; - SXML_CHAR* fmode = -#ifndef SXMLC_UNICODE - C2SX("rt"); -#else - C2SX("rb"); /* In Unicode, open the file as binary so that further 'fgetwc' read all bytes */ - BOM_TYPE bom; -#endif - - - if (sax == NULL || filename == NULL || filename[0] == NULC) - return false; - - f = sx_fopen(filename, fmode); - if (f == NULL) - return false; - /* Microsoft' 'ftell' returns invalid position for Unicode text files - (see http://connect.microsoft.com/VisualStudio/feedback/details/369265/ftell-ftell-nolock-incorrectly-handling-unicode-text-translation) - However, we're opening the file as binary in Unicode so we don't fall into that case... - */ - #if defined(SXMLC_UNICODE) && (defined(WIN32) || defined(WIN64)) - //setvbuf(f, NULL, _IONBF, 0); - #endif - - sd.name = (SXML_CHAR*)filename; - sd.user = user; -#ifdef SXMLC_UNICODE - bom = freadBOM(f, NULL, NULL); /* Skip BOM, if any */ - /* In Unicode, re-open the file in text-mode if there is no BOM (or UTF-8) as we assume that - the file is "plain" text (i.e. 1 byte = 1 character). If opened in binary mode, 'fgetwc' - would read 2 bytes for 1 character, which would not work on "plain" files. */ - if (bom == BOM_NONE || bom == BOM_UTF_8) { - sx_fclose(f); - f = sx_fopen(filename, C2SX("rt")); - if (f == NULL) - return false; - if (bom == BOM_UTF_8) - freadBOM(f, NULL, NULL); /* Skip the UTF-8 BOM that was found */ - } -#endif - ret = _parse_data_SAX((void*)f, DATA_SOURCE_FILE, sax, &sd); - (void)sx_fclose(f); - - return ret; -} - -int XMLDoc_parse_buffer_SAX(const SXML_CHAR* buffer, const SXML_CHAR* name, const SAX_Callbacks* sax, void* user) -{ - DataSourceBuffer dsb = { buffer, 0 }; - SAX_Data sd; - - if (sax == NULL || buffer == NULL) - return false; - - sd.name = name; - sd.user = user; - return _parse_data_SAX((void*)&dsb, DATA_SOURCE_BUFFER, sax, &sd); -} - -int XMLDoc_parse_file_DOM_text_as_nodes(const SXML_CHAR* filename, XMLDoc* doc, int text_as_nodes) -{ - DOM_through_SAX dom; - SAX_Callbacks sax; - - if (doc == NULL || filename == NULL || filename[0] == NULC || doc->init_value != XML_INIT_DONE) - return false; - - sx_strncpy(doc->filename, filename, SXMLC_MAX_PATH - 1); - doc->filename[SXMLC_MAX_PATH - 1] = NULC; - - /* Read potential BOM on file, only when unicode is defined */ -#ifdef SXMLC_UNICODE - { - /* In Unicode, open the file as binary so that further 'fgetwc' read all bytes */ - FILE* f = sx_fopen(filename, C2SX("rb")); - if (f != NULL) { - #if defined(SXMLC_UNICODE) && (defined(WIN32) || defined(WIN64)) - //setvbuf(f, NULL, _IONBF, 0); - #endif - doc->bom_type = freadBOM(f, doc->bom, &doc->sz_bom); - sx_fclose(f); - } - } -#endif - - dom.doc = doc; - dom.current = NULL; - dom.text_as_nodes = text_as_nodes; - SAX_Callbacks_init_DOM(&sax); - - if (!XMLDoc_parse_file_SAX(filename, &sax, &dom)) { - (void)XMLDoc_free(doc); - dom.doc = NULL; - return false; - } - - return true; -} - -int XMLDoc_parse_buffer_DOM_text_as_nodes(const SXML_CHAR* buffer, const SXML_CHAR* name, XMLDoc* doc, int text_as_nodes) -{ - DOM_through_SAX dom; - SAX_Callbacks sax; - - if (doc == NULL || buffer == NULL || doc->init_value != XML_INIT_DONE) - return false; - - dom.doc = doc; - dom.current = NULL; - dom.text_as_nodes = text_as_nodes; - SAX_Callbacks_init_DOM(&sax); - - return XMLDoc_parse_buffer_SAX(buffer, name, &sax, &dom) ? true : XMLDoc_free(doc); -} - - - -/* --- Utility functions (ex sxmlutils.c) --- */ - -#ifdef DBG_MEM -static int nb_alloc = 0, nb_free = 0; - -void* __malloc(size_t sz) -{ - void* p = malloc(sz); - if (p != NULL) - nb_alloc++; - printf("0x%x: MALLOC (%d) - NA %d - NF %d = %d\n", p, sz, nb_alloc, nb_free, nb_alloc - nb_free); - return p; -} - -void* __calloc(size_t count, size_t sz) -{ - void* p = calloc(count, sz); - if (p != NULL) - nb_alloc++; - printf("0x%x: CALLOC (%d, %d) - NA %d - NF %d = %d\n", p, count, sz, nb_alloc, nb_free, nb_alloc - nb_free); - return p; -} - -void* __realloc(void* mem, size_t sz) -{ - void* p = realloc(mem, sz); - if (mem == NULL && p != NULL) - nb_alloc++; - else if (mem != NULL && sz == 0) - nb_free++; - printf("0x%x: REALLOC 0x%x (%d)", p, mem, sz); - if (mem == NULL) - printf(" - NA %d - NF %d = %d", nb_alloc, nb_free, nb_alloc - nb_free); - printf("\n"); - return p; -} - -void __free(void* mem) -{ - nb_free++; - printf("0x%x: FREE - NA %d - NF %d = %d\n", mem, nb_alloc, nb_free, nb_alloc - nb_free); - free(mem); -} - -char* __sx_strdup(const char* s) -{ -/* Mimic the behavior of sx_strdup(), as we can't use it directly here: DBG_MEM is defined - and sx_strdup is this function! (bug #5) */ -#ifdef SXMLC_UNICODE - char* p = wcsdup(s); -#else - char* p = strdup(s); -#endif - if (p != NULL) - nb_alloc++; - printf("0x%x: STRDUP (%d) - NA %d - NF %d = %d\n", p, sx_strlen(s), nb_alloc, nb_free, nb_alloc - nb_free); - return p; -} -#endif - -/* Dictionary of special characters and their HTML equivalent */ -static struct _html_special_dict { - SXML_CHAR chr; /* Original character */ - SXML_CHAR* html; /* Equivalent HTML string */ - int html_len; /* 'sx_strlen(html)' */ -} HTML_SPECIAL_DICT[] = { - { C2SX('<'), C2SX("<"), 4 }, - { C2SX('>'), C2SX(">"), 4 }, - { C2SX('"'), C2SX("""), 6 }, - { C2SX('\''), C2SX("'"), 6 }, - { C2SX('&'), C2SX("&"), 5 }, - { NULC, NULL, 0 }, /* Terminator */ -}; - -int _bgetc(DataSourceBuffer* ds) -{ - if (ds == NULL || ds->buf[ds->cur_pos] == NULC) - return EOF; - - return (int)(ds->buf[ds->cur_pos++]); -} - -int _beob(DataSourceBuffer* ds) -{ - - if (ds == NULL || ds->buf[ds->cur_pos] == NULC) - return true; - - return false; -} - -int read_line_alloc(void* in, DataSourceType in_type, SXML_CHAR** line, int* sz_line, int i0, SXML_CHAR from, SXML_CHAR to, int keep_fromto, SXML_CHAR interest, int* interest_count) -{ - int init_sz = 0; - SXML_CHAR ch, *pt; - int c; - int n, ret; - int (*mgetc)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_bgetc : (int(*)(void*))sx_fgetc); - int (*meos)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_beob : (int(*)(void*))sx_feof); - - if (in == NULL || line == NULL) - return 0; - - if (to == NULC) - to = C2SX('\n'); - /* Search for character 'from' */ - if (interest_count != NULL) - *interest_count = 0; - while (true) { - /* Reaching EOF before 'to' char is not an error but should trigger 'line' alloc and init to '' */ - c = mgetc(in); - ch = (SXML_CHAR)c; - if (c == EOF) - break; - if (interest_count != NULL && ch == interest) - (*interest_count)++; - /* If 'from' is '\0', we stop here */ - if (ch == from || from == NULC) - break; - } - - if (sz_line == NULL) - sz_line = &init_sz; - - if (*line == NULL || *sz_line == 0) { - if (*sz_line == 0) *sz_line = MEM_INCR_RLA; - *line = (SXML_CHAR*)__malloc(*sz_line*sizeof(SXML_CHAR)); - if (*line == NULL) - return 0; - } - if (i0 < 0) - i0 = 0; - if (i0 > *sz_line) - return 0; - - n = i0; - if (c == CSXEOF) { /* EOF reached before 'to' char => return the empty string */ - (*line)[n] = NULC; - return meos(in) ? n : 0; /* Error if not EOF */ - } - if (ch != from || keep_fromto) - (*line)[n++] = ch; - (*line)[n] = NULC; - ret = 0; - while (true) { - if ((c = mgetc(in)) == CSXEOF) { /* EOF or error */ - (*line)[n] = NULC; - ret = meos(in) ? n : 0; - break; - } - ch = (SXML_CHAR)c; - if (interest_count != NULL && ch == interest) - (*interest_count)++; - (*line)[n] = ch; - if (ch != to || (keep_fromto && to != NULC && ch == to)) /* If we reached the 'to' character and we keep it, we still need to add the extra '\0' */ - n++; - if (n >= *sz_line) { /* Too many characters for our line => realloc some more */ - *sz_line += MEM_INCR_RLA; - pt = (SXML_CHAR*)__realloc(*line, *sz_line*sizeof(SXML_CHAR)); - if (pt == NULL) { - ret = 0; - break; - } else - *line = pt; - } - (*line)[n] = NULC; /* If we reached the 'to' character and we want to strip it, 'n' hasn't changed and 'line[n]' (which is 'to') will be replaced by '\0' */ - if (ch == to) { - ret = n; - break; - } - } - -#if 0 /* Automatic buffer resize is deactivated */ - /* Resize line to the exact size */ - pt = (SXML_CHAR*)__realloc(*line, (n+1)*sizeof(SXML_CHAR)); - if (pt != NULL) - *line = pt; -#endif - - return ret; -} - -/* --- */ - -SXML_CHAR* strcat_alloc(SXML_CHAR** src1, const SXML_CHAR* src2) -{ - SXML_CHAR* cat; - int n; - - /* Do not concatenate '*src1' with itself */ - if (src1 == NULL || *src1 == src2) - return NULL; - - /* Concatenate a NULL or empty string */ - if (src2 == NULL || *src2 == NULC) - return *src1; - - n = (*src1 == NULL ? 0 : sx_strlen(*src1)) + sx_strlen(src2) + 1; - cat = (SXML_CHAR*)__realloc(*src1, n*sizeof(SXML_CHAR)); - if (cat == NULL) - return NULL; - if (*src1 == NULL) - *cat = NULC; - *src1 = cat; - sx_strcat(*src1, src2); - - return *src1; -} - -SXML_CHAR* strip_spaces(SXML_CHAR* str, SXML_CHAR repl_sq) -{ - SXML_CHAR* p; - int i, len; - - /* 'p' to the first non-space */ - for (p = str; *p != NULC && sx_isspace(*p); p++) ; /* No need to search for 'protect' as it is not a space */ - len = sx_strlen(str); - for (i = len-1; sx_isspace(str[i]); i--) ; - if (str[i] == C2SX('\\')) /* If last non-space is the protection, keep the last space */ - i++; - str[i+1] = NULC; /* New end of string to last non-space */ - - if (repl_sq == NULC) { - if (p == str && i == len) - return str; /* Nothing to do */ - for (i = 0; (str[i] = *p) != NULC; i++, p++) ; /* Copy 'p' to 'str' */ - return str; - } - - /* Squeeze all spaces with 'repl_sq' */ - i = 0; - while (*p != NULC) { - if (sx_isspace(*p)) { - str[i++] = repl_sq; - while (sx_isspace(*++p)) ; /* Skips all next spaces */ - } else { - if (*p == C2SX('\\')) - p++; - str[i++] = *p++; - } - } - str[i] = NULC; - - return str; -} - -SXML_CHAR* str_unescape(SXML_CHAR* str) -{ - int i, j; - - if (str == NULL) - return NULL; - - for (i = j = 0; str[j]; j++) { - if (str[j] == C2SX('\\')) - j++; - str[i++] = str[j]; - } - - return str; -} - -int split_left_right(SXML_CHAR* str, SXML_CHAR sep, int* l0, int* l1, int* i_sep, int* r0, int* r1, int ignore_spaces, int ignore_quotes) -{ - int n0, n1, is; - SXML_CHAR quote = '\0'; - - if (str == NULL) - return false; - - if (i_sep != NULL) - *i_sep = -1; - - if (!ignore_spaces) /* No sense of ignore quotes if spaces are to be kept */ - ignore_quotes = false; - - /* Parse left part */ - - if (ignore_spaces) { - for (n0 = 0; str[n0] != NULC && sx_isspace(str[n0]); n0++) ; /* Skip head spaces, n0 points to first non-space */ - if (ignore_quotes && isquote(str[n0])) { /* If quote is found, look for next one */ - quote = str[n0++]; /* Quote can be '\'' or '"' */ - for (n1 = n0; str[n1] != NULC && str[n1] != quote; n1++) { - if (str[n1] == C2SX('\\') && str[++n1] == NULC) - break; /* Escape character (can be the last) */ - } - for (is = n1 + 1; str[is] != NULC && sx_isspace(str[is]); is++) ; /* '--' not to take quote into account */ - } else { - for (n1 = n0; str[n1] != NULC && str[n1] != sep && !sx_isspace(str[n1]); n1++) ; /* Search for separator or a space */ - for (is = n1; str[is] != NULC && sx_isspace(str[is]); is++) ; - } - } else { - n0 = 0; - for (n1 = 0; str[n1] != NULC && str[n1] != sep; n1++) ; /* Search for separator only */ - if (str[n1] != sep) /* Separator not found: malformed string */ - return false; - is = n1; - } - - /* Here 'n0' is the start of left member, 'n1' is the character after the end of left member */ - - if (l0 != NULL) - *l0 = n0; - if (l1 != NULL) - *l1 = n1 - 1; - if (i_sep != NULL) - *i_sep = is; - if (str[is] == NULC || str[is+1] == NULC) { /* No separator => empty right member */ - if (r0 != NULL) - *r0 = is; - if (r1 != NULL) - *r1 = is-1; - if (i_sep != NULL) - *i_sep = (str[is] == NULC ? -1 : is); - return true; - } - - /* Parse right part */ - - n0 = is + 1; - if (ignore_spaces) { - for (; str[n0] != NULC && sx_isspace(str[n0]); n0++) ; - if (ignore_quotes && isquote(str[n0])) - quote = str[n0]; - } - - for (n1 = ++n0; str[n1]; n1++) { - if (ignore_quotes && str[n1] == quote) /* Quote was reached */ - break; - if (str[n1] == C2SX('\\') && str[++n1] == NULC) /* Escape character (can be the last) */ - break; - } - if (ignore_quotes && str[n1--] != quote) /* Quote is not the same than earlier, '--' is not to take it into account */ - return false; - if (!ignore_spaces) - while (str[++n1]) ; /* Jump down the end of the string */ - - if (r0 != NULL) - *r0 = n0; - if (r1 != NULL) - *r1 = n1; - - return true; -} - -BOM_TYPE freadBOM(FILE* f, unsigned char* bom, int* sz_bom) -{ - unsigned char c1, c2; - long pos; - - if (f == NULL) - return BOM_NONE; - - /* Save position and try to read and skip BOM if found. If not, go back to save position. */ - pos = ftell(f); - if (pos < 0) - return BOM_NONE; - if (fread(&c1, sizeof(char), 1, f) != 1 || fread(&c2, sizeof(char), 1, f) != 1) { - fseek(f, pos, SEEK_SET); - return BOM_NONE; - } - if (bom != NULL) { - bom[0] = c1; - bom[1] = c2; - bom[2] = '\0'; - if (sz_bom != NULL) - *sz_bom = 2; - } - switch ((unsigned short)(c1 << 8) | c2) { - case (unsigned short)0xfeff: - return BOM_UTF_16BE; - - case (unsigned short)0xfffe: - pos = ftell(f); /* Save current position to get it back if BOM is not UTF-32LE */ - if (pos < 0) - return BOM_UTF_16LE; - if (fread(&c1, sizeof(char), 1, f) != 1 || fread(&c2, sizeof(char), 1, f) != 1) { - fseek(f, pos, SEEK_SET); - return BOM_UTF_16LE; - } - if (c1 == 0x00 && c2 == 0x00) { - if (bom != NULL) - bom[2] = bom[3] = bom[4] = '\0'; - if (sz_bom != NULL) - *sz_bom = 4; - return BOM_UTF_32LE; - } - fseek(f, pos, SEEK_SET); /* fseek(f, -2, SEEK_CUR) is not garanteed on Windows (and actually fail in Unicode...) */ - return BOM_UTF_16LE; - - case (unsigned short)0x0000: - if (fread(&c1, sizeof(char), 1, f) == 1 && fread(&c2, sizeof(char), 1, f) == 1 - && c1 == 0xfe && c2 == 0xff) { - bom[2] = c1; - bom[3] = c2; - bom[4] = '\0'; - if (sz_bom != NULL) - *sz_bom = 4; - return BOM_UTF_32BE; - } - fseek(f, pos, SEEK_SET); - return BOM_NONE; - - case (unsigned short)0xefbb: /* UTF-8? */ - if (fread(&c1, sizeof(char), 1, f) != 1 || c1 != 0xbf) { /* Not UTF-8 */ - fseek(f, pos, SEEK_SET); - if (bom != NULL) - bom[0] = '\0'; - if (sz_bom != NULL) - *sz_bom = 0; - return BOM_NONE; - } - if (bom != NULL) { - bom[2] = c1; - bom[3] = '\0'; - } - if (sz_bom != NULL) - *sz_bom = 3; - return BOM_UTF_8; - - default: /* No BOM, go back */ - fseek(f, pos, SEEK_SET); - if (bom != NULL) - bom[0] = '\0'; - if (sz_bom != NULL) - *sz_bom = 0; - return BOM_NONE; - } -} - -/* --- */ - -SXML_CHAR* html2str(SXML_CHAR* html, SXML_CHAR* str) -{ - SXML_CHAR *ps, *pd; - int i; - - if (html == NULL) return NULL; - - if (str == NULL) str = html; - - /* Look for '&' and matches it to any of the recognized HTML pattern. */ - /* If found, replaces the '&' by the corresponding char. */ - /* 'p2' is the char to analyze, 'p1' is where to insert it */ - for (pd = str, ps = html; *ps; ps++, pd++) { - if (*ps != C2SX('&')) { - if (pd != ps) - *pd = *ps; - continue; - } - - for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) { - if (sx_strncmp(ps, HTML_SPECIAL_DICT[i].html, HTML_SPECIAL_DICT[i].html_len)) - continue; - - *pd = HTML_SPECIAL_DICT[i].chr; - ps += HTML_SPECIAL_DICT[i].html_len-1; - break; - } - /* If no string was found, simply copy the character */ - if (HTML_SPECIAL_DICT[i].chr == NULC && pd != ps) - *pd = *ps; - } - *pd = NULC; - - return str; -} - -/* TODO: Allocate 'html'? */ -SXML_CHAR* str2html(SXML_CHAR* str, SXML_CHAR* html) -{ - SXML_CHAR *ps, *pd; - int i; - - if (str == NULL) - return NULL; - - if (html == str) /* Not handled (yet) */ - return NULL; - - if (html == NULL) { /* Allocate 'html' to the correct size */ - html = __malloc(strlen_html(str) * sizeof(SXML_CHAR)); - if (html == NULL) - return NULL; - } - - for (ps = str, pd = html; *ps; ps++, pd++) { - for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) { - if (*ps == HTML_SPECIAL_DICT[i].chr) { - sx_strcpy(pd, HTML_SPECIAL_DICT[i].html); - pd += HTML_SPECIAL_DICT[i].html_len - 1; - break; - } - } - if (HTML_SPECIAL_DICT[i].chr == NULC && pd != ps) - *pd = *ps; - } - *pd = NULC; - - return html; -} - -int strlen_html(SXML_CHAR* str) -{ - int i, j, n; - - if (str == NULL) - return 0; - - n = 0; - for (i = 0; str[i] != NULC; i++) { - for (j = 0; HTML_SPECIAL_DICT[j].chr; j++) { - if (str[i] == HTML_SPECIAL_DICT[j].chr) { - n += HTML_SPECIAL_DICT[j].html_len; - break; - } - } - if (HTML_SPECIAL_DICT[j].chr == NULC) - n++; - } - - return n; -} - -int fprintHTML(FILE* f, SXML_CHAR* str) -{ - SXML_CHAR* p; - int i, n; - - for (p = str, n = 0; *p != NULC; p++) { - for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) { - if (*p != HTML_SPECIAL_DICT[i].chr) - continue; - sx_fprintf(f, HTML_SPECIAL_DICT[i].html); - n += HTML_SPECIAL_DICT[i].html_len; - break; - } - if (HTML_SPECIAL_DICT[i].chr == NULC) { - (void)sx_fputc(*p, f); - n++; - } - } - - return n; -} - -int regstrcmp(SXML_CHAR* str, SXML_CHAR* pattern) -{ - SXML_CHAR *p, *s; - - if (str == NULL && pattern == NULL) - return true; - - if (str == NULL || pattern == NULL) - return false; - - p = pattern; - s = str; - while (true) { - switch (*p) { - /* Any character matches, go to next one */ - case C2SX('?'): - p++; - s++; - break; - - /* Go to next character in pattern and wait until it is found in 'str' */ - case C2SX('*'): - for (; *p != NULC; p++) { /* Squeeze '**?*??**' to '*' */ - if (*p != C2SX('*') && *p != C2SX('?')) - break; - } - for (; *s != NULC; s++) { - if (*s == *p) - break; - } - break; - - /* NULL character on pattern has to be matched by 'str' */ - case 0: - return *s ? false : true; - - default: - if (*p == C2SX('\\')) /* Escape character */ - p++; - if (*p++ != *s++) /* Characters do not match */ - return false; - break; - } - } - - return false; -} +/* + Copyright (c) 2010, Matthieu Labas + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. + + The views and conclusions contained in the software and documentation are those of the + authors and should not be interpreted as representing official policies, either expressed + or implied, of the FreeBSD Project. +*/ +#if defined(WIN32) || defined(WIN64) +#pragma warning(disable : 4996) +#else +#ifndef strdup +#define _GNU_SOURCE +#endif +#endif + +#include +#include +#include +#include +#include "sxmlc.h" + +/* + Struct defining "special" tags such as "" or "". + These tags are considered having a start and an end with some data in between that will + be stored in the 'tag' member of an XMLNode. + The 'tag_type' member is a constant that is associated to such tag. + All 'len_*' members are basically the "sx_strlen()" of 'start' and 'end' members. + */ +typedef struct _Tag { + TagType tag_type; + SXML_CHAR* start; + int len_start; + SXML_CHAR* end; + int len_end; +} _TAG; + +typedef struct _SpecialTag { + _TAG *tags; + int n_tags; +} SPECIAL_TAG; + +/* + List of "special" tags handled by sxmlc. + NB the "' or ']>'). + */ +static _TAG _spec[] = { + { TAG_INSTR, C2SX(""), 2 }, + { TAG_COMMENT, C2SX(""), 3 }, + { TAG_CDATA, C2SX(""), 3 } +}; +static int NB_SPECIAL_TAGS = (int)(sizeof(_spec) / sizeof(_TAG)); /* Auto computation of number of special tags */ + +/* + User-registered tags. + */ +static SPECIAL_TAG _user_tags = { NULL, 0 }; + +int XML_register_user_tag(TagType tag_type, SXML_CHAR* start, SXML_CHAR* end) +{ + _TAG* p; + int i, n, le; + + if (tag_type < TAG_USER) + return -1; + + if (start == NULL || end == NULL || *start != C2SX('<')) + return -1; + + le = sx_strlen(end); + if (end[le-1] != C2SX('>')) + return -1; + + i = _user_tags.n_tags; + n = i + 1; + p = (_TAG*)__realloc(_user_tags.tags, n * sizeof(_TAG)); + if (p == NULL) + return -1; + + p[i].tag_type = tag_type; + p[i].start = start; + p[i].end = end; + p[i].len_start = sx_strlen(start); + p[i].len_end = le; + _user_tags.tags = p; + _user_tags.n_tags = n; + + return i; +} + +int XML_unregister_user_tag(int i_tag) +{ + _TAG* pt; + + if (i_tag < 0 || i_tag >= _user_tags.n_tags) + return -1; + + if (_user_tags.n_tags == 1) + pt = NULL; + else { + pt = (_TAG*)__malloc((_user_tags.n_tags - 1) * sizeof(_TAG)); + if (pt == NULL) + return -1; + } + + if (pt != NULL) { + memcpy(pt, _user_tags.tags, i_tag * sizeof(_TAG)); + memcpy(&pt[i_tag], &_user_tags.tags[i_tag + 1], (_user_tags.n_tags - i_tag - 1) * sizeof(_TAG)); + } + if (_user_tags.tags != NULL) + __free(_user_tags.tags); + _user_tags.tags = pt; + _user_tags.n_tags--; + + return _user_tags.n_tags; +} + +int XML_get_nb_registered_user_tags(void) +{ + return _user_tags.n_tags; +} + +int XML_get_registered_user_tag(TagType tag_type) +{ + int i; + + for (i = 0; i < _user_tags.n_tags; i++) + if (_user_tags.tags[i].tag_type == tag_type) + return i; + + return -1; +} + +/* --- XMLNode methods --- */ + +/* + Add 'node' to given '*children_array' of '*len_array' elements. + '*len_array' is overwritten with the number of elements in '*children_array' after its reallocation. + Return the index of the newly added 'node' in '*children_array', or '-1' for memory error. + */ +static int _add_node(XMLNode*** children_array, int* len_array, XMLNode* node) +{ + XMLNode** pt = (XMLNode**)__realloc(*children_array, (*len_array+1) * sizeof(XMLNode*)); + + if (pt == NULL) + return -1; + + pt[*len_array] = node; + *children_array = pt; + + return (*len_array)++; +} + +int XMLNode_init(XMLNode* node) +{ + if (node == NULL) + return false; + + if (node->init_value == XML_INIT_DONE) + return true; /*(void)XMLNode_free(node);*/ + + node->tag = NULL; + node->text = NULL; + + node->attributes = NULL; + node->n_attributes = 0; + + node->father = NULL; + node->children = NULL; + node->n_children = 0; + + node->tag_type = TAG_NONE; + node->active = true; + + node->init_value = XML_INIT_DONE; + + return true; +} + +XMLNode* XMLNode_allocN(int n) +{ + int i; + XMLNode* p; + + if (n <= 0) + return NULL; + + p = (XMLNode*)__calloc(n, sizeof(XMLNode)); + if (p == NULL) + return NULL; + + for (i = 0; i < n; i++) + (void)XMLNode_init(&p[i]); + + return p; +} + +XMLNode* XMLNode_dup(const XMLNode* node, int copy_children) +{ + XMLNode* n; + + if (node == NULL) + return NULL; + + n = (XMLNode*)__calloc(1, sizeof(XMLNode)); + if (n == NULL) + return NULL; + + XMLNode_init(n); + if (!XMLNode_copy(n, node, copy_children)) { + XMLNode_free(n); + + return NULL; + } + + return n; +} + +int XMLNode_free(XMLNode* node) +{ + if (node == NULL || node->init_value != XML_INIT_DONE) + return false; + + if (node->tag != NULL) { + __free(node->tag); + node->tag = NULL; + } + + XMLNode_remove_text(node); + XMLNode_remove_all_attributes(node); + XMLNode_remove_children(node); + + node->tag_type = TAG_NONE; + + return true; +} + +int XMLNode_copy(XMLNode* dst, const XMLNode* src, int copy_children) +{ + int i; + + if (dst == NULL || (src != NULL && src->init_value != XML_INIT_DONE)) + return false; + + (void)XMLNode_free(dst); /* 'dst' is freed first */ + + /* NULL 'src' resets 'dst' */ + if (src == NULL) + return true; + + /* Tag */ + if (src->tag != NULL) { + dst->tag = sx_strdup(src->tag); + if (dst->tag == NULL) goto copy_err; + } + + /* Text */ + if (dst->text != NULL) { + dst->text = sx_strdup(src->text); + if (dst->text == NULL) goto copy_err; + } + + /* Attributes */ + if (src->n_attributes > 0) { + dst->attributes = (XMLAttribute*)__calloc(src->n_attributes, sizeof(XMLAttribute)); + if (dst->attributes== NULL) goto copy_err; + dst->n_attributes = src->n_attributes; + for (i = 0; i < src->n_attributes; i++) { + dst->attributes[i].name = sx_strdup(src->attributes[i].name); + dst->attributes[i].value = sx_strdup(src->attributes[i].value); + if (dst->attributes[i].name == NULL || dst->attributes[i].value == NULL) goto copy_err; + dst->attributes[i].active = src->attributes[i].active; + } + } + + dst->tag_type = src->tag_type; + dst->father = src->father; + dst->user = src->user; + dst->active = src->active; + + /* Copy children if required (and there are any) */ + if (copy_children && src->n_children > 0) { + dst->children = (XMLNode**)__calloc(src->n_children, sizeof(XMLNode*)); + if (dst->children == NULL) goto copy_err; + dst->n_children = src->n_children; + for (i = 0; i < src->n_children; i++) { + if (!XMLNode_copy(dst->children[i], src->children[i], true)) goto copy_err; + } + } + + return true; + +copy_err: + (void)XMLNode_free(dst); + + return false; +} + +int XMLNode_set_active(XMLNode* node, int active) +{ + if (node == NULL || node->init_value != XML_INIT_DONE) + return false; + + node->active = active; + + return true; +} + +int XMLNode_set_tag(XMLNode* node, const SXML_CHAR* tag) +{ + SXML_CHAR* newtag; + if (node == NULL || tag == NULL || node->init_value != XML_INIT_DONE) + return false; + + newtag = sx_strdup(tag); + if (newtag == NULL) + return false; + if (node->tag != NULL) __free(node->tag); + node->tag = newtag; + + return true; +} + +int XMLNode_set_type(XMLNode* node, const TagType tag_type) +{ + if (node == NULL || node->init_value != XML_INIT_DONE) + return false; + + switch (tag_type) { + case TAG_ERROR: + case TAG_END: + case TAG_PARTIAL: + case TAG_NONE: + return false; + + default: + node->tag_type = tag_type; + return true; + } +} + +int XMLNode_set_attribute(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR* attr_value) +{ + XMLAttribute* pt; + int i; + + if (node == NULL || attr_name == NULL || attr_name[0] == NULC || node->init_value != XML_INIT_DONE) + return -1; + + i = XMLNode_search_attribute(node, attr_name, 0); + if (i >= 0) { /* Attribute found: update it */ + SXML_CHAR* value = NULL; + if (attr_value != NULL && (value = sx_strdup(attr_value)) == NULL) + return -1; + pt = node->attributes; + if (pt[i].value != NULL) + __free(pt[i].value); + pt[i].value = value; + } else { /* Attribute not found: add it */ + SXML_CHAR* name = sx_strdup(attr_name); + SXML_CHAR* value = (attr_value == NULL ? NULL : sx_strdup(attr_value)); + if (name == NULL || (value == NULL && attr_value != NULL)) { + if (value != NULL) + __free(value); + if (name != NULL) + __free(name); + return -1; + } + i = node->n_attributes; + pt = (XMLAttribute*)__realloc(node->attributes, (i+1) * sizeof(XMLAttribute)); + if (pt == NULL) { + if (value != NULL) + __free(value); + __free(name); + return -1; + } + + pt[i].name = name; + pt[i].value = value; + pt[i].active = true; + node->attributes = pt; + node->n_attributes = i + 1; + } + + return node->n_attributes; +} + +int XMLNode_get_attribute_with_default(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR** attr_value, const SXML_CHAR* default_attr_value) +{ + XMLAttribute* pt; + int i; + + if (node == NULL || attr_name == NULL || attr_name[0] == NULC || attr_value == NULL || node->init_value != XML_INIT_DONE) + return false; + + i = XMLNode_search_attribute(node, attr_name, 0); + if (i >= 0) { + pt = node->attributes; + if (pt[i].value != NULL) { + *attr_value = sx_strdup(pt[i].value); + if (*attr_value == NULL) + return false; + } else + *attr_value = NULL; /* NULL but returns 'true' as 'NULL' is the actual attribute value */ + } else if (default_attr_value != NULL) { + *attr_value = sx_strdup(default_attr_value); + if (*attr_value == NULL) + return false; + } else + *attr_value = NULL; + + return true; +} + +int XMLNode_get_attribute_count(const XMLNode* node) +{ + int i, n; + + if (node == NULL || node->init_value != XML_INIT_DONE) + return -1; + + for (i = n = 0; i < node->n_attributes; i++) + if (node->attributes[i].active) n++; + + return n; +} + +int XMLNode_search_attribute(const XMLNode* node, const SXML_CHAR* attr_name, int i_search) +{ + int i; + + if (node == NULL || attr_name == NULL || attr_name[0] == NULC || i_search < 0 || i_search >= node->n_attributes) + return -1; + + for (i = i_search; i < node->n_attributes; i++) + if (node->attributes[i].active && !sx_strcmp(node->attributes[i].name, attr_name)) + return i; + + return -1; +} + +int XMLNode_remove_attribute(XMLNode* node, int i_attr) +{ + XMLAttribute* pt; + if (node == NULL || node->init_value != XML_INIT_DONE || i_attr < 0 || i_attr >= node->n_attributes) + return -1; + + /* Before modifying first see if we run out of memory */ + if (node->n_attributes == 1) + pt = NULL; + else { + pt = (XMLAttribute*)__malloc((node->n_attributes - 1) * sizeof(XMLAttribute)); + if (pt == NULL) + return -1; + } + + /* Can't fail anymore, free item */ + if (node->attributes[i_attr].name != NULL) __free(node->attributes[i_attr].name); + if (node->attributes[i_attr].value != NULL) __free(node->attributes[i_attr].value); + + if (pt != NULL) { + memcpy(pt, node->attributes, i_attr * sizeof(XMLAttribute)); + memcpy(&pt[i_attr], &node->attributes[i_attr + 1], (node->n_attributes - i_attr - 1) * sizeof(XMLAttribute)); + } + if (node->attributes != NULL) + __free(node->attributes); + node->attributes = pt; + node->n_attributes--; + + return node->n_attributes; +} + +int XMLNode_remove_all_attributes(XMLNode* node) +{ + int i; + + if (node == NULL || node->init_value != XML_INIT_DONE) + return false; + + if (node->attributes != NULL) { + for (i = 0; i < node->n_attributes; i++) { + if (node->attributes[i].name != NULL) + __free(node->attributes[i].name); + if (node->attributes[i].value != NULL) + __free(node->attributes[i].value); + } + __free(node->attributes); + node->attributes = NULL; + } + node->n_attributes = 0; + + return true; +} + +int XMLNode_set_text(XMLNode* node, const SXML_CHAR* text) +{ + SXML_CHAR* p; + if (node == NULL || node->init_value != XML_INIT_DONE) + return false; + + if (text == NULL) { /* We want to remove it => free node text */ + if (node->text != NULL) { + __free(node->text); + node->text = NULL; + } + + return true; + } + + p = (SXML_CHAR*)__realloc(node->text, (sx_strlen(text) + 1)*sizeof(SXML_CHAR)); /* +1 for '\0' */ + if (p == NULL) + return false; + node->text = p; + + sx_strcpy(node->text, text); + + return true; +} + +int XMLNode_add_child(XMLNode* node, XMLNode* child) +{ + if (node == NULL || child == NULL || node->init_value != XML_INIT_DONE || child->init_value != XML_INIT_DONE) + return false; + + if (_add_node(&node->children, &node->n_children, child) >= 0) { + node->tag_type = TAG_FATHER; + child->father = node; + return true; + } else + return false; +} + +int XMLNode_get_children_count(const XMLNode* node) +{ + int i, n; + + if (node == NULL || node->init_value != XML_INIT_DONE) + return -1; + + for (i = n = 0; i < node->n_children; i++) + if (node->children[i]->active) n++; + + return n; +} + +XMLNode* XMLNode_get_child(const XMLNode* node, int i_child) +{ + int i; + + if (node == NULL || node->init_value != XML_INIT_DONE || i_child < 0 || i_child >= node->n_children) + return NULL; + + for (i = 0; i < node->n_children; i++) { + if (!node->children[i]->active) + i_child++; + else if (i == i_child) + return node->children[i]; + } + + return NULL; +} + +int XMLNode_remove_child(XMLNode* node, int i_child, int free_child) +{ + int i; + XMLNode** pt; + + if (node == NULL || node->init_value != XML_INIT_DONE || i_child < 0 || i_child >= node->n_children) + return -1; + + /* Lookup 'i_child'th active child */ + for (i = 0; i < node->n_children; i++) { + if (!node->children[i]->active) + i_child++; + else if (i == i_child) + break; + } + if (i >= node->n_children) + return -1; /* Children is not found */ + + /* Before modifying first see if we run out of memory */ + if (node->n_children == 1) + pt = NULL; + else { + pt = (XMLNode**)__malloc((node->n_children - 1) * sizeof(XMLNode*)); + if (pt == NULL) + return -1; + } + + /* Can't fail anymore, free item */ + (void)XMLNode_free(node->children[i_child]); + if (free_child) + __free(node->children[i_child]); + + if (pt != NULL) { + memcpy(pt, node->children, i_child * sizeof(XMLNode*)); + memcpy(&pt[i_child], &node->children[i_child + 1], (node->n_children - i_child - 1) * sizeof(XMLNode*)); + } + if (node->children != NULL) + __free(node->children); + node->children = pt; + node->n_children--; + if (node->n_children == 0) + node->tag_type = TAG_SELF; + + return node->n_children; +} + +int XMLNode_remove_children(XMLNode* node) +{ + int i; + + if (node == NULL || node->init_value != XML_INIT_DONE) + return false; + + if (node->children != NULL) { + for (i = 0; i < node->n_children; i++) + if (node->children[i] != NULL) { + (void)XMLNode_free(node->children[i]); + __free(node->children[i]); + } + __free(node->children); + node->children = NULL; + } + node->n_children = 0; + + return true; +} + +int XMLNode_equal(const XMLNode* node1, const XMLNode* node2) +{ + int i, j; + + if (node1 == node2) + return true; + + if (node1 == NULL || node2 == NULL || node1->init_value != XML_INIT_DONE || node2->init_value != XML_INIT_DONE) + return false; + + if (sx_strcmp(node1->tag, node2->tag)) + return false; + + /* Test all attributes from 'node1' */ + for (i = 0; i < node1->n_attributes; i++) { + if (!node1->attributes[i].active) + continue; + j = XMLNode_search_attribute(node2, node1->attributes[i].name, 0); + if (j < 0) + return false; + if (sx_strcmp(node1->attributes[i].value, node2->attributes[j].value)) + return false; + } + + /* Test other attributes from 'node2' that might not be in 'node1' */ + for (i = 0; i < node2->n_attributes; i++) { + if (!node2->attributes[i].active) + continue; + j = XMLNode_search_attribute(node1, node2->attributes[i].name, 0); + if (j < 0) + return false; + if (sx_strcmp(node2->attributes[i].name, node1->attributes[j].name)) + return false; + } + + return true; +} + +XMLNode* XMLNode_next_sibling(const XMLNode* node) +{ + int i; + XMLNode* father; + + if (node == NULL || node->init_value != XML_INIT_DONE || node->father == NULL) + return NULL; + + father = node->father; + for (i = 0; i < father->n_children && father->children[i] != node; i++) ; + i++; /* father->children[i] is now 'node' next sibling */ + + return i < father->n_children ? father->children[i] : NULL; +} + +static XMLNode* _XMLNode_next(const XMLNode* node, int in_children) +{ + XMLNode* node2; + + if (node == NULL || node->init_value != XML_INIT_DONE) + return NULL; + + /* Check first child */ + if (in_children && node->n_children > 0) + return node->children[0]; + + /* Check next sibling */ + if ((node2 = XMLNode_next_sibling(node)) != NULL) + return node2; + + /* Check next uncle */ + return _XMLNode_next(node->father, false); +} + +XMLNode* XMLNode_next(const XMLNode* node) +{ + return _XMLNode_next(node, true); +} + +/* --- XMLDoc methods --- */ + +int XMLDoc_init(XMLDoc* doc) +{ + if (doc == NULL) + return false; + + doc->filename[0] = NULC; +#ifdef SXMLC_UNICODE + memset(&doc->bom, 0, sizeof(doc->bom)); +#endif + doc->nodes = NULL; + doc->n_nodes = 0; + doc->i_root = -1; + doc->init_value = XML_INIT_DONE; + + return true; +} + +int XMLDoc_free(XMLDoc* doc) +{ + int i; + + if (doc == NULL || doc->init_value != XML_INIT_DONE) + return false; + + for (i = 0; i < doc->n_nodes; i++) { + (void)XMLNode_free(doc->nodes[i]); + __free(doc->nodes[i]); + } + __free(doc->nodes); + doc->nodes = NULL; + doc->n_nodes = 0; + doc->i_root = -1; + + return true; +} + +int XMLDoc_set_root(XMLDoc* doc, int i_root) +{ + if (doc == NULL || doc->init_value != XML_INIT_DONE || i_root < 0 || i_root >= doc->n_nodes) + return false; + + doc->i_root = i_root; + + return true; +} + +int XMLDoc_add_node(XMLDoc* doc, XMLNode* node) +{ + if (doc == NULL || node == NULL || doc->init_value != XML_INIT_DONE) + return -1; + + if (_add_node(&doc->nodes, &doc->n_nodes, node) < 0) + return -1; + + if (node->tag_type == TAG_FATHER) + doc->i_root = doc->n_nodes - 1; /* Main root node is the last father node */ + + return doc->n_nodes; +} + +int XMLDoc_remove_node(XMLDoc* doc, int i_node, int free_node) +{ + XMLNode** pt; + if (doc == NULL || doc->init_value != XML_INIT_DONE || i_node < 0 || i_node > doc->n_nodes) + return false; + + /* Before modifying first see if we run out of memory */ + if (doc->n_nodes == 1) + pt = NULL; + else { + pt = (XMLNode**)__malloc((doc->n_nodes - 1) * sizeof(XMLNode*)); + if (pt == NULL) + return false; + } + + /* Can't fail anymore, free item */ + (void)XMLNode_free(doc->nodes[i_node]); + if (free_node) __free(doc->nodes[i_node]); + + if (pt != NULL) { + memcpy(pt, &doc->nodes[i_node], i_node * sizeof(XMLNode*)); + memcpy(&pt[i_node], &doc->nodes[i_node + 1], (doc->n_nodes - i_node - 1) * sizeof(XMLNode*)); + } + + if (doc->nodes != NULL) + __free(doc->nodes); + doc->nodes = pt; + doc->n_nodes--; + + return true; +} + +/* + Helper functions to print formatting before a new tag. + Returns the new number of characters in the line. + */ +static int _count_new_char_line(const SXML_CHAR* str, int nb_char_tab, int cur_sz_line) +{ + for (; *str; str++) { + if (*str == C2SX('\n')) + cur_sz_line = 0; + else if (*str == C2SX('\t')) + cur_sz_line += nb_char_tab; + else + cur_sz_line++; + } + + return cur_sz_line; +} +static int _print_formatting(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, int nb_char_tab, int cur_sz_line) +{ + if (tag_sep != NULL) { + sx_fprintf(f, tag_sep); + cur_sz_line = _count_new_char_line(tag_sep, nb_char_tab, cur_sz_line); + } + if (child_sep != NULL) { + for (node = node->father; node != NULL; node = node->father) { + sx_fprintf(f, child_sep); + cur_sz_line = _count_new_char_line(child_sep, nb_char_tab, cur_sz_line); + } + } + + return cur_sz_line; +} + +static int _XMLNode_print_header(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int sz_line, int cur_sz_line, int nb_char_tab) +{ + int i; + SXML_CHAR* p; + + if (node == NULL || f == NULL || !node->active || node->tag == NULL || node->tag[0] == NULC) + return -1; + + /* Special handling of DOCTYPE */ + if (node->tag_type == TAG_DOCTYPE) { + /* Search for an unescaped '[' in the DOCTYPE definition, in which case the end delimiter should be ']>' instead of '>' */ + for (p = sx_strchr(node->tag, C2SX('[')); p != NULL && *(p-1) == C2SX('\\'); p = sx_strchr(p+1, C2SX('['))) ; + cur_sz_line += sx_fprintf(f, C2SX(""), node->tag, p != NULL ? C2SX("]") : C2SX("")); + return cur_sz_line; + } + + /* Check for special tags first */ + for (i = 0; i < NB_SPECIAL_TAGS; i++) { + if (node->tag_type == _spec[i].tag_type) { + sx_fprintf(f, C2SX("%s%s%s"), _spec[i].start, node->tag, _spec[i].end); + cur_sz_line += sx_strlen(_spec[i].start) + sx_strlen(node->tag) + sx_strlen(_spec[i].end); + return cur_sz_line; + } + } + + /* Check for user tags */ + for (i = 0; i < _user_tags.n_tags; i++) { + if (node->tag_type == _user_tags.tags[i].tag_type) { + sx_fprintf(f, C2SX("%s%s%s"), _user_tags.tags[i].start, node->tag, _user_tags.tags[i].end); + cur_sz_line += sx_strlen(_user_tags.tags[i].start) + sx_strlen(node->tag) + sx_strlen(_user_tags.tags[i].end); + return cur_sz_line; + } + } + + /* Print tag name */ + cur_sz_line += sx_fprintf(f, C2SX("<%s"), node->tag); + + /* Print attributes */ + for (i = 0; i < node->n_attributes; i++) { + if (!node->attributes[i].active) + continue; + cur_sz_line += sx_strlen(node->attributes[i].name) + sx_strlen(node->attributes[i].value) + 3; + if (sz_line > 0 && cur_sz_line > sz_line) { + cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line); + /* Add extra separator, as if new line was a child of the previous one */ + if (child_sep != NULL) { + sx_fprintf(f, child_sep); + cur_sz_line = _count_new_char_line(child_sep, nb_char_tab, cur_sz_line); + } + } + /* Attribute name */ + if (attr_sep != NULL) { + sx_fprintf(f, attr_sep); + cur_sz_line = _count_new_char_line(attr_sep, nb_char_tab, cur_sz_line); + sx_fprintf(f, C2SX("%s="), node->attributes[i].name); + } else + sx_fprintf(f, C2SX(" %s="), node->attributes[i].name); + + /* Attribute value */ + (void)sx_fputc(XML_DEFAULT_QUOTE, f); + cur_sz_line += fprintHTML(f, node->attributes[i].value) + 2; + (void)sx_fputc(XML_DEFAULT_QUOTE, f); + } + + /* End the tag if there are no children and no text */ + if (node->n_children == 0 && (node->text == NULL || node->text[0] == NULC)) { + cur_sz_line += sx_fprintf(f, C2SX("/>")); + } else { + (void)sx_fputc(C2SX('>'), f); + cur_sz_line++; + } + + return cur_sz_line; +} + +int XMLNode_print_header(const XMLNode* node, FILE* f, int sz_line, int nb_char_tab) +{ + return _XMLNode_print_header(node, f, NULL, NULL, NULL, sz_line, 0, nb_char_tab) < 0 ? false : true; +} + +static int _XMLNode_print(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int cur_sz_line, int nb_char_tab, int depth) +{ + int i; + SXML_CHAR* p; + + if (node != NULL && node->tag_type==TAG_TEXT) { /* Text has to be printed: check if it is only spaces */ + if (!keep_text_spaces) { + for (p = node->text; *p != NULC && sx_isspace(*p); p++) ; /* 'p' points to first non-space character, or to '\0' if only spaces */ + } else + p = node->text; /* '*p' won't be '\0' */ + if (*p != NULC) + cur_sz_line += fprintHTML(f, node->text); + return cur_sz_line; + } + + if (node == NULL || f == NULL || !node->active || node->tag == NULL || node->tag[0] == NULC) + return -1; + + if (nb_char_tab <= 0) + nb_char_tab = 1; + + /* Print formatting */ + if (depth < 0) /* UGLY HACK: 'depth' forced negative on very first line so we don't print an extra 'tag_sep' (usually "\n" when pretty-printing) */ + depth = 0; + else + cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line); + + _XMLNode_print_header(node, f, tag_sep, child_sep, attr_sep, sz_line, cur_sz_line, nb_char_tab); + + if (node->text != NULL && node->text[0] != NULC) { + /* Text has to be printed: check if it is only spaces */ + if (!keep_text_spaces) { + for (p = node->text; *p != NULC && sx_isspace(*p); p++) ; /* 'p' points to first non-space character, or to '\0' if only spaces */ + } else + p = node->text; /* '*p' won't be '\0' */ + if (*p != NULC) cur_sz_line += fprintHTML(f, node->text); + } else if (node->n_children <= 0) /* Everything has already been printed */ + return cur_sz_line; + + /* Recursively print children */ + for (i = 0; i < node->n_children; i++) + (void)_XMLNode_print(node->children[i], f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, cur_sz_line, nb_char_tab, depth+1); + + /* Print tag end after children */ + /* Print formatting */ + if (node->n_children > 0) + cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line); + cur_sz_line += sx_fprintf(f, C2SX(""), node->tag); + + return cur_sz_line; +} + +int XMLNode_print_attr_sep(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab) +{ + return _XMLNode_print(node, f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, 0, nb_char_tab, 0); +} + +int XMLDoc_print_attr_sep(const XMLDoc* doc, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab) +{ + int i, depth, cur_sz_line; + + if (doc == NULL || f == NULL || doc->init_value != XML_INIT_DONE) + return false; + +#ifdef SXMLC_UNICODE + /* Write BOM if it exist */ + if (doc->sz_bom > 0) fwrite(doc->bom, sizeof(unsigned char), doc->sz_bom, f); +#endif + + depth = -1; /* UGLY HACK: 'depth' forced negative on very first line so we don't print an extra 'tag_sep' (usually "\n") */ + for (i = 0, cur_sz_line = 0; i < doc->n_nodes; i++) { + cur_sz_line = _XMLNode_print(doc->nodes[i], f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, cur_sz_line, nb_char_tab, depth); + depth = 0; + } + /* TODO: Find something more graceful than 'depth=-1', even though everyone knows I probably never will ;) */ + + return true; +} + +/* --- */ + +int XML_parse_attribute_to(const SXML_CHAR* str, int to, XMLAttribute* xmlattr) +{ + const SXML_CHAR *p; + int i, n0, n1, remQ = 0; + int ret = 1; + SXML_CHAR quote = '\0'; + + if (str == NULL || xmlattr == NULL) + return 0; + + if (to < 0) + to = sx_strlen(str) - 1; + + /* Search for the '=' */ + /* 'n0' is where the attribute name stops, 'n1' is where the attribute value starts */ + for (n0 = 0; n0 != to && str[n0] != C2SX('=') && !sx_isspace(str[n0]); n0++) ; /* Search for '=' or a space */ + for (n1 = n0; n1 != to && sx_isspace(str[n1]); n1++) ; /* Search for something not a space */ + if (str[n1] != C2SX('=')) + return 0; /* '=' not found: malformed string */ + for (n1++; n1 != to && sx_isspace(str[n1]); n1++) ; /* Search for something not a space */ + if (isquote(str[n1])) { /* Remove quotes */ + quote = str[n1]; + remQ = 1; + } + + xmlattr->name = (SXML_CHAR*)__malloc((n0+1)*sizeof(SXML_CHAR)); + xmlattr->value = (SXML_CHAR*)__malloc((to+1 - n1 - remQ + 1) * sizeof(SXML_CHAR)); + xmlattr->active = true; + if (xmlattr->name != NULL && xmlattr->value != NULL) { + /* Copy name */ + sx_strncpy(xmlattr->name, str, n0); + xmlattr->name[n0] = NULC; + /* (void)str_unescape(xmlattr->name); do not unescape the name */ + /* Copy value (p starts after the quote (if any) and stops at the end of 'str' + (skipping the quote if any, hence the '*(p+remQ)') */ + for (i = 0, p = str + n1 + remQ; i + n1 + remQ < to && *(p+remQ) != NULC; i++, p++) + xmlattr->value[i] = *p; + xmlattr->value[i] = NULC; + (void)html2str(xmlattr->value, NULL); /* Convert HTML escape sequences, do not str_unescape(xmlattr->value) */ + if (remQ && *p != quote) + ret = 2; /* Quote at the beginning but not at the end: probable presence of '>' inside attribute value, so we need to read more data! */ + } else + ret = 0; + + if (ret == 0) { + if (xmlattr->name != NULL) { + __free(xmlattr->name); + xmlattr->name = NULL; + } + if (xmlattr->value != NULL) { + __free(xmlattr->value); + xmlattr->value = NULL; + } + } + + return ret; +} + +static TagType _parse_special_tag(const SXML_CHAR* str, int len, _TAG* tag, XMLNode* node) +{ + if (sx_strncmp(str, tag->start, tag->len_start)) + return TAG_NONE; + + if (sx_strncmp(str + len - tag->len_end, tag->end, tag->len_end)) /* There probably is a '>' inside the tag */ + return TAG_PARTIAL; + + node->tag = (SXML_CHAR*)__malloc((len - tag->len_start - tag->len_end + 1)*sizeof(SXML_CHAR)); + if (node->tag == NULL) + return TAG_NONE; + sx_strncpy(node->tag, str + tag->len_start, len - tag->len_start - tag->len_end); + node->tag[len - tag->len_start - tag->len_end] = NULC; + node->tag_type = tag->tag_type; + + return node->tag_type; +} + +/* + Reads a string that is supposed to be an xml tag like '' or ''. + Fills the 'xmlnode' structure with the tag name and its attributes. + Returns 'TAG_ERROR' if an error occurred (malformed 'str' or memory). 'TAG_*' when string is recognized. + */ +TagType XML_parse_1string(const SXML_CHAR* str, XMLNode* xmlnode) +{ + SXML_CHAR *p; + XMLAttribute* pt; + int n, nn, len, rc, tag_end = 0; + + if (str == NULL || xmlnode == NULL) + return TAG_ERROR; + len = sx_strlen(str); + + /* Check for malformed string */ + if (str[0] != C2SX('<') || str[len-1] != C2SX('>')) + return TAG_ERROR; + + for (nn = 0; nn < NB_SPECIAL_TAGS; nn++) { + n = (int)_parse_special_tag(str, len, &_spec[nn], xmlnode); + switch (n) { + case TAG_NONE: break; /* Nothing found => do nothing */ + default: return (TagType)n; /* Tag found => return it */ + } + } + + /* "" instead of ">" if a '[' is found inside */ + if (str[1] == C2SX('!')) { + /* DOCTYPE */ + if (!sx_strncmp(str, C2SX("" tag end */ + nn = 0; + if (str[n]) { /* '[' was found */ + if (sx_strncmp(str+len-2, C2SX("]>"), 2)) /* There probably is a '>' inside the DOCTYPE */ + return TAG_PARTIAL; + nn = 1; + } + xmlnode->tag = (SXML_CHAR*)__malloc((len - 9 - nn)*sizeof(SXML_CHAR)); /* 'len' - "" + '\0' */ + if (xmlnode->tag == NULL) + return TAG_ERROR; + sx_strncpy(xmlnode->tag, &str[9], len - 10 - nn); + xmlnode->tag[len - 10 - nn] = NULC; + xmlnode->tag_type = TAG_DOCTYPE; + + return TAG_DOCTYPE; + } + } + + /* Test user tags */ + for (nn = 0; nn < _user_tags.n_tags; nn++) { + n = _parse_special_tag(str, len, &_user_tags.tags[nn], xmlnode); + switch (n) { + case TAG_ERROR: return TAG_NONE; /* Error => exit */ + case TAG_NONE: break; /* Nothing found => do nothing */ + default: return (TagType)n; /* Tag found => return it */ + } + } + + if (str[1] == C2SX('/')) + tag_end = 1; + + /* tag starts at index 1 (or 2 if tag end) and ends at the first space or '/>' */ + for (n = 1 + tag_end; str[n] != NULC && str[n] != C2SX('>') && str[n] != C2SX('/') && !sx_isspace(str[n]); n++) ; + xmlnode->tag = (SXML_CHAR*)__malloc((n - tag_end)*sizeof(SXML_CHAR)); + if (xmlnode->tag == NULL) + return TAG_ERROR; + sx_strncpy(xmlnode->tag, &str[1 + tag_end], n - 1 - tag_end); + xmlnode->tag[n - 1 - tag_end] = NULC; + if (tag_end) { + xmlnode->tag_type = TAG_END; + return TAG_END; + } + + /* Here, 'n' is the position of the first space after tag name */ + while (n < len) { + /* Skips spaces */ + while (sx_isspace(str[n])) n++; + + /* Check for XML end ('>' or '/>') */ + if (str[n] == C2SX('>')) { /* Tag with children */ + int type = (str[n-1] == '/' ? TAG_SELF : TAG_FATHER); // TODO: Find something better to cope with + xmlnode->tag_type = type; + return type; + } + if (!sx_strcmp(str+n, C2SX("/>"))) { /* Tag without children */ + xmlnode->tag_type = TAG_SELF; + return TAG_SELF; + } + + /* New attribute found */ + p = sx_strchr(str+n, C2SX('=')); + if (p == NULL) goto parse_err; + pt = (XMLAttribute*)__realloc(xmlnode->attributes, (xmlnode->n_attributes + 1) * sizeof(XMLAttribute)); + if (pt == NULL) goto parse_err; + + pt[xmlnode->n_attributes].name = NULL; + pt[xmlnode->n_attributes].value = NULL; + pt[xmlnode->n_attributes].active = false; + xmlnode->n_attributes++; + xmlnode->attributes = pt; + while (*p != NULC && sx_isspace(*++p)) ; /* Skip spaces */ + if (isquote(*p)) { /* Attribute value starts with a quote, look for next one, ignoring protected ones with '\' */ + for (nn = p-str+1; str[nn] && str[nn] != *p; nn++) { // CHECK UNICODE "nn = p-str+1" + /* if (str[nn] == C2SX('\\')) nn++; [bugs:#7]: '\' is valid in values */ + } + } else { /* Attribute value stops at first space or end of XML string */ + for (nn = p-str+1; str[nn] != NULC && !sx_isspace(str[nn]) && str[nn] != C2SX('/') && str[nn] != C2SX('>'); nn++) ; /* Go to the end of the attribute value */ // CHECK UNICODE + } + + /* Here 'str[nn]' is the character after value */ + /* the attribute definition ('attrName="attrVal"') is between 'str[n]' and 'str[nn]' */ + rc = XML_parse_attribute_to(&str[n], nn - n, &xmlnode->attributes[xmlnode->n_attributes - 1]); + if (!rc) goto parse_err; + if (rc == 2) { /* Probable presence of '>' inside attribute value, which is legal XML. Remove attribute to re-parse it later */ + XMLNode_remove_attribute(xmlnode, xmlnode->n_attributes - 1); + return TAG_PARTIAL; + } + + n = nn + 1; + } + + sx_fprintf(stderr, C2SX("\nWE SHOULD NOT BE HERE!\n[%s]\n\n"), str); + +parse_err: + (void)XMLNode_free(xmlnode); + + return TAG_ERROR; +} + +static int _parse_data_SAX(void* in, const DataSourceType in_type, const SAX_Callbacks* sax, SAX_Data* sd) +{ + SXML_CHAR *line = NULL, *txt_end, *p; + XMLNode node; + int ret, exit, sz, n0, ncr; + TagType tag_type; + int (*meos)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_beob : (int(*)(void*))sx_feof); + + if (sax->start_doc != NULL && !sax->start_doc(sd)) + return true; + if (sax->all_event != NULL && !sax->all_event(XML_EVENT_START_DOC, NULL, (SXML_CHAR*)sd->name, 0, sd)) + return true; + + ret = true; + exit = false; + sd->line_num = 1; /* Line counter, starts at 1 */ + sz = 0; /* 'line' buffer size */ + node.init_value = 0; + (void)XMLNode_init(&node); + while ((n0 = read_line_alloc(in, in_type, &line, &sz, 0, NULC, C2SX('>'), true, C2SX('\n'), &ncr)) != 0) { + (void)XMLNode_free(&node); + for (p = line; *p != NULC && sx_isspace(*p); p++) ; /* Checks if text is only spaces */ + if (*p == NULC) + break; + sd->line_num += ncr; + + /* Get text for 'father' (i.e. what is before '<') */ + while ((txt_end = sx_strchr(line, C2SX('<'))) == NULL) { /* '<' was not found, indicating a probable '>' inside text (should have been escaped with '>' but we'll handle that ;) */ + int n1 = read_line_alloc(in, in_type, &line, &sz, n0, 0, C2SX('>'), true, C2SX('\n'), &ncr); /* Go on reading the file from current position until next '>' */ + sd->line_num += ncr; + if (n1 <= n0) { + ret = false; + if (sax->on_error == NULL && sax->all_event == NULL) + sx_fprintf(stderr, C2SX("%s:%d: MEMORY ERROR.\n"), sd->name, sd->line_num); + else { + if (sax->on_error != NULL && !sax->on_error(PARSE_ERR_MEMORY, sd->line_num, sd)) + break; + if (sax->all_event != NULL && !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_MEMORY, sd)) + break; + } + break; /* 'txt_end' is still NULL here so we'll display the syntax error below */ + } + n0 = n1; + } + if (txt_end == NULL) { /* Missing tag start */ + ret = false; + if (sax->on_error == NULL && sax->all_event == NULL) + sx_fprintf(stderr, C2SX("%s:%d: ERROR: Unexpected end character '>', without matching '<'!\n"), sd->name, sd->line_num); + else { + if (sax->on_error != NULL && !sax->on_error(PARSE_ERR_UNEXPECTED_TAG_END, sd->line_num, sd)) + break; + if (sax->all_event != NULL && !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_UNEXPECTED_TAG_END, sd)) + break; + } + break; + } + /* First part of 'line' (before '<') is to be added to 'father->text' */ + *txt_end = NULC; /* Have 'line' be the text for 'father' */ + if (*line != NULC && (sax->new_text != NULL || sax->all_event != NULL)) { + if (sax->new_text != NULL && (exit = !sax->new_text(line, sd))) /* no str_unescape(line) */ + break; + if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_TEXT, NULL, line, sd->line_num, sd))) + break; + } + *txt_end = '<'; /* Restores tag start */ + + switch (tag_type = XML_parse_1string(txt_end, &node)) { + case TAG_ERROR: /* Memory error */ + ret = false; + if (sax->on_error == NULL && sax->all_event == NULL) + sx_fprintf(stderr, C2SX("%s:%d: MEMORY ERROR.\n"), sd->name, sd->line_num); + else { + if (sax->on_error != NULL && (exit = !sax->on_error(PARSE_ERR_MEMORY, sd->line_num, sd))) + break; + if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_MEMORY, sd))) + break; + } + break; + + case TAG_NONE: /* Syntax error */ + ret = false; + p = sx_strchr(txt_end, C2SX('\n')); + if (p != NULL) + *p = NULC; + if (sax->on_error == NULL && sax->all_event == NULL) { + sx_fprintf(stderr, C2SX("%s:%d: SYNTAX ERROR (%s%s).\n"), sd->name, sd->line_num, txt_end, p == NULL ? C2SX("") : C2SX("...")); + if (p != NULL) + *p = C2SX('\n'); + } else { + if (sax->on_error != NULL && (exit = !sax->on_error(PARSE_ERR_SYNTAX, sd->line_num, sd))) + break; + if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_SYNTAX, sd))) + break; + } + break; + + case TAG_END: + if (sax->end_node != NULL || sax->all_event != NULL) { + if (sax->end_node != NULL && (exit = !sax->end_node(&node, sd))) + break; + if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_END_NODE, &node, NULL, sd->line_num, sd))) + break; + } + break; + + default: /* Add 'node' to 'father' children */ + /* If the line looks like a comment (or CDATA) but is not properly finished, loop until we find the end. */ + while (tag_type == TAG_PARTIAL) { + int n1 = read_line_alloc(in, in_type, &line, &sz, n0, NULC, C2SX('>'), true, C2SX('\n'), &ncr); /* Go on reading the file from current position until next '>' */ + sd->line_num += ncr; + if (n1 <= n0) { + ret = false; + if (sax->on_error == NULL && sax->all_event == NULL) + sx_fprintf(stderr, C2SX("%s:%d: SYNTAX ERROR.\n"), sd->name, sd->line_num); + else { + if (sax->on_error != NULL && (exit = !sax->on_error(meos(in) ? PARSE_ERR_EOF : PARSE_ERR_MEMORY, sd->line_num, sd))) + break; + if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, meos(in) ? PARSE_ERR_EOF : PARSE_ERR_MEMORY, sd))) + break; + } + break; + } + n0 = n1; + txt_end = sx_strchr(line, C2SX('<')); /* In case 'line' has been moved by the '__realloc' in 'read_line_alloc' */ + tag_type = XML_parse_1string(txt_end, &node); + if (tag_type == TAG_ERROR) { + ret = false; + if (sax->on_error == NULL && sax->all_event == NULL) + sx_fprintf(stderr, C2SX("%s:%d: PARSE ERROR.\n"), sd->name, sd->line_num); + else { + if (sax->on_error != NULL && (exit = !sax->on_error(meos(in) ? PARSE_ERR_EOF : PARSE_ERR_SYNTAX, sd->line_num, sd))) + break; + if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, meos(in) ? PARSE_ERR_EOF : PARSE_ERR_SYNTAX, sd))) + break; + } + break; + } + } + if (ret == false) + break; + if (sax->start_node != NULL && (exit = !sax->start_node(&node, sd))) + break; + if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_START_NODE, &node, NULL, sd->line_num, sd))) + break; + if (node.tag_type != TAG_FATHER && (sax->end_node != NULL || sax->all_event != NULL)) { + if (sax->end_node != NULL && (exit = !sax->end_node(&node, sd))) + break; + if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_END_NODE, &node, NULL, sd->line_num, sd))) + break; + } + break; + } + if (exit == true || ret == false || meos(in)) + break; + } + __free(line); + (void)XMLNode_free(&node); + + if (sax->end_doc != NULL && !sax->end_doc(sd)) + return ret; + if (sax->all_event != NULL) + (void)sax->all_event(XML_EVENT_END_DOC, NULL, (SXML_CHAR*)sd->name, sd->line_num, sd); + + return ret; +} + +int SAX_Callbacks_init(SAX_Callbacks* sax) +{ + if (sax == NULL) + return false; + + sax->start_doc = NULL; + sax->start_node = NULL; + sax->end_node = NULL; + sax->new_text = NULL; + sax->on_error = NULL; + sax->end_doc = NULL; + sax->all_event = NULL; + + return true; +} + +int DOMXMLDoc_doc_start(SAX_Data* sd) +{ + DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; + + dom->current = NULL; + dom->error = PARSE_ERR_NONE; + dom->line_error = 0; + + return true; +} + +int DOMXMLDoc_node_start(const XMLNode* node, SAX_Data* sd) +{ + DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; + XMLNode* new_node; + int i; + + if ((new_node = XMLNode_dup(node, true)) == NULL) goto node_start_err; /* No real need to put 'true' for 'XMLNode_dup', but cleaner */ + + if (dom->current == NULL) { + if ((i = _add_node(&dom->doc->nodes, &dom->doc->n_nodes, new_node)) < 0) goto node_start_err; + + if (dom->doc->i_root < 0 && (node->tag_type == TAG_FATHER || node->tag_type == TAG_SELF)) + dom->doc->i_root = i; + } else { + if (_add_node(&dom->current->children, &dom->current->n_children, new_node) < 0) goto node_start_err; + } + + new_node->father = dom->current; + dom->current = new_node; + + return true; + +node_start_err: + dom->error = PARSE_ERR_MEMORY; + dom->line_error = sd->line_num; + (void)XMLNode_free(new_node); + __free(new_node); + + return false; +} + +int DOMXMLDoc_node_end(const XMLNode* node, SAX_Data* sd) +{ + DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; + + if (dom->current == NULL || sx_strcmp(dom->current->tag, node->tag)) { + sx_fprintf(stderr, C2SX("%s:%d: ERROR - End tag was unexpected"), sd->name, sd->line_num, node->tag); + if (dom->current != NULL) + sx_fprintf(stderr, C2SX(" ( was expected)\n"), dom->current->tag); + else + sx_fprintf(stderr, C2SX(" (no node to end)\n")); + + dom->error = PARSE_ERR_UNEXPECTED_NODE_END; + dom->line_error = sd->line_num; + + return false; + } + + dom->current = dom->current->father; + + return true; +} + +int DOMXMLDoc_node_text(SXML_CHAR* text, SAX_Data* sd) +{ + SXML_CHAR* p = text; + DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; + + /* Keep text, even if it is only spaces */ +#if 0 + while(*p != NULC && sx_isspace(*p++)) ; + if (*p == NULC) return true; /* Only spaces */ +#endif + + /* If there is no current node to add text to, raise an error, except if text is only spaces, in which case it is probably just formatting */ + if (dom->current == NULL) { + while(*p != NULC && sx_isspace(*p++)) ; + if (*p == NULC) /* Only spaces => probably pretty-printing */ + return true; + dom->error = PARSE_ERR_TEXT_OUTSIDE_NODE; + dom->line_error = sd->line_num; + return false; /* There is some "real" text => raise an error */ + } + + if (dom->text_as_nodes) { + XMLNode* new_node = XMLNode_allocN(1); + if (new_node == NULL || (new_node->text = sx_strdup(text)) == NULL + || _add_node(&dom->current->children, &dom->current->n_children, new_node) < 0) { + dom->error = PARSE_ERR_MEMORY; + dom->line_error = sd->line_num; + (void)XMLNode_free(new_node); + __free(new_node); + return false; + } + new_node->tag_type = TAG_TEXT; + new_node->father = dom->current; + //dom->current->tag_type = TAG_FATHER; // OS: should parent field be forced to be TAG_FATHER? now it has at least one TAG_TEXT child. I decided not to enforce this to enforce backward-compatibility related to tag_types + return true; + } else { /* Old behaviour: concatenate text to the previous one */ + /* 'p' will point at the new text */ + if (dom->current->text == NULL) { + p = sx_strdup(text); + } else { + p = (SXML_CHAR*)__realloc(dom->current->text, (sx_strlen(dom->current->text) + sx_strlen(text) + 1)*sizeof(SXML_CHAR)); + if (p != NULL) + sx_strcat(p, text); + } + if (p == NULL) { + dom->error = PARSE_ERR_MEMORY; + dom->line_error = sd->line_num; + return false; + } + + dom->current->text = p; + } + + return true; +} + +int DOMXMLDoc_parse_error(ParseError error_num, int line_number, SAX_Data* sd) +{ + DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; + + dom->error = error_num; + dom->line_error = line_number; + + /* Complete error message will be displayed in 'DOMXMLDoc_doc_end' callback */ + + return false; /* Stop on error */ +} + +int DOMXMLDoc_doc_end(SAX_Data* sd) +{ + DOM_through_SAX* dom = (DOM_through_SAX*)sd->user; + + if (dom->error != PARSE_ERR_NONE) { + SXML_CHAR* msg; + + switch (dom->error) { + case PARSE_ERR_MEMORY: msg = C2SX("MEMORY"); break; + case PARSE_ERR_UNEXPECTED_TAG_END: msg = C2SX("UNEXPECTED_TAG_END"); break; + case PARSE_ERR_SYNTAX: msg = C2SX("SYNTAX"); break; + case PARSE_ERR_EOF: msg = C2SX("UNEXPECTED_END_OF_FILE"); break; + case PARSE_ERR_TEXT_OUTSIDE_NODE: msg = C2SX("TEXT_OUTSIDE_NODE"); break; + case PARSE_ERR_UNEXPECTED_NODE_END: msg = C2SX("UNEXPECTED_NODE_END"); break; + default: msg = C2SX("UNKNOWN"); break; + } + sx_fprintf(stderr, C2SX("%s:%d: An error was found (%s), loading aborted...\n"), sd->name, dom->line_error, msg); + dom->current = NULL; + (void)XMLDoc_free(dom->doc); + dom->doc = NULL; + } + + return true; +} + +int SAX_Callbacks_init_DOM(SAX_Callbacks* sax) +{ + if (sax == NULL) + return false; + + sax->start_doc = DOMXMLDoc_doc_start; + sax->start_node = DOMXMLDoc_node_start; + sax->end_node = DOMXMLDoc_node_end; + sax->new_text = DOMXMLDoc_node_text; + sax->on_error = DOMXMLDoc_parse_error; + sax->end_doc = DOMXMLDoc_doc_end; + sax->all_event = NULL; + + return true; +} + +int XMLDoc_parse_file_SAX(const SXML_CHAR* filename, const SAX_Callbacks* sax, void* user) +{ + FILE* f; + int ret; + SAX_Data sd; + SXML_CHAR* fmode = +#ifndef SXMLC_UNICODE + C2SX("rt"); +#else + C2SX("rb"); /* In Unicode, open the file as binary so that further 'fgetwc' read all bytes */ + BOM_TYPE bom; +#endif + + + if (sax == NULL || filename == NULL || filename[0] == NULC) + return false; + + f = sx_fopen(filename, fmode); + if (f == NULL) + return false; + /* Microsoft' 'ftell' returns invalid position for Unicode text files + (see http://connect.microsoft.com/VisualStudio/feedback/details/369265/ftell-ftell-nolock-incorrectly-handling-unicode-text-translation) + However, we're opening the file as binary in Unicode so we don't fall into that case... + */ + #if defined(SXMLC_UNICODE) && (defined(WIN32) || defined(WIN64)) + //setvbuf(f, NULL, _IONBF, 0); + #endif + + sd.name = (SXML_CHAR*)filename; + sd.user = user; +#ifdef SXMLC_UNICODE + bom = freadBOM(f, NULL, NULL); /* Skip BOM, if any */ + /* In Unicode, re-open the file in text-mode if there is no BOM (or UTF-8) as we assume that + the file is "plain" text (i.e. 1 byte = 1 character). If opened in binary mode, 'fgetwc' + would read 2 bytes for 1 character, which would not work on "plain" files. */ + if (bom == BOM_NONE || bom == BOM_UTF_8) { + sx_fclose(f); + f = sx_fopen(filename, C2SX("rt")); + if (f == NULL) + return false; + if (bom == BOM_UTF_8) + freadBOM(f, NULL, NULL); /* Skip the UTF-8 BOM that was found */ + } +#endif + ret = _parse_data_SAX((void*)f, DATA_SOURCE_FILE, sax, &sd); + (void)sx_fclose(f); + + return ret; +} + +int XMLDoc_parse_buffer_SAX(const SXML_CHAR* buffer, const SXML_CHAR* name, const SAX_Callbacks* sax, void* user) +{ + DataSourceBuffer dsb = { buffer, 0 }; + SAX_Data sd; + + if (sax == NULL || buffer == NULL) + return false; + + sd.name = name; + sd.user = user; + return _parse_data_SAX((void*)&dsb, DATA_SOURCE_BUFFER, sax, &sd); +} + +int XMLDoc_parse_file_DOM_text_as_nodes(const SXML_CHAR* filename, XMLDoc* doc, int text_as_nodes) +{ + DOM_through_SAX dom; + SAX_Callbacks sax; + + if (doc == NULL || filename == NULL || filename[0] == NULC || doc->init_value != XML_INIT_DONE) + return false; + + sx_strncpy(doc->filename, filename, SXMLC_MAX_PATH - 1); + doc->filename[SXMLC_MAX_PATH - 1] = NULC; + + /* Read potential BOM on file, only when unicode is defined */ +#ifdef SXMLC_UNICODE + { + /* In Unicode, open the file as binary so that further 'fgetwc' read all bytes */ + FILE* f = sx_fopen(filename, C2SX("rb")); + if (f != NULL) { + #if defined(SXMLC_UNICODE) && (defined(WIN32) || defined(WIN64)) + //setvbuf(f, NULL, _IONBF, 0); + #endif + doc->bom_type = freadBOM(f, doc->bom, &doc->sz_bom); + sx_fclose(f); + } + } +#endif + + dom.doc = doc; + dom.current = NULL; + dom.text_as_nodes = text_as_nodes; + SAX_Callbacks_init_DOM(&sax); + + if (!XMLDoc_parse_file_SAX(filename, &sax, &dom)) { + (void)XMLDoc_free(doc); + dom.doc = NULL; + return false; + } + + return true; +} + +int XMLDoc_parse_buffer_DOM_text_as_nodes(const SXML_CHAR* buffer, const SXML_CHAR* name, XMLDoc* doc, int text_as_nodes) +{ + DOM_through_SAX dom; + SAX_Callbacks sax; + + if (doc == NULL || buffer == NULL || doc->init_value != XML_INIT_DONE) + return false; + + dom.doc = doc; + dom.current = NULL; + dom.text_as_nodes = text_as_nodes; + SAX_Callbacks_init_DOM(&sax); + + return XMLDoc_parse_buffer_SAX(buffer, name, &sax, &dom) ? true : XMLDoc_free(doc); +} + + + +/* --- Utility functions (ex sxmlutils.c) --- */ + +#ifdef DBG_MEM +static int nb_alloc = 0, nb_free = 0; + +void* __malloc(size_t sz) +{ + void* p = malloc(sz); + if (p != NULL) + nb_alloc++; + printf("0x%x: MALLOC (%d) - NA %d - NF %d = %d\n", p, sz, nb_alloc, nb_free, nb_alloc - nb_free); + return p; +} + +void* __calloc(size_t count, size_t sz) +{ + void* p = calloc(count, sz); + if (p != NULL) + nb_alloc++; + printf("0x%x: CALLOC (%d, %d) - NA %d - NF %d = %d\n", p, count, sz, nb_alloc, nb_free, nb_alloc - nb_free); + return p; +} + +void* __realloc(void* mem, size_t sz) +{ + void* p = realloc(mem, sz); + if (mem == NULL && p != NULL) + nb_alloc++; + else if (mem != NULL && sz == 0) + nb_free++; + printf("0x%x: REALLOC 0x%x (%d)", p, mem, sz); + if (mem == NULL) + printf(" - NA %d - NF %d = %d", nb_alloc, nb_free, nb_alloc - nb_free); + printf("\n"); + return p; +} + +void __free(void* mem) +{ + nb_free++; + printf("0x%x: FREE - NA %d - NF %d = %d\n", mem, nb_alloc, nb_free, nb_alloc - nb_free); + free(mem); +} + +char* __sx_strdup(const char* s) +{ +/* Mimic the behavior of sx_strdup(), as we can't use it directly here: DBG_MEM is defined + and sx_strdup is this function! (bug #5) */ +#ifdef SXMLC_UNICODE + char* p = wcsdup(s); +#else + char* p = strdup(s); +#endif + if (p != NULL) + nb_alloc++; + printf("0x%x: STRDUP (%d) - NA %d - NF %d = %d\n", p, sx_strlen(s), nb_alloc, nb_free, nb_alloc - nb_free); + return p; +} +#endif + +/* Dictionary of special characters and their HTML equivalent */ +static struct _html_special_dict { + SXML_CHAR chr; /* Original character */ + SXML_CHAR* html; /* Equivalent HTML string */ + int html_len; /* 'sx_strlen(html)' */ +} HTML_SPECIAL_DICT[] = { + { C2SX('<'), C2SX("<"), 4 }, + { C2SX('>'), C2SX(">"), 4 }, + { C2SX('"'), C2SX("""), 6 }, + { C2SX('\''), C2SX("'"), 6 }, + { C2SX('&'), C2SX("&"), 5 }, + { NULC, NULL, 0 }, /* Terminator */ +}; + +int _bgetc(DataSourceBuffer* ds) +{ + if (ds == NULL || ds->buf[ds->cur_pos] == NULC) + return EOF; + + return (int)(ds->buf[ds->cur_pos++]); +} + +int _beob(DataSourceBuffer* ds) +{ + + if (ds == NULL || ds->buf[ds->cur_pos] == NULC) + return true; + + return false; +} + +int read_line_alloc(void* in, DataSourceType in_type, SXML_CHAR** line, int* sz_line, int i0, SXML_CHAR from, SXML_CHAR to, int keep_fromto, SXML_CHAR interest, int* interest_count) +{ + int init_sz = 0; + SXML_CHAR ch, *pt; + int c; + int n, ret; + int (*mgetc)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_bgetc : (int(*)(void*))sx_fgetc); + int (*meos)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_beob : (int(*)(void*))sx_feof); + + if (in == NULL || line == NULL) + return 0; + + if (to == NULC) + to = C2SX('\n'); + /* Search for character 'from' */ + if (interest_count != NULL) + *interest_count = 0; + while (true) { + /* Reaching EOF before 'to' char is not an error but should trigger 'line' alloc and init to '' */ + c = mgetc(in); + ch = (SXML_CHAR)c; + if (c == EOF) + break; + if (interest_count != NULL && ch == interest) + (*interest_count)++; + /* If 'from' is '\0', we stop here */ + if (ch == from || from == NULC) + break; + } + + if (sz_line == NULL) + sz_line = &init_sz; + + if (*line == NULL || *sz_line == 0) { + if (*sz_line == 0) *sz_line = MEM_INCR_RLA; + *line = (SXML_CHAR*)__malloc(*sz_line*sizeof(SXML_CHAR)); + if (*line == NULL) + return 0; + } + if (i0 < 0) + i0 = 0; + if (i0 > *sz_line) + return 0; + + n = i0; + if (c == CSXEOF) { /* EOF reached before 'to' char => return the empty string */ + (*line)[n] = NULC; + return meos(in) ? n : 0; /* Error if not EOF */ + } + if (ch != from || keep_fromto) + (*line)[n++] = ch; + (*line)[n] = NULC; + ret = 0; + while (true) { + if ((c = mgetc(in)) == CSXEOF) { /* EOF or error */ + (*line)[n] = NULC; + ret = meos(in) ? n : 0; + break; + } + ch = (SXML_CHAR)c; + if (interest_count != NULL && ch == interest) + (*interest_count)++; + (*line)[n] = ch; + if (ch != to || (keep_fromto && to != NULC && ch == to)) /* If we reached the 'to' character and we keep it, we still need to add the extra '\0' */ + n++; + if (n >= *sz_line) { /* Too many characters for our line => realloc some more */ + *sz_line += MEM_INCR_RLA; + pt = (SXML_CHAR*)__realloc(*line, *sz_line*sizeof(SXML_CHAR)); + if (pt == NULL) { + ret = 0; + break; + } else + *line = pt; + } + (*line)[n] = NULC; /* If we reached the 'to' character and we want to strip it, 'n' hasn't changed and 'line[n]' (which is 'to') will be replaced by '\0' */ + if (ch == to) { + ret = n; + break; + } + } + +#if 0 /* Automatic buffer resize is deactivated */ + /* Resize line to the exact size */ + pt = (SXML_CHAR*)__realloc(*line, (n+1)*sizeof(SXML_CHAR)); + if (pt != NULL) + *line = pt; +#endif + + return ret; +} + +/* --- */ + +SXML_CHAR* strcat_alloc(SXML_CHAR** src1, const SXML_CHAR* src2) +{ + SXML_CHAR* cat; + int n; + + /* Do not concatenate '*src1' with itself */ + if (src1 == NULL || *src1 == src2) + return NULL; + + /* Concatenate a NULL or empty string */ + if (src2 == NULL || *src2 == NULC) + return *src1; + + n = (*src1 == NULL ? 0 : sx_strlen(*src1)) + sx_strlen(src2) + 1; + cat = (SXML_CHAR*)__realloc(*src1, n*sizeof(SXML_CHAR)); + if (cat == NULL) + return NULL; + if (*src1 == NULL) + *cat = NULC; + *src1 = cat; + sx_strcat(*src1, src2); + + return *src1; +} + +SXML_CHAR* strip_spaces(SXML_CHAR* str, SXML_CHAR repl_sq) +{ + SXML_CHAR* p; + int i, len; + + /* 'p' to the first non-space */ + for (p = str; *p != NULC && sx_isspace(*p); p++) ; /* No need to search for 'protect' as it is not a space */ + len = sx_strlen(str); + for (i = len-1; sx_isspace(str[i]); i--) ; + if (str[i] == C2SX('\\')) /* If last non-space is the protection, keep the last space */ + i++; + str[i+1] = NULC; /* New end of string to last non-space */ + + if (repl_sq == NULC) { + if (p == str && i == len) + return str; /* Nothing to do */ + for (i = 0; (str[i] = *p) != NULC; i++, p++) ; /* Copy 'p' to 'str' */ + return str; + } + + /* Squeeze all spaces with 'repl_sq' */ + i = 0; + while (*p != NULC) { + if (sx_isspace(*p)) { + str[i++] = repl_sq; + while (sx_isspace(*++p)) ; /* Skips all next spaces */ + } else { + if (*p == C2SX('\\')) + p++; + str[i++] = *p++; + } + } + str[i] = NULC; + + return str; +} + +SXML_CHAR* str_unescape(SXML_CHAR* str) +{ + int i, j; + + if (str == NULL) + return NULL; + + for (i = j = 0; str[j]; j++) { + if (str[j] == C2SX('\\')) + j++; + str[i++] = str[j]; + } + + return str; +} + +int split_left_right(SXML_CHAR* str, SXML_CHAR sep, int* l0, int* l1, int* i_sep, int* r0, int* r1, int ignore_spaces, int ignore_quotes) +{ + int n0, n1, is; + SXML_CHAR quote = '\0'; + + if (str == NULL) + return false; + + if (i_sep != NULL) + *i_sep = -1; + + if (!ignore_spaces) /* No sense of ignore quotes if spaces are to be kept */ + ignore_quotes = false; + + /* Parse left part */ + + if (ignore_spaces) { + for (n0 = 0; str[n0] != NULC && sx_isspace(str[n0]); n0++) ; /* Skip head spaces, n0 points to first non-space */ + if (ignore_quotes && isquote(str[n0])) { /* If quote is found, look for next one */ + quote = str[n0++]; /* Quote can be '\'' or '"' */ + for (n1 = n0; str[n1] != NULC && str[n1] != quote; n1++) { + if (str[n1] == C2SX('\\') && str[++n1] == NULC) + break; /* Escape character (can be the last) */ + } + for (is = n1 + 1; str[is] != NULC && sx_isspace(str[is]); is++) ; /* '--' not to take quote into account */ + } else { + for (n1 = n0; str[n1] != NULC && str[n1] != sep && !sx_isspace(str[n1]); n1++) ; /* Search for separator or a space */ + for (is = n1; str[is] != NULC && sx_isspace(str[is]); is++) ; + } + } else { + n0 = 0; + for (n1 = 0; str[n1] != NULC && str[n1] != sep; n1++) ; /* Search for separator only */ + if (str[n1] != sep) /* Separator not found: malformed string */ + return false; + is = n1; + } + + /* Here 'n0' is the start of left member, 'n1' is the character after the end of left member */ + + if (l0 != NULL) + *l0 = n0; + if (l1 != NULL) + *l1 = n1 - 1; + if (i_sep != NULL) + *i_sep = is; + if (str[is] == NULC || str[is+1] == NULC) { /* No separator => empty right member */ + if (r0 != NULL) + *r0 = is; + if (r1 != NULL) + *r1 = is-1; + if (i_sep != NULL) + *i_sep = (str[is] == NULC ? -1 : is); + return true; + } + + /* Parse right part */ + + n0 = is + 1; + if (ignore_spaces) { + for (; str[n0] != NULC && sx_isspace(str[n0]); n0++) ; + if (ignore_quotes && isquote(str[n0])) + quote = str[n0]; + } + + for (n1 = ++n0; str[n1]; n1++) { + if (ignore_quotes && str[n1] == quote) /* Quote was reached */ + break; + if (str[n1] == C2SX('\\') && str[++n1] == NULC) /* Escape character (can be the last) */ + break; + } + if (ignore_quotes && str[n1--] != quote) /* Quote is not the same than earlier, '--' is not to take it into account */ + return false; + if (!ignore_spaces) + while (str[++n1]) ; /* Jump down the end of the string */ + + if (r0 != NULL) + *r0 = n0; + if (r1 != NULL) + *r1 = n1; + + return true; +} + +BOM_TYPE freadBOM(FILE* f, unsigned char* bom, int* sz_bom) +{ + unsigned char c1, c2; + long pos; + + if (f == NULL) + return BOM_NONE; + + /* Save position and try to read and skip BOM if found. If not, go back to save position. */ + pos = ftell(f); + if (pos < 0) + return BOM_NONE; + if (fread(&c1, sizeof(char), 1, f) != 1 || fread(&c2, sizeof(char), 1, f) != 1) { + fseek(f, pos, SEEK_SET); + return BOM_NONE; + } + if (bom != NULL) { + bom[0] = c1; + bom[1] = c2; + bom[2] = '\0'; + if (sz_bom != NULL) + *sz_bom = 2; + } + switch ((unsigned short)(c1 << 8) | c2) { + case (unsigned short)0xfeff: + return BOM_UTF_16BE; + + case (unsigned short)0xfffe: + pos = ftell(f); /* Save current position to get it back if BOM is not UTF-32LE */ + if (pos < 0) + return BOM_UTF_16LE; + if (fread(&c1, sizeof(char), 1, f) != 1 || fread(&c2, sizeof(char), 1, f) != 1) { + fseek(f, pos, SEEK_SET); + return BOM_UTF_16LE; + } + if (c1 == 0x00 && c2 == 0x00) { + if (bom != NULL) + bom[2] = bom[3] = bom[4] = '\0'; + if (sz_bom != NULL) + *sz_bom = 4; + return BOM_UTF_32LE; + } + fseek(f, pos, SEEK_SET); /* fseek(f, -2, SEEK_CUR) is not garanteed on Windows (and actually fail in Unicode...) */ + return BOM_UTF_16LE; + + case (unsigned short)0x0000: + if (fread(&c1, sizeof(char), 1, f) == 1 && fread(&c2, sizeof(char), 1, f) == 1 + && c1 == 0xfe && c2 == 0xff) { + bom[2] = c1; + bom[3] = c2; + bom[4] = '\0'; + if (sz_bom != NULL) + *sz_bom = 4; + return BOM_UTF_32BE; + } + fseek(f, pos, SEEK_SET); + return BOM_NONE; + + case (unsigned short)0xefbb: /* UTF-8? */ + if (fread(&c1, sizeof(char), 1, f) != 1 || c1 != 0xbf) { /* Not UTF-8 */ + fseek(f, pos, SEEK_SET); + if (bom != NULL) + bom[0] = '\0'; + if (sz_bom != NULL) + *sz_bom = 0; + return BOM_NONE; + } + if (bom != NULL) { + bom[2] = c1; + bom[3] = '\0'; + } + if (sz_bom != NULL) + *sz_bom = 3; + return BOM_UTF_8; + + default: /* No BOM, go back */ + fseek(f, pos, SEEK_SET); + if (bom != NULL) + bom[0] = '\0'; + if (sz_bom != NULL) + *sz_bom = 0; + return BOM_NONE; + } +} + +/* --- */ + +SXML_CHAR* html2str(SXML_CHAR* html, SXML_CHAR* str) +{ + SXML_CHAR *ps, *pd; + int i; + + if (html == NULL) return NULL; + + if (str == NULL) str = html; + + /* Look for '&' and matches it to any of the recognized HTML pattern. */ + /* If found, replaces the '&' by the corresponding char. */ + /* 'p2' is the char to analyze, 'p1' is where to insert it */ + for (pd = str, ps = html; *ps; ps++, pd++) { + if (*ps != C2SX('&')) { + if (pd != ps) + *pd = *ps; + continue; + } + + for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) { + if (sx_strncmp(ps, HTML_SPECIAL_DICT[i].html, HTML_SPECIAL_DICT[i].html_len)) + continue; + + *pd = HTML_SPECIAL_DICT[i].chr; + ps += HTML_SPECIAL_DICT[i].html_len-1; + break; + } + /* If no string was found, simply copy the character */ + if (HTML_SPECIAL_DICT[i].chr == NULC && pd != ps) + *pd = *ps; + } + *pd = NULC; + + return str; +} + +/* TODO: Allocate 'html'? */ +SXML_CHAR* str2html(SXML_CHAR* str, SXML_CHAR* html) +{ + SXML_CHAR *ps, *pd; + int i; + + if (str == NULL) + return NULL; + + if (html == str) /* Not handled (yet) */ + return NULL; + + if (html == NULL) { /* Allocate 'html' to the correct size */ + html = __malloc(strlen_html(str) * sizeof(SXML_CHAR)); + if (html == NULL) + return NULL; + } + + for (ps = str, pd = html; *ps; ps++, pd++) { + for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) { + if (*ps == HTML_SPECIAL_DICT[i].chr) { + sx_strcpy(pd, HTML_SPECIAL_DICT[i].html); + pd += HTML_SPECIAL_DICT[i].html_len - 1; + break; + } + } + if (HTML_SPECIAL_DICT[i].chr == NULC && pd != ps) + *pd = *ps; + } + *pd = NULC; + + return html; +} + +int strlen_html(SXML_CHAR* str) +{ + int i, j, n; + + if (str == NULL) + return 0; + + n = 0; + for (i = 0; str[i] != NULC; i++) { + for (j = 0; HTML_SPECIAL_DICT[j].chr; j++) { + if (str[i] == HTML_SPECIAL_DICT[j].chr) { + n += HTML_SPECIAL_DICT[j].html_len; + break; + } + } + if (HTML_SPECIAL_DICT[j].chr == NULC) + n++; + } + + return n; +} + +int fprintHTML(FILE* f, SXML_CHAR* str) +{ + SXML_CHAR* p; + int i, n; + + for (p = str, n = 0; *p != NULC; p++) { + for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) { + if (*p != HTML_SPECIAL_DICT[i].chr) + continue; + sx_fprintf(f, HTML_SPECIAL_DICT[i].html); + n += HTML_SPECIAL_DICT[i].html_len; + break; + } + if (HTML_SPECIAL_DICT[i].chr == NULC) { + (void)sx_fputc(*p, f); + n++; + } + } + + return n; +} + +int regstrcmp(SXML_CHAR* str, SXML_CHAR* pattern) +{ + SXML_CHAR *p, *s; + + if (str == NULL && pattern == NULL) + return true; + + if (str == NULL || pattern == NULL) + return false; + + p = pattern; + s = str; + while (true) { + switch (*p) { + /* Any character matches, go to next one */ + case C2SX('?'): + p++; + s++; + break; + + /* Go to next character in pattern and wait until it is found in 'str' */ + case C2SX('*'): + for (; *p != NULC; p++) { /* Squeeze '**?*??**' to '*' */ + if (*p != C2SX('*') && *p != C2SX('?')) + break; + } + for (; *s != NULC; s++) { + if (*s == *p) + break; + } + break; + + /* NULL character on pattern has to be matched by 'str' */ + case 0: + return *s ? false : true; + + default: + if (*p == C2SX('\\')) /* Escape character */ + p++; + if (*p++ != *s++) /* Characters do not match */ + return false; + break; + } + } + + return false; +} diff --git a/sxmlc.h b/sxmlc.h index d0cba28..a693ac7 100644 --- a/sxmlc.h +++ b/sxmlc.h @@ -1,829 +1,829 @@ -/* - Copyright (c) 2010, Matthieu Labas - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY - OF SUCH DAMAGE. - - The views and conclusions contained in the software and documentation are those of the - authors and should not be interpreted as representing official policies, either expressed - or implied, of the FreeBSD Project. -*/ -#ifndef _SXML_H_ -#define _SXML_H_ - -#define SXMLC_VERSION "4.2.7" - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#ifdef SXMLC_UNICODE - typedef wchar_t SXML_CHAR; - #define C2SX(c) L ## c - #define CSXEOF WEOF - #define sx_strcmp wcscmp - #define sx_strncmp wcsncmp - #define sx_strlen wcslen - #define sx_strdup wcsdup - #define sx_strchr wcschr - #define sx_strrchr wcsrchr - #define sx_strcpy wcscpy - #define sx_strncpy wcsncpy - #define sx_strcat wcscat - #define sx_printf wprintf - #define sx_fprintf fwprintf - #define sx_sprintf swprintf - #define sx_fgetc fgetwc - #define sx_fputc fputwc - #define sx_isspace iswspace - #if defined(WIN32) || defined(WIN64) - #define sx_fopen _wfopen - #else - #define sx_fopen fopen - #endif - #define sx_fclose fclose - #define sx_feof feof -#else - typedef char SXML_CHAR; - #define C2SX(c) c - #define CSXEOF EOF - #define sx_strcmp strcmp - #define sx_strncmp strncmp - #define sx_strlen strlen - #define sx_strdup __sx_strdup - #define sx_strchr strchr - #define sx_strrchr strrchr - #define sx_strcpy strcpy - #define sx_strncpy strncpy - #define sx_strcat strcat - #define sx_printf printf - #define sx_fprintf fprintf - #define sx_sprintf sprintf - #define sx_fgetc fgetc - #define sx_fputc fputc - #define sx_isspace(ch) isspace((int)ch) - #define sx_fopen fopen - #define sx_fclose fclose - #define sx_feof feof -#endif - -#ifdef DBG_MEM - void* __malloc(size_t sz); - void* __calloc(size_t count, size_t sz); - void* __realloc(void* mem, size_t sz); - void __free(void* mem); - char* __sx_strdup(const char* s); -#else - #define __malloc malloc - #define __calloc calloc - #define __realloc realloc - #define __free free - #define __sx_strdup strdup -#endif - -#ifndef MEM_INCR_RLA -#define MEM_INCR_RLA (256*sizeof(SXML_CHAR)) /* Initial buffer size and increment for memory reallocations */ -#endif - -#ifndef false -#define false 0 -#endif - -#ifndef true -#define true 1 -#endif - -#define NULC ((SXML_CHAR)C2SX('\0')) -#define isquote(c) (((c) == C2SX('"')) || ((c) == C2SX('\''))) - -/* - Buffer data source used by 'read_line_alloc' when required. - 'buf' should be 0-terminated. - */ -typedef struct _DataSourceBuffer { - const SXML_CHAR* buf; - int cur_pos; -} DataSourceBuffer; - -typedef FILE* DataSourceFile; - -typedef enum _DataSourceType { - DATA_SOURCE_FILE = 0, - DATA_SOURCE_BUFFER, - DATA_SOURCE_MAX -} DataSourceType; - -#ifndef false -#define false 0 -#endif - -#ifndef true -#define true 1 -#endif - -/* Node types */ -typedef enum _TagType { - TAG_ERROR = -1, - TAG_NONE = 0, - TAG_PARTIAL, /* Node containing a legal '>' that stopped file reading */ - TAG_FATHER, /* - Next nodes will be children of this one. */ - TAG_SELF, /* - Standalone node. */ - TAG_INSTR, /* - Processing instructions, or prolog node. */ - TAG_COMMENT, /* */ - TAG_CDATA, /* - CDATA node */ - TAG_DOCTYPE, /* - DOCTYPE node */ - TAG_END, /* - End of father node. */ - TAG_TEXT, /* text node*/ - - TAG_USER = 100 /* User-defined tag start */ -} TagType; - -/* TODO: Performance improvement with some fixed-sized strings ??? (e.g. XMLAttribute.name[64], XMLNode.tag[64]) */ - -typedef struct _XMLAttribute { - SXML_CHAR* name; - SXML_CHAR* value; - int active; -} XMLAttribute; - -/* Constant to know whether a struct has been initialized (XMLNode or XMLDoc) */ -#define XML_INIT_DONE 0x19770522 /* Happy Birthday ;) */ - -/* - An XML node. - */ -typedef struct _XMLNode { - SXML_CHAR* tag; /* Tag name */ - SXML_CHAR* text; /* Text inside the node */ - XMLAttribute* attributes; - int n_attributes; - - struct _XMLNode* father; /* NULL if root */ - struct _XMLNode** children; - int n_children; - - TagType tag_type; /* Node type ('TAG_FATHER', 'TAG_SELF' or 'TAG_END') */ - int active; /* 'true' to tell that node is active and should be displayed by 'XMLDoc_print' */ - - void* user; /* Pointer for user data associated to the node */ - - /* Keep 'init_value' as the last member */ - int init_value; /* Initialized to 'XML_INIT_DONE' to indicate that node has been initialized properly */ -} XMLNode; - -/* - An XML document. - */ -#ifndef SXMLC_MAX_PATH -#define SXMLC_MAX_PATH 256 -#endif -typedef struct _XMLDoc { - SXML_CHAR filename[SXMLC_MAX_PATH]; -#ifdef SXMLC_UNICODE - BOM_TYPE bom_type; - unsigned char bom[5]; /* First characters read that might be a BOM when unicode is used */ - int sz_bom; /* Number of bytes in BOM */ -#endif - XMLNode** nodes; /* Nodes of the document, including prolog, comments and root nodes */ - int n_nodes; /* Number of nodes in 'nodes' */ - int i_root; /* Index of first root node in 'nodes', -1 if document is empty */ - - /* Keep 'init_value' as the last member */ - int init_value; /* Initialized to 'XML_INIT_DONE' to indicate that document has been initialized properly */ -} XMLDoc; - -/* - Register an XML tag, giving its 'start' and 'end' string, which should include '<' and '>'. - The 'tag_type' is user-given and has to be less than or equal to 'TAG_USER'. It will be - returned as the 'tag_type' member of the XMLNode struct. Note that no test is performed - to check for an already-existing tag_type. - Return tag index in user tags table when successful, or '-1' if the 'tag_type' is invalid or - the new tag could not be registered (e.g. when 'start' does not start with '<' or 'end' does not end with '>'). - */ -int XML_register_user_tag(TagType tag_type, SXML_CHAR* start, SXML_CHAR* end); - -/* - Remove a registered user tag. - Return the new number of registered user tags or '-1' if 'i_tag' is invalid. - */ -int XML_unregister_user_tag(int i_tag); - -/* - Return the number of registered tags. - */ -int XML_get_nb_registered_user_tags(void); - -/* - Return the index of first occurrence of 'tag_type' in registered user tags, or '-1' if not found. - */ -int XML_get_registered_user_tag(TagType tag_type); - - -typedef enum _ParseError { - PARSE_ERR_NONE = 0, - PARSE_ERR_MEMORY = -1, - PARSE_ERR_UNEXPECTED_TAG_END = -2, - PARSE_ERR_SYNTAX = -3, - PARSE_ERR_EOF = -4, - PARSE_ERR_TEXT_OUTSIDE_NODE = -5, /* During DOM loading */ - PARSE_ERR_UNEXPECTED_NODE_END = -6 /* During DOM loading */ -} ParseError; - -/* - Events that can happen when loading an XML document. - These will be passed to the 'all_event' callback of the SAX parser. - */ -typedef enum _XMLEvent { - XML_EVENT_START_DOC, - XML_EVENT_START_NODE, - XML_EVENT_END_NODE, - XML_EVENT_TEXT, - XML_EVENT_ERROR, - XML_EVENT_END_DOC -} XMLEvent; - -/* - Structure given as an argument for SAX callbacks to retrieve information about - parsing status - */ -typedef struct _SAX_Data { - const SXML_CHAR* name; - int line_num; - void* user; -} SAX_Data; - -/* - User callbacks used for SAX parsing. Return values of these callbacks should be 0 to stop parsing. - Members can be set to NULL to disable handling of some events. - All parameters are pointers to structures that will no longer be available after callback returns. - It is recommended that the callback uses the information and stores it in its own data structure. - WARNING! SAX PARSING DOES NOT CHECK FOR XML INTEGRITY! e.g. a tag end without a matching tag start - will not be detected by the parser and should be detected by the callbacks instead. - */ -typedef struct _SAX_Callbacks { - /* - Callback called when parsing starts, before parsing the first node. - */ - int (*start_doc)(SAX_Data* sd); - - /* - Callback called when a new node starts (e.g. '' or ''). - If any, attributes can be read from 'node->attributes'. - N.B. '' will trigger an immediate call to the 'end_node' callback - after the 'start_node' callback. - */ - int (*start_node)(const XMLNode* node, SAX_Data* sd); - - /* - Callback called when a node ends (e.g. '' or ''). - */ - int (*end_node)(const XMLNode* node, SAX_Data* sd); - - /* - Callback called when text has been found in the last node. - */ - int (*new_text)(SXML_CHAR* text, SAX_Data* sd); - - /* - Callback called when parsing is finished. - No other callbacks will be called after it. - */ - int (*end_doc)(SAX_Data* sd); - - /* - Callback called when an error occurs during parsing. - 'error_num' is the error number and 'line_number' is the line number in the stream - being read (file or buffer). - */ - int (*on_error)(ParseError error_num, int line_number, SAX_Data* sd); - - /* - Callback called when text has been found in the last node. - 'event' is the type of event for which the callback was called: - XML_EVENT_START_DOC: - 'node' is NULL. - 'text' is the file name if a file is being parsed, NULL if a buffer is being parsed. - 'n' is 0. - XML_EVENT_START_NODE: - 'node' is the node starting, with tag and all attributes initialized. - 'text' is NULL. - 'n' is the number of lines parsed. - XML_EVENT_END_NODE: - 'node' is the node ending, with tag, attributes and text initialized. - 'text' is NULL. - 'n' is the number of lines parsed. - XML_EVENT_TEXT: - 'node' is NULL. - 'text' is the text to be added to last node started and not finished. - 'n' is the number of lines parsed. - XML_EVENT_ERROR: - Everything is NULL. - 'n' is one of the 'PARSE_ERR_*'. - XML_EVENT_END_DOC: - 'node' is NULL. - 'text' is the file name if a file is being parsed, NULL if a buffer is being parsed. - 'n' is the number of lines parsed. - */ - int (*all_event)(XMLEvent event, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd); -} SAX_Callbacks; - -/* - Helper function to initialize all 'sax' members to NULL. - Return 'false' is 'sax' is NULL. - */ -int SAX_Callbacks_init(SAX_Callbacks* sax); - -/* - Set of SAX callbacks used by 'XMLDoc_parse_file_DOM'. - These are made available to be able to load an XML document using DOM implementation - with user-defined code at some point (e.g. counting nodes, running search, ...). - In this case, the 'XMLDoc_parse_file_SAX' has to be called instead of the 'XMLDoc_parse_file_DOM', - providing either these callbacks directly, or a functions calling these callbacks. - To do that, you should initialize the 'doc' member of the 'DOM_through_SAX' struct and call the - 'XMLDoc_parse_file_SAX' giving this struct as a the 'user' data pointer. - */ - -typedef struct _DOM_through_SAX { - XMLDoc* doc; /* Document to fill up */ - XMLNode* current; /* For internal use (current father node) */ - ParseError error; /* For internal use (parse status) */ - int line_error; /* For internal use (line number when error occurred) */ - int text_as_nodes; /* For internal use (store text inside nodes as sequential TAG_TEXT nodes) */ -} DOM_through_SAX; - -int DOMXMLDoc_doc_start(SAX_Data* dom); -int DOMXMLDoc_node_start(const XMLNode* node, SAX_Data* dom); -int DOMXMLDoc_node_text(SXML_CHAR* text, SAX_Data* dom); -int DOMXMLDoc_node_end(const XMLNode* node, SAX_Data* dom); -int DOMXMLDoc_parse_error(ParseError error_num, int line_number, SAX_Data* sd); -int DOMXMLDoc_doc_end(SAX_Data* dom); - -/* - Initialize 'sax' with the "official" DOM callbacks. - */ -int SAX_Callbacks_init_DOM(SAX_Callbacks* sax); - -/* --- XMLNode methods --- */ - -/* - Fills 'xmlattr' with 'xmlattr->name' to 'attrName' and 'xmlattr->value' to 'attr Value'. - 'str' is supposed to be like 'attrName[ ]=[ ]["]attr Value["]'. - Return 0 if not enough memory or bad parameters (NULL 'str' or 'xmlattr'). - 2 if last quote is missing in the attribute value. - 1 if 'xmlattr' was filled correctly. - */ -int XML_parse_attribute_to(const SXML_CHAR* str, int to, XMLAttribute* xmlattr); - -#define XML_parse_attribute(str, xmlattr) XML_parse_attribute_to(str, -1, xmlattr) - -/* - Reads a string that is supposed to be an xml tag like '' or ''. - Fills the 'xmlnode' structure with the tag name and its attributes. - Returns 0 if an error occurred (malformed 'str' or memory). 'TAG_*' when string is recognized. - */ -TagType XML_parse_1string(const SXML_CHAR* str, XMLNode* xmlnode); - -/* - Allocate and initialize XML nodes. - 'n' is the number of contiguous elements to allocate (to create and array). - Return 'NULL' if not enough memory, or the pointer to the elements otherwise. - */ -XMLNode* XMLNode_allocN(int n); - -/* - Shortcut to allocate one node only. - */ -#define XMLNode_alloc() XMLNode_allocN(1) - -/* - Initialize an already-allocated XMLNode. - */ -int XMLNode_init(XMLNode* node); - -/* - Free a node and all its children. - */ -int XMLNode_free(XMLNode* node); - -/* - Free XMLNode 'dst' and copy 'src' to 'dst', along with its children if specified. - If 'src' is NULL, 'dst' is freed and initialized. - */ -int XMLNode_copy(XMLNode* dst, const XMLNode* src, int copy_children); - -/* - Allocate a node and copy 'node' into it. - If 'copy_children' is 'true', all children of 'node' will be copied to the new node. - Return 'NULL' if not enough memory, or a pointer to the new node otherwise. - */ -XMLNode* XMLNode_dup(const XMLNode* node, int copy_children); - -/* - Set the active/inactive state of 'node'. - Set 'active' to 'true' to activate 'node' and all its children, and enable its use - in other functions (e.g. 'XMLDoc_print', 'XMLNode_search_child'). - */ -int XMLNode_set_active(XMLNode* node, int active); - -/* - Set 'node' tag. - Return 'false' for memory error, 'true' otherwise. - */ -int XMLNode_set_tag(XMLNode* node, const SXML_CHAR* tag); - -/* - Set the node type among one of the valid ones (TAG_FATHER, TAG_SELF, TAG_INSTR, - TAG_COMMENT, TAG_CDATA, TAG_DOCTYPE) or any user-registered tag. - Return 'false' when the node or the 'tag_type' is invalid. - */ -int XMLNode_set_type(XMLNode* node, const TagType tag_type); - -/* - Add an attribute to 'node' or update an existing one. - The attribute has a 'name' and a 'value'. - Return the new number of attributes, or -1 for memory problem. - */ -int XMLNode_set_attribute(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR* attr_value); - -/* - Retrieve an attribute value, based on its name, allocating 'attr_value'. - If the attribute name does not exist, set 'attr_value' to the given default value. - Return 'false' when the node is invalid, 'attr_name' is NULL or empty, or 'attr_value' is NULL. - */ -int XMLNode_get_attribute_with_default(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR** attr_value, const SXML_CHAR* default_attr_value); - -/* - Helper macro that retrieve an attribute value, or an empty string if the attribute does - not exist. - */ -#define XMLNode_get_attribute(node, attr_name, attr_value) XMLNode_get_attribute_with_default(node, attr_name, attr_value, C2SX("")) - -/* - Return the number of active attributes of 'node', or '-1' if 'node' is invalid. -*/ -int XMLNode_get_attribute_count(const XMLNode* node); - -/* - Search for the active attribute 'attr_name' in 'node', starting from index 'isearch' - and returns its index, or -1 if not found or error. - */ -int XMLNode_search_attribute(const XMLNode* node, const SXML_CHAR* attr_name, int isearch); - -/* - Remove attribute index 'i_attr'. - Return the new number of attributes or -1 on invalid arguments. - */ -int XMLNode_remove_attribute(XMLNode* node, int i_attr); - -/* - Remove all attributes from 'node'. - */ -int XMLNode_remove_all_attributes(XMLNode* node); - -/* - Set node text. - Return 'true' when successful, 'false' on error. - */ -int XMLNode_set_text(XMLNode* node, const SXML_CHAR* text); - -/* - Helper macro to remove text from 'node'. - */ -#define XMLNode_remove_text(node) XMLNode_set_text(node, NULL); - -/* - Add a child to a node. - Return 'false' for memory problem, 'true' otherwise. - */ -int XMLNode_add_child(XMLNode* node, XMLNode* child); - -/* - Return the number of active children nodes of 'node', or '-1' if 'node' is invalid. - */ -int XMLNode_get_children_count(const XMLNode* node); - -/* - Return a reference to the 'i_child'th active node. - */ -XMLNode* XMLNode_get_child(const XMLNode* node, int i_child); - -/* - Remove the 'i_child'th active child of 'node'. - If 'free_child' is 'true', free the child node itself. This parameter is usually 'true' - but should be 'false' when child nodes are pointers to local or global variables instead of - user-allocated memory. - Return the new number of children or -1 on invalid arguments. - */ -int XMLNode_remove_child(XMLNode* node, int i_child, int free_child); - -/* - Remove all children from 'node'. - */ -int XMLNode_remove_children(XMLNode* node); - -/* - Return 'true' if 'node1' is the same as 'node2' (i.e. same tag, same active attributes). - */ -int XMLNode_equal(const XMLNode* node1, const XMLNode* node2); - -/* - Return the next sibling of node 'node', or NULL if 'node' is invalid or the last child - or if its father could not be determined (i.e. 'node' is a root node). - */ -XMLNode* XMLNode_next_sibling(const XMLNode* node); - -/* - Return the next node in XML order i.e. first child or next sibling, or NULL - if 'node' is invalid or the end of its root node is reached. - */ -XMLNode* XMLNode_next(const XMLNode* node); - - -/* --- XMLDoc methods --- */ - - -/* - Initializes an already-allocated XML document. - */ -int XMLDoc_init(XMLDoc* doc); - -/* - Free an XML document. - Return 'false' if 'doc' was not initialized. - */ -int XMLDoc_free(XMLDoc* doc); - -/* - Set the new 'doc' root node among all existing nodes in 'doc'. - Return 'false' if bad arguments, 'true' otherwise. - */ -int XMLDoc_set_root(XMLDoc* doc, int i_root); - -/* - Add a node to the document, specifying the type. - If its type is TAG_FATHER, it also sets the document root node if previously undefined. - Return the node index, or -1 if bad arguments or memory error. - */ -int XMLDoc_add_node(XMLDoc* doc, XMLNode* node); - -/* - Remove a node from 'doc' root nodes, base on its index. - If 'free_node' is 'true', free the node itself. This parameter is usually 'true' - but should be 'false' when the node is a pointer to local or global variable instead of - user-allocated memory. - Return 'true' if node was removed or 'false' if 'doc' or 'i_node' is invalid. - */ -int XMLDoc_remove_node(XMLDoc* doc, int i_node, int free_node); - -/* - Shortcut macro to retrieve root node from a document. - Equivalent to - doc->nodes[doc->i_root] - */ -#define XMLDoc_root(doc) ((doc)->nodes[(doc)->i_root]) - -/* - Shortcut macro to add a node to 'doc' root node. - Equivalent to - XMLDoc_add_child_root(XMLDoc* doc, XMLNode* child); - */ -#define XMLDoc_add_child_root(doc, child) XMLNode_add_child((doc)->nodes[(doc)->i_root], (child)) - -/* - Default quote to use to print attribute value. - User can redefine it with its own character by adding a #define XML_DEFAULT_QUOTE before including - this file. - */ -#ifndef XML_DEFAULT_QUOTE -#define XML_DEFAULT_QUOTE C2SX('"') -#endif - -/* - Print the node and its children to a file (that can be stdout). - - 'tag_sep' is the string to use to separate nodes from each other (usually "\n"). - - 'child_sep' is the additional string to put for each child level (usually "\t"). - - 'keep_text_spaces' indicates that text should not be printed if it is composed of - spaces, tabs or new lines only (e.g. when XML document spans on several lines due to - pretty-printing). - - 'sz_line' is the maximum number of characters that can be put on a single line. The - node remainder will be output to extra lines. - - 'nb_char_tab' is how many characters should be counted for a tab when counting characters - in the line. It usually is 8 or 4, but at least 1. - - 'depth' is an internal parameter that is used to determine recursively how deep we are in - the tree. It should be initialized to 0 at first call. - Return 'false' on invalid arguments (NULL 'node' or 'f'), 'true' otherwise. - */ -int XMLNode_print_attr_sep(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab); - -/* For backward compatibility */ -#define XMLNode_print(node, f, tag_sep, child_sep, keep_text_spaces, sz_line, nb_char_tab) XMLNode_print_attr_sep(node, f, tag_sep, child_sep, C2SX(" "), keep_text_spaces, sz_line, nb_char_tab) - -/* - Print the node "header": , spanning it on several lines if needed. - Return 'false' on invalid arguments (NULL 'node' or 'f'), 'true' otherwise. - */ -int XMLNode_print_header(const XMLNode* node, FILE* f, int sz_line, int nb_char_tab); - -/* - Prints the XML document using 'XMLNode_print' on all document root nodes. - */ -int XMLDoc_print_attr_sep(const XMLDoc* doc, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab); - -/* For backward compatibility */ -#define XMLDoc_print(doc, f, tag_sep, child_sep, keep_text_spaces, sz_line, nb_char_tab) XMLDoc_print_attr_sep(doc, f, tag_sep, child_sep, C2SX(" "), keep_text_spaces, sz_line, nb_char_tab) - -/* - Create a new XML document from a given 'filename' and load it to 'doc'. - 'text_as_nodes' should be non-zero to put text into separate TAG_TEXT nodes. - Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise. - */ -int XMLDoc_parse_file_DOM_text_as_nodes(const SXML_CHAR* filename, XMLDoc* doc, int text_as_nodes); - -/* For backward compatibility */ -#define XMLDoc_parse_file_DOM(filename, doc) XMLDoc_parse_file_DOM_text_as_nodes(filename, doc, 0) - -/* - Create a new XML document from a memory buffer 'buffer' that can be given a name 'name', and load - it into 'doc'. - 'text_as_nodes' should be non-zero to put text into separate TAG_TEXT nodes. - Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise. - */ -int XMLDoc_parse_buffer_DOM_text_as_nodes(const SXML_CHAR* buffer, const SXML_CHAR* name, XMLDoc* doc, int text_as_nodes); - -/* For backward compatibility */ -#define XMLDoc_parse_buffer_DOM(buffer, name, doc) XMLDoc_parse_buffer_DOM_text_as_nodes(buffer, name, doc, 0) - -/* - Parse an XML document from a given 'filename', calling SAX callbacks given in the 'sax' structure. - 'user' is a user-given pointer that will be given back to all callbacks. - Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise. - */ -int XMLDoc_parse_file_SAX(const SXML_CHAR* filename, const SAX_Callbacks* sax, void* user); - -/* - Parse an XML document from a memory buffer 'buffer' that can be given a name 'name', - calling SAX callbacks given in the 'sax' structure. - 'user' is a user-given pointer that will be given back to all callbacks. - Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise. - */ -int XMLDoc_parse_buffer_SAX(const SXML_CHAR* buffer, const SXML_CHAR* name, const SAX_Callbacks* sax, void* user); - -/* - Parse an XML file using the DOM implementation. - */ -#define XMLDoc_parse_file XMLDOC_parse_file_DOM - - - -/* --- Utility functions --- */ - -/* - Functions to get next byte from buffer data source and know if the end has been reached. - Return as 'fgetc' and 'feof' would for 'FILE*'. - */ -int _bgetc(DataSourceBuffer* ds); -int _beob(DataSourceBuffer* ds); -/* - Reads a line from data source 'in', eventually (re-)allocating a given buffer 'line'. - Characters read will be stored in 'line' starting at 'i0' (this allows multiple calls to - 'read_line_alloc' on the same 'line' buffer without overwriting it at each call). - 'in_type' specifies the type of data source to be read: 'in' is 'FILE*' if 'in_type' - 'sz_line' is the size of the buffer 'line' if previously allocated. 'line' can point - to NULL, in which case it will be allocated '*sz_line' bytes. After the function returns, - '*sz_line' is the actual buffer size. This allows multiple calls to this function using the - same buffer (without re-allocating/freeing). - If 'sz_line' is non NULL and non 0, it means that '*line' is a VALID pointer to a location - of '*sz_line' SXML_CHAR (not bytes! Multiply by sizeof(SXML_CHAR) to get number of bytes). - Searches for character 'from' until character 'to'. If 'from' is 0, starts from - current position. If 'to' is 0, it is replaced by '\n'. - If 'keep_fromto' is 0, removes characters 'from' and 'to' from the line. - If 'interest_count' is not NULL, will receive the count of 'interest' characters while searching - for 'to' (e.g. use 'interest'='\n' to count lines in file). - Returns the number of characters in the line or 0 if an error occurred. - 'read_line_alloc' uses constant 'MEM_INCR_RLA' to reallocate memory when needed. It is possible - to override this definition to use another value. - */ -int read_line_alloc(void* in, DataSourceType in_type, SXML_CHAR** line, int* sz_line, int i0, SXML_CHAR from, SXML_CHAR to, int keep_fromto, SXML_CHAR interest, int* interest_count); - -/* - Concatenates the string pointed at by 'src1' with 'src2' into '*src1' and - return it ('*src1'). - Return NULL when out of memory. - */ -SXML_CHAR* strcat_alloc(SXML_CHAR** src1, const SXML_CHAR* src2); - -/* - Strip spaces at the beginning and end of 'str', modifying 'str'. - If 'repl_sq' is not '\0', squeezes spaces to an single character ('repl_sq'). - If not '\0', 'protect' is used to protect spaces from being deleted (usually a backslash). - Returns the string or NULL if 'protect' is a space (which would not make sense). - */ -SXML_CHAR* strip_spaces(SXML_CHAR* str, SXML_CHAR repl_sq); - -/* - Remove '\' characters from 'str', modifying it. - Return 'str'. - */ -SXML_CHAR* str_unescape(SXML_CHAR* str); - -/* - Split 'str' into a left and right part around a separator 'sep'. - The left part is located between indexes 'l0' and 'l1' while the right part is - between 'r0' and 'r1' and the separator position is at 'i_sep' (whenever these are - not NULL). - If 'ignore_spaces' is 'true', computed indexes will not take into account potential - spaces around the separator as well as before left part and after right part. - if 'ignore_quotes' is 'true', " or ' will not be taken into account when parsing left - and right members. - Whenever the right member is empty (e.g. "attrib" or "attrib="), '*r0' is initialized - to 'str' size and '*r1' to '*r0-1' (crossed). - If the separator was not found (i.e. left member only), '*i_sep' is '-1'. - Return 'false' when 'str' is malformed, 'true' when splitting was successful. - */ -int split_left_right(SXML_CHAR* str, SXML_CHAR sep, int* l0, int* l1, int* i_sep, int* r0, int* r1, int ignore_spaces, int ignore_quotes); - -typedef enum _BOM_TYPE { - BOM_NONE = 0x00, - BOM_UTF_8 = 0xefbbbf, - BOM_UTF_16BE = 0xfeff, - BOM_UTF_16LE = 0xfffe, - BOM_UTF_32BE = 0x0000feff, - BOM_UTF_32LE = 0xfffe0000 -} BOM_TYPE; -/* - Detect a potential BOM at the current file position and read it into 'bom' (if not NULL, - 'bom' should be at least 5 bytes). It also moves the 'f' beyond the BOM so it's possible to - skip it by calling 'freadBOM(f, NULL, NULL)'. If no BOM is found, it leaves 'f' file pointer - is reset to its original location. - If not null, 'sz_bom' is filled with how many bytes are stored in 'bom'. - Return the BOM type or BOM_NONE if none found (empty 'bom' in this case). - */ -BOM_TYPE freadBOM(FILE* f, unsigned char* bom, int* sz_bom); - -/* - Replace occurrences of special HTML characters escape sequences (e.g. '&') found in 'html' - by its character equivalent (e.g. '&') into 'str'. - If 'html' and 'str' are the same pointer replacement is made in 'str' itself, overwriting it. - If 'str' is NULL, replacement is made into 'html', overwriting it. - Returns 'str' (or 'html' if 'str' was NULL). - */ -SXML_CHAR* html2str(SXML_CHAR* html, SXML_CHAR* str); - -/* - Replace occurrences of special characters (e.g. '&') found in 'str' into their XML escaped - equivalent (e.g. '&') into 'xml'. - 'xml' is supposed allocated to the correct size (e.g. using 'malloc(strlen_html(str)+30)') and - different from 'str' (unlike 'html2str'), as string will expand. If it is NULL, 'str' will be - analyzed and a string will be allocated to the exact size, before being returned. In that case, - it is the responsibility of the caller to free() the result! - Return 'xml' or NULL if 'str' or 'xml' are NULL, or when 'xml' is 'str'. -*/ -SXML_CHAR* str2html(SXML_CHAR* str, SXML_CHAR* xml); - -/* - Return the length of 'str' as if all its special character were replaced by their HTML - equivalent. - Return 0 if 'str' is NULL. - */ -int strlen_html(SXML_CHAR* str); - -/* - Print 'str' to 'f', transforming special characters into their HTML equivalent. - Returns the number of output characters. - */ -int fprintHTML(FILE* f, SXML_CHAR* str); - -/* - Checks whether 'str' corresponds to 'pattern'. - 'pattern' can use wildcads such as '*' (any potentially empty string) or - '?' (any character) and use '\' as an escape character. - Returns 'true' when 'str' matches 'pattern', 'false' otherwise. - */ -int regstrcmp(SXML_CHAR* str, SXML_CHAR* pattern); - -#ifdef __cplusplus -} -#endif - -#endif +/* + Copyright (c) 2010, Matthieu Labas + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. + + The views and conclusions contained in the software and documentation are those of the + authors and should not be interpreted as representing official policies, either expressed + or implied, of the FreeBSD Project. +*/ +#ifndef _SXML_H_ +#define _SXML_H_ + +#define SXMLC_VERSION "4.2.7" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef SXMLC_UNICODE + typedef wchar_t SXML_CHAR; + #define C2SX(c) L ## c + #define CSXEOF WEOF + #define sx_strcmp wcscmp + #define sx_strncmp wcsncmp + #define sx_strlen wcslen + #define sx_strdup wcsdup + #define sx_strchr wcschr + #define sx_strrchr wcsrchr + #define sx_strcpy wcscpy + #define sx_strncpy wcsncpy + #define sx_strcat wcscat + #define sx_printf wprintf + #define sx_fprintf fwprintf + #define sx_sprintf swprintf + #define sx_fgetc fgetwc + #define sx_fputc fputwc + #define sx_isspace iswspace + #if defined(WIN32) || defined(WIN64) + #define sx_fopen _wfopen + #else + #define sx_fopen fopen + #endif + #define sx_fclose fclose + #define sx_feof feof +#else + typedef char SXML_CHAR; + #define C2SX(c) c + #define CSXEOF EOF + #define sx_strcmp strcmp + #define sx_strncmp strncmp + #define sx_strlen strlen + #define sx_strdup __sx_strdup + #define sx_strchr strchr + #define sx_strrchr strrchr + #define sx_strcpy strcpy + #define sx_strncpy strncpy + #define sx_strcat strcat + #define sx_printf printf + #define sx_fprintf fprintf + #define sx_sprintf sprintf + #define sx_fgetc fgetc + #define sx_fputc fputc + #define sx_isspace(ch) isspace((int)ch) + #define sx_fopen fopen + #define sx_fclose fclose + #define sx_feof feof +#endif + +#ifdef DBG_MEM + void* __malloc(size_t sz); + void* __calloc(size_t count, size_t sz); + void* __realloc(void* mem, size_t sz); + void __free(void* mem); + char* __sx_strdup(const char* s); +#else + #define __malloc malloc + #define __calloc calloc + #define __realloc realloc + #define __free free + #define __sx_strdup strdup +#endif + +#ifndef MEM_INCR_RLA +#define MEM_INCR_RLA (256*sizeof(SXML_CHAR)) /* Initial buffer size and increment for memory reallocations */ +#endif + +#ifndef false +#define false 0 +#endif + +#ifndef true +#define true 1 +#endif + +#define NULC ((SXML_CHAR)C2SX('\0')) +#define isquote(c) (((c) == C2SX('"')) || ((c) == C2SX('\''))) + +/* + Buffer data source used by 'read_line_alloc' when required. + 'buf' should be 0-terminated. + */ +typedef struct _DataSourceBuffer { + const SXML_CHAR* buf; + int cur_pos; +} DataSourceBuffer; + +typedef FILE* DataSourceFile; + +typedef enum _DataSourceType { + DATA_SOURCE_FILE = 0, + DATA_SOURCE_BUFFER, + DATA_SOURCE_MAX +} DataSourceType; + +#ifndef false +#define false 0 +#endif + +#ifndef true +#define true 1 +#endif + +/* Node types */ +typedef enum _TagType { + TAG_ERROR = -1, + TAG_NONE = 0, + TAG_PARTIAL, /* Node containing a legal '>' that stopped file reading */ + TAG_FATHER, /* - Next nodes will be children of this one. */ + TAG_SELF, /* - Standalone node. */ + TAG_INSTR, /* - Processing instructions, or prolog node. */ + TAG_COMMENT, /* */ + TAG_CDATA, /* - CDATA node */ + TAG_DOCTYPE, /* - DOCTYPE node */ + TAG_END, /* - End of father node. */ + TAG_TEXT, /* text node*/ + + TAG_USER = 100 /* User-defined tag start */ +} TagType; + +/* TODO: Performance improvement with some fixed-sized strings ??? (e.g. XMLAttribute.name[64], XMLNode.tag[64]) */ + +typedef struct _XMLAttribute { + SXML_CHAR* name; + SXML_CHAR* value; + int active; +} XMLAttribute; + +/* Constant to know whether a struct has been initialized (XMLNode or XMLDoc) */ +#define XML_INIT_DONE 0x19770522 /* Happy Birthday ;) */ + +/* + An XML node. + */ +typedef struct _XMLNode { + SXML_CHAR* tag; /* Tag name */ + SXML_CHAR* text; /* Text inside the node */ + XMLAttribute* attributes; + int n_attributes; + + struct _XMLNode* father; /* NULL if root */ + struct _XMLNode** children; + int n_children; + + TagType tag_type; /* Node type ('TAG_FATHER', 'TAG_SELF' or 'TAG_END') */ + int active; /* 'true' to tell that node is active and should be displayed by 'XMLDoc_print' */ + + void* user; /* Pointer for user data associated to the node */ + + /* Keep 'init_value' as the last member */ + int init_value; /* Initialized to 'XML_INIT_DONE' to indicate that node has been initialized properly */ +} XMLNode; + +/* + An XML document. + */ +#ifndef SXMLC_MAX_PATH +#define SXMLC_MAX_PATH 256 +#endif +typedef struct _XMLDoc { + SXML_CHAR filename[SXMLC_MAX_PATH]; +#ifdef SXMLC_UNICODE + BOM_TYPE bom_type; + unsigned char bom[5]; /* First characters read that might be a BOM when unicode is used */ + int sz_bom; /* Number of bytes in BOM */ +#endif + XMLNode** nodes; /* Nodes of the document, including prolog, comments and root nodes */ + int n_nodes; /* Number of nodes in 'nodes' */ + int i_root; /* Index of first root node in 'nodes', -1 if document is empty */ + + /* Keep 'init_value' as the last member */ + int init_value; /* Initialized to 'XML_INIT_DONE' to indicate that document has been initialized properly */ +} XMLDoc; + +/* + Register an XML tag, giving its 'start' and 'end' string, which should include '<' and '>'. + The 'tag_type' is user-given and has to be less than or equal to 'TAG_USER'. It will be + returned as the 'tag_type' member of the XMLNode struct. Note that no test is performed + to check for an already-existing tag_type. + Return tag index in user tags table when successful, or '-1' if the 'tag_type' is invalid or + the new tag could not be registered (e.g. when 'start' does not start with '<' or 'end' does not end with '>'). + */ +int XML_register_user_tag(TagType tag_type, SXML_CHAR* start, SXML_CHAR* end); + +/* + Remove a registered user tag. + Return the new number of registered user tags or '-1' if 'i_tag' is invalid. + */ +int XML_unregister_user_tag(int i_tag); + +/* + Return the number of registered tags. + */ +int XML_get_nb_registered_user_tags(void); + +/* + Return the index of first occurrence of 'tag_type' in registered user tags, or '-1' if not found. + */ +int XML_get_registered_user_tag(TagType tag_type); + + +typedef enum _ParseError { + PARSE_ERR_NONE = 0, + PARSE_ERR_MEMORY = -1, + PARSE_ERR_UNEXPECTED_TAG_END = -2, + PARSE_ERR_SYNTAX = -3, + PARSE_ERR_EOF = -4, + PARSE_ERR_TEXT_OUTSIDE_NODE = -5, /* During DOM loading */ + PARSE_ERR_UNEXPECTED_NODE_END = -6 /* During DOM loading */ +} ParseError; + +/* + Events that can happen when loading an XML document. + These will be passed to the 'all_event' callback of the SAX parser. + */ +typedef enum _XMLEvent { + XML_EVENT_START_DOC, + XML_EVENT_START_NODE, + XML_EVENT_END_NODE, + XML_EVENT_TEXT, + XML_EVENT_ERROR, + XML_EVENT_END_DOC +} XMLEvent; + +/* + Structure given as an argument for SAX callbacks to retrieve information about + parsing status + */ +typedef struct _SAX_Data { + const SXML_CHAR* name; + int line_num; + void* user; +} SAX_Data; + +/* + User callbacks used for SAX parsing. Return values of these callbacks should be 0 to stop parsing. + Members can be set to NULL to disable handling of some events. + All parameters are pointers to structures that will no longer be available after callback returns. + It is recommended that the callback uses the information and stores it in its own data structure. + WARNING! SAX PARSING DOES NOT CHECK FOR XML INTEGRITY! e.g. a tag end without a matching tag start + will not be detected by the parser and should be detected by the callbacks instead. + */ +typedef struct _SAX_Callbacks { + /* + Callback called when parsing starts, before parsing the first node. + */ + int (*start_doc)(SAX_Data* sd); + + /* + Callback called when a new node starts (e.g. '' or ''). + If any, attributes can be read from 'node->attributes'. + N.B. '' will trigger an immediate call to the 'end_node' callback + after the 'start_node' callback. + */ + int (*start_node)(const XMLNode* node, SAX_Data* sd); + + /* + Callback called when a node ends (e.g. '' or ''). + */ + int (*end_node)(const XMLNode* node, SAX_Data* sd); + + /* + Callback called when text has been found in the last node. + */ + int (*new_text)(SXML_CHAR* text, SAX_Data* sd); + + /* + Callback called when parsing is finished. + No other callbacks will be called after it. + */ + int (*end_doc)(SAX_Data* sd); + + /* + Callback called when an error occurs during parsing. + 'error_num' is the error number and 'line_number' is the line number in the stream + being read (file or buffer). + */ + int (*on_error)(ParseError error_num, int line_number, SAX_Data* sd); + + /* + Callback called when text has been found in the last node. + 'event' is the type of event for which the callback was called: + XML_EVENT_START_DOC: + 'node' is NULL. + 'text' is the file name if a file is being parsed, NULL if a buffer is being parsed. + 'n' is 0. + XML_EVENT_START_NODE: + 'node' is the node starting, with tag and all attributes initialized. + 'text' is NULL. + 'n' is the number of lines parsed. + XML_EVENT_END_NODE: + 'node' is the node ending, with tag, attributes and text initialized. + 'text' is NULL. + 'n' is the number of lines parsed. + XML_EVENT_TEXT: + 'node' is NULL. + 'text' is the text to be added to last node started and not finished. + 'n' is the number of lines parsed. + XML_EVENT_ERROR: + Everything is NULL. + 'n' is one of the 'PARSE_ERR_*'. + XML_EVENT_END_DOC: + 'node' is NULL. + 'text' is the file name if a file is being parsed, NULL if a buffer is being parsed. + 'n' is the number of lines parsed. + */ + int (*all_event)(XMLEvent event, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd); +} SAX_Callbacks; + +/* + Helper function to initialize all 'sax' members to NULL. + Return 'false' is 'sax' is NULL. + */ +int SAX_Callbacks_init(SAX_Callbacks* sax); + +/* + Set of SAX callbacks used by 'XMLDoc_parse_file_DOM'. + These are made available to be able to load an XML document using DOM implementation + with user-defined code at some point (e.g. counting nodes, running search, ...). + In this case, the 'XMLDoc_parse_file_SAX' has to be called instead of the 'XMLDoc_parse_file_DOM', + providing either these callbacks directly, or a functions calling these callbacks. + To do that, you should initialize the 'doc' member of the 'DOM_through_SAX' struct and call the + 'XMLDoc_parse_file_SAX' giving this struct as a the 'user' data pointer. + */ + +typedef struct _DOM_through_SAX { + XMLDoc* doc; /* Document to fill up */ + XMLNode* current; /* For internal use (current father node) */ + ParseError error; /* For internal use (parse status) */ + int line_error; /* For internal use (line number when error occurred) */ + int text_as_nodes; /* For internal use (store text inside nodes as sequential TAG_TEXT nodes) */ +} DOM_through_SAX; + +int DOMXMLDoc_doc_start(SAX_Data* dom); +int DOMXMLDoc_node_start(const XMLNode* node, SAX_Data* dom); +int DOMXMLDoc_node_text(SXML_CHAR* text, SAX_Data* dom); +int DOMXMLDoc_node_end(const XMLNode* node, SAX_Data* dom); +int DOMXMLDoc_parse_error(ParseError error_num, int line_number, SAX_Data* sd); +int DOMXMLDoc_doc_end(SAX_Data* dom); + +/* + Initialize 'sax' with the "official" DOM callbacks. + */ +int SAX_Callbacks_init_DOM(SAX_Callbacks* sax); + +/* --- XMLNode methods --- */ + +/* + Fills 'xmlattr' with 'xmlattr->name' to 'attrName' and 'xmlattr->value' to 'attr Value'. + 'str' is supposed to be like 'attrName[ ]=[ ]["]attr Value["]'. + Return 0 if not enough memory or bad parameters (NULL 'str' or 'xmlattr'). + 2 if last quote is missing in the attribute value. + 1 if 'xmlattr' was filled correctly. + */ +int XML_parse_attribute_to(const SXML_CHAR* str, int to, XMLAttribute* xmlattr); + +#define XML_parse_attribute(str, xmlattr) XML_parse_attribute_to(str, -1, xmlattr) + +/* + Reads a string that is supposed to be an xml tag like '' or ''. + Fills the 'xmlnode' structure with the tag name and its attributes. + Returns 0 if an error occurred (malformed 'str' or memory). 'TAG_*' when string is recognized. + */ +TagType XML_parse_1string(const SXML_CHAR* str, XMLNode* xmlnode); + +/* + Allocate and initialize XML nodes. + 'n' is the number of contiguous elements to allocate (to create and array). + Return 'NULL' if not enough memory, or the pointer to the elements otherwise. + */ +XMLNode* XMLNode_allocN(int n); + +/* + Shortcut to allocate one node only. + */ +#define XMLNode_alloc() XMLNode_allocN(1) + +/* + Initialize an already-allocated XMLNode. + */ +int XMLNode_init(XMLNode* node); + +/* + Free a node and all its children. + */ +int XMLNode_free(XMLNode* node); + +/* + Free XMLNode 'dst' and copy 'src' to 'dst', along with its children if specified. + If 'src' is NULL, 'dst' is freed and initialized. + */ +int XMLNode_copy(XMLNode* dst, const XMLNode* src, int copy_children); + +/* + Allocate a node and copy 'node' into it. + If 'copy_children' is 'true', all children of 'node' will be copied to the new node. + Return 'NULL' if not enough memory, or a pointer to the new node otherwise. + */ +XMLNode* XMLNode_dup(const XMLNode* node, int copy_children); + +/* + Set the active/inactive state of 'node'. + Set 'active' to 'true' to activate 'node' and all its children, and enable its use + in other functions (e.g. 'XMLDoc_print', 'XMLNode_search_child'). + */ +int XMLNode_set_active(XMLNode* node, int active); + +/* + Set 'node' tag. + Return 'false' for memory error, 'true' otherwise. + */ +int XMLNode_set_tag(XMLNode* node, const SXML_CHAR* tag); + +/* + Set the node type among one of the valid ones (TAG_FATHER, TAG_SELF, TAG_INSTR, + TAG_COMMENT, TAG_CDATA, TAG_DOCTYPE) or any user-registered tag. + Return 'false' when the node or the 'tag_type' is invalid. + */ +int XMLNode_set_type(XMLNode* node, const TagType tag_type); + +/* + Add an attribute to 'node' or update an existing one. + The attribute has a 'name' and a 'value'. + Return the new number of attributes, or -1 for memory problem. + */ +int XMLNode_set_attribute(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR* attr_value); + +/* + Retrieve an attribute value, based on its name, allocating 'attr_value'. + If the attribute name does not exist, set 'attr_value' to the given default value. + Return 'false' when the node is invalid, 'attr_name' is NULL or empty, or 'attr_value' is NULL. + */ +int XMLNode_get_attribute_with_default(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR** attr_value, const SXML_CHAR* default_attr_value); + +/* + Helper macro that retrieve an attribute value, or an empty string if the attribute does + not exist. + */ +#define XMLNode_get_attribute(node, attr_name, attr_value) XMLNode_get_attribute_with_default(node, attr_name, attr_value, C2SX("")) + +/* + Return the number of active attributes of 'node', or '-1' if 'node' is invalid. +*/ +int XMLNode_get_attribute_count(const XMLNode* node); + +/* + Search for the active attribute 'attr_name' in 'node', starting from index 'isearch' + and returns its index, or -1 if not found or error. + */ +int XMLNode_search_attribute(const XMLNode* node, const SXML_CHAR* attr_name, int isearch); + +/* + Remove attribute index 'i_attr'. + Return the new number of attributes or -1 on invalid arguments. + */ +int XMLNode_remove_attribute(XMLNode* node, int i_attr); + +/* + Remove all attributes from 'node'. + */ +int XMLNode_remove_all_attributes(XMLNode* node); + +/* + Set node text. + Return 'true' when successful, 'false' on error. + */ +int XMLNode_set_text(XMLNode* node, const SXML_CHAR* text); + +/* + Helper macro to remove text from 'node'. + */ +#define XMLNode_remove_text(node) XMLNode_set_text(node, NULL); + +/* + Add a child to a node. + Return 'false' for memory problem, 'true' otherwise. + */ +int XMLNode_add_child(XMLNode* node, XMLNode* child); + +/* + Return the number of active children nodes of 'node', or '-1' if 'node' is invalid. + */ +int XMLNode_get_children_count(const XMLNode* node); + +/* + Return a reference to the 'i_child'th active node. + */ +XMLNode* XMLNode_get_child(const XMLNode* node, int i_child); + +/* + Remove the 'i_child'th active child of 'node'. + If 'free_child' is 'true', free the child node itself. This parameter is usually 'true' + but should be 'false' when child nodes are pointers to local or global variables instead of + user-allocated memory. + Return the new number of children or -1 on invalid arguments. + */ +int XMLNode_remove_child(XMLNode* node, int i_child, int free_child); + +/* + Remove all children from 'node'. + */ +int XMLNode_remove_children(XMLNode* node); + +/* + Return 'true' if 'node1' is the same as 'node2' (i.e. same tag, same active attributes). + */ +int XMLNode_equal(const XMLNode* node1, const XMLNode* node2); + +/* + Return the next sibling of node 'node', or NULL if 'node' is invalid or the last child + or if its father could not be determined (i.e. 'node' is a root node). + */ +XMLNode* XMLNode_next_sibling(const XMLNode* node); + +/* + Return the next node in XML order i.e. first child or next sibling, or NULL + if 'node' is invalid or the end of its root node is reached. + */ +XMLNode* XMLNode_next(const XMLNode* node); + + +/* --- XMLDoc methods --- */ + + +/* + Initializes an already-allocated XML document. + */ +int XMLDoc_init(XMLDoc* doc); + +/* + Free an XML document. + Return 'false' if 'doc' was not initialized. + */ +int XMLDoc_free(XMLDoc* doc); + +/* + Set the new 'doc' root node among all existing nodes in 'doc'. + Return 'false' if bad arguments, 'true' otherwise. + */ +int XMLDoc_set_root(XMLDoc* doc, int i_root); + +/* + Add a node to the document, specifying the type. + If its type is TAG_FATHER, it also sets the document root node if previously undefined. + Return the node index, or -1 if bad arguments or memory error. + */ +int XMLDoc_add_node(XMLDoc* doc, XMLNode* node); + +/* + Remove a node from 'doc' root nodes, base on its index. + If 'free_node' is 'true', free the node itself. This parameter is usually 'true' + but should be 'false' when the node is a pointer to local or global variable instead of + user-allocated memory. + Return 'true' if node was removed or 'false' if 'doc' or 'i_node' is invalid. + */ +int XMLDoc_remove_node(XMLDoc* doc, int i_node, int free_node); + +/* + Shortcut macro to retrieve root node from a document. + Equivalent to + doc->nodes[doc->i_root] + */ +#define XMLDoc_root(doc) ((doc)->nodes[(doc)->i_root]) + +/* + Shortcut macro to add a node to 'doc' root node. + Equivalent to + XMLDoc_add_child_root(XMLDoc* doc, XMLNode* child); + */ +#define XMLDoc_add_child_root(doc, child) XMLNode_add_child((doc)->nodes[(doc)->i_root], (child)) + +/* + Default quote to use to print attribute value. + User can redefine it with its own character by adding a #define XML_DEFAULT_QUOTE before including + this file. + */ +#ifndef XML_DEFAULT_QUOTE +#define XML_DEFAULT_QUOTE C2SX('"') +#endif + +/* + Print the node and its children to a file (that can be stdout). + - 'tag_sep' is the string to use to separate nodes from each other (usually "\n"). + - 'child_sep' is the additional string to put for each child level (usually "\t"). + - 'keep_text_spaces' indicates that text should not be printed if it is composed of + spaces, tabs or new lines only (e.g. when XML document spans on several lines due to + pretty-printing). + - 'sz_line' is the maximum number of characters that can be put on a single line. The + node remainder will be output to extra lines. + - 'nb_char_tab' is how many characters should be counted for a tab when counting characters + in the line. It usually is 8 or 4, but at least 1. + - 'depth' is an internal parameter that is used to determine recursively how deep we are in + the tree. It should be initialized to 0 at first call. + Return 'false' on invalid arguments (NULL 'node' or 'f'), 'true' otherwise. + */ +int XMLNode_print_attr_sep(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab); + +/* For backward compatibility */ +#define XMLNode_print(node, f, tag_sep, child_sep, keep_text_spaces, sz_line, nb_char_tab) XMLNode_print_attr_sep(node, f, tag_sep, child_sep, C2SX(" "), keep_text_spaces, sz_line, nb_char_tab) + +/* + Print the node "header": , spanning it on several lines if needed. + Return 'false' on invalid arguments (NULL 'node' or 'f'), 'true' otherwise. + */ +int XMLNode_print_header(const XMLNode* node, FILE* f, int sz_line, int nb_char_tab); + +/* + Prints the XML document using 'XMLNode_print' on all document root nodes. + */ +int XMLDoc_print_attr_sep(const XMLDoc* doc, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab); + +/* For backward compatibility */ +#define XMLDoc_print(doc, f, tag_sep, child_sep, keep_text_spaces, sz_line, nb_char_tab) XMLDoc_print_attr_sep(doc, f, tag_sep, child_sep, C2SX(" "), keep_text_spaces, sz_line, nb_char_tab) + +/* + Create a new XML document from a given 'filename' and load it to 'doc'. + 'text_as_nodes' should be non-zero to put text into separate TAG_TEXT nodes. + Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise. + */ +int XMLDoc_parse_file_DOM_text_as_nodes(const SXML_CHAR* filename, XMLDoc* doc, int text_as_nodes); + +/* For backward compatibility */ +#define XMLDoc_parse_file_DOM(filename, doc) XMLDoc_parse_file_DOM_text_as_nodes(filename, doc, 0) + +/* + Create a new XML document from a memory buffer 'buffer' that can be given a name 'name', and load + it into 'doc'. + 'text_as_nodes' should be non-zero to put text into separate TAG_TEXT nodes. + Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise. + */ +int XMLDoc_parse_buffer_DOM_text_as_nodes(const SXML_CHAR* buffer, const SXML_CHAR* name, XMLDoc* doc, int text_as_nodes); + +/* For backward compatibility */ +#define XMLDoc_parse_buffer_DOM(buffer, name, doc) XMLDoc_parse_buffer_DOM_text_as_nodes(buffer, name, doc, 0) + +/* + Parse an XML document from a given 'filename', calling SAX callbacks given in the 'sax' structure. + 'user' is a user-given pointer that will be given back to all callbacks. + Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise. + */ +int XMLDoc_parse_file_SAX(const SXML_CHAR* filename, const SAX_Callbacks* sax, void* user); + +/* + Parse an XML document from a memory buffer 'buffer' that can be given a name 'name', + calling SAX callbacks given in the 'sax' structure. + 'user' is a user-given pointer that will be given back to all callbacks. + Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise. + */ +int XMLDoc_parse_buffer_SAX(const SXML_CHAR* buffer, const SXML_CHAR* name, const SAX_Callbacks* sax, void* user); + +/* + Parse an XML file using the DOM implementation. + */ +#define XMLDoc_parse_file XMLDOC_parse_file_DOM + + + +/* --- Utility functions --- */ + +/* + Functions to get next byte from buffer data source and know if the end has been reached. + Return as 'fgetc' and 'feof' would for 'FILE*'. + */ +int _bgetc(DataSourceBuffer* ds); +int _beob(DataSourceBuffer* ds); +/* + Reads a line from data source 'in', eventually (re-)allocating a given buffer 'line'. + Characters read will be stored in 'line' starting at 'i0' (this allows multiple calls to + 'read_line_alloc' on the same 'line' buffer without overwriting it at each call). + 'in_type' specifies the type of data source to be read: 'in' is 'FILE*' if 'in_type' + 'sz_line' is the size of the buffer 'line' if previously allocated. 'line' can point + to NULL, in which case it will be allocated '*sz_line' bytes. After the function returns, + '*sz_line' is the actual buffer size. This allows multiple calls to this function using the + same buffer (without re-allocating/freeing). + If 'sz_line' is non NULL and non 0, it means that '*line' is a VALID pointer to a location + of '*sz_line' SXML_CHAR (not bytes! Multiply by sizeof(SXML_CHAR) to get number of bytes). + Searches for character 'from' until character 'to'. If 'from' is 0, starts from + current position. If 'to' is 0, it is replaced by '\n'. + If 'keep_fromto' is 0, removes characters 'from' and 'to' from the line. + If 'interest_count' is not NULL, will receive the count of 'interest' characters while searching + for 'to' (e.g. use 'interest'='\n' to count lines in file). + Returns the number of characters in the line or 0 if an error occurred. + 'read_line_alloc' uses constant 'MEM_INCR_RLA' to reallocate memory when needed. It is possible + to override this definition to use another value. + */ +int read_line_alloc(void* in, DataSourceType in_type, SXML_CHAR** line, int* sz_line, int i0, SXML_CHAR from, SXML_CHAR to, int keep_fromto, SXML_CHAR interest, int* interest_count); + +/* + Concatenates the string pointed at by 'src1' with 'src2' into '*src1' and + return it ('*src1'). + Return NULL when out of memory. + */ +SXML_CHAR* strcat_alloc(SXML_CHAR** src1, const SXML_CHAR* src2); + +/* + Strip spaces at the beginning and end of 'str', modifying 'str'. + If 'repl_sq' is not '\0', squeezes spaces to an single character ('repl_sq'). + If not '\0', 'protect' is used to protect spaces from being deleted (usually a backslash). + Returns the string or NULL if 'protect' is a space (which would not make sense). + */ +SXML_CHAR* strip_spaces(SXML_CHAR* str, SXML_CHAR repl_sq); + +/* + Remove '\' characters from 'str', modifying it. + Return 'str'. + */ +SXML_CHAR* str_unescape(SXML_CHAR* str); + +/* + Split 'str' into a left and right part around a separator 'sep'. + The left part is located between indexes 'l0' and 'l1' while the right part is + between 'r0' and 'r1' and the separator position is at 'i_sep' (whenever these are + not NULL). + If 'ignore_spaces' is 'true', computed indexes will not take into account potential + spaces around the separator as well as before left part and after right part. + if 'ignore_quotes' is 'true', " or ' will not be taken into account when parsing left + and right members. + Whenever the right member is empty (e.g. "attrib" or "attrib="), '*r0' is initialized + to 'str' size and '*r1' to '*r0-1' (crossed). + If the separator was not found (i.e. left member only), '*i_sep' is '-1'. + Return 'false' when 'str' is malformed, 'true' when splitting was successful. + */ +int split_left_right(SXML_CHAR* str, SXML_CHAR sep, int* l0, int* l1, int* i_sep, int* r0, int* r1, int ignore_spaces, int ignore_quotes); + +typedef enum _BOM_TYPE { + BOM_NONE = 0x00, + BOM_UTF_8 = 0xefbbbf, + BOM_UTF_16BE = 0xfeff, + BOM_UTF_16LE = 0xfffe, + BOM_UTF_32BE = 0x0000feff, + BOM_UTF_32LE = 0xfffe0000 +} BOM_TYPE; +/* + Detect a potential BOM at the current file position and read it into 'bom' (if not NULL, + 'bom' should be at least 5 bytes). It also moves the 'f' beyond the BOM so it's possible to + skip it by calling 'freadBOM(f, NULL, NULL)'. If no BOM is found, it leaves 'f' file pointer + is reset to its original location. + If not null, 'sz_bom' is filled with how many bytes are stored in 'bom'. + Return the BOM type or BOM_NONE if none found (empty 'bom' in this case). + */ +BOM_TYPE freadBOM(FILE* f, unsigned char* bom, int* sz_bom); + +/* + Replace occurrences of special HTML characters escape sequences (e.g. '&') found in 'html' + by its character equivalent (e.g. '&') into 'str'. + If 'html' and 'str' are the same pointer replacement is made in 'str' itself, overwriting it. + If 'str' is NULL, replacement is made into 'html', overwriting it. + Returns 'str' (or 'html' if 'str' was NULL). + */ +SXML_CHAR* html2str(SXML_CHAR* html, SXML_CHAR* str); + +/* + Replace occurrences of special characters (e.g. '&') found in 'str' into their XML escaped + equivalent (e.g. '&') into 'xml'. + 'xml' is supposed allocated to the correct size (e.g. using 'malloc(strlen_html(str)+30)') and + different from 'str' (unlike 'html2str'), as string will expand. If it is NULL, 'str' will be + analyzed and a string will be allocated to the exact size, before being returned. In that case, + it is the responsibility of the caller to free() the result! + Return 'xml' or NULL if 'str' or 'xml' are NULL, or when 'xml' is 'str'. +*/ +SXML_CHAR* str2html(SXML_CHAR* str, SXML_CHAR* xml); + +/* + Return the length of 'str' as if all its special character were replaced by their HTML + equivalent. + Return 0 if 'str' is NULL. + */ +int strlen_html(SXML_CHAR* str); + +/* + Print 'str' to 'f', transforming special characters into their HTML equivalent. + Returns the number of output characters. + */ +int fprintHTML(FILE* f, SXML_CHAR* str); + +/* + Checks whether 'str' corresponds to 'pattern'. + 'pattern' can use wildcads such as '*' (any potentially empty string) or + '?' (any character) and use '\' as an escape character. + Returns 'true' when 'str' matches 'pattern', 'false' otherwise. + */ +int regstrcmp(SXML_CHAR* str, SXML_CHAR* pattern); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tzx2wav.cpp b/tzx2wav.cpp index 7e25c16..0e45bca 100644 --- a/tzx2wav.cpp +++ b/tzx2wav.cpp @@ -1,1395 +1,1395 @@ -///////////////////////////////////////////////////////////////////// -// TZX to VAV Converter v0.2 for Bloodshed Dev-C++ compiler // -// (C) 2005-2006 Francisco Javier Crespo // -// // -// MiSTer adaptation (CSW v1 only) // -// (C) 2017 Francisco Javier Crespo // -// // -// Originally based on source code from these works: // -// PLAYTZX v0.60b for Watcom C compiler (C) 1997-2004 Tomaz Kac // -// PLAYTZX Unix v0.12b (C) 2003 Tero Turtiainen / Fredrick Meunier // -///////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include -#include -#include -#include -#include "tzx2wav.h" -#include "spi.h" - -// Computer entries -const char *hwids_01[] = { - "ZX Spectrum 16k", - "ZX Spectrum 48k, Plus", - "ZX Spectrum 48k Issue 1", - "ZX Spectrum 128k (Sinclair)", - "ZX Spectrum 128k +2 (Grey case)", - "ZX Spectrum 128k +2A, +3", - "Timex Sinclair TC-2048", - "Timex Sinclair TS-2068", - "Pentagon 128", - "Sam Coupe", - "Didaktik M", - "Didaktik Gama", - "ZX-81 with 1k RAM", - "ZX-81 with 16k RAM or more", - "ZX Spectrum 128k, Spanish version", - "ZX Spectrum, Arabic version", - "TK 90-X", - "TK 95", - "Byte", - "Elwro", - "ZS Scorpion", - "Amstrad CPC 464", - "Amstrad CPC 664", - "Amstrad CPC 6128", - "Amstrad CPC 464+", - "Amstrad CPC 6128+", - "Jupiter ACE" - "Enterprise", - "Commodore 64", - "Commodore 128" -}; - -#define MAJREV 1 // Major revision of the format this program supports -#define MINREV 13 // Minor revision of the format this program supports - -// C64 Loader defines ... - -#define ROM_S_HALF 616 // ROM Loader SHORT Half Wave -#define ROM_M_HALF 896 // ROM Loader MEDIUM Half Wave -#define ROM_L_HALF 1176 // ROM Loader LONG Half Wave - -#define STT_0_HALF 426 // Standard Turbo Tape BIT 0 Half Wave -#define STT_1_HALF 596 // Standard Turbo Tape BIT 1 Half Wave - -// Other defines ... - -#define LOAMP 0x26 // Low Level Amplitude (-3 dB) -#define HIAMP 0xDA // High Level Amplitude (-3 dB) -static unsigned int freq = 44100; // Default Sample Frequency - -static unsigned char *mem = 0; // File in Memory -static int pos; // Position in File -static int curr; // Current block that is playing -static int numblocks; // Total Num. of blocks -static unsigned long oflen; // Length of output file -static int block[2048]; // Array of Block starts -static double cycle; // Frequency / 3500000 (Z80 clock) - -static int cpc=0; // Amstrad CPC tape ? -static int sam=0; // SAM Coupe tape ? - -static int id; // Current Block ID -static int pilot; // Len of Pilot signal (in hp's) -static int sb_pilot; // Pilot pulse -static int sb_sync1; // Sync first half-period (hp) -static int sb_sync2; // Sync second -static int sb_bit0; // Bit-0 -static int sb_bit1; // Bit-1 -static int sb_pulse; // Pulse in Sequence of pulses and direct recording block -static int lastbyte; // How many bits are in last byte of data ? -static int pause_ms; // Pause after current block (in milliseconds) -static int skippause=0; // Overrides pause value in last TZX block - -static int singlepulse; // Flag to activate single pulse waves -static int manchester; // Flag to activate manchester encoded waves - -static unsigned char *data; // Data to be played -static int datalen; // Len of ^^^ -static int datapos; // Position in ^^^ -static int bitcount; // How many bits to play in current byte ? -static int sb_bit; // should we play bit 0 or 1 ? -static char databyte; // Current Byte to be replayed of the data -static signed short jump; // Relative Jump -static int not_rec; // Some blocks were not recognised ?? -static int starting=1; // starting block -static int ending=0; // ending block - -static int expand=0; // Expand Groups ? -static int draw=1; // Local flag for outputing a line when in a group - -static int speed; - -static int loop_start=0; // Position of the last Loop Start block -static int loop_count=0; // Counter of the Loop -static int call_pos=0; // Position of the last Call Sequence block -static int call_num=0; // Number of Calls in the last Call Sequence block -static int call_cur=0; // Current Call to be made -static int num_sel; // Number of Selections in the Select block -static int jumparray[256]; // Array of all possible jumps in Select block - -static int sb_bit0_f, sb_bit0_s, sb_bit1_f, sb_bit1_s, xortype, sb_finishbyte_f, - sb_finishbyte_s, sb_finishdata_f, sb_finishdata_s, num_lead_in, xorvalue; -static int trailing, sb_trailing; -static char lead_in_byte; -static int endian; -static char add_bit; - -static int inv = 0; - -static char tstr[255]; -static char spdstr[255]; -static char pstr[255]; - -static void core_write(const void *buf, int size) -{ - const char *addr = (const char*)buf; - while (size--) - { - spi8(*addr++); - oflen++; - } -} - -/////////////////////////////// -// CSW v1.01 handling routines -/////////////////////////////// - -static int amp, cswamp; -static unsigned int cswlen; - -void CSW1_Init(void) -{ - // Official CSW format documentation at: - // http://www.ramsoft.bbk.org/csw.html - - unsigned short Revision = 0x0101; - unsigned char CompType = 1; - unsigned int Reserved = 0; - - core_write("Compressed Square Wave\032", 23); - core_write(&Revision, 2); // Major & Minor revision - core_write(&freq, 2); // Sample Rate - core_write(&CompType, 1); // Compression Type - core_write(&inv, 1); // Polarity - core_write(&Reserved, 3); // Reserved bytes - - cswamp = LOAMP; - cswlen = 0; -} - -void CSW1_Write(unsigned int samples) -{ - if (cswamp == amp) - { - cswlen += samples; - return; - } - if (cswlen < 256) - { - core_write(&cswlen, 1); - } - else - { - int zero = 0; - core_write(&zero, 1); - core_write(&cswlen, 4); - } - - cswamp = amp; - cswlen = samples; -} - -////////////////////////////////// -// Generic wave handling routines -////////////////////////////////// - -unsigned int Samples(unsigned int n) -{ - // Convert a sampling value in Z80 T-States to samples for wave output - return ((unsigned int)(0.5 + (cycle*(double)n))); -} - -void ToggleAmp(void) -{ - // Toggles the sign of the wave - // WHOLE CONCEPT TO BE RECODED IN ToggleSgn(); - - if (amp == LOAMP) amp = HIAMP; - else amp = LOAMP; -} - -void PlayWave(unsigned int len) -{ - CSW1_Write(len); -} - -void PauseWave(unsigned int pause_ms) -{ - // Waits for "pause" milliseconds - - int p; - if (((!skippause) || (curr != (numblocks - 1)))) - { - if (!curr && pause_ms > 2000) pause_ms = 2000; - p = (unsigned int)((((float)pause_ms)*freq) / 1000.0); - PlayWave(p); - } -} - -void PlayFinish() -{ - if (cswlen) - { - ToggleAmp(); - CSW1_Write(0); - } -} - -///////////////////////////// -// TZX Commodore 64 routines -///////////////////////////// - -void PlayC64(unsigned int len) -{ - PlayWave(len); - ToggleAmp(); - PlayWave(len); - ToggleAmp(); -} - -void PlayC64ROMByte(char byte, int finish) -{ - xorvalue = xortype; - while (bitcount) - { - if (!endian) sb_bit = byte & 0x01; - else sb_bit = byte & 0x80; - if (sb_bit) - { - if (sb_bit1_f) PlayC64(sb_bit1_f); - if (sb_bit1_s) PlayC64(sb_bit1_s); - xorvalue ^= sb_bit; - } - else - { - if (sb_bit0_f) PlayC64(sb_bit0_f); - if (sb_bit0_s) PlayC64(sb_bit0_s); - xorvalue ^= sb_bit; - } - if (!endian) byte >>= 1; - else byte <<= 1; - bitcount--; - } - if (xortype != 0xFF) - { - if (xorvalue) - { - if (sb_bit1_f) PlayC64(sb_bit1_f); - if (sb_bit1_s) PlayC64(sb_bit1_s); - } - else - { - if (sb_bit0_f) PlayC64(sb_bit0_f); - if (sb_bit0_s) PlayC64(sb_bit0_s); - } - } - if (!finish) - { - if (sb_finishbyte_f) PlayC64(sb_finishbyte_f); - if (sb_finishbyte_s) PlayC64(sb_finishbyte_s); - } - else - { - if (sb_finishdata_f) PlayC64(sb_finishdata_f); - if (sb_finishdata_s) PlayC64(sb_finishdata_s); - } -} - -void PlayC64TurboByte(char byte) -{ - int add_num; - - add_num = add_bit & 3; - - if (add_num && !(add_bit & 4)) - { - while (add_num) - { - if (add_bit & 8) PlayC64(sb_bit1); - else PlayC64(sb_bit0); - add_num--; - } - } - - while (bitcount) - { - if (!endian) sb_bit = byte & 0x01; - else sb_bit = byte & 0x80; - if (sb_bit) PlayC64(sb_bit1); - else PlayC64(sb_bit0); - if (!endian) byte >>= 1; - else byte <<= 1; - bitcount--; - } - - if (add_num && (add_bit & 4)) - { - while (add_num) - { - if (add_bit & 8) PlayC64(sb_bit1); - else PlayC64(sb_bit0); - add_num--; - } - } -} - -//////////////////////////////// -// Game identification routines -//////////////////////////////// - -void GetC64ROMName(char *name, unsigned char *data) -{ - char d; - int n = 0; - for (; n < 16; n++) - { - d = data[14 + n]; - if (d < 32 || d>125) - name[n] = ' '; - else - name[n] = d; - } - name[n] = 0; -} - -void GetC64StandardTurboTapeName(char *name, unsigned char *data) -{ - char d; - int n = 0; - for (; n < 16; n++) - { - d = data[15 + n]; - if (d < 32 || d>125) - name[n] = ' '; - else - name[n] = d; - } - name[n] = 0; -} - -void IdentifyC64ROM(int pos, unsigned char *data, int type) -{ - char name[255]; - - // Determine Loader type - if ((sb_pilot == ROM_S_HALF) && (sb_sync1 == ROM_L_HALF) && (sb_sync2 == ROM_M_HALF) && - (sb_bit0_f == ROM_S_HALF) && (sb_bit0_s == ROM_M_HALF) && (sb_bit1_f == ROM_M_HALF) && - (sb_bit1_s == ROM_S_HALF) && (xortype == 0x01)) - { - // ROM Loader - if ((data[0] == 0x89) && (data[1] == 0x88) && (data[2] == 0x87) && (data[3] == 0x86) && - (data[4] == 0x85) && (data[5] == 0x84) && (data[6] == 0x83) && (data[7] == 0x82) && - (data[8] == 0x81)) - { - if (pos == 202) - { - if (!type) - { - strcpy(name, "Header: "); - GetC64ROMName(name + 8, data); - } - else - { - strcpy(name, "ROM Header: "); - GetC64ROMName(name + 12, data); - } - } - else - { - if (!type) - { - strcpy(name, "Data Block "); - } - else - { - strcpy(name, "ROM: Data Block"); - } - } - } - else - { - if (!type) strcpy(name, "------------------------"); - else strcpy(name, "ROM: Last Block Repeated"); - } - strcpy(tstr, name); - strcpy(spdstr, "C64 ROM Data "); - return; - } - - if (!type) strcpy(tstr, "------------------------"); - else strcpy(tstr, "Unknown"); - strcpy(spdstr, "C64 Data "); -} - -void IdentifyC64Turbo(int pos, unsigned char *data, int type) -{ - char name[255]; - - // Determine Loader type - if (sb_bit0 == STT_0_HALF && sb_bit1 == STT_1_HALF && lead_in_byte == 0x02) - { - // Standard Turbo Tape Loader - if (data[0] == 0x09 && data[1] == 0x08 && data[2] == 0x07 && data[3] == 0x06 && - data[4] == 0x05 && data[5] == 0x04 && data[6] == 0x03 && data[7] == 0x02 && - data[8] == 0x01) - { - if (pos == 32 && data[9] != 0x00) - { - if (!type) - { - strcpy(name, "Header: "); - GetC64StandardTurboTapeName(name + 8, data); - } - else - { - strcpy(name, "TurboTape Header: "); - GetC64StandardTurboTapeName(name + 18, data); - } - } - else - { - if (!type) strcpy(name, "------------------------"); - else strcpy(name, "TurboTape Data Block"); - } - } - else - { - if (!type) strcpy(name, "------------------------"); - else strcpy(name, "TurboTape Unknown"); - } - strcpy(tstr, name); - strcpy(spdstr, "C64 Turbo "); - return; - } - if (!type) strcpy(tstr, "------------------------"); - else strcpy(tstr, "Unknown"); - strcpy(spdstr, "C64 Data "); -} - -void Identify(int len, unsigned char *temp, int type) -{ - int n; - int s; - - if (cpc) - { - if (temp[0] == 44) - { - if (!type) s = 4; - else s = 0; - strcpy(tstr, " "); - for (n = 0; n < 16; n++) - { - if (temp[n + 1]) tstr[n + s] = temp[n + 1]; - else tstr[n + s] = ' '; - } - for (n = 0; n < 4; n++) tstr[n + s + 16] = ' '; - tstr[n + s + 16] = 0; - } - else - { - if (!type) - strcpy(tstr, " ------------------ "); - else - strcpy(tstr, "Headerless"); - } - return; - } - - if (sam) - { - if (temp[0] == 1 && (len>80 && len < 84) && (temp[1] >= 0x10 && temp[1] <= 0x13)) - { - if (!type) - { - s = 14; - switch (temp[1]) - { - case 0x10: strcpy(tstr, " Program : "); break; - case 0x11: strcpy(tstr, " Num. Array : "); break; - case 0x12: strcpy(tstr, "Char. Array : "); break; - case 0x13: strcpy(tstr, " Bytes : "); break; - } - } - else - { - switch (temp[1]) - { - case 0x10: strcpy(tstr, "Program : "); s = 10; break; - case 0x11: strcpy(tstr, "Num. Array : "); s = 13; break; - case 0x12: strcpy(tstr, "Char. Array : "); s = 14; break; - case 0x13: strcpy(tstr, "Bytes : "); s = 8; break; - } - } - for (n = 0; n < 10; n++) - { - if (temp[n + 2]>31 && temp[n + 2] < 127) - tstr[n + s] = temp[n + 2]; - else - tstr[n + s] = 32; - } - tstr[n + s] = 0; - } - else - { - if (!type) - strcpy(tstr, " --------------------"); // Not Header - else - strcpy(tstr, "Headerless"); - } - return; - } - - if (temp[0] == 0 && (len == 19 || len == 20) && temp[1] < 4) - { - if (!type) - { - s = 14; - switch (temp[1]) - { - case 0x00: strcpy(tstr, " Program : "); break; - case 0x01: strcpy(tstr, " Num. Array : "); break; - case 0x02: strcpy(tstr, "Char. Array : "); break; - case 0x03: strcpy(tstr, " Bytes : "); break; - } - } - else - { - switch (temp[1]) - { - case 0x00: strcpy(tstr, "Program : "); s = 10; break; - case 0x01: strcpy(tstr, "Num. Array : "); s = 13; break; - case 0x02: strcpy(tstr, "Char. Array : "); s = 14; break; - case 0x03: strcpy(tstr, "Bytes : "); s = 8; break; - } - } - for (n = 0; n < 10; n++) - { - if (temp[n + 2]>31 && temp[n + 2] < 127) - tstr[n + s] = temp[n + 2]; - else - tstr[n + s] = 32; - } - tstr[n + s] = 0; - } - else - { - if (!type) - strcpy(tstr, " --------------------"); // Not Header - else - strcpy(tstr, "Headerless"); - } -} - -////////////////////////////////////////////////////////// -// Conversion routines to fetch bytes in Big Endian order -////////////////////////////////////////////////////////// - -unsigned int Get2(unsigned char *pointer) -{ - return (pointer[0] | (pointer[1] << 8)); -} - -unsigned int Get3(unsigned char *pointer) -{ - return (pointer[0] | (pointer[1] << 8) | (pointer[2] << 16)); -} - -unsigned int Get4(unsigned char *pointer) -{ - return (pointer[0] | (pointer[1] << 8) | (pointer[2] << 16) | (pointer[3] << 24)); -} - -///////////////////////// -// Miscelaneous routines -///////////////////////// - -void CopyString(char *destination, unsigned char *source, unsigned int len) -{ - // Could just use strcpy ... - - unsigned int n; - for (n = 0; n < len; n++) - destination[n] = source[n]; - destination[n] = 0; -} - -void MakeFixedString(char *s, int i) -{ - // This will create a fixed length string from null-terminated one... - - int n = 0; - int k = 0; - - while (i) - { - if (!s[n]) k = 1; - if (k) s[n] = ' '; - n++; - i--; - } - s[n] = 0; -} - -/////////////////////////////// -// TZX Blocks Parsing routines -/////////////////////////////// - -void Analyse_ID10(void) // Standard Loading Data block -{ - pause_ms = Get2(&data[0]); - datalen = Get2(&data[2]); - data += 4; - if (data[0] == 0x00) pilot = 8064; - else pilot = 3220; - sb_pilot = Samples(2168); - sb_sync1 = Samples(667); - sb_sync2 = Samples(735); - sb_bit0 = Samples(885); - sb_bit1 = Samples(1710); - lastbyte = 8; -} - -void Analyse_ID11(void) // Custom Loading Data block -{ - sb_pilot = Samples(Get2(&data[0])); - sb_sync1 = Samples(Get2(&data[2])); - sb_sync2 = Samples(Get2(&data[4])); - sb_bit0 = Samples(Get2(&data[6])); - sb_bit1 = Samples(Get2(&data[8])); - speed = (int)((1710.0 / (double)Get2(&data[8]))*100.0); - pilot = Get2(&data[10]); - lastbyte = (int)data[12]; - pause_ms = Get2(&data[13]); - datalen = Get3(&data[15]); - data += 18; -} - -void Analyse_ID12(void) // Pure Tone -{ - sb_pilot = Samples(Get2(&data[0])); - pilot = Get2(&data[2]); - if (draw) printf(" Pure Tone Length: %5d\n", pilot); - while (pilot) - { - PlayWave(sb_pilot); - ToggleAmp(); - pilot--; - } -} - -void Analyse_ID13(void) // Sequence of Pulses -{ - pilot = (int)data[0]; data++; - if (draw) printf(" Sequence of Pulses Length: %5d\n", pilot); - while (pilot) - { - sb_pulse = Samples(Get2(&data[0])); - PlayWave(sb_pulse); - ToggleAmp(); - pilot--; - data += 2; - } -} - -void Analyse_ID14(void) // Pure Data -{ - sb_pilot = pilot = sb_sync1 = sb_sync2 = 0; - sb_bit0 = Samples(Get2(&data[0])); - sb_bit1 = Samples(Get2(&data[2])); - speed = (int)((1710.0 / (double)Get2(&data[2]))*100.0); - lastbyte = (int)data[4]; - pause_ms = Get2(&data[5]); - datalen = Get3(&data[7]); - data += 10; -} - -void Analyse_ID15(void) // Direct Recording -{ - // For now the BEST way is to use the sample frequency for replay that is - // exactly the SAME as the Original Freq. used when sampling this block ! - // i.e. NO downsampling is handled YET ... use TAPER when you need it ! ;-) - - sb_pulse = Samples(Get2(&data[0])); - if (!sb_pulse) sb_pulse = 1; // In case sample frequency > 44100 - pause_ms = Get2(&data[2]); // (Should work for frequencies upto 48000) - lastbyte = (int)data[4]; - datalen = Get3(&data[5]); - if (draw) printf(" Direct Recording Length:%6d Original Freq.: %5d Hz\n", - datalen, (int)(0.5 + (3500000.0 / (double)Get2(&data[0])))); - data = &data[8]; - datapos = 0; - // Replay Direct Recording block ... - while (datalen) - { - if (datalen != 1) bitcount = 8; - else bitcount = lastbyte; - databyte = data[datapos]; - while (bitcount) - { - if (databyte & 0x80) amp = HIAMP; - else amp = LOAMP; - PlayWave(sb_pulse); - databyte <<= 1; - bitcount--; - } - datalen--; - datapos++; - } - ToggleAmp(); - if (pause_ms) PauseWave(pause_ms); -} - -void Analyse_ID16(void) // C64 ROM Type Data Block -{ - data += 4; - sb_pilot = Get2(&data[0]); - pilot = Get2(&data[2]); - sb_sync1 = Get2(&data[4]); - sb_sync2 = Get2(&data[6]); - sb_bit0_f = Get2(&data[8]); - sb_bit0_s = Get2(&data[10]); - sb_bit1_f = Get2(&data[12]); - sb_bit1_s = Get2(&data[14]); - xortype = (int)(data[16]); - sb_finishbyte_f = Get2(&data[17]); - sb_finishbyte_s = Get2(&data[19]); - sb_finishdata_f = Get2(&data[21]); - sb_finishdata_s = Get2(&data[23]); - sb_trailing = Get2(&data[25]); - trailing = Get2(&data[27]); - lastbyte = (int)(data[29]); - endian = data[30]; - pause_ms = Get2(&data[31]); - datalen = Get3(&data[33]); - data += 36; - IdentifyC64ROM(datalen, data, 1); -} - -void Analyse_ID17(void) // C64 Turbo Tape Data Block -{ - data += 4; - sb_bit0 = Get2(&data[0]); - sb_bit1 = Get2(&data[2]); - add_bit = data[4]; - num_lead_in = Get2(&data[5]); - lead_in_byte = data[7]; - lastbyte = (int)data[8]; - endian = data[9]; - trailing = Get2(&data[10]); - sb_trailing = data[12]; - pause_ms = Get2(&data[13]); - datalen = Get3(&data[15]); - data += 18; - IdentifyC64Turbo(datalen, data, 1); -} - -void Analyse_ID20(void) // Pause or Stop the Tape command -{ - pause_ms = Get2(&data[0]); - amp = LOAMP; - if (pause_ms) - { - if (draw) printf(" Pause Length: %2.3fs\n", ((float)pause_ms) / 1000.0); - PauseWave(pause_ms); - amp = LOAMP; - } - else - { - if (draw) printf(" Stop the tape command!\n"); - PauseWave(2000); // 2 seconds of pause in "Stop Tape" wave output - amp = LOAMP; - } -} - -void Analyse_ID21(void) // Group Start -{ - CopyString(pstr, &data[1], data[0]); - if (draw) printf(" Group: %s\n", pstr); - if (!expand) draw = 0; -} - -void Analyse_ID22(void) // Group End -{ - if (draw) printf(" Group End\n"); - draw = 1; -} - -void Analyse_ID23(void) // Jump To Relative -{ - jump = (signed short)(data[0] + data[1] * 256); - if (draw) printf(" Jump Relative: %d (To Block %d)\n", jump, curr + jump + 1); - curr += jump; - curr--; -} - -void Analyse_ID24(void) // Loop Start -{ - loop_start = curr; - loop_count = Get2(&data[0]); - if (draw) printf(" Loop Start, Counter: %d\n", loop_count); -} - -void Analyse_ID25(void) // Loop End -{ - loop_count--; - if (loop_count > 0) - { - if (draw) printf(" Loop End, Still To Go %d Time(s)!\n", loop_count); - curr = loop_start; - } - else - { - if (draw) printf(" Loop End, Finished\n"); - } -} - -void Analyse_ID26(void) // Call Sequence -{ - call_pos = curr; - call_num = Get2(&data[0]); - call_cur = 0; - jump = (signed short)(data[2] + data[3] * 256); - if (draw) printf(" Call Sequence, Number of Calls : %d, First: %d (To Block %d)\n", call_num, jump, curr + jump + 1); - curr += jump; - curr--; -} - -void Analyse_ID27(void) // Return from Sequence -{ - call_cur++; - if (call_cur == call_num) - { - if (draw) printf(" Return from Call, Last Call Finished\n"); - curr = call_pos; - } - else - { - curr = call_pos; - data = &mem[block[curr] + 1]; - jump = (signed short)(data[call_cur * 2 + 2] + data[call_cur * 2 + 3] * 256); - if (draw) printf(" Return from Call, Calls Left: %d, Next: %d (To Block %d)\n", - call_num - call_cur, jump, curr + jump + 1); - curr += jump; - curr--; - } -} - -void Analyse_ID28(void) // Select Block -{ - num_sel = data[2]; - printf(" Select :\n"); - data += 3; - for (int n = 0; n < num_sel; n++) - { - jump = (signed short)(data[0] + data[1] * 256); - jumparray[n] = jump; - CopyString(spdstr, &data[3], data[2]); - printf("%5d : %s\n", n + 1, spdstr); - data += 3 + data[2]; - } - - //no interactive shell. choose 1. - PauseWave(200); - int k = 1; - amp = LOAMP; - - /* - printf(">> Press the number!\n"); - PauseWave(5000); // Why?!?!?!?! - k = getchar(); - if (k == 27) Error("ESCAPE key pressed!"); - k -= 48; - if (k<1 || k>num_sel) printf("Illegal Selection... Continuing...\n"); - else - */ - { - curr += jumparray[k - 1]; - curr--; - } -} - -void Analyse_ID2A(void) // Stop the tape if in 48k mode -{ - if (draw) printf(" Stop the tape in 48k mode!\n"); - PauseWave(3000); - amp=LOAMP; -} - -void Analyse_ID30(void) // Description -{ - CopyString(pstr, &data[1], data[0]); - if (draw) printf(" Description: %s\n", pstr); -} - -void Analyse_ID31(void) // Message -{ - CopyString(pstr, &data[2], data[1]); - // Pause in Message block is ignored ... - if (draw) printf(" Message: %s\n", pstr); -} - -void Analyse_ID32(void) // Archive Info -{ - if (draw) - { - if (data[3] == 0) - { - CopyString(spdstr, &data[5], data[4]); - sprintf(tstr, " Title: %s", spdstr); - MakeFixedString(tstr, 69); - strcpy(tstr + 52, " (-v for more)"); - printf("%s\n", tstr); - } - else - { - sprintf(tstr, " Archive Info"); - MakeFixedString(tstr, 69); - strcpy(tstr + 52, " (-v for more)"); - printf("%s\n", tstr); - } - } -} - -void Analyse_ID33(void) // Hardware Info -{ - if (data[1] == 0 && data[2] > 0x14 && data[2] < 0x1a && data[3] == 1) cpc = 1; - if (data[1] == 0 && data[2] == 0x09 && data[3] == 1) sam = 1; - if (draw) - { - if (data[1] != 0 || data[3] != 1) - { - sprintf(tstr, " Hardware Type"); - MakeFixedString(tstr, 69); - strcpy(tstr + 52, " (-v for more)"); - printf("%s\n", tstr); - } - else - { - printf(" This tape is made for %s !\n", hwids_01[data[2]]); - } - } -} - -void Analyse_ID34(void) // Emulation info -{ - if (draw) printf(" Information for emulators.\n"); -} - -void Analyse_ID35(void) // Custom Info -{ - CopyString(pstr, data, 16); - if (draw) - { - if (strcmp(pstr, "POKEs ")) - printf(" Custom Info: %s\n", pstr); - // Only Name of Custom info except POKEs is used ... - else - { - sprintf(tstr, " Custom Info: %s", pstr); - MakeFixedString(tstr, 69); - strcpy(tstr + 52, " (-v for more)"); - printf("%s\n", tstr); - } - } -} - -void Analyse_ID40(void) // Snapshot -{ - if (draw) printf(" Snapshot (Not Supported yet)\n"); -} - -void Analyse_ID5A(void) // ZXTape! -{ - if (draw) printf(" Start of the new tape (Merged Tapes)\n"); -} - -void Analyse_Unknown(void) // Unknown blocks -{ - if (draw) printf(" Unknown block %02X !\n", id); -} - -//////////////////////// -// Main TZX2WAV program -//////////////////////// - -int tzx2csw(fileTYPE *f) -{ - freq = 44100; - starting = 1; - ending = 0; - expand = 0; - skippause = 0; - inv = 0; - oflen = 0; - - mem = (unsigned char *)malloc(f->size); - if (mem == NULL) - { - printf("\n-- Not enough memory to load the file!"); - return 0; - } - - // Start reading file... - FileReadAdv(f, mem, 10); - mem[7] = 0; - - if (strcmp((const char*)mem, "ZXTape!")) - { - printf("\n-- File is not in ZXTape format!"); - free(mem); - return 0; - } - - printf("\nZXTape file revision %d.%02d\n", mem[8], mem[9]); - if (!mem[8]) - { - printf("\n-- Development versions of ZXTape format are not supported!"); - free(mem); - return 0; - } - - if (mem[8] > MAJREV) printf("\n-- Warning: Some blocks may not be recognised and used!\n"); - if (mem[8] == MAJREV && mem[9] > MINREV) printf("\n-- Warning: Some of the data might not be properly recognised!\n"); - FileReadAdv(f, mem, f->size - 10); - numblocks = 0; pos = 0; - not_rec = 0; - - // Go through the file and record block starts ... - // (not necessary, could just go right through it) - - while (pos < f->size - 10) - { - block[numblocks] = pos; - pos++; - switch (mem[pos - 1]) - { - case 0x10: pos += Get2(&mem[pos + 0x02]) + 0x04; break; - case 0x11: pos += Get3(&mem[pos + 0x0F]) + 0x12; break; - case 0x12: pos += 0x04; break; - case 0x13: pos += (mem[pos + 0x00] * 0x02) + 0x01; break; - case 0x14: pos += Get3(&mem[pos + 0x07]) + 0x0A; break; - case 0x15: pos += Get3(&mem[pos + 0x05]) + 0x08; break; - case 0x16: pos += Get4(&mem[pos + 0x00]) + 0x04; break; - case 0x17: pos += Get4(&mem[pos + 0x00]) + 0x04; break; - - case 0x20: pos += 0x02; break; - case 0x21: pos += mem[pos + 0x00] + 0x01; break; - case 0x22: break; - case 0x23: pos += 0x02; break; - case 0x24: pos += 0x02; break; - case 0x25: break; - case 0x26: pos += Get2(&mem[pos + 0x00]) * 0x02 + 0x02; break; - case 0x27: break; - case 0x28: pos += Get2(&mem[pos + 0x00]) + 0x02; break; - - case 0x2A: pos += 0x04; break; - - case 0x30: pos += mem[pos + 0x00] + 0x01; break; - case 0x31: pos += mem[pos + 0x01] + 0x02; break; - case 0x32: pos += Get2(&mem[pos + 0x00]) + 0x02; break; - case 0x33: pos += (mem[pos + 0x00] * 0x03) + 0x01; break; - case 0x34: pos += 0x08; break; - case 0x35: pos += Get4(&mem[pos + 0x10]) + 0x14; break; - - case 0x40: pos += Get3(&mem[pos + 0x01]) + 0x04; break; - - case 0x5A: pos += 0x09; break; - - default: pos += Get4(&mem[pos + 0x00]) + 0x04; - not_rec = 1; - } - numblocks++; - } - - printf("Number of Blocks: %d\n", numblocks); - - if (not_rec) - { - printf("\n-- Warning: Some blocks were *NOT* recognised!\n"); - } - - curr = 0; - - if (starting > 1) - { - if (starting > numblocks) - { - printf("\n-- Invalid Starting Block"); - free(mem); - return 0; - } - curr = starting - 1; - } - - if (ending > 0) - { - if (ending > numblocks || ending < starting) - { - printf("\n-- Invalid Ending Block"); - free(mem); - return 0; - } - numblocks = ending; - } - - printf("\nCreating CSW v1"); - printf(" file using %d Hz frequency ...\n\n", freq); - - CSW1_Init(); - amp = LOAMP; - - singlepulse = 0; - manchester = 0; - cycle = (double)freq / 3500000.0; // This is for the conversion later ... - - ///////////////////////////////////////////////////// - // Start replay of blocks (Main loop of the program) - ///////////////////////////////////////////////////// - - while (curr < numblocks) - { - if (draw) printf("Block %03d:", curr + 1); - - id = mem[block[curr]]; - data = &mem[block[curr] + 1]; - switch (id) - { - case 0x10: Analyse_ID10(); // Standard Loading Data block - break; - case 0x11: Analyse_ID11(); // Custom Loading Data block - break; - case 0x12: Analyse_ID12(); // Pure Tone - break; - case 0x13: Analyse_ID13(); // Sequence of Pulses - break; - case 0x14: Analyse_ID14(); // Pure Data - break; - case 0x15: Analyse_ID15(); // Direct Recording - break; - case 0x16: Analyse_ID16(); // C64 ROM Type Data Block - break; - case 0x17: Analyse_ID17(); // C64 Turbo Tape Data Block - break; - case 0x20: Analyse_ID20(); // Pause or Stop the Tape command - break; - case 0x21: Analyse_ID21(); // Group Start - break; - case 0x22: Analyse_ID22(); // Group End - break; - case 0x23: Analyse_ID23(); // Jump To Relative - break; - case 0x24: Analyse_ID24(); // Loop Start - break; - case 0x25: Analyse_ID25(); // Loop End - break; - case 0x26: Analyse_ID26(); // Call Sequence - break; - case 0x27: Analyse_ID27(); // Return from Sequence - break; - case 0x28: Analyse_ID28(); // Select Block - break; - case 0x2A: Analyse_ID2A(); // Stop the tape if in 48k mode - break; - case 0x30: Analyse_ID30(); // Description - break; - case 0x31: Analyse_ID31(); // Message - break; - case 0x32: Analyse_ID32(); // Archive Info - break; - case 0x33: Analyse_ID33(); // Hardware Info - break; - case 0x34: Analyse_ID34(); // Emulation info - break; - case 0x35: Analyse_ID35(); // Custom Info - break; - case 0x40: Analyse_ID40(); // Snapshot - break; - case 0x5A: Analyse_ID5A(); // ZXTape! - break; - default: Analyse_Unknown(); // Unknown blocks - } - - // TZX file blocks analysis finished - // Now we start generating the sound waves - - if ((id == 0x10 || id == 0x11 || id == 0x14)) // One of the data blocks ... - { - if (id != 0x14) Identify(datalen, data, 0); - else strcpy(tstr, " Pure Data "); - if (id == 0x10) sprintf(spdstr, "Normal Speed"); - else sprintf(spdstr, " Speed: %3d%%", speed); - sprintf(pstr, "Pause: %5d ms", pause_ms); - if (draw) printf("%s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr); - - { - while (pilot) // Play PILOT TONE - { - PlayWave(sb_pilot); - ToggleAmp(); - pilot--; - } - if (sb_sync1) // Play first SYNC pulse - { - PlayWave(sb_sync1); - ToggleAmp(); - } - if (sb_sync2) // Play second SYNC pulse - { - PlayWave(sb_sync2); - ToggleAmp(); - } - datapos = 0; - while (datalen) // Play actual DATA - { - if (datalen != 1) bitcount = 8; - else bitcount = lastbyte; - databyte = data[datapos]; - while (bitcount) - { - if (databyte & 0x80) sb_bit = sb_bit1; - else sb_bit = sb_bit0; - PlayWave(sb_bit); // Play first pulse of the bit - ToggleAmp(); - if (!singlepulse) - { - PlayWave(sb_bit); // Play second pulse of the bit - ToggleAmp(); - } - databyte <<= 1; - bitcount--; - } - datalen--; datapos++; - } - singlepulse = 0; // Reset flag for next TZX blocks - - // If there is pause after block present then make first millisecond the oposite - // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause - if (pause_ms) - { - PauseWave(1); - amp = LOAMP; - if (pause_ms > 1) PauseWave(pause_ms - 1); - } - } - } - - if (id == 0x16) // C64 ROM data block ... - { - IdentifyC64ROM(datalen, data, 0); - sprintf(pstr, "Pause: %5d ms", pause_ms); - if (draw) printf(" %s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr); - - { - sb_pilot = Samples(sb_pilot); - sb_sync1 = Samples(sb_sync1); sb_sync2 = Samples(sb_sync2); - sb_bit1_f = Samples(sb_bit1_f); sb_bit1_s = Samples(sb_bit1_s); - sb_bit0_f = Samples(sb_bit0_f); sb_bit0_s = Samples(sb_bit0_s); - sb_finishbyte_f = Samples(sb_finishbyte_f); - sb_finishbyte_s = Samples(sb_finishbyte_s); - sb_finishdata_f = Samples(sb_finishdata_f); - sb_finishdata_s = Samples(sb_finishdata_s); - sb_trailing = Samples(sb_trailing); - num_lead_in = 0; - amp = LOAMP; // This might be just opposite !!!! - while (pilot) // Play PILOT TONE - { - PlayC64(sb_pilot); - pilot--; - } - if (sb_sync1) PlayC64(sb_sync1); // Play SYNC PULSES - if (sb_sync2) PlayC64(sb_sync2); - datapos = 0; - while (datalen) // Play actual DATA - { - if (datalen != 1) - { - bitcount = 8; - PlayC64ROMByte(data[datapos], 0); - } - else - { - bitcount = lastbyte; - PlayC64ROMByte(data[datapos], 1); - } - databyte = data[datapos]; - datalen--; datapos++; - } - while (trailing) // Play TRAILING TONE - { - PlayC64(sb_trailing); - trailing--; - } - - // If there is pause after block present then make first millisecond the oposite - // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause - - if (pause_ms) - { - PauseWave(pause_ms / 2); - ToggleAmp(); - PauseWave((pause_ms / 2) + (pause_ms % 2)); - ToggleAmp(); - } - } - } - - if (id == 0x17) // C64 Turbo Tape data block ... - { - IdentifyC64Turbo(datalen, data, 0); - sprintf(pstr, "Pause: %5d ms", pause_ms); - if (draw) printf(" %s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr); - - { - sb_bit1 = Samples(sb_bit1); - sb_bit0 = Samples(sb_bit0); - amp = LOAMP; // This might be just opposite !!!! - while (num_lead_in) // Play Lead In bytes - { - bitcount = 8; - PlayC64TurboByte(lead_in_byte); - num_lead_in--; - } - datapos = 0; - while (datalen) // Play actual DATA - { - if (datalen != 1) bitcount = 8; - else bitcount = lastbyte; - PlayC64TurboByte(data[datapos]); - databyte = data[datapos]; - datalen--; datapos++; - } - while (trailing) // Play Trailing bytes - { - bitcount = 8; - PlayC64TurboByte((unsigned char)sb_trailing); - trailing--; - } - - // If there is pause after block present then make first millisecond the oposite - // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause - - if (pause_ms) - { - PauseWave(pause_ms / 2); - ToggleAmp(); - PauseWave((pause_ms / 2) + (pause_ms % 2)); - ToggleAmp(); - } - } - } - - curr++; // We continue to replay the next TZX block - } // This is the main loop end - - PauseWave(200); // Finish always with 200 ms of pause after the last block - PlayFinish(); - printf("\n%lu bytes sent to the core.\n", oflen); - free(mem); - return 1; -} +///////////////////////////////////////////////////////////////////// +// TZX to VAV Converter v0.2 for Bloodshed Dev-C++ compiler // +// (C) 2005-2006 Francisco Javier Crespo // +// // +// MiSTer adaptation (CSW v1 only) // +// (C) 2017 Francisco Javier Crespo // +// // +// Originally based on source code from these works: // +// PLAYTZX v0.60b for Watcom C compiler (C) 1997-2004 Tomaz Kac // +// PLAYTZX Unix v0.12b (C) 2003 Tero Turtiainen / Fredrick Meunier // +///////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include "tzx2wav.h" +#include "spi.h" + +// Computer entries +const char *hwids_01[] = { + "ZX Spectrum 16k", + "ZX Spectrum 48k, Plus", + "ZX Spectrum 48k Issue 1", + "ZX Spectrum 128k (Sinclair)", + "ZX Spectrum 128k +2 (Grey case)", + "ZX Spectrum 128k +2A, +3", + "Timex Sinclair TC-2048", + "Timex Sinclair TS-2068", + "Pentagon 128", + "Sam Coupe", + "Didaktik M", + "Didaktik Gama", + "ZX-81 with 1k RAM", + "ZX-81 with 16k RAM or more", + "ZX Spectrum 128k, Spanish version", + "ZX Spectrum, Arabic version", + "TK 90-X", + "TK 95", + "Byte", + "Elwro", + "ZS Scorpion", + "Amstrad CPC 464", + "Amstrad CPC 664", + "Amstrad CPC 6128", + "Amstrad CPC 464+", + "Amstrad CPC 6128+", + "Jupiter ACE" + "Enterprise", + "Commodore 64", + "Commodore 128" +}; + +#define MAJREV 1 // Major revision of the format this program supports +#define MINREV 13 // Minor revision of the format this program supports + +// C64 Loader defines ... + +#define ROM_S_HALF 616 // ROM Loader SHORT Half Wave +#define ROM_M_HALF 896 // ROM Loader MEDIUM Half Wave +#define ROM_L_HALF 1176 // ROM Loader LONG Half Wave + +#define STT_0_HALF 426 // Standard Turbo Tape BIT 0 Half Wave +#define STT_1_HALF 596 // Standard Turbo Tape BIT 1 Half Wave + +// Other defines ... + +#define LOAMP 0x26 // Low Level Amplitude (-3 dB) +#define HIAMP 0xDA // High Level Amplitude (-3 dB) +static unsigned int freq = 44100; // Default Sample Frequency + +static unsigned char *mem = 0; // File in Memory +static int pos; // Position in File +static int curr; // Current block that is playing +static int numblocks; // Total Num. of blocks +static unsigned long oflen; // Length of output file +static int block[2048]; // Array of Block starts +static double cycle; // Frequency / 3500000 (Z80 clock) + +static int cpc=0; // Amstrad CPC tape ? +static int sam=0; // SAM Coupe tape ? + +static int id; // Current Block ID +static int pilot; // Len of Pilot signal (in hp's) +static int sb_pilot; // Pilot pulse +static int sb_sync1; // Sync first half-period (hp) +static int sb_sync2; // Sync second +static int sb_bit0; // Bit-0 +static int sb_bit1; // Bit-1 +static int sb_pulse; // Pulse in Sequence of pulses and direct recording block +static int lastbyte; // How many bits are in last byte of data ? +static int pause_ms; // Pause after current block (in milliseconds) +static int skippause=0; // Overrides pause value in last TZX block + +static int singlepulse; // Flag to activate single pulse waves +static int manchester; // Flag to activate manchester encoded waves + +static unsigned char *data; // Data to be played +static int datalen; // Len of ^^^ +static int datapos; // Position in ^^^ +static int bitcount; // How many bits to play in current byte ? +static int sb_bit; // should we play bit 0 or 1 ? +static char databyte; // Current Byte to be replayed of the data +static signed short jump; // Relative Jump +static int not_rec; // Some blocks were not recognised ?? +static int starting=1; // starting block +static int ending=0; // ending block + +static int expand=0; // Expand Groups ? +static int draw=1; // Local flag for outputing a line when in a group + +static int speed; + +static int loop_start=0; // Position of the last Loop Start block +static int loop_count=0; // Counter of the Loop +static int call_pos=0; // Position of the last Call Sequence block +static int call_num=0; // Number of Calls in the last Call Sequence block +static int call_cur=0; // Current Call to be made +static int num_sel; // Number of Selections in the Select block +static int jumparray[256]; // Array of all possible jumps in Select block + +static int sb_bit0_f, sb_bit0_s, sb_bit1_f, sb_bit1_s, xortype, sb_finishbyte_f, + sb_finishbyte_s, sb_finishdata_f, sb_finishdata_s, num_lead_in, xorvalue; +static int trailing, sb_trailing; +static char lead_in_byte; +static int endian; +static char add_bit; + +static int inv = 0; + +static char tstr[255]; +static char spdstr[255]; +static char pstr[255]; + +static void core_write(const void *buf, int size) +{ + const char *addr = (const char*)buf; + while (size--) + { + spi8(*addr++); + oflen++; + } +} + +/////////////////////////////// +// CSW v1.01 handling routines +/////////////////////////////// + +static int amp, cswamp; +static unsigned int cswlen; + +void CSW1_Init(void) +{ + // Official CSW format documentation at: + // http://www.ramsoft.bbk.org/csw.html + + unsigned short Revision = 0x0101; + unsigned char CompType = 1; + unsigned int Reserved = 0; + + core_write("Compressed Square Wave\032", 23); + core_write(&Revision, 2); // Major & Minor revision + core_write(&freq, 2); // Sample Rate + core_write(&CompType, 1); // Compression Type + core_write(&inv, 1); // Polarity + core_write(&Reserved, 3); // Reserved bytes + + cswamp = LOAMP; + cswlen = 0; +} + +void CSW1_Write(unsigned int samples) +{ + if (cswamp == amp) + { + cswlen += samples; + return; + } + if (cswlen < 256) + { + core_write(&cswlen, 1); + } + else + { + int zero = 0; + core_write(&zero, 1); + core_write(&cswlen, 4); + } + + cswamp = amp; + cswlen = samples; +} + +////////////////////////////////// +// Generic wave handling routines +////////////////////////////////// + +unsigned int Samples(unsigned int n) +{ + // Convert a sampling value in Z80 T-States to samples for wave output + return ((unsigned int)(0.5 + (cycle*(double)n))); +} + +void ToggleAmp(void) +{ + // Toggles the sign of the wave + // WHOLE CONCEPT TO BE RECODED IN ToggleSgn(); + + if (amp == LOAMP) amp = HIAMP; + else amp = LOAMP; +} + +void PlayWave(unsigned int len) +{ + CSW1_Write(len); +} + +void PauseWave(unsigned int pause_ms) +{ + // Waits for "pause" milliseconds + + int p; + if (((!skippause) || (curr != (numblocks - 1)))) + { + if (!curr && pause_ms > 2000) pause_ms = 2000; + p = (unsigned int)((((float)pause_ms)*freq) / 1000.0); + PlayWave(p); + } +} + +void PlayFinish() +{ + if (cswlen) + { + ToggleAmp(); + CSW1_Write(0); + } +} + +///////////////////////////// +// TZX Commodore 64 routines +///////////////////////////// + +void PlayC64(unsigned int len) +{ + PlayWave(len); + ToggleAmp(); + PlayWave(len); + ToggleAmp(); +} + +void PlayC64ROMByte(char byte, int finish) +{ + xorvalue = xortype; + while (bitcount) + { + if (!endian) sb_bit = byte & 0x01; + else sb_bit = byte & 0x80; + if (sb_bit) + { + if (sb_bit1_f) PlayC64(sb_bit1_f); + if (sb_bit1_s) PlayC64(sb_bit1_s); + xorvalue ^= sb_bit; + } + else + { + if (sb_bit0_f) PlayC64(sb_bit0_f); + if (sb_bit0_s) PlayC64(sb_bit0_s); + xorvalue ^= sb_bit; + } + if (!endian) byte >>= 1; + else byte <<= 1; + bitcount--; + } + if (xortype != 0xFF) + { + if (xorvalue) + { + if (sb_bit1_f) PlayC64(sb_bit1_f); + if (sb_bit1_s) PlayC64(sb_bit1_s); + } + else + { + if (sb_bit0_f) PlayC64(sb_bit0_f); + if (sb_bit0_s) PlayC64(sb_bit0_s); + } + } + if (!finish) + { + if (sb_finishbyte_f) PlayC64(sb_finishbyte_f); + if (sb_finishbyte_s) PlayC64(sb_finishbyte_s); + } + else + { + if (sb_finishdata_f) PlayC64(sb_finishdata_f); + if (sb_finishdata_s) PlayC64(sb_finishdata_s); + } +} + +void PlayC64TurboByte(char byte) +{ + int add_num; + + add_num = add_bit & 3; + + if (add_num && !(add_bit & 4)) + { + while (add_num) + { + if (add_bit & 8) PlayC64(sb_bit1); + else PlayC64(sb_bit0); + add_num--; + } + } + + while (bitcount) + { + if (!endian) sb_bit = byte & 0x01; + else sb_bit = byte & 0x80; + if (sb_bit) PlayC64(sb_bit1); + else PlayC64(sb_bit0); + if (!endian) byte >>= 1; + else byte <<= 1; + bitcount--; + } + + if (add_num && (add_bit & 4)) + { + while (add_num) + { + if (add_bit & 8) PlayC64(sb_bit1); + else PlayC64(sb_bit0); + add_num--; + } + } +} + +//////////////////////////////// +// Game identification routines +//////////////////////////////// + +void GetC64ROMName(char *name, unsigned char *data) +{ + char d; + int n = 0; + for (; n < 16; n++) + { + d = data[14 + n]; + if (d < 32 || d>125) + name[n] = ' '; + else + name[n] = d; + } + name[n] = 0; +} + +void GetC64StandardTurboTapeName(char *name, unsigned char *data) +{ + char d; + int n = 0; + for (; n < 16; n++) + { + d = data[15 + n]; + if (d < 32 || d>125) + name[n] = ' '; + else + name[n] = d; + } + name[n] = 0; +} + +void IdentifyC64ROM(int pos, unsigned char *data, int type) +{ + char name[255]; + + // Determine Loader type + if ((sb_pilot == ROM_S_HALF) && (sb_sync1 == ROM_L_HALF) && (sb_sync2 == ROM_M_HALF) && + (sb_bit0_f == ROM_S_HALF) && (sb_bit0_s == ROM_M_HALF) && (sb_bit1_f == ROM_M_HALF) && + (sb_bit1_s == ROM_S_HALF) && (xortype == 0x01)) + { + // ROM Loader + if ((data[0] == 0x89) && (data[1] == 0x88) && (data[2] == 0x87) && (data[3] == 0x86) && + (data[4] == 0x85) && (data[5] == 0x84) && (data[6] == 0x83) && (data[7] == 0x82) && + (data[8] == 0x81)) + { + if (pos == 202) + { + if (!type) + { + strcpy(name, "Header: "); + GetC64ROMName(name + 8, data); + } + else + { + strcpy(name, "ROM Header: "); + GetC64ROMName(name + 12, data); + } + } + else + { + if (!type) + { + strcpy(name, "Data Block "); + } + else + { + strcpy(name, "ROM: Data Block"); + } + } + } + else + { + if (!type) strcpy(name, "------------------------"); + else strcpy(name, "ROM: Last Block Repeated"); + } + strcpy(tstr, name); + strcpy(spdstr, "C64 ROM Data "); + return; + } + + if (!type) strcpy(tstr, "------------------------"); + else strcpy(tstr, "Unknown"); + strcpy(spdstr, "C64 Data "); +} + +void IdentifyC64Turbo(int pos, unsigned char *data, int type) +{ + char name[255]; + + // Determine Loader type + if (sb_bit0 == STT_0_HALF && sb_bit1 == STT_1_HALF && lead_in_byte == 0x02) + { + // Standard Turbo Tape Loader + if (data[0] == 0x09 && data[1] == 0x08 && data[2] == 0x07 && data[3] == 0x06 && + data[4] == 0x05 && data[5] == 0x04 && data[6] == 0x03 && data[7] == 0x02 && + data[8] == 0x01) + { + if (pos == 32 && data[9] != 0x00) + { + if (!type) + { + strcpy(name, "Header: "); + GetC64StandardTurboTapeName(name + 8, data); + } + else + { + strcpy(name, "TurboTape Header: "); + GetC64StandardTurboTapeName(name + 18, data); + } + } + else + { + if (!type) strcpy(name, "------------------------"); + else strcpy(name, "TurboTape Data Block"); + } + } + else + { + if (!type) strcpy(name, "------------------------"); + else strcpy(name, "TurboTape Unknown"); + } + strcpy(tstr, name); + strcpy(spdstr, "C64 Turbo "); + return; + } + if (!type) strcpy(tstr, "------------------------"); + else strcpy(tstr, "Unknown"); + strcpy(spdstr, "C64 Data "); +} + +void Identify(int len, unsigned char *temp, int type) +{ + int n; + int s; + + if (cpc) + { + if (temp[0] == 44) + { + if (!type) s = 4; + else s = 0; + strcpy(tstr, " "); + for (n = 0; n < 16; n++) + { + if (temp[n + 1]) tstr[n + s] = temp[n + 1]; + else tstr[n + s] = ' '; + } + for (n = 0; n < 4; n++) tstr[n + s + 16] = ' '; + tstr[n + s + 16] = 0; + } + else + { + if (!type) + strcpy(tstr, " ------------------ "); + else + strcpy(tstr, "Headerless"); + } + return; + } + + if (sam) + { + if (temp[0] == 1 && (len>80 && len < 84) && (temp[1] >= 0x10 && temp[1] <= 0x13)) + { + if (!type) + { + s = 14; + switch (temp[1]) + { + case 0x10: strcpy(tstr, " Program : "); break; + case 0x11: strcpy(tstr, " Num. Array : "); break; + case 0x12: strcpy(tstr, "Char. Array : "); break; + case 0x13: strcpy(tstr, " Bytes : "); break; + } + } + else + { + switch (temp[1]) + { + case 0x10: strcpy(tstr, "Program : "); s = 10; break; + case 0x11: strcpy(tstr, "Num. Array : "); s = 13; break; + case 0x12: strcpy(tstr, "Char. Array : "); s = 14; break; + case 0x13: strcpy(tstr, "Bytes : "); s = 8; break; + } + } + for (n = 0; n < 10; n++) + { + if (temp[n + 2]>31 && temp[n + 2] < 127) + tstr[n + s] = temp[n + 2]; + else + tstr[n + s] = 32; + } + tstr[n + s] = 0; + } + else + { + if (!type) + strcpy(tstr, " --------------------"); // Not Header + else + strcpy(tstr, "Headerless"); + } + return; + } + + if (temp[0] == 0 && (len == 19 || len == 20) && temp[1] < 4) + { + if (!type) + { + s = 14; + switch (temp[1]) + { + case 0x00: strcpy(tstr, " Program : "); break; + case 0x01: strcpy(tstr, " Num. Array : "); break; + case 0x02: strcpy(tstr, "Char. Array : "); break; + case 0x03: strcpy(tstr, " Bytes : "); break; + } + } + else + { + switch (temp[1]) + { + case 0x00: strcpy(tstr, "Program : "); s = 10; break; + case 0x01: strcpy(tstr, "Num. Array : "); s = 13; break; + case 0x02: strcpy(tstr, "Char. Array : "); s = 14; break; + case 0x03: strcpy(tstr, "Bytes : "); s = 8; break; + } + } + for (n = 0; n < 10; n++) + { + if (temp[n + 2]>31 && temp[n + 2] < 127) + tstr[n + s] = temp[n + 2]; + else + tstr[n + s] = 32; + } + tstr[n + s] = 0; + } + else + { + if (!type) + strcpy(tstr, " --------------------"); // Not Header + else + strcpy(tstr, "Headerless"); + } +} + +////////////////////////////////////////////////////////// +// Conversion routines to fetch bytes in Big Endian order +////////////////////////////////////////////////////////// + +unsigned int Get2(unsigned char *pointer) +{ + return (pointer[0] | (pointer[1] << 8)); +} + +unsigned int Get3(unsigned char *pointer) +{ + return (pointer[0] | (pointer[1] << 8) | (pointer[2] << 16)); +} + +unsigned int Get4(unsigned char *pointer) +{ + return (pointer[0] | (pointer[1] << 8) | (pointer[2] << 16) | (pointer[3] << 24)); +} + +///////////////////////// +// Miscelaneous routines +///////////////////////// + +void CopyString(char *destination, unsigned char *source, unsigned int len) +{ + // Could just use strcpy ... + + unsigned int n; + for (n = 0; n < len; n++) + destination[n] = source[n]; + destination[n] = 0; +} + +void MakeFixedString(char *s, int i) +{ + // This will create a fixed length string from null-terminated one... + + int n = 0; + int k = 0; + + while (i) + { + if (!s[n]) k = 1; + if (k) s[n] = ' '; + n++; + i--; + } + s[n] = 0; +} + +/////////////////////////////// +// TZX Blocks Parsing routines +/////////////////////////////// + +void Analyse_ID10(void) // Standard Loading Data block +{ + pause_ms = Get2(&data[0]); + datalen = Get2(&data[2]); + data += 4; + if (data[0] == 0x00) pilot = 8064; + else pilot = 3220; + sb_pilot = Samples(2168); + sb_sync1 = Samples(667); + sb_sync2 = Samples(735); + sb_bit0 = Samples(885); + sb_bit1 = Samples(1710); + lastbyte = 8; +} + +void Analyse_ID11(void) // Custom Loading Data block +{ + sb_pilot = Samples(Get2(&data[0])); + sb_sync1 = Samples(Get2(&data[2])); + sb_sync2 = Samples(Get2(&data[4])); + sb_bit0 = Samples(Get2(&data[6])); + sb_bit1 = Samples(Get2(&data[8])); + speed = (int)((1710.0 / (double)Get2(&data[8]))*100.0); + pilot = Get2(&data[10]); + lastbyte = (int)data[12]; + pause_ms = Get2(&data[13]); + datalen = Get3(&data[15]); + data += 18; +} + +void Analyse_ID12(void) // Pure Tone +{ + sb_pilot = Samples(Get2(&data[0])); + pilot = Get2(&data[2]); + if (draw) printf(" Pure Tone Length: %5d\n", pilot); + while (pilot) + { + PlayWave(sb_pilot); + ToggleAmp(); + pilot--; + } +} + +void Analyse_ID13(void) // Sequence of Pulses +{ + pilot = (int)data[0]; data++; + if (draw) printf(" Sequence of Pulses Length: %5d\n", pilot); + while (pilot) + { + sb_pulse = Samples(Get2(&data[0])); + PlayWave(sb_pulse); + ToggleAmp(); + pilot--; + data += 2; + } +} + +void Analyse_ID14(void) // Pure Data +{ + sb_pilot = pilot = sb_sync1 = sb_sync2 = 0; + sb_bit0 = Samples(Get2(&data[0])); + sb_bit1 = Samples(Get2(&data[2])); + speed = (int)((1710.0 / (double)Get2(&data[2]))*100.0); + lastbyte = (int)data[4]; + pause_ms = Get2(&data[5]); + datalen = Get3(&data[7]); + data += 10; +} + +void Analyse_ID15(void) // Direct Recording +{ + // For now the BEST way is to use the sample frequency for replay that is + // exactly the SAME as the Original Freq. used when sampling this block ! + // i.e. NO downsampling is handled YET ... use TAPER when you need it ! ;-) + + sb_pulse = Samples(Get2(&data[0])); + if (!sb_pulse) sb_pulse = 1; // In case sample frequency > 44100 + pause_ms = Get2(&data[2]); // (Should work for frequencies upto 48000) + lastbyte = (int)data[4]; + datalen = Get3(&data[5]); + if (draw) printf(" Direct Recording Length:%6d Original Freq.: %5d Hz\n", + datalen, (int)(0.5 + (3500000.0 / (double)Get2(&data[0])))); + data = &data[8]; + datapos = 0; + // Replay Direct Recording block ... + while (datalen) + { + if (datalen != 1) bitcount = 8; + else bitcount = lastbyte; + databyte = data[datapos]; + while (bitcount) + { + if (databyte & 0x80) amp = HIAMP; + else amp = LOAMP; + PlayWave(sb_pulse); + databyte <<= 1; + bitcount--; + } + datalen--; + datapos++; + } + ToggleAmp(); + if (pause_ms) PauseWave(pause_ms); +} + +void Analyse_ID16(void) // C64 ROM Type Data Block +{ + data += 4; + sb_pilot = Get2(&data[0]); + pilot = Get2(&data[2]); + sb_sync1 = Get2(&data[4]); + sb_sync2 = Get2(&data[6]); + sb_bit0_f = Get2(&data[8]); + sb_bit0_s = Get2(&data[10]); + sb_bit1_f = Get2(&data[12]); + sb_bit1_s = Get2(&data[14]); + xortype = (int)(data[16]); + sb_finishbyte_f = Get2(&data[17]); + sb_finishbyte_s = Get2(&data[19]); + sb_finishdata_f = Get2(&data[21]); + sb_finishdata_s = Get2(&data[23]); + sb_trailing = Get2(&data[25]); + trailing = Get2(&data[27]); + lastbyte = (int)(data[29]); + endian = data[30]; + pause_ms = Get2(&data[31]); + datalen = Get3(&data[33]); + data += 36; + IdentifyC64ROM(datalen, data, 1); +} + +void Analyse_ID17(void) // C64 Turbo Tape Data Block +{ + data += 4; + sb_bit0 = Get2(&data[0]); + sb_bit1 = Get2(&data[2]); + add_bit = data[4]; + num_lead_in = Get2(&data[5]); + lead_in_byte = data[7]; + lastbyte = (int)data[8]; + endian = data[9]; + trailing = Get2(&data[10]); + sb_trailing = data[12]; + pause_ms = Get2(&data[13]); + datalen = Get3(&data[15]); + data += 18; + IdentifyC64Turbo(datalen, data, 1); +} + +void Analyse_ID20(void) // Pause or Stop the Tape command +{ + pause_ms = Get2(&data[0]); + amp = LOAMP; + if (pause_ms) + { + if (draw) printf(" Pause Length: %2.3fs\n", ((float)pause_ms) / 1000.0); + PauseWave(pause_ms); + amp = LOAMP; + } + else + { + if (draw) printf(" Stop the tape command!\n"); + PauseWave(2000); // 2 seconds of pause in "Stop Tape" wave output + amp = LOAMP; + } +} + +void Analyse_ID21(void) // Group Start +{ + CopyString(pstr, &data[1], data[0]); + if (draw) printf(" Group: %s\n", pstr); + if (!expand) draw = 0; +} + +void Analyse_ID22(void) // Group End +{ + if (draw) printf(" Group End\n"); + draw = 1; +} + +void Analyse_ID23(void) // Jump To Relative +{ + jump = (signed short)(data[0] + data[1] * 256); + if (draw) printf(" Jump Relative: %d (To Block %d)\n", jump, curr + jump + 1); + curr += jump; + curr--; +} + +void Analyse_ID24(void) // Loop Start +{ + loop_start = curr; + loop_count = Get2(&data[0]); + if (draw) printf(" Loop Start, Counter: %d\n", loop_count); +} + +void Analyse_ID25(void) // Loop End +{ + loop_count--; + if (loop_count > 0) + { + if (draw) printf(" Loop End, Still To Go %d Time(s)!\n", loop_count); + curr = loop_start; + } + else + { + if (draw) printf(" Loop End, Finished\n"); + } +} + +void Analyse_ID26(void) // Call Sequence +{ + call_pos = curr; + call_num = Get2(&data[0]); + call_cur = 0; + jump = (signed short)(data[2] + data[3] * 256); + if (draw) printf(" Call Sequence, Number of Calls : %d, First: %d (To Block %d)\n", call_num, jump, curr + jump + 1); + curr += jump; + curr--; +} + +void Analyse_ID27(void) // Return from Sequence +{ + call_cur++; + if (call_cur == call_num) + { + if (draw) printf(" Return from Call, Last Call Finished\n"); + curr = call_pos; + } + else + { + curr = call_pos; + data = &mem[block[curr] + 1]; + jump = (signed short)(data[call_cur * 2 + 2] + data[call_cur * 2 + 3] * 256); + if (draw) printf(" Return from Call, Calls Left: %d, Next: %d (To Block %d)\n", + call_num - call_cur, jump, curr + jump + 1); + curr += jump; + curr--; + } +} + +void Analyse_ID28(void) // Select Block +{ + num_sel = data[2]; + printf(" Select :\n"); + data += 3; + for (int n = 0; n < num_sel; n++) + { + jump = (signed short)(data[0] + data[1] * 256); + jumparray[n] = jump; + CopyString(spdstr, &data[3], data[2]); + printf("%5d : %s\n", n + 1, spdstr); + data += 3 + data[2]; + } + + //no interactive shell. choose 1. + PauseWave(200); + int k = 1; + amp = LOAMP; + + /* + printf(">> Press the number!\n"); + PauseWave(5000); // Why?!?!?!?! + k = getchar(); + if (k == 27) Error("ESCAPE key pressed!"); + k -= 48; + if (k<1 || k>num_sel) printf("Illegal Selection... Continuing...\n"); + else + */ + { + curr += jumparray[k - 1]; + curr--; + } +} + +void Analyse_ID2A(void) // Stop the tape if in 48k mode +{ + if (draw) printf(" Stop the tape in 48k mode!\n"); + PauseWave(3000); + amp=LOAMP; +} + +void Analyse_ID30(void) // Description +{ + CopyString(pstr, &data[1], data[0]); + if (draw) printf(" Description: %s\n", pstr); +} + +void Analyse_ID31(void) // Message +{ + CopyString(pstr, &data[2], data[1]); + // Pause in Message block is ignored ... + if (draw) printf(" Message: %s\n", pstr); +} + +void Analyse_ID32(void) // Archive Info +{ + if (draw) + { + if (data[3] == 0) + { + CopyString(spdstr, &data[5], data[4]); + sprintf(tstr, " Title: %s", spdstr); + MakeFixedString(tstr, 69); + strcpy(tstr + 52, " (-v for more)"); + printf("%s\n", tstr); + } + else + { + sprintf(tstr, " Archive Info"); + MakeFixedString(tstr, 69); + strcpy(tstr + 52, " (-v for more)"); + printf("%s\n", tstr); + } + } +} + +void Analyse_ID33(void) // Hardware Info +{ + if (data[1] == 0 && data[2] > 0x14 && data[2] < 0x1a && data[3] == 1) cpc = 1; + if (data[1] == 0 && data[2] == 0x09 && data[3] == 1) sam = 1; + if (draw) + { + if (data[1] != 0 || data[3] != 1) + { + sprintf(tstr, " Hardware Type"); + MakeFixedString(tstr, 69); + strcpy(tstr + 52, " (-v for more)"); + printf("%s\n", tstr); + } + else + { + printf(" This tape is made for %s !\n", hwids_01[data[2]]); + } + } +} + +void Analyse_ID34(void) // Emulation info +{ + if (draw) printf(" Information for emulators.\n"); +} + +void Analyse_ID35(void) // Custom Info +{ + CopyString(pstr, data, 16); + if (draw) + { + if (strcmp(pstr, "POKEs ")) + printf(" Custom Info: %s\n", pstr); + // Only Name of Custom info except POKEs is used ... + else + { + sprintf(tstr, " Custom Info: %s", pstr); + MakeFixedString(tstr, 69); + strcpy(tstr + 52, " (-v for more)"); + printf("%s\n", tstr); + } + } +} + +void Analyse_ID40(void) // Snapshot +{ + if (draw) printf(" Snapshot (Not Supported yet)\n"); +} + +void Analyse_ID5A(void) // ZXTape! +{ + if (draw) printf(" Start of the new tape (Merged Tapes)\n"); +} + +void Analyse_Unknown(void) // Unknown blocks +{ + if (draw) printf(" Unknown block %02X !\n", id); +} + +//////////////////////// +// Main TZX2WAV program +//////////////////////// + +int tzx2csw(fileTYPE *f) +{ + freq = 44100; + starting = 1; + ending = 0; + expand = 0; + skippause = 0; + inv = 0; + oflen = 0; + + mem = (unsigned char *)malloc(f->size); + if (mem == NULL) + { + printf("\n-- Not enough memory to load the file!"); + return 0; + } + + // Start reading file... + FileReadAdv(f, mem, 10); + mem[7] = 0; + + if (strcmp((const char*)mem, "ZXTape!")) + { + printf("\n-- File is not in ZXTape format!"); + free(mem); + return 0; + } + + printf("\nZXTape file revision %d.%02d\n", mem[8], mem[9]); + if (!mem[8]) + { + printf("\n-- Development versions of ZXTape format are not supported!"); + free(mem); + return 0; + } + + if (mem[8] > MAJREV) printf("\n-- Warning: Some blocks may not be recognised and used!\n"); + if (mem[8] == MAJREV && mem[9] > MINREV) printf("\n-- Warning: Some of the data might not be properly recognised!\n"); + FileReadAdv(f, mem, f->size - 10); + numblocks = 0; pos = 0; + not_rec = 0; + + // Go through the file and record block starts ... + // (not necessary, could just go right through it) + + while (pos < f->size - 10) + { + block[numblocks] = pos; + pos++; + switch (mem[pos - 1]) + { + case 0x10: pos += Get2(&mem[pos + 0x02]) + 0x04; break; + case 0x11: pos += Get3(&mem[pos + 0x0F]) + 0x12; break; + case 0x12: pos += 0x04; break; + case 0x13: pos += (mem[pos + 0x00] * 0x02) + 0x01; break; + case 0x14: pos += Get3(&mem[pos + 0x07]) + 0x0A; break; + case 0x15: pos += Get3(&mem[pos + 0x05]) + 0x08; break; + case 0x16: pos += Get4(&mem[pos + 0x00]) + 0x04; break; + case 0x17: pos += Get4(&mem[pos + 0x00]) + 0x04; break; + + case 0x20: pos += 0x02; break; + case 0x21: pos += mem[pos + 0x00] + 0x01; break; + case 0x22: break; + case 0x23: pos += 0x02; break; + case 0x24: pos += 0x02; break; + case 0x25: break; + case 0x26: pos += Get2(&mem[pos + 0x00]) * 0x02 + 0x02; break; + case 0x27: break; + case 0x28: pos += Get2(&mem[pos + 0x00]) + 0x02; break; + + case 0x2A: pos += 0x04; break; + + case 0x30: pos += mem[pos + 0x00] + 0x01; break; + case 0x31: pos += mem[pos + 0x01] + 0x02; break; + case 0x32: pos += Get2(&mem[pos + 0x00]) + 0x02; break; + case 0x33: pos += (mem[pos + 0x00] * 0x03) + 0x01; break; + case 0x34: pos += 0x08; break; + case 0x35: pos += Get4(&mem[pos + 0x10]) + 0x14; break; + + case 0x40: pos += Get3(&mem[pos + 0x01]) + 0x04; break; + + case 0x5A: pos += 0x09; break; + + default: pos += Get4(&mem[pos + 0x00]) + 0x04; + not_rec = 1; + } + numblocks++; + } + + printf("Number of Blocks: %d\n", numblocks); + + if (not_rec) + { + printf("\n-- Warning: Some blocks were *NOT* recognised!\n"); + } + + curr = 0; + + if (starting > 1) + { + if (starting > numblocks) + { + printf("\n-- Invalid Starting Block"); + free(mem); + return 0; + } + curr = starting - 1; + } + + if (ending > 0) + { + if (ending > numblocks || ending < starting) + { + printf("\n-- Invalid Ending Block"); + free(mem); + return 0; + } + numblocks = ending; + } + + printf("\nCreating CSW v1"); + printf(" file using %d Hz frequency ...\n\n", freq); + + CSW1_Init(); + amp = LOAMP; + + singlepulse = 0; + manchester = 0; + cycle = (double)freq / 3500000.0; // This is for the conversion later ... + + ///////////////////////////////////////////////////// + // Start replay of blocks (Main loop of the program) + ///////////////////////////////////////////////////// + + while (curr < numblocks) + { + if (draw) printf("Block %03d:", curr + 1); + + id = mem[block[curr]]; + data = &mem[block[curr] + 1]; + switch (id) + { + case 0x10: Analyse_ID10(); // Standard Loading Data block + break; + case 0x11: Analyse_ID11(); // Custom Loading Data block + break; + case 0x12: Analyse_ID12(); // Pure Tone + break; + case 0x13: Analyse_ID13(); // Sequence of Pulses + break; + case 0x14: Analyse_ID14(); // Pure Data + break; + case 0x15: Analyse_ID15(); // Direct Recording + break; + case 0x16: Analyse_ID16(); // C64 ROM Type Data Block + break; + case 0x17: Analyse_ID17(); // C64 Turbo Tape Data Block + break; + case 0x20: Analyse_ID20(); // Pause or Stop the Tape command + break; + case 0x21: Analyse_ID21(); // Group Start + break; + case 0x22: Analyse_ID22(); // Group End + break; + case 0x23: Analyse_ID23(); // Jump To Relative + break; + case 0x24: Analyse_ID24(); // Loop Start + break; + case 0x25: Analyse_ID25(); // Loop End + break; + case 0x26: Analyse_ID26(); // Call Sequence + break; + case 0x27: Analyse_ID27(); // Return from Sequence + break; + case 0x28: Analyse_ID28(); // Select Block + break; + case 0x2A: Analyse_ID2A(); // Stop the tape if in 48k mode + break; + case 0x30: Analyse_ID30(); // Description + break; + case 0x31: Analyse_ID31(); // Message + break; + case 0x32: Analyse_ID32(); // Archive Info + break; + case 0x33: Analyse_ID33(); // Hardware Info + break; + case 0x34: Analyse_ID34(); // Emulation info + break; + case 0x35: Analyse_ID35(); // Custom Info + break; + case 0x40: Analyse_ID40(); // Snapshot + break; + case 0x5A: Analyse_ID5A(); // ZXTape! + break; + default: Analyse_Unknown(); // Unknown blocks + } + + // TZX file blocks analysis finished + // Now we start generating the sound waves + + if ((id == 0x10 || id == 0x11 || id == 0x14)) // One of the data blocks ... + { + if (id != 0x14) Identify(datalen, data, 0); + else strcpy(tstr, " Pure Data "); + if (id == 0x10) sprintf(spdstr, "Normal Speed"); + else sprintf(spdstr, " Speed: %3d%%", speed); + sprintf(pstr, "Pause: %5d ms", pause_ms); + if (draw) printf("%s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr); + + { + while (pilot) // Play PILOT TONE + { + PlayWave(sb_pilot); + ToggleAmp(); + pilot--; + } + if (sb_sync1) // Play first SYNC pulse + { + PlayWave(sb_sync1); + ToggleAmp(); + } + if (sb_sync2) // Play second SYNC pulse + { + PlayWave(sb_sync2); + ToggleAmp(); + } + datapos = 0; + while (datalen) // Play actual DATA + { + if (datalen != 1) bitcount = 8; + else bitcount = lastbyte; + databyte = data[datapos]; + while (bitcount) + { + if (databyte & 0x80) sb_bit = sb_bit1; + else sb_bit = sb_bit0; + PlayWave(sb_bit); // Play first pulse of the bit + ToggleAmp(); + if (!singlepulse) + { + PlayWave(sb_bit); // Play second pulse of the bit + ToggleAmp(); + } + databyte <<= 1; + bitcount--; + } + datalen--; datapos++; + } + singlepulse = 0; // Reset flag for next TZX blocks + + // If there is pause after block present then make first millisecond the oposite + // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause + if (pause_ms) + { + PauseWave(1); + amp = LOAMP; + if (pause_ms > 1) PauseWave(pause_ms - 1); + } + } + } + + if (id == 0x16) // C64 ROM data block ... + { + IdentifyC64ROM(datalen, data, 0); + sprintf(pstr, "Pause: %5d ms", pause_ms); + if (draw) printf(" %s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr); + + { + sb_pilot = Samples(sb_pilot); + sb_sync1 = Samples(sb_sync1); sb_sync2 = Samples(sb_sync2); + sb_bit1_f = Samples(sb_bit1_f); sb_bit1_s = Samples(sb_bit1_s); + sb_bit0_f = Samples(sb_bit0_f); sb_bit0_s = Samples(sb_bit0_s); + sb_finishbyte_f = Samples(sb_finishbyte_f); + sb_finishbyte_s = Samples(sb_finishbyte_s); + sb_finishdata_f = Samples(sb_finishdata_f); + sb_finishdata_s = Samples(sb_finishdata_s); + sb_trailing = Samples(sb_trailing); + num_lead_in = 0; + amp = LOAMP; // This might be just opposite !!!! + while (pilot) // Play PILOT TONE + { + PlayC64(sb_pilot); + pilot--; + } + if (sb_sync1) PlayC64(sb_sync1); // Play SYNC PULSES + if (sb_sync2) PlayC64(sb_sync2); + datapos = 0; + while (datalen) // Play actual DATA + { + if (datalen != 1) + { + bitcount = 8; + PlayC64ROMByte(data[datapos], 0); + } + else + { + bitcount = lastbyte; + PlayC64ROMByte(data[datapos], 1); + } + databyte = data[datapos]; + datalen--; datapos++; + } + while (trailing) // Play TRAILING TONE + { + PlayC64(sb_trailing); + trailing--; + } + + // If there is pause after block present then make first millisecond the oposite + // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause + + if (pause_ms) + { + PauseWave(pause_ms / 2); + ToggleAmp(); + PauseWave((pause_ms / 2) + (pause_ms % 2)); + ToggleAmp(); + } + } + } + + if (id == 0x17) // C64 Turbo Tape data block ... + { + IdentifyC64Turbo(datalen, data, 0); + sprintf(pstr, "Pause: %5d ms", pause_ms); + if (draw) printf(" %s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr); + + { + sb_bit1 = Samples(sb_bit1); + sb_bit0 = Samples(sb_bit0); + amp = LOAMP; // This might be just opposite !!!! + while (num_lead_in) // Play Lead In bytes + { + bitcount = 8; + PlayC64TurboByte(lead_in_byte); + num_lead_in--; + } + datapos = 0; + while (datalen) // Play actual DATA + { + if (datalen != 1) bitcount = 8; + else bitcount = lastbyte; + PlayC64TurboByte(data[datapos]); + databyte = data[datapos]; + datalen--; datapos++; + } + while (trailing) // Play Trailing bytes + { + bitcount = 8; + PlayC64TurboByte((unsigned char)sb_trailing); + trailing--; + } + + // If there is pause after block present then make first millisecond the oposite + // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause + + if (pause_ms) + { + PauseWave(pause_ms / 2); + ToggleAmp(); + PauseWave((pause_ms / 2) + (pause_ms % 2)); + ToggleAmp(); + } + } + } + + curr++; // We continue to replay the next TZX block + } // This is the main loop end + + PauseWave(200); // Finish always with 200 ms of pause after the last block + PlayFinish(); + printf("\n%lu bytes sent to the core.\n", oflen); + free(mem); + return 1; +} diff --git a/tzx2wav.h b/tzx2wav.h index 64e6a9e..eed543f 100644 --- a/tzx2wav.h +++ b/tzx2wav.h @@ -1,3 +1,3 @@ -#include "file_io.h" - -int tzx2csw(fileTYPE *f); +#include "file_io.h" + +int tzx2csw(fileTYPE *f); diff --git a/user_io.cpp b/user_io.cpp index 09934b2..cb6de1d 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -702,8 +702,8 @@ void user_io_digital_joystick(unsigned char joystick, uint32_t map, int newdir) return; } - spi_uio_cmd_cont((joy < 2) ? (UIO_JOYSTICK0 + joy) : (UIO_JOYSTICK2 + joy - 2)); - spi_w(map); + spi_uio_cmd_cont((joy < 2) ? (UIO_JOYSTICK0 + joy) : (UIO_JOYSTICK2 + joy - 2)); + spi_w(map); if(joy_bcount>12) spi_w(map>>16); DisableIO(); diff --git a/user_io.h b/user_io.h index b6b8505..4264fb4 100644 --- a/user_io.h +++ b/user_io.h @@ -161,10 +161,10 @@ #define UIO_PRIORITY_KEYBOARD 0 #define UIO_PRIORITY_GAMEPAD 1 -#define EMU_NONE 0 -#define EMU_MOUSE 1 -#define EMU_JOY0 2 -#define EMU_JOY1 3 +#define EMU_NONE 0 +#define EMU_MOUSE 1 +#define EMU_JOY0 2 +#define EMU_JOY1 3 // serial status data type returned from the core typedef struct { @@ -178,7 +178,7 @@ typedef struct { void user_io_init(const char *path); unsigned char user_io_core_type(); char is_minimig(); -char is_archie(); +char is_archie(); char is_sharpmz(); void user_io_poll(); char user_io_menu_button(); @@ -195,7 +195,7 @@ const char *user_io_get_core_name_ex(); char is_menu_core(); char is_x86_core(); char is_snes_core(); -char has_menu(); +char has_menu(); const char *get_image_name(int i); @@ -212,42 +212,42 @@ void user_io_eth_receive_tx_frame(uint8_t *, uint16_t); void user_io_mouse(unsigned char b, int16_t x, int16_t y); void user_io_kbd(uint16_t key, int press); char* user_io_create_config_name(); -int user_io_get_joy_transl(); +int user_io_get_joy_transl(); void user_io_digital_joystick(unsigned char, uint32_t, int); void user_io_analog_joystick(unsigned char, char, char); -void user_io_set_joyswap(int swap); -int user_io_get_joyswap(); +void user_io_set_joyswap(int swap); +int user_io_get_joyswap(); char user_io_osd_is_visible(); void user_io_send_buttons(char); -void parse_video_mode(); +void parse_video_mode(); void user_io_set_index(unsigned char index); unsigned char user_io_ext_idx(char *, char*); -void user_io_check_reset(unsigned short modifiers, char useKeys); - -void user_io_rtc_reset(); - -int hasAPI1_5(); - -const char* get_rbf_dir(); -const char* get_rbf_name(); - +void user_io_check_reset(unsigned short modifiers, char useKeys); + +void user_io_rtc_reset(); + +int hasAPI1_5(); + +const char* get_rbf_dir(); +const char* get_rbf_name(); + int user_io_get_scaler_flt(); char* user_io_get_scaler_coeff(); void user_io_set_scaler_flt(int n); void user_io_set_scaler_coeff(char *name); - + void user_io_minimig_set_adjust(char n); char user_io_minimig_get_adjust(); - -#define HomeDir (is_minimig() ? "Amiga" : is_archie() ? "Archie" : user_io_get_core_name()) - + +#define HomeDir (is_minimig() ? "Amiga" : is_archie() ? "Archie" : user_io_get_core_name()) + int GetUARTMode(); int GetMidiLinkMode(); void SetMidiLinkMode(int mode); - + void set_volume(int cmd); int get_volume(); - + #endif // USER_IO_H