diff --git a/.gitignore b/.gitignore index 46f42f8..8cacd03 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,36 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake _deps +.config +*.o +*.pyc +*.a +*.d + +# gtags +GTAGS +GRTAGS +GPATH + +# emacs +.dir-locals.el + +# emacs temp file suffixes +*~ +.#* +\#*# + +sdkconfig +sdkconfig.old +sdkconfig.lobo +.cproject +.project +.settings +BUILD +build/ +temp/ +local/ +*.dis +*.elf +*.map +**/.DS_Store diff --git a/README.md b/README.md index 28763e6..522af21 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,177 @@ -# lilybook -Little ePaper reader for ESP32 + +### ePaper library for ESP32 + +--- + + +#### Features + +* Support for **GDEH029A1** / **SSD1608** based ePaper modules in 4-wire SPI mode. Support for other controllers will be added later +* **emulated** 4-bit gray scale mode +* **SPI displays oriented SPI driver library** based on *spi-master* driver +* Combined **DMA SPI** transfer mode and **direct SPI** for maximal speed +* **4-bit Grayscale mode** or **1-bit b/w mode** can be selected during runtime +* SPI speeds up to **20 MHz** are tested and works without problems +* **Demo application** included which demonstrates most of the library features + + +* **Graphics drawing functions**: + * **EPD_drawPixel** Draw pixel at given x,y coordinates + * **EPD_drawLine** Draw line between two points + * **EPD_drawFastVLine**, **EPD_drawFastHLine** Draw vertical or horizontal line of given lenght + * **EPD_drawLineByAngle** Draw line on screen from (x,y) point at given angle + * **EPD_drawRect**, **EPD_fillRect** Draw rectangle on screen or fill given rectangular screen region with color + * **EPD_drawRoundRect**, **EPD_fillRoundRect** Draw rectangle on screen or fill given rectangular screen region with color with rounded corners + * **EPD_drawCircle**, **EPD_fillCircle** Draw or fill circle on screen + * **EPD_drawEllipse**, **EPD_fillEllipse** Draw or fill ellipse on screen + * **EPD_drawTriangel**, **EPD_fillTriangle** Draw or fill triangle on screen + * **EPD_drawArc** Draw circle arc on screen, from ~ to given angles, with given thickness. Can be outlined with different color + * **EPD_drawPolygon** Draw poligon on screen with given number of sides (3~60). Can be outlined with different color and rotated by given angle. +* **Fonts**: + * **fixed** width and proportional fonts are supported; 8 fonts embeded + * unlimited number of **fonts from file** + * **7-segment vector font** with variable width/height is included (only numbers and few characters) + * Proportional fonts can be used in fixed width mode. + * Related functions: + * **EPD_setFont** Set current font from one of embeded fonts or font file + * **EPD_getfontsize** Returns current font height & width in pixels. + * **EPD_getfontheight** Returns current font height in pixels. + * **set_7seg_font_atrib** Set atributes for 7 segment vector font + * **getFontCharacters** Get all font's characters to buffer +* **String write function**: + * **EPD_print** Write text to display. + * Strings can be printed at **any angle**. Rotation of the displayed text depends on *font_ratate* variable (0~360) + * if *font_transparent* variable is set to 1, no background pixels will be printed + * If the text does not fit the screen/window width it will be clipped ( if *text_wrap=0* ), or continued on next line ( if *text_wrap=1* ) + * Two special characters are allowed in strings: *\r* CR (0x0D), clears the display to EOL, *\n* LF (ox0A), continues to the new line, x=0 + * Special values can be entered for X position: + * *CENTER* centers the text + * *RIGHT* right justifies the text horizontaly + * *LASTX* continues from last X position; offset can be used: *LASTX+n* + * Special values can be entered for Y: + * *CENTER* centers the text verticaly + * *BOTTOM* bottom justifies the text + * *LASTY* continues from last Y position; offset can be used: *LASTY+n* + * **EPD_getStringWidth** Returns the string width in pixels based on current font characteristics. Useful for positioning strings on the screen. + * **EPD_clearStringRect** Fills the rectangle occupied by string with current background color +* **Images**: + * **EPD_jpg_image** Decodes and displays JPG images + * Limits: + * Baseline only. Progressive and Lossless JPEG format are not supported. + * Image size: Up to 65520 x 65520 pixels + * Color space: YCbCr three components only. Gray scale image is not supported. + * Sampling factor: 4:4:4, 4:2:2 or 4:2:0. + * Can display the image **from file** or **memory buffer** + * Image can be **scaled** by factor 0 ~ 3 (1/1, 1/2, 1/4 or 1/8) + * Image is displayed from X,Y position on screen/window: + * X: image left position; constants CENTER & RIGHT can be used; *negative* value is accepted + * Y: image top position; constants CENTER & BOTTOM can be used; *negative* value is accepted + * Image is converted to **4-bit Gray Scale mode** +* **Other display functions**: + * **EPD_fillScreen** Fill the whole screen with black, white or gray scale +* **compile_font_file** Function which compiles font c source file to font file which can be used in *EPD_setFont()* function to select external font. Created file have the same name as source file and extension *.fnt* + + +* **Global wariables** + * **orientation** current screen orientation + * **font_ratate** current font rotate angle (0~395) + * **font_transparent** if not 0 draw fonts transparent + * **font_forceFixed** if not zero force drawing proportional fonts with fixed width + * **text_wrap** if not 0 wrap long text to the new line, else clip + * **_fg** current foreground color for fonts + * **_bg** current background for non transparent fonts + * **_angleOffset** angle offset for arc, polygon and line by angle functions + * **image_debug** print debug messages during image decode if set to 1 + * **cfont** Currently used font structure + * **EPD_X** X position of the next character after EPD_print() function + * **EPD_Y** Y position of the next character after EPD_print() function + * **_gs** use 4-bit Gray scale if set to 1 + * **_width** screen width (larger dimension) in pixels + * **_height** screen height (smaller dimension) in pixels + +--- + +Full functions **syntax and descriptions** can be found in *EPD.h* and *EPDspi.h* files. + +Full **demo application**, well documented, is included, please **analyze it** to learn how to use the library functions. + +--- + +#### Connecting the display + +To run the demo, attach display module to ESP32. Default pins used are: +* mosi: 23 +* sck: 18 +* CS: 5 (display CS) +* DC: 26 (display DC) +* RST: 27 (display RESET) +* BUSY: 32 (display BUSY output) + +The display can be powered from 3.3V or from **GPIO pin**. See *EPDspi.h* for configuration options. + +**If you want to use different pins, change them in** *EPDspi.h* + +Using *make menuconfig* **select tick rate 1000** ( → Component config → FreeRTOS → Tick rate (Hz) ) to get more accurate timings + +--- + +#### How to build + +Configure your esp32 build environment as for **esp-idf examples** + +Clone the repository + +`git clone https://github.com/loboris/ESP32_ePaper_example.git` + +Execute menuconfig and configure your Serial flash config and other settings. Included *sdkconfig.defaults* sets some defaults to be used. + +Navigate to **ePaper Display DEMO Configuration** and set **SPIFFS** options. + +Select if you want to use **wifi** (recommended) to get the time from **NTP** server and set your WiFi SSID and password. + +`make menuconfig` + +Make and flash the example. + +`make all && make flash` + +--- + +#### Prepare **SPIFFS** image + +*The demo uses some image and font files and it is necessary to flash the spiffs image* + +**To flash already prepared image** *components/spiffs_image/spiffs_image.img* execute: + +`make copyfs` + +--- + +You can also prepare different SFPIFFS **image** and flash it to ESP32. + +Files to be included on spiffs are already in **components/spiffs_image/image/** directory. You can add or remove the files you want to include. + +Then execute: + +`make makefs` + +to create **spiffs image** in *build* directory **without flashing** to ESP32 + +Or execute: + +`make flashfs` + +to create **spiffs image** in *build* directory and **flash** it to ESP32 + +--- + +Tested on Waveshare 2.9" ePaper module connected to SparkFun ESP32 Thing board. +![Tested on](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/2.9inch-e-paper-module-4.jpg) + +--- + +![Fonts](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD-fonts.jpg) +![Rotated](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD-Rotated.jpg) +![7-segFont](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD-7sef_font.jpg) +![Grayscale](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD_Grayscale.jpg) +![No power](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD-No_power.jpg) diff --git a/components/epaper/DefaultFont.c b/components/epaper/DefaultFont.c new file mode 100644 index 0000000..3e3c2a0 --- /dev/null +++ b/components/epaper/DefaultFont.c @@ -0,0 +1,333 @@ +// Default font + +// ======================================================================== +// This comes with no warranty, implied or otherwise + +// This data structure was designed to support Proportional fonts +// fonts. Individual characters do not have to be multiples of 8 bits wide. +// Any width is fine and does not need to be fixed. + +// The data bits are packed to minimize data requirements, but the tradeoff +// is that a header is required per character. + +// Header Format: +// ------------------------------------------------ +// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) +// Character Height +// First Character (Reserved. 0x00) +// Number Of Characters (Reserved. 0x00) + +// Individual Character Format: +// ---------------------------- +// Character Code +// Adjusted Y Offset (start Y of visible pixels) +// Width (width of the visible pixels) +// Height (height of the visible pixels) +// xOffset (start X of visible pixels) +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] + +// NOTE: You can remove any of these characters if they are not needed in +// your application. The first character number in each Glyph indicates +// the ASCII character code. Therefore, these do not have to be sequential. +// Just remove all the content for a particular character to save space. +// ======================================================================== + +// dejavu +// Point Size : 12 +// Memory usage : 1158 bytes +// # characters : 95 + +const unsigned char tft_DefaultFont[] = +{ +0x00, 0x0B, 0x86, 0x04, + +// ' ' +0x20,0x0A,0x00,0x00,0x00,0x04, + +// '!' +0x21,0x01,0x01,0x09,0x02,0x05, +0xFD,0x80, +// '"' +0x22,0x01,0x03,0x03,0x01,0x05, +0xB6,0x80, +// '#' +0x23,0x02,0x08,0x08,0x01,0x0A, +0x12,0x14,0x7F,0x24,0x24,0xFE,0x28,0x48, +// '$' +0x24,0x01,0x06,0x0B,0x02,0x08, +0x21,0xCA,0xA8,0xE0,0xE2,0xAA,0x70,0x82,0x00, +// '%' +0x25,0x01,0x0A,0x09,0x00,0x0B, +0x61,0x24,0x89,0x22,0x50,0x6D,0x82,0x91,0x24,0x49,0x21,0x80, +// '&' +0x26,0x01,0x09,0x09,0x01,0x0A, +0x30,0x24,0x10,0x0C,0x05,0x14,0x4A,0x19,0x8C,0x7B,0x00, +// ''' +0x27,0x01,0x01,0x03,0x01,0x03, +0xE0, +// '(' +0x28,0x00,0x03,0x0B,0x01,0x05, +0x69,0x49,0x24,0x48,0x80, +// ')' +0x29,0x00,0x03,0x0B,0x01,0x05, +0x89,0x12,0x49,0x4A,0x00, +// '*' +0x2A,0x01,0x05,0x06,0x01,0x06, +0x25,0x5C,0xEA,0x90, +// '+' +0x2B,0x03,0x07,0x07,0x01,0x0A, +0x10,0x20,0x47,0xF1,0x02,0x04,0x00, +// ',' +0x2C,0x08,0x01,0x03,0x01,0x04, +0xE0, +// '-' +0x2D,0x06,0x03,0x01,0x01,0x04, +0xE0, +// '.' +0x2E,0x08,0x01,0x02,0x01,0x04, +0xC0, +// '/' +0x2F,0x01,0x04,0x0A,0x00,0x04, +0x11,0x22,0x24,0x44,0x88, +// '0' +0x30,0x01,0x06,0x09,0x01,0x08, +0x79,0x28,0x61,0x86,0x18,0x52,0x78, +// '1' +0x31,0x01,0x05,0x09,0x01,0x08, +0xE1,0x08,0x42,0x10,0x84,0xF8, +// '2' +0x32,0x01,0x07,0x09,0x01,0x08, +0x79,0x18,0x10,0x20,0x82,0x08,0x20,0xFC, +// '3' +0x33,0x01,0x06,0x09,0x01,0x08, +0x7A,0x10,0x41,0x38,0x30,0x63,0x78, +// '4' +0x34,0x01,0x06,0x09,0x01,0x08, +0x18,0x62,0x92,0x4A,0x2F,0xC2,0x08, +// '5' +0x35,0x01,0x06,0x09,0x01,0x08, +0xFA,0x08,0x3C,0x0C,0x10,0x63,0x78, +// '6' +0x36,0x01,0x06,0x09,0x01,0x08, +0x39,0x18,0x3E,0xCE,0x18,0x53,0x78, +// '7' +0x37,0x01,0x06,0x09,0x01,0x08, +0xFC,0x10,0x82,0x10,0x42,0x08,0x40, +// '8' +0x38,0x01,0x06,0x09,0x01,0x08, +0x7B,0x38,0x73,0x7B,0x38,0x73,0x78, +// '9' +0x39,0x01,0x06,0x09,0x01,0x08, +0x7B,0x28,0x61,0xCD,0xD0,0x62,0x70, +// ':' +0x3A,0x04,0x01,0x06,0x01,0x04, +0xCC, +// ';' +0x3B,0x04,0x01,0x07,0x01,0x04, +0xCE, +// '<' +0x3C,0x03,0x08,0x06,0x01,0x0A, +0x03,0x1E,0xE0,0xE0,0x1E,0x03, +// '=' +0x3D,0x05,0x08,0x03,0x01,0x0A, +0xFF,0x00,0xFF, +// '>' +0x3E,0x03,0x08,0x06,0x01,0x0A, +0xC0,0x78,0x07,0x07,0x78,0xC0, +// '?' +0x3F,0x01,0x05,0x09,0x00,0x06, +0x74,0x42,0x22,0x10,0x04,0x20, +// '@' +0x40,0x01,0x0B,0x0B,0x01,0x0D, +0x1F,0x06,0x19,0x01,0x46,0x99,0x13,0x22,0x64,0x54,0x6C,0x40,0x04,0x10,0x7C,0x00, +// 'A' +0x41,0x01,0x08,0x09,0x00,0x08, +0x18,0x18,0x24,0x24,0x24,0x42,0x7E,0x42,0x81, +// 'B' +0x42,0x01,0x06,0x09,0x01,0x08, +0xFA,0x18,0x61,0xFA,0x18,0x61,0xF8, +// 'C' +0x43,0x01,0x06,0x09,0x01,0x08, +0x39,0x18,0x20,0x82,0x08,0x11,0x38, +// 'D' +0x44,0x01,0x07,0x09,0x01,0x09, +0xF9,0x0A,0x0C,0x18,0x30,0x60,0xC2,0xF8, +// 'E' +0x45,0x01,0x06,0x09,0x01,0x08, +0xFE,0x08,0x20,0xFE,0x08,0x20,0xFC, +// 'F' +0x46,0x01,0x05,0x09,0x01,0x07, +0xFC,0x21,0x0F,0xC2,0x10,0x80, +// 'G' +0x47,0x01,0x07,0x09,0x01,0x09, +0x3C,0x86,0x04,0x08,0xF0,0x60,0xA1,0x3C, +// 'H' +0x48,0x01,0x07,0x09,0x01,0x09, +0x83,0x06,0x0C,0x1F,0xF0,0x60,0xC1,0x82, +// 'I' +0x49,0x01,0x01,0x09,0x01,0x03, +0xFF,0x80, +// 'J' +0x4A,0x01,0x03,0x0B,0xFF,0x03, +0x24,0x92,0x49,0x27,0x00, +// 'K' +0x4B,0x01,0x07,0x09,0x01,0x07, +0x85,0x12,0x45,0x0C,0x14,0x24,0x44,0x84, +// 'L' +0x4C,0x01,0x05,0x09,0x01,0x06, +0x84,0x21,0x08,0x42,0x10,0xF8, +// 'M' +0x4D,0x01,0x08,0x09,0x01,0x0A, +0x81,0xC3,0xC3,0xA5,0xA5,0x99,0x99,0x81,0x81, +// 'N' +0x4E,0x01,0x07,0x09,0x01,0x09, +0xC3,0x86,0x8D,0x19,0x31,0x62,0xC3,0x86, +// 'O' +0x4F,0x01,0x07,0x09,0x01,0x09, +0x38,0x8A,0x0C,0x18,0x30,0x60,0xA2,0x38, +// 'P' +0x50,0x01,0x06,0x09,0x01,0x08, +0xFA,0x38,0x63,0xFA,0x08,0x20,0x80, +// 'Q' +0x51,0x01,0x07,0x0B,0x01,0x09, +0x38,0x8A,0x0C,0x18,0x30,0x60,0xA2,0x38,0x10,0x10, +// 'R' +0x52,0x01,0x07,0x09,0x01,0x08, +0xF9,0x1A,0x14,0x6F,0x91,0x21,0x42,0x82, +// 'S' +0x53,0x01,0x06,0x09,0x01,0x08, +0x7B,0x18,0x30,0x78,0x30,0x63,0x78, +// 'T' +0x54,0x01,0x07,0x09,0x00,0x07, +0xFE,0x20,0x40,0x81,0x02,0x04,0x08,0x10, +// 'U' +0x55,0x01,0x07,0x09,0x01,0x09, +0x83,0x06,0x0C,0x18,0x30,0x60,0xA2,0x38, +// 'V' +0x56,0x01,0x0A,0x09,0xFF,0x08, +0x40,0x90,0x22,0x10,0x84,0x21,0x04,0x81,0x20,0x30,0x0C,0x00, +// 'W' +0x57,0x01,0x0B,0x09,0x00,0x0B, +0x84,0x28,0x89,0x11,0x27,0x22,0xA8,0x55,0x0E,0xE0,0x88,0x11,0x00, +// 'X' +0x58,0x01,0x07,0x09,0x00,0x07, +0xC6,0x88,0xA1,0xC1,0x07,0x0A,0x22,0x82, +// 'Y' +0x59,0x01,0x07,0x09,0x00,0x07, +0x82,0x89,0x11,0x43,0x82,0x04,0x08,0x10, +// 'Z' +0x5A,0x01,0x07,0x09,0x01,0x09, +0xFE,0x04,0x10,0x41,0x04,0x10,0x40,0xFE, +// '[' +0x5B,0x01,0x02,0x0B,0x02,0x05, +0xEA,0xAA,0xAC, +// '\' +0x5C,0x01,0x04,0x0A,0x00,0x04, +0x88,0x44,0x42,0x22,0x11, +// ']' +0x5D,0x01,0x02,0x0B,0x01,0x05, +0xD5,0x55,0x5C, +// '^' +0x5E,0x01,0x08,0x03,0x01,0x0A, +0x18,0x24,0x42, +// '_' +0x5F,0x0C,0x06,0x01,0x00,0x06, +0xFC, +// '`' +0x60,0x00,0x03,0x02,0x01,0x06, +0x44, +// 'a' +0x61,0x03,0x06,0x07,0x01,0x08, +0x7A,0x30,0x5F,0x86,0x37,0x40, +// 'b' +0x62,0x00,0x06,0x0A,0x01,0x08, +0x82,0x08,0x2E,0xCA,0x18,0x61,0xCE,0xE0, +// 'c' +0x63,0x03,0x05,0x07,0x01,0x07, +0x72,0x61,0x08,0x25,0xC0, +// 'd' +0x64,0x00,0x06,0x0A,0x01,0x08, +0x04,0x10,0x5D,0xCE,0x18,0x61,0xCD,0xD0, +// 'e' +0x65,0x03,0x06,0x07,0x01,0x08, +0x39,0x38,0x7F,0x81,0x13,0x80, +// 'f' +0x66,0x00,0x04,0x0A,0x00,0x04, +0x34,0x4F,0x44,0x44,0x44, +// 'g' +0x67,0x03,0x06,0x0A,0x01,0x08, +0x77,0x38,0x61,0x87,0x37,0x41,0x4C,0xE0, +// 'h' +0x68,0x00,0x06,0x0A,0x01,0x08, +0x82,0x08,0x2E,0xC6,0x18,0x61,0x86,0x10, +// 'i' +0x69,0x01,0x01,0x09,0x01,0x03, +0xBF,0x80, +// 'j' +0x6A,0x01,0x02,0x0C,0x00,0x03, +0x45,0x55,0x56, +// 'k' +0x6B,0x00,0x06,0x0A,0x01,0x07, +0x82,0x08,0x22,0x92,0x8E,0x28,0x92,0x20, +// 'l' +0x6C,0x00,0x01,0x0A,0x01,0x03, +0xFF,0xC0, +// 'm' +0x6D,0x03,0x09,0x07,0x01,0x0B, +0xB3,0x66,0x62,0x31,0x18,0x8C,0x46,0x22, +// 'n' +0x6E,0x03,0x06,0x07,0x01,0x08, +0xBB,0x18,0x61,0x86,0x18,0x40, +// 'o' +0x6F,0x03,0x06,0x07,0x01,0x08, +0x7B,0x38,0x61,0x87,0x37,0x80, +// 'p' +0x70,0x03,0x06,0x0A,0x01,0x08, +0xBB,0x28,0x61,0x87,0x3B,0xA0,0x82,0x00, +// 'q' +0x71,0x03,0x06,0x0A,0x01,0x08, +0x77,0x38,0x61,0x87,0x37,0x41,0x04,0x10, +// 'r' +0x72,0x03,0x04,0x07,0x01,0x05, +0xBC,0x88,0x88,0x80, +// 's' +0x73,0x03,0x06,0x07,0x01,0x07, +0x72,0x28,0x1C,0x0A,0x27,0x00, +// 't' +0x74,0x01,0x04,0x09,0x00,0x05, +0x44,0xF4,0x44,0x44,0x30, +// 'u' +0x75,0x03,0x06,0x07,0x01,0x08, +0x86,0x18,0x61,0x86,0x37,0x40, +// 'v' +0x76,0x03,0x08,0x07,0xFF,0x06, +0x42,0x42,0x24,0x24,0x24,0x18,0x18, +// 'w' +0x77,0x03,0x09,0x07,0x00,0x09, +0x88,0xC4,0x57,0x4A,0xA5,0x51,0x10,0x88, +// 'x' +0x78,0x03,0x06,0x07,0x00,0x06, +0x85,0x24,0x8C,0x49,0x28,0x40, +// 'y' +0x79,0x03,0x08,0x0A,0xFF,0x06, +0x42,0x42,0x24,0x24,0x14,0x18,0x08,0x08,0x10,0x60, +// 'z' +0x7A,0x03,0x05,0x07,0x00,0x05, +0xF8,0x44,0x44,0x43,0xE0, +// '{' +0x7B,0x01,0x05,0x0B,0x02,0x08, +0x19,0x08,0x42,0x60,0x84,0x21,0x06, +// '|' +0x7C,0x01,0x01,0x0C,0x02,0x04, +0xFF,0xF0, +// '}' +0x7D,0x01,0x05,0x0B,0x01,0x08, +0xC1,0x08,0x42,0x0C,0x84,0x21,0x30, +// '~' +0x7E,0x04,0x08,0x03,0x01,0x0A, +0x00,0x71,0x8E, + +// Terminator +0xFF +}; diff --git a/components/epaper/DejaVuSans18.c b/components/epaper/DejaVuSans18.c new file mode 100644 index 0000000..fe53372 --- /dev/null +++ b/components/epaper/DejaVuSans18.c @@ -0,0 +1,322 @@ +// ============================================================================ +// Proportional font Header Format: +// ------------------------------------------------ +// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) +// Character Height +// First Character (Reserved. 0x00) +// Number Of Characters (Reserved. 0x00) + +// Individual Character Format: +// ---------------------------- +// Character Code +// Adjusted Y Offset +// Width +// Height +// xOffset +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] + +// NOTE: You can remove any of these characters if they are not needed in +// your application. The first character number in each Glyph indicates +// the ASCII character code. Therefore, these do not have to be sequential. +// Just remove all the content for a particular character to save space. +// ============================================================================ + +// DejaVuSans +// Point Size : 18 +// Memory usage : 1828 bytes +// # characters : 95 + +const unsigned char tft_Dejavu18x[] = +{ +0x00, 0x12, 0x00, 0x00, + +// ' ' +0x20,0x0E,0x00,0x00,0x00,0x06, + +// '!' +0x21,0x01,0x02,0x0D,0x03,0x07, +0xFF,0xFF,0xC3,0xC0, +// '"' +0x22,0x01,0x06,0x05,0x01,0x08, +0xCF,0x3C,0xF3,0xCC, +// '#' +0x23,0x00,0x0C,0x0E,0x01,0x0F, +0x04,0x40,0x44,0x0C,0xC0,0xC8,0x7F,0xF7,0xFF,0x09,0x81,0x90,0xFF,0xEF,0xFE,0x13,0x03,0x30,0x32,0x02,0x20, +// '$' +0x24,0x00,0x0A,0x11,0x01,0x0B, +0x08,0x02,0x03,0xE1,0xFC,0xE9,0x32,0x0F,0x81,0xF8,0x0F,0x02,0x60,0x9A,0x2E,0xFF,0x1F,0x80,0x80,0x20,0x08,0x00, +// '%' +0x25,0x01,0x0F,0x0D,0x01,0x11, +0x78,0x10,0x90,0x43,0x31,0x86,0x62,0x0C,0xC8,0x19,0x10,0x1E,0x4F,0x01,0x12,0x02,0x66,0x08,0xCC,0x31,0x98,0x41,0x21,0x03,0xC0, +// '&' +0x26,0x01,0x0C,0x0D,0x01,0x0D, +0x0F,0x01,0xF8,0x30,0x83,0x00,0x38,0x03,0xC0,0x7E,0x6C,0x76,0xC3,0xCC,0x18,0xE1,0xC7,0xFE,0x3E,0x70, +// ''' +0x27,0x01,0x02,0x05,0x01,0x04, +0xFF,0xC0, +// '(' +0x28,0x00,0x04,0x10,0x02,0x07, +0x32,0x66,0x4C,0xCC,0xCC,0xC4,0x66,0x23, +// ')' +0x29,0x00,0x04,0x10,0x01,0x07, +0xC4,0x66,0x23,0x33,0x33,0x32,0x66,0x4C, +// '*' +0x2A,0x01,0x07,0x08,0x01,0x09, +0x11,0x25,0x51,0xC3,0x8A,0xA4,0x88, +// '+' +0x2B,0x02,0x0C,0x0C,0x02,0x0F, +0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x0F,0xFF,0xFF,0xF0,0x60,0x06,0x00,0x60,0x06,0x00,0x60, +// ',' +0x2C,0x0C,0x03,0x04,0x01,0x06, +0x6D,0x40, +// '-' +0x2D,0x08,0x05,0x02,0x01,0x07, +0xFF,0xC0, +// '.' +0x2E,0x0C,0x02,0x02,0x02,0x06, +0xF0, +// '/' +0x2F,0x01,0x06,0x0F,0x00,0x06, +0x0C,0x31,0x86,0x18,0xE3,0x0C,0x31,0xC6,0x18,0x63,0x0C,0x00, +// '0' +0x30,0x01,0x09,0x0D,0x01,0x0B, +0x3E,0x3F,0x98,0xD8,0x3C,0x1E,0x0F,0x07,0x83,0xC1,0xE0,0xD8,0xCF,0xE3,0xE0, +// '1' +0x31,0x01,0x08,0x0D,0x02,0x0B, +0x38,0xF8,0xD8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xFF, +// '2' +0x32,0x01,0x09,0x0D,0x01,0x0B, +0x7C,0x7F,0x21,0xC0,0x60,0x30,0x30,0x18,0x18,0x18,0x18,0x18,0x1F,0xEF,0xF0, +// '3' +0x33,0x01,0x09,0x0D,0x01,0x0B, +0x7E,0x7F,0xA0,0xE0,0x30,0x39,0xF0,0xFC,0x07,0x01,0x80,0xE0,0xFF,0xE7,0xE0, +// '4' +0x34,0x01,0x0A,0x0D,0x01,0x0B, +0x07,0x01,0xC0,0xB0,0x6C,0x13,0x08,0xC6,0x31,0x0C,0xFF,0xFF,0xF0,0x30,0x0C,0x03,0x00, +// '5' +0x35,0x01,0x08,0x0D,0x01,0x0B, +0x7E,0x7E,0x60,0x60,0x7C,0x7E,0x47,0x03,0x03,0x03,0x87,0xFE,0x7C, +// '6' +0x36,0x01,0x09,0x0D,0x01,0x0B, +0x1E,0x1F,0x9C,0x5C,0x0C,0x06,0xF3,0xFD,0xC7,0xC1,0xE0,0xD8,0xEF,0xE1,0xE0, +// '7' +0x37,0x01,0x08,0x0D,0x01,0x0B, +0xFF,0xFF,0x06,0x06,0x06,0x0E,0x0C,0x0C,0x1C,0x18,0x18,0x38,0x30, +// '8' +0x38,0x01,0x09,0x0D,0x01,0x0B, +0x3E,0x3F,0xB8,0xF8,0x3E,0x39,0xF1,0xFD,0xC7,0xC1,0xE0,0xF8,0xEF,0xE3,0xE0, +// '9' +0x39,0x01,0x09,0x0D,0x01,0x0B, +0x3C,0x3F,0xB8,0xD8,0x3C,0x1F,0x1D,0xFE,0x7B,0x01,0x81,0xD1,0xCF,0xC3,0xC0, +// ':' +0x3A,0x05,0x02,0x09,0x02,0x06, +0xF0,0x03,0xC0, +// ';' +0x3B,0x05,0x03,0x0B,0x01,0x06, +0x6C,0x00,0x03,0x6A,0x00, +// '<' +0x3C,0x04,0x0B,0x0A,0x02,0x0F, +0x00,0x20,0x3C,0x1F,0x1F,0x0F,0x81,0xF0,0x0F,0x80,0x3E,0x01,0xE0,0x04, +// '=' +0x3D,0x05,0x0B,0x06,0x02,0x0F, +0xFF,0xFF,0xFC,0x00,0x00,0x0F,0xFF,0xFF,0xC0, +// '>' +0x3E,0x04,0x0B,0x0A,0x02,0x0F, +0x80,0x1E,0x01,0xF0,0x07,0xC0,0x3E,0x07,0xC3,0xE3,0xE0,0xF0,0x10,0x00, +// '?' +0x3F,0x01,0x07,0x0D,0x01,0x0A, +0x79,0xFA,0x38,0x30,0x61,0x86,0x18,0x30,0x60,0x01,0x83,0x00, +// '@' +0x40,0x01,0x10,0x10,0x01,0x12, +0x07,0xE0,0x1F,0xF8,0x3C,0x1C,0x70,0x06,0x60,0x07,0xE3,0x63,0xC7,0xE3,0xC6,0x63,0xC6,0x66,0xC7,0xFC,0xE3,0x70,0x60,0x00,0x70,0x00,0x3C,0x30,0x1F,0xF0,0x07,0xC0, +// 'A' +0x41,0x01,0x0C,0x0D,0x00,0x0C, +0x06,0x00,0x60,0x0F,0x00,0xF0,0x19,0x81,0x98,0x19,0x83,0x0C,0x3F,0xC7,0xFE,0x60,0x66,0x06,0xC0,0x30, +// 'B' +0x42,0x01,0x09,0x0D,0x02,0x0C, +0xFC,0x7F,0xB0,0xD8,0x6C,0x37,0xF3,0xF9,0x86,0xC1,0xE0,0xF0,0xFF,0xEF,0xE0, +// 'C' +0x43,0x01,0x0B,0x0D,0x01,0x0D, +0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x0C,0x01,0xC0,0x9F,0xF0,0xFC, +// 'D' +0x44,0x01,0x0B,0x0D,0x02,0x0E, +0xFE,0x1F,0xF3,0x07,0x60,0x6C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x1B,0x07,0x7F,0xCF,0xE0, +// 'E' +0x45,0x01,0x08,0x0D,0x02,0x0B, +0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, +// 'F' +0x46,0x01,0x08,0x0D,0x02,0x0A, +0xFF,0xFF,0xC0,0xC0,0xC0,0xFE,0xFE,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +// 'G' +0x47,0x01,0x0B,0x0D,0x01,0x0E, +0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x87,0xF0,0xFE,0x03,0xC0,0x6C,0x0D,0xC1,0x9F,0xE0,0xF8, +// 'H' +0x48,0x01,0x0A,0x0D,0x02,0x0E, +0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xFF,0xFF,0xFF,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xC0, +// 'I' +0x49,0x01,0x02,0x0D,0x02,0x06, +0xFF,0xFF,0xFF,0xC0, +// 'J' +0x4A,0x01,0x05,0x11,0xFF,0x06, +0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0x8C,0xFE,0xE0, +// 'K' +0x4B,0x01,0x0B,0x0D,0x02,0x0C, +0xC1,0x98,0x63,0x18,0x66,0x0D,0x81,0xE0,0x3C,0x06,0xC0,0xCC,0x18,0xC3,0x0C,0x60,0xCC,0x0C, +// 'L' +0x4C,0x01,0x08,0x0D,0x02,0x0A, +0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, +// 'M' +0x4D,0x01,0x0C,0x0D,0x02,0x10, +0xE0,0x7F,0x0F,0xF0,0xFD,0x8B,0xD9,0xBD,0x9B,0xCF,0x3C,0xF3,0xC6,0x3C,0x63,0xC0,0x3C,0x03,0xC0,0x30, +// 'N' +0x4E,0x01,0x0A,0x0D,0x02,0x0E, +0xE0,0xF8,0x3F,0x0F,0xC3,0xD8,0xF6,0x3C,0xCF,0x1B,0xC6,0xF0,0xFC,0x3F,0x07,0xC1,0xC0, +// 'O' +0x4F,0x01,0x0C,0x0D,0x01,0x0E, +0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80, +// 'P' +0x50,0x01,0x08,0x0D,0x02,0x0B, +0xFC,0xFE,0xC7,0xC3,0xC3,0xC7,0xFE,0xFC,0xC0,0xC0,0xC0,0xC0,0xC0, +// 'Q' +0x51,0x01,0x0C,0x0F,0x01,0x0E, +0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80,0x18,0x00,0xC0, +// 'R' +0x52,0x01,0x0A,0x0D,0x02,0x0D, +0xFC,0x3F,0x8C,0x73,0x0C,0xC3,0x31,0xCF,0xE3,0xF0,0xC6,0x30,0xCC,0x33,0x06,0xC1,0xC0, +// 'S' +0x53,0x01,0x0A,0x0D,0x01,0x0B, +0x3E,0x1F,0xCE,0x13,0x00,0xC0,0x1F,0x03,0xF0,0x0E,0x01,0x80,0x68,0x3B,0xFC,0x7E,0x00, +// 'T' +0x54,0x01,0x0C,0x0D,0x00,0x0C, +0xFF,0xFF,0xFF,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00, +// 'U' +0x55,0x01,0x0A,0x0D,0x02,0x0E, +0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x36,0x19,0xFE,0x1E,0x00, +// 'V' +0x56,0x01,0x0C,0x0D,0x00,0x0C, +0xC0,0x36,0x06,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60,0x06,0x00, +// 'W' +0x57,0x01,0x11,0x0D,0x01,0x13, +0xC1,0xC1,0xE0,0xE0,0xD8,0xF8,0xCC,0x6C,0x66,0x36,0x33,0x1B,0x18,0xD8,0xD8,0x6C,0x6C,0x36,0x36,0x1F,0x1F,0x07,0x07,0x03,0x83,0x81,0xC1,0xC0, +// 'X' +0x58,0x01,0x0B,0x0D,0x01,0x0D, +0x70,0xE6,0x18,0xE6,0x0D,0xC0,0xF0,0x1C,0x03,0x80,0x78,0x1B,0x07,0x30,0xC7,0x30,0x6E,0x0E, +// 'Y' +0x59,0x01,0x0C,0x0D,0x00,0x0C, +0xE0,0x76,0x06,0x30,0xC1,0x98,0x19,0x80,0xF0,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00, +// 'Z' +0x5A,0x01,0x0B,0x0D,0x01,0x0D, +0xFF,0xFF,0xFC,0x07,0x01,0xC0,0x30,0x0E,0x03,0x80,0xE0,0x18,0x06,0x01,0xC0,0x7F,0xFF,0xFE, +// '[' +0x5B,0x00,0x04,0x10,0x01,0x07, +0xFF,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xFF, +// '\' +0x5C,0x01,0x06,0x0F,0x00,0x06, +0xC3,0x06,0x18,0x61,0xC3,0x0C,0x30,0xE1,0x86,0x18,0x30,0xC0, +// ']' +0x5D,0x00,0x04,0x10,0x02,0x07, +0xFF,0x33,0x33,0x33,0x33,0x33,0x33,0xFF, +// '^' +0x5E,0x01,0x0B,0x05,0x02,0x0F, +0x0E,0x03,0xE0,0xC6,0x30,0x6C,0x06, +// '_' +0x5F,0x10,0x09,0x02,0x00,0x09, +0xFF,0xFF,0xC0, +// '`' +0x60,0x00,0x04,0x03,0x02,0x09, +0xC6,0x30, +// 'a' +0x61,0x04,0x08,0x0A,0x01,0x0A, +0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, +// 'b' +0x62,0x00,0x09,0x0E,0x02,0x0B, +0xC0,0x60,0x30,0x18,0x0D,0xE7,0xFB,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x7F,0xF6,0xF0, +// 'c' +0x63,0x04,0x08,0x0A,0x01,0x09, +0x1E,0x7F,0x61,0xC0,0xC0,0xC0,0xC0,0x61,0x7F,0x1E, +// 'd' +0x64,0x00,0x09,0x0E,0x01,0x0B, +0x01,0x80,0xC0,0x60,0x33,0xDB,0xFF,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x77,0xF9,0xEC, +// 'e' +0x65,0x04,0x0A,0x0A,0x01,0x0B, +0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, +// 'f' +0x66,0x00,0x07,0x0E,0x00,0x06, +0x1E,0x7C,0xC1,0x8F,0xFF,0xCC,0x18,0x30,0x60,0xC1,0x83,0x06,0x00, +// 'g' +0x67,0x04,0x09,0x0E,0x01,0x0B, +0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x68,0x67,0xF1,0xF0, +// 'h' +0x68,0x00,0x08,0x0E,0x02,0x0B, +0xC0,0xC0,0xC0,0xC0,0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, +// 'i' +0x69,0x00,0x02,0x0E,0x02,0x05, +0xF0,0xFF,0xFF,0xF0, +// 'j' +0x6A,0x00,0x04,0x12,0x00,0x05, +0x33,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0xEC, +// 'k' +0x6B,0x00,0x09,0x0E,0x02,0x0A, +0xC0,0x60,0x30,0x18,0x0C,0x36,0x33,0x31,0xB0,0xF0,0x78,0x36,0x19,0x8C,0x66,0x18, +// 'l' +0x6C,0x00,0x02,0x0E,0x02,0x05, +0xFF,0xFF,0xFF,0xF0, +// 'm' +0x6D,0x04,0x0E,0x0A,0x02,0x11, +0xDC,0x7B,0xFB,0xEE,0x79,0xF0,0xC3,0xC3,0x0F,0x0C,0x3C,0x30,0xF0,0xC3,0xC3,0x0F,0x0C,0x30, +// 'n' +0x6E,0x04,0x08,0x0A,0x02,0x0B, +0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, +// 'o' +0x6F,0x04,0x0A,0x0A,0x01,0x0B, +0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, +// 'p' +0x70,0x04,0x09,0x0E,0x02,0x0B, +0xDE,0x7F,0xB8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0xFF,0x6F,0x30,0x18,0x0C,0x06,0x00, +// 'q' +0x71,0x04,0x09,0x0E,0x01,0x0B, +0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x60,0x30,0x18,0x0C, +// 'r' +0x72,0x04,0x06,0x0A,0x02,0x08, +0xDF,0xFE,0x30,0xC3,0x0C,0x30,0xC3,0x00, +// 's' +0x73,0x04,0x08,0x0A,0x01,0x08, +0x7C,0xFE,0xC2,0xE0,0x7C,0x1E,0x06,0x86,0xFE,0x78, +// 't' +0x74,0x01,0x06,0x0D,0x01,0x07, +0x61,0x86,0x3F,0xFD,0x86,0x18,0x61,0x86,0x1F,0x3C, +// 'u' +0x75,0x04,0x08,0x0A,0x02,0x0B, +0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, +// 'v' +0x76,0x04,0x0C,0x0A,0x00,0x0B, +0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60, +// 'w' +0x77,0x04,0x0F,0x0A,0x01,0x10, +0x63,0x8C,0xC7,0x19,0x8E,0x31,0xB6,0xC3,0x6D,0x86,0xDB,0x0F,0x1E,0x0E,0x38,0x1C,0x70,0x38,0xE0, +// 'x' +0x78,0x04,0x0A,0x0A,0x01,0x0B, +0xE1,0xD8,0x63,0x30,0xCC,0x1E,0x07,0x83,0x30,0xCC,0x61,0xB8,0x70, +// 'y' +0x79,0x04,0x0C,0x0E,0x00,0x0B, +0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80, +// 'z' +0x7A,0x04,0x08,0x0A,0x01,0x09, +0xFF,0xFF,0x06,0x0C,0x1C,0x38,0x30,0x70,0xFF,0xFF, +// '{' +0x7B,0x00,0x08,0x11,0x02,0x0B, +0x0F,0x1F,0x18,0x18,0x18,0x18,0x38,0xF0,0xF0,0x38,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F, +// '|' +0x7C,0x00,0x02,0x12,0x02,0x06, +0xFF,0xFF,0xFF,0xFF,0xF0, +// '}' +0x7D,0x00,0x08,0x11,0x02,0x0B, +0xF0,0xF8,0x18,0x18,0x18,0x18,0x1C,0x0F,0x0F,0x1C,0x18,0x18,0x18,0x18,0x18,0xF8,0xF0, +// '~' +0x7E,0x05,0x0B,0x05,0x02,0x0F, +0x00,0x0F,0x87,0xFF,0xC3,0xE0,0x00, + +// Terminator +0xFF +}; diff --git a/components/epaper/DejaVuSans24.c b/components/epaper/DejaVuSans24.c new file mode 100644 index 0000000..aa7e2f1 --- /dev/null +++ b/components/epaper/DejaVuSans24.c @@ -0,0 +1,331 @@ +// ======================================================================== +// This comes with no warranty, implied or otherwise + +// This data structure was designed to support Proportional fonts +// fonts. Individual characters do not have to be multiples of 8 bits wide. +// Any width is fine and does not need to be fixed. + +// The data bits are packed to minimize data requirements, but the tradeoff +// is that a header is required per character. + +// Header Format: +// ------------------------------------------------ +// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) +// Character Height +// First Character (Reserved. 0x00) +// Number Of Characters (Reserved. 0x00) + +// Individual Character Format: +// ---------------------------- +// Character Code +// Adjusted Y Offset +// Width +// Height +// xOffset +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] + +// NOTE: You can remove any of these characters if they are not needed in +// your application. The first character number in each Glyph indicates +// the ASCII character code. Therefore, these do not have to be sequential. +// Just remove all the content for a particular character to save space. +// ======================================================================== + +// dejavu +// Point Size : 24 +// Memory usage : 2724 bytes +// # characters : 95 + +const unsigned char tft_Dejavu24[] = +{ +0x00, 0x17, 0x00, 0x00, + +// ' ' +0x20,0x13,0x00,0x00,0x00,0x08, + +// '!' +0x21,0x01,0x02,0x12,0x04,0x0A, +0xFF,0xFF,0xFF,0x03,0xF0, +// '"' +0x22,0x01,0x06,0x07,0x02,0x0B, +0xCF,0x3C,0xF3,0xCF,0x3C,0xC0, +// '#' +0x23,0x01,0x10,0x12,0x02,0x14, +0x03,0x08,0x03,0x18,0x03,0x18,0x03,0x18,0x02,0x18,0x7F,0xFF,0x7F,0xFF,0x06,0x30,0x04,0x30,0x0C,0x20,0x0C,0x60,0xFF,0xFE,0xFF,0xFE,0x18,0x40,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x10,0xC0, +// '$' +0x24,0x01,0x0B,0x16,0x02,0x0F, +0x04,0x00,0x80,0x10,0x0F,0xC7,0xFD,0xC8,0xB1,0x06,0x20,0xE4,0x0F,0x80,0xFE,0x03,0xE0,0x4E,0x08,0xC1,0x1E,0x27,0xFF,0xC7,0xE0,0x10,0x02,0x00,0x40,0x08,0x00, +// '%' +0x25,0x01,0x14,0x12,0x01,0x17, +0x3C,0x03,0x06,0x60,0x60,0xC3,0x06,0x0C,0x30,0xC0,0xC3,0x1C,0x0C,0x31,0x80,0xC3,0x38,0x0C,0x33,0x00,0x66,0x63,0xC3,0xC6,0x66,0x00,0xCC,0x30,0x1C,0xC3,0x01,0x8C,0x30,0x38,0xC3,0x03,0x0C,0x30,0x60,0xC3,0x06,0x06,0x60,0xC0,0x3C, +// '&' +0x26,0x01,0x10,0x12,0x01,0x13, +0x07,0xC0,0x1F,0xE0,0x38,0x20,0x30,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x1C,0x00,0x3E,0x00,0x77,0x06,0xE3,0x86,0xC1,0xCC,0xC0,0xFC,0xC0,0x78,0xE0,0x78,0x70,0xFC,0x3F,0xCE,0x0F,0x87, +// ''' +0x27,0x01,0x02,0x07,0x02,0x07, +0xFF,0xFC, +// '(' +0x28,0x01,0x05,0x15,0x02,0x09, +0x19,0x8C,0xC6,0x31,0x18,0xC6,0x31,0x8C,0x61,0x0C,0x63,0x0C,0x61,0x80, +// ')' +0x29,0x01,0x05,0x15,0x02,0x09, +0xC3,0x18,0x63,0x18,0x43,0x18,0xC6,0x31,0x8C,0x46,0x31,0x98,0xCC,0x00, +// '*' +0x2A,0x01,0x0B,0x0A,0x00,0x0C, +0x04,0x00,0x83,0x11,0xBA,0xE1,0xF0,0x3E,0x1D,0x76,0x23,0x04,0x00,0x80, +// '+' +0x2B,0x03,0x10,0x10,0x03,0x14, +0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0xFF,0xFF,0xFF,0xFF,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80, +// ',' +0x2C,0x10,0x03,0x06,0x02,0x08, +0x6D,0xBD,0x80, +// '-' +0x2D,0x0B,0x06,0x02,0x01,0x09, +0xFF,0xF0, +// '.' +0x2E,0x10,0x02,0x03,0x03,0x08, +0xFC, +// '/' +0x2F,0x01,0x08,0x14,0x00,0x08, +0x03,0x07,0x06,0x06,0x06,0x0C,0x0C,0x0C,0x18,0x18,0x18,0x18,0x30,0x30,0x30,0x60,0x60,0x60,0xE0,0xC0, +// '0' +0x30,0x01,0x0C,0x12,0x02,0x0F, +0x0F,0x03,0xFC,0x70,0xE6,0x06,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x60,0x67,0x0E,0x3F,0xC0,0xF0, +// '1' +0x31,0x01,0x0A,0x12,0x03,0x0F, +0x3C,0x3F,0x0C,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0, +// '2' +0x32,0x01,0x0C,0x12,0x02,0x0F, +0x3F,0x0F,0xF8,0xC1,0xC0,0x0E,0x00,0x60,0x06,0x00,0x60,0x0C,0x01,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xEF,0xFE, +// '3' +0x33,0x01,0x0C,0x12,0x02,0x0F, +0x3F,0x07,0xFC,0x41,0xC0,0x06,0x00,0x60,0x06,0x00,0x60,0x0C,0x1F,0x81,0xFC,0x00,0xE0,0x07,0x00,0x30,0x03,0x00,0x78,0x0E,0xFF,0xC3,0xF0, +// '4' +0x34,0x01,0x0D,0x12,0x01,0x0F, +0x01,0xC0,0x1E,0x00,0xB0,0x0D,0x80,0xCC,0x06,0x60,0x63,0x03,0x18,0x30,0xC3,0x06,0x18,0x31,0x81,0x8F,0xFF,0xFF,0xFC,0x03,0x00,0x18,0x00,0xC0,0x06,0x00, +// '5' +0x35,0x01,0x0B,0x12,0x02,0x0F, +0x7F,0xCF,0xF9,0x80,0x30,0x06,0x00,0xC0,0x1F,0xC3,0xFC,0x41,0xC0,0x1C,0x01,0x80,0x30,0x06,0x00,0xC0,0x3C,0x0E,0xFF,0x8F,0xC0, +// '6' +0x36,0x01,0x0C,0x12,0x02,0x0F, +0x07,0xC1,0xFE,0x38,0x27,0x00,0x60,0x0C,0x00,0xCF,0x8D,0xFC,0xF8,0xEF,0x07,0xE0,0x3E,0x03,0xE0,0x36,0x03,0x70,0x77,0x8E,0x3F,0xC0,0xF8, +// '7' +0x37,0x01,0x0B,0x12,0x02,0x0F, +0xFF,0xFF,0xFC,0x03,0x00,0x60,0x1C,0x03,0x00,0x60,0x18,0x03,0x00,0xE0,0x18,0x03,0x00,0xC0,0x18,0x07,0x00,0xC0,0x18,0x06,0x00, +// '8' +0x38,0x01,0x0C,0x12,0x02,0x0F, +0x1F,0x87,0xFE,0x70,0xEC,0x03,0xC0,0x3C,0x03,0xC0,0x37,0x0E,0x3F,0xC3,0xFC,0x70,0xEC,0x03,0xC0,0x3C,0x03,0xC0,0x37,0x0E,0x7F,0xE1,0xF8, +// '9' +0x39,0x01,0x0C,0x12,0x02,0x0F, +0x1F,0x03,0xFC,0x71,0xCE,0x0E,0xC0,0x6C,0x07,0xC0,0x7C,0x07,0xE0,0xF7,0x1F,0x3F,0xB1,0xF3,0x00,0x30,0x06,0x00,0xE4,0x1C,0x7F,0x83,0xE0, +// ':' +0x3A,0x07,0x02,0x0C,0x03,0x08, +0xFC,0x00,0x3F, +// ';' +0x3B,0x07,0x03,0x0F,0x02,0x08, +0x6D,0x80,0x00,0x0D,0xB7,0xB0, +// '<' +0x3C,0x05,0x0F,0x0D,0x03,0x14, +0x00,0x02,0x00,0x3C,0x03,0xF0,0x3F,0x01,0xF8,0x1F,0x80,0x3C,0x00,0x7E,0x00,0x1F,0x80,0x0F,0xC0,0x03,0xF0,0x00,0xF0,0x00,0x20, +// '=' +0x3D,0x08,0x0F,0x07,0x03,0x14, +0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0x80, +// '>' +0x3E,0x05,0x0F,0x0D,0x03,0x14, +0x80,0x01,0xE0,0x01,0xF8,0x00,0x7E,0x00,0x3F,0x00,0x0F,0xC0,0x07,0x80,0x3F,0x03,0xF0,0x1F,0x81,0xF8,0x07,0x80,0x08,0x00,0x00, +// '?' +0x3F,0x01,0x09,0x12,0x02,0x0D, +0x3E,0x3F,0xB0,0xF0,0x30,0x18,0x0C,0x0C,0x0E,0x0E,0x0E,0x06,0x03,0x01,0x80,0x00,0x00,0x30,0x18,0x0C,0x00, +// '@' +0x40,0x02,0x15,0x15,0x02,0x18, +0x00,0xFC,0x00,0x3F,0xF8,0x03,0xC0,0xF0,0x38,0x01,0xC3,0x80,0x07,0x38,0x79,0x99,0x8F,0xEC,0xFC,0x71,0xE3,0xC7,0x07,0x1E,0x30,0x18,0xF1,0x80,0xC7,0x8C,0x06,0x3C,0x70,0x73,0x71,0xC7,0xB9,0x8F,0xEF,0x8E,0x1E,0x70,0x38,0x00,0x00,0xE0,0x04,0x03,0xC0,0xE0,0x0F,0xFE,0x00,0x0F,0x80,0x00, +// 'A' +0x41,0x01,0x10,0x12,0x00,0x10, +0x03,0xC0,0x03,0xC0,0x03,0xC0,0x07,0xE0,0x06,0x60,0x06,0x60,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x18,0x18,0x18,0x18,0x38,0x1C,0x3F,0xFC,0x3F,0xFC,0x60,0x06,0x60,0x06,0x60,0x06,0xC0,0x03, +// 'B' +0x42,0x01,0x0C,0x12,0x02,0x10, +0xFF,0x0F,0xFC,0xC0,0xEC,0x06,0xC0,0x6C,0x06,0xC0,0x6C,0x0C,0xFF,0x8F,0xFC,0xC0,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x06,0xFF,0xEF,0xF8, +// 'C' +0x43,0x01,0x0E,0x12,0x01,0x11, +0x07,0xE0,0x7F,0xE3,0xC1,0xDC,0x01,0x60,0x01,0x80,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0x60,0x01,0x80,0x07,0x00,0x4F,0x07,0x1F,0xF8,0x1F,0x80, +// 'D' +0x44,0x01,0x0F,0x12,0x02,0x12, +0xFF,0x81,0xFF,0xE3,0x01,0xE6,0x00,0xEC,0x00,0xD8,0x01,0xF0,0x01,0xE0,0x03,0xC0,0x07,0x80,0x0F,0x00,0x1E,0x00,0x3C,0x00,0xF8,0x01,0xB0,0x07,0x60,0x3C,0xFF,0xF1,0xFF,0x00, +// 'E' +0x45,0x01,0x0B,0x12,0x02,0x0F, +0xFF,0xFF,0xFF,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xDF,0xFB,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xFF,0xFC, +// 'F' +0x46,0x01,0x0A,0x12,0x02,0x0E, +0xFF,0xFF,0xFC,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xFF,0xBF,0xEC,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x00, +// 'G' +0x47,0x01,0x0F,0x12,0x01,0x13, +0x07,0xE0,0x3F,0xF0,0xE0,0x73,0x80,0x26,0x00,0x1C,0x00,0x30,0x00,0x60,0x00,0xC0,0x7F,0x80,0xFF,0x00,0x1E,0x00,0x36,0x00,0x6C,0x00,0xDC,0x01,0x9E,0x07,0x1F,0xFC,0x0F,0xE0, +// 'H' +0x48,0x01,0x0D,0x12,0x02,0x12, +0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xFF,0xFF,0xFF,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xC0, +// 'I' +0x49,0x01,0x02,0x12,0x02,0x07, +0xFF,0xFF,0xFF,0xFF,0xF0, +// 'J' +0x4A,0x01,0x06,0x17,0xFE,0x07, +0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x1B,0xEF,0x00, +// 'K' +0x4B,0x01,0x0F,0x12,0x02,0x10, +0xC0,0x71,0x81,0xC3,0x07,0x06,0x1C,0x0C,0x70,0x19,0xC0,0x37,0x00,0x7C,0x00,0xF8,0x01,0xB0,0x03,0x38,0x06,0x38,0x0C,0x38,0x18,0x38,0x30,0x38,0x60,0x38,0xC0,0x39,0x80,0x38, +// 'L' +0x4C,0x01,0x0B,0x12,0x02,0x0D, +0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xFF,0xFC, +// 'M' +0x4D,0x01,0x10,0x12,0x02,0x15, +0xE0,0x07,0xF0,0x0F,0xF0,0x0F,0xF8,0x1F,0xD8,0x1B,0xD8,0x1B,0xCC,0x33,0xCC,0x33,0xCC,0x33,0xC6,0x63,0xC6,0x63,0xC7,0xE3,0xC3,0xC3,0xC3,0xC3,0xC1,0x83,0xC0,0x03,0xC0,0x03,0xC0,0x03, +// 'N' +0x4E,0x01,0x0D,0x12,0x02,0x12, +0xE0,0x1F,0x80,0xFC,0x07,0xF0,0x3D,0x81,0xE6,0x0F,0x30,0x78,0xC3,0xC6,0x1E,0x18,0xF0,0xC7,0x83,0x3C,0x19,0xE0,0x6F,0x03,0x78,0x0F,0xC0,0x7E,0x01,0xC0, +// 'O' +0x4F,0x01,0x10,0x12,0x01,0x13, +0x07,0xE0,0x1F,0xF8,0x3C,0x3C,0x70,0x0E,0x60,0x06,0x60,0x06,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0x60,0x06,0x60,0x06,0x70,0x0E,0x3C,0x3C,0x1F,0xF8,0x07,0xE0, +// 'P' +0x50,0x01,0x0B,0x12,0x02,0x0E, +0xFF,0x1F,0xFB,0x07,0x60,0x3C,0x07,0x80,0xF0,0x1E,0x0E,0xFF,0xDF,0xE3,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x00, +// 'Q' +0x51,0x01,0x10,0x15,0x01,0x13, +0x07,0xE0,0x1F,0xF8,0x3C,0x3C,0x70,0x0E,0x60,0x06,0x60,0x06,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0x60,0x07,0x60,0x06,0x70,0x0E,0x3C,0x3C,0x1F,0xF8,0x07,0xF0,0x00,0x38,0x00,0x18,0x00,0x0C, +// 'R' +0x52,0x01,0x0D,0x12,0x02,0x11, +0xFF,0x07,0xFE,0x30,0x31,0x80,0xCC,0x06,0x60,0x33,0x01,0x98,0x18,0xFF,0xC7,0xFC,0x30,0x71,0x81,0x8C,0x06,0x60,0x33,0x01,0xD8,0x06,0xC0,0x36,0x00,0xC0, +// 'S' +0x53,0x01,0x0C,0x12,0x02,0x0F, +0x1F,0x87,0xFE,0x70,0x6C,0x00,0xC0,0x0C,0x00,0xC0,0x07,0x00,0x7F,0x01,0xFC,0x00,0xE0,0x07,0x00,0x30,0x03,0x00,0x3C,0x0E,0xFF,0xE3,0xF8, +// 'T' +0x54,0x01,0x0E,0x12,0x00,0x0F, +0xFF,0xFF,0xFF,0xF0,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00, +// 'U' +0x55,0x01,0x0D,0x12,0x02,0x12, +0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0D,0x80,0xCE,0x0E,0x3F,0xE0,0x7C,0x00, +// 'V' +0x56,0x01,0x10,0x12,0x00,0x10, +0xC0,0x03,0x60,0x06,0x60,0x06,0x60,0x06,0x30,0x0C,0x30,0x0C,0x38,0x1C,0x18,0x18,0x18,0x18,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x06,0x60,0x06,0x60,0x07,0x60,0x03,0xC0,0x03,0xC0,0x03,0xC0, +// 'W' +0x57,0x01,0x16,0x12,0x01,0x18, +0xC0,0x78,0x0F,0x01,0xE0,0x36,0x07,0x81,0x98,0x1E,0x06,0x60,0xEC,0x19,0x83,0x30,0x63,0x0C,0xC3,0x0C,0x33,0x0C,0x30,0xCE,0x30,0xC6,0x18,0xC1,0x98,0x66,0x06,0x61,0x98,0x19,0x86,0x60,0x6C,0x0D,0x80,0xF0,0x3C,0x03,0xC0,0xF0,0x0F,0x03,0xC0,0x38,0x07,0x00, +// 'X' +0x58,0x01,0x0F,0x12,0x01,0x11, +0x70,0x0E,0x60,0x18,0x60,0x60,0xE1,0xC0,0xC7,0x00,0xCC,0x01,0xF0,0x01,0xE0,0x03,0x80,0x07,0x80,0x1F,0x00,0x37,0x00,0xC6,0x03,0x86,0x0E,0x0E,0x18,0x0C,0x60,0x0D,0xC0,0x1C, +// 'Y' +0x59,0x01,0x0E,0x12,0x00,0x0F, +0xE0,0x1D,0x80,0x63,0x03,0x0E,0x1C,0x18,0x60,0x33,0x00,0xFC,0x01,0xE0,0x07,0x80,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00, +// 'Z' +0x5A,0x01,0x0E,0x12,0x01,0x10, +0xFF,0xFF,0xFF,0xF0,0x01,0x80,0x0E,0x00,0x70,0x01,0x80,0x0C,0x00,0x60,0x03,0x80,0x1C,0x00,0x60,0x03,0x00,0x18,0x00,0xE0,0x07,0x00,0x18,0x00,0xFF,0xFF,0xFF,0xF0, +// '[' +0x5B,0x01,0x05,0x15,0x02,0x09, +0xFF,0xF1,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0xFF,0x80, +// '\' +0x5C,0x01,0x08,0x14,0x00,0x08, +0xC0,0xE0,0x60,0x60,0x60,0x30,0x30,0x30,0x18,0x18,0x18,0x18,0x0C,0x0C,0x0C,0x06,0x06,0x06,0x07,0x03, +// ']' +0x5D,0x01,0x05,0x15,0x02,0x09, +0xFF,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC7,0xFF,0x80, +// '^' +0x5E,0x01,0x0F,0x07,0x03,0x14, +0x03,0x80,0x0F,0x80,0x3B,0x80,0xE3,0x83,0x83,0x8E,0x03,0xB8,0x03,0x80, +// '_' +0x5F,0x17,0x0C,0x02,0x00,0x0C, +0xFF,0xFF,0xFF, +// '`' +0x60,0x00,0x06,0x04,0x02,0x0C, +0x60,0xC1,0x83, +// 'a' +0x61,0x06,0x0B,0x0D,0x01,0x0E, +0x3F,0x0F,0xF9,0x03,0x00,0x30,0x06,0x3F,0xDF,0xFF,0x03,0xC0,0x78,0x1F,0x87,0xBF,0xF3,0xE6, +// 'b' +0x62,0x01,0x0C,0x12,0x02,0x0F, +0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0xF8,0xFF,0xCF,0x0E,0xE0,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xE0,0x6F,0x0E,0xFF,0xCC,0xF8, +// 'c' +0x63,0x06,0x0A,0x0D,0x01,0x0D, +0x0F,0x8F,0xF7,0x05,0x80,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x18,0x07,0x04,0xFF,0x0F,0x80, +// 'd' +0x64,0x01,0x0C,0x12,0x01,0x0F, +0x00,0x30,0x03,0x00,0x30,0x03,0x00,0x31,0xF3,0x3F,0xF7,0x0F,0x60,0x7C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x77,0x0F,0x3F,0xF1,0xF3, +// 'e' +0x65,0x06,0x0C,0x0D,0x01,0x0E, +0x0F,0x83,0xFC,0x70,0xE6,0x07,0xC0,0x3F,0xFF,0xFF,0xFC,0x00,0xC0,0x06,0x00,0x70,0x23,0xFE,0x0F,0xC0, +// 'f' +0x66,0x01,0x08,0x12,0x01,0x08, +0x0F,0x1F,0x38,0x30,0x30,0xFF,0xFF,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, +// 'g' +0x67,0x06,0x0C,0x12,0x01,0x0F, +0x1F,0x33,0xFF,0x70,0xF6,0x07,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x07,0x70,0xF3,0xFF,0x1F,0x30,0x03,0x00,0x72,0x0E,0x3F,0xC1,0xF8, +// 'h' +0x68,0x01,0x0B,0x12,0x02,0x0F, +0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x9F,0x3F,0xF7,0x87,0xE0,0x78,0x0F,0x01,0xE0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x0C, +// 'i' +0x69,0x01,0x02,0x12,0x02,0x07, +0xFC,0x3F,0xFF,0xFF,0xF0, +// 'j' +0x6A,0x01,0x05,0x17,0xFF,0x07, +0x18,0xC6,0x00,0x0C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x33,0xFB,0x80, +// 'k' +0x6B,0x01,0x0C,0x12,0x02,0x0E, +0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x1C,0xC3,0x8C,0x70,0xCE,0x0D,0xC0,0xF8,0x0F,0x80,0xDC,0x0C,0xE0,0xC7,0x0C,0x38,0xC1,0xCC,0x0E, +// 'l' +0x6C,0x01,0x02,0x12,0x02,0x06, +0xFF,0xFF,0xFF,0xFF,0xF0, +// 'm' +0x6D,0x06,0x14,0x0D,0x02,0x18, +0xCF,0x87,0xCF,0xFC,0xFE,0xF0,0xF8,0x7E,0x07,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x30, +// 'n' +0x6E,0x06,0x0B,0x0D,0x02,0x0F, +0xCF,0x9F,0xFB,0xC3,0xF0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x0F,0x01,0xE0,0x3C,0x06, +// 'o' +0x6F,0x06,0x0C,0x0D,0x01,0x0E, +0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80, +// 'p' +0x70,0x06,0x0C,0x12,0x02,0x0F, +0xCF,0x8F,0xFC,0xF0,0xEE,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x3E,0x06,0xF0,0xEF,0xFC,0xCF,0x8C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00, +// 'q' +0x71,0x06,0x0C,0x12,0x01,0x0F, +0x1F,0x33,0xFF,0x70,0xF6,0x07,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x07,0x70,0xF3,0xFF,0x1F,0x30,0x03,0x00,0x30,0x03,0x00,0x30,0x03, +// 'r' +0x72,0x06,0x08,0x0D,0x02,0x0A, +0xCF,0xFF,0xF0,0xE0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +// 's' +0x73,0x06,0x0B,0x0D,0x01,0x0C, +0x3F,0x0F,0xF3,0x82,0x60,0x0C,0x00,0xF0,0x0F,0xC0,0x3C,0x00,0xC0,0x1A,0x07,0x7F,0xC7,0xF0, +// 't' +0x74,0x02,0x08,0x11,0x00,0x09, +0x30,0x30,0x30,0x30,0xFF,0xFF,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x1F,0x0F, +// 'u' +0x75,0x06,0x0B,0x0D,0x02,0x0F, +0xC0,0x78,0x0F,0x01,0xE0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x1F,0x87,0xBF,0xF3,0xE6, +// 'v' +0x76,0x06,0x0D,0x0D,0x01,0x0F, +0xC0,0x1B,0x01,0x98,0x0C,0xC0,0x63,0x06,0x18,0x30,0x63,0x03,0x18,0x18,0xC0,0x6C,0x03,0x60,0x1F,0x00,0x70,0x00, +// 'w' +0x77,0x06,0x12,0x0D,0x01,0x14, +0xC1,0xE0,0xF0,0x78,0x36,0x1E,0x19,0x87,0x86,0x63,0x31,0x9C,0xCC,0xE3,0x33,0x30,0xCC,0xCC,0x36,0x1B,0x07,0x87,0x81,0xE1,0xE0,0x78,0x78,0x1C,0x0E,0x00, +// 'x' +0x78,0x06,0x0D,0x0D,0x01,0x0F, +0xE0,0x3B,0x83,0x8E,0x38,0x31,0x80,0xD8,0x07,0xC0,0x1C,0x01,0xF0,0x1D,0xC0,0xC6,0x0C,0x18,0xE0,0xEE,0x03,0x80, +// 'y' +0x79,0x06,0x0D,0x12,0x01,0x0F, +0xC0,0x1B,0x01,0x98,0x0C,0xE0,0xE3,0x06,0x18,0x70,0x63,0x03,0x18,0x0D,0x80,0x6C,0x03,0xE0,0x0E,0x00,0x70,0x03,0x00,0x18,0x01,0x80,0x7C,0x03,0xC0,0x00, +// 'z' +0x7A,0x06,0x0B,0x0D,0x01,0x0D, +0xFF,0xFF,0xFC,0x03,0x00,0xE0,0x38,0x0E,0x03,0x80,0xE0,0x38,0x0E,0x01,0x80,0x7F,0xFF,0xFE, +// '{' +0x7B,0x01,0x09,0x16,0x03,0x0F, +0x03,0x83,0xC3,0x81,0x80,0xC0,0x60,0x30,0x18,0x0C,0x0E,0x3E,0x1F,0x01,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0xC0,0x78,0x1C, +// '|' +0x7C,0x01,0x02,0x18,0x03,0x08, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +// '}' +0x7D,0x01,0x09,0x16,0x03,0x0F, +0xE0,0x78,0x0E,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0E,0x03,0xE1,0xF1,0xC0,0xC0,0x60,0x30,0x18,0x0C,0x06,0x07,0x0F,0x07,0x00, +// '~' +0x7E,0x09,0x0F,0x05,0x03,0x14, +0x00,0x00,0x7C,0x05,0xFE,0x1E,0x1F,0xE0,0x0F,0x80, + +// Terminator +0xFF +}; diff --git a/components/epaper/EPD.c b/components/epaper/EPD.c new file mode 100644 index 0000000..e8b74e7 --- /dev/null +++ b/components/epaper/EPD.c @@ -0,0 +1,2322 @@ +/* EPD library + * + * Author: LoBo (loboris@gmail.com, loboris.github) + * + * Module supporting SPI ePaper displays +*/ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "time.h" +#include +#include "rom/tjpgd.h" +#include "esp_heap_alloc_caps.h" +#include "EPD.h" +#include "EPDspi.h" +#include "rom/tjpgd.h" + + +#define DEG_TO_RAD 0.01745329252 +#define RAD_TO_DEG 57.295779513 +#define deg_to_rad 0.01745329252 + 3.14159265359 +#define swap(a, b) { int16_t t = a; a = b; b = t; } +#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) + +#if !defined(max) +#define max(A,B) ( (A) > (B) ? (A):(B)) +#endif + +#if !defined(min) +#define min(A,B) ( (A) < (B) ? (A):(B)) +#endif + +// Embedded fonts +extern uint8_t tft_SmallFont[]; +extern uint8_t tft_DefaultFont[]; +extern uint8_t tft_Dejavu18[]; +extern uint8_t tft_Dejavu24[]; +extern uint8_t tft_Ubuntu16[]; +extern uint8_t tft_Comic24[]; +extern uint8_t tft_minya24[]; +extern uint8_t tft_tooney32[]; + +// ============================================================== +// ==== Set default values of global variables ================== + +uint8_t orientation = LANDSCAPE_0; // screen orientation +uint16_t font_rotate = 0; // font rotation +uint8_t font_transparent = 0; +uint8_t font_forceFixed = 0; +uint8_t text_wrap = 0; // character wrapping to new line +color_t _fg = EPD_BLACK; +color_t _bg = EPD_WHITE; + +float _angleOffset = DEFAULT_ANGLE_OFFSET; + +int EPD_X = 0; +int EPD_Y = 0; + +dispWin_t dispWin = { + .x1 = 0, + .y1 = 0, + .x2 = EPD_DISPLAY_WIDTH-1, + .y2 = EPD_DISPLAY_HEIGHT-1, +}; + +Font_t cfont = { + .font = tft_DefaultFont, + .x_size = 0, + .y_size = 0x0B, + .offset = 0, + .numchars = 95, + .bitmap = 1, +}; + +uint8_t font_line_space = 0; +uint8_t image_debug = 0; + +// ============================================================== + + +typedef struct { + uint8_t charCode; + int adjYOffset; + int width; + int height; + int xOffset; + int xDelta; + uint16_t dataPtr; +} propFont; + +static dispWin_t dispWinTemp; + +static uint8_t *userfont = NULL; +static int EPD_OFFSET = 0; +static propFont fontChar; +static float _arcAngleMax = DEFAULT_ARC_ANGLE_MAX; + + + +// ============================================================================================================== +//---------------------------------------------- +static void drawPixel(int x, int y, uint8_t val) +{ + if (orientation == LANDSCAPE_180) { + x = _width - x - 1; + y = _height - y - 1; + } + if (_gs) { + val &= 0x0F; + //if (gs_drawBuff[(y * _width) + x] != val) { + gs_drawBuff[(y * _width) + x] = val; + gs_used_shades |= (1<> 3)) + (y>>3)]; + uint8_t new_val = buf_val; + if (val) new_val &= (0x80 >> (y % 8)) ^ 0xFF; + else new_val |= (0x80 >> (y % 8)); + //if (new_val != buf_val) drawBuff[(x * (_height>>3)) + (y>>3)] = new_val; + drawBuff[(x * (_height>>3)) + (y>>3)] = new_val; + } +} + +//------------------------------------------------------------------------- +static void EPD_pushColorRep(int x1, int y1, int x2, int y2, color_t color) +{ + if (_gs == 0) color &= 0x01; + else color &= 0x0F; + for (int y=y1; y<=y2; y++) { + for (int x = x1; x<=x2; x++){ + drawPixel(x, y, color); + } + } +} + +/* +//--------------------------------------------------------------------------------------- +static void copyBuff(int x1, int y1, int x2, int y2, uint8_t *src_buf, uint8_t *dest_buf) +{ + if + uint8_t buf_val1 = (src_buf[(x1 * (EPD_DISPLAY_HEIGHT>>3)) + (y1>>3)]) ; + uint8_t val = 0x80 >> (y1 % 8); + if (val) buf_val &= (0x80 >> (y % 8)) ^ 0xFF; + else buf_val |= (0x80 >> (y % 8)); + + drawBuff[(x * (EPD_DISPLAY_HEIGHT>>3)) + (y>>3)] = buf_val; +} +*/ + +// ============================================================================================================== + +// ========================================================================= +// ** All drawings are clipped to 'dispWin' ** +// ** All x,y coordinates in public functions are relative to clip window ** +// =========== : Public functions +// ----------- : Local functions +// ========================================================================= + + +// Compare two colors; return 0 if equal +//============================================ +int EPD_compare_colors(color_t c1, color_t c2) +{ + if (c1 != c2) return 1; + + return 0; +} + +// draw color pixel on screen +//----------------------------------------------------------- +static void _drawPixel(int16_t x, int16_t y, color_t color) { + + if ((x < dispWin.x1) || (y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return; + drawPixel(x, y, color); +} + +//======================================================= +void EPD_drawPixel(int16_t x, int16_t y, color_t color) { + + _drawPixel(x+dispWin.x1, y+dispWin.y1, color); +} + +//-------------------------------------------------------------------------- +static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) { + // clipping + if ((x < dispWin.x1) || (x > dispWin.x2) || (y > dispWin.y2)) return; + if (y < dispWin.y1) { + h -= (dispWin.y1 - y); + y = dispWin.y1; + } + if (h < 0) h = 0; + if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1; + if (h == 0) h = 1; + EPD_pushColorRep(x, y, x, y+h-1, color); +} + +//-------------------------------------------------------------------------- +static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) { + // clipping + if ((y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return; + if (x < dispWin.x1) { + w -= (dispWin.x1 - x); + x = dispWin.x1; + } + if (w < 0) w = 0; + if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1; + if (w == 0) w = 1; + + EPD_pushColorRep(x, y, x+w-1, y, color); +} + +//====================================================================== +void EPD_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) { + _drawFastVLine(x+dispWin.x1, y+dispWin.y1, h, color); +} + +//====================================================================== +void EPD_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) { + _drawFastHLine(x+dispWin.x1, y+dispWin.y1, w, color); +} + +// Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer this uses +// the eficient FastH/V Line draw routine for segments of 2 pixels or more +//---------------------------------------------------------------------------------- +static void _drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color) +{ + if (x0 == x1) { + if (y0 <= y1) _drawFastVLine(x0, y0, y1-y0, color); + else _drawFastVLine(x0, y1, y0-y1, color); + return; + } + if (y0 == y1) { + if (x0 <= x1) _drawFastHLine(x0, y0, x1-x0, color); + else _drawFastHLine(x1, y0, x0-x1, color); + return; + } + + int steep = 0; + if (abs(y1 - y0) > abs(x1 - x0)) steep = 1; + if (steep) { + swap(x0, y0); + swap(x1, y1); + } + if (x0 > x1) { + swap(x0, x1); + swap(y0, y1); + } + + int16_t dx = x1 - x0, dy = abs(y1 - y0);; + int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; + + if (y0 < y1) ystep = 1; + + // Split into steep and not steep for FastH/V separation + if (steep) { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) _drawPixel(y0, xs, color); + else _drawFastVLine(y0, xs, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) _drawFastVLine(y0, xs, dlen, color); + } + else + { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + err += dx; + if (dlen == 1) _drawPixel(xs, y0, color); + else _drawFastHLine(xs, y0, dlen, color); + dlen = 0; y0 += ystep; xs = x0 + 1; + } + } + if (dlen) _drawFastHLine(xs, y0, dlen, color); + } +} + +//============================================================================== +void EPD_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color) +{ + _drawLine(x0+dispWin.x1, y0+dispWin.y1, x1+dispWin.x1, y1+dispWin.y1, color); +} + +// fill a rectangle +//-------------------------------------------------------------------------------- +static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) { + // clipping + if ((x >= dispWin.x2) || (y > dispWin.y2)) return; + + if (x < dispWin.x1) { + w -= (dispWin.x1 - x); + x = dispWin.x1; + } + if (y < dispWin.y1) { + h -= (dispWin.y1 - y); + y = dispWin.y1; + } + if (w < 0) w = 0; + if (h < 0) h = 0; + + if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1; + if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1; + if (w == 0) w = 1; + if (h == 0) h = 1; + EPD_pushColorRep(x, y, x+w-1, y+h-1, color); +} + +//============================================================================ +void EPD_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) { + _fillRect(x+dispWin.x1, y+dispWin.y1, w, h, color); +} + +//================================== +void EPD_fillScreen(color_t color) { + color &= 0x0F; + memset(disp_buffer, ((color&1) ? 0 : 0xFF), _width * (_height/8)); + memset(gs_disp_buffer, color, _width * _height); + gs_used_shades = 0; +} + +//================================== +void EPD_fillWindow(color_t color) { + EPD_pushColorRep(dispWin.x1, dispWin.y1, dispWin.x2, dispWin.y2, 0xFF); +} + +// ^^^============= Basics drawing functions ================================^^^ + + +// ================ Graphics drawing functions ================================== + +//----------------------------------------------------------------------------------- +static void _drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) { + _drawFastHLine(x1,y1,w, color); + _drawFastVLine(x1+w-1,y1,h, color); + _drawFastHLine(x1,y1+h-1,w, color); + _drawFastVLine(x1,y1,h, color); +} + +//=============================================================================== +void EPD_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) { + _drawRect(x1+dispWin.x1, y1+dispWin.y1, w, h, color); +} + +//------------------------------------------------------------------------------------------------- +static void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, color_t color) +{ + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (cornername & 0x4) { + _drawPixel(x0 + x, y0 + y, color); + _drawPixel(x0 + y, y0 + x, color); + } + if (cornername & 0x2) { + _drawPixel(x0 + x, y0 - y, color); + _drawPixel(x0 + y, y0 - x, color); + } + if (cornername & 0x8) { + _drawPixel(x0 - y, y0 + x, color); + _drawPixel(x0 - x, y0 + y, color); + } + if (cornername & 0x1) { + _drawPixel(x0 - y, y0 - x, color); + _drawPixel(x0 - x, y0 - y, color); + } + } +} + +// Used to do circles and roundrects +//---------------------------------------------------------------------------------------------------------------- +static void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, color_t color) +{ + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + int16_t ylm = x0 - r; + + while (x < y) { + if (f >= 0) { + if (cornername & 0x1) _drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color); + if (cornername & 0x2) _drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color); + ylm = x0 - y; + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + if ((x0 - x) > ylm) { + if (cornername & 0x1) _drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color); + if (cornername & 0x2) _drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color); + } + } +} + +// Draw a rounded rectangle +//============================================================================================= +void EPD_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color) +{ + x += dispWin.x1; + y += dispWin.y1; + + // smarter version + _drawFastHLine(x + r, y, w - 2 * r, color); // Top + _drawFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom + _drawFastVLine(x, y + r, h - 2 * r, color); // Left + _drawFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right + + // draw four corners + drawCircleHelper(x + r, y + r, r, 1, color); + drawCircleHelper(x + w - r - 1, y + r, r, 2, color); + drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color); + drawCircleHelper(x + r, y + h - r - 1, r, 8, color); +} + +// Fill a rounded rectangle +//============================================================================================= +void EPD_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color) +{ + x += dispWin.x1; + y += dispWin.y1; + + // smarter version + _fillRect(x + r, y, w - 2 * r, h, color); + + // draw four corners + fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color); + fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color); +} + + + + +//----------------------------------------------------------------------------------------------- +static void _drawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t length, color_t color) +{ + _drawLine( + x, + y, + x + length * cos((angle + _angleOffset) * DEG_TO_RAD), + y + length * sin((angle + _angleOffset) * DEG_TO_RAD), color); +} + +//--------------------------------------------------------------------------------------------------------------- +static void _DrawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, color_t color) +{ + _drawLine( + x + start * cos((angle + _angleOffset) * DEG_TO_RAD), + y + start * sin((angle + _angleOffset) * DEG_TO_RAD), + x + (start + length) * cos((angle + _angleOffset) * DEG_TO_RAD), + y + (start + length) * sin((angle + _angleOffset) * DEG_TO_RAD), color); +} + +//=========================================================================================================== +void EPD_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color) +{ + x += dispWin.x1; + y += dispWin.y1; + + if (start == 0) _drawLineByAngle(x, y, angle, len, color); + else _DrawLineByAngle(x, y, angle, start, len, color); +} + + +// Draw a triangle +//-------------------------------------------------------------------------------------------------------------------- +static void _drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color) +{ + _drawLine(x0, y0, x1, y1, color); + _drawLine(x1, y1, x2, y2, color); + _drawLine(x2, y2, x0, y0, color); +} + +//================================================================================================================ +void EPD_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color) +{ + x0 += dispWin.x1; + y0 += dispWin.y1; + x1 += dispWin.x1; + y1 += dispWin.y1; + x2 += dispWin.x1; + y2 += dispWin.y1; + + _drawLine(x0, y0, x1, y1, color); + _drawLine(x1, y1, x2, y2, color); + _drawLine(x2, y2, x0, y0, color); +} + +// Fill a triangle +//-------------------------------------------------------------------------------------------------------------------- +static void _fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color) +{ + int16_t a, b, y, last; + + // Sort coordinates by Y order (y2 >= y1 >= y0) + if (y0 > y1) { + swap(y0, y1); swap(x0, x1); + } + if (y1 > y2) { + swap(y2, y1); swap(x2, x1); + } + if (y0 > y1) { + swap(y0, y1); swap(x0, x1); + } + + if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing + a = b = x0; + if(x1 < a) a = x1; + else if(x1 > b) b = x1; + if(x2 < a) a = x2; + else if(x2 > b) b = x2; + _drawFastHLine(a, y0, b-a+1, color); + return; + } + + int16_t + dx01 = x1 - x0, + dy01 = y1 - y0, + dx02 = x2 - x0, + dy02 = y2 - y0, + dx12 = x2 - x1, + dy12 = y2 - y1; + int32_t + sa = 0, + sb = 0; + + // For upper part of triangle, find scanline crossings for segments + // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 + // is included here (and second loop will be skipped, avoiding a /0 + // error there), otherwise scanline y1 is skipped here and handled + // in the second loop...which also avoids a /0 error here if y0=y1 + // (flat-topped triangle). + if(y1 == y2) last = y1; // Include y1 scanline + else last = y1-1; // Skip it + + for(y=y0; y<=last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + /* longhand: + a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if(a > b) swap(a,b); + _drawFastHLine(a, y, b-a+1, color); + } + + // For lower part of triangle, find scanline crossings for segments + // 0-2 and 1-2. This loop is skipped if y1=y2. + sa = dx12 * (y - y1); + sb = dx02 * (y - y0); + for(; y<=y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + /* longhand: + a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if(a > b) swap(a,b); + _drawFastHLine(a, y, b-a+1, color); + } +} + +//================================================================================================================ +void EPD_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color) +{ + _fillTriangle( + x0 + dispWin.x1, y0 + dispWin.y1, + x1 + dispWin.x1, y1 + dispWin.y1, + x2 + dispWin.x1, y2 + dispWin.y1, + color); +} + +//==================================================================== +void EPD_drawCircle(int16_t x, int16_t y, int radius, color_t color) { + x += dispWin.x1; + y += dispWin.y1; + int f = 1 - radius; + int ddF_x = 1; + int ddF_y = -2 * radius; + int x1 = 0; + int y1 = radius; + + _drawPixel(x, y + radius, color); + _drawPixel(x, y - radius, color); + _drawPixel(x + radius, y, color); + _drawPixel(x - radius, y, color); + while(x1 < y1) { + if (f >= 0) { + y1--; + ddF_y += 2; + f += ddF_y; + } + x1++; + ddF_x += 2; + f += ddF_x; + _drawPixel(x + x1, y + y1, color); + _drawPixel(x - x1, y + y1, color); + _drawPixel(x + x1, y - y1, color); + _drawPixel(x - x1, y - y1, color); + _drawPixel(x + y1, y + x1, color); + _drawPixel(x - y1, y + x1, color); + _drawPixel(x + y1, y - x1, color); + _drawPixel(x - y1, y - x1, color); + } +} + +//==================================================================== +void EPD_fillCircle(int16_t x, int16_t y, int radius, color_t color) { + x += dispWin.x1; + y += dispWin.y1; + + _drawFastVLine(x, y-radius, 2*radius+1, color); + fillCircleHelper(x, y, radius, 3, 0, color); +} + +//---------------------------------------------------------------------------------------------------------------- +static void _draw_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option) +{ + // upper right + if ( option & EPD_ELLIPSE_UPPER_RIGHT ) _drawPixel(x0 + x, y0 - y, color); + // upper left + if ( option & EPD_ELLIPSE_UPPER_LEFT ) _drawPixel(x0 - x, y0 - y, color); + // lower right + if ( option & EPD_ELLIPSE_LOWER_RIGHT ) _drawPixel(x0 + x, y0 + y, color); + // lower left + if ( option & EPD_ELLIPSE_LOWER_LEFT ) _drawPixel(x0 - x, y0 + y, color); +} + +//===================================================================================================== +void EPD_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option) +{ + x0 += dispWin.x1; + y0 += dispWin.y1; + + uint16_t x, y; + int32_t xchg, ychg; + int32_t err; + int32_t rxrx2; + int32_t ryry2; + int32_t stopx, stopy; + + rxrx2 = rx; + rxrx2 *= rx; + rxrx2 *= 2; + + ryry2 = ry; + ryry2 *= ry; + ryry2 *= 2; + + x = rx; + y = 0; + + xchg = 1; + xchg -= rx; + xchg -= rx; + xchg *= ry; + xchg *= ry; + + ychg = rx; + ychg *= rx; + + err = 0; + + stopx = ryry2; + stopx *= rx; + stopy = 0; + + while( stopx >= stopy ) { + _draw_ellipse_section(x, y, x0, y0, color, option); + y++; + stopy += rxrx2; + err += ychg; + ychg += rxrx2; + if ( 2*err+xchg > 0 ) { + x--; + stopx -= ryry2; + err += xchg; + xchg += ryry2; + } + } + + x = 0; + y = ry; + + xchg = ry; + xchg *= ry; + + ychg = 1; + ychg -= ry; + ychg -= ry; + ychg *= rx; + ychg *= rx; + + err = 0; + + stopx = 0; + + stopy = rxrx2; + stopy *= ry; + + while( stopx <= stopy ) { + _draw_ellipse_section(x, y, x0, y0, color, option); + x++; + stopx += ryry2; + err += xchg; + xchg += ryry2; + if ( 2*err+ychg > 0 ) { + y--; + stopy -= rxrx2; + err += ychg; + ychg += rxrx2; + } + } +} + +//----------------------------------------------------------------------------------------------------------------------- +static void _draw_filled_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option) +{ + // upper right + if ( option & EPD_ELLIPSE_UPPER_RIGHT ) _drawFastVLine(x0+x, y0-y, y+1, color); + // upper left + if ( option & EPD_ELLIPSE_UPPER_LEFT ) _drawFastVLine(x0-x, y0-y, y+1, color); + // lower right + if ( option & EPD_ELLIPSE_LOWER_RIGHT ) _drawFastVLine(x0+x, y0, y+1, color); + // lower left + if ( option & EPD_ELLIPSE_LOWER_LEFT ) _drawFastVLine(x0-x, y0, y+1, color); +} + +//===================================================================================================== +void EPD_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option) +{ + x0 += dispWin.x1; + y0 += dispWin.y1; + + uint16_t x, y; + int32_t xchg, ychg; + int32_t err; + int32_t rxrx2; + int32_t ryry2; + int32_t stopx, stopy; + + rxrx2 = rx; + rxrx2 *= rx; + rxrx2 *= 2; + + ryry2 = ry; + ryry2 *= ry; + ryry2 *= 2; + + x = rx; + y = 0; + + xchg = 1; + xchg -= rx; + xchg -= rx; + xchg *= ry; + xchg *= ry; + + ychg = rx; + ychg *= rx; + + err = 0; + + stopx = ryry2; + stopx *= rx; + stopy = 0; + + while( stopx >= stopy ) { + _draw_filled_ellipse_section(x, y, x0, y0, color, option); + y++; + stopy += rxrx2; + err += ychg; + ychg += rxrx2; + if ( 2*err+xchg > 0 ) { + x--; + stopx -= ryry2; + err += xchg; + xchg += ryry2; + } + } + + x = 0; + y = ry; + + xchg = ry; + xchg *= ry; + + ychg = 1; + ychg -= ry; + ychg -= ry; + ychg *= rx; + ychg *= rx; + + err = 0; + + stopx = 0; + + stopy = rxrx2; + stopy *= ry; + + while( stopx <= stopy ) { + _draw_filled_ellipse_section(x, y, x0, y0, color, option); + x++; + stopx += ryry2; + err += xchg; + xchg += ryry2; + if ( 2*err+ychg > 0 ) { + y--; + stopy -= rxrx2; + err += ychg; + ychg += rxrx2; + } + } +} + + +// ==== ARC DRAWING =================================================================== + +//--------------------------------------------------------------------------------------------------------------------------------- +static void _fillArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, color_t color) +{ + //float sslope = (float)cos_lookup(start) / (float)sin_lookup(start); + //float eslope = (float)cos_lookup(end) / (float)sin_lookup(end); + float sslope = (cos(start/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(start/_arcAngleMax * 2 * PI) * _arcAngleMax) ; + float eslope = (cos(end/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(end/_arcAngleMax * 2 * PI) * _arcAngleMax); + + if (end == 360) eslope = -1000000; + + int ir2 = (radius - thickness) * (radius - thickness); + int or2 = radius * radius; + + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + int x2 = x * x; + int y2 = y * y; + + if ( + (x2 + y2 < or2 && x2 + y2 >= ir2) && + ( + (y > 0 && start < 180 && x <= y * sslope) || + (y < 0 && start > 180 && x >= y * sslope) || + (y < 0 && start <= 180) || + (y == 0 && start <= 180 && x < 0) || + (y == 0 && start == 0 && x > 0) + ) && + ( + (y > 0 && end < 180 && x >= y * eslope) || + (y < 0 && end > 180 && x <= y * eslope) || + (y > 0 && end >= 180) || + (y == 0 && end >= 180 && x < 0) || + (y == 0 && start == 0 && x > 0) + ) + ) + _drawPixel(cx+x, cy+y, color); + } + } +} + + +//=========================================================================================================================== +void EPD_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor) +{ + cx += dispWin.x1; + cy += dispWin.y1; + + if (th < 1) th = 1; + if (th > r) th = r; + + int f = EPD_compare_colors(fillcolor, color); + + float astart = fmodf(start, _arcAngleMax); + float aend = fmodf(end, _arcAngleMax); + + astart += _angleOffset; + aend += _angleOffset; + + if (astart < 0) astart += (float)360; + if (aend < 0) aend += (float)360; + + if (aend == 0) aend = (float)360; + + if (astart > aend) { + _fillArcOffsetted(cx, cy, r, th, astart, _arcAngleMax, fillcolor); + _fillArcOffsetted(cx, cy, r, th, 0, aend, fillcolor); + if (f) { + _fillArcOffsetted(cx, cy, r, 1, astart, _arcAngleMax, color); + _fillArcOffsetted(cx, cy, r, 1, 0, aend, color); + _fillArcOffsetted(cx, cy, r-th, 1, astart, _arcAngleMax, color); + _fillArcOffsetted(cx, cy, r-th, 1, 0, aend, color); + } + } + else { + _fillArcOffsetted(cx, cy, r, th, astart, aend, fillcolor); + if (f) { + _fillArcOffsetted(cx, cy, r, 1, astart, aend, color); + _fillArcOffsetted(cx, cy, r-th, 1, astart, aend, color); + } + } + if (f) { + _drawLine(cx + (r-th) * cos(astart * DEG_TO_RAD), cy + (r-th) * sin(astart * DEG_TO_RAD), + cx + (r-1) * cos(astart * DEG_TO_RAD), cy + (r-1) * sin(astart * DEG_TO_RAD), color); + _drawLine(cx + (r-th) * cos(aend * DEG_TO_RAD), cy + (r-th) * sin(aend * DEG_TO_RAD), + cx + (r-1) * cos(aend * DEG_TO_RAD), cy + (r-1) * sin(aend * DEG_TO_RAD), color); + } +} + +//============================================================================================================= +void EPD_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int rot, uint8_t th) +{ + cx += dispWin.x1; + cy += dispWin.y1; + + int deg = rot - _angleOffset; + int f = EPD_compare_colors(fill, color); + + if (sides < MIN_POLIGON_SIDES) sides = MIN_POLIGON_SIDES; // This ensures the minimum side number + if (sides > MAX_POLIGON_SIDES) sides = MAX_POLIGON_SIDES; // This ensures the maximum side number + + int Xpoints[sides], Ypoints[sides]; // Set the arrays based on the number of sides entered + int rads = 360 / sides; // This equally spaces the points. + + for (int idx = 0; idx < sides; idx++) { + Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * diameter; + Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * diameter; + } + + // Draw the polygon on the screen. + if (f) { + for(int idx = 0; idx < sides; idx++) { + if((idx+1) < sides) _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], fill); + else _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], fill); + } + } + + if (th) { + for (int n=0; n 0) { + for (int idx = 0; idx < sides; idx++) { + Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * (diameter-n); + Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * (diameter-n); + } + } + for(int idx = 0; idx < sides; idx++) { + if( (idx+1) < sides) + _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], color); // draw the lines + else + _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], color); // finishes the last line to close up the polygon. + } + } + } +} + +/* +// Similar to the Polygon function. +//===================================================================================== +void EPD_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor) +{ + cx += dispWin.x1; + cy += dispWin.y1; + + factor = constrain(factor, 1.0, 4.0); + uint8_t sides = 5; + uint8_t rads = 360 / sides; + + int Xpoints_O[sides], Ypoints_O[sides], Xpoints_I[sides], Ypoints_I[sides];//Xpoints_T[5], Ypoints_T[5]; + + for(int idx = 0; idx < sides; idx++) { + // makes the outer points + Xpoints_O[idx] = cx + sin((float)(idx*rads + 72) * deg_to_rad) * diameter; + Ypoints_O[idx] = cy + cos((float)(idx*rads + 72) * deg_to_rad) * diameter; + // makes the inner points + Xpoints_I[idx] = cx + sin((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor); + // 36 is half of 72, and this will allow the inner and outer points to line up like a triangle. + Ypoints_I[idx] = cy + cos((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor); + } + + for(int idx = 0; idx < sides; idx++) { + if((idx+1) < sides) { + if(fill) {// this part below should be self explanatory. It fills in the star. + _fillTriangle(cx,cy,Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color); + _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color); + } + else { + _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color); + _drawLine(Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color); + } + } + else { + if(fill) { + _fillTriangle(cx,cy,Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color); + _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color); + } + else { + _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color); + _drawLine(Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color); + } + } + } +} +*/ + +// ================ Font and string functions ================================== + +//-------------------------------------------------------- +static int load_file_font(const char * fontfile, int info) +{ + int err = 0; + char err_msg[256] = {'\0'}; + + if (userfont != NULL) { + free(userfont); + userfont = NULL; + } + + struct stat sb; + + // Open the file + FILE *fhndl = fopen(fontfile, "r"); + if (!fhndl) { + sprintf(err_msg, "Error opening font file '%s'", fontfile); + err = 1; + goto exit; + } + + // Get file size + if (stat(fontfile, &sb) != 0) { + sprintf(err_msg, "Error getting font file size"); + err = 2; + goto exit; + } + int fsize = sb.st_size; + if (fsize < 30) { + sprintf(err_msg, "Error getting font file size"); + err = 3; + goto exit; + } + + userfont = malloc(fsize+4); + if (userfont == NULL) { + sprintf(err_msg, "Font memory allocation error"); + fclose(fhndl); + err = 4; + goto exit; + } + + int read = fread(userfont, 1, fsize, fhndl); + + fclose(fhndl); + + if (read != fsize) { + sprintf(err_msg, "Font read error"); + err = 5; + goto exit; + } + + userfont[read] = 0; + if (strstr((char *)(userfont+read-8), "RPH_font") == NULL) { + sprintf(err_msg, "Font ID not found"); + err = 6; + goto exit; + } + + // Check size + int size = 0; + int numchar = 0; + int width = userfont[0]; + int height = userfont[1]; + uint8_t first = 255; + uint8_t last = 0; + //int offst = 0; + int pminwidth = 255; + int pmaxwidth = 0; + + if (width != 0) { + // Fixed font + numchar = userfont[3]; + first = userfont[2]; + last = first + numchar - 1; + size = ((width * height * numchar) / 8) + 4; + } + else { + // Proportional font + size = 4; // point at first char data + uint8_t charCode; + int charwidth; + + do { + charCode = userfont[size]; + charwidth = userfont[size+2]; + + if (charCode != 0xFF) { + numchar++; + if (charwidth != 0) size += ((((charwidth * userfont[size+3])-1) / 8) + 7); + else size += 6; + + if (info) { + if (charwidth > pmaxwidth) pmaxwidth = charwidth; + if (charwidth < pminwidth) pminwidth = charwidth; + if (charCode < first) first = charCode; + if (charCode > last) last = charCode; + } + } + else size++; + } while ((size < (read-8)) && (charCode != 0xFF)); + } + + if (size != (read-8)) { + sprintf(err_msg, "Font size error: found %d expected %d)", size, (read-8)); + err = 7; + goto exit; + } + + if (info) { + if (width != 0) { + printf("Fixed width font:\r\n size: %d width: %d height: %d characters: %d (%d~%d)", + size, width, height, numchar, first, last); + } + else { + printf("Proportional font:\r\n size: %d width: %d~%d height: %d characters: %d (%d~%d)\n", + size, pminwidth, pmaxwidth, height, numchar, first, last); + } + } + +exit: + if (err) { + if (userfont) { + free(userfont); + userfont = NULL; + } + if (info) printf("Error: %d [%s]\r\n", err, err_msg); + } + return err; +} + +//------------------------------------------------ +int compile_font_file(char *fontfile, uint8_t dbg) +{ + int err = 0; + char err_msg[128] = {'\0'}; + char outfile[128] = {'\0'}; + size_t len; + struct stat sb; + FILE *ffd = NULL; + FILE *ffd_out = NULL; + char *sourcebuf = NULL; + + len = strlen(fontfile); + + // check here that filename end with ".c". + if ((len < 3) || (len > 125) || (strcmp(fontfile + len - 2, ".c") != 0)) { + sprintf(err_msg, "not a .c file"); + err = 1; + goto exit; + } + + sprintf(outfile, "%s", fontfile); + sprintf(outfile+strlen(outfile)-1, "fon"); + + // Open the source file + if (stat(fontfile, &sb) != 0) { + sprintf(err_msg, "Error opening source file '%s'", fontfile); + err = 2; + goto exit; + } + // Open the file + ffd = fopen(fontfile, "rb"); + if (!ffd) { + sprintf(err_msg, "Error opening source file '%s'", fontfile); + err = 3; + goto exit; + } + + // Open the font file + ffd_out= fopen(outfile, "wb"); + if (!ffd_out) { + sprintf(err_msg, "error opening destination file"); + err = 4; + goto exit; + } + + // Get file size + int fsize = sb.st_size; + if (fsize <= 0) { + sprintf(err_msg, "source file size error"); + err = 5; + goto exit; + } + + sourcebuf = malloc(fsize+4); + if (sourcebuf == NULL) { + sprintf(err_msg, "memory allocation error"); + err = 6; + goto exit; + } + char *fbuf = sourcebuf; + + int rdsize = fread(fbuf, 1, fsize, ffd); + fclose(ffd); + ffd = NULL; + + if (rdsize != fsize) { + sprintf(err_msg, "error reading from source file"); + err = 7; + goto exit; + } + + *(fbuf+rdsize) = '\0'; + + fbuf = strchr(fbuf, '{'); // beginning of font data + char *fend = strstr(fbuf, "};"); // end of font data + + if ((fbuf == NULL) || (fend == NULL) || ((fend-fbuf) < 22)) { + sprintf(err_msg, "wrong source file format"); + err = 8; + goto exit; + } + + fbuf++; + *fend = '\0'; + char hexstr[5] = {'\0'}; + int lastline = 0; + + fbuf = strstr(fbuf, "0x"); + int size = 0; + char *nextline; + char *numptr; + + int bptr = 0; + + while ((fbuf != NULL) && (fbuf < fend) && (lastline == 0)) { + nextline = strchr(fbuf, '\n'); // beginning of the next line + if (nextline == NULL) { + nextline = fend-1; + lastline++; + } + else nextline++; + + while (fbuf < nextline) { + numptr = strstr(fbuf, "0x"); + if ((numptr == NULL) || ((fbuf+4) > nextline)) numptr = strstr(fbuf, "0X"); + if ((numptr != NULL) && ((numptr+4) <= nextline)) { + fbuf = numptr; + if (bptr >= 128) { + // buffer full, write to file + if (fwrite(outfile, 1, 128, ffd_out) != 128) goto error; + bptr = 0; + size += 128; + } + memcpy(hexstr, fbuf, 4); + hexstr[4] = 0; + outfile[bptr++] = (uint8_t)strtol(hexstr, NULL, 0); + fbuf += 4; + } + else fbuf = nextline; + } + fbuf = nextline; + } + + if (bptr > 0) { + size += bptr; + if (fwrite(outfile, 1, bptr, ffd_out) != bptr) goto error; + } + + // write font ID + sprintf(outfile, "RPH_font"); + if (fwrite(outfile, 1, 8, ffd_out) != 8) goto error; + + // === Test compiled font === + sprintf(outfile, "%s", fontfile); + sprintf(outfile+strlen(outfile)-1, "fon"); + + uint8_t *uf = userfont; // save userfont pointer + userfont = NULL; + if (load_file_font(outfile, 1) == 0) { + sprintf(err_msg, "Error compiling file!"); + } + else { + free(userfont); + sprintf(err_msg, "File compiled successfully."); + } + userfont = uf; // restore userfont + + goto exit; + +error: + sprintf(err_msg, "error writing to destination file"); + err = 9; + +exit: + if (sourcebuf) free(sourcebuf); + if (ffd) fclose(ffd); + if (ffd_out) fclose(ffd_out); + + if (dbg) printf("%s\r\n", err_msg); + + return err; +} + + +// ----------------------------------------------------------------------------------------- +// Individual Proportional Font Character Format: +// ----------------------------------------------------------------------------------------- +// Character Code +// yOffset (start Y of visible pixels) +// Width (width of the visible pixels) +// Height (height of the visible pixels) +// xOffset (start X of visible pixels) +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] +// ----------------------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------------------- +// Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1) +// Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1) +//--------------------------------------------------------------------------------------------- + +//---------------------------------- +void getFontCharacters(uint8_t *buf) +{ + if (cfont.bitmap == 2) { + //For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available. + for (uint8_t n=0; n < 11; n++) { + buf[n] = n + 0x30; + } + buf[11] = '.'; + buf[12] = '-'; + buf[13] = '/'; + buf[14] = '\0'; + return; + } + + if (cfont.x_size > 0) { + for (uint8_t n=0; n < cfont.numchars; n++) { + buf[n] = cfont.offset + n; + } + buf[cfont.numchars] = '\0'; + return; + } + + uint16_t tempPtr = 4; // point at first char data + uint8_t cc, cw, ch, n; + + n = 0; + cc = cfont.font[tempPtr++]; + while (cc != 0xFF) { + cfont.numchars++; + tempPtr++; + cw = cfont.font[tempPtr++]; + ch = cfont.font[tempPtr++]; + tempPtr++; + tempPtr++; + if (cw != 0) { + // packed bits + tempPtr += (((cw * ch)-1) / 8) + 1; + } + buf[n++] = cc; + cc = cfont.font[tempPtr++]; + } + buf[n] = '\0'; +} + +// Set max width & height of the proportional font +//----------------------------- +static void getMaxWidthHeight() +{ + uint16_t tempPtr = 4; // point at first char data + uint8_t cc, cw, ch, cd, cy; + + cfont.numchars = 0; + cfont.max_x_size = 0; + + cc = cfont.font[tempPtr++]; + while (cc != 0xFF) { + cfont.numchars++; + cy = cfont.font[tempPtr++]; + cw = cfont.font[tempPtr++]; + ch = cfont.font[tempPtr++]; + tempPtr++; + cd = cfont.font[tempPtr++]; + cy += ch; + if (cw > cfont.max_x_size) cfont.max_x_size = cw; + if (cd > cfont.max_x_size) cfont.max_x_size = cd; + if (ch > cfont.y_size) cfont.y_size = ch; + if (cy > cfont.y_size) cfont.y_size = cy; + if (cw != 0) { + // packed bits + tempPtr += (((cw * ch)-1) / 8) + 1; + } + cc = cfont.font[tempPtr++]; + } + cfont.size = tempPtr; +} + +// Return the Glyph data for an individual character in the proportional font +//------------------------------------ +static uint8_t getCharPtr(uint8_t c) { + uint16_t tempPtr = 4; // point at first char data + + do { + fontChar.charCode = cfont.font[tempPtr++]; + if (fontChar.charCode == 0xFF) return 0; + + fontChar.adjYOffset = cfont.font[tempPtr++]; + fontChar.width = cfont.font[tempPtr++]; + fontChar.height = cfont.font[tempPtr++]; + fontChar.xOffset = cfont.font[tempPtr++]; + fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset); + fontChar.xDelta = cfont.font[tempPtr++]; + + if (c != fontChar.charCode && fontChar.charCode != 0xFF) { + if (fontChar.width != 0) { + // packed bits + tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1; + } + } + } while ((c != fontChar.charCode) && (fontChar.charCode != 0xFF)); + + fontChar.dataPtr = tempPtr; + if (c == fontChar.charCode) { + if (font_forceFixed > 0) { + // fix width & offset for forced fixed width + fontChar.xDelta = cfont.max_x_size; + fontChar.xOffset = (fontChar.xDelta - fontChar.width) / 2; + } + } + else return 0; + + return 1; +} + +/* +//----------------------- +static void _testFont() { + if (cfont.x_size) { + printf("FONT TEST: fixed font\r\n"); + return; + } + uint16_t tempPtr = 4; // point at first char data + uint8_t c = 0x20; + for (c=0x20; c <0xFF; c++) { + fontChar.charCode = cfont.font[tempPtr++]; + if (fontChar.charCode == 0xFF) break; + if (fontChar.charCode != c) { + printf("FONT TEST: last sequential char: %d, expected %d\r\n", fontChar.charCode, c); + break; + } + c = fontChar.charCode; + fontChar.adjYOffset = cfont.font[tempPtr++]; + fontChar.width = cfont.font[tempPtr++]; + fontChar.height = cfont.font[tempPtr++]; + fontChar.xOffset = cfont.font[tempPtr++]; + fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset); + fontChar.xDelta = cfont.font[tempPtr++]; + + if (fontChar.charCode != 0xFF) { + if (fontChar.width != 0) { + // packed bits + tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1; + } + } + } + printf("FONT TEST: W=%d H=%d last char: %d [%c]; length: %d\r\n", cfont.max_x_size, cfont.y_size, c, c, tempPtr); +} +*/ + +//=================================================== +void EPD_setFont(uint8_t font, const char *font_file) +{ + cfont.font = NULL; + + if (font == FONT_7SEG) { + cfont.bitmap = 2; + cfont.x_size = 24; + cfont.y_size = 6; + cfont.offset = 0; + cfont.color = _fg; + } + else { + if (font == USER_FONT) { + if (load_file_font(font_file, 0) != 0) cfont.font = tft_DefaultFont; + else cfont.font = userfont; + } + else if (font == DEJAVU18_FONT) cfont.font = tft_Dejavu18; + else if (font == DEJAVU24_FONT) cfont.font = tft_Dejavu24; + else if (font == UBUNTU16_FONT) cfont.font = tft_Ubuntu16; + else if (font == COMIC24_FONT) cfont.font = tft_Comic24; + else if (font == MINYA24_FONT) cfont.font = tft_minya24; + else if (font == TOONEY32_FONT) cfont.font = tft_tooney32; + else if (font == SMALL_FONT) cfont.font = tft_SmallFont; + else cfont.font = tft_DefaultFont; + + cfont.bitmap = 1; + cfont.x_size = cfont.font[0]; + cfont.y_size = cfont.font[1]; + if (cfont.x_size > 0) { + cfont.offset = cfont.font[2]; + cfont.numchars = cfont.font[3]; + cfont.size = cfont.x_size * cfont.y_size * cfont.numchars; + } + else { + cfont.offset = 4; + getMaxWidthHeight(); + } + //_testFont(); + } +} + +// ----------------------------------------------------------------------------------------- +// Individual Proportional Font Character Format: +// ----------------------------------------------------------------------------------------- +// Character Code +// yOffset (start Y of visible pixels) +// Width (width of the visible pixels) +// Height (height of the visible pixels) +// xOffset (start X of visible pixels) +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] +// ----------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +// Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1) +// Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1) +//--------------------------------------------------------------------------------------------- + +// print non-rotated proportional character +// character is already in fontChar +//---------------------------------------------- +static int printProportionalChar(int x, int y) { + uint8_t ch = 0; + int i, j, char_width; + + char_width = ((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta); + int cx, cy; + + if (!font_transparent) _fillRect(x, y, char_width+1, cfont.y_size, _bg); + + // draw Glyph + uint8_t mask = 0x80; + for (j=0; j < fontChar.height; j++) { + for (i=0; i < fontChar.width; i++) { + if (((i + (j*fontChar.width)) % 8) == 0) { + mask = 0x80; + ch = cfont.font[fontChar.dataPtr++]; + } + + if ((ch & mask) !=0) { + cx = (uint16_t)(x+fontChar.xOffset+i); + cy = (uint16_t)(y+j+fontChar.adjYOffset); + _drawPixel(cx, cy, _fg); + } + mask >>= 1; + } + } + + return char_width; +} + +// non-rotated fixed width character +//---------------------------------------------- +static void printChar(uint8_t c, int x, int y) { + uint8_t i, j, ch, fz, mask; + uint16_t k, temp, cx, cy; + + // fz = bytes per char row + fz = cfont.x_size/8; + if (cfont.x_size % 8) fz++; + + // get character position in buffer + temp = ((c-cfont.offset)*((fz)*cfont.y_size))+4; + + if (!font_transparent) _fillRect(x, y, cfont.x_size, cfont.y_size, _bg); + + for (j=0; j>= 1; + } + } + temp += (fz); + } +} + +// print rotated proportional character +// character is already in fontChar +//--------------------------------------------------- +static int rotatePropChar(int x, int y, int offset) { + uint8_t ch = 0; + double radian = font_rotate * DEG_TO_RAD; + float cos_radian = cos(radian); + float sin_radian = sin(radian); + + uint8_t mask = 0x80; + for (int j=0; j < fontChar.height; j++) { + for (int i=0; i < fontChar.width; i++) { + if (((i + (j*fontChar.width)) % 8) == 0) { + mask = 0x80; + ch = cfont.font[fontChar.dataPtr++]; + } + + int newX = (int)(x + (((offset + i) * cos_radian) - ((j+fontChar.adjYOffset)*sin_radian))); + int newY = (int)(y + (((j+fontChar.adjYOffset) * cos_radian) + ((offset + i) * sin_radian))); + + if ((ch & mask) != 0) _drawPixel(newX,newY,_fg); + else if (!font_transparent) _drawPixel(newX,newY,_bg); + + mask >>= 1; + } + } + + return fontChar.xDelta+1; +} + +// rotated fixed width character +//-------------------------------------------------------- +static void rotateChar(uint8_t c, int x, int y, int pos) { + uint8_t i,j,ch,fz,mask; + uint16_t temp; + int newx,newy; + double radian = font_rotate*0.0175; + float cos_radian = cos(radian); + float sin_radian = sin(radian); + int zz; + + if( cfont.x_size < 8 ) fz = cfont.x_size; + else fz = cfont.x_size/8; + temp=((c-cfont.offset)*((fz)*cfont.y_size))+4; + + for (j=0; j>= 1; + } + } + temp+=(fz); + } + // calculate x,y for the next char + EPD_X = (int)(x + ((pos+1) * cfont.x_size * cos_radian)); + EPD_Y = (int)(y + ((pos+1) * cfont.x_size * sin_radian)); +} + +//---------------------- +static int _7seg_width() +{ + return (2 * (2 * cfont.y_size + 1)) + cfont.x_size; +} + +//----------------------- +static int _7seg_height() +{ + return (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size); +} + +// Returns the string width in pixels. +// Useful for positions strings on the screen. +//=============================== +int EPD_getStringWidth(char* str) +{ + int strWidth = 0; + + if (cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2; // 7-segment font + else if (cfont.x_size != 0) strWidth = strlen(str) * cfont.x_size; // fixed width font + else { + // calculate the width of the string of proportional characters + char* tempStrptr = str; + while (*tempStrptr != 0) { + if (getCharPtr(*tempStrptr++)) { + strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + 1); + } + } + strWidth--; + } + return strWidth; +} + +//=============================================== +void EPD_clearStringRect(int x, int y, char *str) +{ + int w = EPD_getStringWidth(str); + int h = EPD_getfontheight(); + EPD_fillRect(x+dispWin.x1, y+dispWin.y1, w, h, _bg); +} + +//============================================================================== +/** + * bit-encoded bar position of all digits' bcd segments + * + * 6 + * +-----+ + * 3 | . | 2 + * +--5--+ + * 1 | . | 0 + * +--.--+ + * 4 + */ +static const uint16_t font_bcd[] = { + 0x200, // 0010 0000 0000 // - + 0x080, // 0000 1000 0000 // . + 0x06C, // 0100 0110 1100 // /, degree + 0x05f, // 0000 0101 1111, // 0 + 0x005, // 0000 0000 0101, // 1 + 0x076, // 0000 0111 0110, // 2 + 0x075, // 0000 0111 0101, // 3 + 0x02d, // 0000 0010 1101, // 4 + 0x079, // 0000 0111 1001, // 5 + 0x07b, // 0000 0111 1011, // 6 + 0x045, // 0000 0100 0101, // 7 + 0x07f, // 0000 0111 1111, // 8 + 0x07d, // 0000 0111 1101 // 9 + 0x900 // 1001 0000 0000 // : +}; + +//----------------------------------------------------------------------------------------------- +static void barVert(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) { + _fillTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, color); + _fillTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, color); + _fillRect(x, y+2*w+1, 2*w+1, l, color); + if (cfont.offset) { + _drawTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, outline); + _drawTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, outline); + _drawRect(x, y+2*w+1, 2*w+1, l, outline); + } +} + +//---------------------------------------------------------------------------------------------- +static void barHor(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) { + _fillTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, color); + _fillTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, color); + _fillRect(x+2*w+1, y, l, 2*w+1, color); + if (cfont.offset) { + _drawTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, outline); + _drawTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, outline); + _drawRect(x+2*w+1, y, l, 2*w+1, outline); + } +} + +//-------------------------------------------------------------------------------------------- +static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, color_t color) { + /* TODO: clipping */ + if (num < 0x2D || num > 0x3A) return; + + int16_t c = font_bcd[num-0x2D]; + int16_t d = 2*w+l+1; + + // === Clear unused segments === + /* + if (!(c & 0x001)) barVert(x+d, y+d, w, l, _bg, _bg); + if (!(c & 0x002)) barVert(x, y+d, w, l, _bg, _bg); + if (!(c & 0x004)) barVert(x+d, y, w, l, _bg, _bg); + if (!(c & 0x008)) barVert(x, y, w, l, _bg, _bg); + if (!(c & 0x010)) barHor(x, y+2*d, w, l, _bg, _bg); + if (!(c & 0x020)) barHor(x, y+d, w, l, _bg, _bg); + if (!(c & 0x040)) barHor(x, y, w, l, _bg, _bg); + + if (!(c & 0x080)) { + // low point + _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg); + if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg); + } + if (!(c & 0x100)) { + // down middle point + _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg); + if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg); + } + if (!(c & 0x800)) { + // up middle point + _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg); + if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg); + } + if (!(c & 0x200)) { + // middle, minus + _fillRect(x+2*w+1, y+d, l, 2*w+1, _bg); + if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, _bg); + } + */ + barVert(x+d, y+d, w, l, _bg, _bg); + barVert(x, y+d, w, l, _bg, _bg); + barVert(x+d, y, w, l, _bg, _bg); + barVert(x, y, w, l, _bg, _bg); + barHor(x, y+2*d, w, l, _bg, _bg); + barHor(x, y+d, w, l, _bg, _bg); + barHor(x, y, w, l, _bg, _bg); + + _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg); + _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg); + _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg); + _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg); + _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg); + _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg); + _fillRect(x+2*w+1, y+d, l, 2*w+1, _bg); + _drawRect(x+2*w+1, y+d, l, 2*w+1, _bg); + + // === Draw used segments === + if (c & 0x001) barVert(x+d, y+d, w, l, color, cfont.color); // down right + if (c & 0x002) barVert(x, y+d, w, l, color, cfont.color); // down left + if (c & 0x004) barVert(x+d, y, w, l, color, cfont.color); // up right + if (c & 0x008) barVert(x, y, w, l, color, cfont.color); // up left + if (c & 0x010) barHor(x, y+2*d, w, l, color, cfont.color); // down + if (c & 0x020) barHor(x, y+d, w, l, color, cfont.color); // middle + if (c & 0x040) barHor(x, y, w, l, color, cfont.color); // up + + if (c & 0x080) { + // low point + _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, color); + if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, cfont.color); + } + if (c & 0x100) { + // down middle point + _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, color); + if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, cfont.color); + } + if (c & 0x800) { + // up middle point + _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, color); + if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, cfont.color); + } + if (c & 0x200) { + // middle, minus + _fillRect(x+2*w+1, y+d, l, 2*w+1, color); + if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, cfont.color); + } +} +//============================================================================== + +//====================================== +void EPD_print(char *st, int x, int y) { + int stl, i, tmpw, tmph, fh; + uint8_t ch; + + if (cfont.bitmap == 0) return; // wrong font selected + + // ** Rotated strings cannot be aligned + if ((font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return; + + if ((x < LASTX) || (font_rotate == 0)) EPD_OFFSET = 0; + + if ((x >= LASTX) && (x < LASTY)) x = EPD_X + (x-LASTX); + else if (x > CENTER) x += dispWin.x1; + + if (y >= LASTY) y = EPD_Y + (y-LASTY); + else if (y > CENTER) y += dispWin.y1; + + // ** Get number of characters in string to print + stl = strlen(st); + + // ** Calculate CENTER, RIGHT or BOTTOM position + tmpw = EPD_getStringWidth(st); // string width in pixels + fh = cfont.y_size; // font height + if ((cfont.x_size != 0) && (cfont.bitmap == 2)) { + // 7-segment font + fh = (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size); // 7-seg character height + } + + if (x == RIGHT) x = dispWin.x2 - tmpw + dispWin.x1; + else if (x == CENTER) x = (((dispWin.x2 - dispWin.x1 + 1) - tmpw) / 2) + dispWin.x1; + + if (y == BOTTOM) y = dispWin.y2 - fh + dispWin.y1; + else if (y==CENTER) y = (((dispWin.y2 - dispWin.y1 + 1) - (fh/2)) / 2) + dispWin.y1; + + if (x < dispWin.x1) x = dispWin.x1; + if (y < dispWin.y1) y = dispWin.y1; + if ((x > dispWin.x2) || (y > dispWin.y2)) return; + + EPD_X = x; + EPD_Y = y; + + // ** Adjust y position + tmph = cfont.y_size; // font height + // for non-proportional fonts, char width is the same for all chars + tmpw = cfont.x_size; + if (cfont.x_size != 0) { + if (cfont.bitmap == 2) { // 7-segment font + tmpw = _7seg_width(); // character width + tmph = _7seg_height(); // character height + } + } + else EPD_OFFSET = 0; // fixed font; offset not needed + + if (( EPD_Y + tmph - 1) > dispWin.y2) return; + + int offset = EPD_OFFSET; + + for (i=0; i (dispWin.y2-tmph)) break; + EPD_X = dispWin.x1; + } + } + + else { // ==== other characters ==== + if (cfont.x_size == 0) { + // for proportional font get character data to 'fontChar' + if (getCharPtr(ch)) tmpw = fontChar.xDelta; + else continue; + } + + // check if character can be displayed in the current line + if (( EPD_X+tmpw) > (dispWin.x2)) { + if (text_wrap == 0) break; + EPD_Y += tmph + font_line_space; + if ( EPD_Y > (dispWin.y2-tmph)) break; + EPD_X = dispWin.x1; + } + + // Let's print the character + if (cfont.x_size == 0) { + // == proportional font + if (font_rotate == 0) EPD_X += printProportionalChar( EPD_X, EPD_Y) + 1; + else { + // rotated proportional font + offset += rotatePropChar(x, y, offset); + EPD_OFFSET = offset; + } + } + else { + if (cfont.bitmap == 1) { + // == fixed font + if ((ch < cfont.offset) || ((ch-cfont.offset) > cfont.numchars)) ch = cfont.offset; + if (font_rotate == 0) { + printChar(ch, EPD_X, EPD_Y); + EPD_X += tmpw; + } + else rotateChar(ch, x, y, i); + } + else if (cfont.bitmap == 2) { + // == 7-segment font == + _draw7seg( EPD_X, EPD_Y, ch, cfont.y_size, cfont.x_size, _fg); + EPD_X += (tmpw + 2); + } + } + } + } +} + + +// ================ Service functions ========================================== + +//===================================================================== +void EPD_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) +{ + dispWin.x1 = x1; + dispWin.y1 = y1; + dispWin.x2 = x2; + dispWin.y2 = y2; + + if (dispWin.x2 >= EPD_DISPLAY_WIDTH) dispWin.x2 = EPD_DISPLAY_WIDTH-1; + if (dispWin.y2 >= EPD_DISPLAY_HEIGHT) dispWin.y2 = EPD_DISPLAY_HEIGHT-1; + if (dispWin.x1 > dispWin.x2) dispWin.x1 = dispWin.x2; + if (dispWin.y1 > dispWin.y2) dispWin.y1 = dispWin.y2; +} + +//===================== +void EPD_resetclipwin() +{ + dispWin.x2 = EPD_DISPLAY_WIDTH-1; + dispWin.y2 = EPD_DISPLAY_HEIGHT-1; + dispWin.x1 = 0; + dispWin.y1 = 0; +} + +//========================================================================== +void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) { + if (cfont.bitmap != 2) return; + + if (l < 6) l = 6; + if (l > 40) l = 40; + if (w < 1) w = 1; + if (w > (l/2)) w = l/2; + if (w > 12) w = 12; + + cfont.x_size = l; + cfont.y_size = w; + cfont.offset = outline; + cfont.color = color; +} + +//========================================== +int EPD_getfontsize(int *width, int* height) +{ + if (cfont.bitmap == 1) { + if (cfont.x_size != 0) *width = cfont.x_size; // fixed width font + else *width = cfont.max_x_size; // proportional font + *height = cfont.y_size; + } + else if (cfont.bitmap == 2) { + // 7-segment font + *width = _7seg_width(); + *height = _7seg_height(); + } + else { + *width = 0; + *height = 0; + return 0; + } + return 1; +} + +//===================== +int EPD_getfontheight() +{ + if (cfont.bitmap == 1) return cfont.y_size; // Bitmap font + else if (cfont.bitmap == 2) return _7seg_height(); // 7-segment font + return 0; +} + +//==================== +void EPD_saveClipWin() +{ + dispWinTemp.x1 = dispWin.x1; + dispWinTemp.y1 = dispWin.y1; + dispWinTemp.x2 = dispWin.x2; + dispWinTemp.y2 = dispWin.y2; +} + +//======================= +void EPD_restoreClipWin() +{ + dispWin.x1 = dispWinTemp.x1; + dispWin.y1 = dispWinTemp.y1; + dispWin.x2 = dispWinTemp.x2; + dispWin.y2 = dispWinTemp.y2; +} + + +// ================ JPG SUPPORT ================================================ + +// RGB to GRAYSCALE constants +// 0.2989 0.5870 0.1140 +#define GS_FACT_R 0.2989 +#define GS_FACT_G 0.4870 +#define GS_FACT_B 0.2140 + +// User defined device identifier +typedef struct { + FILE *fhndl; // File handler for input function + int x; // image top left point X position + int y; // image top left point Y position + uint8_t *membuff; // memory buffer containing the image + uint32_t bufsize; // size of the memory buffer + uint32_t bufptr; // memory buffer current position +} JPGIODEV; + + +// User defined call-back function to input JPEG data from file +//--------------------- +static UINT tjd_input ( + JDEC* jd, // Decompression object + BYTE* buff, // Pointer to the read buffer (NULL:skip) + UINT nd // Number of bytes to read/skip from input stream +) +{ + int rb = 0; + // Device identifier for the session (5th argument of jd_prepare function) + JPGIODEV *dev = (JPGIODEV*)jd->device; + + if (buff) { // Read nd bytes from the input strem + rb = fread(buff, 1, nd, dev->fhndl); + return rb; // Returns actual number of bytes read + } + else { // Remove nd bytes from the input stream + if (fseek(dev->fhndl, nd, SEEK_CUR) >= 0) return nd; + else return 0; + } +} + +// User defined call-back function to input JPEG data from memory buffer +//------------------------- +static UINT tjd_buf_input ( + JDEC* jd, // Decompression object + BYTE* buff, // Pointer to the read buffer (NULL:skip) + UINT nd // Number of bytes to read/skip from input stream +) +{ + // Device identifier for the session (5th argument of jd_prepare function) + JPGIODEV *dev = (JPGIODEV*)jd->device; + if (!dev->membuff) return 0; + if (dev->bufptr >= (dev->bufsize + 2)) return 0; // end of stream + + if ((dev->bufptr + nd) > (dev->bufsize + 2)) nd = (dev->bufsize + 2) - dev->bufptr; + + if (buff) { // Read nd bytes from the input strem + memcpy(buff, dev->membuff + dev->bufptr, nd); + dev->bufptr += nd; + return nd; // Returns number of bytes read + } + else { // Remove nd bytes from the input stream + dev->bufptr += nd; + return nd; + } +} + +// User defined call-back function to output RGB bitmap to display device +//---------------------- +static UINT tjd_output ( + JDEC* jd, // Decompression object of current session + void* bitmap, // Bitmap data to be output + JRECT* rect // Rectangular region to output +) +{ + // Device identifier for the session (5th argument of jd_prepare function) + JPGIODEV *dev = (JPGIODEV*)jd->device; + + // ** Put the rectangular into the display device ** + int x; + int y; + int dleft, dtop, dright, dbottom; + BYTE *src = (BYTE*)bitmap; + + int left = rect->left + dev->x; + int top = rect->top + dev->y; + int right = rect->right + dev->x; + int bottom = rect->bottom + dev->y; + + if ((left > dispWin.x2) || (top > dispWin.y2)) return 1; // out of screen area, return + if ((right < dispWin.x1) || (bottom < dispWin.y1)) return 1;// out of screen area, return + + if (left < dispWin.x1) dleft = dispWin.x1; + else dleft = left; + if (top < dispWin.y1) dtop = dispWin.y1; + else dtop = top; + if (right > dispWin.x2) dright = dispWin.x2; + else dright = right; + if (bottom > dispWin.y2) dbottom = dispWin.y2; + else dbottom = bottom; + + if ((dleft > dispWin.x2) || (dtop > dispWin.y2)) return 1; // out of screen area, return + if ((dright < dispWin.x1) || (dbottom < dispWin.y1)) return 1; // out of screen area, return + + uint32_t len = ((dright-dleft+1) * (dbottom-dtop+1)); // calculate length of data + + float gs_clr = 0; + uint8_t rgb_color[3]; + uint8_t last_lvl, i; + uint8_t pix; + if ((len > 0) && (len <= JPG_IMAGE_LINE_BUF_SIZE)) { + for (y = top; y <= bottom; y++) { + for (x = left; x <= right; x++) { + // Clip to display area + if ((x >= dleft) && (y >= dtop) && (x <= dright) && (y <= dbottom)) { + // Directly convert color to 4-bit gray scale + pix = 0; + pix |= ((*src++) >> 4) & 0x08; + pix |= ((*src++) >> 5) & 0x06; + pix |= ((*src++) >> 7); + pix ^= 0x0F; + + + /* Convert rgb color to gray scale + memcpy(rgb_color, src, 3); + src += 3; + gs_clr = (GS_FACT_R * rgb_color[0]) + (GS_FACT_G * rgb_color[1]) + (GS_FACT_B * rgb_color[2]); + if (gs_clr > 255) gs_clr = 255; + // Use only 4 bits & invert + //pix = ((uint8_t)gs_clr >> 4) ^ 0x0F; + pix = (uint8_t)gs_clr; + + // Using gray scale lookup table + last_lvl = 0; + i = 0; + for (i=0; i<16; i++) { + if ((pix > last_lvl) && (pix <= lvl_buf_jpg[i])) { + pix = 15 - i; + last_lvl = lvl_buf[i]; + break; + } + last_lvl = lvl_buf[i]; + } + */ + gs_disp_buffer[(y * EPD_DISPLAY_WIDTH) + x] = pix; + gs_used_shades |= (1 << pix); + } + else src += 3; // skip + } + } + } + else { + printf("Data size error: %d jpg: (%d,%d,%d,%d) disp: (%d,%d,%d,%d)\r\n", len, left,top,right,bottom, dleft,dtop,dright,dbottom); + return 0; // stop decompression + } + + return 1; // Continue to decompression +} + +// X & Y can be < 0 ! +//================================================================================= +int EPD_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size) +{ + JPGIODEV dev; + struct stat sb; + char *work = NULL; // Pointer to the working buffer (must be 4-byte aligned) + UINT sz_work = 3800; // Size of the working buffer (must be power of 2) + JDEC jd; // Decompression object (70 bytes) + JRESULT rc; + int res = -10; + + if (fname == NULL) { + // image from buffer + dev.fhndl = NULL; + dev.membuff = buf; + dev.bufsize = size; + dev.bufptr = 0; + } + else { + // image from file + dev.membuff = NULL; + dev.bufsize = 0; + dev.bufptr = 0; + + if (stat(fname, &sb) != 0) { + if (image_debug) printf("File error: %ss\r\n", strerror(errno)); + res = -11; + goto exit; + } + + dev.fhndl = fopen(fname, "r"); + if (!dev.fhndl) { + if (image_debug) printf("Error opening file: %s\r\n", strerror(errno)); + res = -12; + goto exit; + } + } + + if (scale > 3) scale = 3; + + work = malloc(sz_work); + if (work) { + if (dev.membuff) rc = jd_prepare(&jd, tjd_buf_input, (void *)work, sz_work, &dev); + else rc = jd_prepare(&jd, tjd_input, (void *)work, sz_work, &dev); + if (rc == JDR_OK) { + if (x == CENTER) x = ((dispWin.x2 - dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + dispWin.x1; + else if (x == RIGHT) x = dispWin.x2 + 1 - (int)(jd.width >> scale); + + if (y == CENTER) y = ((dispWin.y2 - dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + dispWin.y1; + else if (y == BOTTOM) y = dispWin.y2 + 1 - (int)(jd.height >> scale); + + if (x < ((dispWin.x2-1) * -1)) x = (dispWin.x2-1) * -1; + if (y < ((dispWin.y2-1)) * -1) y = (dispWin.y2-1) * -1; + if (x > (dispWin.x2-1)) x = dispWin.x2 - 1; + if (y > (dispWin.y2-1)) y = dispWin.y2-1; + + dev.x = x; + dev.y = y; + + // Start to decode the JPEG file + rc = jd_decomp(&jd, tjd_output, scale); + + if (rc != JDR_OK) { + if (image_debug) printf("jpg decompression error %d\r\n", rc); + res = rc * -1; + } + res = 0; + if (image_debug) printf("Jpg size: %dx%d, position; %d,%d, scale: %d, bytes used: %d\r\n", jd.width, jd.height, x, y, scale, jd.sz_pool); + } + else { + if (image_debug) printf("jpg prepare error %d\r\n", rc); + res = rc * -1; + } + } + else { + if (image_debug) printf("work buffer allocation error\r\n"); + res = -13; + } + +exit: + if (work) free(work); // free work buffer + if (dev.fhndl) fclose(dev.fhndl); // close input file + return res; +} + diff --git a/components/epaper/EPD.h b/components/epaper/EPD.h new file mode 100644 index 0000000..784f8d5 --- /dev/null +++ b/components/epaper/EPD.h @@ -0,0 +1,571 @@ +/* + * High level EPD functions + * Author: LoBo 06/2017, https://github/loboris + * + */ + +#ifndef _EPD_H_ +#define _EPD_H_ + +#include +#include "EPDspi.h" + +typedef uint8_t color_t; + +typedef struct { + uint16_t x1; + uint16_t y1; + uint16_t x2; + uint16_t y2; +} dispWin_t; + +typedef struct { + uint8_t *font; + uint8_t x_size; + uint8_t y_size; + uint8_t offset; + uint16_t numchars; + uint16_t size; + uint8_t max_x_size; + uint8_t bitmap; + color_t color; +} Font_t; + + + +//========================================================================================== +// ==== Global variables =================================================================== +//========================================================================================== +uint8_t orientation; // current screen orientation +uint16_t font_rotate; // current font font_rotate angle (0~395) +uint8_t font_transparent; // if not 0 draw fonts transparent +uint8_t font_forceFixed; // if not zero force drawing proportional fonts with fixed width +uint8_t font_buffered_char; +uint8_t font_line_space; // additional spacing between text lines; added to font height +uint8_t text_wrap; // if not 0 wrap long text to the new line, else clip +color_t _fg; // current foreground color for fonts +color_t _bg; // current background for non transparent fonts +dispWin_t dispWin; // display clip window +float _angleOffset; // angle offset for arc, polygon and line by angle functions + +Font_t cfont; // Current font structure +uint8_t image_debug; + +int EPD_X; // X position of the next character after EPD_print() function +int EPD_Y; // Y position of the next character after EPD_print() function +// ========================================================================================= + + +// Buffer is created during jpeg decode for sending data +// Total size of the buffer is 2 * (JPG_IMAGE_LINE_BUF_SIZE * 3) +// The size must be multiple of 256 bytes !! +#define JPG_IMAGE_LINE_BUF_SIZE 512 + +// --- Constants for ellipse function --- +#define EPD_ELLIPSE_UPPER_RIGHT 0x01 +#define EPD_ELLIPSE_UPPER_LEFT 0x02 +#define EPD_ELLIPSE_LOWER_LEFT 0x04 +#define EPD_ELLIPSE_LOWER_RIGHT 0x08 + +// Constants for Arc function +// number representing the maximum angle (e.g. if 100, then if you pass in start=0 and end=50, you get a half circle) +// this can be changed with setArcParams function at runtime +#define DEFAULT_ARC_ANGLE_MAX 360 +// rotational offset in degrees defining position of value 0 (-90 will put it at the top of circle) +// this can be changed with setAngleOffset function at runtime +#define DEFAULT_ANGLE_OFFSET -90 + +#define PI 3.14159265359 + +#define MIN_POLIGON_SIDES 3 +#define MAX_POLIGON_SIDES 60 + +// === Color names constants === +#define EPD_BLACK 15 +#define EPD_WHITE 0 + +// === Color invert constants === +#define INVERT_ON 1 +#define INVERT_OFF 0 + +// === Screen orientation constants === +#define LANDSCAPE_0 1 +#define LANDSCAPE_180 2 + +// === Special coordinates constants === +#define CENTER -9003 +#define RIGHT -9004 +#define BOTTOM -9004 + +#define LASTX 7000 +#define LASTY 8000 + +// === Embedded fonts constants === +#define DEFAULT_FONT 0 +#define DEJAVU18_FONT 1 +#define DEJAVU24_FONT 2 +#define UBUNTU16_FONT 3 +#define COMIC24_FONT 4 +#define MINYA24_FONT 5 +#define TOONEY32_FONT 6 +#define SMALL_FONT 7 +#define FONT_7SEG 8 +#define USER_FONT 9 // font will be read from file + + + +// ===== PUBLIC FUNCTIONS ========================================================================= + +/* + * Draw pixel at given x,y coordinates + * + * Params: + * x: horizontal position + * y: vertical position + * color: pixel color +*/ +//------------------------------------------------------ +void EPD_drawPixel(int16_t x, int16_t y, color_t color); + +/* + * Read pixel color value from display GRAM at given x,y coordinates + * + * Params: + * x: horizontal position + * y: vertical position + * + * Returns: + * pixel color at x,y +*/ +//------------------------------------------ +color_t EPD_readPixel(int16_t x, int16_t y); + +/* + * Draw vertical line at given x,y coordinates + * + * Params: + * x: horizontal start position + * y: vertical start position + * h: line height in pixels + * color: line color +*/ +//--------------------------------------------------------------------- +void EPD_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color); + +/* + * Draw horizontal line at given x,y coordinates + * + * Params: + * x: horizontal start position + * y: vertical start position + * w: line width in pixels + * color: line color +*/ +//--------------------------------------------------------------------- +void EPD_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color); + +/* + * Draw line on screen + * + * Params: + * x0: horizontal start position + * y0: vertical start position + * x1: horizontal end position + * y1: vertical end position + * color: line color +*/ +//------------------------------------------------------------------------------- +void EPD_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color); + + +/* + * Draw line on screen from (x,y) point at given angle + * Line drawing angle starts at lower right quadrant of the screen and is offseted by + * '_angleOffset' global variable (default: -90 degrees) + * + * Params: + * x: horizontal start position + * y: vertical start position + * start: start offset from (x,y) + * len: length of the line + * angle: line angle in degrees + * color: line color +*/ +//----------------------------------------------------------------------------------------------------------- +void EPD_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color); + +/* + * Fill given rectangular screen region with color + * + * Params: + * x: horizontal rect start position + * y: vertical rect start position + * w: rectangle width + * h: rectangle height + * color: fill color +*/ +//--------------------------------------------------------------------------- +void EPD_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color); + +/* + * Draw rectangle on screen + * + * Params: + * x: horizontal rect start position + * y: vertical rect start position + * w: rectangle width + * h: rectangle height + * color: rect line color +*/ +//------------------------------------------------------------------------------ +void EPD_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color); + +/* + * Draw rectangle with rounded corners on screen + * + * Params: + * x: horizontal rect start position + * y: vertical rect start position + * w: rectangle width + * h: rectangle height + * r: corner radius + * color: rectangle color +*/ +//---------------------------------------------------------------------------------------------- +void EPD_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color); + +/* + * Fill given rectangular screen region with rounded corners with color + * + * Params: + * x: horizontal rect start position + * y: vertical rect start position + * w: rectangle width + * h: rectangle height + * r: corner radius + * color: fill color +*/ +//---------------------------------------------------------------------------------------------- +void EPD_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color); + +/* + * Fill the whole screen with color + * + * Params: + * color: fill color +*/ +//-------------------------------- +void EPD_fillScreen(color_t color); + +/* + * Fill the current clip window with color + * + * Params: + * color: fill color +*/ +//--------------------------------- +void EPD_fillWindow(color_t color); + +/* + * Draw triangle on screen + * + * Params: + * x0: first triangle point x position + * y0: first triangle point y position + * x0: second triangle point x position + * y0: second triangle point y position + * x0: third triangle point x position + * y0: third triangle point y position + * color: triangle color +*/ +//----------------------------------------------------------------------------------------------------------------- +void EPD_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color); + +/* + * Fill triangular screen region with color + * + * Params: + * x0: first triangle point x position + * y0: first triangle point y position + * x0: second triangle point x position + * y0: second triangle point y position + * x0: third triangle point x position + * y0: third triangle point y position + * color: fill color +*/ +//----------------------------------------------------------------------------------------------------------------- +void EPD_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color); + +/* + * Draw circle on screen + * + * Params: + * x: circle center x position + * y: circle center x position + * r: circle radius + * color: circle color +*/ +//------------------------------------------------------------------- +void EPD_drawCircle(int16_t x, int16_t y, int radius, color_t color); + +/* + * Fill circle on screen with color + * + * Params: + * x: circle center x position + * y: circle center x position + * r: circle radius + * color: circle fill color +*/ +//------------------------------------------------------------------- +void EPD_fillCircle(int16_t x, int16_t y, int radius, color_t color); + +/* + * Draw ellipse on screen + * + * Params: + * x0: ellipse center x position + * y0: ellipse center x position + * rx: ellipse horizontal radius + * ry: ellipse vertical radius + * option: drawing options, multiple options can be combined + 1 (TFT_ELLIPSE_UPPER_RIGHT) draw upper right corner + 2 (TFT_ELLIPSE_UPPER_LEFT) draw upper left corner + 4 (TFT_ELLIPSE_LOWER_LEFT) draw lower left corner + 8 (TFT_ELLIPSE_LOWER_RIGHT) draw lower right corner + to draw the whole ellipse use option value 15 (1 | 2 | 4 | 8) + * + * color: circle color +*/ +//------------------------------------------------------------------------------------------------------ +void EPD_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option); + +/* + * Fill elliptical region on screen + * + * Params: + * x0: ellipse center x position + * y0: ellipse center x position + * rx: ellipse horizontal radius + * ry: ellipse vertical radius + * option: drawing options, multiple options can be combined + 1 (TFT_ELLIPSE_UPPER_RIGHT) fill upper right corner + 2 (TFT_ELLIPSE_UPPER_LEFT) fill upper left corner + 4 (TFT_ELLIPSE_LOWER_LEFT) fill lower left corner + 8 (TFT_ELLIPSE_LOWER_RIGHT) fill lower right corner + to fill the whole ellipse use option value 15 (1 | 2 | 4 | 8) + * + * color: fill color +*/ +//------------------------------------------------------------------------------------------------------ +void EPD_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option); + + +/* + * Draw circle arc on screen + * Arc drawing angle starts at lower right quadrant of the screen and is offseted by + * '_angleOffset' global variable (default: -90 degrees) + * + * Params: + * cx: arc center X position + * cy: arc center Y position + * th: thickness of the drawn arc + * ry: arc vertical radius + * start: arc start angle in degrees + * end: arc end angle in degrees + * color: arc outline color + * fillcolor: arc fill color +*/ +//---------------------------------------------------------------------------------------------------------------------------- +void EPD_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor); + + +/* + * Draw polygon on screen + * + * Params: + * cx: polygon center X position + * cy: arc center Y position + * sides: number of polygon sides; MAX_POLIGON_SIDES ~ MAX_POLIGON_SIDES (3 ~ 60) + * diameter: diameter of the circle inside which the polygon is drawn + * color: polygon outline color + * fill: polygon fill color; if same as color, polygon is not filled + * deg: polygon rotation angle; 0 ~ 360 + * th: thickness of the polygon outline +*/ +//-------------------------------------------------------------------------------------------------------------- +void EPD_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int deg, uint8_t th); + + +//-------------------------------------------------------------------------------------- +//void EPD_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor); + + +/* + * Set the font used for writing the text to display. + * + * ------------------------------------------------------------------------------------ + * For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available. + * Character ‘/‘ draws the degree sign. + * ------------------------------------------------------------------------------------ + * + * Params: + * font: font number; use defined font names + * font_file: pointer to font file name; NULL for embeded fonts + */ +//---------------------------------------------------- +void EPD_setFont(uint8_t font, const char *font_file); + +/* + * Returns current font height & width in pixels. + * + * Params: + * width: pointer to returned font width + * height: pointer to returned font height + */ +//------------------------------------------- +int EPD_getfontsize(int *width, int* height); + + +/* + * Returns current font height in pixels. + * + */ +//---------------------- +int EPD_getfontheight(); + +/* + * Write text to display. + * + * Rotation of the displayed text depends on 'font_rotate' variable (0~360) + * if 'font_transparent' variable is set to 1, no background pixels will be printed + * + * If the text does not fit the screen width it will be clipped (if text_wrap=0), + * or continued on next line (if text_wrap=1) + * + * Two special characters are allowed in strings: + * ‘\r’ CR (0x0D), clears the display to EOL + * ‘\n’ LF (ox0A), continues to the new line, x=0 + * + * Params: + * st: pointer to null terminated string to be printed + * x: horizontal position of the upper left point in pixels + * Special values can be entered: + * CENTER, centers the text + * RIGHT, right justifies the text + * LASTX, continues from last X position; offset can be used: LASTX+n + * y: vertical position of the upper left point in pixels + * Special values can be entered: + * CENTER, centers the text + * BOTTOM, bottom justifies the text + * LASTY, continues from last Y position; offset can be used: LASTY+n + * + */ +//------------------------------------- +void EPD_print(char *st, int x, int y); + +/* + * Set atributes for 7 segment vector font + * == 7 segment font must be the current font to this function to have effect == + * + * Params: + * l: 6~40; distance between bars in pixels + * w: 1~12, max l/2; bar width in pixels + * outline: draw font outline if set to 1 + * color: font outline color, only used if outline=1 + * + */ +//------------------------------------------------------------------------- +void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color); + +/* + * Sets the clipping area coordinates. + * All writing to screen is clipped to that area. + * Starting x & y in all functions will be adjusted to the clipping area. + * + * Params: + * x1,y1: upper left point of the clipping area + * x2,y2: bottom right point of the clipping area + * + */ +//---------------------------------------------------------------------- +void EPD_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); + +/* + * Resets the clipping area to full screen (0,0),(_wodth,_height) + * + */ +//---------------------- +void EPD_resetclipwin(); + +/* + * Save current clipping area to temporary variable + * + */ +//--------------------- +void EPD_saveClipWin(); + +/* + * Restore current clipping area from temporary variable + * + */ +//------------------------ +void EPD_restoreClipWin(); + +/* + * returns the string width in pixels. + * Useful for positions strings on the screen. + */ +//-------------------------------- +int EPD_getStringWidth(char* str); + + +/* + * Fills the rectangle occupied by string with current background color + */ +void EPD_clearStringRect(int x, int y, char *str); + + +/* + * Compile font c source file to .fnt file + * which can be used in EPD_setFont() function to select external font + * Created file have the same name as source file and extension .fnt + * + * Params: + * fontfile: pointer to c source font file name; must have .c extension + * dbg: if set to 1, prints debug information + * + * Returns: + * 0 on success + * err no on error + * + */ +//------------------------------------------------ +int compile_font_file(char *fontfile, uint8_t dbg); + +/* + * Get all font's characters to buffer + */ +void getFontCharacters(uint8_t *buf); + +/* + * Decodes and displays JPG image. RGB colors are converted to 4-bit Gray scale + * Limits: + * Baseline only. Progressive and Lossless JPEG format are not supported. + * Image size: Up to 65520 x 65520 pixels + * Color space: YCbCr three components only. Gray scale image is not supported. + * Sampling factor: 4:4:4, 4:2:2 or 4:2:0. + * + * Params: + * x: image left position; constants CENTER & RIGHT can be used; negative value is accepted + * y: image top position; constants CENTER & BOTTOM can be used; negative value is accepted + * scale: image scale factor: 0~3; if scale>0, image is scaled by factor 1/(2^scale) (1/2, 1/4 or 1/8) + * fname: pointer to the name of the file from which the image will be read + * if set to NULL, image will be read from memory buffer pointed to by 'buf' + * buf: pointer to the memory buffer from which the image will be read; used if fname=NULL + * size: size of the memory buffer from which the image will be read; used if fname=NULL & buf!=NULL + * + */ +int EPD_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size); + +#endif diff --git a/components/epaper/EPDspi.c b/components/epaper/EPDspi.c new file mode 100644 index 0000000..278b253 --- /dev/null +++ b/components/epaper/EPDspi.c @@ -0,0 +1,759 @@ +/* + * Author: LoBo (loboris@gmail.com, loboris.github) + * + * Module supporting SPI ePaper displays + * + * HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS + * USING DIRECT or DMA SPI TRANSFER MODEs + * +*/ + + +#include "spi_master_lobo.h" +#include +#include +#include +#include +#include "esp_system.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_heap_alloc_caps.h" +#include "soc/spi_reg.h" +#include "EPDspi.h" + +#define EPD_DEBUG 1 + +#define EPD2X9 1 + + #define xDot 128 + #define yDot 296 + #define DELAYTIME 1500 + +static uint8_t GDOControl[] = {0x01, (yDot-1)%256, (yDot-1)/256, 0x00}; +static uint8_t softstart[4] = {0x0c, 0xd7, 0xd6, 0x9d}; +static uint8_t VCOMVol[2] = {0x2c, 0xa8}; // VCOM 7c +static uint8_t DummyLine[2] = {0x3a, 0x1a}; // 4 dummy line per gate +static uint8_t Gatetime[2] = {0x3b, 0x08}; // 2us per line +static uint8_t RamDataEntryMode[2] = {0x11, 0x01}; // Ram data entry mode +static uint8_t Border[2] = {0x3c, 0x61}; // Border control ( 0x61: white border; 0x51: black border + +/* +There are totally 20 phases for programmable Source waveform of different phase length. +The phase period defined as TP [n] * T FRAME , where TP [n] range from 0 to 15. +TP [n] = 0 indicates phase skipped +Source Voltage Level: VS [n-XY] is constant in each phase +VS [n-XY] indicates the voltage in phase n for transition from GS X to GS Y + 00 – VSS + 01 – VSH + 10 – VSL + 11 – NA +VS [n-XY] and TP[n] are stored in waveform lookup table register [LUT]. + +VS coding: VS[0-11] VS[0-10] VS[0-01] VS[0-00] + +*/ +// --- VS ---- ---- TP ---- +//uint8_t LUTDefault_full[31] = {0x32, 0x02,0x02,0x01,0x11,0x12,0x12,0x22,0x22,0x66,0x69,0x69,0x59,0x58,0x99,0x99,0x88,0x00,0x00,0x00,0x00, 0xF8,0xB4,0x13,0x51,0x35,0x51,0x51,0x19,0x01,0x00}; +uint8_t LUTDefault_full[31] = {0x32, 0x11,0x11,0x10,0x02,0x02,0x22,0x22,0x22,0x22,0x22,0x51,0x51,0x55,0x88,0x08,0x08,0x88,0x88,0x00,0x00, 0x34,0x23,0x12,0x21,0x24,0x28,0x22,0x21,0xA1,0x01}; +uint8_t LUTDefault_part[31] = {0x32, 0x10,0x18,0x18,0x08,0x18,0x18,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x13,0x14,0x44,0x12,0x00,0x00,0x00,0x00,0x00,0x00}; +uint8_t LUT_gs[31] = {0x32, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; +uint8_t LUTFastest[31] = {0x32, 0x99,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + +uint8_t lvl_buf[16] = {32,70,110,150,185,210,220,225,230,235,240,243,248,251,253,255}; +uint8_t lvl_buf_jpg[16] = {4,8,12,16,22,30,40,60,80,110,140,180,220,240,250,255}; + +uint8_t *LUT_part = LUTDefault_part; +spi_lobo_device_handle_t disp_spi = NULL; +uint8_t *gs_disp_buffer = NULL; +uint8_t *disp_buffer = NULL; +uint8_t *drawBuff = NULL; +uint8_t *gs_drawBuff = NULL; +int _width = EPD_DISPLAY_WIDTH; +int _height = EPD_DISPLAY_HEIGHT; +uint8_t _gs = 0; + +uint16_t gs_used_shades = 0; + +//----------------------------------------------------------- +static void IRAM_ATTR _dma_send(uint8_t *data, uint32_t size) +{ + //Fill DMA descriptors + spi_lobo_dmaworkaround_transfer_active(disp_spi->host->dma_chan); //mark channel as active + spi_lobo_setup_dma_desc_links(disp_spi->host->dmadesc_tx, size, data, false); + disp_spi->host->hw->user.usr_mosi_highpart=0; + disp_spi->host->hw->dma_out_link.addr=(int)(&disp_spi->host->dmadesc_tx[0]) & 0xFFFFF; + disp_spi->host->hw->dma_out_link.start=1; + disp_spi->host->hw->user.usr_mosi_highpart=0; + + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1; + + // Start transfer + disp_spi->host->hw->cmd.usr = 1; + // Wait for SPI bus ready + while (disp_spi->host->hw->cmd.usr); + + //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. + if (disp_spi->host->dma_chan) spi_lobo_dmaworkaround_idle(disp_spi->host->dma_chan); + + // Reset DMA + disp_spi->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + disp_spi->host->hw->dma_out_link.start=0; + disp_spi->host->hw->dma_in_link.start=0; + disp_spi->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + disp_spi->host->hw->dma_conf.out_data_burst_en=1; +} + +//-------------------------------------------------------------------------- +static void IRAM_ATTR _direct_send(uint8_t *data, uint32_t len, uint8_t rep) +{ + uint32_t cidx = 0; // buffer index + uint32_t wd = 0; + int idx = 0; + int bits = 0; + int wbits = 0; + + taskDISABLE_INTERRUPTS(); + + while (len) { + + wd |= (uint32_t)data[idx] << wbits; + wbits += 8; + if (wbits == 32) { + bits += wbits; + wbits = 0; + disp_spi->host->hw->data_buf[idx++] = wd; + wd = 0; + } + len--; // Decrement data counter + if (rep == 0) cidx++; // if not repeating data, increment buffer index + } + if (bits) { + while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + // Load send buffer + disp_spi->host->hw->user.usr_mosi_highpart = 0; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; + disp_spi->host->hw->user.usr_mosi = 1; + disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0; + disp_spi->host->hw->user.usr_miso = 0; + disp_spi->host->hw->cmd.usr = 1; // Start transfer + } + // Wait for SPI bus ready + while (disp_spi->host->hw->cmd.usr); + taskENABLE_INTERRUPTS(); +} + +// ================================================================ +// === Main function to send data to display ====================== +// If rep==true: repeat sending data to display 'len' times +// If rep==false: send 'len' data bytes from buffer to display +// ** Device must already be selected and address window set ** +// ================================================================ +//--------------------------------------------------------------------------- +static void IRAM_ATTR SPI_send_data(uint8_t *data, uint32_t len, uint8_t rep) +{ + if (len == 0) return; + + if ((len*8) <= 512) _direct_send(data, len, rep); + else if (rep == 0) _dma_send(data, len); + else { + // ==== Repeat data, more than 512 bits total ==== + uint8_t *transbuf = pvPortMallocCaps(len, MALLOC_CAP_DMA); + if (transbuf == NULL) return; + + memset(transbuf, data[0], len); + _dma_send(transbuf, len); + free(transbuf); + } +} + +// Send one byte to display +//------------------------------------- +void IRAM_ATTR SPI_Write(uint8_t value) +{ + disp_spi->host->hw->data_buf[0] = (uint32_t)value; + // Load send buffer + disp_spi->host->hw->user.usr_mosi_highpart = 0; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + disp_spi->host->hw->user.usr_mosi = 1; + disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0; + disp_spi->host->hw->user.usr_miso = 0; + // Start transfer + disp_spi->host->hw->cmd.usr = 1; + // Wait for SPI bus ready + while (disp_spi->host->hw->cmd.usr); +} + +// Check display busy line and wait while busy +//----------------------- +static uint8_t ReadBusy() +{ + for (int i=0; i<400; i++){ + if (isEPD_BUSY == EPD_BUSY_LEVEL) return 1; + vTaskDelay(10 / portTICK_RATE_MS); + } + return 0; +} + +//----------------------- +static uint8_t WaitBusy() +{ + if (isEPD_BUSY != EPD_BUSY_LEVEL) return 1; + vTaskDelay(10 / portTICK_RATE_MS); + if (isEPD_BUSY != EPD_BUSY_LEVEL) return 1; + return 0; +} + +// Write one command without parameters +//--------------------------------------- +static void EPD_WriteCMD(uint8_t command) +{ + spi_lobo_device_select(disp_spi, 0); + EPD_DC_0; // command write + SPI_Write(command); +} + +// Write command with one paramet +//--------------------------------------- +static void EPD_WriteCMD_p1(uint8_t command,uint8_t para) +{ + spi_lobo_device_select(disp_spi, 0); + //ReadBusy(); + EPD_DC_0; // command write + SPI_Write(command); + EPD_DC_1; // data write + SPI_Write(para); + spi_lobo_device_deselect(disp_spi); +} + +//---------------- +void EPD_PowerOn() +{ + EPD_WriteCMD_p1(0x22,0xc0); + EPD_WriteCMD(0x20); + //EPD_WriteCMD(0xff); + spi_lobo_device_deselect(disp_spi); +#if EPD_DEBUG + if (!WaitBusy()) printf("[EPD] NOT BUSY\r\n"); + if (!ReadBusy()) printf("[EPD] NOT READY\r\n"); +#else + WaitBusy(); + ReadBusy(); +#endif +} + +//----------------- +void EPD_PowerOff() +{ + EPD_WriteCMD_p1(0x22,0x03); + EPD_WriteCMD(0x20); + //EPD_WriteCMD(0xff); + spi_lobo_device_deselect(disp_spi); +#if EPD_DEBUG + if (!WaitBusy()) printf("[EPD] NOT BUSY\r\n"); + if (!ReadBusy()) printf("[EPD] NOT READY\r\n"); +#else + WaitBusy(); + ReadBusy(); +#endif +#if POWER_Pin + gpio_set_level(DC_Pin, 0); + gpio_set_level(MOSI_Pin, 0); + gpio_set_level(SCK_Pin, 0); + gpio_set_level(RST_Pin, 0); + gpio_set_level(CS_Pin, 0); + gpio_set_level(POWER_Pin, 0); +#endif +} + +// Send command with multiple parameters +//---------------------------------------------------- +static void EPD_Write(uint8_t *value, uint8_t datalen) +{ + uint8_t i = 0; + uint8_t *ptemp; + + ptemp = value; + spi_lobo_device_select(disp_spi, 0); + //ReadBusy(); + EPD_DC_0; // When DC is 0, write command + SPI_Write(*ptemp); //The first byte is written with the command value + ptemp++; + EPD_DC_1; // When DC is 1, write data + for(i= 0;i> 8; + RamAreaY[3] = Yend & 0xFF; + RamAreaY[4] = Yend >> 8; + EPD_Write(RamAreaX, sizeof(RamAreaX)); + EPD_Write(RamAreaY, sizeof(RamAreaY)); +} + +//Set RAM X and Y address counter +/* + === Set RAM Address Counter (4Eh-4Fh) === + adrX[4:0]: Make initial settings for the RAM X address in the address counter (AC). + adrY[8:0]: Make initial settings for the RAM Y address in the address counter (AC). + After RAM data is written, the address counter is automatically updated according to the settings with AM, ID + bits and setting for a new RAM address is not required in the address counter. + Therefore, data is written consecutively without setting an address. + The address counter is not automatically updated when data is read out from the RAM. + RAM address setting cannot be made during the standby mode. + The address setting should be made within the area designated with window addresses which is controlled + by the Data Entry Setting (R11h) {AM, ID[1:0]} ; RAM Address XStart / XEnd Position (R44h) and RAM Address Ystart /Yend Position (R45h). + Otherwise undesirable image will be displayed on the Panel. +*/ +//---------------------------------------------------------- +static void EPD_SetRamPointer(uint8_t addrX, uint16_t addrY) +{ + uint8_t RamPointerX[2]; // default (0,0) + uint8_t RamPointerY[3]; + //Set RAM X address counter + RamPointerX[0] = 0x4e; + RamPointerX[1] = addrX; + //Set RAM Y address counter + RamPointerY[0] = 0x4f; + RamPointerY[1] = addrY & 0xFF; + RamPointerY[2] = addrY >> 8; + + EPD_Write(RamPointerX, sizeof(RamPointerX)); + EPD_Write(RamPointerY, sizeof(RamPointerY)); +} + + +//Set RAM X and Y address Start / End position +//Set RAM X and Y address counter +//---------------------------------------------------------------------------------------------- +static void part_display(uint8_t RAM_XST, uint8_t RAM_XEND ,uint16_t RAM_YST, uint16_t RAM_YEND) +{ + EPD_SetRamArea(RAM_XST, RAM_XEND, RAM_YST, RAM_YEND); + EPD_SetRamPointer (RAM_XST, RAM_YST); +} + +//Initialize the display +//-------------------- +static void EPD_Init() +{ +#if POWER_Pin + gpio_set_level(POWER_Pin, 1); + vTaskDelay(100 / portTICK_RATE_MS); +#else + vTaskDelay(10 / portTICK_RATE_MS); +#endif + // reset + EPD_RST_0; + vTaskDelay(10 / portTICK_RATE_MS); +#if EPD_DEBUG + uint32_t t1 = clock(); +#endif + EPD_RST_1; + for (int n=0; n<50; n++) { + vTaskDelay(10 / portTICK_RATE_MS); + if (isEPD_BUSY == EPD_BUSY_LEVEL) break; + } + + SPI_Write(0x12); // software reset + vTaskDelay(10 / portTICK_RATE_MS); + ReadBusy(); + + // set registers + EPD_Write(GDOControl, sizeof(GDOControl)); // Panel configuration, Gate selection + EPD_Write(softstart, sizeof(softstart)); // X decrease, Y decrease + EPD_Write(VCOMVol, sizeof(VCOMVol)); // VCOM setting + EPD_Write(DummyLine, sizeof(DummyLine)); // dummy line per gate + EPD_Write(Gatetime, sizeof(Gatetime)); // Gate time setting + EPD_Write(Border, sizeof(Border)); + EPD_Write(RamDataEntryMode, sizeof(RamDataEntryMode)); // X increase, Y decrease + + EPD_SetRamArea(0x00, (xDot-1)/8, yDot-1, 0); + EPD_SetRamPointer(0x00, yDot-1); +#if EPD_DEBUG + t1 = clock() - t1; + printf("[EPD] Init: %u ms\r\n", t1); +#endif +} + +//------------------------------ +static void EPD_UpdateFull(void) +{ + /* + + Enable Clock Signal, + + Then Enable CP + - Then Load Temperature value + - Then Load LUT + - Then INITIAL DISPLAY + + Then PATTERN DISPLAY + + Then Disable CP + + Then Disable OSC + */ + EPD_WriteCMD_p1(0x22,0xC7); + EPD_WriteCMD(0x20); + //EPD_WriteCMD(0xff); + spi_lobo_device_deselect(disp_spi); + +#if EPD_DEBUG + if (!WaitBusy()) printf("[EPD] NOT BUSY\r\n"); + if (!ReadBusy()) printf("[EPD] NOT READY\r\n"); +#else + WaitBusy(); + ReadBusy(); +#endif +} + +//------------------------------- +static void EPD_Update_Part(void) +{ + /* + - Enable Clock Signal, + - Then Enable CP + - Then Load Temperature value + - Then Load LUT + - Then INITIAL DISPLAY + + Then PATTERN DISPLAY + - Then Disable CP + - Then Disable OSC + */ + EPD_WriteCMD_p1(0x22,0x04); + EPD_WriteCMD(0x20); + //EPD_WriteCMD(0xff); + spi_lobo_device_deselect(disp_spi); + +#if EPD_DEBUG + if (!WaitBusy()) printf("[EPD] NOT BUSY\r\n"); + if (!ReadBusy()) printf("[EPD] NOT READY\r\n"); +#else + WaitBusy(); + ReadBusy(); +#endif +} + +/******************************************************************************* +Full screen initialization +********************************************************************************/ +static void EPD_init_Full(void) +{ + EPD_Init(); // Reset and set register + EPD_Write((uint8_t *)LUTDefault_full,sizeof(LUTDefault_full)); + + EPD_PowerOn(); +} + +/******************************************************************************* +Part screen initialization +********************************************************************************/ +static void EPD_init_Part(void) +{ + EPD_Init(); // display + EPD_Write((uint8_t *)LUT_part, 31); + EPD_PowerOn(); +} +/******************************************************************************** +parameter: + Label : + =1 Displays the contents of the DisBuffer + =0 Displays the contents of the first byte in DisBuffer, +********************************************************************************/ +static void EPD_Dis_Full(uint8_t *DisBuffer,uint8_t type) +{ + EPD_SetRamPointer(0x00, yDot-1); // set ram pointer + if (type == 0){ + // Fill screen with white + EPD_WriteDispRamMono(xDot, yDot, 0xff); + } + else { + // Fill screen from buffer + EPD_WriteDispRam(xDot, yDot, (uint8_t *)DisBuffer); + } + EPD_UpdateFull(); + +} + +/******************************************************************************** +WARNING: X is smaller screen dimension (0~127) ! + Y is larger screen dimension (0~295) ! +parameter: + xStart : X direction Start coordinate + xEnd : X direction end coordinate + yStart : Y direction Start coordinate + yEnd : Y direction end coordinate + DisBuffer : Display content + type : + =1 Displays the contents of the DisBuffer + =0 Displays the contents of the first byte in DisBuffer, +********************************************************************************/ +static void EPD_Dis_Part(uint8_t xStart, uint8_t xEnd, uint16_t yStart, uint16_t yEnd, uint8_t *DisBuffer, uint8_t type) +{ + if (type == 0) { + // Repeated color + part_display(xStart/8, xEnd/8, yEnd, yStart); + EPD_WriteDispRamMono(xEnd-xStart+1, yEnd-yStart+1, DisBuffer[0]); + EPD_Update_Part(); + part_display(xStart/8, xEnd/8, yEnd, yStart); + EPD_WriteDispRamMono(xEnd-xStart+1, yEnd-yStart+1, DisBuffer[0]); + } + else { + // From buffer + part_display(xStart/8, xEnd/8, yEnd, yStart); + EPD_WriteDispRam(xEnd-xStart+1, yEnd-yStart+1,DisBuffer); + EPD_Update_Part(); + part_display(xStart/8, xEnd/8, yEnd, yStart); + EPD_WriteDispRam(xEnd-xStart+1, yEnd-yStart+1,DisBuffer); + } +} + +//====================================================================================================================================== + +// Clear full screen +//========================= +void EPD_DisplayClearFull() +{ + uint8_t m; + EPD_init_Full(); + +#if EPD_DEBUG + uint32_t t1 = clock(); +#endif + m = 0x00; + EPD_Dis_Full(&m, 0); //all black +#if EPD_DEBUG + t1 = clock() - t1; + printf("[EPD] Clear black: %u ms\r\n", t1); + t1 = clock(); +#endif + m = 0xff; + EPD_Dis_Full(&m, 0); //all white +#if EPD_DEBUG + t1 = clock() - t1; + printf("[EPD] Clear white: %u ms\r\n", t1); +#endif +} + +// Partial clear screen +//========================= +void EPD_DisplayClearPart() +{ + uint8_t m = 0xFF; + EPD_init_Part(); +#if EPD_DEBUG + uint32_t t1 = clock(); + EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all white + m = 0x00; + EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all black + m = 0xFF; + EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all white + t1 = clock() - t1; + printf("[EPD] Part Clear: %u ms\r\n", t1); +#else + EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all white + m = 0x00; + EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all black + m = 0xFF; + EPD_Dis_Part(0, xDot-1, 0, yDot-1, &m, 0); //all white +#endif +} + +//================================== +void EPD_DisplaySetFull(uint8_t val) +{ + EPD_Write((uint8_t *)LUTDefault_full,sizeof(LUTDefault_full)); +#if EPD_DEBUG + uint32_t t1 = clock(); + EPD_Dis_Full(&val, 0); + t1 = clock() - t1; + printf("[EPD] Display Set Full: %u ms [%02x]\r\n", t1, val); +#else + EPD_Dis_Full(&val, 0); +#endif +} + +//====================================================================================== +void EPD_DisplaySetPart(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t val) +{ + EPD_Write((uint8_t *)LUT_part, 31); +#if EPD_DEBUG + uint32_t t1 = clock(); + EPD_Dis_Part(yStart,yEnd,xStart,xEnd, &val,0); + t1 = clock() - t1; + printf("[EPD] Display Set Part: %u ms [%02x]\r\n", t1, val); +#else + EPD_Dis_Part(yStart,yEnd,xStart,xEnd, &val,0); +#endif +} + +//====================================== +void EPD_DisplayFull(uint8_t *DisBuffer) +{ + EPD_Write((uint8_t *)LUTDefault_full,sizeof(LUTDefault_full)); +#if EPD_DEBUG + uint32_t t1 = clock(); + EPD_Dis_Full((uint8_t *)DisBuffer,1); + t1 = clock() - t1; + printf("[EPD] Display Full: %u ms\r\n", t1); +#else + EPD_Dis_Full((uint8_t *)DisBuffer,1); +#endif +} + +//========================================================================================== +void EPD_DisplayPart(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t *DisBuffer) +{ + EPD_Write((uint8_t *)LUT_part, 31); +#if EPD_DEBUG + uint32_t t1 = clock(); + EPD_Dis_Part(yStart,yEnd,xStart,xEnd,(uint8_t *)DisBuffer,1); + t1 = clock() - t1; + printf("[EPD] Display Part: %u ms [%02x:%02x]\r\n", t1, LUT_gs[1], LUT_gs[21]); +#else + EPD_Dis_Part(yStart,yEnd,xStart,xEnd,(uint8_t *)DisBuffer,1); +#endif +} + +//============ +void EPD_Cls() +{ + EPD_DisplaySetPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, 0xFF); + memset(disp_buffer, 0xFF, _width * (_height/8)); + memset(gs_disp_buffer, 0, _width * _height); + gs_used_shades = 0; +} + +//------------------------------------------------------------------------------- +void EPD_gsUpdate(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t gs) +{ + uint8_t val, buf_val, new_val; + int count=0, changed=0; + int x; + uint8_t y; + for (x=xStart; x<=xEnd; x++) { + for (y=yStart; y<=yEnd; y++) { + val = gs_drawBuff[(y * (xEnd-xStart+1)) + x]; + if (val > 15) val >>= 4; + if (val == gs) { + buf_val = drawBuff[(x * ((yEnd-yStart+1)>>3)) + (y>>3)]; + new_val = buf_val; + if (gs > 0) new_val &= (0x80 >> (y % 8)) ^ 0xFF; + else new_val |= (0x80 >> (y % 8)); + if (new_val != buf_val) { + drawBuff[(x * (_height>>3)) + (y>>3)] = new_val; + changed++; + } + count++; + } + } + } + + if (changed) { + #if EPD_DEBUG + printf("[EPD] GS Update %02x, count=%d changed=%d\r\n", gs, count, changed); + #endif + uint8_t *_lutPart = LUT_part; + memset(LUT_gs+1, 0, 30); + if (gs > 0) { + if (gs > 0) { + LUT_gs[1] = 0x18; + LUT_gs[21] = gs; + } + } + else { + LUT_gs[1] = 0x28; + LUT_gs[2] = 0x00; + LUT_gs[21] = 15; + } + LUT_part = LUT_gs; + EPD_DisplayPart(xStart, xEnd, yStart, yEnd, drawBuff); + + LUT_part = _lutPart; + } +} + +//----------------------------------------------------------------------- +void EPD_Update(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd) +{ + if (_gs == 0) EPD_DisplayPart(xStart, xEnd, yStart, yEnd, drawBuff); + else { + for (int n=0; n<16; n++) { + if (gs_used_shades & (1< +#include "spi_master_lobo.h" + +#define EPD_DISPLAY_WIDTH 296 +#define EPD_DISPLAY_HEIGHT 128 + +#define SCK_Pin 18 +#define MOSI_Pin 23 +//#define MISO_Pin 19 +#define DC_Pin 17//26 +#define BUSY_Pin 4//32 +#define RST_Pin 16//27 +#define CS_Pin 5 +// ePaper display can be powered from GPIO +// if powered directly from Vcc, set this to 0 +#define POWER_Pin 22 + +#define DC_VAL (1 << DC_Pin) + +#define EPD_CS_0 gpio_set_level(CS_Pin, 0) +#define EPD_CS_1 gpio_set_level(CS_Pin, 1) +#define isEPD_CS gpio_get_level(CS_Pin) + +#define EPD_RST_0 gpio_set_level(RST_Pin, 0) +#define EPD_RST_1 gpio_set_level(RST_Pin, 1) +#define isEPD_RST gpio_get_level(RST_Pin) + +#define EPD_DC_0 gpio_set_level(DC_Pin, 0) +#define EPD_DC_1 gpio_set_level(DC_Pin, 1) + +#define isEPD_BUSY gpio_get_level(BUSY_Pin) + +#define EPD_BUSY_LEVEL 0 + +// ================================================== +// Define which spi bus to use VSPI_HOST or HSPI_HOST +#define SPI_BUS VSPI_HOST +// ================================================== + +spi_lobo_device_handle_t disp_spi; +uint8_t *gs_disp_buffer; +uint8_t *disp_buffer; +uint8_t *gs_drawBuff; +uint8_t *drawBuff; +int _width; +int _height; +uint16_t gs_used_shades; +uint8_t _gs; +uint8_t *LUT_part; +uint8_t LUTDefault_fastest[31]; +uint8_t LUTDefault_part[31]; +uint8_t LUT_gs[31]; +uint8_t LUTDefault_full[31]; +uint8_t lvl_buf[16]; +uint8_t lvl_buf_jpg[16]; + +void EPD_wait(uint32_t ms); +void EPD_DisplaySetFull(uint8_t val); +void EPD_DisplaySetPart(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t val); +void EPD_DisplayClearFull(); +void EPD_DisplayClearPart(); +void EPD_DisplayFull(uint8_t *DisBuffer); +void EPD_DisplayPart(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t *DisBuffer); +void EPD_gsUpdate(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t gs); +void EPD_Update(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd); +void EPD_UpdateScreen(); +void EPD_Cls(); +void EPD_PowerOn(); +void EPD_PowerOff(); + + +#endif diff --git a/components/epaper/SmallFont.c b/components/epaper/SmallFont.c new file mode 100644 index 0000000..663f59c --- /dev/null +++ b/components/epaper/SmallFont.c @@ -0,0 +1,120 @@ +// SmallFont.c +// Font type : Full (95 characters) +// Font size : 8x12 pixels +// Memory usage : 1144 bytes + +#if defined(__AVR__) + #include + #define fontdatatype const uint8_t +#elif defined(__PIC32MX__) + #define PROGMEM + #define fontdatatype const unsigned char +#elif defined(__arm__) + #define PROGMEM + #define fontdatatype const unsigned char +#endif + +const unsigned char tft_SmallFont[1144] = +{ +0x08,0x0C,0x20,0x5F, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // +0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, // ! +0x00,0x28,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // " +0x00,0x00,0x28,0x28,0xFC,0x28,0x50,0xFC,0x50,0x50,0x00,0x00, // # +0x00,0x20,0x78,0xA8,0xA0,0x60,0x30,0x28,0xA8,0xF0,0x20,0x00, // $ +0x00,0x00,0x48,0xA8,0xB0,0x50,0x28,0x34,0x54,0x48,0x00,0x00, // % +0x00,0x00,0x20,0x50,0x50,0x78,0xA8,0xA8,0x90,0x6C,0x00,0x00, // & +0x00,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ' +0x00,0x04,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x08,0x04,0x00, // ( +0x00,0x40,0x20,0x10,0x10,0x10,0x10,0x10,0x10,0x20,0x40,0x00, // ) +0x00,0x00,0x00,0x20,0xA8,0x70,0x70,0xA8,0x20,0x00,0x00,0x00, // * +0x00,0x00,0x20,0x20,0x20,0xF8,0x20,0x20,0x20,0x00,0x00,0x00, // + +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x80, // , +0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, // - +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, // . +0x00,0x08,0x10,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x80,0x00, // / + +0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, // 0 +0x00,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // 1 +0x00,0x00,0x70,0x88,0x88,0x10,0x20,0x40,0x80,0xF8,0x00,0x00, // 2 +0x00,0x00,0x70,0x88,0x08,0x30,0x08,0x08,0x88,0x70,0x00,0x00, // 3 +0x00,0x00,0x10,0x30,0x50,0x50,0x90,0x78,0x10,0x18,0x00,0x00, // 4 +0x00,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0x88,0x70,0x00,0x00, // 5 +0x00,0x00,0x70,0x90,0x80,0xF0,0x88,0x88,0x88,0x70,0x00,0x00, // 6 +0x00,0x00,0xF8,0x90,0x10,0x20,0x20,0x20,0x20,0x20,0x00,0x00, // 7 +0x00,0x00,0x70,0x88,0x88,0x70,0x88,0x88,0x88,0x70,0x00,0x00, // 8 +0x00,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x48,0x70,0x00,0x00, // 9 +0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x20,0x00,0x00, // : +0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x20,0x00, // ; +0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00, // < +0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0x00,0x00, // = +0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00,0x00, // > +0x00,0x00,0x70,0x88,0x88,0x10,0x20,0x20,0x00,0x20,0x00,0x00, // ? + +0x00,0x00,0x70,0x88,0x98,0xA8,0xA8,0xB8,0x80,0x78,0x00,0x00, // @ +0x00,0x00,0x20,0x20,0x30,0x50,0x50,0x78,0x48,0xCC,0x00,0x00, // A +0x00,0x00,0xF0,0x48,0x48,0x70,0x48,0x48,0x48,0xF0,0x00,0x00, // B +0x00,0x00,0x78,0x88,0x80,0x80,0x80,0x80,0x88,0x70,0x00,0x00, // C +0x00,0x00,0xF0,0x48,0x48,0x48,0x48,0x48,0x48,0xF0,0x00,0x00, // D +0x00,0x00,0xF8,0x48,0x50,0x70,0x50,0x40,0x48,0xF8,0x00,0x00, // E +0x00,0x00,0xF8,0x48,0x50,0x70,0x50,0x40,0x40,0xE0,0x00,0x00, // F +0x00,0x00,0x38,0x48,0x80,0x80,0x9C,0x88,0x48,0x30,0x00,0x00, // G +0x00,0x00,0xCC,0x48,0x48,0x78,0x48,0x48,0x48,0xCC,0x00,0x00, // H +0x00,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, // I +0x00,0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x90,0xE0,0x00, // J +0x00,0x00,0xEC,0x48,0x50,0x60,0x50,0x50,0x48,0xEC,0x00,0x00, // K +0x00,0x00,0xE0,0x40,0x40,0x40,0x40,0x40,0x44,0xFC,0x00,0x00, // L +0x00,0x00,0xD8,0xD8,0xD8,0xD8,0xA8,0xA8,0xA8,0xA8,0x00,0x00, // M +0x00,0x00,0xDC,0x48,0x68,0x68,0x58,0x58,0x48,0xE8,0x00,0x00, // N +0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, // O + +0x00,0x00,0xF0,0x48,0x48,0x70,0x40,0x40,0x40,0xE0,0x00,0x00, // P +0x00,0x00,0x70,0x88,0x88,0x88,0x88,0xE8,0x98,0x70,0x18,0x00, // Q +0x00,0x00,0xF0,0x48,0x48,0x70,0x50,0x48,0x48,0xEC,0x00,0x00, // R +0x00,0x00,0x78,0x88,0x80,0x60,0x10,0x08,0x88,0xF0,0x00,0x00, // S +0x00,0x00,0xF8,0xA8,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // T +0x00,0x00,0xCC,0x48,0x48,0x48,0x48,0x48,0x48,0x30,0x00,0x00, // U +0x00,0x00,0xCC,0x48,0x48,0x50,0x50,0x30,0x20,0x20,0x00,0x00, // V +0x00,0x00,0xA8,0xA8,0xA8,0x70,0x50,0x50,0x50,0x50,0x00,0x00, // W +0x00,0x00,0xD8,0x50,0x50,0x20,0x20,0x50,0x50,0xD8,0x00,0x00, // X +0x00,0x00,0xD8,0x50,0x50,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // Y +0x00,0x00,0xF8,0x90,0x10,0x20,0x20,0x40,0x48,0xF8,0x00,0x00, // Z +0x00,0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, // [ +0x00,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x10,0x08,0x00,0x00, // +0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70,0x00, // ] +0x00,0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ^ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC, // _ + +0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ` +0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x38,0x48,0x3C,0x00,0x00, // a +0x00,0x00,0xC0,0x40,0x40,0x70,0x48,0x48,0x48,0x70,0x00,0x00, // b +0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x40,0x40,0x38,0x00,0x00, // c +0x00,0x00,0x18,0x08,0x08,0x38,0x48,0x48,0x48,0x3C,0x00,0x00, // d +0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x78,0x40,0x38,0x00,0x00, // e +0x00,0x00,0x1C,0x20,0x20,0x78,0x20,0x20,0x20,0x78,0x00,0x00, // f +0x00,0x00,0x00,0x00,0x00,0x3C,0x48,0x30,0x40,0x78,0x44,0x38, // g +0x00,0x00,0xC0,0x40,0x40,0x70,0x48,0x48,0x48,0xEC,0x00,0x00, // h +0x00,0x00,0x20,0x00,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x00, // i +0x00,0x00,0x10,0x00,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0xE0, // j +0x00,0x00,0xC0,0x40,0x40,0x5C,0x50,0x70,0x48,0xEC,0x00,0x00, // k +0x00,0x00,0xE0,0x20,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, // l +0x00,0x00,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00,0x00, // m +0x00,0x00,0x00,0x00,0x00,0xF0,0x48,0x48,0x48,0xEC,0x00,0x00, // n +0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x48,0x48,0x30,0x00,0x00, // o + +0x00,0x00,0x00,0x00,0x00,0xF0,0x48,0x48,0x48,0x70,0x40,0xE0, // p +0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x48,0x48,0x38,0x08,0x1C, // q +0x00,0x00,0x00,0x00,0x00,0xD8,0x60,0x40,0x40,0xE0,0x00,0x00, // r +0x00,0x00,0x00,0x00,0x00,0x78,0x40,0x30,0x08,0x78,0x00,0x00, // s +0x00,0x00,0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x18,0x00,0x00, // t +0x00,0x00,0x00,0x00,0x00,0xD8,0x48,0x48,0x48,0x3C,0x00,0x00, // u +0x00,0x00,0x00,0x00,0x00,0xEC,0x48,0x50,0x30,0x20,0x00,0x00, // v +0x00,0x00,0x00,0x00,0x00,0xA8,0xA8,0x70,0x50,0x50,0x00,0x00, // w +0x00,0x00,0x00,0x00,0x00,0xD8,0x50,0x20,0x50,0xD8,0x00,0x00, // x +0x00,0x00,0x00,0x00,0x00,0xEC,0x48,0x50,0x30,0x20,0x20,0xC0, // y +0x00,0x00,0x00,0x00,0x00,0x78,0x10,0x20,0x20,0x78,0x00,0x00, // z +0x00,0x18,0x10,0x10,0x10,0x20,0x10,0x10,0x10,0x10,0x18,0x00, // { +0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, // | +0x00,0x60,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x20,0x60,0x00, // } +0x40,0xA4,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ~ +}; diff --git a/components/epaper/Ubuntu16.c b/components/epaper/Ubuntu16.c new file mode 100644 index 0000000..4f75855 --- /dev/null +++ b/components/epaper/Ubuntu16.c @@ -0,0 +1,331 @@ +// This comes with no warranty, implied or otherwise + +// This data structure was designed to support Proportional fonts +// on Arduinos. It can however handle any ttf font that has been converted +// using the conversion program. These could be fixed width or proportional +// fonts. Individual characters do not have to be multiples of 8 bits wide. +// Any width is fine and does not need to be fixed. + +// The data bits are packed to minimize data requirements, but the tradeoff +// is that a header is required per character. + +// Ubuntu16.c +// Point Size : 16 +// Memory usage : 1433 bytes +// # characters : 95 + +// Header Format (to make Arduino UTFT Compatible): +// ------------------------------------------------ +// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) +// Character Height +// First Character (Reserved. 0x00) +// Number Of Characters (Reserved. 0x00) + +const unsigned char tft_Ubuntu16[] = +{ +0x00, 0x10, 0x00, 0x00, + +// Individual Character Format: +// ---------------------------- +// Character Code +// Adjusted Y Offset +// Width +// Height +// xOffset +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] + +// NOTE: You can remove any of these characters if they are not needed in +// your application. The first character number in each Glyph indicates +// the ASCII character code. Therefore, these do not have to be sequential. +// Just remove all the content for a particular character to save space. + +// ' ' +0x20,0x0D,0x00,0x00,0x00,0x04, + +// '!' +0x21,0x02,0x01,0x0B,0x01,0x04, +0xFC,0x60, +// '"' +0x22,0x00,0x04,0x04,0x01,0x07, +0x99,0x99, +// '#' +0x23,0x02,0x09,0x0B,0x01,0x0B, +0x11,0x08,0x84,0x5F,0xF2,0x21,0x10,0x89,0xFF,0x44,0x22,0x11,0x00, +// '$' +0x24,0x00,0x07,0x10,0x01,0x09, +0x10,0x20,0xF6,0x08,0x10,0x18,0x08,0x0C,0x0C,0x08,0x3F,0xC2,0x04,0x00, +// '%' +0x25,0x02,0x0C,0x0B,0x01,0x0E, +0x70,0x4D,0x88,0x89,0x08,0x90,0xDA,0x07,0x4E,0x05,0xB0,0x91,0x09,0x11,0x1B,0x20,0xE0, +// '&' +0x26,0x02,0x0A,0x0B,0x01,0x0B, +0x3C,0x18,0x84,0x21,0x08,0x2C,0x0C,0x04,0x8A,0x10,0x83,0x30,0xC7,0xC8, +// ''' +0x27,0x00,0x01,0x04,0x01,0x04, +0xF0, +// '(' +0x28,0x00,0x04,0x10,0x01,0x05, +0x02,0x44,0x48,0x88,0x88,0x84,0x44,0x20, +// ')' +0x29,0x00,0x04,0x10,0x00,0x05, +0x04,0x22,0x21,0x11,0x11,0x12,0x22,0x40, +// '*' +0x2A,0x02,0x09,0x06,0x00,0x08, +0x08,0x24,0x8F,0x83,0x81,0x41,0x10, +// '+' +0x2B,0x05,0x07,0x07,0x01,0x09, +0x10,0x20,0x47,0xF1,0x02,0x04,0x00, +// ',' +0x2C,0x0B,0x02,0x05,0x00,0x04, +0x54,0x80, +// '-' +0x2D,0x08,0x04,0x01,0x01,0x06, +0xF0, +// '.' +0x2E,0x0B,0x01,0x02,0x01,0x04, +0xC0, +// '/' +0x2F,0x00,0x07,0x10,0x00,0x06, +0x02,0x08,0x10,0x20,0x81,0x02,0x08,0x10,0x40,0x81,0x04,0x08,0x10,0x40, +// '0' +0x30,0x02,0x07,0x0B,0x01,0x09, +0x38,0x8B,0x1C,0x18,0x30,0x60,0xC1,0x86,0x88,0xE0, +// '1' +0x31,0x02,0x04,0x0B,0x01,0x09, +0x13,0x59,0x11,0x11,0x11,0x10, +// '2' +0x32,0x02,0x06,0x0B,0x01,0x09, +0x7A,0x30,0x41,0x08,0x21,0x08,0x42,0x0F,0xC0, +// '3' +0x33,0x02,0x07,0x0B,0x01,0x09, +0x78,0x08,0x08,0x10,0x47,0x01,0x01,0x02,0x0B,0xE0, +// '4' +0x34,0x02,0x07,0x0B,0x01,0x09, +0x04,0x18,0x51,0x22,0x48,0xA1,0x7F,0x04,0x08,0x10, +// '5' +0x35,0x02,0x07,0x0B,0x01,0x09, +0x7E,0x81,0x02,0x07,0x81,0x80,0x81,0x02,0x0B,0xE0, +// '6' +0x36,0x02,0x07,0x0B,0x01,0x09, +0x1C,0x61,0x00,0x0F,0x90,0xA0,0xC1,0x82,0x88,0xE0, +// '7' +0x37,0x02,0x07,0x0B,0x01,0x09, +0xFE,0x04,0x10,0x40,0x82,0x04,0x08,0x20,0x40,0x80, +// '8' +0x38,0x02,0x07,0x0B,0x01,0x09, +0x39,0x8A,0x0C,0x14,0x47,0x11,0x41,0x83,0x89,0xE0, +// '9' +0x39,0x02,0x07,0x0B,0x01,0x09, +0x38,0x8A,0x0C,0x18,0x28,0x4F,0x81,0x04,0x11,0xC0, +// ':' +0x3A,0x05,0x01,0x08,0x01,0x04, +0xC3, +// ';' +0x3B,0x05,0x02,0x0B,0x00,0x04, +0x50,0x05,0x48, +// '<' +0x3C,0x05,0x08,0x07,0x01,0x09, +0x02,0x0C,0x30,0x60,0x30,0x0C,0x02, +// '=' +0x3D,0x06,0x07,0x04,0x01,0x09, +0xFE,0x00,0x07,0xF0, +// '>' +0x3E,0x05,0x09,0x07,0x00,0x09, +0x40,0x1C,0x01,0x80,0x70,0x61,0xC1,0x00, +// '?' +0x3F,0x02,0x06,0x0B,0x01,0x07, +0x78,0x30,0x41,0x18,0xC2,0x00,0x00,0x82,0x00, +// '@' +0x40,0x02,0x0D,0x0D,0x01,0x0F, +0x0F,0x81,0x83,0x10,0x0C,0x8F,0xA8,0x84,0xC8,0x26,0x41,0x32,0x09,0x88,0x5A,0x3F,0x90,0x00,0x60,0x00,0xFC,0x00, +// 'A' +0x41,0x02,0x0B,0x0B,0x00,0x0B, +0x04,0x01,0xC0,0x28,0x08,0x81,0x10,0x61,0x08,0x21,0xFC,0x60,0x48,0x0B,0x00,0x80, +// 'B' +0x42,0x02,0x08,0x0B,0x01,0x0A, +0xF8,0x86,0x82,0x82,0x86,0xFC,0x82,0x81,0x81,0x82,0xFC, +// 'C' +0x43,0x02,0x09,0x0B,0x01,0x0B, +0x1F,0x10,0x10,0x10,0x08,0x04,0x02,0x01,0x00,0x40,0x30,0x07,0xC0, +// 'D' +0x44,0x02,0x09,0x0B,0x01,0x0B, +0xFC,0x41,0x20,0x50,0x18,0x0C,0x06,0x03,0x01,0x81,0x41,0x3F,0x00, +// 'E' +0x45,0x02,0x07,0x0B,0x01,0x09, +0xFF,0x02,0x04,0x08,0x1F,0xA0,0x40,0x81,0x03,0xF8, +// 'F' +0x46,0x02,0x07,0x0B,0x01,0x09, +0xFF,0x02,0x04,0x08,0x1F,0xA0,0x40,0x81,0x02,0x00, +// 'G' +0x47,0x02,0x09,0x0B,0x01,0x0B, +0x1F,0x10,0x10,0x10,0x08,0x04,0x02,0x03,0x01,0x40,0xB0,0x47,0xE0, +// 'H' +0x48,0x02,0x09,0x0B,0x01,0x0B, +0x80,0xC0,0x60,0x30,0x18,0x0F,0xFE,0x03,0x01,0x80,0xC0,0x60,0x20, +// 'I' +0x49,0x02,0x01,0x0B,0x01,0x03, +0xFF,0xE0, +// 'J' +0x4A,0x02,0x07,0x0B,0x00,0x08, +0x02,0x04,0x08,0x10,0x20,0x40,0x81,0x02,0x09,0xE0, +// 'K' +0x4B,0x02,0x09,0x0B,0x01,0x0A, +0x81,0x41,0x23,0x12,0x0A,0x06,0x02,0xC1,0x10,0x86,0x40,0xA0,0x20, +// 'L' +0x4C,0x02,0x07,0x0B,0x01,0x08, +0x81,0x02,0x04,0x08,0x10,0x20,0x40,0x81,0x03,0xF8, +// 'M' +0x4D,0x02,0x0B,0x0B,0x01,0x0D, +0x40,0x4C,0x19,0x01,0x28,0xA5,0x14,0x94,0xB2,0x9C,0x33,0x84,0x30,0x06,0x00,0x80, +// 'N' +0x4E,0x02,0x09,0x0B,0x01,0x0B, +0x80,0xE0,0x68,0x32,0x19,0x0C,0x46,0x13,0x05,0x82,0xC0,0xE0,0x20, +// 'O' +0x4F,0x02,0x0B,0x0B,0x01,0x0D, +0x1F,0x04,0x11,0x01,0x40,0x18,0x03,0x00,0x60,0x0C,0x01,0x40,0x44,0x10,0x7C,0x00, +// 'P' +0x50,0x02,0x08,0x0B,0x01,0x0A, +0xFC,0x82,0x81,0x81,0x81,0x82,0xFC,0x80,0x80,0x80,0x80, +// 'Q' +0x51,0x02,0x0B,0x0E,0x01,0x0D, +0x1F,0x04,0x11,0x01,0x40,0x18,0x03,0x00,0x60,0x0C,0x01,0x40,0x44,0x10,0x78,0x02,0x00,0x30,0x01,0x80, +// 'R' +0x52,0x02,0x09,0x0B,0x01,0x0A, +0xFC,0x41,0x20,0x50,0x28,0x14,0x13,0xF1,0x08,0x82,0x40,0xA0,0x20, +// 'S' +0x53,0x02,0x08,0x0B,0x01,0x09, +0x3C,0xC2,0x80,0x80,0x40,0x1C,0x06,0x02,0x02,0x06,0x78, +// 'T' +0x54,0x02,0x09,0x0B,0x00,0x09, +0xFF,0x84,0x02,0x01,0x00,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x00, +// 'U' +0x55,0x02,0x09,0x0B,0x01,0x0B, +0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xA0,0x8F,0x80, +// 'V' +0x56,0x02,0x09,0x0B,0x00,0x09, +0x80,0xE0,0xD0,0x48,0x26,0x21,0x10,0x88,0x28,0x14,0x0E,0x02,0x00, +// 'W' +0x57,0x02,0x0D,0x0B,0x00,0x0D, +0x80,0x0E,0x10,0xD0,0x84,0x8E,0x24,0x51,0x22,0x88,0xA2,0x85,0x14,0x38,0xE0,0xC2,0x04,0x10, +// 'X' +0x58,0x02,0x09,0x0B,0x00,0x09, +0xC1,0xA0,0x88,0x86,0xC1,0x40,0x60,0x70,0x6C,0x22,0x20,0xB0,0x60, +// 'Y' +0x59,0x02,0x09,0x0B,0x00,0x09, +0x80,0xA0,0x90,0x44,0x41,0x40,0xA0,0x20,0x10,0x08,0x04,0x02,0x00, +// 'Z' +0x5A,0x02,0x07,0x0B,0x01,0x09, +0xFE,0x04,0x10,0x41,0x02,0x08,0x00,0x41,0x03,0xF8, +// '[' +0x5B,0x00,0x03,0x10,0x02,0x05, +0xF2,0x49,0x24,0x92,0x49,0x27, +// '\' +0x5C,0x00,0x07,0x10,0x00,0x06, +0x80,0x81,0x02,0x02,0x04,0x08,0x08,0x10,0x10,0x20,0x40,0x40,0x81,0x01, +// ']' +0x5D,0x00,0x03,0x10,0x00,0x05, +0xE4,0x92,0x49,0x24,0x92,0x4F, +// '^' +0x5E,0x02,0x07,0x06,0x01,0x09, +0x10,0x70,0xA2,0x24,0x50,0x40, +// '_' +0x5F,0x0F,0x08,0x01,0x00,0x08, +0xFF, +// '`' +0x60,0x01,0x04,0x03,0x01,0x06, +0x86,0x10, +// 'a' +0x61,0x05,0x06,0x08,0x01,0x08, +0x78,0x30,0x5F,0xC6,0x18,0x5F, +// 'b' +0x62,0x01,0x07,0x0C,0x01,0x09, +0x81,0x02,0x04,0x0F,0x90,0xA0,0xC1,0x83,0x06,0x17,0xC0, +// 'c' +0x63,0x05,0x06,0x08,0x01,0x08, +0x3D,0x08,0x20,0x82,0x04,0x0F, +// 'd' +0x64,0x01,0x07,0x0C,0x01,0x09, +0x02,0x04,0x08,0x13,0xE8,0x60,0xC1,0x83,0x05,0x09,0xF0, +// 'e' +0x65,0x05,0x07,0x08,0x01,0x09, +0x3C,0x8A,0x0F,0xF8,0x10,0x10,0x1E, +// 'f' +0x66,0x01,0x05,0x0C,0x01,0x06, +0x7E,0x21,0x0F,0xC2,0x10,0x84,0x21,0x00, +// 'g' +0x67,0x05,0x07,0x0B,0x01,0x09, +0x3E,0x86,0x0C,0x18,0x30,0x50,0x9F,0x02,0x0B,0xE0, +// 'h' +0x68,0x01,0x07,0x0C,0x01,0x09, +0x81,0x02,0x04,0x0F,0x90,0xE0,0xC1,0x83,0x06,0x0C,0x10, +// 'i' +0x69,0x01,0x03,0x0C,0x00,0x03, +0x48,0x04,0x92,0x49,0x20, +// 'j' +0x6A,0x01,0x04,0x0F,0xFF,0x03, +0x22,0x00,0x22,0x22,0x22,0x22,0x22,0xC0, +// 'k' +0x6B,0x01,0x06,0x0C,0x01,0x08, +0x82,0x08,0x20,0x8A,0x4A,0x30,0xA2,0x48,0xA1, +// 'l' +0x6C,0x01,0x04,0x0C,0x01,0x04, +0x88,0x88,0x88,0x88,0x88,0x86, +// 'm' +0x6D,0x05,0x0B,0x08,0x01,0x0D, +0xFB,0xD1,0x8E,0x10,0xC2,0x18,0x43,0x08,0x61,0x0C,0x21, +// 'n' +0x6E,0x05,0x07,0x08,0x01,0x09, +0xFD,0x0E,0x0C,0x18,0x30,0x60,0xC1, +// 'o' +0x6F,0x05,0x08,0x08,0x01,0x0A, +0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C, +// 'p' +0x70,0x05,0x07,0x0B,0x01,0x09, +0xF9,0x0A,0x0C,0x18,0x30,0x61,0x7C,0x81,0x02,0x00, +// 'q' +0x71,0x05,0x07,0x0B,0x01,0x09, +0x3E,0x86,0x0C,0x18,0x30,0x50,0x9F,0x02,0x04,0x08, +// 'r' +0x72,0x05,0x05,0x08,0x01,0x06, +0xFC,0x21,0x08,0x42,0x10, +// 's' +0x73,0x05,0x05,0x08,0x01,0x07, +0x7C,0x20,0xC3,0x04,0x3E, +// 't' +0x74,0x02,0x05,0x0B,0x01,0x07, +0x84,0x21,0xF8,0x42,0x10,0x84,0x1E, +// 'u' +0x75,0x05,0x07,0x08,0x01,0x09, +0x83,0x06,0x0C,0x18,0x30,0x50,0xBF, +// 'v' +0x76,0x05,0x07,0x08,0x00,0x07, +0x83,0x05,0x12,0x22,0x85,0x0E,0x08, +// 'w' +0x77,0x05,0x0D,0x08,0x00,0x0D, +0x82,0x0C,0x10,0x51,0xC4,0x8A,0x26,0x5B,0x14,0x50,0xE3,0x82,0x08, +// 'x' +0x78,0x05,0x08,0x08,0x00,0x08, +0xC3,0x66,0x24,0x18,0x18,0x24,0x42,0xC3, +// 'y' +0x79,0x05,0x07,0x0B,0x00,0x07, +0x82,0x89,0x12,0x22,0x85,0x04,0x08,0x10,0x43,0x00, +// 'z' +0x7A,0x05,0x06,0x08,0x01,0x08, +0xFC,0x10,0x84,0x21,0x08,0x3F, +// '{' +0x7B,0x00,0x05,0x10,0x00,0x05, +0x19,0x08,0x42,0x10,0x98,0x61,0x08,0x42,0x10,0x83, +// '|' +0x7C,0x00,0x01,0x10,0x02,0x05, +0xFF,0xFF, +// '}' +0x7D,0x00,0x05,0x10,0x00,0x05, +0xC1,0x08,0x42,0x10,0x83,0x31,0x08,0x42,0x10,0x98, +// '~' +0x7E,0x07,0x07,0x02,0x01,0x09, +0x73,0x18, + +// Terminator +0xFF +}; diff --git a/components/epaper/comic24.c b/components/epaper/comic24.c new file mode 100644 index 0000000..2e534c9 --- /dev/null +++ b/components/epaper/comic24.c @@ -0,0 +1,331 @@ +// This comes with no warranty, implied or otherwise + +// This data structure was designed to support Proportional fonts +// on Arduinos. It can however handle any ttf font that has been converted +// using the conversion program. These could be fixed width or proportional +// fonts. Individual characters do not have to be multiples of 8 bits wide. +// Any width is fine and does not need to be fixed. + +// The data bits are packed to minimize data requirements, but the tradeoff +// is that a header is required per character. + +// comic.c +// Point Size : 24 +// Memory usage : 2814 bytes +// # characters : 95 + +// Header Format (to make Arduino UTFT Compatible): +// ------------------------------------------------ +// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) +// Character Height +// First Character (Reserved. 0x00) +// Number Of Characters (Reserved. 0x00) + +unsigned char tft_Comic24[] = +{ +0x00, 0x19, 0x00, 0x00, + +// Individual Character Format: +// ---------------------------- +// Character Code +// Adjusted Y Offset +// Width +// Height +// xOffset +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] + +// NOTE: You can remove any of these characters if they are not needed in +// your application. The first character number in each Glyph indicates +// the ASCII character code. Therefore, these do not have to be sequential. +// Just remove all the content for a particular character to save space. + +// ' ' +0x20,0x15,0x00,0x00,0x00,0x07, + +// '!' +0x21,0x02,0x02,0x14,0x01,0x06, +0xFF,0xFF,0xFF,0xFC,0x2D, +// '"' +0x22,0x03,0x06,0x08,0x02,0x0A, +0xCF,0x3C,0xF3,0xCF,0x3C,0xF3, +// '#' +0x23,0x03,0x14,0x12,0x01,0x14, +0x01,0x81,0x80,0x18,0x18,0x01,0x81,0x80,0x30,0x30,0x03,0x03,0x07,0xFF,0xFF,0x7F,0xFF,0xF0,0x60,0x60,0x06,0x06,0x00,0xC0,0xC0,0x0C,0x0C,0x0F,0xFF,0xFE,0xFF,0xFF,0xE1,0x81,0x80,0x18,0x18,0x03,0x83,0x00,0x30,0x30,0x03,0x03,0x00, +// '$' +0x24,0x00,0x0B,0x19,0x02,0x11, +0x0C,0x01,0x80,0x30,0x0F,0x83,0xFC,0xD9,0xBB,0x06,0x60,0xCC,0x19,0x83,0xB0,0x3F,0x83,0xFC,0x1B,0x83,0x18,0x63,0x0C,0x71,0x9F,0x37,0x7F,0xC3,0xF0,0x18,0x03,0x00,0x60,0x0C,0x00, +// '%' +0x25,0x01,0x11,0x14,0x02,0x14, +0x00,0x00,0x00,0x0C,0x0E,0x0E,0x0F,0x86,0x0C,0x67,0x06,0x33,0x03,0x19,0x80,0xF9,0x80,0x38,0xC0,0x00,0xE0,0x00,0x60,0x00,0x70,0x00,0x31,0xE0,0x39,0xF8,0x19,0xCE,0x1C,0xC3,0x0C,0x61,0x86,0x39,0xC6,0x0F,0xC3,0x03,0xC0, +// '&' +0x26,0x03,0x0F,0x13,0x01,0x10, +0x01,0xC0,0x07,0xC0,0x19,0x80,0x33,0x00,0x6E,0x00,0xF8,0x01,0xE0,0x07,0x80,0x1F,0x8C,0x73,0x19,0xC3,0x37,0x07,0xEC,0x07,0xD8,0x07,0x30,0x0E,0x38,0x7E,0x3F,0xEC,0x3F,0x0C,0x00,0x18, +// ''' +0x27,0x03,0x02,0x06,0x03,0x09, +0xFF,0xF0, +// '(' +0x28,0x02,0x07,0x18,0x01,0x09, +0x06,0x1C,0x71,0xC3,0x0E,0x18,0x30,0xE1,0x83,0x06,0x0C,0x18,0x30,0x60,0xE0,0xC1,0x83,0x83,0x83,0x87,0x83, +// ')' +0x29,0x02,0x06,0x18,0x02,0x09, +0xC3,0x86,0x0C,0x30,0x61,0x86,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x61,0x86,0x31,0xCE,0x30, +// '*' +0x2A,0x03,0x0B,0x09,0x01,0x0D, +0x0C,0x01,0x83,0xBF,0xFF,0xF3,0xFC,0x3C,0x0F,0xC3,0x9C,0x61,0x80, +// '+' +0x2B,0x09,0x0A,0x0A,0x00,0x0C, +0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0,0xC0,0x30,0x0C,0x03,0x00, +// ',' +0x2C,0x13,0x04,0x06,0x02,0x07, +0x37,0x66,0xEC, +// '-' +0x2D,0x0E,0x08,0x02,0x01,0x0A, +0xFF,0xFF, +// '.' +0x2E,0x12,0x03,0x03,0x02,0x06, +0xFF,0x80, +// '/' +0x2F,0x01,0x0A,0x15,0x01,0x0C, +0x00,0x00,0x30,0x0C,0x06,0x01,0x80,0x60,0x30,0x0C,0x06,0x01,0x80,0xC0,0x30,0x18,0x06,0x03,0x00,0xC0,0x60,0x18,0x0E,0x03,0x00,0xC0,0x00, +// '0' +0x30,0x03,0x0D,0x12,0x01,0x0F, +0x0F,0x80,0xFF,0x0E,0x18,0xE0,0x66,0x03,0x70,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x03,0xB0,0x19,0x81,0xC7,0x1C,0x3F,0xC0,0x7C,0x00, +// '1' +0x31,0x03,0x06,0x12,0x03,0x0B, +0x10,0xC7,0x3C,0xB0,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0xFF,0xF0, +// '2' +0x32,0x03,0x0B,0x12,0x02,0x0F, +0x1F,0x07,0xFB,0xC3,0xE0,0x30,0x06,0x00,0xC0,0x38,0x0E,0x07,0x81,0xE0,0xF8,0x3C,0x07,0x01,0xC0,0x30,0x06,0x00,0xFF,0xDF,0xFC, +// '3' +0x33,0x03,0x0B,0x12,0x02,0x0F, +0x1F,0x0F,0xF9,0xC3,0x80,0x30,0x06,0x00,0xC0,0x78,0x7E,0x0F,0x80,0x78,0x03,0x80,0x30,0x06,0x00,0xF0,0x1F,0x0E,0x7F,0x83,0xE0, +// '4' +0x34,0x03,0x0D,0x12,0x02,0x0F, +0x01,0xC0,0x0E,0x00,0xF0,0x0F,0x80,0x6C,0x07,0x60,0x33,0x03,0x98,0x38,0xC1,0x86,0x1C,0x31,0xFF,0xFF,0xFF,0x80,0x60,0x03,0x00,0x18,0x00,0xC0,0x06,0x00, +// '5' +0x35,0x02,0x0C,0x13,0x02,0x0F, +0x00,0x0F,0xFE,0xFF,0xE6,0x00,0x60,0x0E,0x00,0xEF,0x8F,0xFC,0xF8,0x6E,0x07,0xC0,0x30,0x03,0x00,0x30,0x03,0x00,0x7C,0x06,0xE1,0xE7,0xFC,0x3F,0x00, +// '6' +0x36,0x03,0x0C,0x12,0x01,0x0F, +0x03,0x00,0x70,0x0E,0x01,0xC0,0x38,0x03,0x00,0x60,0x06,0xF8,0xFF,0xEE,0x0E,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x77,0x0E,0x3F,0xC1,0xF8, +// '7' +0x37,0x02,0x0D,0x13,0x01,0x0F, +0x00,0x07,0xFF,0xFF,0xFE,0x00,0xE0,0x0E,0x00,0x60,0x06,0x00,0x30,0x03,0x80,0x18,0x01,0xC0,0x0C,0x00,0x60,0x07,0x00,0x30,0x03,0x80,0x18,0x00,0xC0,0x04,0x00, +// '8' +0x38,0x02,0x0C,0x13,0x01,0x0F, +0x00,0x00,0xFC,0x3F,0xE3,0x07,0x60,0x36,0x03,0x60,0x37,0x8F,0x3F,0xE1,0xFE,0x38,0xE7,0x07,0x60,0x36,0x03,0x60,0x36,0x03,0x30,0x63,0xFE,0x0F,0x80, +// '9' +0x39,0x03,0x0D,0x13,0x01,0x0F, +0x0F,0x01,0xFE,0x1C,0x38,0xC0,0xCC,0x07,0x60,0x1B,0x00,0xD8,0x06,0xE0,0x73,0x87,0x8F,0xF8,0x3E,0xC0,0x0E,0x00,0x60,0x07,0x00,0xF0,0x1F,0x03,0xE0,0x1C,0x00, +// ':' +0x3A,0x09,0x03,0x0B,0x02,0x07, +0xFF,0x80,0x00,0xFF,0x80, +// ';' +0x3B,0x09,0x04,0x0E,0x02,0x07, +0xEE,0xE0,0x00,0x00,0x03,0x7E,0xCC, +// '<' +0x3C,0x09,0x07,0x0A,0x01,0x09, +0x06,0x1C,0x71,0xC7,0x1E,0x1E,0x0E,0x0E,0x0C, +// '=' +0x3D,0x0A,0x09,0x09,0x01,0x0C, +0xFF,0xFF,0xC0,0x00,0x00,0x00,0x03,0xFF,0xFF,0x00,0x00, +// '>' +0x3E,0x08,0x08,0x0B,0x01,0x0A, +0x60,0x70,0x38,0x3C,0x1E,0x0F,0x06,0x0C,0x38,0x70,0xC0, +// '?' +0x3F,0x04,0x0B,0x12,0x01,0x0D, +0x1E,0x0F,0xE3,0xC6,0x60,0x60,0x06,0x00,0xC0,0x18,0x07,0x01,0xE0,0xF8,0x3E,0x0F,0x01,0x80,0x00,0x00,0x01,0x80,0x30,0x06,0x00, +// '@' +0x40,0x02,0x13,0x14,0x01,0x16, +0x03,0xF8,0x01,0xFF,0xC0,0x78,0x3C,0x1C,0x01,0xC3,0x00,0x1C,0xC1,0xC1,0x98,0xF8,0x1E,0x3C,0x03,0xC6,0x30,0x79,0x8E,0x0F,0x31,0xC1,0xE6,0x78,0x6C,0x7F,0xFC,0xC7,0x3E,0x18,0x00,0x01,0x80,0x00,0x38,0x00,0x03,0xC0,0xE0,0x1F,0xFC,0x00,0xFE,0x00, +// 'A' +0x41,0x03,0x0E,0x12,0x01,0x11, +0x00,0x80,0x07,0x00,0x1C,0x00,0xF0,0x03,0xC0,0x1D,0x80,0x76,0x03,0x98,0x0E,0x20,0x70,0xC1,0xFF,0x0F,0xFC,0x7C,0x19,0xC0,0x67,0x01,0xB8,0x07,0xE0,0x0F,0x00,0x30, +// 'B' +0x42,0x03,0x0B,0x13,0x03,0x0F, +0x7C,0x1F,0xE3,0x0E,0x60,0xEC,0x0D,0x81,0xB0,0x36,0x0E,0xC3,0x9F,0xE3,0xFC,0x61,0xEC,0x0F,0x80,0xF0,0x1E,0x0E,0xC7,0xDF,0xE3,0xF0,0x00, +// 'C' +0x43,0x03,0x0D,0x12,0x01,0x0E, +0x01,0xF8,0x3F,0xC3,0xC6,0x38,0x31,0x80,0x1C,0x01,0xC0,0x0C,0x00,0x60,0x06,0x00,0x30,0x01,0x80,0x0C,0x00,0x60,0x19,0x81,0xCE,0x3C,0x3F,0xC0,0xF8,0x00, +// 'D' +0x44,0x03,0x0D,0x12,0x02,0x11, +0x60,0x07,0xC0,0x37,0x81,0x8F,0x0C,0x1C,0x60,0x73,0x01,0xD8,0x06,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x1B,0x01,0xDC,0x1C,0xFF,0xC1,0xF8,0x00, +// 'E' +0x45,0x03,0x0D,0x12,0x02,0x0F, +0xFF,0xF7,0xFF,0xF0,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x7E,0xFF,0xF7,0xE0,0x30,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x00,0x7F,0xF1,0xFF,0x80, +// 'F' +0x46,0x03,0x0C,0x12,0x02,0x0F, +0xFF,0xCF,0xFF,0xC0,0x7C,0x00,0xC0,0x0C,0x00,0xC0,0x0D,0xFE,0xFF,0xEF,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00, +// 'G' +0x47,0x03,0x0F,0x12,0x01,0x10, +0x03,0xE0,0x0F,0xF0,0x38,0xE0,0xE0,0x03,0x80,0x06,0x00,0x18,0x00,0x30,0x00,0x61,0xFF,0x9F,0xFF,0x3C,0x36,0x00,0x6C,0x01,0x98,0x07,0x30,0x0C,0x30,0x70,0x7F,0xC0,0x3E,0x00, +// 'H' +0x48,0x03,0x0F,0x12,0x02,0x12, +0xC0,0x03,0x80,0x0F,0x00,0x1E,0x00,0x3C,0x00,0x78,0x00,0xF0,0x01,0xE0,0x03,0xC0,0xFF,0xFF,0xFF,0xFC,0x1E,0x00,0x3C,0x00,0x78,0x00,0xF0,0x01,0xE0,0x03,0xC0,0x07,0x80,0x0C, +// 'I' +0x49,0x03,0x0C,0x12,0x00,0x0D, +0xFF,0xEF,0xFF,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0xFF,0xFF,0xFF, +// 'J' +0x4A,0x03,0x0E,0x12,0x01,0x10, +0x1F,0xFC,0x7F,0xF0,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0xC0,0xC3,0x06,0x0E,0x18,0x1C,0x60,0x3F,0x80,0x3C,0x00, +// 'K' +0x4B,0x03,0x0C,0x12,0x03,0x0F, +0xC0,0x6C,0x0E,0xC1,0xCC,0x38,0xC7,0x0C,0xE0,0xDC,0x0F,0x80,0xF0,0x0F,0x00,0xF8,0x0F,0xC0,0xDE,0x0C,0xF0,0xC7,0x8C,0x1E,0xC0,0xFC,0x07, +// 'L' +0x4C,0x03,0x0B,0x12,0x01,0x0D, +0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xEF,0xFC, +// 'M' +0x4D,0x03,0x13,0x13,0x01,0x15, +0x0C,0x06,0x01,0x80,0xC0,0x78,0x3C,0x0F,0x07,0x81,0xE0,0xF0,0x3C,0x1E,0x07,0x83,0xC1,0xD8,0xEC,0x3B,0x1D,0x87,0x63,0xB0,0xCC,0xE6,0x38,0xDC,0x47,0x1B,0x8C,0xE3,0xF1,0xB8,0x3C,0x37,0x07,0x86,0xE0,0xF0,0x7C,0x1E,0x0F,0x01,0x81,0x80, +// 'N' +0x4E,0x03,0x11,0x12,0x01,0x13, +0x60,0x01,0x38,0x00,0xDE,0x00,0x6F,0x00,0x37,0xC0,0x1B,0x70,0x0D,0x9C,0x06,0xCF,0x03,0x63,0x81,0xB0,0xE0,0xD8,0x38,0x6C,0x0E,0x36,0x03,0x9B,0x00,0xED,0x80,0x3E,0xC0,0x0F,0x60,0x03,0xB0,0x00,0xC0, +// 'O' +0x4F,0x03,0x11,0x12,0x01,0x13, +0x01,0xF8,0x03,0xFF,0x07,0x81,0xC3,0x00,0x63,0x00,0x1B,0x80,0x0D,0x80,0x07,0xC0,0x03,0xC0,0x01,0xE0,0x00,0xF0,0x00,0xF8,0x00,0x6C,0x00,0x33,0x00,0x31,0xC0,0x38,0x70,0x78,0x1F,0xF8,0x03,0xF0,0x00, +// 'P' +0x50,0x03,0x0B,0x12,0x01,0x0D, +0xFE,0x1F,0xF3,0x0F,0x60,0x7C,0x07,0x80,0xF0,0x1E,0x06,0xC3,0xDF,0xF3,0xF8,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x00, +// 'Q' +0x51,0x03,0x14,0x17,0x01,0x15, +0x01,0xF8,0x00,0x7F,0xE0,0x1E,0x07,0x03,0x80,0x18,0x30,0x01,0xC6,0x00,0x0C,0x60,0x00,0xEC,0x00,0x06,0xC0,0x00,0x6C,0x00,0x06,0xC0,0x00,0x6C,0x00,0x06,0x60,0xE0,0xE7,0x0F,0x0C,0x38,0x79,0xC1,0xC3,0xF8,0x0F,0xFF,0x00,0x3F,0x78,0x00,0x03,0xC0,0x00,0x1E,0x00,0x00,0xF0,0x00,0x07,0x00,0x00,0x20, +// 'R' +0x52,0x02,0x0D,0x13,0x01,0x0F, +0x00,0x03,0xE0,0x3F,0xC1,0x8F,0x0C,0x0E,0x60,0x33,0x00,0xD8,0x06,0xC0,0x36,0x03,0xB0,0x79,0xFF,0x8F,0xF0,0x7F,0x83,0x1F,0x18,0x3C,0xC0,0xF6,0x01,0xF0,0x06, +// 'S' +0x53,0x03,0x0F,0x13,0x01,0x11, +0x01,0xF0,0x07,0xF8,0x18,0x70,0x60,0x01,0x80,0x03,0x00,0x06,0x00,0x0E,0x00,0x0F,0xF0,0x07,0xF0,0x00,0xF0,0x00,0x70,0x00,0x60,0x00,0xD8,0x01,0xB8,0x06,0x78,0x3C,0x7F,0xE0,0x3F,0x00, +// 'T' +0x54,0x02,0x0F,0x13,0x01,0x10, +0x00,0x01,0xFF,0xFD,0xFF,0xF8,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00, +// 'U' +0x55,0x03,0x11,0x12,0x01,0x12, +0x60,0x03,0x30,0x01,0x98,0x00,0xCC,0x00,0x66,0x00,0x33,0x00,0x19,0x80,0x0C,0xC0,0x06,0x60,0x03,0x30,0x01,0x98,0x01,0xCC,0x00,0xC7,0x00,0x61,0x80,0x70,0xE0,0x30,0x38,0x38,0x0F,0xF8,0x01,0xF0,0x00, +// 'V' +0x56,0x03,0x0E,0x13,0x02,0x10, +0x80,0x0F,0x00,0x3C,0x01,0xB0,0x06,0x60,0x31,0x80,0xC6,0x03,0x0C,0x18,0x30,0x60,0xC1,0x81,0x8C,0x06,0x30,0x0D,0x80,0x36,0x00,0xF8,0x01,0xC0,0x07,0x00,0x08,0x00,0x00,0x00, +// 'W' +0x57,0x03,0x17,0x12,0x01,0x19, +0xC0,0x20,0x0F,0xC0,0x60,0x19,0x81,0xC0,0x23,0x03,0x80,0xC6,0x07,0x01,0x86,0x1E,0x03,0x0C,0x36,0x0C,0x18,0x6C,0x18,0x11,0x98,0x60,0x33,0x30,0xC0,0x66,0x61,0x80,0xD8,0x66,0x01,0xB0,0xCC,0x01,0xC1,0xB0,0x03,0x83,0x60,0x07,0x07,0x80,0x0C,0x07,0x00,0x08,0x0E,0x00, +// 'X' +0x58,0x03,0x10,0x12,0x01,0x11, +0x60,0x03,0x70,0x07,0x38,0x0E,0x1C,0x1C,0x0C,0x1C,0x0E,0x38,0x07,0x70,0x03,0xE0,0x01,0xC0,0x03,0xC0,0x07,0xE0,0x07,0x70,0x0E,0x38,0x1C,0x18,0x38,0x1C,0x70,0x0E,0xE0,0x07,0xC0,0x03, +// 'Y' +0x59,0x03,0x0F,0x13,0x00,0x10, +0x60,0x06,0xE0,0x1D,0xC0,0x31,0xC0,0xE1,0xC1,0x83,0x83,0x03,0x8C,0x07,0x18,0x07,0x70,0x0F,0xC0,0x0F,0x80,0x0F,0x00,0x1C,0x00,0x38,0x00,0x60,0x01,0xC0,0x03,0x00,0x06,0x00,0x08,0x00, +// 'Z' +0x5A,0x03,0x0F,0x12,0x01,0x11, +0xFF,0xFF,0xFF,0xFC,0x00,0xF0,0x03,0x80,0x0E,0x00,0x3C,0x00,0xF0,0x03,0xC0,0x07,0x00,0x1E,0x00,0x38,0x00,0xE0,0x03,0xC0,0x07,0x00,0x1C,0x00,0x70,0x00,0xFF,0xFF,0xFF,0xFC, +// '[' +0x5B,0x01,0x07,0x1A,0x01,0x09, +0x00,0xFD,0xFB,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x3F,0x7E,0x00, +// '\' +0x5C,0x03,0x0B,0x14,0x02,0x0D, +0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x60,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x20,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x60, +// ']' +0x5D,0x01,0x07,0x1A,0x02,0x09, +0x01,0xFB,0xF0,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x7E,0xFC,0x00, +// '^' +0x5E,0x02,0x0A,0x06,0x02,0x0E, +0x0C,0x07,0x83,0xF1,0xCE,0xE1,0xF0,0x30, +// '_' +0x5F,0x16,0x0F,0x04,0x00,0x0F, +0x00,0x01,0xFF,0xFF,0xFF,0xF8,0x00,0x00, +// '`' +0x60,0x02,0x05,0x06,0x02,0x0D, +0xC7,0x1C,0x63,0x8C, +// 'a' +0x61,0x09,0x0B,0x0C,0x01,0x0C, +0x0F,0x87,0xF9,0xE3,0x30,0x6E,0x0D,0x81,0xB0,0x36,0x06,0xC0,0xCC,0x39,0xFF,0x9F,0x30, +// 'b' +0x62,0x02,0x0C,0x13,0x01,0x0E, +0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x78,0x7F,0xC7,0x8E,0x60,0x76,0x03,0x60,0x36,0x03,0x60,0x36,0x06,0x70,0xE7,0xFC,0x7F,0x00, +// 'c' +0x63,0x09,0x0A,0x0C,0x01,0x0C, +0x0F,0x07,0xF3,0x0D,0x80,0x60,0x30,0x0C,0x03,0x00,0xC0,0x1C,0x33,0xFC,0x7C, +// 'd' +0x64,0x02,0x0C,0x13,0x01,0x0E, +0x00,0x20,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x61,0xF6,0x3F,0xE7,0x0E,0x60,0x6C,0x06,0xC0,0x6C,0x06,0xC0,0x6E,0x06,0x70,0xE3,0xFE,0x1F,0x60, +// 'e' +0x65,0x09,0x0B,0x0C,0x01,0x0D, +0x1F,0x07,0xF9,0xC7,0x30,0xEC,0x79,0xBE,0x3E,0x07,0x00,0xC0,0x6E,0x1D,0xFF,0x0F,0x80, +// 'f' +0x66,0x02,0x0A,0x14,0x01,0x0C, +0x03,0x83,0xE0,0xE0,0x70,0x18,0x06,0x01,0x83,0xFF,0xFF,0xC6,0x01,0x80,0x60,0x18,0x06,0x01,0x80,0x60,0x18,0x06,0x01,0x80,0x60, +// 'g' +0x67,0x09,0x0A,0x13,0x02,0x0D, +0x0F,0x0F,0xF7,0x0D,0x83,0xC0,0xF0,0x3C,0x1F,0x07,0xC1,0xD8,0xF7,0xEC,0xF3,0x00,0xC0,0x30,0x18,0x06,0x03,0xBF,0xC7,0xE0, +// 'h' +0x68,0x02,0x0B,0x13,0x01,0x0E, +0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x1E,0x6F,0xEF,0x8D,0xE1,0xB8,0x36,0x06,0xC0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0x80, +// 'i' +0x69,0x04,0x02,0x11,0x03,0x07, +0xF0,0x3F,0xFF,0xFF,0xC0, +// 'j' +0x6A,0x04,0x08,0x18,0x00,0x0A, +0x03,0x03,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xC3,0xE3,0x77,0x7E,0x1C, +// 'k' +0x6B,0x03,0x0B,0x13,0x02,0x0E, +0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x36,0x0E,0xC7,0x99,0xE3,0x70,0x7E,0x0F,0xE1,0xCE,0x30,0xE6,0x0E,0xC0,0xF8,0x08,0x00,0x00, +// 'l' +0x6C,0x02,0x02,0x13,0x03,0x07, +0xFF,0xFF,0xFF,0xFF,0xFC, +// 'm' +0x6D,0x09,0x10,0x0C,0x01,0x12, +0x67,0x3C,0x6F,0xFE,0x7D,0xEE,0x79,0x86,0x71,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86, +// 'n' +0x6E,0x09,0x0B,0x0C,0x01,0x0D, +0x63,0x8D,0xF9,0xF1,0xBC,0x37,0x06,0xE0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0xB0,0x30, +// 'o' +0x6F,0x09,0x0C,0x0C,0x01,0x0D, +0x0F,0x81,0xFC,0x38,0xC3,0x06,0x60,0x66,0x06,0x60,0x66,0x06,0x60,0xE3,0x1C,0x1F,0x80,0xF0, +// 'p' +0x70,0x08,0x0A,0x14,0x02,0x0D, +0xC0,0x33,0xCF,0xFB,0xC6,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x7C,0x1B,0xFC,0xFE,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00, +// 'q' +0x71,0x08,0x0A,0x14,0x01,0x0C, +0x00,0x03,0xF3,0xFD,0xE3,0x60,0xF8,0x3C,0x0F,0x03,0xC0,0xF0,0x76,0x1D,0xFF,0x1F,0x80,0x60,0x18,0x06,0x01,0x80,0x60,0x18,0x06, +// 'r' +0x72,0x09,0x09,0x0C,0x01,0x0B, +0xCF,0x6F,0xFE,0x7C,0x3C,0x1E,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x00, +// 's' +0x73,0x09,0x09,0x0C,0x02,0x0C, +0x03,0x9F,0xDE,0x7C,0x3E,0x07,0xF0,0xFC,0x07,0x01,0xE0,0xFF,0xC7,0xC0, +// 't' +0x74,0x05,0x0A,0x10,0x00,0x0A, +0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30, +// 'u' +0x75,0x09,0x0B,0x0C,0x01,0x0C, +0xC0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0xB0,0x36,0x06,0xC0,0xD8,0x19,0xFF,0x1F,0x60, +// 'v' +0x76,0x09,0x0B,0x0D,0x01,0x0C, +0xC0,0x78,0x1F,0x83,0x30,0x67,0x1C,0x63,0x0C,0xE0,0xD8,0x1E,0x03,0xC0,0x30,0x06,0x00,0x00, +// 'w' +0x77,0x09,0x0F,0x0D,0x01,0x11, +0xC1,0x87,0x83,0x0F,0x0E,0x1E,0x1C,0x66,0x7C,0xCC,0xD9,0x99,0x36,0x36,0x6C,0x7C,0xD8,0x70,0xE0,0xE1,0xC0,0x83,0x80,0x00,0x00, +// 'x' +0x78,0x09,0x0D,0x0D,0x01,0x0E, +0x60,0x1B,0x81,0xCE,0x1C,0x39,0xC0,0xFC,0x03,0xC0,0x3C,0x03,0xF0,0x39,0xC3,0x87,0x38,0x1D,0x80,0x70,0x01,0x80, +// 'y' +0x79,0x09,0x0C,0x13,0x00,0x0D, +0xC0,0x3E,0x07,0x60,0x67,0x0C,0x30,0xC3,0x98,0x19,0x81,0xD8,0x0F,0x00,0xF0,0x06,0x00,0x60,0x0C,0x00,0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x30,0x00, +// 'z' +0x7A,0x09,0x0B,0x0C,0x01,0x0D, +0xFF,0xFF,0xFC,0x07,0x00,0xC0,0x30,0x0C,0x03,0x80,0xE0,0x38,0x0E,0x03,0xFF,0xFF,0xF0, +// '{' +0x7B,0x02,0x08,0x18,0x01,0x09, +0x0F,0x1F,0x38,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x60,0xE0,0xE0,0x70,0x30,0x30,0x30,0x30,0x30,0x38,0x18,0x1F,0x07, +// '|' +0x7C,0x01,0x02,0x18,0x04,0x0A, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +// '}' +0x7D,0x02,0x08,0x18,0x01,0x09, +0x70,0xF8,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x06,0x07,0x07,0x0E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1C,0x18,0xF8,0xE0, +// '~' +0x7E,0x0B,0x0C,0x05,0x01,0x0E, +0x38,0x37,0xE3,0xE7,0x7C,0x3E,0x01,0xC0, + +// Terminator +0xFF +}; diff --git a/components/epaper/component.mk b/components/epaper/component.mk new file mode 100644 index 0000000..0f76ee5 --- /dev/null +++ b/components/epaper/component.mk @@ -0,0 +1,7 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/components/epaper/dejavuX.c b/components/epaper/dejavuX.c new file mode 100644 index 0000000..c7215e4 --- /dev/null +++ b/components/epaper/dejavuX.c @@ -0,0 +1,718 @@ +// This comes with no warranty, implied or otherwise + +// This data structure was designed to support Proportional fonts +// on Arduinos. It can however handle any ttf font that has been converted +// using the conversion program. These could be fixed width or proportional +// fonts. Individual characters do not have to be multiples of 8 bits wide. +// Any width is fine and does not need to be fixed. + +// The data bits are packed to minimize data requirements, but the tradeoff +// is that a header is required per character. + +// dejavuX.c +// Point Size : 18 +// Memory usage : 4676 bytes +// # characters : 224 + +// Header Format (to make Arduino UTFT Compatible): +// ------------------------------------------------ +// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) +// Character Height +// First Character (Reserved. 0x00) +// Number Of Characters (Reserved. 0x00) + +const unsigned char tft_Dejavu18[] = +{ +0x00, 0x12, 0x00, 0x00, + +// Individual Character Format: +// ---------------------------- +// Character Code +// Adjusted Y Offset +// Width +// Height +// xOffset +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] + +// NOTE: You can remove any of these characters if they are not needed in +// your application. The first character number in each Glyph indicates +// the ASCII character code. Therefore, these do not have to be sequential. +// Just remove all the content for a particular character to save space. + +// ' ' +0x20,0x11,0x00,0x00,0x00,0x06, + +// '!' +0x21,0x04,0x02,0x0D,0x03,0x07, +0xFF,0xFF,0xC3,0xC0, +// '"' +0x22,0x04,0x06,0x05,0x01,0x08, +0xCF,0x3C,0xF3,0xCC, +// '#' +0x23,0x03,0x0C,0x0E,0x01,0x0F, +0x04,0x40,0x44,0x0C,0xC0,0xC8,0x7F,0xF7,0xFF,0x09,0x81,0x90,0xFF,0xEF,0xFE,0x13,0x03,0x30,0x32,0x02,0x20, +// '$' +0x24,0x03,0x0A,0x11,0x01,0x0B, +0x08,0x02,0x03,0xE1,0xFC,0xE9,0x32,0x0F,0x81,0xF8,0x0F,0x02,0x60,0x9A,0x2E,0xFF,0x1F,0x80,0x80,0x20,0x08,0x00, +// '%' +0x25,0x04,0x0F,0x0D,0x01,0x11, +0x78,0x10,0x90,0x43,0x31,0x86,0x62,0x0C,0xC8,0x19,0x10,0x1E,0x4F,0x01,0x12,0x02,0x66,0x08,0xCC,0x31,0x98,0x41,0x21,0x03,0xC0, +// '&' +0x26,0x04,0x0C,0x0D,0x01,0x0D, +0x0F,0x01,0xF8,0x30,0x83,0x00,0x38,0x03,0xC0,0x7E,0x6C,0x76,0xC3,0xCC,0x18,0xE1,0xC7,0xFE,0x3E,0x70, +// ''' +0x27,0x04,0x02,0x05,0x01,0x04, +0xFF,0xC0, +// '(' +0x28,0x03,0x04,0x10,0x02,0x07, +0x32,0x66,0x4C,0xCC,0xCC,0xC4,0x66,0x23, +// ')' +0x29,0x03,0x04,0x10,0x01,0x07, +0xC4,0x66,0x23,0x33,0x33,0x32,0x66,0x4C, +// '*' +0x2A,0x04,0x07,0x08,0x01,0x09, +0x11,0x25,0x51,0xC3,0x8A,0xA4,0x88, +// '+' +0x2B,0x05,0x0C,0x0C,0x02,0x0F, +0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x0F,0xFF,0xFF,0xF0,0x60,0x06,0x00,0x60,0x06,0x00,0x60, +// ',' +0x2C,0x0F,0x03,0x04,0x01,0x06, +0x6D,0x40, +// '-' +0x2D,0x0B,0x05,0x02,0x01,0x07, +0xFF,0xC0, +// '.' +0x2E,0x0F,0x02,0x02,0x02,0x06, +0xF0, +// '/' +0x2F,0x04,0x06,0x0F,0x00,0x06, +0x0C,0x31,0x86,0x18,0xE3,0x0C,0x31,0xC6,0x18,0x63,0x0C,0x00, +// '0' +0x30,0x04,0x09,0x0D,0x01,0x0B, +0x3E,0x3F,0x98,0xD8,0x3C,0x1E,0x0F,0x07,0x83,0xC1,0xE0,0xD8,0xCF,0xE3,0xE0, +// '1' +0x31,0x04,0x08,0x0D,0x02,0x0B, +0x38,0xF8,0xD8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xFF, +// '2' +0x32,0x04,0x09,0x0D,0x01,0x0B, +0x7C,0x7F,0x21,0xC0,0x60,0x30,0x30,0x18,0x18,0x18,0x18,0x18,0x1F,0xEF,0xF0, +// '3' +0x33,0x04,0x09,0x0D,0x01,0x0B, +0x7E,0x7F,0xA0,0xE0,0x30,0x39,0xF0,0xFC,0x07,0x01,0x80,0xE0,0xFF,0xE7,0xE0, +// '4' +0x34,0x04,0x0A,0x0D,0x01,0x0B, +0x07,0x01,0xC0,0xB0,0x6C,0x13,0x08,0xC6,0x31,0x0C,0xFF,0xFF,0xF0,0x30,0x0C,0x03,0x00, +// '5' +0x35,0x04,0x08,0x0D,0x01,0x0B, +0x7E,0x7E,0x60,0x60,0x7C,0x7E,0x47,0x03,0x03,0x03,0x87,0xFE,0x7C, +// '6' +0x36,0x04,0x09,0x0D,0x01,0x0B, +0x1E,0x1F,0x9C,0x5C,0x0C,0x06,0xF3,0xFD,0xC7,0xC1,0xE0,0xD8,0xEF,0xE1,0xE0, +// '7' +0x37,0x04,0x08,0x0D,0x01,0x0B, +0xFF,0xFF,0x06,0x06,0x06,0x0E,0x0C,0x0C,0x1C,0x18,0x18,0x38,0x30, +// '8' +0x38,0x04,0x09,0x0D,0x01,0x0B, +0x3E,0x3F,0xB8,0xF8,0x3E,0x39,0xF1,0xFD,0xC7,0xC1,0xE0,0xF8,0xEF,0xE3,0xE0, +// '9' +0x39,0x04,0x09,0x0D,0x01,0x0B, +0x3C,0x3F,0xB8,0xD8,0x3C,0x1F,0x1D,0xFE,0x7B,0x01,0x81,0xD1,0xCF,0xC3,0xC0, +// ':' +0x3A,0x08,0x02,0x09,0x02,0x06, +0xF0,0x03,0xC0, +// ';' +0x3B,0x08,0x03,0x0B,0x01,0x06, +0x6C,0x00,0x03,0x6A,0x00, +// '<' +0x3C,0x07,0x0B,0x0A,0x02,0x0F, +0x00,0x20,0x3C,0x1F,0x1F,0x0F,0x81,0xF0,0x0F,0x80,0x3E,0x01,0xE0,0x04, +// '=' +0x3D,0x08,0x0B,0x06,0x02,0x0F, +0xFF,0xFF,0xFC,0x00,0x00,0x0F,0xFF,0xFF,0xC0, +// '>' +0x3E,0x07,0x0B,0x0A,0x02,0x0F, +0x80,0x1E,0x01,0xF0,0x07,0xC0,0x3E,0x07,0xC3,0xE3,0xE0,0xF0,0x10,0x00, +// '?' +0x3F,0x04,0x07,0x0D,0x01,0x0A, +0x79,0xFA,0x38,0x30,0x61,0x86,0x18,0x30,0x60,0x01,0x83,0x00, +// '@' +0x40,0x04,0x10,0x10,0x01,0x12, +0x07,0xE0,0x1F,0xF8,0x3C,0x1C,0x70,0x06,0x60,0x07,0xE3,0x63,0xC7,0xE3,0xC6,0x63,0xC6,0x66,0xC7,0xFC,0xE3,0x70,0x60,0x00,0x70,0x00,0x3C,0x30,0x1F,0xF0,0x07,0xC0, +// 'A' +0x41,0x04,0x0C,0x0D,0x00,0x0C, +0x06,0x00,0x60,0x0F,0x00,0xF0,0x19,0x81,0x98,0x19,0x83,0x0C,0x3F,0xC7,0xFE,0x60,0x66,0x06,0xC0,0x30, +// 'B' +0x42,0x04,0x09,0x0D,0x02,0x0C, +0xFC,0x7F,0xB0,0xD8,0x6C,0x37,0xF3,0xF9,0x86,0xC1,0xE0,0xF0,0xFF,0xEF,0xE0, +// 'C' +0x43,0x04,0x0B,0x0D,0x01,0x0D, +0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x0C,0x01,0xC0,0x9F,0xF0,0xFC, +// 'D' +0x44,0x04,0x0B,0x0D,0x02,0x0E, +0xFE,0x1F,0xF3,0x07,0x60,0x6C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x1B,0x07,0x7F,0xCF,0xE0, +// 'E' +0x45,0x04,0x08,0x0D,0x02,0x0B, +0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, +// 'F' +0x46,0x04,0x08,0x0D,0x02,0x0A, +0xFF,0xFF,0xC0,0xC0,0xC0,0xFE,0xFE,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +// 'G' +0x47,0x04,0x0B,0x0D,0x01,0x0E, +0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x87,0xF0,0xFE,0x03,0xC0,0x6C,0x0D,0xC1,0x9F,0xE0,0xF8, +// 'H' +0x48,0x04,0x0A,0x0D,0x02,0x0E, +0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xFF,0xFF,0xFF,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xC0, +// 'I' +0x49,0x04,0x02,0x0D,0x02,0x06, +0xFF,0xFF,0xFF,0xC0, +// 'J' +0x4A,0x04,0x05,0x11,0xFF,0x06, +0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0x8C,0xFE,0xE0, +// 'K' +0x4B,0x04,0x0B,0x0D,0x02,0x0C, +0xC1,0x98,0x63,0x18,0x66,0x0D,0x81,0xE0,0x3C,0x06,0xC0,0xCC,0x18,0xC3,0x0C,0x60,0xCC,0x0C, +// 'L' +0x4C,0x04,0x08,0x0D,0x02,0x0A, +0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, +// 'M' +0x4D,0x04,0x0C,0x0D,0x02,0x10, +0xE0,0x7F,0x0F,0xF0,0xFD,0x8B,0xD9,0xBD,0x9B,0xCF,0x3C,0xF3,0xC6,0x3C,0x63,0xC0,0x3C,0x03,0xC0,0x30, +// 'N' +0x4E,0x04,0x0A,0x0D,0x02,0x0E, +0xE0,0xF8,0x3F,0x0F,0xC3,0xD8,0xF6,0x3C,0xCF,0x1B,0xC6,0xF0,0xFC,0x3F,0x07,0xC1,0xC0, +// 'O' +0x4F,0x04,0x0C,0x0D,0x01,0x0E, +0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80, +// 'P' +0x50,0x04,0x08,0x0D,0x02,0x0B, +0xFC,0xFE,0xC7,0xC3,0xC3,0xC7,0xFE,0xFC,0xC0,0xC0,0xC0,0xC0,0xC0, +// 'Q' +0x51,0x04,0x0C,0x0F,0x01,0x0E, +0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80,0x18,0x00,0xC0, +// 'R' +0x52,0x04,0x0A,0x0D,0x02,0x0D, +0xFC,0x3F,0x8C,0x73,0x0C,0xC3,0x31,0xCF,0xE3,0xF0,0xC6,0x30,0xCC,0x33,0x06,0xC1,0xC0, +// 'S' +0x53,0x04,0x0A,0x0D,0x01,0x0B, +0x3E,0x1F,0xCE,0x13,0x00,0xC0,0x1F,0x03,0xF0,0x0E,0x01,0x80,0x68,0x3B,0xFC,0x7E,0x00, +// 'T' +0x54,0x04,0x0C,0x0D,0x00,0x0C, +0xFF,0xFF,0xFF,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00, +// 'U' +0x55,0x04,0x0A,0x0D,0x02,0x0E, +0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x36,0x19,0xFE,0x1E,0x00, +// 'V' +0x56,0x04,0x0C,0x0D,0x00,0x0C, +0xC0,0x36,0x06,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60,0x06,0x00, +// 'W' +0x57,0x04,0x11,0x0D,0x01,0x13, +0xC1,0xC1,0xE0,0xE0,0xD8,0xF8,0xCC,0x6C,0x66,0x36,0x33,0x1B,0x18,0xD8,0xD8,0x6C,0x6C,0x36,0x36,0x1F,0x1F,0x07,0x07,0x03,0x83,0x81,0xC1,0xC0, +// 'X' +0x58,0x04,0x0B,0x0D,0x01,0x0D, +0x70,0xE6,0x18,0xE6,0x0D,0xC0,0xF0,0x1C,0x03,0x80,0x78,0x1B,0x07,0x30,0xC7,0x30,0x6E,0x0E, +// 'Y' +0x59,0x04,0x0C,0x0D,0x00,0x0C, +0xE0,0x76,0x06,0x30,0xC1,0x98,0x19,0x80,0xF0,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00, +// 'Z' +0x5A,0x04,0x0B,0x0D,0x01,0x0D, +0xFF,0xFF,0xFC,0x07,0x01,0xC0,0x30,0x0E,0x03,0x80,0xE0,0x18,0x06,0x01,0xC0,0x7F,0xFF,0xFE, +// '[' +0x5B,0x03,0x04,0x10,0x01,0x07, +0xFF,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xFF, +// '\' +0x5C,0x04,0x06,0x0F,0x00,0x06, +0xC3,0x06,0x18,0x61,0xC3,0x0C,0x30,0xE1,0x86,0x18,0x30,0xC0, +// ']' +0x5D,0x03,0x04,0x10,0x02,0x07, +0xFF,0x33,0x33,0x33,0x33,0x33,0x33,0xFF, +// '^' +0x5E,0x04,0x0B,0x05,0x02,0x0F, +0x0E,0x03,0xE0,0xC6,0x30,0x6C,0x06, +// '_' +0x5F,0x13,0x09,0x02,0x00,0x09, +0xFF,0xFF,0xC0, +// '`' +0x60,0x03,0x04,0x03,0x02,0x09, +0xC6,0x30, +// 'a' +0x61,0x07,0x08,0x0A,0x01,0x0A, +0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, +// 'b' +0x62,0x03,0x09,0x0E,0x02,0x0B, +0xC0,0x60,0x30,0x18,0x0D,0xE7,0xFB,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x7F,0xF6,0xF0, +// 'c' +0x63,0x07,0x08,0x0A,0x01,0x09, +0x1E,0x7F,0x61,0xC0,0xC0,0xC0,0xC0,0x61,0x7F,0x1E, +// 'd' +0x64,0x03,0x09,0x0E,0x01,0x0B, +0x01,0x80,0xC0,0x60,0x33,0xDB,0xFF,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x77,0xF9,0xEC, +// 'e' +0x65,0x07,0x0A,0x0A,0x01,0x0B, +0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, +// 'f' +0x66,0x03,0x07,0x0E,0x00,0x06, +0x1E,0x7C,0xC1,0x8F,0xFF,0xCC,0x18,0x30,0x60,0xC1,0x83,0x06,0x00, +// 'g' +0x67,0x07,0x09,0x0E,0x01,0x0B, +0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x68,0x67,0xF1,0xF0, +// 'h' +0x68,0x03,0x08,0x0E,0x02,0x0B, +0xC0,0xC0,0xC0,0xC0,0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, +// 'i' +0x69,0x03,0x02,0x0E,0x02,0x05, +0xF0,0xFF,0xFF,0xF0, +// 'j' +0x6A,0x03,0x04,0x12,0x00,0x05, +0x33,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0xEC, +// 'k' +0x6B,0x03,0x09,0x0E,0x02,0x0A, +0xC0,0x60,0x30,0x18,0x0C,0x36,0x33,0x31,0xB0,0xF0,0x78,0x36,0x19,0x8C,0x66,0x18, +// 'l' +0x6C,0x03,0x02,0x0E,0x02,0x05, +0xFF,0xFF,0xFF,0xF0, +// 'm' +0x6D,0x07,0x0E,0x0A,0x02,0x11, +0xDC,0x7B,0xFB,0xEE,0x79,0xF0,0xC3,0xC3,0x0F,0x0C,0x3C,0x30,0xF0,0xC3,0xC3,0x0F,0x0C,0x30, +// 'n' +0x6E,0x07,0x08,0x0A,0x02,0x0B, +0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, +// 'o' +0x6F,0x07,0x0A,0x0A,0x01,0x0B, +0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, +// 'p' +0x70,0x07,0x09,0x0E,0x02,0x0B, +0xDE,0x7F,0xB8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0xFF,0x6F,0x30,0x18,0x0C,0x06,0x00, +// 'q' +0x71,0x07,0x09,0x0E,0x01,0x0B, +0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x60,0x30,0x18,0x0C, +// 'r' +0x72,0x07,0x06,0x0A,0x02,0x08, +0xDF,0xFE,0x30,0xC3,0x0C,0x30,0xC3,0x00, +// 's' +0x73,0x07,0x08,0x0A,0x01,0x08, +0x7C,0xFE,0xC2,0xE0,0x7C,0x1E,0x06,0x86,0xFE,0x78, +// 't' +0x74,0x04,0x06,0x0D,0x01,0x07, +0x61,0x86,0x3F,0xFD,0x86,0x18,0x61,0x86,0x1F,0x3C, +// 'u' +0x75,0x07,0x08,0x0A,0x02,0x0B, +0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, +// 'v' +0x76,0x07,0x0C,0x0A,0x00,0x0B, +0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60, +// 'w' +0x77,0x07,0x0F,0x0A,0x01,0x10, +0x63,0x8C,0xC7,0x19,0x8E,0x31,0xB6,0xC3,0x6D,0x86,0xDB,0x0F,0x1E,0x0E,0x38,0x1C,0x70,0x38,0xE0, +// 'x' +0x78,0x07,0x0A,0x0A,0x01,0x0B, +0xE1,0xD8,0x63,0x30,0xCC,0x1E,0x07,0x83,0x30,0xCC,0x61,0xB8,0x70, +// 'y' +0x79,0x07,0x0C,0x0E,0x00,0x0B, +0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80, +// 'z' +0x7A,0x07,0x08,0x0A,0x01,0x09, +0xFF,0xFF,0x06,0x0C,0x1C,0x38,0x30,0x70,0xFF,0xFF, +// '{' +0x7B,0x03,0x08,0x11,0x02,0x0B, +0x0F,0x1F,0x18,0x18,0x18,0x18,0x38,0xF0,0xF0,0x38,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F, +// '|' +0x7C,0x03,0x02,0x12,0x02,0x06, +0xFF,0xFF,0xFF,0xFF,0xF0, +// '}' +0x7D,0x03,0x08,0x11,0x02,0x0B, +0xF0,0xF8,0x18,0x18,0x18,0x18,0x1C,0x0F,0x0F,0x1C,0x18,0x18,0x18,0x18,0x18,0xF8,0xF0, +// '~' +0x7E,0x08,0x0B,0x05,0x02,0x0F, +0x00,0x0F,0x87,0xFF,0xC3,0xE0,0x00, +// '' +0x7F,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x80,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x81,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x82,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x83,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x84,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x85,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x86,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x87,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x88,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x89,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x8A,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x8B,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x8C,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x8D,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x8E,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x8F,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x90,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x91,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x92,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x93,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x94,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x95,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x96,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x97,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x98,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x99,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x9A,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x9B,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x9C,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x9D,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x9E,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0x9F,0x04,0x09,0x10,0x01,0x0B, +0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, +// '' +0xA0,0x11,0x00,0x00,0x00,0x06, + +// '' +0xA1,0x07,0x02,0x0D,0x03,0x07, +0xF0,0xFF,0xFF,0xC0, +// '' +0xA2,0x04,0x08,0x10,0x02,0x0B, +0x04,0x04,0x04,0x1E,0x7F,0x75,0xC4,0xC4,0xC4,0xC4,0x65,0x7F,0x1E,0x04,0x04,0x04, +// '' +0xA3,0x04,0x09,0x0D,0x01,0x0B, +0x0F,0x0F,0xCE,0x26,0x03,0x01,0x83,0xF9,0xFC,0x30,0x18,0x0C,0x1F,0xFF,0xF8, +// '' +0xA4,0x06,0x0A,0x0A,0x00,0x0B, +0x00,0x10,0x13,0x58,0xFE,0x11,0x0C,0x61,0x10,0xFE,0x35,0x90,0x10, +// '' +0xA5,0x04,0x0A,0x0D,0x01,0x0B, +0xC0,0xD8,0x66,0x18,0xCC,0x33,0x3F,0xF1,0xE0,0x30,0xFF,0xC3,0x00,0xC0,0x30,0x0C,0x00, +// '' +0xA6,0x04,0x02,0x10,0x02,0x06, +0xFF,0xFC,0x3F,0xFF, +// '' +0xA7,0x04,0x07,0x0F,0x01,0x09, +0x3C,0xF9,0x83,0x03,0x0F,0x33,0x63,0x66,0x78,0x60,0x60,0xCF,0x9E,0x00, +// '' +0xA8,0x03,0x06,0x02,0x02,0x09, +0xCF,0x30, +// '' +0xA9,0x04,0x0D,0x0D,0x02,0x12, +0x0F,0x81,0x83,0x18,0x0C,0x8F,0x28,0x80,0xC8,0x06,0x40,0x32,0x01,0x88,0x8A,0x38,0x98,0x0C,0x60,0xC0,0xF8,0x00, +// '' +0xAA,0x04,0x08,0x09,0x01,0x08, +0x7C,0x02,0x7E,0xC2,0x82,0xC6,0x7A,0x00,0xFE, +// '' +0xAB,0x08,0x08,0x08,0x01,0x0B, +0x11,0x33,0x66,0xCC,0xCC,0x66,0x33,0x11, +// '' +0xAC,0x09,0x0B,0x05,0x02,0x0F, +0xFF,0xFF,0xFC,0x01,0x80,0x30,0x06, +// '' +0xAD,0x0B,0x05,0x02,0x01,0x07, +0xFF,0xC0, +// '' +0xAE,0x04,0x0D,0x0D,0x02,0x12, +0x0F,0x81,0x83,0x18,0x0C,0x9E,0x28,0x88,0xC4,0x46,0x3C,0x31,0x21,0x88,0x8A,0x46,0x98,0x0C,0x60,0xC0,0xF8,0x00, +// '' +0xAF,0x04,0x05,0x02,0x02,0x09, +0xFF,0xC0, +// '' +0xB0,0x04,0x06,0x06,0x02,0x09, +0x7B,0x38,0x61,0xCD,0xE0, +// '' +0xB1,0x06,0x0C,0x0B,0x02,0x0F, +0x06,0x00,0x60,0x06,0x0F,0xFF,0xFF,0xF0,0x60,0x06,0x00,0x60,0x00,0x0F,0xFF,0xFF,0xF0, +// '' +0xB2,0x04,0x05,0x07,0x01,0x07, +0x74,0x42,0x22,0x23,0xE0, +// '' +0xB3,0x04,0x05,0x07,0x01,0x07, +0xF0,0x5C,0x30,0x8F,0xC0, +// '' +0xB4,0x03,0x04,0x03,0x03,0x09, +0x36,0xC0, +// '' +0xB5,0x07,0x09,0x0E,0x02,0x0B, +0xC3,0x61,0xB0,0xD8,0x6C,0x36,0x1B,0x0D,0xCE,0xFF,0xEC,0xF0,0x18,0x0C,0x06,0x00, +// '' +0xB6,0x04,0x08,0x0F,0x01,0x0B, +0x3F,0x79,0xF9,0xF9,0xF9,0xF9,0x79,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, +// '' +0xB7,0x0A,0x02,0x02,0x02,0x06, +0xF0, +// '' +0xB8,0x11,0x04,0x03,0x03,0x09, +0x23,0xF0, +// '' +0xB9,0x04,0x05,0x07,0x01,0x07, +0xE1,0x08,0x42,0x13,0xE0, +// '' +0xBA,0x04,0x07,0x09,0x01,0x08, +0x38,0x8A,0x0C,0x18,0x28,0x8E,0x00,0xFE, +// '' +0xBB,0x08,0x08,0x08,0x02,0x0B, +0x88,0xCC,0x66,0x33,0x33,0x66,0xCC,0x88, +// '' +0xBC,0x04,0x10,0x0D,0x01,0x11, +0xE0,0x18,0x20,0x10,0x20,0x20,0x20,0x60,0x20,0x40,0x20,0x80,0xF9,0x86,0x01,0x0A,0x02,0x12,0x06,0x22,0x04,0x3F,0x08,0x02,0x18,0x02, +// '' +0xBD,0x04,0x0F,0x0D,0x01,0x11, +0xE0,0x18,0x40,0x20,0x80,0x81,0x03,0x02,0x04,0x04,0x10,0x3E,0x67,0x00,0x91,0x02,0x02,0x0C,0x08,0x10,0x20,0x40,0x81,0x83,0xE0, +// '' +0xBE,0x04,0x10,0x0D,0x01,0x11, +0xF0,0x18,0x08,0x10,0x70,0x20,0x18,0x60,0x08,0x40,0x18,0x80,0xF1,0x86,0x01,0x0A,0x02,0x12,0x06,0x22,0x04,0x3F,0x08,0x02,0x18,0x02, +// '' +0xBF,0x07,0x07,0x0E,0x02,0x0A, +0x18,0x30,0x00,0xC1,0x83,0x0C,0x18,0x61,0x83,0x07,0x17,0xE7,0x80, +// '' +0xC0,0x01,0x0C,0x10,0x00,0x0C, +0x06,0x00,0x30,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, +// '' +0xC1,0x01,0x0C,0x10,0x00,0x0C, +0x03,0x00,0x60,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, +// '' +0xC2,0x01,0x0C,0x10,0x00,0x0C, +0x06,0x00,0x90,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, +// '' +0xC3,0x01,0x0C,0x10,0x00,0x0C, +0x0C,0x81,0x30,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, +// '' +0xC4,0x01,0x0C,0x10,0x00,0x0C, +0x19,0x81,0x98,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, +// '' +0xC5,0x00,0x0C,0x11,0x00,0x0C, +0x06,0x00,0x90,0x09,0x00,0x90,0x06,0x00,0x60,0x0F,0x00,0xF0,0x19,0x81,0x98,0x19,0x83,0x0C,0x3F,0xC7,0xFE,0x60,0x66,0x06,0xC0,0x30, +// '' +0xC6,0x04,0x10,0x0D,0x00,0x11, +0x07,0xFF,0x07,0xFF,0x06,0xC0,0x0C,0xC0,0x0C,0xC0,0x18,0xFF,0x18,0xFF,0x30,0xC0,0x3F,0xC0,0x3F,0xC0,0x60,0xC0,0x60,0xFF,0xC0,0xFF, +// '' +0xC7,0x04,0x0B,0x10,0x01,0x0D, +0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x0C,0x01,0xC0,0x9F,0xF0,0xFC,0x02,0x00,0x60,0x3C, +// '' +0xC8,0x01,0x08,0x10,0x02,0x0B, +0x30,0x18,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, +// '' +0xC9,0x01,0x08,0x10,0x02,0x0B, +0x18,0x30,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, +// '' +0xCA,0x01,0x08,0x10,0x02,0x0B, +0x38,0x6C,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, +// '' +0xCB,0x01,0x08,0x10,0x02,0x0B, +0x66,0x66,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, +// '' +0xCC,0x01,0x05,0x10,0x00,0x06, +0x61,0x80,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6, +// '' +0xCD,0x01,0x04,0x10,0x01,0x06, +0x6C,0x06,0x66,0x66,0x66,0x66,0x66,0x66, +// '' +0xCE,0x01,0x06,0x10,0x00,0x06, +0x31,0x20,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C, +// '' +0xCF,0x01,0x06,0x10,0x00,0x06, +0xCF,0x30,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C, +// '' +0xD0,0x04,0x0D,0x0D,0x00,0x0E, +0x3F,0x81,0xFF,0x0C,0x1C,0x60,0x73,0x01,0xFF,0x0F,0xF8,0x66,0x03,0x30,0x19,0x81,0xCC,0x1C,0x7F,0xC3,0xF8,0x00, +// '' +0xD1,0x01,0x0A,0x10,0x02,0x0E, +0x19,0x09,0x80,0x03,0x83,0xE0,0xFC,0x3F,0x0F,0x63,0xD8,0xF3,0x3C,0x6F,0x1B,0xC3,0xF0,0xFC,0x1F,0x07, +// '' +0xD2,0x01,0x0C,0x10,0x01,0x0E, +0x06,0x00,0x30,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, +// '' +0xD3,0x01,0x0C,0x10,0x01,0x0E, +0x03,0x00,0x60,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, +// '' +0xD4,0x01,0x0C,0x10,0x01,0x0E, +0x06,0x00,0x90,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, +// '' +0xD5,0x01,0x0C,0x10,0x01,0x0E, +0x0C,0x81,0x30,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, +// '' +0xD6,0x01,0x0C,0x10,0x01,0x0E, +0x19,0x81,0x98,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, +// '' +0xD7,0x06,0x0A,0x0A,0x02,0x0F, +0x40,0xB8,0x77,0x38,0xFC,0x1E,0x07,0x83,0xF1,0xCE,0xE1,0xD0,0x20, +// '' +0xD8,0x03,0x0E,0x0F,0x00,0x0E, +0x00,0x00,0x3E,0x21,0xFD,0x0E,0x1C,0x70,0x71,0x83,0x66,0x19,0x98,0xC6,0x66,0x19,0xB0,0x63,0x83,0x8E,0x1C,0x2F,0xE1,0x1F,0x00,0x00,0x00, +// '' +0xD9,0x01,0x0A,0x10,0x02,0x0E, +0x18,0x03,0x00,0x03,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xD8,0x67,0xF8,0x78, +// '' +0xDA,0x01,0x0A,0x10,0x02,0x0E, +0x0C,0x06,0x00,0x03,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xD8,0x67,0xF8,0x78, +// '' +0xDB,0x01,0x0A,0x10,0x02,0x0E, +0x0C,0x04,0x80,0x03,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xD8,0x67,0xF8,0x78, +// '' +0xDC,0x01,0x0A,0x10,0x02,0x0E, +0x33,0x0C,0xC0,0x03,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xD8,0x67,0xF8,0x78, +// '' +0xDD,0x01,0x0C,0x10,0x00,0x0C, +0x06,0x00,0xC0,0x00,0x0E,0x07,0x60,0x63,0x0C,0x19,0x81,0x98,0x0F,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60, +// '' +0xDE,0x04,0x08,0x0D,0x02,0x0B, +0xC0,0xC0,0xFC,0xFE,0xC7,0xC3,0xC3,0xC7,0xFE,0xFC,0xC0,0xC0,0xC0, +// '' +0xDF,0x03,0x09,0x0E,0x02,0x0B, +0x3C,0x3F,0x39,0xD8,0x6C,0x66,0x63,0x31,0x8C,0xC3,0x60,0xF0,0x7A,0x7D,0xF6,0x70, +// '' +0xE0,0x03,0x08,0x0E,0x01,0x0A, +0x30,0x18,0x0C,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, +// '' +0xE1,0x03,0x08,0x0E,0x01,0x0A, +0x06,0x0C,0x18,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, +// '' +0xE2,0x03,0x08,0x0E,0x01,0x0A, +0x18,0x3C,0x66,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, +// '' +0xE3,0x03,0x08,0x0E,0x01,0x0A, +0x32,0x4C,0x00,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, +// '' +0xE4,0x03,0x08,0x0E,0x01,0x0A, +0x66,0x66,0x00,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, +// '' +0xE5,0x01,0x08,0x10,0x01,0x0A, +0x1C,0x22,0x22,0x22,0x1C,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, +// '' +0xE6,0x07,0x0F,0x0A,0x01,0x11, +0x3C,0x78,0xFD,0xF9,0x1E,0x38,0x18,0x33,0xFF,0xFF,0xFF,0xF0,0xC0,0x63,0xC1,0xFD,0xFE,0xF0,0xF8, +// '' +0xE7,0x07,0x08,0x0D,0x01,0x09, +0x1E,0x7F,0x61,0xC0,0xC0,0xC0,0xC0,0x61,0x7F,0x1E,0x04,0x06,0x1E, +// '' +0xE8,0x03,0x0A,0x0E,0x01,0x0B, +0x30,0x06,0x00,0xC0,0x00,0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, +// '' +0xE9,0x03,0x0A,0x0E,0x01,0x0B, +0x06,0x03,0x01,0x80,0x00,0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, +// '' +0xEA,0x03,0x0A,0x0E,0x01,0x0B, +0x0C,0x07,0x83,0x30,0x00,0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, +// '' +0xEB,0x03,0x0A,0x0E,0x01,0x0B, +0x33,0x0C,0xC0,0x00,0x00,0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, +// '' +0xEC,0x03,0x04,0x0E,0x00,0x05, +0xC6,0x30,0x33,0x33,0x33,0x33,0x33, +// '' +0xED,0x03,0x04,0x0E,0x01,0x05, +0x36,0xC0,0x66,0x66,0x66,0x66,0x66, +// '' +0xEE,0x03,0x06,0x0E,0x00,0x05, +0x31,0xEC,0xC0,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC0, +// '' +0xEF,0x03,0x06,0x0E,0x00,0x05, +0xCF,0x30,0x00,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC0, +// '' +0xF0,0x03,0x0A,0x0E,0x01,0x0B, +0x10,0x07,0xC7,0xC0,0x18,0x1F,0x1F,0xE6,0x1F,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, +// '' +0xF1,0x03,0x08,0x0E,0x02,0x0B, +0x32,0x4C,0x00,0x00,0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, +// '' +0xF2,0x03,0x0A,0x0E,0x01,0x0B, +0x30,0x06,0x00,0xC0,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, +// '' +0xF3,0x03,0x0A,0x0E,0x01,0x0B, +0x06,0x03,0x01,0x80,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, +// '' +0xF4,0x03,0x0A,0x0E,0x01,0x0B, +0x0C,0x07,0x83,0x30,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, +// '' +0xF5,0x03,0x0A,0x0E,0x01,0x0B, +0x19,0x09,0x80,0x00,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, +// '' +0xF6,0x03,0x0A,0x0E,0x01,0x0B, +0x33,0x0C,0xC0,0x00,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, +// '' +0xF7,0x07,0x0C,0x08,0x02,0x0F, +0x06,0x00,0x60,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x06,0x00,0x60, +// '' +0xF8,0x06,0x0C,0x0C,0x00,0x0B, +0x00,0x00,0xF2,0x1F,0xC3,0x0C,0x61,0xE6,0x26,0x64,0x67,0x86,0x30,0xC3,0xFC,0x4F,0x00,0x00, +// '' +0xF9,0x03,0x08,0x0E,0x02,0x0B, +0x60,0x30,0x18,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, +// '' +0xFA,0x03,0x08,0x0E,0x02,0x0B, +0x0C,0x18,0x30,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, +// '' +0xFB,0x03,0x08,0x0E,0x02,0x0B, +0x18,0x3C,0x66,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, +// '' +0xFC,0x03,0x08,0x0E,0x02,0x0B, +0x66,0x66,0x00,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, +// '' +0xFD,0x03,0x0C,0x12,0x00,0x0B, +0x03,0x00,0x60,0x0C,0x00,0x00,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80, +// '' +0xFE,0x03,0x09,0x12,0x02,0x0B, +0xC0,0x60,0x30,0x18,0x0D,0xE7,0xFB,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x7F,0xF6,0xF3,0x01,0x80,0xC0,0x60,0x00, +// '' +0xFF,0x03,0x0C,0x12,0x00,0x0B, +0x1B,0x01,0xB0,0x00,0x00,0x00,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80, + +// Terminator +0xFF +}; diff --git a/components/epaper/minya24.c b/components/epaper/minya24.c new file mode 100644 index 0000000..6bab953 --- /dev/null +++ b/components/epaper/minya24.c @@ -0,0 +1,331 @@ +// This comes with no warranty, implied or otherwise + +// This data structure was designed to support Proportional fonts +// on Arduinos. It can however handle any ttf font that has been converted +// using the conversion program. These could be fixed width or proportional +// fonts. Individual characters do not have to be multiples of 8 bits wide. +// Any width is fine and does not need to be fixed. + +// The data bits are packed to minimize data requirements, but the tradeoff +// is that a header is required per character. + +// minya24.c +// Point Size : 24 +// Memory usage : 2807 bytes +// # characters : 95 + +// Header Format (to make Arduino UTFT Compatible): +// ------------------------------------------------ +// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) +// Character Height +// First Character (Reserved. 0x00) +// Number Of Characters (Reserved. 0x00) + +unsigned char tft_minya24[] = +{ +0x00, 0x15, 0x00, 0x00, + +// Individual Character Format: +// ---------------------------- +// Character Code +// Adjusted Y Offset +// Width +// Height +// xOffset +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] + +// NOTE: You can remove any of these characters if they are not needed in +// your application. The first character number in each Glyph indicates +// the ASCII character code. Therefore, these do not have to be sequential. +// Just remove all the content for a particular character to save space. + +// ' ' +0x20,0x13,0x00,0x00,0x00,0x07, + +// '!' +0x21,0x02,0x05,0x12,0x00,0x05, +0x11,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x42,0x01,0xCE,0x73,0x80, +// '"' +0x22,0x01,0x06,0x08,0x00,0x06, +0x01,0xA6,0xDB,0x6D,0xB6,0xC0, +// '#' +0x23,0x04,0x0C,0x0E,0x00,0x0B, +0x04,0x80,0x6C,0x0C,0x80,0xD8,0x7F,0xE7,0xFE,0x1B,0x01,0xB0,0x7F,0xE7,0xFC,0x12,0x03,0x20,0x32,0x00,0x00, +// '$' +0x24,0x02,0x0A,0x11,0x00,0x0B, +0x04,0x01,0x61,0xF8,0xFE,0x65,0x19,0x06,0x40,0xF0,0x1F,0x01,0xE0,0x4C,0x93,0x7C,0xCF,0xE0,0x60,0x10,0x04,0x00, +// '%' +0x25,0x01,0x0D,0x14,0x01,0x0F, +0x00,0x01,0xC1,0x1F,0x19,0x8C,0xCC,0x6C,0x63,0x61,0x36,0x0F,0xB0,0x1B,0x00,0x18,0x01,0x80,0x0C,0x00,0xCE,0x06,0xF8,0x6C,0x66,0x63,0x33,0x13,0x0F,0x98,0x38,0x00,0x00, +// '&' +0x26,0x02,0x0E,0x11,0x00,0x0D, +0x0E,0x00,0x7C,0x01,0xB0,0x06,0xC0,0x1E,0x00,0x38,0x00,0xC3,0x07,0x8C,0x37,0x60,0xCD,0x86,0x1E,0x18,0x70,0x41,0xE1,0x07,0xC6,0x33,0x9F,0x86,0x38,0x00, +// ''' +0x27,0x02,0x03,0x08,0x00,0x03, +0x6D,0xB6,0xD8, +// '(' +0x28,0x01,0x05,0x14,0x02,0x07, +0x00,0x8E,0x66,0x33,0x18,0xC6,0x31,0x8C,0x61,0x0C,0x63,0x8C,0x00, +// ')' +0x29,0x01,0x06,0x15,0x00,0x07, +0x01,0x86,0x0C,0x18,0x61,0x82,0x08,0x30,0xC2,0x08,0x61,0x86,0x30,0xC6,0x10,0x00, +// '*' +0x2A,0x04,0x0A,0x0D,0x01,0x0B, +0x08,0x03,0x04,0xC1,0xF6,0x7F,0x07,0x81,0xC0,0xF8,0x3F,0x1B,0xCC,0xD8,0x30,0x0C,0x00, +// '+' +0x2B,0x06,0x0A,0x0A,0x01,0x0B, +0x00,0x03,0x00,0xC0,0x30,0x7F,0xBF,0xE0,0xC0,0x30,0x0C,0x00,0x00, +// ',' +0x2C,0x10,0x05,0x07,0x00,0x05, +0x33,0x9C,0x63,0x20,0x00, +// '-' +0x2D,0x09,0x07,0x02,0x00,0x07, +0x7D,0xF8, +// '.' +0x2E,0x10,0x04,0x04,0x01,0x05, +0x6E,0xE6, +// '/' +0x2F,0x01,0x0C,0x13,0x00,0x0B, +0x00,0x00,0x06,0x00,0x60,0x0C,0x00,0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x60,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x30,0x06,0x00,0x40,0x00, +// '0' +0x30,0x08,0x0B,0x0B,0x00,0x0B, +0x0E,0x03,0xE0,0xC6,0x30,0x66,0x0C,0xC0,0x98,0x33,0x06,0x61,0xC7,0xF0,0x7C,0x00, +// '1' +0x31,0x08,0x0A,0x0D,0x00,0x09, +0x04,0x1F,0x03,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x07,0xF9,0xFE,0x00,0x00, +// '2' +0x32,0x06,0x0A,0x0E,0x00,0x0A, +0x0E,0x07,0xC3,0x30,0xCC,0x03,0x01,0x80,0x60,0x30,0x18,0x0C,0x03,0x01,0xF8,0x7F,0xC0,0x20, +// '3' +0x33,0x08,0x09,0x10,0x00,0x08, +0x78,0x3F,0x81,0x81,0x81,0xC0,0xC0,0xE0,0x78,0x06,0x01,0x01,0x80,0xC0,0xE1,0xE0,0xC0,0x00, +// '4' +0x34,0x03,0x0B,0x13,0x00,0x0A, +0x00,0x00,0xC0,0x18,0x03,0x00,0x60,0x08,0x03,0x00,0x60,0x0D,0x83,0x20,0x64,0x18,0x83,0x10,0xC6,0x1F,0xF3,0xFE,0x03,0x00,0x60,0x0C,0x00, +// '5' +0x35,0x09,0x0A,0x0E,0x00,0x0A, +0x3F,0x8F,0xE3,0x00,0x80,0x60,0x1F,0x87,0xF0,0x06,0x01,0x80,0x60,0x19,0x86,0x7F,0x07,0x80, +// '6' +0x36,0x03,0x0A,0x12,0x00,0x0A, +0x00,0x01,0x80,0xE0,0x30,0x18,0x06,0x03,0x00,0xC0,0x60,0x1B,0x87,0xF1,0x86,0x61,0x90,0x66,0x19,0x86,0x3F,0x07,0x80, +// '7' +0x37,0x09,0x0A,0x0E,0x00,0x0A, +0x7F,0x9F,0xF0,0x18,0x0C,0x06,0x01,0x80,0xC0,0x30,0x18,0x06,0x01,0x80,0x40,0x10,0x0C,0x00, +// '8' +0x38,0x03,0x0B,0x11,0x00,0x0B, +0x0F,0x07,0xF0,0xC3,0x30,0x66,0x0C,0x63,0x87,0xE0,0xF8,0x39,0xCE,0x19,0x81,0x30,0x34,0x06,0xC1,0x98,0x71,0xFC,0x1F,0x00, +// '9' +0x39,0x07,0x0A,0x11,0x00,0x0A, +0x1E,0x0F,0xC6,0x19,0x86,0x41,0x98,0x66,0x18,0xFE,0x1D,0x80,0x40,0x30,0x0C,0x06,0x03,0x01,0xC0,0x20,0x00,0x00, +// ':' +0x3A,0x0A,0x05,0x0A,0x00,0x05, +0x33,0x9C,0x60,0x00,0xCE,0x71,0x80, +// ';' +0x3B,0x09,0x05,0x0D,0x00,0x06, +0x33,0xDE,0x60,0x00,0x0E,0x73,0x8C,0xC0,0x00, +// '<' +0x3C,0x06,0x0A,0x0C,0x00,0x09, +0x00,0x00,0xE0,0x70,0x38,0x1C,0x1C,0x03,0x00,0x70,0x0E,0x01,0xC0,0x10,0x00, +// '=' +0x3D,0x09,0x08,0x06,0x01,0x0A, +0xFF,0xFE,0x00,0x00,0x7F,0xFF, +// '>' +0x3E,0x06,0x0A,0x0C,0x00,0x09, +0x00,0x18,0x03,0x80,0x70,0x0E,0x00,0xE0,0x30,0x38,0x1C,0x0E,0x02,0x00,0x00, +// '?' +0x3F,0x02,0x09,0x12,0x00,0x09, +0x1E,0x1F,0x98,0x6C,0x30,0x18,0x18,0x38,0x30,0x30,0x18,0x0C,0x02,0x01,0x00,0x00,0x60,0x78,0x3C,0x0C,0x00, +// '@' +0x40,0x02,0x11,0x11,0x00,0x11, +0x01,0xF0,0x03,0xFE,0x03,0x03,0x83,0x00,0xC3,0x04,0x31,0x1F,0x19,0x9F,0x0C,0xCC,0x86,0x4C,0x43,0x24,0x21,0x12,0x39,0x89,0xF7,0x84,0x71,0x83,0x00,0x00,0xC1,0x80,0x3F,0xC0,0x0F,0x80,0x00, +// 'A' +0x41,0x02,0x12,0x13,0x00,0x10, +0x00,0x00,0x01,0xF0,0x00,0x7E,0x00,0x03,0x80,0x01,0xA0,0x00,0x6C,0x00,0x1B,0x00,0x0C,0x40,0x03,0x18,0x01,0x86,0x00,0x61,0x80,0x1F,0xE0,0x0F,0xFC,0x03,0x03,0x01,0x80,0xC0,0x60,0x10,0x18,0x1F,0x9F,0xC7,0xEF,0xF8,0x00, +// 'B' +0x42,0x02,0x0E,0x13,0x00,0x0E, +0x7F,0x81,0xFF,0x81,0x86,0x06,0x18,0x18,0xC0,0x66,0x01,0xFC,0x07,0xFC,0x1C,0x30,0x60,0x61,0x81,0x86,0x06,0x18,0x18,0x60,0x61,0x81,0x06,0x0C,0x18,0xE0,0xFE,0x03,0xE0,0x00, +// 'C' +0x43,0x02,0x0F,0x12,0x00,0x0F, +0x03,0xF0,0x1F,0xE0,0x70,0xC1,0x80,0x83,0x00,0x04,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x06,0x00,0x0E,0x01,0x0E,0x0E,0x0F,0xF8,0x07,0xE0, +// 'D' +0x44,0x02,0x0F,0x13,0x00,0x0F, +0x3F,0xC0,0x7F,0xE0,0x60,0xE0,0xC0,0xC1,0x80,0xC3,0x01,0x86,0x03,0x0C,0x02,0x18,0x04,0x30,0x08,0x60,0x30,0xC0,0x61,0x80,0x83,0x03,0x06,0x0C,0x0C,0x38,0x18,0xE0,0xFF,0x81,0xF8,0x00, +// 'E' +0x45,0x02,0x0D,0x13,0x00,0x0D, +0x3F,0xF1,0xFF,0x86,0x0C,0x30,0x61,0x80,0x0C,0x40,0x62,0x03,0xF0,0x1F,0x80,0xC4,0x06,0x20,0x30,0x01,0x80,0x0C,0x00,0x60,0xC3,0x06,0x7F,0xF3,0xFF,0x80,0x00, +// 'F' +0x46,0x01,0x0E,0x13,0x00,0x0E, +0x00,0x01,0xFF,0xE7,0xFF,0x86,0x06,0x18,0x18,0x63,0x01,0x8C,0x03,0xF0,0x0F,0xC0,0x63,0x01,0x8C,0x06,0x30,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x3F,0x81,0xFE,0x00,0x00,0x00, +// 'G' +0x47,0x01,0x11,0x14,0x00,0x11, +0x00,0x00,0x03,0xC8,0x03,0xFC,0x03,0x0E,0x03,0x03,0x01,0x00,0x01,0x80,0x00,0xC0,0x00,0x60,0x03,0x30,0x3F,0xD8,0x1F,0x0C,0x01,0x86,0x00,0xC3,0x80,0x60,0xC0,0x30,0x70,0x38,0x1C,0x3C,0x07,0xF6,0x01,0xF3,0x80,0x01,0xE0, +// 'H' +0x48,0x01,0x10,0x13,0x00,0x10, +0x00,0x00,0x7C,0x0F,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x08,0x0C,0x08,0x0F,0xF8,0x0F,0xF8,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x1A,0x7C,0x1F,0x7C,0x10, +// 'I' +0x49,0x02,0x08,0x13,0x00,0x08, +0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E, +// 'J' +0x4A,0x01,0x0C,0x14,0x00,0x0B, +0x00,0x00,0x7E,0x07,0xE0,0x18,0x01,0x80,0x18,0x01,0x80,0x18,0x00,0x80,0x08,0x00,0x80,0x18,0x61,0x84,0x18,0x41,0x8C,0x18,0xC3,0x06,0x70,0x7E,0x01,0x80, +// 'K' +0x4B,0x02,0x11,0x13,0x00,0x10, +0x7E,0x7E,0x3F,0x3E,0x06,0x06,0x03,0x06,0x01,0x86,0x00,0xC6,0x00,0x66,0x00,0x37,0x00,0x1F,0x80,0x0E,0x60,0x06,0x10,0x03,0x0C,0x01,0x06,0x00,0x81,0x00,0x60,0x80,0x30,0x66,0x7E,0x1B,0x3F,0x0F,0x80,0x01,0x80, +// 'L' +0x4C,0x02,0x0D,0x11,0x00,0x0D, +0x7E,0x03,0xF0,0x06,0x00,0x30,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x00,0xC0,0x06,0x00,0x30,0x01,0x81,0x0C,0x08,0x60,0x4F,0xFF,0x7F,0xF8, +// 'M' +0x4D,0x02,0x14,0x12,0x00,0x13, +0x7C,0x07,0x87,0xC0,0x7C,0x0C,0x06,0x00,0xE0,0xE0,0x0E,0x0E,0x00,0xE0,0xE0,0x1B,0x1E,0x01,0xB1,0x60,0x0B,0x16,0x00,0x9B,0x60,0x09,0xA6,0x00,0x9E,0x60,0x08,0xE6,0x00,0x8C,0x60,0x08,0xC6,0x03,0x8C,0x7E,0x78,0x47,0xE0,0x00,0x00, +// 'N' +0x4E,0x01,0x12,0x13,0x00,0x12, +0x00,0x00,0x1F,0x03,0xF3,0xC0,0x7C,0x38,0x10,0x0E,0x04,0x03,0xC1,0x00,0xB0,0x40,0x26,0x10,0x09,0x84,0x02,0x31,0x00,0x8C,0x40,0x21,0x90,0x08,0x74,0x06,0x0F,0x01,0x81,0xC0,0x60,0x70,0x78,0x0C,0x3E,0x03,0x00,0x00,0x40, +// 'O' +0x4F,0x02,0x11,0x12,0x00,0x11, +0x03,0xE0,0x07,0xFC,0x07,0x07,0x06,0x01,0x82,0x00,0x63,0x00,0x11,0x80,0x0C,0x80,0x06,0x40,0x03,0x20,0x01,0x98,0x00,0xCC,0x00,0x66,0x00,0x31,0x80,0x30,0xE0,0x18,0x38,0x18,0x0F,0xF8,0x01,0xF0,0x00, +// 'P' +0x50,0x02,0x0C,0x12,0x00,0x0C, +0x7F,0x07,0xFC,0x10,0x61,0x03,0x10,0x31,0x03,0x10,0x31,0x06,0x10,0xE1,0xFC,0x1E,0x01,0x00,0x18,0x01,0x80,0x18,0x01,0x80,0x7E,0x07,0xF0, +// 'Q' +0x51,0x02,0x13,0x15,0x00,0x11, +0x07,0xE0,0x01,0xFE,0x00,0x60,0x70,0x18,0x06,0x06,0x00,0x60,0xC0,0x0C,0x18,0x00,0xC2,0x00,0x18,0x40,0x03,0x08,0x0C,0x61,0x83,0xCC,0x30,0xCD,0x83,0x09,0xE0,0x60,0x3C,0x06,0x07,0x00,0x7F,0xE3,0x07,0xEC,0x40,0x01,0x98,0x00,0x33,0x00,0x03,0xC0,0x00,0x30, +// 'R' +0x52,0x02,0x0F,0x13,0x00,0x0F, +0x7F,0x80,0xFF,0xE0,0x60,0xE0,0xC0,0x61,0x80,0xC3,0x01,0x86,0x07,0x0C,0x1C,0x1F,0xF0,0x3F,0x80,0x66,0x00,0xC4,0x01,0x8C,0x63,0x18,0xC6,0x11,0x8C,0x33,0x7E,0x7C,0xFC,0x70,0x00,0x00, +// 'S' +0x53,0x01,0x0D,0x13,0x00,0x0D, +0x00,0x60,0x7B,0x07,0xF8,0x71,0xC3,0x06,0x18,0x00,0xC0,0x03,0x80,0x0E,0x00,0x1C,0x00,0x78,0x00,0xC0,0x03,0x30,0x19,0x80,0xCC,0x06,0x30,0x70,0xFF,0x03,0xE0, +// 'T' +0x54,0x01,0x0F,0x12,0x00,0x0F, +0x00,0x00,0xFF,0xF9,0xFF,0xF3,0x18,0x66,0x30,0xCC,0x61,0x90,0xC3,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x0F,0xE0,0x1F,0xC0, +// 'U' +0x55,0x02,0x10,0x11,0x00,0x0F, +0x7C,0x7E,0x7C,0x7E,0x30,0x08,0x30,0x08,0x30,0x08,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x10,0x08,0x18,0x18,0x18,0x18,0x0C,0x30,0x0F,0xF0,0x03,0xC0, +// 'V' +0x56,0x01,0x10,0x12,0x00,0x10, +0x00,0x7E,0xFE,0x7E,0x7E,0x18,0x18,0x30,0x18,0x30,0x18,0x20,0x08,0x60,0x0C,0x60,0x0C,0x60,0x0C,0x40,0x0C,0xC0,0x04,0xC0,0x04,0xC0,0x06,0x80,0x07,0x80,0x07,0x80,0x03,0x00,0x03,0x00, +// 'W' +0x57,0x01,0x14,0x13,0x00,0x14, +0x00,0x00,0x0F,0xC4,0xFF,0xF0,0x4F,0xE3,0x06,0x10,0x30,0xE3,0x01,0x8E,0x30,0x18,0xE3,0x01,0x8E,0x30,0x19,0xA3,0x01,0x9A,0x20,0x09,0xA6,0x00,0xD2,0x60,0x0D,0x36,0x00,0xF3,0x40,0x0F,0x34,0x00,0x63,0xC0,0x06,0x1C,0x00,0x61,0x80,0x00,0x18,0x00, +// 'X' +0x58,0x01,0x10,0x13,0x00,0x10, +0x00,0x00,0x7F,0x7F,0x7E,0x7E,0x0C,0x30,0x06,0x20,0x06,0x60,0x03,0xC0,0x01,0x80,0x01,0x80,0x03,0x80,0x02,0xC0,0x06,0x40,0x04,0x60,0x0C,0x60,0x18,0x30,0x18,0x30,0x7C,0x30,0xFC,0xFE,0x00,0xFF, +// 'Y' +0x59,0x01,0x11,0x13,0x00,0x10, +0x00,0x00,0x7F,0x3F,0xBF,0x07,0x03,0x03,0x00,0xC1,0x00,0x31,0x80,0x18,0x80,0x06,0xC0,0x01,0xC0,0x00,0xE0,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x07,0x80,0x03,0xF8,0x00,0x7C,0x00, +// 'Z' +0x5A,0x02,0x0E,0x12,0x00,0x0D, +0x7F,0xF9,0xFF,0xE6,0x03,0x18,0x18,0x60,0xC1,0x07,0x00,0x18,0x00,0xC0,0x06,0x00,0x18,0x00,0xC0,0x06,0x00,0x18,0x30,0xC0,0xC3,0x03,0x1F,0xFE,0x7F,0xF8,0x00,0x60, +// '[' +0x5B,0x01,0x08,0x14,0x01,0x08, +0x00,0x7C,0x7C,0x40,0x40,0x40,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x7E,0x00, +// '\' +0x5C,0x01,0x0A,0x13,0x00,0x0A, +0x00,0x10,0x06,0x01,0x80,0x30,0x0C,0x01,0x80,0x60,0x0C,0x03,0x00,0x60,0x18,0x06,0x00,0xC0,0x30,0x06,0x01,0x80,0x60,0x08, +// ']' +0x5D,0x01,0x08,0x14,0x00,0x08, +0x00,0x7C,0x7E,0x04,0x04,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x7C,0xFC, +// '^' +0x5E,0x03,0x0B,0x0B,0x00,0x0B, +0x00,0x00,0x40,0x18,0x03,0x00,0xF0,0x32,0x0C,0x63,0x86,0x60,0xC0,0x0C,0x00,0x00, +// '_' +0x5F,0x15,0x11,0x02,0xFF,0x0F, +0x7F,0xFF,0x3F,0xFF,0x80, +// '`' +0x60,0x00,0x06,0x07,0x00,0x06, +0x01,0x87,0x0C,0x18,0x20,0x00, +// 'a' +0x61,0x06,0x0C,0x0D,0x00,0x0B, +0x0E,0x03,0xF0,0x31,0x06,0x10,0x23,0x80,0xF8,0x3D,0x87,0x10,0x61,0x0C,0x18,0x63,0xA7,0xFE,0x1C,0xC0, +// 'b' +0x62,0x01,0x0C,0x14,0x00,0x0C, +0x00,0x07,0xC0,0x3C,0x01,0x80,0x18,0x01,0x80,0x18,0x01,0x98,0x1F,0xC1,0xC6,0x18,0x21,0x83,0x10,0x31,0x03,0x18,0x31,0x83,0x18,0x6F,0xC6,0xF7,0xC0,0x30, +// 'c' +0x63,0x06,0x0B,0x0D,0x00,0x0B, +0x06,0x03,0xF0,0xC7,0x31,0xE4,0x1C,0x80,0x30,0x06,0x00,0x60,0x0C,0x01,0xC3,0x1F,0xC0,0xF0, +// 'd' +0x64,0x01,0x0D,0x13,0x00,0x0D, +0x00,0x00,0x1E,0x00,0xF0,0x01,0x80,0x0C,0x00,0x60,0x03,0x01,0x98,0x3E,0x81,0x9C,0x18,0x60,0xC3,0x04,0x08,0x60,0x41,0x02,0x0C,0x30,0x71,0x91,0xFF,0x87,0x1C, +// 'e' +0x65,0x07,0x0B,0x0C,0x00,0x0B, +0x0F,0x03,0xF0,0xC3,0x30,0x66,0x1C,0xDE,0x1E,0x03,0x00,0x60,0x06,0x18,0x7E,0x07,0x80, +// 'f' +0x66,0x02,0x0A,0x12,0x00,0x08, +0x07,0x07,0xE1,0xB8,0x4E,0x10,0x04,0x07,0xE1,0xF8,0x18,0x06,0x01,0x80,0x60,0x18,0x06,0x01,0x81,0xF8,0x7E,0x00,0x00, +// 'g' +0x67,0x05,0x0A,0x12,0x00,0x0A, +0x00,0x00,0x20,0x18,0x7E,0x3F,0x18,0x64,0x09,0x02,0x61,0x9F,0xC1,0xE0,0x0C,0x01,0x84,0x67,0x19,0x8C,0x7E,0x0F,0x00, +// 'h' +0x68,0x02,0x0E,0x12,0x00,0x0D, +0x78,0x01,0xE0,0x01,0x80,0x06,0x00,0x19,0xC0,0x2F,0x80,0xE6,0x07,0x08,0x18,0x20,0x60,0x81,0x82,0x06,0x08,0x18,0x20,0x60,0x81,0x82,0x1F,0x9E,0x7E,0x78,0x00,0x00, +// 'i' +0x69,0x02,0x08,0x11,0x00,0x08, +0x30,0x78,0x78,0x30,0x00,0x78,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7F, +// 'j' +0x6A,0x02,0x09,0x15,0xFE,0x07, +0x06,0x07,0x03,0x81,0xC0,0x00,0xF8,0x7C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x99,0x8F,0xC3,0xC0, +// 'k' +0x6B,0x02,0x0D,0x12,0x00,0x0D, +0x78,0x03,0xC0,0x06,0x00,0x37,0xC1,0xBC,0x0C,0xC0,0x6C,0x03,0xC0,0x1C,0x00,0xC0,0x07,0x00,0x3E,0x01,0xB8,0x08,0x61,0xC1,0xCF,0x07,0x00,0x10,0x00,0x00, +// 'l' +0x6C,0x02,0x08,0x12,0x00,0x08, +0x78,0x78,0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00, +// 'm' +0x6D,0x08,0x14,0x0D,0x00,0x14, +0x40,0xC3,0x0F,0xBE,0xF8,0x1E,0x78,0xC1,0xC7,0x0C,0x18,0x20,0xC1,0x82,0x0C,0x10,0x20,0xC1,0x82,0x08,0x18,0x60,0x81,0x84,0x18,0x7E,0x71,0xE7,0xE7,0x9E,0x00,0x00,0x00, +// 'n' +0x6E,0x06,0x0E,0x0E,0x00,0x0D, +0x79,0xC1,0xEF,0x81,0xE6,0x07,0x08,0x18,0x30,0x60,0xC1,0x82,0x06,0x08,0x18,0x20,0x60,0x81,0x82,0x1F,0x1E,0x7E,0x78,0x00,0x00, +// 'o' +0x6F,0x08,0x0B,0x0C,0x00,0x0C, +0x0F,0x03,0xF8,0xC3,0x30,0x36,0x06,0x80,0xD0,0x1B,0x03,0x60,0x66,0x18,0xFE,0x07,0x80, +// 'p' +0x70,0x07,0x0B,0x10,0x00,0x0B, +0x07,0x1F,0xF1,0xE3,0x18,0x23,0x06,0x60,0xCC,0x19,0x82,0x30,0xC6,0x18,0xFE,0x1B,0x83,0x00,0x60,0x1C,0x07,0x80, +// 'q' +0x71,0x06,0x0D,0x11,0x00,0x0B, +0x0C,0x01,0xF7,0x0C,0xF8,0xC3,0x04,0x18,0x20,0x41,0x02,0x08,0x10,0x60,0x83,0x04,0x0C,0x60,0x7F,0x01,0xE8,0x00,0xC0,0x06,0xC0,0x1E,0x00,0xE0, +// 'r' +0x72,0x08,0x0A,0x0D,0x00,0x09, +0x77,0x3F,0xE3,0xB8,0xCE,0x33,0x0C,0x03,0x00,0xC0,0x30,0x04,0x07,0xC1,0xF0,0x00,0x00, +// 's' +0x73,0x05,0x0A,0x0E,0x00,0x0A, +0x03,0x00,0xC1,0xF0,0xFC,0x63,0x18,0x47,0x00,0xF8,0x07,0x80,0x64,0x19,0x86,0x7F,0x07,0x80, +// 't' +0x74,0x02,0x09,0x13,0x00,0x09, +0x30,0x18,0x0C,0x06,0x02,0x07,0xE3,0xF8,0x40,0x20,0x10,0x08,0x04,0x02,0x11,0x18,0x84,0x62,0x33,0x0F,0x83,0x00, +// 'u' +0x75,0x06,0x0E,0x0E,0x00,0x0D, +0x00,0x03,0xE7,0xC7,0x9F,0x0C,0x30,0x30,0xC0,0xC3,0x03,0x0C,0x0C,0x30,0x30,0xC0,0xC3,0x03,0x0C,0x0E,0x7C,0x1F,0xF8,0x3C,0x00, +// 'v' +0x76,0x07,0x0E,0x0D,0x00,0x0D, +0x01,0xFB,0xF3,0xEF,0xC6,0x06,0x18,0x18,0x60,0x31,0x00,0xCC,0x01,0x30,0x06,0x80,0x0A,0x00,0x38,0x00,0xE0,0x01,0x00, +// 'w' +0x77,0x08,0x11,0x0D,0x00,0x11, +0x04,0x1F,0xFF,0x6F,0xBE,0x31,0x83,0x1C,0xC1,0x9E,0x40,0x4D,0x20,0x34,0xB0,0x1A,0x78,0x05,0x3C,0x03,0x8E,0x01,0x86,0x00,0x43,0x00,0x00,0x00, +// 'x' +0x78,0x08,0x0D,0x0D,0x00,0x0D, +0x0C,0xFB,0xE7,0xDE,0x18,0x31,0x80,0xD8,0x03,0x80,0x18,0x01,0xE0,0x19,0x81,0x8C,0x0E,0x7D,0xF3,0xE4,0x00,0x00, +// 'y' +0x79,0x06,0x0F,0x11,0x00,0x0D, +0x7C,0x01,0xF8,0x00,0xC3,0xF0,0xC7,0xE1,0x82,0x01,0x0C,0x03,0x18,0x06,0x20,0x06,0xC0,0x0D,0x00,0x0E,0x00,0x18,0x00,0x30,0x06,0xC0,0x1F,0x00,0x3E,0x00,0x30,0x00, +// 'z' +0x7A,0x08,0x0B,0x0B,0x00,0x0B, +0x7F,0xCF,0xF9,0x86,0x31,0x86,0x60,0x1C,0x07,0x00,0xC6,0x30,0xCF,0xF9,0xFF,0x00, +// '{' +0x7B,0x02,0x09,0x12,0x00,0x08, +0x07,0x07,0x83,0x01,0x01,0x80,0xC0,0x60,0xE0,0x70,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x1F,0x03,0x80, +// '|' +0x7C,0x02,0x03,0x12,0x01,0x05, +0x49,0x24,0x92,0x6D,0xB6,0xDB,0x6C, +// '}' +0x7D,0x02,0x07,0x11,0x00,0x07, +0x30,0xF0,0x20,0x41,0x83,0x02,0x06,0x0E,0x18,0x60,0xC0,0x81,0x83,0x3C,0x78, +// '~' +0x7E,0x09,0x0D,0x04,0x00,0x0D, +0x18,0x03,0xF1,0x98,0xFC,0x83,0xC0, + +// Terminator +0xFF +}; \ No newline at end of file diff --git a/components/epaper/tooney32.c b/components/epaper/tooney32.c new file mode 100644 index 0000000..753fb2d --- /dev/null +++ b/components/epaper/tooney32.c @@ -0,0 +1,331 @@ +// This comes with no warranty, implied or otherwise + +// This data structure was designed to support Proportional fonts +// on Arduinos. It can however handle any ttf font that has been converted +// using the conversion program. These could be fixed width or proportional +// fonts. Individual characters do not have to be multiples of 8 bits wide. +// Any width is fine and does not need to be fixed. + +// The data bits are packed to minimize data requirements, but the tradeoff +// is that a header is required per character. + +// tooney32.c +// Point Size : 32 +// Memory usage : 5470 bytes +// # characters : 95 + +// Header Format (to make Arduino UTFT Compatible): +// ------------------------------------------------ +// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) +// Character Height +// First Character (Reserved. 0x00) +// Number Of Characters (Reserved. 0x00) + +unsigned char tft_tooney32[] = +{ +0x00, 0x20, 0x00, 0x00, + +// Individual Character Format: +// ---------------------------- +// Character Code +// Adjusted Y Offset +// Width +// Height +// xOffset +// xDelta (the distance to move the cursor. Effective width of the character.) +// Data[n] + +// NOTE: You can remove any of these characters if they are not needed in +// your application. The first character number in each Glyph indicates +// the ASCII character code. Therefore, these do not have to be sequential. +// Just remove all the content for a particular character to save space. + +// ' ' +0x20,0x1E,0x00,0x00,0x00,0x09, + +// '!' +0x21,0x09,0x0B,0x16,0x00,0x0B, +0x3F,0xC8,0x07,0x81,0x70,0x27,0x00,0xE0,0x1C,0x21,0x84,0x30,0x86,0x10,0xC2,0x18,0x43,0xF0,0x61,0x0C,0x13,0x02,0x60,0x4E,0x09,0xE2,0x1F,0x81,0xE0,0x00,0x00, +// '"' +0x22,0x05,0x0E,0x0A,0xFF,0x0D, +0x04,0x30,0x2D,0x61,0x8C,0x44,0x71,0x31,0x88,0xCE,0x42,0x72,0x18,0xC8,0x7B,0xC0,0xC6,0x00, +// '#' +0x23,0x07,0x18,0x16,0x00,0x18, +0x00,0xFF,0xF8,0x01,0x83,0x08,0x01,0x82,0x08,0x01,0x82,0x08,0x0F,0x06,0x0F,0x10,0x00,0x01,0x30,0x00,0x01,0x30,0x00,0x00,0x20,0x00,0x02,0x7E,0x0C,0x1E,0x7E,0x0C,0x1E,0x60,0x00,0x02,0x60,0x00,0x00,0x40,0x00,0x04,0x40,0x00,0x04,0xC0,0x00,0x04,0xFC,0x10,0x78,0xFC,0x30,0x78,0x08,0x30,0x40,0x18,0x30,0x40,0x1F,0xFF,0x80,0x1F,0xFF,0x80, +// '$' +0x24,0x09,0x0F,0x14,0x00,0x0F, +0x01,0x80,0x04,0xF8,0x18,0x08,0x20,0x10,0xC0,0x41,0x00,0x86,0x01,0x0C,0x12,0x18,0x38,0x30,0x60,0xA0,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x10,0x30,0x20,0x6E,0x40,0xFF,0x81,0x9E,0x00,0x18,0x00, +// '%' +0x25,0x07,0x17,0x16,0x00,0x17, +0x0F,0x81,0xF8,0x20,0x84,0x10,0x80,0x98,0x42,0x00,0x21,0x0C,0x01,0x82,0x18,0x43,0x08,0x30,0x04,0x10,0x70,0x18,0x40,0xE0,0x21,0x00,0xF1,0x82,0x00,0xFF,0x0F,0xC0,0xFC,0x10,0x40,0x18,0x40,0x40,0x21,0x80,0x40,0x82,0x00,0x83,0x0C,0x21,0x04,0x18,0x02,0x18,0x78,0x04,0x21,0x30,0x10,0xC2,0x78,0xC3,0xF8,0x7F,0x07,0xE0,0x7C,0x00, +// '&' +0x26,0x08,0x17,0x17,0x00,0x17, +0x01,0xF6,0x00,0x04,0x1A,0x00,0x10,0x04,0x00,0x40,0x10,0x01,0x00,0x40,0x06,0x00,0x80,0x0C,0x1A,0x00,0x18,0x1F,0xC0,0x30,0x18,0xF8,0x60,0x08,0x09,0x80,0x00,0x33,0x00,0x00,0xCC,0x0C,0x01,0x18,0x3C,0x04,0x30,0x30,0x07,0x60,0x00,0x01,0xE0,0x00,0x07,0xC0,0x00,0x09,0xC0,0x0C,0x23,0xC0,0x38,0x03,0xE1,0xF8,0x03,0xFF,0x70,0x01,0xF8,0x60,0x00, +// ''' +0x27,0x05,0x09,0x0A,0xFF,0x08, +0x06,0x05,0x86,0x23,0x13,0x11,0x90,0x90,0xC8,0x78,0x18,0x00, +// '(' +0x28,0x05,0x0D,0x1D,0x00,0x0D, +0x03,0x00,0x34,0x01,0x90,0x08,0x40,0x81,0x88,0x1C,0xC1,0xC4,0x08,0x60,0x83,0x04,0x10,0x41,0x82,0x0C,0x10,0x60,0x83,0x04,0x18,0x20,0xC0,0x06,0x04,0x38,0x20,0xC0,0x87,0x06,0x38,0x1C,0xE0,0x67,0x86,0x1C,0x60,0x76,0x01,0xE0,0x0E,0x00,0x60,0x00, +// ')' +0x29,0x05,0x0D,0x1D,0x00,0x0D, +0x01,0x00,0x10,0x01,0x20,0x11,0x03,0x04,0x30,0x11,0xE0,0x8F,0x82,0x1C,0x10,0x70,0x81,0x82,0x0E,0x10,0x30,0x81,0x84,0x0C,0x20,0x61,0x02,0x08,0x10,0x01,0x84,0x08,0x20,0x81,0x18,0x11,0x80,0x8E,0x08,0x78,0x80,0xE4,0x03,0xE0,0x0E,0x00,0x30,0x00, +// '*' +0x2A,0x09,0x0C,0x0D,0x01,0x0D, +0x07,0x00,0x88,0x18,0xE4,0x11,0xC0,0x1F,0x8E,0xC0,0x1C,0x11,0xC8,0xBF,0x8E,0x7D,0xC1,0xE0,0x0C,0x00, +// '+' +0x2B,0x09,0x15,0x14,0x00,0x15, +0x00,0x7C,0x00,0x04,0x10,0x00,0x60,0x80,0x07,0x04,0x00,0x38,0x20,0x01,0xC1,0x00,0xFE,0x0F,0xCC,0x00,0x01,0xE0,0x00,0x0F,0x00,0x00,0x78,0x00,0x03,0xFF,0x07,0xFF,0xF8,0x3F,0x7F,0xC1,0xF0,0x0E,0x08,0x00,0x70,0x40,0x03,0x82,0x00,0x1F,0xF0,0x00,0xFE,0x00,0x03,0xE0,0x00, +// ',' +0x2C,0x17,0x09,0x0B,0x00,0x09, +0x1E,0x10,0x98,0x38,0x1C,0x0F,0x0B,0xC4,0xE4,0x32,0x1E,0x0E,0x00, +// '-' +0x2D,0x11,0x09,0x06,0x00,0x09, +0x1B,0x90,0x50,0x39,0x2F,0xE7,0xF0, +// '.' +0x2E,0x16,0x09,0x09,0x00,0x09, +0x1E,0x10,0x90,0x38,0x1C,0x0F,0x07,0xCC,0xFC,0x3C,0x00, +// '/' +0x2F,0x09,0x11,0x19,0x00,0x11, +0x00,0x3F,0x80,0x30,0x40,0x30,0x20,0x18,0x20,0x18,0x10,0x0C,0x08,0x06,0x08,0x06,0x04,0x03,0x04,0x03,0x02,0x01,0x81,0x00,0xC1,0x00,0xC0,0x80,0x60,0x00,0x60,0x40,0x30,0x20,0x18,0x20,0x18,0x10,0x0C,0x08,0x06,0x08,0x06,0x04,0x03,0x04,0x03,0xFE,0x01,0xFE,0x00,0xFE,0x00,0x00, +// '0' +0x30,0x08,0x17,0x17,0x00,0x17, +0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00, +// '1' +0x31,0x09,0x0D,0x16,0x00,0x0D, +0x00,0x30,0x06,0x40,0xC4,0x18,0x43,0x02,0x20,0x13,0xC0,0x9E,0x04,0x30,0x21,0x81,0x0C,0x08,0x60,0x43,0x02,0x18,0x10,0xC0,0x86,0x04,0x30,0x21,0x81,0x98,0x03,0xFF,0xE7,0xFE,0x00,0x00, +// '2' +0x32,0x08,0x12,0x17,0x00,0x12, +0x00,0xF0,0x00,0x81,0x00,0xC0,0x20,0xE0,0x04,0x40,0x01,0x38,0x00,0x2F,0x84,0x08,0xF9,0x82,0x1F,0xE0,0x81,0xF0,0x00,0x1C,0x10,0x06,0x04,0x01,0x03,0x80,0xC0,0xD0,0x20,0x04,0x18,0x01,0x0C,0x00,0x43,0x00,0x11,0x80,0x04,0x60,0x01,0x3F,0xFF,0x4F,0xFF,0xE0,0x00,0x30, +// '3' +0x33,0x08,0x12,0x17,0x00,0x12, +0x0C,0x00,0x05,0xFF,0xE3,0x00,0x08,0xC0,0x02,0x30,0x01,0x0C,0x00,0x43,0x00,0x10,0xC0,0x0C,0x37,0x01,0x0F,0x80,0x23,0xE0,0x08,0x10,0x01,0x0F,0xE0,0x43,0xF8,0x10,0x4E,0x04,0x23,0x01,0x10,0x00,0x08,0x00,0x24,0x00,0x13,0x00,0x08,0xFC,0x0C,0x1F,0xFE,0x00,0xFE,0x00, +// '4' +0x34,0x09,0x12,0x16,0x00,0x12, +0x00,0x0E,0x00,0x04,0x80,0x06,0x20,0x03,0x08,0x01,0x82,0x00,0xC0,0x80,0x40,0x20,0x20,0x08,0x10,0x03,0x88,0x20,0x94,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0xFE,0x09,0xFF,0x83,0x80,0x60,0xC0,0x30,0x10,0x0F,0xF8,0x03,0xFC,0x00,0x00,0x00, +// '5' +0x35,0x08,0x11,0x17,0x00,0x11, +0x00,0x03,0x00,0xFF,0x40,0x80,0x20,0xC0,0x10,0x60,0x08,0x30,0x04,0x30,0x02,0x18,0x1D,0x0C,0x07,0x06,0x01,0x82,0x00,0x43,0x00,0x11,0xFC,0x08,0xFF,0x04,0x17,0x82,0x18,0x01,0x18,0x00,0x08,0x00,0x8C,0x00,0x8C,0x00,0xC7,0xC1,0xC3,0xFF,0x80,0xBF,0x00, +// '6' +0x36,0x08,0x13,0x17,0x00,0x13, +0x00,0x7F,0xC0,0x30,0x08,0x18,0x01,0x06,0x00,0x41,0x80,0x10,0x60,0x02,0x08,0x0C,0x83,0x00,0x30,0x60,0x01,0x18,0x00,0x13,0x00,0x01,0x60,0x00,0x0C,0x00,0x03,0x80,0xC0,0x78,0x3C,0x0F,0x03,0x01,0x60,0x00,0x0E,0x00,0x08,0xE0,0x02,0x1E,0x00,0x81,0xF0,0x60,0x1F,0xF8,0x00,0xFC,0x00, +// '7' +0x37,0x08,0x12,0x17,0x00,0x12, +0x0C,0x00,0x0D,0xFF,0xF3,0x00,0x04,0xC0,0x02,0x30,0x00,0x8C,0x00,0x43,0x00,0x10,0xDC,0x08,0x3F,0x02,0x0E,0x81,0x00,0x60,0x40,0x10,0x20,0x0C,0x08,0x02,0x04,0x01,0x81,0x00,0x40,0x80,0x30,0x20,0x10,0x10,0x0F,0x04,0x03,0xF1,0x00,0x3F,0x40,0x03,0xE0,0x00,0x30,0x00, +// '8' +0x38,0x08,0x12,0x17,0x00,0x12, +0x01,0xF0,0x00,0x83,0x00,0xC0,0x20,0x20,0x08,0x10,0x01,0x0C,0x18,0x43,0x06,0x10,0xC0,0x04,0x38,0x01,0x0C,0x00,0x42,0x00,0x09,0x80,0x01,0xC0,0x00,0x70,0x3C,0x1C,0x0F,0x07,0x00,0x01,0xE0,0x00,0x9C,0x00,0x27,0x80,0x30,0xF8,0x38,0x1F,0xFC,0x01,0xFC,0x00,0x00,0x00, +// '9' +0x39,0x08,0x13,0x17,0x00,0x13, +0x01,0xF8,0x00,0xC0,0xC0,0x20,0x04,0x08,0x00,0x42,0x00,0x08,0xC0,0x00,0x90,0x18,0x16,0x07,0x81,0xC0,0x60,0x38,0x00,0x07,0x80,0x00,0xF0,0x00,0x17,0x00,0x02,0xF0,0x00,0x0F,0x80,0x10,0xE6,0x02,0x08,0x00,0x83,0x00,0x20,0x40,0x0C,0x10,0x03,0x06,0xC1,0xC0,0xFF,0xE0,0x1F,0xF0,0x00, +// ':' +0x3A,0x0E,0x09,0x11,0x00,0x09, +0x0E,0x10,0x90,0x38,0x1C,0x0F,0x07,0xC4,0xFC,0x3C,0x19,0x98,0x38,0x1C,0x0F,0x07,0xCC,0xFC,0x3C,0x00, +// ';' +0x3B,0x0F,0x09,0x13,0x00,0x09, +0x0E,0x10,0x98,0x38,0x1C,0x0F,0x07,0xC4,0xFC,0x3E,0x19,0x98,0x38,0x1C,0x0F,0x03,0xC4,0xE4,0x32,0x1E,0x0E,0x00, +// '<' +0x3C,0x0A,0x13,0x13,0x00,0x13, +0x00,0x00,0xC0,0x00,0x64,0x00,0x60,0x80,0x30,0x10,0x38,0x02,0x18,0x03,0x8C,0x01,0xE3,0x01,0xF0,0xE0,0xF8,0x1C,0x06,0x03,0x80,0x18,0x7E,0x00,0xCF,0xF0,0x06,0x7F,0xC0,0x43,0xFE,0x08,0x0F,0xF9,0x00,0x7F,0xE0,0x01,0xF8,0x00,0x0C,0x00, +// '=' +0x3D,0x0D,0x14,0x0E,0x00,0x14, +0x3F,0xFF,0xE6,0x00,0x01,0xE0,0x00,0x1E,0x00,0x01,0xE0,0x00,0x1F,0xFF,0xFE,0xFF,0xFF,0xE6,0x00,0x01,0x60,0x00,0x1E,0x00,0x01,0xE0,0x00,0x1F,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF8, +// '>' +0x3E,0x0A,0x13,0x13,0x00,0x13, +0x38,0x00,0x0C,0xC0,0x03,0x87,0x00,0x70,0x18,0x0E,0x00,0xE1,0xF8,0x03,0x3F,0xC0,0x19,0xFF,0x01,0x0F,0xF8,0x20,0x3C,0x04,0x0C,0x00,0x86,0x00,0xE3,0x00,0x78,0xC0,0x7C,0x38,0x3E,0x07,0x3E,0x00,0xFF,0x00,0x1F,0x00,0x03,0x80,0x00,0x00, +// '?' +0x3F,0x08,0x11,0x16,0x00,0x11, +0x00,0xF0,0x01,0x82,0x01,0x00,0x81,0x00,0x23,0x00,0x0B,0x00,0x05,0xC0,0x02,0xF8,0xC1,0x3F,0x40,0x87,0xE0,0x80,0xE0,0x40,0x30,0x40,0x18,0x20,0x0F,0xE0,0x07,0x30,0x03,0x04,0x03,0x02,0x01,0x81,0x00,0xE0,0x80,0x78,0x80,0x1F,0x80,0x07,0x80, +// '@' +0x40,0x09,0x16,0x16,0x00,0x16, +0x00,0x3F,0x00,0x06,0x03,0x00,0x23,0xFB,0x01,0x3F,0xFC,0x09,0x81,0xF8,0x48,0x7F,0xE2,0x44,0x13,0xD9,0x23,0x8F,0x41,0x1E,0x1F,0x2C,0x51,0x7C,0xE0,0xC5,0xF3,0x8B,0x06,0xCE,0x28,0x3B,0x38,0xE2,0xEE,0x71,0x4D,0x19,0xE3,0x88,0x73,0xFF,0xD1,0xE7,0x9F,0xA3,0xC7,0xF1,0x07,0xE0,0x38,0x07,0xFF,0x80,0x07,0xF8,0x00, +// 'A' +0x41,0x08,0x19,0x17,0x00,0x19, +0x00,0x0C,0x00,0x00,0x09,0x00,0x00,0x08,0x80,0x00,0x0C,0x20,0x00,0x04,0x08,0x00,0x06,0x04,0x00,0x02,0x01,0x00,0x03,0x00,0xC0,0x03,0x00,0x20,0x01,0x00,0x08,0x01,0x80,0x04,0x00,0x80,0x01,0x00,0xC0,0xC0,0x40,0xC0,0x60,0x20,0x40,0x00,0x08,0x60,0x00,0x06,0x40,0x00,0x00,0xF0,0x00,0x00,0xFE,0x1F,0xE1,0xCF,0xCF,0xF1,0x81,0xF4,0x1B,0x80,0x3E,0x0F,0x00,0x0E,0x06,0x00, +// 'B' +0x42,0x09,0x13,0x15,0x00,0x13, +0x3F,0xFC,0x0C,0x00,0x61,0xC0,0x06,0x38,0x00,0x43,0x00,0x04,0x60,0x60,0x8C,0x0C,0x11,0x81,0x02,0x30,0x00,0x46,0x00,0x08,0xC0,0x00,0x98,0x18,0x13,0x03,0x02,0x60,0x60,0x4C,0x00,0x09,0x80,0x01,0x30,0x00,0x44,0x00,0x11,0x80,0x0E,0x3F,0xFF,0x07,0xFF,0x80, +// 'C' +0x43,0x08,0x16,0x17,0x00,0x16, +0x00,0x7E,0x00,0x06,0x02,0x00,0x60,0x06,0x02,0x00,0x06,0x10,0x00,0x00,0xC0,0x00,0x22,0x00,0x01,0x18,0x0F,0x18,0x40,0x7E,0xC3,0x02,0x3E,0x0C,0x08,0xF0,0x30,0x21,0x80,0xC0,0x83,0x03,0x81,0x1A,0x0E,0x03,0xC4,0x18,0x00,0x08,0x70,0x00,0x19,0xE0,0x00,0x23,0xC0,0x01,0x87,0x80,0x1E,0x0F,0xC1,0xE0,0x0F,0xFE,0x00,0x0F,0xC0,0x00, +// 'D' +0x44,0x09,0x16,0x15,0xFF,0x15, +0x1F,0xFE,0x00,0x80,0x06,0x07,0x00,0x06,0x1C,0x00,0x0C,0x30,0x00,0x10,0xC0,0x00,0x23,0x00,0x00,0x8C,0x0F,0x01,0x30,0x3E,0x04,0xC0,0xF8,0x13,0x03,0xE0,0x4C,0x0F,0x01,0x30,0x00,0x04,0xC0,0x00,0x23,0x00,0x00,0x8C,0x00,0x04,0x30,0x00,0x20,0x80,0x01,0x06,0x00,0x18,0x1F,0xFF,0x80,0x3F,0xF0,0x00, +// 'E' +0x45,0x08,0x11,0x17,0x00,0x11, +0x00,0x01,0x0F,0xFF,0x48,0x00,0x2C,0x00,0x17,0x00,0x0B,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x1A,0x98,0x01,0x8C,0x00,0x86,0x00,0x43,0x00,0x31,0x80,0xD4,0xC0,0x02,0x60,0x01,0x30,0x00,0x98,0x00,0x4C,0x00,0x24,0x00,0x17,0xFF,0xEB,0xFF,0xF8,0x00,0x08, +// 'F' +0x46,0x08,0x11,0x16,0xFF,0x10, +0x00,0x01,0x0F,0xFF,0x48,0x00,0x2E,0x00,0x17,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x3A,0x98,0x01,0x8C,0x00,0xC6,0x00,0x43,0x00,0x21,0x81,0xD0,0xC0,0xF0,0x60,0x70,0x30,0x20,0x18,0x10,0x08,0x02,0x0C,0x01,0x07,0xFF,0x03,0xFF,0x00, +// 'G' +0x47,0x08,0x16,0x17,0x00,0x16, +0x00,0x7F,0x00,0x06,0x03,0x00,0x60,0x03,0x82,0x00,0x02,0x10,0x00,0x08,0xC0,0x00,0xC2,0x00,0x06,0x18,0x07,0x30,0x40,0x3C,0x83,0x01,0x3C,0x0C,0x04,0xFF,0xB0,0x14,0x02,0xC0,0x78,0x1B,0x80,0xE0,0x4E,0x01,0x81,0x18,0x00,0x04,0x70,0x00,0x11,0xE0,0x00,0x43,0xC0,0x01,0x07,0x80,0x18,0x0F,0x81,0xC0,0x1F,0xFC,0x00,0x0F,0xC0,0x00, +// 'H' +0x48,0x09,0x16,0x15,0x00,0x16, +0x3F,0xC7,0xF9,0x00,0xE0,0x1E,0x03,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x80,0xF0,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x00,0x09,0x80,0x00,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x3C,0x09,0x80,0xF0,0x26,0x02,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x00,0x20,0x1F,0xFF,0xFF,0xFF,0xF7,0xFC,0x00,0x00,0x00, +// 'I' +0x49,0x09,0x0B,0x15,0x00,0x0B, +0x3F,0xC8,0x07,0x81,0x70,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x4C,0x09,0x81,0x30,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x48,0x07,0x00,0xFF,0xEF,0xF8, +// 'J' +0x4A,0x09,0x0F,0x16,0x00,0x0F, +0x03,0xFC,0x08,0x04,0x38,0x08,0x70,0x10,0x60,0x20,0xC0,0x41,0x80,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x08,0x30,0x10,0xA0,0x23,0x00,0x44,0x00,0x98,0x01,0x20,0x00,0xC0,0x09,0x00,0x27,0xC1,0x8F,0xFE,0x0F,0xF0,0x00, +// 'K' +0x4B,0x08,0x17,0x18,0x00,0x17, +0x00,0x01,0x80,0x7F,0xEE,0x81,0x80,0x38,0x87,0x81,0xE0,0x8F,0x03,0x80,0x86,0x06,0x00,0x8C,0x08,0x07,0x18,0x00,0x1C,0x30,0x00,0x70,0x60,0x01,0x00,0xC0,0x02,0x01,0x80,0x02,0x03,0x00,0x04,0x06,0x04,0x04,0x0C,0x0C,0x04,0x18,0x18,0x06,0x30,0x38,0x00,0x60,0x78,0x09,0x00,0x30,0x26,0x00,0x71,0x8F,0xFF,0xE4,0x0F,0xFC,0xD0,0x00,0x01,0xC0,0x00,0x00,0x00, +// 'L' +0x4C,0x09,0x11,0x16,0x00,0x11, +0x3F,0xF0,0x30,0x04,0x1C,0x06,0x0E,0x02,0x03,0x01,0x01,0x80,0x80,0xC0,0x40,0x60,0x20,0x30,0x10,0x18,0x08,0x0C,0x04,0xC6,0x03,0xD3,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x00,0xB0,0x00,0x58,0x00,0x2F,0xFF,0xD7,0xFF,0xF0,0x00,0x30, +// 'M' +0x4D,0x09,0x20,0x16,0x00,0x1F, +0x00,0xF8,0x1F,0x00,0x01,0x84,0x30,0x80,0x01,0x82,0x61,0x00,0x01,0x82,0x40,0x00,0x01,0x81,0xC0,0x80,0x01,0x01,0x80,0x80,0x03,0x00,0x80,0x40,0x02,0x00,0x00,0x40,0x06,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x04,0x00,0x00,0x10,0x0C,0x00,0x00,0x10,0x08,0x00,0x00,0x10,0x18,0x08,0x08,0x08,0x10,0x18,0x18,0x08,0x30,0x1C,0x1C,0x04,0x60,0x3C,0x3C,0x02,0x70,0x2E,0x2E,0x06,0x7C,0x27,0x4E,0x3C,0x3F,0x27,0xC6,0xF0,0x07,0xC3,0x8F,0x80,0x01,0x83,0x06,0x00, +// 'N' +0x4E,0x09,0x17,0x15,0x00,0x17, +0x3F,0x07,0xFC,0xC1,0x10,0x07,0xC1,0x78,0x1F,0x81,0xF0,0x23,0x01,0xE0,0x46,0x01,0xC0,0x8C,0x01,0x81,0x18,0x01,0x02,0x30,0x00,0x04,0x60,0x00,0x08,0xC0,0x00,0x11,0x80,0x00,0x23,0x02,0x00,0x46,0x06,0x00,0x8C,0x0E,0x01,0x18,0x1E,0x02,0x30,0x3F,0x04,0x60,0x3F,0x0B,0x00,0x2F,0x17,0xFF,0x8F,0xEF,0xFE,0x0F,0x80, +// 'O' +0x4F,0x08,0x17,0x17,0x00,0x17, +0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00, +// 'P' +0x50,0x09,0x13,0x15,0x00,0x13, +0x3F,0xFC,0x08,0x00,0x43,0x80,0x06,0x70,0x00,0x46,0x00,0x04,0xC0,0x00,0x18,0x00,0x0B,0x01,0x81,0x60,0x30,0x2C,0x04,0x05,0x80,0x00,0x30,0x00,0x26,0x00,0x08,0xC0,0x02,0x18,0x01,0x83,0x01,0xE0,0x60,0x30,0x08,0x04,0x03,0x00,0x80,0x7F,0xE0,0x07,0xF8,0x00, +// 'Q' +0x51,0x09,0x17,0x1C,0x00,0x17, +0x00,0x7E,0x00,0x03,0x01,0x00,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x01,0x08,0x00,0x01,0x30,0x1E,0x02,0x40,0x7E,0x03,0x81,0x1E,0x07,0x02,0x1C,0x0E,0x04,0x18,0x1C,0x04,0x20,0x3C,0x07,0x80,0x78,0x00,0x01,0x30,0x00,0x02,0x70,0x00,0x04,0xF0,0x00,0x10,0xF0,0x00,0x70,0xF0,0x00,0x00,0xFC,0x00,0x40,0xFF,0x81,0x00,0x3F,0x84,0x00,0x0F,0x10,0x00,0x06,0x40,0x00,0x0F,0x00,0x00,0x1C,0x00,0x00,0x00,0x00, +// 'R' +0x52,0x09,0x18,0x17,0x00,0x18, +0x3F,0xFE,0x00,0x60,0x01,0x80,0x70,0x00,0x40,0x70,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x10,0x10,0x30,0x18,0x10,0x30,0x10,0x10,0x30,0x00,0x10,0x30,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x1E,0x30,0x00,0x0A,0x30,0x18,0x06,0x30,0x1C,0x0C,0x20,0x0E,0x18,0x7F,0xFF,0x30,0x7F,0xE7,0x20,0x00,0x03,0xC0,0x00,0x03,0x80,0x00,0x03,0x00, +// 'S' +0x53,0x07,0x11,0x19,0x00,0x11, +0x00,0x06,0x00,0x02,0x80,0x1E,0x40,0x10,0x20,0x30,0x10,0x30,0x08,0x10,0x04,0x18,0x01,0x08,0x00,0x8C,0x0F,0x46,0x07,0xA3,0x01,0xE1,0xC0,0xE1,0xE0,0x41,0x70,0x21,0x80,0x10,0xC0,0x08,0x70,0x08,0x38,0x04,0x0C,0x04,0x06,0x04,0x03,0x3C,0x01,0xFC,0x00,0xF0,0x00,0x30,0x00,0x00, +// 'T' +0x54,0x08,0x12,0x16,0x01,0x13, +0x30,0x01,0x93,0xFF,0xDC,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0x00,0x01,0xCC,0x07,0x7F,0x01,0xFF,0xC0,0x79,0xB0,0x1C,0x0C,0x04,0x03,0x01,0x00,0xC0,0x40,0x30,0x10,0x0C,0x04,0x03,0x01,0x00,0xC0,0x60,0x60,0x08,0x1F,0xFC,0x07,0xFE,0x00, +// 'U' +0x55,0x09,0x19,0x16,0xFF,0x18, +0x1F,0xF1,0xFF,0x10,0x05,0x00,0x9C,0x07,0xC0,0xCE,0x02,0xE0,0x43,0x01,0x30,0x21,0x80,0x98,0x10,0xC0,0x4C,0x08,0x60,0x26,0x04,0x30,0x13,0x02,0x18,0x09,0x81,0x0C,0x04,0xC0,0x86,0x02,0x60,0x43,0x01,0x30,0x21,0xC0,0x70,0x10,0xE0,0x00,0x10,0x30,0x00,0x08,0x1C,0x00,0x08,0x0F,0x00,0x08,0x03,0xC0,0x08,0x00,0xFC,0x18,0x00,0x1F,0xF8,0x00,0x03,0xF0,0x00, +// 'V' +0x56,0x07,0x19,0x18,0x00,0x19, +0x00,0xC1,0xC0,0x01,0x91,0x98,0x03,0x18,0xC3,0x06,0x04,0x60,0x64,0x02,0x20,0x1F,0x00,0xB0,0x1B,0xC0,0x70,0x0C,0xF0,0x10,0x08,0x38,0x00,0x08,0x0E,0x00,0x04,0x07,0x00,0x04,0x01,0xC0,0x06,0x00,0xE0,0x02,0x00,0x38,0x02,0x00,0x1C,0x01,0x00,0x07,0x01,0x00,0x03,0x81,0x80,0x00,0xE0,0x80,0x00,0x30,0x80,0x00,0x1C,0x40,0x00,0x06,0x40,0x00,0x03,0xE0,0x00,0x00,0xE0,0x00,0x00,0x60,0x00, +// 'W' +0x57,0x07,0x20,0x18,0x00,0x20, +0x00,0x60,0x03,0x80,0x01,0x90,0x06,0x60,0x0E,0x10,0xC6,0x1C,0x38,0x11,0xA6,0x07,0x60,0x11,0x26,0x01,0x78,0x12,0x1C,0x06,0x78,0x0E,0x1C,0x04,0x3C,0x0C,0x08,0x08,0x1C,0x00,0x08,0x10,0x0C,0x00,0x00,0x10,0x0E,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x07,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x80,0x00,0x80,0x03,0x80,0x00,0x80,0x01,0xC0,0x81,0x00,0x01,0xC1,0xC0,0x00,0x00,0xE1,0xC2,0x00,0x00,0xE2,0xE4,0x00,0x00,0x74,0xE4,0x00,0x00,0x7C,0x78,0x00,0x00,0x38,0x78,0x00,0x00,0x30,0x30,0x00, +// 'X' +0x58,0x05,0x19,0x1D,0x00,0x18, +0x00,0x01,0x80,0x00,0x01,0x20,0x00,0x61,0x8C,0x00,0x48,0xC3,0x00,0x44,0x40,0x60,0x43,0x60,0x10,0xC0,0xE0,0x18,0xC0,0x30,0x1C,0xC0,0x00,0x18,0x7C,0x00,0x08,0x1F,0x00,0x08,0x03,0x80,0x08,0x00,0xE0,0x04,0x00,0x38,0x02,0x00,0x0C,0x01,0x80,0x04,0x00,0x60,0x06,0x00,0x10,0x02,0x00,0x07,0x03,0x00,0x00,0x41,0x01,0x00,0x63,0x00,0xC0,0x62,0x00,0xF0,0x63,0x80,0x7C,0x61,0xF0,0x4E,0x60,0x7C,0x63,0xE0,0x0F,0x91,0xE0,0x01,0xF0,0x40,0x00,0x70,0x00,0x00,0x00,0x00,0x00, +// 'Y' +0x59,0x06,0x19,0x1B,0x00,0x19, +0x00,0x00,0x60,0x00,0xC0,0x48,0x00,0x80,0x62,0x00,0x88,0x20,0xC0,0x82,0x30,0x11,0x80,0xB0,0x01,0x80,0x30,0x0D,0x80,0x00,0x0C,0xF8,0x00,0x08,0x3E,0x00,0x04,0x07,0x80,0x04,0x01,0xE0,0x04,0x00,0x78,0x04,0x00,0x1C,0x02,0x00,0x0C,0x02,0x00,0x04,0x02,0x00,0x04,0x02,0x00,0x06,0x01,0x00,0x0E,0x01,0x00,0x0C,0x01,0x00,0x07,0x01,0x00,0x03,0xC0,0x80,0x01,0xF0,0x80,0x00,0x3E,0x00,0x00,0x0F,0xC0,0x00,0x03,0xE0,0x00,0x00,0x40,0x00,0x00, +// 'Z' +0x5A,0x08,0x13,0x17,0xFF,0x12, +0x0C,0x00,0x02,0xFF,0xFC,0xC0,0x01,0x98,0x00,0x23,0x00,0x04,0x60,0x01,0x0C,0x00,0x21,0x80,0x08,0x37,0x01,0x07,0xE0,0x60,0xF8,0x08,0x03,0x01,0x00,0x40,0x70,0x18,0x0D,0x02,0x00,0x20,0xC0,0x04,0x18,0x00,0x86,0x00,0x10,0xC0,0x02,0x10,0x00,0x47,0xFF,0xE8,0xFF,0xFE,0x00,0x01,0x80, +// '[' +0x5B,0x04,0x0B,0x20,0x00,0x0B, +0x00,0x40,0x19,0xFD,0x60,0x2C,0x05,0x80,0xB0,0x16,0x0E,0xC1,0xD8,0x33,0x04,0x60,0x8C,0x11,0x82,0x30,0x46,0x08,0xC1,0x18,0x23,0x04,0x60,0x8C,0x11,0x82,0xB0,0x76,0x02,0xC0,0x58,0x0B,0x01,0x60,0x2F,0xFD,0xFF,0x80,0x60,0x00, +// '\' +0x5C,0x09,0x11,0x18,0x00,0x11, +0x3F,0x00,0x30,0x40,0x38,0x10,0x1E,0x08,0x0F,0x04,0x03,0x81,0x01,0xE0,0x80,0x70,0x20,0x3C,0x10,0x1E,0x08,0x07,0x02,0x03,0xC1,0x00,0xE0,0x40,0x78,0x20,0x3C,0x08,0x0F,0x04,0x07,0x82,0x01,0xC0,0x80,0xF0,0x40,0x38,0x10,0x1E,0x08,0x0F,0xFC,0x03,0xFC,0x01,0xFC, +// ']' +0x5D,0x04,0x0C,0x1F,0xFF,0x0B, +0x30,0x02,0xFF,0x60,0x16,0x01,0x60,0x16,0x01,0x60,0x16,0xC1,0x7C,0x16,0xC1,0x6C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x13,0xC1,0x6C,0x16,0x01,0x60,0x16,0x01,0x60,0x16,0x01,0x7F,0xE7,0xFE,0x60,0x00, +// '^' +0x5E,0x1E,0x00,0x00,0x00,0x09, + +// '_' +0x5F,0x20,0x10,0x04,0x00,0x10, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +// '`' +0x60,0x00,0x0A,0x09,0x00,0x0B, +0x00,0x06,0x02,0x61,0x8C,0x60,0xBF,0x17,0xFC,0x3E,0x03,0x00, +// 'a' +0x61,0x08,0x19,0x17,0x00,0x19, +0x00,0x0C,0x00,0x00,0x09,0x00,0x00,0x08,0x80,0x00,0x0C,0x20,0x00,0x04,0x08,0x00,0x06,0x04,0x00,0x02,0x01,0x00,0x03,0x00,0xC0,0x03,0x00,0x20,0x01,0x00,0x08,0x01,0x80,0x04,0x00,0x80,0x01,0x00,0xC0,0xC0,0x40,0xC0,0x60,0x20,0x40,0x00,0x08,0x60,0x00,0x06,0x40,0x00,0x00,0xF0,0x00,0x00,0xFE,0x1F,0xE1,0xCF,0xCF,0xF1,0x81,0xF4,0x1B,0x80,0x3E,0x0F,0x00,0x0E,0x06,0x00, +// 'b' +0x62,0x09,0x13,0x15,0x00,0x13, +0x3F,0xFC,0x0C,0x00,0x61,0xC0,0x06,0x38,0x00,0x43,0x00,0x04,0x60,0x60,0x8C,0x0C,0x11,0x81,0x02,0x30,0x00,0x46,0x00,0x08,0xC0,0x00,0x98,0x18,0x13,0x03,0x02,0x60,0x60,0x4C,0x00,0x09,0x80,0x01,0x30,0x00,0x44,0x00,0x11,0x80,0x0E,0x3F,0xFF,0x07,0xFF,0x80, +// 'c' +0x63,0x08,0x16,0x17,0x00,0x16, +0x00,0x7E,0x00,0x06,0x02,0x00,0x60,0x06,0x02,0x00,0x06,0x10,0x00,0x00,0xC0,0x00,0x22,0x00,0x01,0x18,0x0F,0x18,0x40,0x7E,0xC3,0x02,0x3E,0x0C,0x08,0xF0,0x30,0x21,0x80,0xC0,0x83,0x03,0x81,0x1A,0x0E,0x03,0xC4,0x18,0x00,0x08,0x70,0x00,0x19,0xE0,0x00,0x23,0xC0,0x01,0x87,0x80,0x1E,0x0F,0xC1,0xE0,0x0F,0xFE,0x00,0x0F,0xC0,0x00, +// 'd' +0x64,0x09,0x16,0x15,0xFF,0x15, +0x1F,0xFE,0x00,0x80,0x06,0x07,0x00,0x06,0x1C,0x00,0x0C,0x30,0x00,0x10,0xC0,0x00,0x23,0x00,0x00,0x8C,0x0F,0x01,0x30,0x3E,0x04,0xC0,0xF8,0x13,0x03,0xE0,0x4C,0x0F,0x01,0x30,0x00,0x04,0xC0,0x00,0x23,0x00,0x00,0x8C,0x00,0x04,0x30,0x00,0x20,0x80,0x01,0x06,0x00,0x18,0x1F,0xFF,0x80,0x3F,0xF0,0x00, +// 'e' +0x65,0x08,0x11,0x17,0x00,0x11, +0x00,0x01,0x0F,0xFF,0x48,0x00,0x2C,0x00,0x17,0x00,0x0B,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x1A,0x98,0x01,0x8C,0x00,0x86,0x00,0x43,0x00,0x31,0x80,0xD4,0xC0,0x02,0x60,0x01,0x30,0x00,0x98,0x00,0x4C,0x00,0x24,0x00,0x17,0xFF,0xEB,0xFF,0xF8,0x00,0x08, +// 'f' +0x66,0x08,0x11,0x16,0xFF,0x10, +0x00,0x01,0x0F,0xFF,0x48,0x00,0x2E,0x00,0x17,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x3A,0x98,0x01,0x8C,0x00,0xC6,0x00,0x43,0x00,0x21,0x81,0xD0,0xC0,0xF0,0x60,0x70,0x30,0x20,0x18,0x10,0x08,0x02,0x0C,0x01,0x07,0xFF,0x03,0xFF,0x00, +// 'g' +0x67,0x08,0x16,0x17,0x00,0x16, +0x00,0x7F,0x00,0x06,0x03,0x00,0x60,0x03,0x82,0x00,0x02,0x10,0x00,0x08,0xC0,0x00,0xC2,0x00,0x06,0x18,0x07,0x30,0x40,0x3C,0x83,0x01,0x3C,0x0C,0x04,0xFF,0xB0,0x14,0x02,0xC0,0x78,0x1B,0x80,0xE0,0x4E,0x01,0x81,0x18,0x00,0x04,0x70,0x00,0x11,0xE0,0x00,0x43,0xC0,0x01,0x07,0x80,0x18,0x0F,0x81,0xC0,0x1F,0xFC,0x00,0x0F,0xC0,0x00, +// 'h' +0x68,0x09,0x16,0x15,0x00,0x16, +0x3F,0xC7,0xF9,0x00,0xE0,0x1E,0x03,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x80,0xF0,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x00,0x09,0x80,0x00,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x3C,0x09,0x80,0xF0,0x26,0x02,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x00,0x20,0x1F,0xFF,0xFF,0xFF,0xF7,0xFC,0x00,0x00,0x00, +// 'i' +0x69,0x09,0x0B,0x15,0x00,0x0B, +0x3F,0xC8,0x07,0x81,0x70,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x4C,0x09,0x81,0x30,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x48,0x07,0x00,0xFF,0xEF,0xF8, +// 'j' +0x6A,0x09,0x0F,0x16,0x00,0x0F, +0x03,0xFC,0x08,0x04,0x38,0x08,0x70,0x10,0x60,0x20,0xC0,0x41,0x80,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x08,0x30,0x10,0xA0,0x23,0x00,0x44,0x00,0x98,0x01,0x20,0x00,0xC0,0x09,0x00,0x27,0xC1,0x8F,0xFE,0x0F,0xF0,0x00, +// 'k' +0x6B,0x08,0x17,0x18,0x00,0x17, +0x00,0x01,0x80,0x7F,0xEE,0x81,0x80,0x38,0x87,0x81,0xE0,0x8F,0x03,0x80,0x86,0x06,0x00,0x8C,0x08,0x07,0x18,0x00,0x1C,0x30,0x00,0x70,0x60,0x01,0x00,0xC0,0x02,0x01,0x80,0x02,0x03,0x00,0x04,0x06,0x04,0x04,0x0C,0x0C,0x04,0x18,0x18,0x06,0x30,0x38,0x00,0x60,0x78,0x09,0x00,0x30,0x26,0x00,0x71,0x8F,0xFF,0xE4,0x0F,0xFC,0xD0,0x00,0x01,0xC0,0x00,0x00,0x00, +// 'l' +0x6C,0x09,0x11,0x16,0x00,0x11, +0x3F,0xF0,0x30,0x04,0x1C,0x06,0x0E,0x02,0x03,0x01,0x01,0x80,0x80,0xC0,0x40,0x60,0x20,0x30,0x10,0x18,0x08,0x0C,0x04,0xC6,0x03,0xD3,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x00,0xB0,0x00,0x58,0x00,0x2F,0xFF,0xD7,0xFF,0xF0,0x00,0x30, +// 'm' +0x6D,0x09,0x20,0x16,0x00,0x1F, +0x00,0xF8,0x1F,0x00,0x01,0x84,0x30,0x80,0x01,0x82,0x61,0x00,0x01,0x82,0x40,0x00,0x01,0x81,0xC0,0x80,0x01,0x01,0x80,0x80,0x03,0x00,0x80,0x40,0x02,0x00,0x00,0x40,0x06,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x04,0x00,0x00,0x10,0x0C,0x00,0x00,0x10,0x08,0x00,0x00,0x10,0x18,0x08,0x08,0x08,0x10,0x18,0x18,0x08,0x30,0x1C,0x1C,0x04,0x60,0x3C,0x3C,0x02,0x70,0x2E,0x2E,0x06,0x7C,0x27,0x4E,0x3C,0x3F,0x27,0xC6,0xF0,0x07,0xC3,0x8F,0x80,0x01,0x83,0x06,0x00, +// 'n' +0x6E,0x09,0x17,0x15,0x00,0x17, +0x3F,0x07,0xFC,0xC1,0x10,0x07,0xC1,0x78,0x1F,0x81,0xF0,0x23,0x01,0xE0,0x46,0x01,0xC0,0x8C,0x01,0x81,0x18,0x01,0x02,0x30,0x00,0x04,0x60,0x00,0x08,0xC0,0x00,0x11,0x80,0x00,0x23,0x02,0x00,0x46,0x06,0x00,0x8C,0x0E,0x01,0x18,0x1E,0x02,0x30,0x3F,0x04,0x60,0x3F,0x0B,0x00,0x2F,0x17,0xFF,0x8F,0xEF,0xFE,0x0F,0x80, +// 'o' +0x6F,0x08,0x17,0x17,0x00,0x17, +0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00, +// 'p' +0x70,0x09,0x13,0x15,0x00,0x13, +0x3F,0xFC,0x08,0x00,0x43,0x80,0x06,0x70,0x00,0x46,0x00,0x04,0xC0,0x00,0x18,0x00,0x0B,0x01,0x81,0x60,0x30,0x2C,0x04,0x05,0x80,0x00,0x30,0x00,0x26,0x00,0x08,0xC0,0x02,0x18,0x01,0x83,0x01,0xE0,0x60,0x30,0x08,0x04,0x03,0x00,0x80,0x7F,0xE0,0x07,0xF8,0x00, +// 'q' +0x71,0x09,0x17,0x1C,0x00,0x17, +0x00,0x7E,0x00,0x03,0x01,0x00,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x01,0x08,0x00,0x01,0x30,0x1E,0x02,0x40,0x7E,0x03,0x81,0x1E,0x07,0x02,0x1C,0x0E,0x04,0x18,0x1C,0x04,0x20,0x3C,0x07,0x80,0x78,0x00,0x01,0x30,0x00,0x02,0x70,0x00,0x04,0xF0,0x00,0x10,0xF0,0x00,0x70,0xF0,0x00,0x00,0xFC,0x00,0x40,0xFF,0x81,0x00,0x3F,0x84,0x00,0x0F,0x10,0x00,0x06,0x40,0x00,0x0F,0x00,0x00,0x1C,0x00,0x00,0x00,0x00, +// 'r' +0x72,0x09,0x18,0x17,0x00,0x18, +0x3F,0xFE,0x00,0x60,0x01,0x80,0x70,0x00,0x40,0x70,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x10,0x10,0x30,0x18,0x10,0x30,0x10,0x10,0x30,0x00,0x10,0x30,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x1E,0x30,0x00,0x0A,0x30,0x18,0x06,0x30,0x1C,0x0C,0x20,0x0E,0x18,0x7F,0xFF,0x30,0x7F,0xE7,0x20,0x00,0x03,0xC0,0x00,0x03,0x80,0x00,0x03,0x00, +// 's' +0x73,0x07,0x11,0x19,0x00,0x11, +0x00,0x06,0x00,0x02,0x80,0x1E,0x40,0x10,0x20,0x30,0x10,0x30,0x08,0x10,0x04,0x18,0x01,0x08,0x00,0x8C,0x0F,0x46,0x07,0xA3,0x01,0xE1,0xC0,0xE1,0xE0,0x41,0x70,0x21,0x80,0x10,0xC0,0x08,0x70,0x08,0x38,0x04,0x0C,0x04,0x06,0x04,0x03,0x3C,0x01,0xFC,0x00,0xF0,0x00,0x30,0x00,0x00, +// 't' +0x74,0x08,0x12,0x16,0x01,0x13, +0x30,0x01,0x93,0xFF,0xDC,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0x00,0x01,0xCC,0x07,0x7F,0x01,0xFF,0xC0,0x79,0xB0,0x1C,0x0C,0x04,0x03,0x01,0x00,0xC0,0x40,0x30,0x10,0x0C,0x04,0x03,0x01,0x00,0xC0,0x60,0x60,0x08,0x1F,0xFC,0x07,0xFE,0x00, +// 'u' +0x75,0x09,0x19,0x16,0xFF,0x18, +0x1F,0xF1,0xFF,0x10,0x05,0x00,0x9C,0x07,0xC0,0xCE,0x02,0xE0,0x43,0x01,0x30,0x21,0x80,0x98,0x10,0xC0,0x4C,0x08,0x60,0x26,0x04,0x30,0x13,0x02,0x18,0x09,0x81,0x0C,0x04,0xC0,0x86,0x02,0x60,0x43,0x01,0x30,0x21,0xC0,0x70,0x10,0xE0,0x00,0x10,0x30,0x00,0x08,0x1C,0x00,0x08,0x0F,0x00,0x08,0x03,0xC0,0x08,0x00,0xFC,0x18,0x00,0x1F,0xF8,0x00,0x03,0xF0,0x00, +// 'v' +0x76,0x07,0x19,0x18,0x00,0x19, +0x00,0xC1,0xC0,0x01,0x91,0x98,0x03,0x18,0xC3,0x06,0x04,0x60,0x64,0x02,0x20,0x1F,0x00,0xB0,0x1B,0xC0,0x70,0x0C,0xF0,0x10,0x08,0x38,0x00,0x08,0x0E,0x00,0x04,0x07,0x00,0x04,0x01,0xC0,0x06,0x00,0xE0,0x02,0x00,0x38,0x02,0x00,0x1C,0x01,0x00,0x07,0x01,0x00,0x03,0x81,0x80,0x00,0xE0,0x80,0x00,0x30,0x80,0x00,0x1C,0x40,0x00,0x06,0x40,0x00,0x03,0xE0,0x00,0x00,0xE0,0x00,0x00,0x60,0x00, +// 'w' +0x77,0x07,0x20,0x18,0x00,0x20, +0x00,0x60,0x03,0x80,0x01,0x90,0x06,0x60,0x0E,0x10,0xC6,0x1C,0x38,0x11,0xA6,0x07,0x60,0x11,0x26,0x01,0x78,0x12,0x1C,0x06,0x78,0x0E,0x1C,0x04,0x3C,0x0C,0x08,0x08,0x1C,0x00,0x08,0x10,0x0C,0x00,0x00,0x10,0x0E,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x07,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x80,0x00,0x80,0x03,0x80,0x00,0x80,0x01,0xC0,0x81,0x00,0x01,0xC1,0xC0,0x00,0x00,0xE1,0xC2,0x00,0x00,0xE2,0xE4,0x00,0x00,0x74,0xE4,0x00,0x00,0x7C,0x78,0x00,0x00,0x38,0x78,0x00,0x00,0x30,0x30,0x00, +// 'x' +0x78,0x05,0x19,0x1D,0x00,0x18, +0x00,0x01,0x80,0x00,0x01,0x20,0x00,0x61,0x8C,0x00,0x48,0xC3,0x00,0x44,0x40,0x60,0x43,0x60,0x10,0xC0,0xE0,0x18,0xC0,0x30,0x1C,0xC0,0x00,0x18,0x7C,0x00,0x08,0x1F,0x00,0x08,0x03,0x80,0x08,0x00,0xE0,0x04,0x00,0x38,0x02,0x00,0x0C,0x01,0x80,0x04,0x00,0x60,0x06,0x00,0x10,0x02,0x00,0x07,0x03,0x00,0x00,0x41,0x01,0x00,0x63,0x00,0xC0,0x62,0x00,0xF0,0x63,0x80,0x7C,0x61,0xF0,0x4E,0x60,0x7C,0x63,0xE0,0x0F,0x91,0xE0,0x01,0xF0,0x40,0x00,0x70,0x00,0x00,0x00,0x00,0x00, +// 'y' +0x79,0x06,0x19,0x1B,0x00,0x19, +0x00,0x00,0x60,0x00,0xC0,0x48,0x00,0x80,0x62,0x00,0x88,0x20,0xC0,0x82,0x30,0x11,0x80,0xB0,0x01,0x80,0x30,0x0D,0x80,0x00,0x0C,0xF8,0x00,0x08,0x3E,0x00,0x04,0x07,0x80,0x04,0x01,0xE0,0x04,0x00,0x78,0x04,0x00,0x1C,0x02,0x00,0x0C,0x02,0x00,0x04,0x02,0x00,0x04,0x02,0x00,0x06,0x01,0x00,0x0E,0x01,0x00,0x0C,0x01,0x00,0x07,0x01,0x00,0x03,0xC0,0x80,0x01,0xF0,0x80,0x00,0x3E,0x00,0x00,0x0F,0xC0,0x00,0x03,0xE0,0x00,0x00,0x40,0x00,0x00, +// 'z' +0x7A,0x08,0x13,0x17,0xFF,0x12, +0x0C,0x00,0x02,0xFF,0xFC,0xC0,0x01,0x98,0x00,0x23,0x00,0x04,0x60,0x01,0x0C,0x00,0x21,0x80,0x08,0x37,0x01,0x07,0xE0,0x60,0xF8,0x08,0x03,0x01,0x00,0x40,0x70,0x18,0x0D,0x02,0x00,0x20,0xC0,0x04,0x18,0x00,0x86,0x00,0x10,0xC0,0x02,0x10,0x00,0x47,0xFF,0xE8,0xFF,0xFE,0x00,0x01,0x80, +// '{' +0x7B,0x05,0x0D,0x1F,0x01,0x0E, +0x00,0x10,0x0E,0x81,0x04,0x10,0x21,0x81,0x08,0x08,0xC0,0x46,0x0E,0x30,0x71,0x83,0x0C,0x10,0x60,0x84,0x04,0x60,0x23,0x02,0x18,0x08,0xC0,0x47,0x82,0x3C,0x10,0x60,0x83,0x05,0x18,0x38,0xC0,0x46,0x02,0x38,0x11,0xC0,0x87,0x84,0x1F,0xE0,0x7F,0x00,0x30,0x00,0x00, +// '|' +0x7C,0x1E,0x00,0x00,0x00,0x09, + +// '}' +0x7D,0x04,0x0E,0x1F,0x00,0x0F, +0x30,0x00,0xBC,0x06,0x0C,0x18,0x08,0x60,0x21,0x80,0x46,0x01,0x1F,0x04,0x7C,0x11,0xB0,0x44,0xC1,0x03,0x04,0x0C,0x0C,0x30,0x10,0xE0,0x43,0x81,0x04,0x04,0x30,0x70,0xC1,0x03,0x04,0x0C,0x10,0xF0,0x46,0xC1,0x18,0x04,0x60,0x11,0x80,0x86,0x04,0x18,0x60,0x7F,0x81,0xF8,0x06,0x00,0x00, +// '~' +0x7E,0x1E,0x00,0x00,0x00,0x09, + + +// Terminator +0xFF +}; diff --git a/components/mkspiffs/.travis.yml b/components/mkspiffs/.travis.yml new file mode 100644 index 0000000..5c77d15 --- /dev/null +++ b/components/mkspiffs/.travis.yml @@ -0,0 +1,24 @@ +language: cpp +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 +script: + - export CXX="g++-4.8" CC="gcc-4.8" + - make dist +notifications: + email: + recipients: + - ivan@esp8266.com + on_success: change + on_failure: always +deploy: + provider: releases + api_key: + secure: k/DDoCfLXIct8TcGjekKm5CAoTL6Wy6LXhI74Ssgc8VSbjJQ1crO2J4V5HnQw7QplgAXwqjkoAUkzEJiz34wqBaAv0w9o8+5jPwCP6rMQ/VEvbn1MPI52KIFbYqKxmXe5J24B00BttbGc773ldXPnvmv+qPWWcXDTpcosni2laBt3z8bxCGVwkQ7nuQUaAelzs21wQuhjmKQ6F1dgNN5XhdJ5qgFYYM8cwiigvcqIaCwLMcrOs7gj22TS242pzp38etWhfxUbqUKejWgOH4qYeU3Tf5gsu4WV0otbqXYMvV18gVSoAiMnodDfYJUNHlfelCAEqebvECrIvvE8D0syuVYz6fh2/BrxZ6HeiYj1CXILghOjPUZZZ7/chKglWnA1vL+6Uxn5LtyTJ5gbgMYXvKQUXrRVZ3Zd9xrmv/YaMnGq+6BDBkS30aXpSK2X0HvW9/6JyQ9L1sjnKdOzDmagvikHm2rrPXBRMMfYTt4nucgUBWqJqyFe0uGva/n8TG5RzOzdOgRxFx/lF8XudtR4Z4gUBUFpve/meVHVVK82+cxzfN97aFdxvBzyGq18EDjOEpDi7k7mZjXUycvD8UZ7o12sxJ0Zr6/9esiFUsaqyE+2Vi6bMbgEHGx7hfWbdfREnpOtjFLH+u5mAPIqOh89a/UJ6SbYm0cON+ewTMUkJ8= + file: mkspiffs-$TRAVIS_TAG-linux64.tar.gz + on: + repo: igrr/mkspiffs + tags: true diff --git a/components/mkspiffs/Makefile.projbuild b/components/mkspiffs/Makefile.projbuild new file mode 100644 index 0000000..0f805ad --- /dev/null +++ b/components/mkspiffs/Makefile.projbuild @@ -0,0 +1,14 @@ +MKSPIFFS_COMPONENT_PATH := $(COMPONENT_PATH) +MKSPIFFS_BUILD_DIR=$(abspath $(MKSPIFFS_COMPONENT_PATH)/mkspiffs) + +# Custom recursive make for mkspiffs sub-project +MKSPIFFS_MAKE=+$(MAKE) -C $(MKSPIFFS_COMPONENT_PATH)/src + +.PHONY: mkspiffs clean + +mkspiffs: $(SDKCONFIG_MAKEFILE) + $(MKSPIFFS_MAKE) all + +clean: $(SDKCONFIG_MAKEFILE) + $(MKSPIFFS_MAKE) clean + diff --git a/components/mkspiffs/component.mk b/components/mkspiffs/component.mk new file mode 100644 index 0000000..12f5e46 --- /dev/null +++ b/components/mkspiffs/component.mk @@ -0,0 +1,6 @@ +# +# Component Makefile +# + +COMPONENT_SRCDIRS := +COMPONENT_ADD_INCLUDEDIRS := diff --git a/components/mkspiffs/src/Makefile.original b/components/mkspiffs/src/Makefile.original new file mode 100644 index 0000000..ce2261c --- /dev/null +++ b/components/mkspiffs/src/Makefile.original @@ -0,0 +1,93 @@ +CFLAGS ?= -std=gnu99 -Os -Wall +CXXFLAGS ?= -std=gnu++11 -Os -Wall + +ifeq ($(OS),Windows_NT) + TARGET_OS := WINDOWS + DIST_SUFFIX := windows + ARCHIVE_CMD := 7z a + ARCHIVE_EXTENSION := zip + TARGET := mkspiffs.exe + TARGET_CFLAGS := -mno-ms-bitfields + TARGET_LDFLAGS := -Wl,-static -static-libgcc + +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + TARGET_OS := LINUX + UNAME_P := $(shell uname -p) + ifeq ($(UNAME_P),x86_64) + DIST_SUFFIX := linux64 + endif + ifneq ($(filter %86,$(UNAME_P)),) + DIST_SUFFIX := linux32 + endif + endif + ifeq ($(UNAME_S),Darwin) + TARGET_OS := OSX + DIST_SUFFIX := osx + CC=clang + CXX=clang++ + TARGET_CFLAGS = -mmacosx-version-min=10.7 -arch i386 -arch x86_64 + TARGET_CXXFLAGS = -mmacosx-version-min=10.7 -arch i386 -arch x86_64 -stdlib=libc++ + TARGET_LDFLAGS = -arch i386 -arch x86_64 -stdlib=libc++ + endif + ARCHIVE_CMD := tar czf + ARCHIVE_EXTENSION := tar.gz + TARGET := mkspiffs +endif + +VERSION ?= $(shell git describe --always) + +OBJ := main.o \ + spiffs/spiffs_cache.o \ + spiffs/spiffs_check.o \ + spiffs/spiffs_gc.o \ + spiffs/spiffs_hydrogen.o \ + spiffs/spiffs_nucleus.o \ + +INCLUDES := -Itclap -Ispiffs -I. + +CFLAGS += $(TARGET_CFLAGS) +CXXFLAGS += $(TARGET_CXXFLAGS) +LDFLAGS += $(TARGET_LDFLAGS) + +CPPFLAGS += $(INCLUDES) -D$(TARGET_OS) -DVERSION=\"$(VERSION)\" -D__NO_INLINE__ + +DIST_NAME := mkspiffs-$(VERSION)-$(DIST_SUFFIX) +DIST_DIR := $(DIST_NAME) +DIST_ARCHIVE := $(DIST_NAME).$(ARCHIVE_EXTENSION) + +.PHONY: all clean dist + +all: $(TARGET) + +dist: test $(DIST_ARCHIVE) + +$(DIST_ARCHIVE): $(TARGET) $(DIST_DIR) + cp $(TARGET) $(DIST_DIR)/ + $(ARCHIVE_CMD) $(DIST_ARCHIVE) $(DIST_DIR) + +$(TARGET): $(OBJ) + $(CXX) $^ -o $@ $(LDFLAGS) + strip $(TARGET) + +$(DIST_DIR): + @mkdir -p $@ + +clean: + @rm -f *.o + @rm -f spiffs/*.o + @rm -f $(TARGET) + +SPIFFS_TEST_FS_CONFIG := -s 0x100000 -p 512 -b 0x2000 + +test: $(TARGET) + ls -1 spiffs > out.list0 + ./mkspiffs -c spiffs $(SPIFFS_TEST_FS_CONFIG) out.spiffs | sort | sed s/^\\/// > out.list1 + ./mkspiffs -u spiffs_u $(SPIFFS_TEST_FS_CONFIG) out.spiffs | sort | sed s/^\\/// > out.list_u + ./mkspiffs -l $(SPIFFS_TEST_FS_CONFIG) out.spiffs | cut -f 2 | sort | sed s/^\\/// > out.list2 + diff --strip-trailing-cr out.list0 out.list1 + diff --strip-trailing-cr out.list0 out.list2 + diff spiffs spiffs_u + rm -f out.{list0,list1,list2,list_u,spiffs} + rm -R spiffs_u diff --git a/components/mkspiffs/src/README.md b/components/mkspiffs/src/README.md new file mode 100644 index 0000000..6d9a275 --- /dev/null +++ b/components/mkspiffs/src/README.md @@ -0,0 +1,80 @@ +# mkspiffs +Tool to build and unpack [SPIFFS](https://github.com/pellepl/spiffs) images. + + +## Usage + +``` + + mkspiffs {-c |-u |-l|-i} [-d <0-5>] [-b ] + [-p ] [-s ] [--] [--version] [-h] + + + +Where: + + -c , --create + (OR required) create spiffs image from a directory + -- OR -- + -u , --unpack + (OR required) unpack spiffs image to a directory + -- OR -- + -l, --list + (OR required) list files in spiffs image + -- OR -- + -i, --visualize + (OR required) visualize spiffs image + + + -d <0-5>, --debug <0-5> + Debug level. 0 means no debug output. + + -b , --block + fs block size, in bytes + + -p , --page + fs page size, in bytes + + -s , --size + fs image size, in bytes + + --, --ignore_rest + Ignores the rest of the labeled arguments following this flag. + + --version + Displays version information and exits. + + -h, --help + Displays usage information and exits. + + + (required) spiffs image file + + +``` +## Build + +You need gcc (≥4.8) or clang(≥600.0.57), and make. On Windows, use MinGW. + +Run: +```bash +$ make dist +``` + +### Build status + +Linux | Windows +------|------- + [![Linux build status](http://img.shields.io/travis/igrr/mkspiffs.svg)](https://travis-ci.org/igrr/mkspiffs) | [![Windows build status](http://img.shields.io/appveyor/ci/igrr/mkspiffs.svg)](https://ci.appveyor.com/project/igrr/mkspiffs) + + +## License + +MIT + +## To do + +- [ ] Add more debug output and print SPIFFS debug output +- [ ] Error handling +- [ ] Determine the image size automatically when opening a file +- [ ] Code cleanup diff --git a/components/mkspiffs/src/appveyor.yml b/components/mkspiffs/src/appveyor.yml new file mode 100644 index 0000000..42f99d4 --- /dev/null +++ b/components/mkspiffs/src/appveyor.yml @@ -0,0 +1,28 @@ +version: 0.0.{build} + +platform: + - x86 + +skip_commits: + message: /\[ci skip\]/ + +matrix: + fast_finish: true + +build_script: + - SET PATH=C:\MinGW\bin;C:\MinGW\msys\1.0\bin;%PATH% + - make dist + +artifacts: + - path: '*.zip' + +deploy: + release: $(PRODUCT_VERSION) + provider: GitHub + auth_token: + secure: 'PGg5fnoBpP1Omzr6f3KIYDiD8J30rretQjSl/MITRpzvSCmN88kM6VDMz1TBGZTA' + artifact: /.*\.zip/ + draft: true + prerelease: false + on: + appveyor_repo_tag: true diff --git a/components/mkspiffs/src/main.cpp b/components/mkspiffs/src/main.cpp new file mode 100644 index 0000000..1a7a774 --- /dev/null +++ b/components/mkspiffs/src/main.cpp @@ -0,0 +1,646 @@ +// +// main.cpp +// make_spiffs +// +// Created by Ivan Grokhotkov on 13/05/15. +// Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. +// +#define TCLAP_SETBASE_ZERO 1 +#define VERSION "0.3.6" + +#include +#include "spiffs/spiffs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tclap/CmdLine.h" +#include "tclap/UnlabeledValueArg.h" + +static std::vector s_flashmem; + +static std::string s_dirName; +static std::string s_imageName; +static int s_imageSize; +static int s_pageSize; +static int s_blockSize; + +typedef struct { + time_t mtime; + time_t ctime; + time_t atime; + uint8_t spare[SPIFFS_OBJ_META_LEN - (sizeof(time_t)*3)]; +} spiffs_metadata_t; + +enum Action { ACTION_NONE, ACTION_PACK, ACTION_UNPACK, ACTION_LIST, ACTION_VISUALIZE }; +static Action s_action = ACTION_NONE; + +static spiffs s_fs; + +static std::vector s_spiffsWorkBuf; +static std::vector s_spiffsFds; +static std::vector s_spiffsCache; + + +static s32_t api_spiffs_read(u32_t addr, u32_t size, u8_t *dst){ + memcpy(dst, &s_flashmem[0] + addr, size); + return SPIFFS_OK; +} + +static s32_t api_spiffs_write(u32_t addr, u32_t size, u8_t *src){ + memcpy(&s_flashmem[0] + addr, src, size); + return SPIFFS_OK; +} + +static s32_t api_spiffs_erase(u32_t addr, u32_t size){ + memset(&s_flashmem[0] + addr, 0xff, size); + return SPIFFS_OK; +} + + +int g_debugLevel = 0; + + +//implementation + +int spiffsTryMount(){ + spiffs_config cfg = {0}; + + cfg.phys_addr = 0x0000; + cfg.phys_size = (u32_t) s_flashmem.size(); + + cfg.phys_erase_block = s_blockSize; + cfg.log_block_size = s_blockSize; + cfg.log_page_size = s_pageSize; + + cfg.hal_read_f = api_spiffs_read; + cfg.hal_write_f = api_spiffs_write; + cfg.hal_erase_f = api_spiffs_erase; + + const int maxOpenFiles = 4; + s_spiffsWorkBuf.resize(s_pageSize * 2); + s_spiffsFds.resize(32 * maxOpenFiles); + s_spiffsCache.resize((32 + s_pageSize) * maxOpenFiles); + + return SPIFFS_mount(&s_fs, &cfg, + &s_spiffsWorkBuf[0], + &s_spiffsFds[0], s_spiffsFds.size(), + &s_spiffsCache[0], s_spiffsCache.size(), + NULL); +} + +bool spiffsMount(){ + if(SPIFFS_mounted(&s_fs)) + return true; + int res = spiffsTryMount(); + return (res == SPIFFS_OK); +} + +bool spiffsFormat(){ + spiffsMount(); + SPIFFS_unmount(&s_fs); + int formated = SPIFFS_format(&s_fs); + if(formated != SPIFFS_OK) + return false; + return (spiffsTryMount() == SPIFFS_OK); +} + +void spiffsUnmount(){ + if(SPIFFS_mounted(&s_fs)) + SPIFFS_unmount(&s_fs); +} + +// WHITECAT BEGIN +int addDir(const char* name) { + spiffs_metadata_t meta; + + std::string fileName = name; + fileName += "/."; + + std::cout << fileName << std::endl; + + spiffs_file dst = SPIFFS_open(&s_fs, fileName.c_str(), SPIFFS_CREAT, 0); + if (dst < 0) { + std::cerr << "SPIFFS_write error(" << s_fs.err_code << "): "; + + if (s_fs.err_code == SPIFFS_ERR_FULL) { + std::cerr << "File system is full." << std::endl; + } else { + std::cerr << "unknown"; + } + std::cerr << std::endl; + + SPIFFS_close(&s_fs, dst); + return 1; + } + + SPIFFS_close(&s_fs, dst); + if (strlen(name) > 0) { + // Get the system time to file timestamps + meta.atime = time(NULL); + meta.ctime = meta.atime; + meta.mtime = meta.atime; + SPIFFS_update_meta(&s_fs, fileName.c_str(), &meta); + } + + return 0; +} +// WHITECAT END + +int addFile(char* name, const char* path) { + spiffs_metadata_t meta; + + FILE* src = fopen(path, "rb"); + if (!src) { + std::cerr << "error: failed to open " << path << " for reading" << std::endl; + return 1; + } + + spiffs_file dst = SPIFFS_open(&s_fs, name, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0); + + // read file size + fseek(src, 0, SEEK_END); + size_t size = ftell(src); + fseek(src, 0, SEEK_SET); + + if (g_debugLevel > 0) { + std::cout << "file size: " << size << std::endl; + } + + size_t left = size; + uint8_t data_byte; + while (left > 0){ + if (1 != fread(&data_byte, 1, 1, src)) { + std::cerr << "fread error!" << std::endl; + + fclose(src); + SPIFFS_close(&s_fs, dst); + return 1; + } + int res = SPIFFS_write(&s_fs, dst, &data_byte, 1); + if (res < 0) { + std::cerr << "SPIFFS_write error(" << s_fs.err_code << "): "; + + if (s_fs.err_code == SPIFFS_ERR_FULL) { + std::cerr << "File system is full." << std::endl; + } else { + std::cerr << "unknown"; + } + std::cerr << std::endl; + + if (g_debugLevel > 0) { + std::cout << "data left: " << left << std::endl; + } + + fclose(src); + SPIFFS_close(&s_fs, dst); + return 1; + } + left -= 1; + } + + SPIFFS_close(&s_fs, dst); + + // Get the system time to file timestamps + meta.atime = time(NULL); + meta.ctime = meta.atime; + meta.mtime = meta.atime; + SPIFFS_update_meta(&s_fs, name, &meta); + + fclose(src); + + return 0; +} + +int addFiles(const char* dirname, const char* subPath) { + DIR *dir; + struct dirent *ent; + bool error = false; + std::string dirPath = dirname; + dirPath += subPath; + + // Open directory + if ((dir = opendir (dirPath.c_str())) != NULL) { + + // Read files from directory. + while ((ent = readdir (dir)) != NULL) { + // Ignore dir itself. + if (ent->d_name[0] == '.') + continue; + + std::string fullpath = dirPath; + fullpath += ent->d_name; + struct stat path_stat; + stat (fullpath.c_str(), &path_stat); + + if (!S_ISREG(path_stat.st_mode)) { + // Check if path is a directory. + if (S_ISDIR(path_stat.st_mode)) { + // Prepare new sub path. + std::string newSubPath = subPath; + newSubPath += ent->d_name; + + // WHITECAT BEGIN + addDir(newSubPath.c_str()); + // WHITECAT END + + newSubPath += "/"; + + if (addFiles(dirname, newSubPath.c_str()) != 0) + { + std::cerr << "Error for adding content from " << ent->d_name << "!" << std::endl; + } + + continue; + } + else + { + std::cerr << "skipping " << ent->d_name << std::endl; + continue; + } + } + + // Filepath with dirname as root folder. + std::string filepath = subPath; + filepath += ent->d_name; + std::cout << filepath << std::endl; + + // Add File to image. + if (addFile((char*)filepath.c_str(), fullpath.c_str()) != 0) { + std::cerr << "error adding file!" << std::endl; + error = true; + if (g_debugLevel > 0) { + std::cout << std::endl; + } + break; + } + } // end while + closedir (dir); + } else { + std::cerr << "warning: can't read source directory" << std::endl; + return 1; + } + + return (error) ? 1 : 0; +} + +void listFiles() { + spiffs_DIR dir; + spiffs_dirent ent; + + SPIFFS_opendir(&s_fs, 0, &dir); + spiffs_dirent* it; + while (true) { + it = SPIFFS_readdir(&dir, &ent); + if (!it) + break; + + std::cout << it->size << '\t' << it->name << std::endl; + } + SPIFFS_closedir(&dir); +} + +/** + * @brief Check if directory exists. + * @param path Directory path. + * @return True if exists otherwise false. + * + * @author Pascal Gollor (http://www.pgollor.de/cms/) + */ +bool dirExists(const char* path) { + DIR *d = opendir(path); + + if (d) { + closedir(d); + return true; + } + + return false; +} + +/** + * @brief Create directory if it not exists. + * @param path Directory path. + * @return True or false. + * + * @author Pascal Gollor (http://www.pgollor.de/cms/) + */ +bool dirCreate(const char* path) { + // Check if directory also exists. + if (dirExists(path)) { + return false; + } + + // platform stuff... +#if defined(_WIN32) + if (_mkdir(path) != 0) { +#else + if (mkdir(path, S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH) != 0) { +#endif + std::cerr << "Can not create directory!!!" << std::endl; + return false; + } + + return true; +} + +/** + * @brief Unpack file from file system. + * @param spiffsFile SPIFFS dir entry pointer. + * @param destPath Destination file path path. + * @return True or false. + * + * @author Pascal Gollor (http://www.pgollor.de/cms/) + */ +bool unpackFile(spiffs_dirent *spiffsFile, const char *destPath) { + u8_t buffer[spiffsFile->size]; + std::string filename = (const char*)(spiffsFile->name); + + // Open file from spiffs file system. + spiffs_file src = SPIFFS_open(&s_fs, (char *)(filename.c_str()), SPIFFS_RDONLY, 0); + + // read content into buffer + SPIFFS_read(&s_fs, src, buffer, spiffsFile->size); + + // Close spiffs file. + SPIFFS_close(&s_fs, src); + + // Open file. + FILE* dst = fopen(destPath, "wb"); + + // Write content into file. + fwrite(buffer, sizeof(u8_t), sizeof(buffer), dst); + + // Close file. + fclose(dst); + + + return true; +} + +/** + * @brief Unpack files from file system. + * @param sDest Directory path as std::string. + * @return True or false. + * + * @author Pascal Gollor (http://www.pgollor.de/cms/) + * + * todo: Do unpack stuff for directories. + */ +bool unpackFiles(std::string sDest) { + spiffs_DIR dir; + spiffs_dirent ent; + + // Add "./" to path if is not given. + if (sDest.find("./") == std::string::npos && sDest.find("/") == std::string::npos) { + sDest = "./" + sDest; + } + + // Check if directory exists. If it does not then try to create it with permissions 755. + if (! dirExists(sDest.c_str())) { + std::cout << "Directory " << sDest << " does not exists. Try to create it." << std::endl; + + // Try to create directory. + if (! dirCreate(sDest.c_str())) { + return false; + } + } + + // Open directory. + SPIFFS_opendir(&s_fs, 0, &dir); + + // Read content from directory. + spiffs_dirent* it = SPIFFS_readdir(&dir, &ent); + while (it) { + // Check if content is a file. + if ((int)(it->type) == 1) { + std::string name = (const char*)(it->name); + std::string sDestFilePath = sDest + name; + size_t pos = name.find_last_of("/"); + + // If file is in sub directory? + if (pos > 0) { + // Subdir path. + std::string path = sDest; + path += name.substr(0, pos); + + // Create subddir if subdir not exists. + if (!dirExists(path.c_str())) { + if (!dirCreate(path.c_str())) { + return false; + } + } + } + + // Unpack file to destination directory. + if (! unpackFile(it, sDestFilePath.c_str()) ) { + std::cout << "Can not unpack " << it->name << "!" << std::endl; + return false; + } + + // Output stuff. + std::cout + << it->name + << '\t' + << " > " << sDestFilePath + << '\t' + << "size: " << it->size << " Bytes" + << std::endl; + } + + // Get next file handle. + it = SPIFFS_readdir(&dir, &ent); + } // end while + + // Close directory. + SPIFFS_closedir(&dir); + + return true; +} + +// Actions + +int actionPack() { + s_flashmem.resize(s_imageSize, 0xff); + + FILE* fdres = fopen(s_imageName.c_str(), "wb"); + if (!fdres) { + std::cerr << "error: failed to open image file" << std::endl; + return 1; + } + + spiffsFormat(); + + // WHITECAT BEGIN + addDir(""); + // WHITECAT END + + int result = addFiles(s_dirName.c_str(), "/"); + spiffsUnmount(); + + fwrite(&s_flashmem[0], 4, s_flashmem.size()/4, fdres); + fclose(fdres); + + return result; +} + +/** + * @brief Unpack action. + * @return 0 success, 1 error + * + * @author Pascal Gollor (http://www.pgollor.de/cms/) + */ +int actionUnpack(void) { + int ret = 0; + s_flashmem.resize(s_imageSize, 0xff); + + // open spiffs image + FILE* fdsrc = fopen(s_imageName.c_str(), "rb"); + if (!fdsrc) { + std::cerr << "error: failed to open image file" << std::endl; + return 1; + } + + // read content into s_flashmem + ret = fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc); + + // close fiel handle + fclose(fdsrc); + + // mount file system + spiffsMount(); + + // unpack files + if (! unpackFiles(s_dirName)) { + ret = 1; + } + + // unmount file system + spiffsUnmount(); + + return ret; +} + + +int actionList() { + int ret = 0; + s_flashmem.resize(s_imageSize, 0xff); + + FILE* fdsrc = fopen(s_imageName.c_str(), "rb"); + if (!fdsrc) { + std::cerr << "error: failed to open image file" << std::endl; + return 1; + } + + ret = fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc); + fclose(fdsrc); + spiffsMount(); + listFiles(); + spiffsUnmount(); + + ret = 0; + return ret; +} + +int actionVisualize() { + int ret = 0; + s_flashmem.resize(s_imageSize, 0xff); + + FILE* fdsrc = fopen(s_imageName.c_str(), "rb"); + if (!fdsrc) { + std::cerr << "error: failed to open image file" << std::endl; + return 1; + } + + ret = fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc); + fclose(fdsrc); + + spiffsMount(); + //SPIFFS_vis(&s_fs); + uint32_t total, used; + SPIFFS_info(&s_fs, &total, &used); + std::cout << "total: " << total << std::endl << "used: " << used << std::endl; + spiffsUnmount(); + + ret = 0; + return ret; +} + +void processArgs(int argc, const char** argv) { + TCLAP::CmdLine cmd("", ' ', VERSION); + TCLAP::ValueArg packArg( "c", "create", "create spiffs image from a directory", true, "", "pack_dir"); + TCLAP::ValueArg unpackArg( "u", "unpack", "unpack spiffs image to a directory", true, "", "dest_dir"); + TCLAP::SwitchArg listArg( "l", "list", "list files in spiffs image", false); + TCLAP::SwitchArg visualizeArg( "i", "visualize", "visualize spiffs image", false); + TCLAP::UnlabeledValueArg outNameArg( "image_file", "spiffs image file", true, "", "image_file" ); + TCLAP::ValueArg imageSizeArg( "s", "size", "fs image size, in bytes", false, 0x10000, "number" ); + TCLAP::ValueArg pageSizeArg( "p", "page", "fs page size, in bytes", false, 256, "number" ); + TCLAP::ValueArg blockSizeArg( "b", "block", "fs block size, in bytes", false, 4096, "number" ); + TCLAP::ValueArg debugArg( "d", "debug", "Debug level. 0 means no debug output.", false, 0, "0-5" ); + + cmd.add( imageSizeArg ); + cmd.add( pageSizeArg ); + cmd.add( blockSizeArg ); + cmd.add(debugArg); + std::vector args = {&packArg, &unpackArg, &listArg, &visualizeArg}; + cmd.xorAdd( args ); + cmd.add( outNameArg ); + cmd.parse( argc, argv ); + + if (debugArg.getValue() > 0) { + std::cout << "Debug output enabled" << std::endl; + g_debugLevel = debugArg.getValue(); + } + + if (packArg.isSet()) { + s_dirName = packArg.getValue(); + s_action = ACTION_PACK; + } else if (unpackArg.isSet()) { + s_dirName = unpackArg.getValue(); + s_action = ACTION_UNPACK; + } else if (listArg.isSet()) { + s_action = ACTION_LIST; + } else if (visualizeArg.isSet()) { + s_action = ACTION_VISUALIZE; + } + + s_imageName = outNameArg.getValue(); + s_imageSize = imageSizeArg.getValue(); + s_pageSize = pageSizeArg.getValue(); + s_blockSize = blockSizeArg.getValue(); +} + +int main(int argc, const char * argv[]) { + + try { + processArgs(argc, argv); + } catch(...) { + std::cerr << "Invalid arguments" << std::endl; + return 1; + } + + switch (s_action) { + case ACTION_PACK: + return actionPack(); + break; + case ACTION_UNPACK: + return actionUnpack(); + break; + case ACTION_LIST: + return actionList(); + break; + case ACTION_VISUALIZE: + return actionVisualize(); + break; + default: + break; + } + + return 1; +} diff --git a/components/mkspiffs/src/mkspiffs b/components/mkspiffs/src/mkspiffs new file mode 100755 index 0000000..cb8fb9a Binary files /dev/null and b/components/mkspiffs/src/mkspiffs differ diff --git a/components/mkspiffs/src/spiffs/esp_spiffs.c b/components/mkspiffs/src/spiffs/esp_spiffs.c new file mode 100644 index 0000000..088480c --- /dev/null +++ b/components/mkspiffs/src/spiffs/esp_spiffs.c @@ -0,0 +1,138 @@ +/* + * Lua RTOS, SPIFFS low access + * + * Copyright (C) 2015 - 2017 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * + * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + */ + +#include + +#include "esp_spiffs.h" +#include "esp_attr.h" + +#include "spiffs.h" + +#include + +s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) { + u32_t aaddr; + u8_t *buff = NULL; + u8_t *abuff = NULL; + u32_t asize; + + asize = size; + + // Align address to 4 byte + aaddr = (addr + (4 - 1)) & (u32_t)-4; + if (aaddr != addr) { + aaddr -= 4; + asize += (addr - aaddr); + } + + // Align size to 4 byte + asize = (asize + (4 - 1)) & (u32_t)-4; + + if ((aaddr != addr) || (asize != size)) { + // Align buffer + buff = malloc(asize + 4); + if (!buff) { + return SPIFFS_ERR_INTERNAL; + } + + abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & (u32_t)-4); + + if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) { + free(buff); + return SPIFFS_ERR_INTERNAL; + } + + memcpy(dst, abuff + (addr - aaddr), size); + + free(buff); + } else { + if (spi_flash_read(addr, (void *)dst, size) != 0) { + return SPIFFS_ERR_INTERNAL; + } + } + + return SPIFFS_OK; +} + +s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) { + u32_t aaddr; + u8_t *buff = NULL; + u8_t *abuff = NULL; + u32_t asize; + + asize = size; + + // Align address to 4 byte + aaddr = (addr + (4 - 1)) & -4; + if (aaddr != addr) { + aaddr -= 4; + asize += (addr - aaddr); + } + + // Align size to 4 byte + asize = (asize + (4 - 1)) & -4; + + if ((aaddr != addr) || (asize != size)) { + // Align buffer + buff = malloc(asize + 4); + if (!buff) { + return SPIFFS_ERR_INTERNAL; + } + + abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & -4); + + if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) { + free(buff); + return SPIFFS_ERR_INTERNAL; + } + + memcpy(abuff + (addr - aaddr), src, size); + + if (spi_flash_write(aaddr, (uint32_t *)abuff, asize) != 0) { + free(buff); + return SPIFFS_ERR_INTERNAL; + } + + free(buff); + } else { + if (spi_flash_write(addr, (uint32_t *)src, size) != 0) { + return SPIFFS_ERR_INTERNAL; + } + } + + return SPIFFS_OK; +} + +s32_t IRAM_ATTR esp32_spi_flash_erase(u32_t addr, u32_t size) { + if (spi_flash_erase_sector(addr >> 12) != 0) { + return SPIFFS_ERR_INTERNAL; + } + + return SPIFFS_OK; +} diff --git a/components/mkspiffs/src/spiffs/esp_spiffs.h b/components/mkspiffs/src/spiffs/esp_spiffs.h new file mode 100644 index 0000000..c68e652 --- /dev/null +++ b/components/mkspiffs/src/spiffs/esp_spiffs.h @@ -0,0 +1,43 @@ +/* + * Lua RTOS, write syscall implementation + * + * Copyright (C) 2015 - 2017 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * + * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + */ + +#ifndef __ESP_SPIFFS_H__ +#define __ESP_SPIFFS_H__ + +#include "spiffs.h" + +s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst); +s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src); +s32_t esp32_spi_flash_erase(u32_t addr, u32_t size); + +#define low_spiffs_read (spiffs_read *)esp32_spi_flash_read +#define low_spiffs_write (spiffs_write *)esp32_spi_flash_write +#define low_spiffs_erase (spiffs_erase *)esp32_spi_flash_erase + +#endif // __ESP_SPIFFS_H__ diff --git a/components/mkspiffs/src/spiffs/spiffs.h b/components/mkspiffs/src/spiffs/spiffs.h new file mode 100644 index 0000000..d87422d --- /dev/null +++ b/components/mkspiffs/src/spiffs/spiffs.h @@ -0,0 +1,813 @@ +/* + * spiffs.h + * + * Created on: May 26, 2013 + * Author: petera + */ + +#ifndef SPIFFS_H_ +#define SPIFFS_H_ +#if defined(__cplusplus) +extern "C" { +#endif + +#include "spiffs_config.h" + +#define SPIFFS_OK 0 +#define SPIFFS_ERR_NOT_MOUNTED -10000 +#define SPIFFS_ERR_FULL -10001 +#define SPIFFS_ERR_NOT_FOUND -10002 +#define SPIFFS_ERR_END_OF_OBJECT -10003 +#define SPIFFS_ERR_DELETED -10004 +#define SPIFFS_ERR_NOT_FINALIZED -10005 +#define SPIFFS_ERR_NOT_INDEX -10006 +#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007 +#define SPIFFS_ERR_FILE_CLOSED -10008 +#define SPIFFS_ERR_FILE_DELETED -10009 +#define SPIFFS_ERR_BAD_DESCRIPTOR -10010 +#define SPIFFS_ERR_IS_INDEX -10011 +#define SPIFFS_ERR_IS_FREE -10012 +#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013 +#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014 +#define SPIFFS_ERR_INDEX_REF_FREE -10015 +#define SPIFFS_ERR_INDEX_REF_LU -10016 +#define SPIFFS_ERR_INDEX_REF_INVALID -10017 +#define SPIFFS_ERR_INDEX_FREE -10018 +#define SPIFFS_ERR_INDEX_LU -10019 +#define SPIFFS_ERR_INDEX_INVALID -10020 +#define SPIFFS_ERR_NOT_WRITABLE -10021 +#define SPIFFS_ERR_NOT_READABLE -10022 +#define SPIFFS_ERR_CONFLICTING_NAME -10023 +#define SPIFFS_ERR_NOT_CONFIGURED -10024 + +#define SPIFFS_ERR_NOT_A_FS -10025 +#define SPIFFS_ERR_MOUNTED -10026 +#define SPIFFS_ERR_ERASE_FAIL -10027 +#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028 + +#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 + +#define SPIFFS_ERR_FILE_EXISTS -10030 + +#define SPIFFS_ERR_NOT_A_FILE -10031 +#define SPIFFS_ERR_RO_NOT_IMPL -10032 +#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033 +#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034 +#define SPIFFS_ERR_PROBE_NOT_A_FS -10035 +#define SPIFFS_ERR_NAME_TOO_LONG -10036 + +#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037 +#define SPIFFS_ERR_IX_MAP_MAPPED -10038 +#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039 + +#define SPIFFS_ERR_INTERNAL -10050 + +#define SPIFFS_ERR_TEST -10100 + + +// spiffs file descriptor index type. must be signed +typedef s16_t spiffs_file; +// spiffs file descriptor flags +typedef u16_t spiffs_flags; +// spiffs file mode +typedef u16_t spiffs_mode; +// object type +typedef u8_t spiffs_obj_type; + +struct spiffs_t; + +#if SPIFFS_HAL_CALLBACK_EXTRA + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); + +#else // SPIFFS_HAL_CALLBACK_EXTRA + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +/* file system check callback report operation */ +typedef enum { + SPIFFS_CHECK_LOOKUP = 0, + SPIFFS_CHECK_INDEX, + SPIFFS_CHECK_PAGE +} spiffs_check_type; + +/* file system check callback report type */ +typedef enum { + SPIFFS_CHECK_PROGRESS = 0, + SPIFFS_CHECK_ERROR, + SPIFFS_CHECK_FIX_INDEX, + SPIFFS_CHECK_FIX_LOOKUP, + SPIFFS_CHECK_DELETE_ORPHANED_INDEX, + SPIFFS_CHECK_DELETE_PAGE, + SPIFFS_CHECK_DELETE_BAD_FILE +} spiffs_check_report; + +/* file system check callback function */ +#if SPIFFS_HAL_CALLBACK_EXTRA +typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); +#else // SPIFFS_HAL_CALLBACK_EXTRA +typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +/* file system listener callback operation */ +typedef enum { + /* the file has been created */ + SPIFFS_CB_CREATED = 0, + /* the file has been updated or moved to another page */ + SPIFFS_CB_UPDATED, + /* the file has been deleted */ + SPIFFS_CB_DELETED +} spiffs_fileop_type; + +/* file system listener callback function */ +typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix); + +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) \ + printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) +#endif + +/* Any write to the filehandle is appended to end of the file */ +#define SPIFFS_APPEND (1<<0) +#define SPIFFS_O_APPEND SPIFFS_APPEND +/* If the opened file exists, it will be truncated to zero length before opened */ +#define SPIFFS_TRUNC (1<<1) +#define SPIFFS_O_TRUNC SPIFFS_TRUNC +/* If the opened file does not exist, it will be created before opened */ +#define SPIFFS_CREAT (1<<2) +#define SPIFFS_O_CREAT SPIFFS_CREAT +/* The opened file may only be read */ +#define SPIFFS_RDONLY (1<<3) +#define SPIFFS_O_RDONLY SPIFFS_RDONLY +/* The opened file may only be written */ +#define SPIFFS_WRONLY (1<<4) +#define SPIFFS_O_WRONLY SPIFFS_WRONLY +/* The opened file may be both read and written */ +#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) +#define SPIFFS_O_RDWR SPIFFS_RDWR +/* Any writes to the filehandle will never be cached but flushed directly */ +#define SPIFFS_DIRECT (1<<5) +#define SPIFFS_O_DIRECT SPIFFS_DIRECT +/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */ +#define SPIFFS_EXCL (1<<6) +#define SPIFFS_O_EXCL SPIFFS_EXCL + +#define SPIFFS_SEEK_SET (0) +#define SPIFFS_SEEK_CUR (1) +#define SPIFFS_SEEK_END (2) + +#define SPIFFS_TYPE_FILE (1) +#define SPIFFS_TYPE_DIR (2) +#define SPIFFS_TYPE_HARD_LINK (3) +#define SPIFFS_TYPE_SOFT_LINK (4) + +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) +#endif + +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#endif + +// phys structs + +// spiffs spi configuration struct +typedef struct { + // physical read function + spiffs_read hal_read_f; + // physical write function + spiffs_write hal_write_f; + // physical erase function + spiffs_erase hal_erase_f; +#if SPIFFS_SINGLETON == 0 + // physical size of the spi flash + u32_t phys_size; + // physical offset in spi flash used for spiffs, + // must be on block boundary + u32_t phys_addr; + // physical size when erasing a block + u32_t phys_erase_block; + + // logical size of a block, must be on physical + // block size boundary and must never be less than + // a physical block + u32_t log_block_size; + // logical size of a page, must be at least + // log_block_size / 8 + u32_t log_page_size; + +#endif +#if SPIFFS_FILEHDL_OFFSET + // an integer offset added to each file handle + u16_t fh_ix_offset; +#endif +} spiffs_config; + +typedef struct spiffs_t { + // file system configuration + spiffs_config cfg; + // number of logical blocks + u32_t block_count; + + // cursor for free blocks, block index + spiffs_block_ix free_cursor_block_ix; + // cursor for free blocks, entry index + int free_cursor_obj_lu_entry; + // cursor when searching, block index + spiffs_block_ix cursor_block_ix; + // cursor when searching, entry index + int cursor_obj_lu_entry; + + // primary work buffer, size of a logical page + u8_t *lu_work; + // secondary work buffer, size of a logical page + u8_t *work; + // file descriptor memory area + u8_t *fd_space; + // available file descriptors + u32_t fd_count; + + // last error + s32_t err_code; + + // current number of free blocks + u32_t free_blocks; + // current number of busy pages + u32_t stats_p_allocated; + // current number of deleted pages + u32_t stats_p_deleted; + // flag indicating that garbage collector is cleaning + u8_t cleaning; + // max erase count amongst all blocks + spiffs_obj_id max_erase_count; + +#if SPIFFS_GC_STATS + u32_t stats_gc_runs; +#endif + +#if SPIFFS_CACHE + // cache memory + void *cache; + // cache size + u32_t cache_size; +#if SPIFFS_CACHE_STATS + u32_t cache_hits; + u32_t cache_misses; +#endif +#endif + + // check callback function + spiffs_check_callback check_cb_f; + // file callback function + spiffs_file_callback file_cb_f; + // mounted flag + u8_t mounted; + // user data + void *user_data; + // config magic + u32_t config_magic; +} spiffs; + +/* spiffs file status struct */ +typedef struct { + spiffs_obj_id obj_id; + u32_t size; + spiffs_obj_type type; + spiffs_page_ix pix; + u8_t name[SPIFFS_OBJ_NAME_LEN]; +#if SPIFFS_OBJ_META_LEN + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +} spiffs_stat; + +struct spiffs_dirent { + spiffs_obj_id obj_id; + u8_t name[SPIFFS_OBJ_NAME_LEN]; + spiffs_obj_type type; + u32_t size; + spiffs_page_ix pix; +#if SPIFFS_OBJ_META_LEN + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +}; + +typedef struct { + spiffs *fs; + spiffs_block_ix block; + int entry; +} spiffs_DIR; + +#if SPIFFS_IX_MAP + +typedef struct { + // buffer with looked up data pixes + spiffs_page_ix *map_buf; + // precise file byte offset + u32_t offset; + // start data span index of lookup buffer + spiffs_span_ix start_spix; + // end data span index of lookup buffer + spiffs_span_ix end_spix; +} spiffs_ix_map; + +#endif + +// functions + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +/** + * Special function. This takes a spiffs config struct and returns the number + * of blocks this file system was formatted with. This function relies on + * that following info is set correctly in given config struct: + * + * phys_addr, log_page_size, and log_block_size. + * + * Also, hal_read_f must be set in the config struct. + * + * One must be sure of the correct page size and that the physical address is + * correct in the probed file system when calling this function. It is not + * checked if the phys_addr actually points to the start of the file system, + * so one might get a false positive if entering a phys_addr somewhere in the + * middle of the file system at block boundary. In addition, it is not checked + * if the page size is actually correct. If it is not, weird file system sizes + * will be returned. + * + * If this function detects a file system it returns the assumed file system + * size, which can be used to set the phys_size. + * + * Otherwise, it returns an error indicating why it is not regarded as a file + * system. + * + * Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK + * macros. It returns the error code directly, instead of as read by + * SPIFFS_errno. + * + * @param config essential parts of the physical and logical + * configuration of the file system. + */ +s32_t SPIFFS_probe_fs(spiffs_config *config); +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +/** + * Initializes the file system dynamic parameters and mounts the filesystem. + * If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS + * if the flash does not contain a recognizable file system. + * In this case, SPIFFS_format must be called prior to remounting. + * @param fs the file system struct + * @param config the physical and logical configuration of the file system + * @param work a memory work buffer comprising 2*config->log_page_size + * bytes used throughout all file system operations + * @param fd_space memory for file descriptors + * @param fd_space_size memory size of file descriptors + * @param cache memory for cache, may be null + * @param cache_size memory size of cache + * @param check_cb_f callback function for reporting during consistency checks + */ +s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f); + +/** + * Unmounts the file system. All file handles will be flushed of any + * cached writes and closed. + * @param fs the file system struct + */ +void SPIFFS_unmount(spiffs *fs); + +/** + * Creates a new file. + * @param fs the file system struct + * @param path the path of the new file + * @param mode ignored, for posix compliance + */ +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); + +/** + * Opens/creates a file. + * @param fs the file system struct + * @param path the path of the new file + * @param flags the flags for the open command, can be combinations of + * SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, + * SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); + +/** + * Opens a file by given dir entry. + * Optimization purposes, when traversing a file system with SPIFFS_readdir + * a normal SPIFFS_open would need to traverse the filesystem again to find + * the file, whilst SPIFFS_open_by_dirent already knows where the file resides. + * @param fs the file system struct + * @param e the dir entry to the file + * @param flags the flags for the open command, can be combinations of + * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + * SPIFFS_CREAT will have no effect in this case. + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode); + +/** + * Opens a file by given page index. + * Optimization purposes, opens a file by directly pointing to the page + * index in the spi flash. + * If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE + * is returned. + * @param fs the file system struct + * @param page_ix the page index + * @param flags the flags for the open command, can be combinations of + * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + * SPIFFS_CREAT will have no effect in this case. + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode); + +/** + * Reads from given filehandle. + * @param fs the file system struct + * @param fh the filehandle + * @param buf where to put read data + * @param len how much to read + * @returns number of bytes read, or -1 if error + */ +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); + +/** + * Writes to given filehandle. + * @param fs the file system struct + * @param fh the filehandle + * @param buf the data to write + * @param len how much to write + * @returns number of bytes written, or -1 if error + */ +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); + +/** + * Moves the read/write file offset. Resulting offset is returned or negative if error. + * lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. + * @param fs the file system struct + * @param fh the filehandle + * @param offs how much/where to move the offset + * @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes + * if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset + * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative + */ +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); + +/** + * Removes a file by path + * @param fs the file system struct + * @param path the path of the file to remove + */ +s32_t SPIFFS_remove(spiffs *fs, const char *path); + +/** + * Removes a file by filehandle + * @param fs the file system struct + * @param fh the filehandle of the file to remove + */ +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); + +/** + * Gets file status by path + * @param fs the file system struct + * @param path the path of the file to stat + * @param s the stat struct to populate + */ +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); + +/** + * Gets file status by filehandle + * @param fs the file system struct + * @param fh the filehandle of the file to stat + * @param s the stat struct to populate + */ +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); + +/** + * Flushes all pending write operations from cache for given file + * @param fs the file system struct + * @param fh the filehandle of the file to flush + */ +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); + +/** + * Closes a filehandle. If there are pending write operations, these are finalized before closing. + * @param fs the file system struct + * @param fh the filehandle of the file to close + */ +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); + +/** + * Renames a file + * @param fs the file system struct + * @param old path of file to rename + * @param newPath new path of file + */ +s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath); + +#if SPIFFS_OBJ_META_LEN +/** + * Updates file's metadata + * @param fs the file system struct + * @param path path to the file + * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. + */ +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); + +/** + * Updates file's metadata + * @param fs the file system struct + * @param fh file handle of the file + * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. + */ +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); +#endif + +/** + * Returns last error of last file operation. + * @param fs the file system struct + */ +s32_t SPIFFS_errno(spiffs *fs); + +/** + * Clears last error. + * @param fs the file system struct + */ +void SPIFFS_clearerr(spiffs *fs); + +/** + * Opens a directory stream corresponding to the given name. + * The stream is positioned at the first entry in the directory. + * On hydrogen builds the name argument is ignored as hydrogen builds always correspond + * to a flat file structure - no directories. + * @param fs the file system struct + * @param name the name of the directory + * @param d pointer the directory stream to be populated + */ +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); + +/** + * Closes a directory stream + * @param d the directory stream to close + */ +s32_t SPIFFS_closedir(spiffs_DIR *d); + +/** + * Reads a directory into given spifs_dirent struct. + * @param d pointer to the directory stream + * @param e the dirent struct to be populated + * @returns null if error or end of stream, else given dirent is returned + */ +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); + +/** + * Runs a consistency check on given filesystem. + * @param fs the file system struct + */ +s32_t SPIFFS_check(spiffs *fs); + +/** + * Returns number of total bytes available and number of used bytes. + * This is an estimation, and depends on if there a many files with little + * data or few files with much data. + * NB: If used number of bytes exceeds total bytes, a SPIFFS_check should + * run. This indicates a power loss in midst of things. In worst case + * (repeated powerlosses in mending or gc) you might have to delete some files. + * + * @param fs the file system struct + * @param total total number of bytes in filesystem + * @param used used number of bytes in filesystem + */ +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); + +/** + * Formats the entire file system. All data will be lost. + * The filesystem must not be mounted when calling this. + * + * NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount + * MUST be called prior to formatting in order to configure the filesystem. + * If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling + * SPIFFS_format. + * If SPIFFS_mount fails, SPIFFS_format can be called directly without calling + * SPIFFS_unmount first. + * + * @param fs the file system struct + */ +s32_t SPIFFS_format(spiffs *fs); + +/** + * Returns nonzero if spiffs is mounted, or zero if unmounted. + * @param fs the file system struct + */ +u8_t SPIFFS_mounted(spiffs *fs); + +/** + * Tries to find a block where most or all pages are deleted, and erase that + * block if found. Does not care for wear levelling. Will not move pages + * around. + * If parameter max_free_pages are set to 0, only blocks with only deleted + * pages will be selected. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * Setting max_free_pages to anything larger than zero will eventually wear + * flash more as a block containing free pages can be erased. + * + * Will set err_no to SPIFFS_OK if a block was found and erased, + * SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, + * or other error. + * + * @param fs the file system struct + * @param max_free_pages maximum number allowed free pages in block + */ +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); + +/** + * Will try to make room for given amount of bytes in the filesystem by moving + * pages and erasing blocks. + * If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If + * there already is this amount (or more) of free space, SPIFFS_gc will + * silently return. It is recommended to call SPIFFS_info before invoking + * this method in order to determine what amount of bytes to give. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * @param fs the file system struct + * @param size amount of bytes that should be freed + */ +s32_t SPIFFS_gc(spiffs *fs, u32_t size); + +/** + * Check if EOF reached. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); + +/** + * Get position in file. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); + +/** + * Registers a callback function that keeps track on operations on file + * headers. Do note, that this callback is called from within internal spiffs + * mechanisms. Any operations on the actual file system being callbacked from + * in this callback will mess things up for sure - do not do this. + * This can be used to track where files are and move around during garbage + * collection, which in turn can be used to build location tables in ram. + * Used in conjuction with SPIFFS_open_by_page this may improve performance + * when opening a lot of files. + * Must be invoked after mount. + * + * @param fs the file system struct + * @param cb_func the callback on file operations + */ +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); + +#if SPIFFS_IX_MAP + +/** + * Maps the first level index lookup to a given memory map. + * This will make reading big files faster, as the memory map will be used for + * looking up data pages instead of searching for the indices on the physical + * medium. When mapping, all affected indicies are found and the information is + * copied to the array. + * Whole file or only parts of it may be mapped. The index map will cover file + * contents from argument offset until and including arguments (offset+len). + * It is valid to map a longer range than the current file size. The map will + * then be populated when the file grows. + * On garbage collections and file data page movements, the map array will be + * automatically updated. Do not tamper with the map array, as this contains + * the references to the data pages. Modifying it from outside will corrupt any + * future readings using this file descriptor. + * The map will no longer be used when the file descriptor closed or the file + * is unmapped. + * This can be useful to get faster and more deterministic timing when reading + * large files, or when seeking and reading a lot within a file. + * @param fs the file system struct + * @param fh the file handle of the file to map + * @param map a spiffs_ix_map struct, describing the index map + * @param offset absolute file offset where to start the index map + * @param len length of the mapping in actual file bytes + * @param map_buf the array buffer for the look up data - number of required + * elements in the array can be derived from function + * SPIFFS_bytes_to_ix_map_entries given the length + */ +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf); + +/** + * Unmaps the index lookup from this filehandle. All future readings will + * proceed as normal, requiring reading of the first level indices from + * physical media. + * The map and map buffer given in function SPIFFS_ix_map will no longer be + * referenced by spiffs. + * It is not strictly necessary to unmap a file before closing it, as closing + * a file will automatically unmap it. + * @param fs the file system struct + * @param fh the file handle of the file to unmap + */ +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh); + +/** + * Moves the offset for the index map given in function SPIFFS_ix_map. Parts or + * all of the map buffer will repopulated. + * @param fs the file system struct + * @param fh the mapped file handle of the file to remap + * @param offset new absolute file offset where to start the index map + */ +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs); + +/** + * Utility function to get number of spiffs_page_ix entries a map buffer must + * contain on order to map given amount of file data in bytes. + * See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. + * @param fs the file system struct + * @param bytes number of file data bytes to map + * @return needed number of elements in a spiffs_page_ix array needed to + * map given amount of bytes in a file + */ +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes); + +/** + * Utility function to amount of file data bytes that can be mapped when + * mapping a file with buffer having given number of spiffs_page_ix entries. + * See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. + * @param fs the file system struct + * @param map_page_ix_entries number of entries in a spiffs_page_ix array + * @return amount of file data in bytes that can be mapped given a map + * buffer having given amount of spiffs_page_ix entries + */ +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); + +#endif // SPIFFS_IX_MAP + + +#if SPIFFS_TEST_VISUALISATION +/** + * Prints out a visualization of the filesystem. + * @param fs the file system struct + */ +s32_t SPIFFS_vis(spiffs *fs); +#endif + +#if SPIFFS_BUFFER_HELP +/** + * Returns number of bytes needed for the filedescriptor buffer given + * amount of file descriptors. + */ +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs); + +#if SPIFFS_CACHE +/** + * Returns number of bytes needed for the cache buffer given + * amount of cache pages. + */ +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); +#endif +#endif + +#if SPIFFS_CACHE +#endif +#if defined(__cplusplus) +} +#endif + +#endif /* SPIFFS_H_ */ diff --git a/components/mkspiffs/src/spiffs/spiffs_cache.c b/components/mkspiffs/src/spiffs/spiffs_cache.c new file mode 100644 index 0000000..018f763 --- /dev/null +++ b/components/mkspiffs/src/spiffs/spiffs_cache.c @@ -0,0 +1,314 @@ +/* + * spiffs_cache.c + * + * Created on: Jun 23, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if SPIFFS_CACHE + +// returns cached page for give page index, or null if no such cached page +static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache *cache = spiffs_get_cache(fs); + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + cp->pix == pix ) { + SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix); + cp->last_access = cache->last_access; + return cp; + } + } + //SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix); + return 0; +} + +// frees cached page +static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix); + if (cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { + u8_t *mem = spiffs_get_cache_page(fs, cache, ix); + res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + } + + cp->flags = 0; + cache->cpage_use_map &= ~(1 << ix); + + if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id); + } else { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + } + } + + return res; +} + +// removes the oldest accessed cached page +static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) { + // at least one free cpage + return SPIFFS_OK; + } + + // all busy, scan thru all to find the cpage which has oldest access + int i; + int cand_ix = -1; + u32_t oldest_val = 0; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->last_access - cp->last_access) > oldest_val && + (cp->flags & flag_mask) == flags) { + oldest_val = cache->last_access - cp->last_access; + cand_ix = i; + } + } + + if (cand_ix >= 0) { + res = spiffs_cache_page_free(fs, cand_ix, 1); + } + + return res; +} + +// allocates a new cached page and returns it, or null if all cache pages are busy +static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) { + spiffs_cache *cache = spiffs_get_cache(fs); + if (cache->cpage_use_map == 0xffffffff) { + // out of cache memory + return 0; + } + int i; + for (i = 0; i < cache->cpage_count; i++) { + if ((cache->cpage_use_map & (1<cpage_use_map |= (1<last_access = cache->last_access; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i); + return cp; + } + } + // out of cache entries + return 0; +} + +// drops the cache page for give page index +void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + if (cp) { + spiffs_cache_page_free(fs, cp->ix, 0); + } +} + +// ------------------------------ + +// reads from spi flash or the cache +s32_t spiffs_phys_rd( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *dst) { + (void)fh; + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); + cache->last_access++; + if (cp) { + // we've already got one, you see +#if SPIFFS_CACHE_STATS + fs->cache_hits++; +#endif + cp->last_access = cache->last_access; + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } else { + if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { + // for second layer lookup functions, we do not cache in order to prevent shredding + return SPIFFS_HAL_READ(fs, addr, len, dst); + } +#if SPIFFS_CACHE_STATS + fs->cache_misses++; +#endif + // this operation will always free one cache page (unless all already free), + // the result code stems from the write operation of the possibly freed cache page + res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + + cp = spiffs_cache_page_allocate(fs); + if (cp) { + cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; + cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + + s32_t res2 = SPIFFS_HAL_READ(fs, + addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + spiffs_get_cache_page(fs, cache, cp->ix)); + if (res2 != SPIFFS_OK) { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } else { + // this will never happen, last resort for sake of symmetry + s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst); + if (res2 != SPIFFS_OK) { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + } + } + return res; +} + +// writes to spi flash and/or the cache +s32_t spiffs_phys_wr( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *src) { + (void)fh; + spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + + if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) { + // have a cache page + // copy in data to cache page + + if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE && + (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { + // page is being deleted, wipe from cache - unless it is a lookup page + spiffs_cache_page_free(fs, cp->ix, 0); + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } + + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + + cache->last_access++; + cp->last_access = cache->last_access; + + if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) { + // page is being updated, no write-cache, just pass thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } else { + return SPIFFS_OK; + } + } else { + // no cache page, no write cache - just write thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } +} + +#if SPIFFS_CACHE_WR +// returns the cache page that this fd refers, or null if no cache page +spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) { + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) { + // all cpages free, no cpage cannot be assigned to obj_id + return 0; + } + + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) && + cp->obj_id == fd->obj_id) { + return cp; + } + } + + return 0; +} + +// allocates a new cache page and refers this to given fd - flushes an old cache +// page if all cache is busy +spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) { + // before this function is called, it is ensured that there is no already existing + // cache page with same object id + spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + spiffs_cache_page *cp = spiffs_cache_page_allocate(fs); + if (cp == 0) { + // could not get cache page + return 0; + } + + cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; + cp->obj_id = fd->obj_id; + fd->cache_page = cp; + return cp; +} + +// unrefers all fds that this cache page refers to and releases the cache page +void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) { + if (cp == 0) return; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) { + cur_fd->cache_page = 0; + } + } + spiffs_cache_page_free(fs, cp->ix, 0); + + cp->obj_id = 0; +} + +#endif + +// initializes the cache +void spiffs_cache_init(spiffs *fs) { + if (fs->cache == 0) return; + u32_t sz = fs->cache_size; + u32_t cache_mask = 0; + int i; + int cache_entries = + (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs)); + if (cache_entries <= 0) return; + + for (i = 0; i < cache_entries; i++) { + cache_mask <<= 1; + cache_mask |= 1; + } + + spiffs_cache cache; + memset(&cache, 0, sizeof(spiffs_cache)); + cache.cpage_count = cache_entries; + cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache)); + + cache.cpage_use_map = 0xffffffff; + cache.cpage_use_mask = cache_mask; + memcpy(fs->cache, &cache, sizeof(spiffs_cache)); + + spiffs_cache *c = spiffs_get_cache(fs); + + memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); + + c->cpage_use_map &= ~(c->cpage_use_mask); + for (i = 0; i < cache.cpage_count; i++) { + spiffs_get_cache_page_hdr(fs, c, i)->ix = i; + } +} + +#endif // SPIFFS_CACHE diff --git a/components/mkspiffs/src/spiffs/spiffs_check.c b/components/mkspiffs/src/spiffs/spiffs_check.c new file mode 100644 index 0000000..dde85ef --- /dev/null +++ b/components/mkspiffs/src/spiffs/spiffs_check.c @@ -0,0 +1,995 @@ +/* + * spiffs_check.c + * + * Contains functionality for checking file system consistency + * and mending problems. + * Three levels of consistency checks are implemented: + * + * Look up consistency + * Checks if indices in lookup pages are coherent with page headers + * Object index consistency + * Checks if there are any orphaned object indices (missing object index headers). + * If an object index is found but not its header, the object index is deleted. + * This is critical for the following page consistency check. + * Page consistency + * Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed + * + * + * Created on: Jul 7, 2013 + * Author: petera + */ + + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if !SPIFFS_READ_ONLY + +#if SPIFFS_HAL_CALLBACK_EXTRA +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#else +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#endif + +//--------------------------------------- +// Look up consistency + +// searches in the object indices and returns the referenced page index given +// the object id and the data span index +// destroys fs->lu_work +static s32_t spiffs_object_get_data_page_index_reference( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix data_spix, + spiffs_page_ix *pix, + spiffs_page_ix *objix_pix) { + s32_t res; + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // find obj index for obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix); + SPIFFS_CHECK_RES(res); + + // load obj index entry + u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); + if (objix_spix == 0) { + // get referenced page from object index header + addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); + } else { + // get referenced page from object index + addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); + } + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); + + return res; +} + +// copies page contents to a new page +static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) { + s32_t res; + res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + return res; +} + +// rewrites the object index for given object id and replaces the +// data page index to a new page index +static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (objix_spix == 0) { + // calc index in index header + entry = data_spix; + } else { + // calc entry in index + entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); + + } + // load index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + // be ultra safe, double check header against provided data + if (objix_p_hdr->obj_id != obj_id) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_OBJ_ID_MISM; + } + if (objix_p_hdr->span_ix != objix_spix) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_SPIX_MISM; + } + if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX | + SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) != + (SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_FLAGS_BAD; + } + + // rewrite in mem + if (objix_spix == 0) { + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + } else { + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + res = spiffs_page_delete(fs, objix_pix); + + return res; +} + +// deletes an object just by marking object index header as deleted +static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) { + spiffs_page_ix objix_hdr_pix; + s32_t res; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + return SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + return res; +} + +// validates the given look up entry +static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr, + spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) { + (void)cur_block; + (void)cur_entry; + u8_t delete_page = 0; + s32_t res = SPIFFS_OK; + spiffs_page_ix objix_pix; + spiffs_page_ix ref_pix; + // check validity, take actions + if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || + ((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) { + // look up entry deleted / free but used in page header + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix); + *reload_lu = 1; + delete_page = 1; + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { + // header says data page + // data page can be removed if not referenced by some object index + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } else { + SPIFFS_CHECK_RES(res); + if (ref_pix == cur_pix) { + // data page referenced by object index but deleted in lu + // copy page to new place and re-write the object index to new place + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } else { + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + } + } + } else { + // header says index page + // index page can be removed if other index with same obj_id and spanix is found + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no such index page found, check for a data page amongst page headers + // lu cannot be trusted + res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0); + if (res == SPIFFS_OK) { // ignore other errors + // got a data page also, assume lu corruption only, rewrite to new page + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + } + } else { + SPIFFS_CHECK_RES(res); + } + } + } + if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) { + // look up entry used + if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id); + delete_page = 1; + if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || + (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || + (p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) { + // page deleted or not finalized, just remove it + } else { + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { + // if data page, check for reference to this page + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } else { + SPIFFS_CHECK_RES(res); + // if found, rewrite page with object id, update index, and delete current + if (ref_pix == cur_pix) { + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + *reload_lu = 1; + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } + SPIFFS_CHECK_RES(res); + } + } + } else { + // else if index, check for other pages with both obj_id's and spanix + spiffs_page_ix objix_pix_lu, objix_pix_ph; + // see if other object index page exists for lookup obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + // if both obj_id's found, just delete current + if (objix_pix_ph == 0 || objix_pix_lu == 0) { + // otherwise try finding first corresponding data pages + spiffs_page_ix data_pix_lu, data_pix_ph; + // see if other data page exists for look up obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other data page exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL); + new_ph.span_ix = p_hdr->span_ix; + spiffs_page_ix new_pix; + if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) || + (objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) { + // got a data page for page header obj id + // rewrite as obj_id_ph + new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || + (objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) { + // got a data page for look up obj id + // rewrite as obj_id_lu + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } else { + // cannot safely do anything + SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n"); + } + } + } + } + } else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || + ((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) { + SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix); + spiffs_page_ix data_pix, objix_pix_d; + // see if other data page exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_d = 0; + } + SPIFFS_CHECK_RES(res); + + delete_page = 1; + // if other data page exists and object index exists, just delete page + if (data_pix && objix_pix_d) { + SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n"); + } else + // if only data page exists, make this page index + if (data_pix && objix_pix_d == 0) { + SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } else + // if only index exists, make data page + if (data_pix == 0 && objix_pix_d) { + SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } else { + // if nothing exists, we cannot safely make a decision - delete + } + } + else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix); + delete_page = 1; + } else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix); + // page can be removed if not referenced by object index + *reload_lu = 1; + res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + delete_page = 1; + } else { + SPIFFS_CHECK_RES(res); + if (ref_pix != cur_pix) { + SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n"); + delete_page = 1; + } else { + // page referenced by object index but not final + // just finalize + SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), (u8_t*)&flags); + } + } + } + } + + if (delete_page) { + SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + + return res; +} + +static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, + const void *user_const_p, void *user_var_p) { + (void)user_const_p; + (void)user_var_p; + s32_t res = SPIFFS_OK; + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, + (cur_block * 256)/fs->block_count, 0); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + int reload_lu = 0; + + res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu); + SPIFFS_CHECK_RES(res); + + if (res == SPIFFS_OK) { + return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; + } + return res; +} + + +// Scans all object look up. For each entry, corresponding page header is checked for validity. +// If an object index header page is found, this is also checked +s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { + (void)check_all_objects; + s32_t res = SPIFFS_OK; + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); + + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); + } + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); + + return res; +} + +//--------------------------------------- +// Page consistency + +// Scans all pages (except lu pages), reserves 4 bits in working memory for each page +// bit 0: 0 == FREE|DELETED, 1 == USED +// bit 1: 0 == UNREFERENCED, 1 == REFERENCED +// bit 2: 0 == NOT_INDEX, 1 == INDEX +// bit 3: unused +// A consistent file system will have only pages being +// * x000 free, unreferenced, not index +// * x011 used, referenced only once, not index +// * x101 used, unreferenced, index +// The working memory might not fit all pages so several scans might be needed +static s32_t spiffs_page_consistency_check_i(spiffs *fs) { + const u32_t bits = 4; + const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits; + + s32_t res = SPIFFS_OK; + spiffs_page_ix pix_offset = 0; + + // for each range of pages fitting into work memory + while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { + // set this flag to abort all checks and rescan the page range + u8_t restart = 0; + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + spiffs_block_ix cur_block = 0; + // build consistency bitmap for id range traversing all blocks + while (!restart && cur_block < fs->block_count) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, + (pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + + ((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), + 0); + // traverse each page except for lookup pages + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; + while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { + //if ((cur_pix & 0xff) == 0) + // SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n", + // cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); + + // read header + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan); + const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits); + const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits; + + if (within_range && + (p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) { + // used + fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0)); + } + if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) && + (p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) { + // found non-deleted index + if (within_range) { + fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2)); + } + + // load non-deleted index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + + // traverse index for referenced pages + spiffs_page_ix *object_page_index; + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + int entries; + int i; + spiffs_span_ix data_spix_offset; + if (p_hdr.span_ix == 0) { + // object header page index + entries = SPIFFS_OBJ_HDR_IX_LEN(fs); + data_spix_offset = 0; + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); + } else { + // object page index + entries = SPIFFS_OBJ_IX_LEN(fs); + data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1); + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)); + } + + // for all entries in index + for (i = 0; !restart && i < entries; i++) { + spiffs_page_ix rpix = object_page_index[i]; + u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan; + + if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs)) + || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) { + + // bad reference + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n", + rpix, cur_pix); + // check for data page elsewhere + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, 0, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) { + // if not, allocate free page + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = data_spix_offset + i; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); + SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix); + } + // remap index + SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix); + res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); + // delete file + res = spiffs_page_delete(fs, cur_pix); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + + } else if (rpix_within_range) { + + // valid reference + // read referenced page header + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + SPIFFS_CHECK_RES(res); + + // cross reference page header check + if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) || + rp_hdr.span_ix != data_spix_offset + i || + (rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) { + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n", + rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, + rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); + // try finding correct page + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, rpix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) { + // not found, this index is badly borked + SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + break; + } else { + // found it, so rewrite index + SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n", + data_pix, cur_pix, p_hdr.obj_id); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + } + } + else { + // mark rpix as referenced + const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits); + const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits; + if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) { + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n", + rpix, cur_pix); + // Here, we should have fixed all broken references - getting this means there + // must be multiple files with same object id. Only solution is to delete + // the object which is referring to this page + SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\n", + p_hdr.obj_id, cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + // extra precaution, delete this page also + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + restart = 1; + } + fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1)); + } + } + } // for all index entries + } // found index + + // next page + cur_pix++; + } + // next block + cur_block++; + } + // check consistency bitmap + if (!restart) { + spiffs_page_ix objix_pix; + spiffs_page_ix rpix; + + u32_t byte_ix; + u8_t bit_ix; + for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) { + for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) { + u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7; + spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix; + + // 000 ok - free, unreferenced, not index + + if (bitmask == 0x1) { + + // 001 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix); + + u8_t rewrite_ix_to_this = 0; + u8_t delete_page = 0; + // check corresponding object index entry + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix, + &rpix, &objix_pix); + if (res == SPIFFS_OK) { + if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) { + // pointing to a bad page altogether, rewrite index to this + rewrite_ix_to_this = 1; + SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix); + } else { + // pointing to something else, check what + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + SPIFFS_CHECK_RES(res); + if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) && + ((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == + (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) { + // pointing to something else valid, just delete this page then + SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix); + delete_page = 1; + } else { + // pointing to something weird, update index to point to this page instead + if (rpix != cur_pix) { + SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix, + (rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", + (rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", + (rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", + (rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "", + cur_pix); + rewrite_ix_to_this = 1; + } else { + // should not happen, destined for fubar + } + } + } + } else if (res == SPIFFS_ERR_NOT_FOUND) { + SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix); + delete_page = 1; + res = SPIFFS_OK; + } + + if (rewrite_ix_to_this) { + // if pointing to invalid page, redirect index to this page + SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n", + p_hdr.obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + continue; + } else if (delete_page) { + SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + } + SPIFFS_CHECK_RES(res); + } + if (bitmask == 0x2) { + + // 010 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + + // 011 ok - busy, referenced, not index + + if (bitmask == 0x4) { + + // 100 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix); + + // this should never happen, major fubar + } + + // 101 ok - busy, unreferenced, index + + if (bitmask == 0x6) { + + // 110 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + if (bitmask == 0x7) { + + // 111 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + } + } + } + + SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\n", pix_offset, restart); + // next page range + if (!restart) { + pix_offset += pages_per_scan; + } + } // while page range not reached end + return res; +} + +// Checks consistency amongst all pages and fixes irregularities +s32_t spiffs_page_consistency_check(spiffs *fs) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); + s32_t res = spiffs_page_consistency_check_i(fs); + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); + } + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; +} + +//--------------------------------------- +// Object index consistency + +// searches for given object id in temporary object id index, +// returns the index or -1 +static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) { + u32_t i; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) { + if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) { + return i; + } + } + return -1; +} + +static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, + int cur_entry, const void *user_const_p, void *user_var_p) { + (void)user_const_p; + s32_t res_c = SPIFFS_VIS_COUNTINUE; + s32_t res = SPIFFS_OK; + u32_t *log_ix = (u32_t*)user_var_p; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, + (cur_block * 256)/fs->block_count, 0); + + if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + if (p_hdr.span_ix == 0 && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET)) { + SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + return res_c; + } + + if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + return res_c; + } + + if (p_hdr.span_ix == 0) { + // objix header page, register objid as reachable + int r = spiffs_object_index_search(fs, obj_id); + if (r == -1) { + // not registered, do it + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { + *log_ix = 0; + } + } + } else { // span index + // objix page, see if header can be found + int r = spiffs_object_index_search(fs, obj_id); + u8_t delete = 0; + if (r == -1) { + // not in temporary index, try finding it + spiffs_page_ix objix_hdr_pix; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix); + res_c = SPIFFS_VIS_COUNTINUE_RELOAD; + if (res == SPIFFS_OK) { + // found, register as reachable + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + } else if (res == SPIFFS_ERR_NOT_FOUND) { + // not found, register as unreachable + delete = 1; + obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG; + } else { + SPIFFS_CHECK_RES(res); + } + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { + *log_ix = 0; + } + } else { + // in temporary index, check reachable flag + if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) { + // registered as unreachable + delete = 1; + } + } + + if (delete) { + SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" is orphan index - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + } // span index + } // valid object index id + + return res_c; +} + +// Removes orphaned and partially deleted index pages. +// Scans for index pages. When an index page is found, corresponding index header is searched for. +// If no such page exists, the index page cannot be reached as no index header exists and must be +// deleted. +s32_t spiffs_object_index_consistency_check(spiffs *fs) { + s32_t res = SPIFFS_OK; + // impl note: + // fs->work is used for a temporary object index memory, listing found object ids and + // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. + // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate + // a reachable/unreachable object id. + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + u32_t obj_id_log_ix = 0; + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, + 0, 0); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); + } + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; +} + +#endif // !SPIFFS_READ_ONLY diff --git a/components/mkspiffs/src/spiffs/spiffs_config.h b/components/mkspiffs/src/spiffs/spiffs_config.h new file mode 100644 index 0000000..af1f5ec --- /dev/null +++ b/components/mkspiffs/src/spiffs/spiffs_config.h @@ -0,0 +1,361 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +// ----------- 8< ------------ +// Following includes are for the linux test build of spiffs +// These may/should/must be removed/altered/replaced in your target +#include +#include +#include +#include +#include +#include +#include +// ----------- >8 ------------ + +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) +#endif + + +// Defines spiffs debug print formatters +// some general signed number +#ifndef _SPIPRIi +#define _SPIPRIi "%d" +#endif +// address +#ifndef _SPIPRIad +#define _SPIPRIad "%08x" +#endif +// block +#ifndef _SPIPRIbl +#define _SPIPRIbl "%04x" +#endif +// page +#ifndef _SPIPRIpg +#define _SPIPRIpg "%04x" +#endif +// span index +#ifndef _SPIPRIsp +#define _SPIPRIsp "%04x" +#endif +// file descriptor +#ifndef _SPIPRIfd +#define _SPIPRIfd "%d" +#endif +// file object id +#ifndef _SPIPRIid +#define _SPIPRIid "%04x" +#endif +// file flags +#ifndef _SPIPRIfl +#define _SPIPRIfl "%02x" +#endif + +// Enable/disable API functions to determine exact number of bytes +// for filedescriptor and cache buffers. Once decided for a configuration, +// this can be disabled to reduce flash. +#ifndef SPIFFS_BUFFER_HELP +#define SPIFFS_BUFFER_HELP 0 +#endif + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifndef SPIFFS_CACHE +#define SPIFFS_CACHE 1 +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifndef SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR 1 +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifndef SPIFFS_CACHE_STATS +#define SPIFFS_CACHE_STATS 0 +#endif +#endif + +// Always check header of each accessed page to ensure consistent state. +// If enabled it will increase number of reads, will increase flash. +#ifndef SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK 1 +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#ifndef SPIFFS_GC_MAX_RUNS +#define SPIFFS_GC_MAX_RUNS 5 +#endif + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifndef SPIFFS_GC_STATS +#define SPIFFS_GC_STATS 0 +#endif + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// To have a fair wear-leveling, the erase age is also included in score, +// whose factor normally is the most positive. +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// Garbage collecting heuristics - weight used for deleted pages. +#ifndef SPIFFS_GC_HEUR_W_DELET +#define SPIFFS_GC_HEUR_W_DELET (5) +#endif +// Garbage collecting heuristics - weight used for used pages. +#ifndef SPIFFS_GC_HEUR_W_USED +#define SPIFFS_GC_HEUR_W_USED (-1) +#endif +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) +#endif + +// Object name maximum length. Note that this length include the +// zero-termination character, meaning maximum string of characters +// can at most be SPIFFS_OBJ_NAME_LEN - 1. +#ifndef SPIFFS_OBJ_NAME_LEN +#define SPIFFS_OBJ_NAME_LEN (64) +#endif + +// Maximum length of the metadata associated with an object. +// Setting to non-zero value enables metadata-related API but also +// changes the on-disk format, so the change is not backward-compatible. +// +// Do note: the meta length must never exceed +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64) +// +// This is derived from following: +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + +// spiffs_object_ix_header fields + at least some LUT entries) +#ifndef SPIFFS_OBJ_META_LEN +#define SPIFFS_OBJ_META_LEN (64) +#endif + +// Size of buffer allocated on stack used when copying data. +// Lower value generates more read/writes. No meaning having it bigger +// than logical page size. +#ifndef SPIFFS_COPY_BUFFER_STACK +#define SPIFFS_COPY_BUFFER_STACK (256) +#endif + +// Enable this to have an identifiable spiffs filesystem. This will look for +// a magic in all sectors to determine if this is a valid spiffs system or +// not on mount point. If not, SPIFFS_format must be called prior to mounting +// again. +#ifndef SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC (1) +#endif + +#if SPIFFS_USE_MAGIC +// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is +// enabled, the magic will also be dependent on the length of the filesystem. +// For example, a filesystem configured and formatted for 4 megabytes will not +// be accepted for mounting with a configuration defining the filesystem as 2 +// megabytes. +#ifndef SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH (1) +#endif +#endif + +// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level +// These should be defined on a multithreaded system + +// define this to enter a mutex if you're running on a multithreaded system +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) +#endif +// define this to exit a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#endif + +// Enable if only one spiffs instance with constant configuration will exist +// on the target. This will reduce calculations, flash and memory accesses. +// Parts of configuration must be defined below instead of at time of mount. +#ifndef SPIFFS_SINGLETON +#define SPIFFS_SINGLETON 0 +#endif + +#if SPIFFS_SINGLETON +// Instead of giving parameters in config struct, singleton build must +// give parameters in defines below. +#ifndef SPIFFS_CFG_PHYS_SZ +#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536) +#endif +#ifndef SPIFFS_CFG_PHYS_ADDR +#define SPIFFS_CFG_PHYS_ADDR(ignore) (0) +#endif +#ifndef SPIFFS_CFG_LOG_PAGE_SZ +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#endif +#ifndef SPIFFS_CFG_LOG_BLOCK_SZ +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536) +#endif +#endif + +// Enable this if your target needs aligned data for index tables +#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 +#endif + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 0 +#endif + +// Enable this if you want to add an integer offset to all file handles +// (spiffs_file). This is useful if running multiple instances of spiffs on +// same target, in order to recognise to what spiffs instance a file handle +// belongs. +// NB: This adds config field fh_ix_offset in the configuration struct when +// mounting, which must be defined. +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 0 +#endif + +// Enable this to compile a read only version of spiffs. +// This will reduce binary size of spiffs. All code comprising modification +// of the file system will not be compiled. Some config will be ignored. +// HAL functions for erasing and writing to spi-flash may be null. Cache +// can be disabled for even further binary size reduction (and ram savings). +// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. +// If the file system cannot be mounted due to aborted erase operation and +// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be +// returned. +// Might be useful for e.g. bootloaders and such. +#ifndef SPIFFS_READ_ONLY +#define SPIFFS_READ_ONLY 0 +#endif + +// Enable this to add a temporal file cache using the fd buffer. +// The effects of the cache is that SPIFFS_open will find the file faster in +// certain cases. It will make it a lot easier for spiffs to find files +// opened frequently, reducing number of readings from the spi flash for +// finding those files. +// This will grow each fd by 6 bytes. If your files are opened in patterns +// with a degree of temporal locality, the system is optimized. +// Examples can be letting spiffs serve web content, where one file is the css. +// The css is accessed for each html file that is opened, meaning it is +// accessed almost every second time a file is opened. Another example could be +// a log file that is often opened, written, and closed. +// The size of the cache is number of given file descriptors, as it piggybacks +// on the fd update mechanism. The cache lives in the closed file descriptors. +// When closed, the fd know the whereabouts of the file. Instead of forgetting +// this, the temporal cache will keep handling updates to that file even if the +// fd is closed. If the file is opened again, the location of the file is found +// directly. If all available descriptors become opened, all cache memory is +// lost. +#ifndef SPIFFS_TEMPORAL_FD_CACHE +#define SPIFFS_TEMPORAL_FD_CACHE 1 +#endif + +// Temporal file cache hit score. Each time a file is opened, all cached files +// will lose one point. If the opened file is found in cache, that entry will +// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this +// value for the specific access patterns of the application. However, it must +// be between 1 (no gain for hitting a cached entry often) and 255. +#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE +#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 8 +#endif + +// Enable to be able to map object indices to memory. +// This allows for faster and more deterministic reading if cases of reading +// large files and when changing file offset by seeking around a lot. +// When mapping a file's index, the file system will be scanned for index pages +// and the info will be put in memory provided by user. When reading, the +// memory map can be looked up instead of searching for index pages on the +// medium. This way, user can trade memory against performance. +// Whole, parts of, or future parts not being written yet can be mapped. The +// memory array will be owned by spiffs and updated accordingly during garbage +// collecting or when modifying the indices. The latter is invoked by when the +// file is modified in some way. The index buffer is tied to the file +// descriptor. +#ifndef SPIFFS_IX_MAP +#define SPIFFS_IX_MAP 1 +#endif + +// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function +// in the api. This function will visualize all filesystem using given printf +// function. +#ifndef SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 0 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) printf(__VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#ifndef SPIFFS_TEST_VIS_FREE_STR +#define SPIFFS_TEST_VIS_FREE_STR "_" +#endif +// spiffs_printf argument for a deleted page +#ifndef SPIFFS_TEST_VIS_DELE_STR +#define SPIFFS_TEST_VIS_DELE_STR "/" +#endif +// spiffs_printf argument for an index page for given object id +#ifndef SPIFFS_TEST_VIS_INDX_STR +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +#endif +// spiffs_printf argument for a data page for given object id +#ifndef SPIFFS_TEST_VIS_DATA_STR +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif +#endif + +// Types depending on configuration such as the amount of flash bytes +// given to spiffs file system in total (spiffs_file_system_size), +// the logical block size (log_block_size), and the logical page size +// (log_page_size) + +// Block index type. Make sure the size of this type can hold +// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size +typedef u16_t spiffs_block_ix; +// Page index type. Make sure the size of this type can hold +// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size +typedef u16_t spiffs_page_ix; +// Object id type - most significant bit is reserved for index flag. Make sure the +// size of this type can hold the highest object id on a full system, +// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 +typedef u16_t spiffs_obj_id; +// Object span index type. Make sure the size of this type can +// hold the largest possible span index on the system - +// i.e. (spiffs_file_system_size / log_page_size) - 1 +typedef u16_t spiffs_span_ix; + +#endif /* SPIFFS_CONFIG_H_ */ diff --git a/components/mkspiffs/src/spiffs/spiffs_gc.c b/components/mkspiffs/src/spiffs/spiffs_gc.c new file mode 100644 index 0000000..db1af4c --- /dev/null +++ b/components/mkspiffs/src/spiffs/spiffs_gc.c @@ -0,0 +1,606 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if !SPIFFS_READ_ONLY + +// Erases a logical block and updates the erase counter. +// If cache is enabled, all pages that might be cached in this block +// is dropped. +static s32_t spiffs_gc_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + + SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix); + res = spiffs_erase_block(fs, bix); + SPIFFS_CHECK_RES(res); + +#if SPIFFS_CACHE + { + u32_t i; + for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { + spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); + } + } +#endif + return res; +} + +// Searches for blocks where all entries are deleted - if one is found, +// the block is erased. Compared to the non-quick gc, the quick one ensures +// that no updates are needed on existing objects on pages that are erased. +s32_t spiffs_gc_quick( + spiffs *fs, u16_t max_free_pages) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + + SPIFFS_GC_DBG("gc_quick: running\n"); +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // find fully deleted blocks + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + u16_t free_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && + cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_DELETED) { + deleted_pages_in_block++; + } else if (obj_id == SPIFFS_OBJ_ID_FREE) { + // kill scan, go for next block + free_pages_in_block++; + if (free_pages_in_block > max_free_pages) { + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } + } else { + // kill scan, go for next block + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + if (res == 1) res = SPIFFS_OK; + + if (res == SPIFFS_OK && + deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) && + free_pages_in_block <= max_free_pages) { + // found a fully deleted block + fs->stats_p_deleted -= deleted_pages_in_block; + res = spiffs_gc_erase_block(fs, cur_block); + return res; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + if (res == SPIFFS_OK) { + res = SPIFFS_ERR_NO_DELETED_BLOCKS; + } + return res; +} + +// Checks if garbage collecting is necessary. If so a candidate block is found, +// cleansed and erased +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len) { + s32_t res; + s32_t free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2) + - fs->stats_p_allocated - fs->stats_p_deleted; + int tries = 0; + + if (fs->free_blocks > 3 && + (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { + return SPIFFS_OK; + } + + u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); +// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { +// SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); +// return SPIFFS_ERR_FULL; +// } + if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { + SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); + return SPIFFS_ERR_FULL; + } + + do { + SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n", + tries, + fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), + len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); + + spiffs_block_ix *cands; + int count; + spiffs_block_ix cand; + s32_t prev_free_pages = free_pages; + // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state + res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); + SPIFFS_CHECK_RES(res); + if (count == 0) { + SPIFFS_GC_DBG("gc_check: no candidates, return\n"); + return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; + } +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + cand = cands[0]; + fs->cleaning = 1; + //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand); + res = spiffs_gc_clean(fs, cand); + fs->cleaning = 0; + if (res < 0) { + SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); + } else { + SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); + } + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_page_stats(fs, cand); + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_block(fs, cand); + SPIFFS_CHECK_RES(res); + + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + + if (prev_free_pages <= 0 && prev_free_pages == free_pages) { + // abort early to reduce wear, at least tried once + SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n"); + break; + } + + } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || + (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); + + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { + res = SPIFFS_ERR_FULL; + } + + SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n", + fs->stats_p_allocated + fs->stats_p_deleted, + fs->free_blocks, free_pages, tries, res); + + return res; +} + +// Updates page statistics for a block that is about to be erased +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + int obj_lookup_page = 0; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + u32_t dele = 0; + u32_t allo = 0; + + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + dele++; + } else { + allo++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele); + fs->stats_p_allocated -= allo; + fs->stats_p_deleted -= dele; + return res; +} + +// Finds block candidates to erase +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidates, + int *candidate_count, + char fs_crammed) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + + // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score + int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t))); + *candidate_count = 0; + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + // divide up work area into block indices and scores + spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; + s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); + + // align cand_scores on s32_t boundary + cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); + + *block_candidates = cand_blocks; + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + u16_t used_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && + cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + // when a free entry is encountered, scan logic ensures that all following entries are free also + res = 1; // kill object lu loop + break; + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + deleted_pages_in_block++; + } else { + used_pages_in_block++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + if (res == 1) res = SPIFFS_OK; + + // calculate score and insert into candidate table + // stoneage sort, but probably not so many blocks + if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) { + // read erase count + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + spiffs_obj_id erase_age; + if (fs->max_erase_count > erase_count) { + erase_age = fs->max_erase_count - erase_count; + } else { + erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); + } + + s32_t score = + deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + + used_pages_in_block * SPIFFS_GC_HEUR_W_USED + + erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); + int cand_ix = 0; + SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); + while (cand_ix < max_candidates) { + if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } else if (cand_scores[cand_ix] < score) { + int reorder_cand_ix = max_candidates - 2; + while (reorder_cand_ix >= cand_ix) { + cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; + cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; + reorder_cand_ix--; + } + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } + cand_ix++; + } + (*candidate_count)++; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + return res; +} + +typedef enum { + FIND_OBJ_DATA, + MOVE_OBJ_DATA, + MOVE_OBJ_IX, + FINISHED +} spiffs_gc_clean_state; + +typedef struct { + spiffs_gc_clean_state state; + spiffs_obj_id cur_obj_id; + spiffs_span_ix cur_objix_spix; + spiffs_page_ix cur_objix_pix; + spiffs_page_ix cur_data_pix; + int stored_scan_entry_index; + u8_t obj_id_found; +} spiffs_gc; + +// Empties given block by moving all data into free pages of another block +// Strategy: +// loop: +// scan object lookup for object data pages +// for first found id, check spix and load corresponding object index page to memory +// push object scan lookup entry index +// rescan object lookup, find data pages with same id and referenced by same object index +// move data page, update object index in memory +// when reached end of lookup, store updated object index +// pop object scan lookup entry index +// repeat loop until end of object lookup +// scan object lookup again for remaining object index pages, move to new page in other block +// +s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + // this is the global localizer being pushed and popped + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_gc gc; // our stack frame/state + spiffs_page_ix cur_pix = 0; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix); + + memset(&gc, 0, sizeof(spiffs_gc)); + gc.state = FIND_OBJ_DATA; + + if (fs->free_cursor_block_ix == bix) { + // move free cursor to next block, cannot use free pages from the block we want to clean + fs->free_cursor_block_ix = (bix+1)%fs->block_count; + fs->free_cursor_obj_lu_entry = 0; + SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix); + } + + while (res == SPIFFS_OK && gc.state != FINISHED) { + SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry); + gc.obj_id_found = 0; // reset (to no found data page) + + // scan through lookup pages + int obj_lookup_page = cur_entry / entries_per_page; + u8_t scan = 1; + // check each object lookup page + while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each object lookup entry + while (scan && res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); + + // act upon object id depending on gc state + switch (gc.state) { + case FIND_OBJ_DATA: + // find a data page + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { + // found a data page, stop scanning and handle in switch case below + SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id); + gc.obj_id_found = 1; + gc.cur_obj_id = obj_id; + gc.cur_data_pix = cur_pix; + scan = 0; + } + break; + case MOVE_OBJ_DATA: + // evacuate found data pages for corresponding object index we have in memory, + // update memory representation + if (obj_id == gc.cur_obj_id) { + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); + if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { + SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); + } else { + spiffs_page_ix new_data_pix; + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); + SPIFFS_CHECK_RES(res); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + new_data_pix = SPIFFS_OBJ_ID_FREE; + } + // update memory representation of object index page with new data page + if (gc.cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } + } + } + break; + case MOVE_OBJ_IX: + // find and evacuate object index pages + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + // found an index object id + spiffs_page_header p_hdr; + spiffs_page_ix new_pix; + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, + SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + if (res == SPIFFS_OK) { + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); + } + } + SPIFFS_CHECK_RES(res); + } + break; + default: + scan = 0; + break; + } // switch gc state + cur_entry++; + } // per entry + obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop + } // per object lookup page + if (res != SPIFFS_OK) break; + + // state finalization and switch + switch (gc.state) { + case FIND_OBJ_DATA: + if (gc.obj_id_found) { + // handle found data page - + // find out corresponding obj ix page and load it to memory + spiffs_page_header p_hdr; + spiffs_page_ix objix_pix; + gc.stored_scan_entry_index = cur_entry; // push cursor + cur_entry = 0; // restart scan from start + gc.state = MOVE_OBJ_DATA; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); + SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix); + res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // on borked systems we might get an ERR_NOT_FOUND here - + // this is handled by simply deleting the page as it is not referenced + // from anywhere + SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix); + res = spiffs_page_delete(fs, gc.cur_data_pix); + SPIFFS_CHECK_RES(res); + // then we restore states and continue scanning for data pages + cur_entry = gc.stored_scan_entry_index; // pop cursor + gc.state = FIND_OBJ_DATA; + break; // done + } + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + // cannot allow a gc if the presumed index in fact is no index, a + // check must run or lot of data may be lost + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); + gc.cur_objix_pix = objix_pix; + } else { + // no more data pages found, passed thru all block, start evacuating object indices + gc.state = MOVE_OBJ_IX; + cur_entry = 0; // restart entry scan index + } + break; + case MOVE_OBJ_DATA: { + // store modified objix (hdr) page residing in memory now that all + // data pages belonging to this object index and residing in the block + // we want to evacuate + spiffs_page_ix new_objix_pix; + gc.state = FIND_OBJ_DATA; + cur_entry = gc.stored_scan_entry_index; // pop cursor + if (gc.cur_objix_spix == 0) { + // store object index header page + res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0); + SPIFFS_CHECK_RES(res); + } else { + // store object index page + res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + break; + case MOVE_OBJ_IX: + // scanned thru all block, no more object indices found - our work here is done + gc.state = FINISHED; + break; + default: + cur_entry = 0; + break; + } // switch gc.state + SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state); + } // while state != FINISHED + + + return res; +} + +#endif // !SPIFFS_READ_ONLY diff --git a/components/mkspiffs/src/spiffs/spiffs_hydrogen.c b/components/mkspiffs/src/spiffs/spiffs_hydrogen.c new file mode 100644 index 0000000..9ff3e7a --- /dev/null +++ b/components/mkspiffs/src/spiffs/spiffs_hydrogen.c @@ -0,0 +1,1405 @@ +/* + * spiffs_hydrogen.c + * + * Created on: Jun 16, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0) +#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0) +#else +#define SPIFFS_FH_OFFS(fs, fh) (fh) +#define SPIFFS_FH_UNOFFS(fs, fh) (fh) +#endif + +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); +#endif + +#if SPIFFS_BUFFER_HELP +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { + return num_descs * sizeof(spiffs_fd); +} +#if SPIFFS_CACHE +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { + return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); +} +#endif +#endif + +u8_t SPIFFS_mounted(spiffs *fs) { + return SPIFFS_CHECK_MOUNT(fs); +} + +s32_t SPIFFS_format(spiffs *fs) { +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + if (SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_MOUNTED; + return -1; + } + + s32_t res; + SPIFFS_LOCK(fs); + + spiffs_block_ix bix = 0; + while (bix < fs->block_count) { + fs->max_erase_count = 0; + res = spiffs_erase_block(fs, bix); + if (res != SPIFFS_OK) { + res = SPIFFS_ERR_ERASE_FAIL; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + bix++; + } + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_probe_fs(spiffs_config *config) { + s32_t res = spiffs_probe(config); + return res; +} + +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f) { + void *user_data; + SPIFFS_LOCK(fs); + user_data = fs->user_data; + memset(fs, 0, sizeof(spiffs)); + memcpy(&fs->cfg, config, sizeof(spiffs_config)); + fs->user_data = user_data; + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary + u8_t ptr_size = sizeof(void*); + u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1); + if (addr_lsb) { + fd_space += (ptr_size-addr_lsb); + fd_space_size -= (ptr_size-addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); + + // align cache pointer to 4 byte boundary + addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1); + if (addr_lsb) { + u8_t *cache_8 = (u8_t *)cache; + cache_8 += (ptr_size-addr_lsb); + cache = cache_8; + cache_size -= (ptr_size-addr_lsb); + } + if (cache_size & (ptr_size-1)) { + cache_size -= (cache_size & (ptr_size-1)); + } + +#if SPIFFS_CACHE + fs->cache = cache; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size; + spiffs_cache_init(fs); +#endif + + s32_t res; + +#if SPIFFS_USE_MAGIC + res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + fs->config_magic = SPIFFS_CONFIG_MAGIC; + + res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count); + SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks); + + fs->check_cb_f = check_cb_f; + + fs->mounted = 1; + + SPIFFS_UNLOCK(fs); + + return 0; +} + +void SPIFFS_unmount(spiffs *fs) { + if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; + SPIFFS_LOCK(fs); + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0) { +#if SPIFFS_CACHE + (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); +#endif + spiffs_fd_return(fs, cur_fd->file_nbr); + } + } + fs->mounted = 0; + + SPIFFS_UNLOCK(fs); +} + +s32_t SPIFFS_errno(spiffs *fs) { + return fs->err_code; +} + +void SPIFFS_clearerr(spiffs *fs) { + fs->err_code = SPIFFS_OK; +} + +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { +#if SPIFFS_READ_ONLY + (void)fs; (void)path; (void)mode; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; + + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + +#if SPIFFS_READ_ONLY + // not valid flags in read only mode + flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); +#endif // SPIFFS_READ_ONLY + + s32_t res = spiffs_fd_find_new(fs, &fd, path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if ((flags & SPIFFS_O_CREAT) == 0) { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (res == SPIFFS_OK && + (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { +#if !SPIFFS_READ_ONLY + spiffs_obj_id obj_id; + // no need to enter conflicting name here, already looked for it above + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + flags &= ~SPIFFS_O_TRUNC; +#endif // !SPIFFS_READ_ONLY + } else { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { + res = SPIFFS_ERR_NOT_A_FILE; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); + if (res == SPIFFS_ERR_IS_FREE || + res == SPIFFS_ERR_DELETED || + res == SPIFFS_ERR_NOT_FINALIZED || + res == SPIFFS_ERR_NOT_INDEX || + res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { + res = SPIFFS_ERR_NOT_A_FILE; + } + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_RDONLY) == 0) { + res = SPIFFS_ERR_NOT_READABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { + // special case for zero sized files + res = SPIFFS_ERR_END_OF_OBJECT; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + if (fd->fdoffset + len >= fd->size) { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } else { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + len = avail; + } + } else { + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return len; +} + +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + s32_t res = spiffs_hydro_read(fs, fh, buf, len); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + res = 0; + } + return res; +} + + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { + (void)fs; + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN((s32_t)(fd->size - offset), len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + u8_t *buf_8 = (u8_t *)buf; + buf_8 += m_len; + buf = buf_8; + offset += m_len; + } + if (remaining > 0) { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; + +} +#endif // !SPIFFS_READ_ONLY + +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)buf; (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + u32_t offset; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((fd->flags & SPIFFS_O_APPEND)) { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + } + + offset = fd->fdoffset; + +#if SPIFFS_CACHE_WR + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } +#endif + if (fd->flags & SPIFFS_O_APPEND) { + if (fd->size == SPIFFS_UNDEFINED_LEN) { + offset = 0; + } else { + offset = fd->size; + } +#if SPIFFS_CACHE_WR + if (fd->cache_page) { + offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); + } +#endif + } + +#if SPIFFS_CACHE_WR + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->offset || // writing before cache + offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache + offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page + { + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) { + fd->cache_page->offset = offset; + fd->cache_page->size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) { + u32_t offset_in_cpage = offset - fd->cache_page->offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = spiffs_get_cache(fs); + u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); + memcpy(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } else { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // data written below + } + } + } +#endif + + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + s32_t fileSize = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + + switch (whence) { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset+offs; + break; + case SPIFFS_SEEK_END: + offs = fileSize + offs; + break; + } + + if ((offs > fileSize)) { + fd->fdoffset = fileSize; + res = SPIFFS_ERR_END_OF_OBJECT; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; + + SPIFFS_UNLOCK(fs); + + return offs; +} + +s32_t SPIFFS_remove(spiffs *fs, const char *path) { +#if SPIFFS_READ_ONLY + (void)fs; (void)path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t res; + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0,0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_truncate(fd, 0, 1); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_cache_fd_release(fs, fd->cache_page); +#endif + + res = spiffs_object_truncate(fd, 0, 1); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { + (void)fh; + spiffs_page_object_ix_header objix_hdr; + spiffs_obj_id obj_id; + s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES(fs, res); + + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); + res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, + obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_API_CHECK_RES(fs, res); + + s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + s->pix = pix; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + memcpy(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + + return res; +} + +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_page_ix pix; + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_stat_pix(fs, pix, 0, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +// Checks if there are any cached writes for the object id associated with +// given filehandle. If so, these writes are flushed. +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + (void)fs; + (void)fh; + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + if (res < SPIFFS_OK) { + fs->err_code = res; + } + spiffs_cache_fd_release(fs, fd->cache_page); + } + } +#endif + + return res; +} +#endif + +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + (void)fh; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + SPIFFS_LOCK(fs); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs,res); + SPIFFS_UNLOCK(fs); +#endif + + return res; +} + +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + + s32_t res = SPIFFS_OK; + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); +#if SPIFFS_CACHE + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { +#if SPIFFS_READ_ONLY + (void)fs; (void)old_path; (void)new_path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || + strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_page_ix pix_old, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + } else if (res == SPIFFS_OK) { + res = SPIFFS_ERR_CONFLICTING_NAME; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, + 0, 0, &pix_dummy); +#if SPIFFS_TEMPORAL_FD_CACHE + if (res == SPIFFS_OK) { + spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); + } +#endif + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_OBJ_META_LEN +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)name; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_page_ix pix, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} +#endif // SPIFFS_OBJ_META_LEN + +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { + (void)name; + + if (!SPIFFS_CHECK_CFG((fs))) { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return 0; + } + + if (!SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; +} + +static s32_t spiffs_read_dir_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) return res; + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; + e->obj_id = obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + e->pix = pix; +#if SPIFFS_OBJ_META_LEN + memcpy(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + return SPIFFS_OK; + } + return SPIFFS_VIS_COUNTINUE; +} + +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { + if (!SPIFFS_CHECK_MOUNT(d->fs)) { + d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + SPIFFS_LOCK(d->fs); + + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; + + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) { + d->block = bix; + d->entry = entry + 1; + e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + ret = e; + } else { + d->fs->err_code = res; + } + SPIFFS_UNLOCK(d->fs); + return ret; +} + +s32_t SPIFFS_closedir(spiffs_DIR *d) { + SPIFFS_API_CHECK_CFG(d->fs); + SPIFFS_API_CHECK_MOUNT(d->fs); + return 0; +} + +s32_t SPIFFS_check(spiffs *fs) { +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_lookup_consistency_check(fs, 0); + + res = spiffs_object_index_consistency_check(fs); + + res = spiffs_page_consistency_check(fs); + + res = spiffs_obj_lu_scan(fs); + + SPIFFS_UNLOCK(fs); + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); + u32_t blocks = fs->block_count; + u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); + u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); + u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page + + if (total) { + *total = total_data_pages * data_page_size; + } + + if (used) { + *used = fs->stats_p_allocated * data_page_size; + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { +#if SPIFFS_READ_ONLY + (void)fs; (void)max_free_pages; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_quick(fs, max_free_pages); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + + +s32_t SPIFFS_gc(spiffs *fs, u32_t size) { +#if SPIFFS_READ_ONLY + (void)fs; (void)size; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_check(fs, size); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = fd->fdoffset; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) { + SPIFFS_LOCK(fs); + fs->file_cb_f = cb_func; + SPIFFS_UNLOCK(fs); + return 0; +} + +#if SPIFFS_IX_MAP + +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); + } + + map->map_buf = map_buf; + map->offset = offset; + // nb: spix range includes last + map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); + memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); + fd->ix_map = map; + + // scan for pixes + res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + fd->ix_map = 0; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + spiffs_ix_map *map = fd->ix_map; + + s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; + map->offset = offset; + + // move existing pixes if within map offs + if (spix_diff != 0) { + // move vector + int i; + const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last + map->start_spix += spix_diff; + map->end_spix += spix_diff; + if (spix_diff >= vec_len) { + // moving beyond range + memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else if (spix_diff > 0) { + // diff positive + for (i = 0; i < vec_len - spix_diff; i++) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // diff negative + for (i = vec_len - 1; i >= -spix_diff; i--) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) { + SPIFFS_API_CHECK_CFG(fs); + // always add one extra page, the offset might change to the middle of a page + return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs); +} + +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) { + SPIFFS_API_CHECK_CFG(fs); + return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); +} + +#endif // SPIFFS_IX_MAP + +#if SPIFFS_TEST_VISUALISATION +s32_t SPIFFS_vis(spiffs *fs) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_block_ix bix = 0; + + while (bix < fs->block_count) { + // check each object lookup page + int obj_lookup_page = 0; + int cur_entry = 0; + + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (cur_entry == 0) { + spiffs_printf(_SPIPRIbl" ", bix); + } else if ((cur_entry & 0x3f) == 0) { + spiffs_printf(" "); + } + if (obj_id == SPIFFS_OBJ_ID_FREE) { + spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); + } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){ + spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + } else { + spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + } + cur_entry++; + if ((cur_entry & 0x3f) == 0) { + spiffs_printf("\n"); + } + } // per entry + obj_lookup_page++; + } // per object lookup page + + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + if (erase_count != (spiffs_obj_id)-1) { + spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count); + } else { + spiffs_printf("\tera_cnt: N/A\n"); + } + + bix++; + } // per block + + spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count); + spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code); + spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count); + spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks); + spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated); + spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted); + SPIFFS_UNLOCK(fs); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total); + return res; +} +#endif diff --git a/components/mkspiffs/src/spiffs/spiffs_nucleus.c b/components/mkspiffs/src/spiffs/spiffs_nucleus.c new file mode 100644 index 0000000..44ba711 --- /dev/null +++ b/components/mkspiffs/src/spiffs/spiffs_nucleus.c @@ -0,0 +1,2327 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" + +static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix)-1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_REF_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_REF_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_REF_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); +#endif + return res; +} + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix)-1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); +#endif + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_CACHE + +s32_t spiffs_phys_rd( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *dst) { + return SPIFFS_HAL_READ(fs, addr, len, dst); +} + +s32_t spiffs_phys_wr( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *src) { + return SPIFFS_HAL_WRITE(fs, addr, len, src); +} + +#endif + +#if !SPIFFS_READ_ONLY +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len) { + (void)fh; + s32_t res; + u8_t b[SPIFFS_COPY_BUFFER_STACK]; + while (len > 0) { + u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); + SPIFFS_CHECK_RES(res); + len -= chunk_size; + src += chunk_size; + dst += chunk_size; + } + return SPIFFS_OK; +} +#endif // !SPIFFS_READ_ONLY + +// Find object lookup entry containing given id with visitor. +// Iterate over object lookup pages in each block until a given object id entry is found. +// When found, the visitor function is called with block index, entry index and user data. +// If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be +// ended and visitor's return code is returned to caller. +// If no visitor is given (0) the search returns on first entry with matching object id. +// If no match is found in all look up, SPIFFS_VIS_END is returned. +// @param fs the file system +// @param starting_block the starting block to start search in +// @param starting_lu_entry the look up index entry to start search in +// @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH, +// SPIFFS_VIS_NO_WRAP +// @param obj_id argument object id +// @param v visitor callback function +// @param user_const_p any const pointer, passed to the callback visitor function +// @param user_var_p any pointer, passed to the callback visitor function +// @param block_ix reported block index where match was found +// @param lu_entry reported look up index where match was found +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + const void *user_const_p, + void *user_var_p, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res = SPIFFS_OK; + s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); + spiffs_block_ix cur_block = starting_block; + u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = starting_lu_entry; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // wrap initial + if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + cur_entry = 0; + cur_block++; + cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } + + // check each block + while (res == SPIFFS_OK && entry_count > 0) { + int obj_lookup_page = cur_entry / entries_per_page; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages + cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page + { + if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) { + if (block_ix) *block_ix = cur_block; + if (lu_entry) *lu_entry = cur_entry; + if (v) { + res = v( + fs, + (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset], + cur_block, + cur_entry, + user_const_p, + user_var_p); + if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { + if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } + res = SPIFFS_OK; + cur_entry++; + entry_count--; + continue; + } else { + return res; + } + } else { + return SPIFFS_OK; + } + } + entry_count--; + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } // per block + + SPIFFS_CHECK_RES(res); + + return SPIFFS_VIS_END; +} + +#if !SPIFFS_READ_ONLY +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + // here we ignore res, just try erasing the block + while (size > 0) { + SPIFFS_DBG("erase "_SPIPRIad":"_SPIPRIi"\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); + size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); + } + fs->free_blocks++; + + // register erase count for this block + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + SPIFFS_CHECK_RES(res); + +#if SPIFFS_USE_MAGIC + // finally, write magic + spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + SPIFFS_CHECK_RES(res); +#endif + + fs->max_erase_count++; + if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { + fs->max_erase_count = 0; + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +s32_t spiffs_probe( + spiffs_config *cfg) { + s32_t res; + u32_t paddr; + spiffs dummy_fs; // create a dummy fs struct just to be able to use macros + memcpy(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); + dummy_fs.block_count = 0; + + // Read three magics, as one block may be in an aborted erase state. + // At least two of these must contain magic and be in decreasing order. + spiffs_obj_id magic[3]; + spiffs_obj_id bix_count[3]; + + spiffs_block_ix bix; + for (bix = 0; bix < 3; bix++) { + paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); +#if SPIFFS_HAL_CALLBACK_EXTRA + // not any proper fs to report here, so callback with null + // (cross fingers that no-one gets angry) + res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#else + res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#endif + bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); + SPIFFS_CHECK_RES(res); + } + + // check that we have sane number of blocks + if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; + // check that the order is correct, take aborted erases in calculation + // first block aborted erase + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { + return (bix_count[1]+1) * cfg->log_block_size; + } + // second block aborted erase + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { + return bix_count[0] * cfg->log_block_size; + } + // third block aborted erase + if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { + return bix_count[0] * cfg->log_block_size; + } + // no block has aborted erase + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { + return bix_count[0] * cfg->log_block_size; + } + + return SPIFFS_ERR_PROBE_NOT_A_FS; +} +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + + +static s32_t spiffs_obj_lu_scan_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)bix; + (void)user_const_p; + (void)user_var_p; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + if (ix_entry == 0) { + fs->free_blocks++; + // todo optimize further, return SPIFFS_NEXT_BLOCK + } + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + fs->stats_p_deleted++; + } else { + fs->stats_p_allocated++; + } + + return SPIFFS_VIS_COUNTINUE; +} + + +// Scans thru all obj lu and counts free, deleted and used pages +// Find the maximum block erase count +// Checks magic if enabled +s32_t spiffs_obj_lu_scan( + spiffs *fs) { + s32_t res; + spiffs_block_ix bix; + int entry; +#if SPIFFS_USE_MAGIC + spiffs_block_ix unerased_bix = (spiffs_block_ix)-1; +#endif + + // find out erase count + // if enabled, check magic + bix = 0; + spiffs_obj_id erase_count_final; + spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; + spiffs_obj_id erase_count_max = 0; + while (bix < fs->block_count) { +#if SPIFFS_USE_MAGIC + spiffs_obj_id magic; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_MAGIC_PADDR(fs, bix) , + sizeof(spiffs_obj_id), (u8_t *)&magic); + + SPIFFS_CHECK_RES(res); + if (magic != SPIFFS_MAGIC(fs, bix)) { + if (unerased_bix == (spiffs_block_ix)-1) { + // allow one unerased block as it might be powered down during an erase + unerased_bix = bix; + } else { + // more than one unerased block, bail out + SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); + } + } +#endif + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix) , + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + if (erase_count != SPIFFS_OBJ_ID_FREE) { + erase_count_min = MIN(erase_count_min, erase_count); + erase_count_max = MAX(erase_count_max, erase_count); + } + bix++; + } + + if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) { + // clean system, set counter to zero + erase_count_final = 0; + } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE)/2) { + // wrap, take min + erase_count_final = erase_count_min+1; + } else { + erase_count_final = erase_count_max+1; + } + + fs->max_erase_count = erase_count_final; + +#if SPIFFS_USE_MAGIC + if (unerased_bix != (spiffs_block_ix)-1) { + // found one unerased block, remedy + SPIFFS_DBG("mount: erase block "_SPIPRIbl"\n", bix); +#if SPIFFS_READ_ONLY + res = SPIFFS_ERR_RO_ABORTED_OPERATION; +#else + res = spiffs_erase_block(fs, unerased_bix); +#endif // SPIFFS_READ_ONLY + SPIFFS_CHECK_RES(res); + } +#endif + + // count blocks + + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; + + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + SPIFFS_CHECK_RES(res); + + return res; +} + +#if !SPIFFS_READ_ONLY +// Find free object lookup entry +// Iterate over object lookup pages in each block until a free object id entry is found +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res; + if (!fs->cleaning && fs->free_blocks < 2) { + res = spiffs_gc_quick(fs, 0); + if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { + res = SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + if (fs->free_blocks < 2) { + return SPIFFS_ERR_FULL; + } + } + res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, + SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); + if (res == SPIFFS_OK) { + fs->free_cursor_block_ix = *block_ix; + fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; + if (*lu_entry == 0) { + fs->free_blocks--; + } + } + if (res == SPIFFS_ERR_FULL) { + SPIFFS_DBG("fs full\n"); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +// Find object lookup entry containing given id +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res = spiffs_obj_lu_find_entry_visitor( + fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + return res; +} + + +static s32_t spiffs_obj_lu_find_id_and_span_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + s32_t res; + spiffs_page_header ph; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + if (ph.obj_id == obj_id && + ph.span_ix == *((spiffs_span_ix*)user_var_p) && + (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && + !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && + (user_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) { + return SPIFFS_OK; + } else { + return SPIFFS_VIS_COUNTINUE; + } +} + +// Find object lookup entry containing given id and span index +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_ID, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Find object lookup entry containing given id and span index in page headers only +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_PH, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +#if SPIFFS_IX_MAP + +// update index map of given fd with given object index data +static void spiffs_update_ix_map(spiffs *fs, + spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) { +#if SPIFFS_SINGLETON + (void)fs; +#endif + spiffs_ix_map *map = fd->ix_map; + spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); + spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); + + // check if updated ix is within map range + if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) { + return; + } + + // update memory mapped page index buffer to new pages + + // get range of updated object index map data span indices + spiffs_span_ix objix_data_spix_start = + SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); + spiffs_span_ix objix_data_spix_end = objix_data_spix_start + + (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); + + // calc union of object index range and index map range array + spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); + spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); + + while (map_spix < map_spix_end) { + spiffs_page_ix objix_data_pix; + if (objix_spix == 0) { + // get data page from object index header page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; + } else { + // get data page from object index page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + } + + if (objix_data_pix == (spiffs_page_ix)-1) { + // reached end of object, abort + break; + } + + map->map_buf[map_spix - map->start_spix] = objix_data_pix; + SPIFFS_DBG("map "_SPIPRIid":"_SPIPRIsp" ("_SPIPRIsp"--"_SPIPRIsp") objix.spix:"_SPIPRIsp" to pix "_SPIPRIpg"\n", + fd->obj_id, map_spix - map->start_spix, + map->start_spix, map->end_spix, + objix->p_hdr.span_ix, + objix_data_pix); + + map_spix++; + } +} + +typedef struct { + spiffs_fd *fd; + u32_t remaining_objix_pages_to_visit; + spiffs_span_ix map_objix_start_spix; + spiffs_span_ix map_objix_end_spix; +} spiffs_ix_map_populate_state; + +static s32_t spiffs_populate_ix_map_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + + // load header to check it + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); + + // check if hdr is ok, and if objix range overlap with ix map range + if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && + objix->p_hdr.span_ix >= state->map_objix_start_spix && + objix->p_hdr.span_ix <= state->map_objix_end_spix) { + // ok, load rest of object index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), + (u8_t *)objix + sizeof(spiffs_page_object_ix)); + SPIFFS_CHECK_RES(res); + + spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); + + state->remaining_objix_pages_to_visit--; + SPIFFS_DBG("map "_SPIPRIid" ("_SPIPRIsp"--"_SPIPRIsp") remaining objix pages "_SPIPRIi"\n", + state->fd->obj_id, + state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, + state->remaining_objix_pages_to_visit); + } + + if (res == SPIFFS_OK) { + res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; + } + return res; +} + +// populates index map, from vector entry start to vector entry end, inclusive +s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) { + s32_t res; + spiffs_ix_map *map = fd->ix_map; + spiffs_ix_map_populate_state state; + vec_entry_start = MIN((map->end_spix - map->start_spix + 1) - 1, (s32_t)vec_entry_start); + vec_entry_end = MAX((map->end_spix - map->start_spix + 1) - 1, (s32_t)vec_entry_end); + if (vec_entry_start > vec_entry_end) { + return SPIFFS_ERR_IX_MAP_BAD_RANGE; + } + state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); + state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); + state.remaining_objix_pages_to_visit = + state.map_objix_end_spix - state.map_objix_start_spix + 1; + state.fd = fd; + + res = spiffs_obj_lu_find_entry_visitor( + fs, + SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_VIS_CHECK_ID, + fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + spiffs_populate_ix_map_v, + 0, + &state, + 0, + 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + return res; +} + +#endif + + +#if !SPIFFS_READ_ONLY +// Allocates a free defined page with given obj_id +// Occupies object lookup entry and page +// data may be NULL; where only page header is stored, len and page_offs is ignored +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + int entry; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write page header + ph->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); + SPIFFS_CHECK_RES(res); + + // write page data + if (data) { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0,SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + SPIFFS_CHECK_RES(res); + } + + // finalize header if necessary + if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { + ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&ph->flags); + SPIFFS_CHECK_RES(res); + } + + // return written page + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. +// If page data is null, provided header is used for metainfo and page data is physically copied. +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix) { + s32_t res; + u8_t was_final = 0; + spiffs_page_header *p_hdr; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + if (dst_pix) *dst_pix = free_pix; + + p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; + if (page_data) { + // got page data + was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; + // write unfinalized page + p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; + p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data); + } else { + // copy page data + res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } + SPIFFS_CHECK_RES(res); + + // mark entry in destination object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + if (was_final) { + // mark finalized in destination page + p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fh, + SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr->flags); + SPIFFS_CHECK_RES(res); + } + // mark source deleted + res = spiffs_page_delete(fs, src_pix); + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Deletes a page and removes it from object lookup. +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix) { + s32_t res; + spiffs_page_header hdr; + hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); + // mark deleted entry in source object lookup + spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, + 0, + SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&d_obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_deleted++; + fs->stats_p_allocated--; + + // mark deleted in source page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&hdr.flags); + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Create an object index header page with empty index and undefined length +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + const u8_t name[], + const u8_t meta[], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + spiffs_page_object_ix_header oix_hdr; + int entry; + + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("create: found free page @ "_SPIPRIpg" bix:"_SPIPRIbl" entry:"_SPIPRIsp"\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write empty object index page + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); + oix_hdr.type = type; + oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page + strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + if (meta) { + memcpy(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + } else { + memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + + // update page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); + + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, + SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + + if (objix_hdr_pix) { + *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// update object index header with any combination of name/size/index +// new_objix_hdr_data may be null, if so the object index header page is loaded +// name may be null, if so name is not changed +// size may be null, if so size is not changed +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + const u8_t name[], + const u8_t meta[], + u32_t size, + spiffs_page_ix *new_pix) { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header *objix_hdr; + spiffs_page_ix new_objix_hdr_pix; + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + if (new_objix_hdr_data) { + // object index header page already given to us, no need to load it + objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; + } else { + // read object index header page + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + objix_hdr = (spiffs_page_object_ix_header *)fs->work; + } + + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); + + // change name + if (name) { + strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); + } +#if SPIFFS_OBJ_META_LEN + if (meta) { + memcpy(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + if (size) { + objix_hdr->size = size; + } + + // move and update page + res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); + + if (res == SPIFFS_OK) { + if (new_pix) { + *new_pix = new_objix_hdr_pix; + } + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, + obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, + int ev, + spiffs_obj_id obj_id_raw, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size) { +#if SPIFFS_IX_MAP == 0 + (void)objix; +#endif + // update index caches in all file descriptors + spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; +#if SPIFFS_TEMPORAL_FD_CACHE + if (cur_fd->score == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; +#else + if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; +#endif + if (spix == 0) { + if (ev != SPIFFS_EV_IX_DEL) { + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size); + cur_fd->objix_hdr_pix = new_pix; + if (new_size != 0) { + cur_fd->size = new_size; + } + } else { + cur_fd->file_nbr = 0; + cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; + } + } + if (cur_fd->cursor_objix_spix == spix) { + if (ev != SPIFFS_EV_IX_DEL) { + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix); + cur_fd->cursor_objix_pix = new_pix; + } else { + cur_fd->cursor_objix_pix = 0; + } + } + } + +#if SPIFFS_IX_MAP + + // update index maps + if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) { + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + // check fd opened, having ix map, match obj id + if (cur_fd->file_nbr == 0 || + cur_fd->ix_map == 0 || + (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; + SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", cur_fd->file_nbr, cur_fd->obj_id, spix); + spiffs_update_ix_map(fs, cur_fd, spix, objix); + } + } + +#endif + + // callback to user if object index header + if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_fileop_type op; + if (ev == SPIFFS_EV_IX_NEW) { + op = SPIFFS_CB_CREATED; + } else if (ev == SPIFFS_EV_IX_UPD || + ev == SPIFFS_EV_IX_MOV || + ev == SPIFFS_EV_IX_UPD_HDR) { + op = SPIFFS_CB_UPDATED; + } else if (ev == SPIFFS_EV_IX_DEL) { + op = SPIFFS_CB_DELETED; + } else { + SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi"\n", ev); + return; // bail out + } + fs->file_cb_f(fs, op, obj_id, new_pix); + } +} + +// Open object by id +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + s32_t res = SPIFFS_OK; + spiffs_page_ix pix; + + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + + return res; +} + +// Open object by page index +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + (void)mode; + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header oix_hdr; + spiffs_obj_id obj_id; + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + SPIFFS_CHECK_RES(res); + + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + + fd->fs = fs; + fd->objix_hdr_pix = pix; + fd->size = oix_hdr.size; + fd->offset = 0; + fd->cursor_objix_pix = pix; + fd->cursor_objix_spix = 0; + fd->obj_id = obj_id; + fd->flags = flags; + + SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); + + SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", fd->file_nbr, fd->obj_id); + + return res; +} + +#if !SPIFFS_READ_ONLY +// Append to object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + SPIFFS_DBG("append: "_SPIPRIi" bytes @ offs "_SPIPRIi" of size "_SPIPRIi"\n", len, offset, fd->size); + + if (offset > fd->size) { + SPIFFS_DBG("append: offset reversed to size\n"); + offset = fd->size; + } + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta + if (res != SPIFFS_OK) { + SPIFFS_DBG("append: gc check fail "_SPIPRIi"\n", res); + } + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_page; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_page; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index page, unless first pass + SPIFFS_DBG("append: "_SPIPRIid" store objix "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, prev_objix_spix, written); + if (prev_objix_spix == 0) { + // this is an update to object index header page + objix_hdr->size = offset+written; + if (offset == 0) { + // was an empty object, update same page (size was 0xffffffff) + res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + } else { + // was a nonempty object, update to new page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + new_objix_hdr_page, 0, written); + } + } else { + // this is an update to an object index page + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + // update length in object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new size I "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + offset+written, new_objix_hdr_page, 0, written); + } + fd->size = offset+written; + fd->offset = offset+written; + } + + // create or load new object index page + if (cur_objix_spix == 0) { + // load object index header page, must always exist + SPIFFS_DBG("append: "_SPIPRIid" load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs)); + // on subsequent passes, create a new object index page + if (written > 0 || cur_objix_spix > len_objix_spix) { + p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = cur_objix_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 1, &cur_objix_pix); + SPIFFS_CHECK_RES(res); + // quick "load" of new object index page + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header)); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + } else { + // on first pass, we load existing object index page + spiffs_page_ix pix; + SPIFFS_DBG("append: "_SPIPRIid" find objix span_ix:"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("append: "_SPIPRIid" found object index at page "_SPIPRIpg" [fd size "_SPIPRIi"]\n", fd->obj_id, pix, fd->size); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + fd->size = offset+written; + } + prev_objix_spix = cur_objix_spix; + } + + // write data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + if (page_offs == 0) { + // at beginning of a page, allocate and write a new page of data + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_page); + SPIFFS_DBG("append: "_SPIPRIid" store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id, + data_page, data_spix, page_offs, to_write, written); + } else { + // append to existing page, fill out free data in existing page + if (cur_objix_spix == 0) { + // get data page from object index header page + data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + res = spiffs_page_data_check(fs, fd, data_page, data_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + SPIFFS_DBG("append: "_SPIPRIid" store to existing data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id + , data_page, data_spix, page_offs, to_write, written); + } + + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, data_spix); + objix_hdr->size = offset+written; + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->size = offset+written; + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page, unless object header index page + SPIFFS_DBG("append: "_SPIPRIid" store objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + + // update size in object header index page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store new size II "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi", res "_SPIPRIi"\n", fd->obj_id + , offset+written, new_objix_hdr_page, 0, written, res2); + SPIFFS_CHECK_RES(res2); + } else { + // wrote within object index header page + if (offset == 0) { + // wrote to empty object - simply update size and write whole page + objix_hdr->size = offset+written; + SPIFFS_DBG("append: "_SPIPRIid" store fresh objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + } else { + // modifying object index header page, update size and make new copy + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } + } + + return res; +} // spiffs_object_append +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Modify object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_pix; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_pix; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index (header) page, unless first pass + if (prev_objix_spix == 0) { + // store previous object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res); + } else { + // store new version of previous object index page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + + // load next object index page + if (cur_objix_spix == 0) { + // load object index header page, must exist + SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + // load existing object index page on first pass + spiffs_page_ix pix; + SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp"\n", cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("modify: found object index at page "_SPIPRIpg"\n", pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + prev_objix_spix = cur_objix_spix; + } + + // write partial data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + spiffs_page_ix orig_data_pix; + if (cur_objix_spix == 0) { + // get data page from object index header page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { + // a full page, allocate and write a new page of data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); + SPIFFS_DBG("modify: store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", data_pix, data_spix, page_offs, to_write, written); + } else { + // write to existing page, allocate new and copy unmodified data + + res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &data_pix); + if (res != SPIFFS_OK) break; + + // copy unmodified data + if (page_offs > 0) { + // before modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), + page_offs); + if (res != SPIFFS_OK) break; + } + if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { + // after modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); + if (res != SPIFFS_OK) break; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg", dst:"_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + } + + // delete original data page + res = spiffs_page_delete(fs, orig_data_pix); + if (res != SPIFFS_OK) break; + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", data_pix, data_spix); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page + // move and update page + spiffs_page_ix new_objix_pix; + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, cur_objix_spix, written); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + + } else { + // wrote within object index header page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res2); + } + + return res; +} // spiffs_object_modify +#endif // !SPIFFS_READ_ONLY + +static s32_t spiffs_object_find_object_index_header_by_name_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_var_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { + return SPIFFS_OK; + } + } + + return SPIFFS_VIS_COUNTINUE; +} + +// Finds object index header page by name +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + const u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + 0, + 0, + spiffs_object_find_object_index_header_by_name_v, + name, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +#if !SPIFFS_READ_ONLY +// Truncates object to new size. If new size is null, object may be removed totally +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_size, + u8_t remove_full) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + + if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) { + // no op + return res; + } + + // need 2 pages if not removing: object index page + possibly chopped data page + if (remove_full == 0) { + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); + SPIFFS_CHECK_RES(res); + } + + spiffs_page_ix objix_pix = fd->objix_hdr_pix; + spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_ix data_pix; + spiffs_page_ix new_objix_hdr_pix; + + // before truncating, check if object is to be fully removed and mark this + if (remove_full && new_size == 0) { + u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + SPIFFS_CHECK_RES(res); + } + + // delete from end of object until desired len is reached + while (cur_size > new_size) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // put object index for current data span index in work buffer + if (prev_objix_spix != cur_objix_spix) { + if (prev_objix_spix != (spiffs_span_ix)-1) { + // remove previous object index page + SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg":"_SPIPRIsp"\n", objix_pix, prev_objix_spix); + + res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); + if (prev_objix_spix > 0) { + // Update object index header page, unless we totally want to remove the file. + // If fully removing, we're not keeping consistency as good as when storing the header between chunks, + // would we be aborted. But when removing full files, a crammed system may otherwise + // report ERR_FULL a la windows. We cannot have that. + // Hence, take the risk - if aborted, a file check would free the lost pages and mend things + // as the file is marked as fully deleted in the beginning. + if (remove_full == 0) { + SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg":"_SPIPRIsp" to size "_SPIPRIi"\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + } + } + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + + SPIFFS_DBG("truncate: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + } + + SPIFFS_DBG("truncate: got data pix "_SPIPRIpg"\n", data_pix); + + if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) { + // delete full data page + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { + SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi"\n", res); + break; + } + + if (res == SPIFFS_OK) { + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) { + SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi"\n", res); + break; + } + } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { + res = SPIFFS_OK; + } + + // update current size + if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { + cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); + } else { + cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); + } + fd->size = cur_size; + fd->offset = cur_size; + SPIFFS_DBG("truncate: delete data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", data_pix, data_spix, cur_size); + } else { + // delete last page, partially + spiffs_page_header p_hdr; + spiffs_page_ix new_data_pix; + u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_DBG("truncate: delete "_SPIPRIi" bytes from data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", bytes_to_remove, data_pix, data_spix, cur_size); + + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + // allocate new page and copy unmodified data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &new_data_pix); + if (res != SPIFFS_OK) break; + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); + if (res != SPIFFS_OK) break; + // delete original data page + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + cur_size = new_size; + fd->size = new_size; + fd->offset = cur_size; + break; + } + data_spix--; + } // while all data + + // update object indices + if (cur_objix_spix == 0) { + // update object index header page + if (cur_size == 0) { + if (remove_full) { + // remove object altogether + SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg"\n", objix_pix); + + res = spiffs_page_index_check(fs, fd, objix_pix, 0); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + } else { + // make uninitialized object + SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg"\n", objix_pix); + memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update object index header page + SPIFFS_DBG("truncate: update object index header page with indices and size\n"); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update both current object index page and object index header page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res); + + // move and update object index page + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, cur_objix_spix); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + // update object index header page with new size + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + + return res; +} // spiffs_object_truncate +#endif // !SPIFFS_READ_ONLY + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + spiffs_page_ix objix_pix; + spiffs_page_ix data_pix; + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_offset = offset; + spiffs_span_ix cur_objix_spix; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + while (cur_offset < offset + len) { +#if SPIFFS_IX_MAP + // check if we have a memory, index map and if so, if we're within index map's range + // and if so, if the entry is populated + if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix + && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) { + data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; + } else { +#endif + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) { + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + SPIFFS_DBG("read: find objix "_SPIPRIid":"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + objix_pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + } + SPIFFS_DBG("read: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); + + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } +#if SPIFFS_IX_MAP + } +#endif + // all remaining data + u32_t len_to_read = offset + len - cur_offset; + // remaining data in page + len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + // remaining data in file + len_to_read = MIN(len_to_read, fd->size); + SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix, + (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); + if (len_to_read <= 0) { + res = SPIFFS_ERR_END_OF_OBJECT; + break; + } + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + SPIFFS_CHECK_RES(res); + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), + len_to_read, + dst); + SPIFFS_CHECK_RES(res); + dst += len_to_read; + cur_offset += len_to_read; + fd->offset = cur_offset; + data_spix++; + } + + return res; +} + +#if !SPIFFS_READ_ONLY +typedef struct { + spiffs_obj_id min_obj_id; + spiffs_obj_id max_obj_id; + u32_t compaction; + const u8_t *conflicting_name; +} spiffs_free_obj_id_state; + +static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p) { + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { + spiffs_obj_id min_obj_id = *((spiffs_obj_id*)user_var_p); + const u8_t *conflicting_name = (const u8_t*)user_const_p; + + // if conflicting name parameter is given, also check if this name is found in object index hdrs + if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + int res; + spiffs_page_object_ix_header objix_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + } + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t bit_ix = (id-min_obj_id) & 7; + int byte_ix = (id-min_obj_id) >> 3; + if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + fs->work[byte_ix] |= (1<conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + if (id >= state->min_obj_id && id <= state->max_obj_id) { + u8_t *map = (u8_t *)fs->work; + int ix = (id - state->min_obj_id) / state->compaction; + //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + map[ix]++; + } + } + } + return SPIFFS_VIS_COUNTINUE; +} + +// Scans thru all object lookup for object index header pages. If total possible number of +// object ids cannot fit into a work buffer, these are grouped. When a group containing free +// object ids is found, the object lu is again scanned for object ids within group and bitmasked. +// Finally, the bitmask is searched for a free id +s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) { + s32_t res = SPIFFS_OK; + u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; + spiffs_free_obj_id_state state; + spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; + state.min_obj_id = 1; + state.max_obj_id = max_objects + 1; + if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG; + } + state.compaction = 0; + state.conflicting_name = conflicting_name; + while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { + if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) { + // possible to represent in bitmap + u32_t i, j; + SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid" max:"_SPIPRIid"\n", state.min_obj_id, state.max_obj_id); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, + conflicting_name, &state.min_obj_id, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + // traverse bitmask until found free obj_id + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { + u8_t mask = fs->work[i]; + if (mask == 0xff) { + continue; + } + for (j = 0; j < 8; j++) { + if ((mask & (1<work; + u8_t min_count = 0xff; + + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) { + if (map[i] < min_count) { + min_count = map[i]; + min_i = i; + if (min_count == 0) { + break; + } + } + } + + if (min_count == state.compaction) { + // there are no free objids! + SPIFFS_DBG("free_obj_id: compacted table is full\n"); + return SPIFFS_ERR_FULL; + } + + SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi" min_count:"_SPIPRIi" min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + + if (min_count == 0) { + // no id in this range, skip compacting and use directly + *obj_id = min_i * state.compaction + state.min_obj_id; + return SPIFFS_OK; + } else { + SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi" min:"_SPIPRIid" -> "_SPIPRIid"\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + state.min_obj_id += min_i * state.compaction; + state.max_obj_id = state.min_obj_id + state.compaction; + // decrease compaction + } + if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) { + // no need for compacting, use bitmap + continue; + } + } + // in a work memory of log_page_size bytes, we may fit in log_page_size ids + // todo what if compaction is > 255 - then we cannot fit it in a byte + state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); + SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", state.min_obj_id, state.max_obj_id, state.compaction); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + state.conflicting_name = 0; // searched for conflicting name once, no need to do it again + } + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_TEMPORAL_FD_CACHE +// djb2 hash +static u32_t spiffs_hash(spiffs *fs, const u8_t *name) { + (void)fs; + u32_t hash = 5381; + u8_t c; + int i = 0; + while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) { + hash = (hash * 33) ^ c; + } + return hash; +} +#endif + +s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) { +#if SPIFFS_TEMPORAL_FD_CACHE + u32_t i; + u16_t min_score = 0xffff; + u32_t cand_ix = (u32_t)-1; + u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + + if (name) { + // first, decrease score of all closed descriptors + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (cur_fd->score > 1) { // score == 0 indicates never used fd + cur_fd->score--; + } + } + } + } + + // find the free fd with least score + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (name && cur_fd->name_hash == name_hash) { + cand_ix = i; + break; + } + if (cur_fd->score < min_score) { + min_score = cur_fd->score; + cand_ix = i; + } + } + } + + if (cand_ix != (u32_t)-1) { + spiffs_fd *cur_fd = &fds[cand_ix]; + if (name) { + if (cur_fd->name_hash == name_hash && cur_fd->score > 0) { + // opened an fd with same name hash, assume same file + // set search point to saved obj index page and hope we have a correct match directly + // when start searching - if not, we will just keep searching until it is found + fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + // update score + if (cur_fd->score < 0xffff-SPIFFS_TEMPORAL_CACHE_HIT_SCORE) { + cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + } else { + cur_fd->score = 0xffff; + } + } else { + // no hash hit, restore this fd to initial state + cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + cur_fd->name_hash = name_hash; + } + } + cur_fd->file_nbr = cand_ix+1; + *fd = cur_fd; + return SPIFFS_OK; + } else { + return SPIFFS_ERR_OUT_OF_FILE_DESCS; + } +#else + (void)name; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + cur_fd->file_nbr = i+1; + *fd = cur_fd; + return SPIFFS_OK; + } + } + return SPIFFS_ERR_OUT_OF_FILE_DESCS; +#endif +} + +s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + spiffs_fd *fd = &fds[f-1]; + if (fd->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + fd->file_nbr = 0; +#if SPIFFS_IX_MAP + fd->ix_map = 0; +#endif + return SPIFFS_OK; +} + +s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + *fd = &fds[f-1]; + if ((*fd)->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + return SPIFFS_OK; +} + +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path) { + u32_t i; + u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); + u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->score > 0 && cur_fd->name_hash == old_hash) { + cur_fd->name_hash = new_hash; + } + } +} +#endif diff --git a/components/mkspiffs/src/spiffs/spiffs_nucleus.h b/components/mkspiffs/src/spiffs/spiffs_nucleus.h new file mode 100644 index 0000000..7d676ee --- /dev/null +++ b/components/mkspiffs/src/spiffs/spiffs_nucleus.h @@ -0,0 +1,797 @@ +/* + * spiffs_nucleus.h + * + * Created on: Jun 15, 2013 + * Author: petera + */ + +/* SPIFFS layout + * + * spiffs is designed for following spi flash characteristics: + * - only big areas of data (blocks) can be erased + * - erasing resets all bits in a block to ones + * - writing pulls ones to zeroes + * - zeroes cannot be pulled to ones, without erase + * - wear leveling + * + * spiffs is also meant to be run on embedded, memory constraint devices. + * + * Entire area is divided in blocks. Entire area is also divided in pages. + * Each block contains same number of pages. A page cannot be erased, but a + * block can be erased. + * + * Entire area must be block_size * x + * page_size must be block_size / (2^y) where y > 2 + * + * ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes + * + * BLOCK 0 PAGE 0 object lookup 1 + * PAGE 1 object lookup 2 + * ... + * PAGE n-1 object lookup n + * PAGE n object data 1 + * PAGE n+1 object data 2 + * ... + * PAGE n+m-1 object data m + * + * BLOCK 1 PAGE n+m object lookup 1 + * PAGE n+m+1 object lookup 2 + * ... + * PAGE 2n+m-1 object lookup n + * PAGE 2n+m object data 1 + * PAGE 2n+m object data 2 + * ... + * PAGE 2n+2m-1 object data m + * ... + * + * n is number of object lookup pages, which is number of pages needed to index all pages + * in a block by object id + * : block_size / page_size * sizeof(obj_id) / page_size + * m is number data pages, which is number of pages in block minus number of lookup pages + * : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size + * thus, n+m is total number of pages in a block + * : block_size / page_size + * + * ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 + * + * Object lookup pages contain object id entries. Each entry represent the corresponding + * data page. + * Assuming a 16 bit object id, an object id being 0xffff represents a free page. + * An object id being 0x0000 represents a deleted page. + * + * ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. + * page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. + * page 2 : data : data for object id 0008 + * page 3 : data : data for object id 0001 + * page 4 : data : data for object id 0aaa + * ... + * + * + * Object data pages can be either object index pages or object content. + * All object data pages contains a data page header, containing object id and span index. + * The span index denotes the object page ordering amongst data pages with same object id. + * This applies to both object index pages (when index spans more than one page of entries), + * and object data pages. + * An object index page contains page entries pointing to object content page. The entry index + * in a object index page correlates to the span index in the actual object data page. + * The first object index page (span index 0) is called object index header page, and also + * contains object flags (directory/file), size, object name etc. + * + * ex: + * BLOCK 1 + * PAGE 256: objectl lookup page 1 + * [*123] [ 123] [ 123] [ 123] + * [ 123] [*123] [ 123] [ 123] + * [free] [free] [free] [free] ... + * PAGE 257: objectl lookup page 2 + * [free] [free] [free] [free] ... + * PAGE 258: object index page (header) + * obj.id:0123 span.ix:0000 flags:INDEX + * size:1600 name:ex.txt type:file + * [259] [260] [261] [262] + * PAGE 259: object data page + * obj.id:0123 span.ix:0000 flags:DATA + * PAGE 260: object data page + * obj.id:0123 span.ix:0001 flags:DATA + * PAGE 261: object data page + * obj.id:0123 span.ix:0002 flags:DATA + * PAGE 262: object data page + * obj.id:0123 span.ix:0003 flags:DATA + * PAGE 263: object index page + * obj.id:0123 span.ix:0001 flags:INDEX + * [264] [265] [fre] [fre] + * [fre] [fre] [fre] [fre] + * PAGE 264: object data page + * obj.id:0123 span.ix:0004 flags:DATA + * PAGE 265: object data page + * obj.id:0123 span.ix:0005 flags:DATA + * + */ +#ifndef SPIFFS_NUCLEUS_H_ +#define SPIFFS_NUCLEUS_H_ + +#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1) +#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1) +#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2) +#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3) +#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4) + +// visitor result, continue searching +#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20) +// visitor result, continue searching after reloading lu buffer +#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21) +// visitor result, stop searching +#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22) + +// updating an object index contents +#define SPIFFS_EV_IX_UPD (0) +// creating a new object index +#define SPIFFS_EV_IX_NEW (1) +// deleting an object index +#define SPIFFS_EV_IX_DEL (2) +// moving an object index without updating contents +#define SPIFFS_EV_IX_MOV (3) +// updating an object index header data only, not the table itself +#define SPIFFS_EV_IX_UPD_HDR (4) + +#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) + +#define SPIFFS_UNDEFINED_LEN (u32_t)(-1) + +#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) +#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) + +#if SPIFFS_USE_MAGIC +#if !SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_MAGIC(fs, bix) \ + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) +#else // SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_MAGIC(fs, bix) \ + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) +#endif // SPIFFS_USE_MAGIC_LENGTH +#endif // SPIFFS_USE_MAGIC + +#define SPIFFS_CONFIG_MAGIC (0x20090315) + +#if SPIFFS_SINGLETON == 0 +#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ + ((fs)->cfg.log_page_size) +#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ + ((fs)->cfg.log_block_size) +#define SPIFFS_CFG_PHYS_SZ(fs) \ + ((fs)->cfg.phys_size) +#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ + ((fs)->cfg.phys_erase_block) +#define SPIFFS_CFG_PHYS_ADDR(fs) \ + ((fs)->cfg.phys_addr) +#endif + +// total number of pages +#define SPIFFS_MAX_PAGES(fs) \ + ( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// total number of pages per block, including object lookup pages +#define SPIFFS_PAGES_PER_BLOCK(fs) \ + ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// number of object lookup pages per block +#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ + (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) +// checks if page index belongs to object lookup +#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ + (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// number of object lookup entries in all object lookup pages +#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ + (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// converts a block to physical address +#define SPIFFS_BLOCK_TO_PADDR(fs, block) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) +// converts a object lookup entry to page index +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ + ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) +// converts a object lookup entry to physical address of corresponding page +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ + (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a page to physical address +#define SPIFFS_PAGE_TO_PADDR(fs, page) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a physical address to page +#define SPIFFS_PADDR_TO_PAGE(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// gives index in page for a physical address +#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// returns containing block for given page +#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ + ( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) +// returns starting page for block +#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ + ( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) +// converts page to entry in object lookup page +#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ + ( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) +// returns data size in a data page +#define SPIFFS_DATA_PAGE_SIZE(fs) \ + ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) +// returns physical address for block's erase count, +// always in the physical last entry of the last object lookup page +#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) +// returns physical address for block's magic, +// always in the physical second last entry of the last object lookup page +#define SPIFFS_MAGIC_PADDR(fs, bix) \ + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) +// checks if there is any room for magic in the object luts +#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \ + ( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ + <= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) + +// define helpers object + +// entries in an object header page index +#define SPIFFS_OBJ_HDR_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) +// entries in an object page index +#define SPIFFS_OBJ_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) +// object index entry for given data span index +#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) +// object index span index number for given data span index or entry +#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) +// get data span index for object index span index +#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ + ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) + +#define SPIFFS_OP_T_OBJ_LU (0<<0) +#define SPIFFS_OP_T_OBJ_LU2 (1<<0) +#define SPIFFS_OP_T_OBJ_IX (2<<0) +#define SPIFFS_OP_T_OBJ_DA (3<<0) +#define SPIFFS_OP_C_DELE (0<<2) +#define SPIFFS_OP_C_UPDT (1<<2) +#define SPIFFS_OP_C_MOVS (2<<2) +#define SPIFFS_OP_C_MOVD (3<<2) +#define SPIFFS_OP_C_FLSH (4<<2) +#define SPIFFS_OP_C_READ (5<<2) +#define SPIFFS_OP_C_WRTHRU (6<<2) + +#define SPIFFS_OP_TYPE_MASK (3<<0) +#define SPIFFS_OP_COM_MASK (7<<2) + + +// if 0, this page is written to, else clean +#define SPIFFS_PH_FLAG_USED (1<<0) +// if 0, writing is finalized, else under modification +#define SPIFFS_PH_FLAG_FINAL (1<<1) +// if 0, this is an index page, else a data page +#define SPIFFS_PH_FLAG_INDEX (1<<2) +// if 0, page is deleted, else valid +#define SPIFFS_PH_FLAG_DELET (1<<7) +// if 0, this index header is being deleted +#define SPIFFS_PH_FLAG_IXDELE (1<<6) + + +#define SPIFFS_CHECK_MOUNT(fs) \ + ((fs)->mounted != 0) + +#define SPIFFS_CHECK_CFG(fs) \ + ((fs)->config_magic == SPIFFS_CONFIG_MAGIC) + +#define SPIFFS_CHECK_RES(res) \ + do { \ + if ((res) < SPIFFS_OK) return (res); \ + } while (0); + +#define SPIFFS_API_CHECK_MOUNT(fs) \ + if (!SPIFFS_CHECK_MOUNT((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ + return SPIFFS_ERR_NOT_MOUNTED; \ + } + +#define SPIFFS_API_CHECK_CFG(fs) \ + if (!SPIFFS_CHECK_CFG((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ + return SPIFFS_ERR_NOT_CONFIGURED; \ + } + +#define SPIFFS_API_CHECK_RES(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->err_code = (res); \ + return (res); \ + } + +#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->err_code = (res); \ + SPIFFS_UNLOCK(fs); \ + return (res); \ + } + +#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ + if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH; + //if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; + +#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \ + if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; + + +// check id, only visit matching objec ids +#define SPIFFS_VIS_CHECK_ID (1<<0) +// report argument object id to visitor - else object lookup id is reported +#define SPIFFS_VIS_CHECK_PH (1<<1) +// stop searching at end of all look up pages +#define SPIFFS_VIS_NO_WRAP (1<<2) + +#if SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) + +#else // SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_paddr), (_len)) + +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +#if SPIFFS_CACHE + +#define SPIFFS_CACHE_FLAG_DIRTY (1<<0) +#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1) +#define SPIFFS_CACHE_FLAG_OBJLU (1<<2) +#define SPIFFS_CACHE_FLAG_OBJIX (1<<3) +#define SPIFFS_CACHE_FLAG_DATA (1<<4) +#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) + +#define SPIFFS_CACHE_PAGE_SIZE(fs) \ + (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) + +#define spiffs_get_cache(fs) \ + ((spiffs_cache *)((fs)->cache)) + +#define spiffs_get_cache_page_hdr(fs, c, ix) \ + ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) + +#define spiffs_get_cache_page(fs, c, ix) \ + ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) + +// cache page struct +typedef struct { + // cache flags + u8_t flags; + // cache page index + u8_t ix; + // last access of this cache page + u32_t last_access; + union { + // type read cache + struct { + // read cache page index + spiffs_page_ix pix; + }; +#if SPIFFS_CACHE_WR + // type write cache + struct { + // write cache + spiffs_obj_id obj_id; + // offset in cache page + u32_t offset; + // size of cache page + u16_t size; + }; +#endif + }; +} spiffs_cache_page; + +// cache struct +typedef struct { + u8_t cpage_count; + u32_t last_access; + u32_t cpage_use_map; + u32_t cpage_use_mask; + u8_t *cpages; +} spiffs_cache; + +#endif + + +// spiffs nucleus file descriptor +typedef struct { + // the filesystem of this descriptor + spiffs *fs; + // number of file descriptor - if 0, the file descriptor is closed + spiffs_file file_nbr; + // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted + spiffs_obj_id obj_id; + // size of the file + u32_t size; + // cached object index header page index + spiffs_page_ix objix_hdr_pix; + // cached offset object index page index + spiffs_page_ix cursor_objix_pix; + // cached offset object index span index + spiffs_span_ix cursor_objix_spix; + // current absolute offset + u32_t offset; + // current file descriptor offset + u32_t fdoffset; + // fd flags + spiffs_flags flags; +#if SPIFFS_CACHE_WR + spiffs_cache_page *cache_page; +#endif +#if SPIFFS_TEMPORAL_FD_CACHE + // djb2 hash of filename + u32_t name_hash; + // hit score (score == 0 indicates never used fd) + u16_t score; +#endif +#if SPIFFS_IX_MAP + // spiffs index map, if 0 it means unmapped + spiffs_ix_map *ix_map; +#endif +} spiffs_fd; + + +// object structs + +// page header, part of each page except object lookup pages +// NB: this is always aligned when the data page is an object index, +// as in this case struct spiffs_page_object_ix is used +typedef struct __attribute(( packed )) { + // object id + spiffs_obj_id obj_id; + // object span index + spiffs_span_ix span_ix; + // flags + u8_t flags; +} spiffs_page_header; + +// object index header page header +typedef struct __attribute(( packed )) +#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES + __attribute(( aligned(sizeof(spiffs_page_ix)) )) +#endif +{ + // common page header + spiffs_page_header p_hdr; + // alignment + u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; + // size of object + u32_t size; + // type of object + spiffs_obj_type type; + // name of object + u8_t name[SPIFFS_OBJ_NAME_LEN]; +#if SPIFFS_OBJ_META_LEN + // metadata. not interpreted by SPIFFS in any way. + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +} spiffs_page_object_ix_header; + +// object index page header +typedef struct __attribute(( packed )) { + spiffs_page_header p_hdr; + u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; +} spiffs_page_object_ix; + +// callback func for object lookup visitor +typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p); + + +#if SPIFFS_CACHE +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src)) +#else +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (addr), (len), (src)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +// --------------- + +s32_t spiffs_phys_rd( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *dst); + +s32_t spiffs_phys_wr( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *src); + +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len); + +s32_t spiffs_phys_count_free_blocks( + spiffs *fs); + +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + const void *user_const_p, + void *user_var_p, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix); + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH +s32_t spiffs_probe( + spiffs_config *cfg); +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH + +// --------------- + +s32_t spiffs_obj_lu_scan( + spiffs *fs); + +s32_t spiffs_obj_lu_find_free_obj_id( + spiffs *fs, + spiffs_obj_id *obj_id, + const u8_t *conflicting_name); + +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix); + +s32_t spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix); + +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix); + +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix); + +// --------------- + +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + const u8_t name[], + const u8_t meta[], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix); + +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + const u8_t name[], + const u8_t meta[], + u32_t size, + spiffs_page_ix *new_pix); + +#if SPIFFS_IX_MAP + +s32_t spiffs_populate_ix_map( + spiffs *fs, + spiffs_fd *fd, + u32_t vec_entry_start, + u32_t vec_entry_end); + +#endif + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, + int ev, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size); + +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *f, + spiffs_flags flags, + spiffs_mode mode); + +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *f, + spiffs_flags flags, + spiffs_mode mode); + +s32_t spiffs_object_append( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_modify( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst); + +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_len, + u8_t remove_object); + +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + const u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len); + +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidate, + int *candidate_count, + char fs_crammed); + +s32_t spiffs_gc_clean( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_quick( + spiffs *fs, u16_t max_free_pages); + +// --------------- + +s32_t spiffs_fd_find_new( + spiffs *fs, + spiffs_fd **fd, + const char *name); + +s32_t spiffs_fd_return( + spiffs *fs, + spiffs_file f); + +s32_t spiffs_fd_get( + spiffs *fs, + spiffs_file f, + spiffs_fd **fd); + +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path); +#endif + +#if SPIFFS_CACHE +void spiffs_cache_init( + spiffs *fs); + +void spiffs_cache_drop_page( + spiffs *fs, + spiffs_page_ix pix); + +#if SPIFFS_CACHE_WR +spiffs_cache_page *spiffs_cache_page_allocate_by_fd( + spiffs *fs, + spiffs_fd *fd); + +void spiffs_cache_fd_release( + spiffs *fs, + spiffs_cache_page *cp); + +spiffs_cache_page *spiffs_cache_page_get_by_fd( + spiffs *fs, + spiffs_fd *fd); +#endif +#endif + +s32_t spiffs_lookup_consistency_check( + spiffs *fs, + u8_t check_all_objects); + +s32_t spiffs_page_consistency_check( + spiffs *fs); + +s32_t spiffs_object_index_consistency_check( + spiffs *fs); + +#endif /* SPIFFS_NUCLEUS_H_ */ diff --git a/components/mkspiffs/src/tclap/Arg.h b/components/mkspiffs/src/tclap/Arg.h new file mode 100644 index 0000000..d653164 --- /dev/null +++ b/components/mkspiffs/src/tclap/Arg.h @@ -0,0 +1,692 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: Arg.h + * + * Copyright (c) 2003, Michael E. Smoot . + * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_ARGUMENT_H +#define TCLAP_ARGUMENT_H + +#ifdef HAVE_CONFIG_H +#include +#else +#define HAVE_SSTREAM +#endif + +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_SSTREAM) +#include +typedef std::istringstream istringstream; +#elif defined(HAVE_STRSTREAM) +#include +typedef std::istrstream istringstream; +#else +#error "Need a stringstream (sstream or strstream) to compile!" +#endif + +#include "ArgException.h" +#include "Visitor.h" +#include "CmdLineInterface.h" +#include "ArgTraits.h" +#include "StandardTraits.h" + +namespace TCLAP { + +/** + * A virtual base class that defines the essential data for all arguments. + * This class, or one of its existing children, must be subclassed to do + * anything. + */ +class Arg +{ + private: + /** + * Prevent accidental copying. + */ + Arg(const Arg& rhs); + + /** + * Prevent accidental copying. + */ + Arg& operator=(const Arg& rhs); + + /** + * Indicates whether the rest of the arguments should be ignored. + */ + static bool& ignoreRestRef() { static bool ign = false; return ign; } + + /** + * The delimiter that separates an argument flag/name from the + * value. + */ + static char& delimiterRef() { static char delim = ' '; return delim; } + + protected: + + /** + * The single char flag used to identify the argument. + * This value (preceded by a dash {-}), can be used to identify + * an argument on the command line. The _flag can be blank, + * in fact this is how unlabeled args work. Unlabeled args must + * override appropriate functions to get correct handling. Note + * that the _flag does NOT include the dash as part of the flag. + */ + std::string _flag; + + /** + * A single work namd indentifying the argument. + * This value (preceded by two dashed {--}) can also be used + * to identify an argument on the command line. Note that the + * _name does NOT include the two dashes as part of the _name. The + * _name cannot be blank. + */ + std::string _name; + + /** + * Description of the argument. + */ + std::string _description; + + /** + * Indicating whether the argument is required. + */ + bool _required; + + /** + * Label to be used in usage description. Normally set to + * "required", but can be changed when necessary. + */ + std::string _requireLabel; + + /** + * Indicates whether a value is required for the argument. + * Note that the value may be required but the argument/value + * combination may not be, as specified by _required. + */ + bool _valueRequired; + + /** + * Indicates whether the argument has been set. + * Indicates that a value on the command line has matched the + * name/flag of this argument and the values have been set accordingly. + */ + bool _alreadySet; + + /** + * A pointer to a vistitor object. + * The visitor allows special handling to occur as soon as the + * argument is matched. This defaults to NULL and should not + * be used unless absolutely necessary. + */ + Visitor* _visitor; + + /** + * Whether this argument can be ignored, if desired. + */ + bool _ignoreable; + + /** + * Indicates that the arg was set as part of an XOR and not on the + * command line. + */ + bool _xorSet; + + bool _acceptsMultipleValues; + + /** + * Performs the special handling described by the Vistitor. + */ + void _checkWithVisitor() const; + + /** + * Primary constructor. YOU (yes you) should NEVER construct an Arg + * directly, this is a base class that is extended by various children + * that are meant to be used. Use SwitchArg, ValueArg, MultiArg, + * UnlabeledValueArg, or UnlabeledMultiArg instead. + * + * \param flag - The flag identifying the argument. + * \param name - The name identifying the argument. + * \param desc - The description of the argument, used in the usage. + * \param req - Whether the argument is required. + * \param valreq - Whether the a value is required for the argument. + * \param v - The visitor checked by the argument. Defaults to NULL. + */ + Arg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + bool valreq, + Visitor* v = NULL ); + + public: + /** + * Destructor. + */ + virtual ~Arg(); + + /** + * Adds this to the specified list of Args. + * \param argList - The list to add this to. + */ + virtual void addToList( std::list& argList ) const; + + /** + * Begin ignoring arguments since the "--" argument was specified. + */ + static void beginIgnoring() { ignoreRestRef() = true; } + + /** + * Whether to ignore the rest. + */ + static bool ignoreRest() { return ignoreRestRef(); } + + /** + * The delimiter that separates an argument flag/name from the + * value. + */ + static char delimiter() { return delimiterRef(); } + + /** + * The char used as a place holder when SwitchArgs are combined. + * Currently set to the bell char (ASCII 7). + */ + static char blankChar() { return (char)7; } + + /** + * The char that indicates the beginning of a flag. Defaults to '-', but + * clients can define TCLAP_FLAGSTARTCHAR to override. + */ +#ifndef TCLAP_FLAGSTARTCHAR +#define TCLAP_FLAGSTARTCHAR '-' +#endif + static char flagStartChar() { return TCLAP_FLAGSTARTCHAR; } + + /** + * The sting that indicates the beginning of a flag. Defaults to "-", but + * clients can define TCLAP_FLAGSTARTSTRING to override. Should be the same + * as TCLAP_FLAGSTARTCHAR. + */ +#ifndef TCLAP_FLAGSTARTSTRING +#define TCLAP_FLAGSTARTSTRING "-" +#endif + static const std::string flagStartString() { return TCLAP_FLAGSTARTSTRING; } + + /** + * The sting that indicates the beginning of a name. Defaults to "--", but + * clients can define TCLAP_NAMESTARTSTRING to override. + */ +#ifndef TCLAP_NAMESTARTSTRING +#define TCLAP_NAMESTARTSTRING "--" +#endif + static const std::string nameStartString() { return TCLAP_NAMESTARTSTRING; } + + /** + * The name used to identify the ignore rest argument. + */ + static const std::string ignoreNameString() { return "ignore_rest"; } + + /** + * Sets the delimiter for all arguments. + * \param c - The character that delimits flags/names from values. + */ + static void setDelimiter( char c ) { delimiterRef() = c; } + + /** + * Pure virtual method meant to handle the parsing and value assignment + * of the string on the command line. + * \param i - Pointer the the current argument in the list. + * \param args - Mutable list of strings. What is + * passed in from main. + */ + virtual bool processArg(int *i, std::vector& args) = 0; + + /** + * Operator ==. + * Equality operator. Must be virtual to handle unlabeled args. + * \param a - The Arg to be compared to this. + */ + virtual bool operator==(const Arg& a) const; + + /** + * Returns the argument flag. + */ + const std::string& getFlag() const; + + /** + * Returns the argument name. + */ + const std::string& getName() const; + + /** + * Returns the argument description. + */ + std::string getDescription() const; + + /** + * Indicates whether the argument is required. + */ + virtual bool isRequired() const; + + /** + * Sets _required to true. This is used by the XorHandler. + * You really have no reason to ever use it. + */ + void forceRequired(); + + /** + * Sets the _alreadySet value to true. This is used by the XorHandler. + * You really have no reason to ever use it. + */ + void xorSet(); + + /** + * Indicates whether a value must be specified for argument. + */ + bool isValueRequired() const; + + /** + * Indicates whether the argument has already been set. Only true + * if the arg has been matched on the command line. + */ + bool isSet() const; + + /** + * Indicates whether the argument can be ignored, if desired. + */ + bool isIgnoreable() const; + + /** + * A method that tests whether a string matches this argument. + * This is generally called by the processArg() method. This + * method could be re-implemented by a child to change how + * arguments are specified on the command line. + * \param s - The string to be compared to the flag/name to determine + * whether the arg matches. + */ + virtual bool argMatches( const std::string& s ) const; + + /** + * Returns a simple string representation of the argument. + * Primarily for debugging. + */ + virtual std::string toString() const; + + /** + * Returns a short ID for the usage. + * \param valueId - The value used in the id. + */ + virtual std::string shortID( const std::string& valueId = "val" ) const; + + /** + * Returns a long ID for the usage. + * \param valueId - The value used in the id. + */ + virtual std::string longID( const std::string& valueId = "val" ) const; + + /** + * Trims a value off of the flag. + * \param flag - The string from which the flag and value will be + * trimmed. Contains the flag once the value has been trimmed. + * \param value - Where the value trimmed from the string will + * be stored. + */ + virtual void trimFlag( std::string& flag, std::string& value ) const; + + /** + * Checks whether a given string has blank chars, indicating that + * it is a combined SwitchArg. If so, return true, otherwise return + * false. + * \param s - string to be checked. + */ + bool _hasBlanks( const std::string& s ) const; + + /** + * Sets the requireLabel. Used by XorHandler. You shouldn't ever + * use this. + * \param s - Set the requireLabel to this value. + */ + void setRequireLabel( const std::string& s ); + + /** + * Used for MultiArgs and XorHandler to determine whether args + * can still be set. + */ + virtual bool allowMore(); + + /** + * Use by output classes to determine whether an Arg accepts + * multiple values. + */ + virtual bool acceptsMultipleValues(); + + /** + * Clears the Arg object and allows it to be reused by new + * command lines. + */ + virtual void reset(); +}; + +/** + * Typedef of an Arg list iterator. + */ +typedef std::list::iterator ArgListIterator; + +/** + * Typedef of an Arg vector iterator. + */ +typedef std::vector::iterator ArgVectorIterator; + +/** + * Typedef of a Visitor list iterator. + */ +typedef std::list::iterator VisitorListIterator; + +/* + * Extract a value of type T from it's string representation contained + * in strVal. The ValueLike parameter used to select the correct + * specialization of ExtractValue depending on the value traits of T. + * ValueLike traits use operator>> to assign the value from strVal. + */ +template void +ExtractValue(T &destVal, const std::string& strVal, ValueLike vl) +{ + static_cast(vl); // Avoid warning about unused vl + std::istringstream is(strVal); + + int valuesRead = 0; + while ( is.good() ) { + if ( is.peek() != EOF ) +#ifdef TCLAP_SETBASE_ZERO + is >> std::setbase(0) >> destVal; +#else + is >> destVal; +#endif + else + break; + + valuesRead++; + } + + if ( is.fail() ) + throw( ArgParseException("Couldn't read argument value " + "from string '" + strVal + "'")); + + + if ( valuesRead > 1 ) + throw( ArgParseException("More than one valid value parsed from " + "string '" + strVal + "'")); + +} + +/* + * Extract a value of type T from it's string representation contained + * in strVal. The ValueLike parameter used to select the correct + * specialization of ExtractValue depending on the value traits of T. + * StringLike uses assignment (operator=) to assign from strVal. + */ +template void +ExtractValue(T &destVal, const std::string& strVal, StringLike sl) +{ + static_cast(sl); // Avoid warning about unused sl + SetString(destVal, strVal); +} + +////////////////////////////////////////////////////////////////////// +//BEGIN Arg.cpp +////////////////////////////////////////////////////////////////////// + +inline Arg::Arg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + bool valreq, + Visitor* v) : + _flag(flag), + _name(name), + _description(desc), + _required(req), + _requireLabel("required"), + _valueRequired(valreq), + _alreadySet(false), + _visitor( v ), + _ignoreable(true), + _xorSet(false), + _acceptsMultipleValues(false) +{ + if ( _flag.length() > 1 ) + throw(SpecificationException( + "Argument flag can only be one character long", toString() ) ); + + if ( _name != ignoreNameString() && + ( _flag == Arg::flagStartString() || + _flag == Arg::nameStartString() || + _flag == " " ) ) + throw(SpecificationException("Argument flag cannot be either '" + + Arg::flagStartString() + "' or '" + + Arg::nameStartString() + "' or a space.", + toString() ) ); + + if ( ( _name.substr( 0, Arg::flagStartString().length() ) == Arg::flagStartString() ) || + ( _name.substr( 0, Arg::nameStartString().length() ) == Arg::nameStartString() ) || + ( _name.find( " ", 0 ) != std::string::npos ) ) + throw(SpecificationException("Argument name begin with either '" + + Arg::flagStartString() + "' or '" + + Arg::nameStartString() + "' or space.", + toString() ) ); + +} + +inline Arg::~Arg() { } + +inline std::string Arg::shortID( const std::string& valueId ) const +{ + std::string id = ""; + + if ( _flag != "" ) + id = Arg::flagStartString() + _flag; + else + id = Arg::nameStartString() + _name; + + if ( _valueRequired ) + id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">"; + + if ( !_required ) + id = "[" + id + "]"; + + return id; +} + +inline std::string Arg::longID( const std::string& valueId ) const +{ + std::string id = ""; + + if ( _flag != "" ) + { + id += Arg::flagStartString() + _flag; + + if ( _valueRequired ) + id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">"; + + id += ", "; + } + + id += Arg::nameStartString() + _name; + + if ( _valueRequired ) + id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">"; + + return id; + +} + +inline bool Arg::operator==(const Arg& a) const +{ + if ( ( _flag != "" && _flag == a._flag ) || _name == a._name) + return true; + else + return false; +} + +inline std::string Arg::getDescription() const +{ + std::string desc = ""; + if ( _required ) + desc = "(" + _requireLabel + ") "; + +// if ( _valueRequired ) +// desc += "(value required) "; + + desc += _description; + return desc; +} + +inline const std::string& Arg::getFlag() const { return _flag; } + +inline const std::string& Arg::getName() const { return _name; } + +inline bool Arg::isRequired() const { return _required; } + +inline bool Arg::isValueRequired() const { return _valueRequired; } + +inline bool Arg::isSet() const +{ + if ( _alreadySet && !_xorSet ) + return true; + else + return false; +} + +inline bool Arg::isIgnoreable() const { return _ignoreable; } + +inline void Arg::setRequireLabel( const std::string& s) +{ + _requireLabel = s; +} + +inline bool Arg::argMatches( const std::string& argFlag ) const +{ + if ( ( argFlag == Arg::flagStartString() + _flag && _flag != "" ) || + argFlag == Arg::nameStartString() + _name ) + return true; + else + return false; +} + +inline std::string Arg::toString() const +{ + std::string s = ""; + + if ( _flag != "" ) + s += Arg::flagStartString() + _flag + " "; + + s += "(" + Arg::nameStartString() + _name + ")"; + + return s; +} + +inline void Arg::_checkWithVisitor() const +{ + if ( _visitor != NULL ) + _visitor->visit(); +} + +/** + * Implementation of trimFlag. + */ +inline void Arg::trimFlag(std::string& flag, std::string& value) const +{ + int stop = 0; + for ( int i = 0; static_cast(i) < flag.length(); i++ ) + if ( flag[i] == Arg::delimiter() ) + { + stop = i; + break; + } + + if ( stop > 1 ) + { + value = flag.substr(stop+1); + flag = flag.substr(0,stop); + } + +} + +/** + * Implementation of _hasBlanks. + */ +inline bool Arg::_hasBlanks( const std::string& s ) const +{ + for ( int i = 1; static_cast(i) < s.length(); i++ ) + if ( s[i] == Arg::blankChar() ) + return true; + + return false; +} + +inline void Arg::forceRequired() +{ + _required = true; +} + +inline void Arg::xorSet() +{ + _alreadySet = true; + _xorSet = true; +} + +/** + * Overridden by Args that need to added to the end of the list. + */ +inline void Arg::addToList( std::list& argList ) const +{ + argList.push_front( const_cast(this) ); +} + +inline bool Arg::allowMore() +{ + return false; +} + +inline bool Arg::acceptsMultipleValues() +{ + return _acceptsMultipleValues; +} + +inline void Arg::reset() +{ + _xorSet = false; + _alreadySet = false; +} + +////////////////////////////////////////////////////////////////////// +//END Arg.cpp +////////////////////////////////////////////////////////////////////// + +} //namespace TCLAP + +#endif + diff --git a/components/mkspiffs/src/tclap/ArgException.h b/components/mkspiffs/src/tclap/ArgException.h new file mode 100644 index 0000000..3411aa9 --- /dev/null +++ b/components/mkspiffs/src/tclap/ArgException.h @@ -0,0 +1,200 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: ArgException.h + * + * Copyright (c) 2003, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_ARG_EXCEPTION_H +#define TCLAP_ARG_EXCEPTION_H + +#include +#include + +namespace TCLAP { + +/** + * A simple class that defines and argument exception. Should be caught + * whenever a CmdLine is created and parsed. + */ +class ArgException : public std::exception +{ + public: + + /** + * Constructor. + * \param text - The text of the exception. + * \param id - The text identifying the argument source. + * \param td - Text describing the type of ArgException it is. + * of the exception. + */ + ArgException( const std::string& text = "undefined exception", + const std::string& id = "undefined", + const std::string& td = "Generic ArgException") + : std::exception(), + _errorText(text), + _argId( id ), + _typeDescription(td) + { } + + /** + * Destructor. + */ + virtual ~ArgException() throw() { } + + /** + * Returns the error text. + */ + std::string error() const { return ( _errorText ); } + + /** + * Returns the argument id. + */ + std::string argId() const + { + if ( _argId == "undefined" ) + return " "; + else + return ( "Argument: " + _argId ); + } + + /** + * Returns the arg id and error text. + */ + const char* what() const throw() + { + static std::string ex; + ex = _argId + " -- " + _errorText; + return ex.c_str(); + } + + /** + * Returns the type of the exception. Used to explain and distinguish + * between different child exceptions. + */ + std::string typeDescription() const + { + return _typeDescription; + } + + + private: + + /** + * The text of the exception message. + */ + std::string _errorText; + + /** + * The argument related to this exception. + */ + std::string _argId; + + /** + * Describes the type of the exception. Used to distinguish + * between different child exceptions. + */ + std::string _typeDescription; + +}; + +/** + * Thrown from within the child Arg classes when it fails to properly + * parse the argument it has been passed. + */ +class ArgParseException : public ArgException +{ + public: + /** + * Constructor. + * \param text - The text of the exception. + * \param id - The text identifying the argument source + * of the exception. + */ + ArgParseException( const std::string& text = "undefined exception", + const std::string& id = "undefined" ) + : ArgException( text, + id, + std::string( "Exception found while parsing " ) + + std::string( "the value the Arg has been passed." )) + { } +}; + +/** + * Thrown from CmdLine when the arguments on the command line are not + * properly specified, e.g. too many arguments, required argument missing, etc. + */ +class CmdLineParseException : public ArgException +{ + public: + /** + * Constructor. + * \param text - The text of the exception. + * \param id - The text identifying the argument source + * of the exception. + */ + CmdLineParseException( const std::string& text = "undefined exception", + const std::string& id = "undefined" ) + : ArgException( text, + id, + std::string( "Exception found when the values ") + + std::string( "on the command line do not meet ") + + std::string( "the requirements of the defined ") + + std::string( "Args." )) + { } +}; + +/** + * Thrown from Arg and CmdLine when an Arg is improperly specified, e.g. + * same flag as another Arg, same name, etc. + */ +class SpecificationException : public ArgException +{ + public: + /** + * Constructor. + * \param text - The text of the exception. + * \param id - The text identifying the argument source + * of the exception. + */ + SpecificationException( const std::string& text = "undefined exception", + const std::string& id = "undefined" ) + : ArgException( text, + id, + std::string("Exception found when an Arg object ")+ + std::string("is improperly defined by the ") + + std::string("developer." )) + { } + +}; + +class ExitException { +public: + ExitException(int estat) : _estat(estat) {} + + int getExitStatus() const { return _estat; } + +private: + int _estat; +}; + +} // namespace TCLAP + +#endif + diff --git a/components/mkspiffs/src/tclap/ArgTraits.h b/components/mkspiffs/src/tclap/ArgTraits.h new file mode 100644 index 0000000..0b2c18f --- /dev/null +++ b/components/mkspiffs/src/tclap/ArgTraits.h @@ -0,0 +1,87 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: ArgTraits.h + * + * Copyright (c) 2007, Daniel Aarno, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +// This is an internal tclap file, you should probably not have to +// include this directly + +#ifndef TCLAP_ARGTRAITS_H +#define TCLAP_ARGTRAITS_H + +namespace TCLAP { + +// We use two empty structs to get compile type specialization +// function to work + +/** + * A value like argument value type is a value that can be set using + * operator>>. This is the default value type. + */ +struct ValueLike { + typedef ValueLike ValueCategory; + virtual ~ValueLike() {} +}; + +/** + * A string like argument value type is a value that can be set using + * operator=(string). Usefull if the value type contains spaces which + * will be broken up into individual tokens by operator>>. + */ +struct StringLike { + virtual ~StringLike() {} +}; + +/** + * A class can inherit from this object to make it have string like + * traits. This is a compile time thing and does not add any overhead + * to the inherenting class. + */ +struct StringLikeTrait { + typedef StringLike ValueCategory; + virtual ~StringLikeTrait() {} +}; + +/** + * A class can inherit from this object to make it have value like + * traits. This is a compile time thing and does not add any overhead + * to the inherenting class. + */ +struct ValueLikeTrait { + typedef ValueLike ValueCategory; + virtual ~ValueLikeTrait() {} +}; + +/** + * Arg traits are used to get compile type specialization when parsing + * argument values. Using an ArgTraits you can specify the way that + * values gets assigned to any particular type during parsing. The two + * supported types are StringLike and ValueLike. + */ +template +struct ArgTraits { + typedef typename T::ValueCategory ValueCategory; + virtual ~ArgTraits() {} + //typedef ValueLike ValueCategory; +}; + +#endif + +} // namespace diff --git a/components/mkspiffs/src/tclap/COPYING b/components/mkspiffs/src/tclap/COPYING new file mode 100644 index 0000000..987be0c --- /dev/null +++ b/components/mkspiffs/src/tclap/COPYING @@ -0,0 +1,25 @@ + + +Copyright (c) 2003 Michael E. Smoot + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + diff --git a/components/mkspiffs/src/tclap/CmdLine.h b/components/mkspiffs/src/tclap/CmdLine.h new file mode 100644 index 0000000..0e7dda3 --- /dev/null +++ b/components/mkspiffs/src/tclap/CmdLine.h @@ -0,0 +1,633 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: CmdLine.h + * + * Copyright (c) 2003, Michael E. Smoot . + * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_CMDLINE_H +#define TCLAP_CMDLINE_H + +#include "SwitchArg.h" +#include "MultiSwitchArg.h" +#include "UnlabeledValueArg.h" +#include "UnlabeledMultiArg.h" + +#include "XorHandler.h" +#include "HelpVisitor.h" +#include "VersionVisitor.h" +#include "IgnoreRestVisitor.h" + +#include "CmdLineOutput.h" +#include "StdOutput.h" + +#include "Constraint.h" +#include "ValuesConstraint.h" + +#include +#include +#include +#include +#include +#include +#include // Needed for exit(), which isn't defined in some envs. + +namespace TCLAP { + +template void DelPtr(T ptr) +{ + delete ptr; +} + +template void ClearContainer(C &c) +{ + typedef typename C::value_type value_type; + std::for_each(c.begin(), c.end(), DelPtr); + c.clear(); +} + + +/** + * The base class that manages the command line definition and passes + * along the parsing to the appropriate Arg classes. + */ +class CmdLine : public CmdLineInterface +{ + protected: + + /** + * The list of arguments that will be tested against the + * command line. + */ + std::list _argList; + + /** + * The name of the program. Set to argv[0]. + */ + std::string _progName; + + /** + * A message used to describe the program. Used in the usage output. + */ + std::string _message; + + /** + * The version to be displayed with the --version switch. + */ + std::string _version; + + /** + * The number of arguments that are required to be present on + * the command line. This is set dynamically, based on the + * Args added to the CmdLine object. + */ + int _numRequired; + + /** + * The character that is used to separate the argument flag/name + * from the value. Defaults to ' ' (space). + */ + char _delimiter; + + /** + * The handler that manages xoring lists of args. + */ + XorHandler _xorHandler; + + /** + * A list of Args to be explicitly deleted when the destructor + * is called. At the moment, this only includes the three default + * Args. + */ + std::list _argDeleteOnExitList; + + /** + * A list of Visitors to be explicitly deleted when the destructor + * is called. At the moment, these are the Vistors created for the + * default Args. + */ + std::list _visitorDeleteOnExitList; + + /** + * Object that handles all output for the CmdLine. + */ + CmdLineOutput* _output; + + /** + * Should CmdLine handle parsing exceptions internally? + */ + bool _handleExceptions; + + /** + * Throws an exception listing the missing args. + */ + void missingArgsException(); + + /** + * Checks whether a name/flag string matches entirely matches + * the Arg::blankChar. Used when multiple switches are combined + * into a single argument. + * \param s - The message to be used in the usage. + */ + bool _emptyCombined(const std::string& s); + + /** + * Perform a delete ptr; operation on ptr when this object is deleted. + */ + void deleteOnExit(Arg* ptr); + + /** + * Perform a delete ptr; operation on ptr when this object is deleted. + */ + void deleteOnExit(Visitor* ptr); + +private: + + /** + * Prevent accidental copying. + */ + CmdLine(const CmdLine& rhs); + CmdLine& operator=(const CmdLine& rhs); + + /** + * Encapsulates the code common to the constructors + * (which is all of it). + */ + void _constructor(); + + + /** + * Is set to true when a user sets the output object. We use this so + * that we don't delete objects that are created outside of this lib. + */ + bool _userSetOutput; + + /** + * Whether or not to automatically create help and version switches. + */ + bool _helpAndVersion; + + public: + + /** + * Command line constructor. Defines how the arguments will be + * parsed. + * \param message - The message to be used in the usage + * output. + * \param delimiter - The character that is used to separate + * the argument flag/name from the value. Defaults to ' ' (space). + * \param version - The version number to be used in the + * --version switch. + * \param helpAndVersion - Whether or not to create the Help and + * Version switches. Defaults to true. + */ + CmdLine(const std::string& message, + const char delimiter = ' ', + const std::string& version = "none", + bool helpAndVersion = true); + + /** + * Deletes any resources allocated by a CmdLine object. + */ + virtual ~CmdLine(); + + /** + * Adds an argument to the list of arguments to be parsed. + * \param a - Argument to be added. + */ + void add( Arg& a ); + + /** + * An alternative add. Functionally identical. + * \param a - Argument to be added. + */ + void add( Arg* a ); + + /** + * Add two Args that will be xor'd. If this method is used, add does + * not need to be called. + * \param a - Argument to be added and xor'd. + * \param b - Argument to be added and xor'd. + */ + void xorAdd( Arg& a, Arg& b ); + + /** + * Add a list of Args that will be xor'd. If this method is used, + * add does not need to be called. + * \param xors - List of Args to be added and xor'd. + */ + void xorAdd( std::vector& xors ); + + /** + * Parses the command line. + * \param argc - Number of arguments. + * \param argv - Array of arguments. + */ + void parse(int argc, const char * const * argv); + + /** + * Parses the command line. + * \param args - A vector of strings representing the args. + * args[0] is still the program name. + */ + void parse(std::vector& args); + + /** + * + */ + CmdLineOutput* getOutput(); + + /** + * + */ + void setOutput(CmdLineOutput* co); + + /** + * + */ + std::string& getVersion(); + + /** + * + */ + std::string& getProgramName(); + + /** + * + */ + std::list& getArgList(); + + /** + * + */ + XorHandler& getXorHandler(); + + /** + * + */ + char getDelimiter(); + + /** + * + */ + std::string& getMessage(); + + /** + * + */ + bool hasHelpAndVersion(); + + /** + * Disables or enables CmdLine's internal parsing exception handling. + * + * @param state Should CmdLine handle parsing exceptions internally? + */ + void setExceptionHandling(const bool state); + + /** + * Returns the current state of the internal exception handling. + * + * @retval true Parsing exceptions are handled internally. + * @retval false Parsing exceptions are propagated to the caller. + */ + bool getExceptionHandling() const; + + /** + * Allows the CmdLine object to be reused. + */ + void reset(); + +}; + + +/////////////////////////////////////////////////////////////////////////////// +//Begin CmdLine.cpp +/////////////////////////////////////////////////////////////////////////////// + +inline CmdLine::CmdLine(const std::string& m, + char delim, + const std::string& v, + bool help ) + : + _argList(std::list()), + _progName("not_set_yet"), + _message(m), + _version(v), + _numRequired(0), + _delimiter(delim), + _xorHandler(XorHandler()), + _argDeleteOnExitList(std::list()), + _visitorDeleteOnExitList(std::list()), + _output(0), + _handleExceptions(true), + _userSetOutput(false), + _helpAndVersion(help) +{ + _constructor(); +} + +inline CmdLine::~CmdLine() +{ + ClearContainer(_argDeleteOnExitList); + ClearContainer(_visitorDeleteOnExitList); + + if ( !_userSetOutput ) { + delete _output; + _output = 0; + } +} + +inline void CmdLine::_constructor() +{ + _output = new StdOutput; + + Arg::setDelimiter( _delimiter ); + + Visitor* v; + + if ( _helpAndVersion ) + { + v = new HelpVisitor( this, &_output ); + SwitchArg* help = new SwitchArg("h","help", + "Displays usage information and exits.", + false, v); + add( help ); + deleteOnExit(help); + deleteOnExit(v); + + v = new VersionVisitor( this, &_output ); + SwitchArg* vers = new SwitchArg("","version", + "Displays version information and exits.", + false, v); + add( vers ); + deleteOnExit(vers); + deleteOnExit(v); + } + + v = new IgnoreRestVisitor(); + SwitchArg* ignore = new SwitchArg(Arg::flagStartString(), + Arg::ignoreNameString(), + "Ignores the rest of the labeled arguments following this flag.", + false, v); + add( ignore ); + deleteOnExit(ignore); + deleteOnExit(v); +} + +inline void CmdLine::xorAdd( std::vector& ors ) +{ + _xorHandler.add( ors ); + + for (ArgVectorIterator it = ors.begin(); it != ors.end(); it++) + { + (*it)->forceRequired(); + (*it)->setRequireLabel( "OR required" ); + add( *it ); + } +} + +inline void CmdLine::xorAdd( Arg& a, Arg& b ) +{ + std::vector ors; + ors.push_back( &a ); + ors.push_back( &b ); + xorAdd( ors ); +} + +inline void CmdLine::add( Arg& a ) +{ + add( &a ); +} + +inline void CmdLine::add( Arg* a ) +{ + for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ ) + if ( *a == *(*it) ) + throw( SpecificationException( + "Argument with same flag/name already exists!", + a->longID() ) ); + + a->addToList( _argList ); + + if ( a->isRequired() ) + _numRequired++; +} + + +inline void CmdLine::parse(int argc, const char * const * argv) +{ + // this step is necessary so that we have easy access to + // mutable strings. + std::vector args; + for (int i = 0; i < argc; i++) + args.push_back(argv[i]); + + parse(args); +} + +inline void CmdLine::parse(std::vector& args) +{ + bool shouldExit = false; + int estat = 0; + + try { + _progName = args.front(); + args.erase(args.begin()); + + int requiredCount = 0; + + for (int i = 0; static_cast(i) < args.size(); i++) + { + bool matched = false; + for (ArgListIterator it = _argList.begin(); + it != _argList.end(); it++) { + if ( (*it)->processArg( &i, args ) ) + { + requiredCount += _xorHandler.check( *it ); + matched = true; + break; + } + } + + // checks to see if the argument is an empty combined + // switch and if so, then we've actually matched it + if ( !matched && _emptyCombined( args[i] ) ) + matched = true; + + if ( !matched && !Arg::ignoreRest() ) + throw(CmdLineParseException("Couldn't find match " + "for argument", + args[i])); + } + + if ( requiredCount < _numRequired ) + missingArgsException(); + + if ( requiredCount > _numRequired ) + throw(CmdLineParseException("Too many arguments!")); + + } catch ( ArgException& e ) { + // If we're not handling the exceptions, rethrow. + if ( !_handleExceptions) { + throw; + } + + try { + _output->failure(*this,e); + } catch ( ExitException &ee ) { + estat = ee.getExitStatus(); + shouldExit = true; + } + } catch (ExitException &ee) { + // If we're not handling the exceptions, rethrow. + if ( !_handleExceptions) { + throw; + } + + estat = ee.getExitStatus(); + shouldExit = true; + } + + if (shouldExit) + exit(estat); +} + +inline bool CmdLine::_emptyCombined(const std::string& s) +{ + if ( s.length() > 0 && s[0] != Arg::flagStartChar() ) + return false; + + for ( int i = 1; static_cast(i) < s.length(); i++ ) + if ( s[i] != Arg::blankChar() ) + return false; + + return true; +} + +inline void CmdLine::missingArgsException() +{ + int count = 0; + + std::string missingArgList; + for (ArgListIterator it = _argList.begin(); it != _argList.end(); it++) + { + if ( (*it)->isRequired() && !(*it)->isSet() ) + { + missingArgList += (*it)->getName(); + missingArgList += ", "; + count++; + } + } + missingArgList = missingArgList.substr(0,missingArgList.length()-2); + + std::string msg; + if ( count > 1 ) + msg = "Required arguments missing: "; + else + msg = "Required argument missing: "; + + msg += missingArgList; + + throw(CmdLineParseException(msg)); +} + +inline void CmdLine::deleteOnExit(Arg* ptr) +{ + _argDeleteOnExitList.push_back(ptr); +} + +inline void CmdLine::deleteOnExit(Visitor* ptr) +{ + _visitorDeleteOnExitList.push_back(ptr); +} + +inline CmdLineOutput* CmdLine::getOutput() +{ + return _output; +} + +inline void CmdLine::setOutput(CmdLineOutput* co) +{ + if ( !_userSetOutput ) + delete _output; + _userSetOutput = true; + _output = co; +} + +inline std::string& CmdLine::getVersion() +{ + return _version; +} + +inline std::string& CmdLine::getProgramName() +{ + return _progName; +} + +inline std::list& CmdLine::getArgList() +{ + return _argList; +} + +inline XorHandler& CmdLine::getXorHandler() +{ + return _xorHandler; +} + +inline char CmdLine::getDelimiter() +{ + return _delimiter; +} + +inline std::string& CmdLine::getMessage() +{ + return _message; +} + +inline bool CmdLine::hasHelpAndVersion() +{ + return _helpAndVersion; +} + +inline void CmdLine::setExceptionHandling(const bool state) +{ + _handleExceptions = state; +} + +inline bool CmdLine::getExceptionHandling() const +{ + return _handleExceptions; +} + +inline void CmdLine::reset() +{ + for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ ) + (*it)->reset(); + + _progName.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +//End CmdLine.cpp +/////////////////////////////////////////////////////////////////////////////// + + + +} //namespace TCLAP +#endif diff --git a/components/mkspiffs/src/tclap/CmdLineInterface.h b/components/mkspiffs/src/tclap/CmdLineInterface.h new file mode 100644 index 0000000..1b25e9b --- /dev/null +++ b/components/mkspiffs/src/tclap/CmdLineInterface.h @@ -0,0 +1,150 @@ + +/****************************************************************************** + * + * file: CmdLineInterface.h + * + * Copyright (c) 2003, Michael E. Smoot . + * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_COMMANDLINE_INTERFACE_H +#define TCLAP_COMMANDLINE_INTERFACE_H + +#include +#include +#include +#include +#include + + +namespace TCLAP { + +class Arg; +class CmdLineOutput; +class XorHandler; + +/** + * The base class that manages the command line definition and passes + * along the parsing to the appropriate Arg classes. + */ +class CmdLineInterface +{ + public: + + /** + * Destructor + */ + virtual ~CmdLineInterface() {} + + /** + * Adds an argument to the list of arguments to be parsed. + * \param a - Argument to be added. + */ + virtual void add( Arg& a )=0; + + /** + * An alternative add. Functionally identical. + * \param a - Argument to be added. + */ + virtual void add( Arg* a )=0; + + /** + * Add two Args that will be xor'd. + * If this method is used, add does + * not need to be called. + * \param a - Argument to be added and xor'd. + * \param b - Argument to be added and xor'd. + */ + virtual void xorAdd( Arg& a, Arg& b )=0; + + /** + * Add a list of Args that will be xor'd. If this method is used, + * add does not need to be called. + * \param xors - List of Args to be added and xor'd. + */ + virtual void xorAdd( std::vector& xors )=0; + + /** + * Parses the command line. + * \param argc - Number of arguments. + * \param argv - Array of arguments. + */ + virtual void parse(int argc, const char * const * argv)=0; + + /** + * Parses the command line. + * \param args - A vector of strings representing the args. + * args[0] is still the program name. + */ + void parse(std::vector& args); + + /** + * Returns the CmdLineOutput object. + */ + virtual CmdLineOutput* getOutput()=0; + + /** + * \param co - CmdLineOutput object that we want to use instead. + */ + virtual void setOutput(CmdLineOutput* co)=0; + + /** + * Returns the version string. + */ + virtual std::string& getVersion()=0; + + /** + * Returns the program name string. + */ + virtual std::string& getProgramName()=0; + + /** + * Returns the argList. + */ + virtual std::list& getArgList()=0; + + /** + * Returns the XorHandler. + */ + virtual XorHandler& getXorHandler()=0; + + /** + * Returns the delimiter string. + */ + virtual char getDelimiter()=0; + + /** + * Returns the message string. + */ + virtual std::string& getMessage()=0; + + /** + * Indicates whether or not the help and version switches were created + * automatically. + */ + virtual bool hasHelpAndVersion()=0; + + /** + * Resets the instance as if it had just been constructed so that the + * instance can be reused. + */ + virtual void reset()=0; +}; + +} //namespace + + +#endif diff --git a/components/mkspiffs/src/tclap/CmdLineOutput.h b/components/mkspiffs/src/tclap/CmdLineOutput.h new file mode 100644 index 0000000..71ee5a3 --- /dev/null +++ b/components/mkspiffs/src/tclap/CmdLineOutput.h @@ -0,0 +1,74 @@ + + +/****************************************************************************** + * + * file: CmdLineOutput.h + * + * Copyright (c) 2004, Michael E. Smoot + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_CMDLINEOUTPUT_H +#define TCLAP_CMDLINEOUTPUT_H + +#include +#include +#include +#include +#include +#include + +namespace TCLAP { + +class CmdLineInterface; +class ArgException; + +/** + * The interface that any output object must implement. + */ +class CmdLineOutput +{ + + public: + + /** + * Virtual destructor. + */ + virtual ~CmdLineOutput() {} + + /** + * Generates some sort of output for the USAGE. + * \param c - The CmdLine object the output is generated for. + */ + virtual void usage(CmdLineInterface& c)=0; + + /** + * Generates some sort of output for the version. + * \param c - The CmdLine object the output is generated for. + */ + virtual void version(CmdLineInterface& c)=0; + + /** + * Generates some sort of output for a failure. + * \param c - The CmdLine object the output is generated for. + * \param e - The ArgException that caused the failure. + */ + virtual void failure( CmdLineInterface& c, + ArgException& e )=0; + +}; + +} //namespace TCLAP +#endif diff --git a/components/mkspiffs/src/tclap/Constraint.h b/components/mkspiffs/src/tclap/Constraint.h new file mode 100644 index 0000000..a92acf9 --- /dev/null +++ b/components/mkspiffs/src/tclap/Constraint.h @@ -0,0 +1,68 @@ + +/****************************************************************************** + * + * file: Constraint.h + * + * Copyright (c) 2005, Michael E. Smoot + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_CONSTRAINT_H +#define TCLAP_CONSTRAINT_H + +#include +#include +#include +#include +#include +#include + +namespace TCLAP { + +/** + * The interface that defines the interaction between the Arg and Constraint. + */ +template +class Constraint +{ + + public: + /** + * Returns a description of the Constraint. + */ + virtual std::string description() const =0; + + /** + * Returns the short ID for the Constraint. + */ + virtual std::string shortID() const =0; + + /** + * The method used to verify that the value parsed from the command + * line meets the constraint. + * \param value - The value that will be checked. + */ + virtual bool check(const T& value) const =0; + + /** + * Destructor. + * Silences warnings about Constraint being a base class with virtual + * functions but without a virtual destructor. + */ + virtual ~Constraint() { ; } +}; + +} //namespace TCLAP +#endif diff --git a/components/mkspiffs/src/tclap/DocBookOutput.h b/components/mkspiffs/src/tclap/DocBookOutput.h new file mode 100644 index 0000000..84abf46 --- /dev/null +++ b/components/mkspiffs/src/tclap/DocBookOutput.h @@ -0,0 +1,299 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: DocBookOutput.h + * + * Copyright (c) 2004, Michael E. Smoot + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_DOCBOOKOUTPUT_H +#define TCLAP_DOCBOOKOUTPUT_H + +#include +#include +#include +#include +#include + +#include "CmdLineInterface.h" +#include "CmdLineOutput.h" +#include "XorHandler.h" +#include "Arg.h" + +namespace TCLAP { + +/** + * A class that generates DocBook output for usage() method for the + * given CmdLine and its Args. + */ +class DocBookOutput : public CmdLineOutput +{ + + public: + + /** + * Prints the usage to stdout. Can be overridden to + * produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + */ + virtual void usage(CmdLineInterface& c); + + /** + * Prints the version to stdout. Can be overridden + * to produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + */ + virtual void version(CmdLineInterface& c); + + /** + * Prints (to stderr) an error message, short usage + * Can be overridden to produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + * \param e - The ArgException that caused the failure. + */ + virtual void failure(CmdLineInterface& c, + ArgException& e ); + + protected: + + /** + * Substitutes the char r for string x in string s. + * \param s - The string to operate on. + * \param r - The char to replace. + * \param x - What to replace r with. + */ + void substituteSpecialChars( std::string& s, char r, std::string& x ); + void removeChar( std::string& s, char r); + void basename( std::string& s ); + + void printShortArg(Arg* it); + void printLongArg(Arg* it); + + char theDelimiter; +}; + + +inline void DocBookOutput::version(CmdLineInterface& _cmd) +{ + std::cout << _cmd.getVersion() << std::endl; +} + +inline void DocBookOutput::usage(CmdLineInterface& _cmd ) +{ + std::list argList = _cmd.getArgList(); + std::string progName = _cmd.getProgramName(); + std::string xversion = _cmd.getVersion(); + theDelimiter = _cmd.getDelimiter(); + XorHandler xorHandler = _cmd.getXorHandler(); + std::vector< std::vector > xorList = xorHandler.getXorList(); + basename(progName); + + std::cout << "" << std::endl; + std::cout << "" << std::endl << std::endl; + + std::cout << "" << std::endl; + + std::cout << "" << std::endl; + std::cout << "" << progName << "" << std::endl; + std::cout << "1" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << std::endl; + std::cout << "" << progName << "" << std::endl; + std::cout << "" << _cmd.getMessage() << "" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << progName << "" << std::endl; + + // xor + for ( int i = 0; (unsigned int)i < xorList.size(); i++ ) + { + std::cout << "" << std::endl; + for ( ArgVectorIterator it = xorList[i].begin(); + it != xorList[i].end(); it++ ) + printShortArg((*it)); + + std::cout << "" << std::endl; + } + + // rest of args + for (ArgListIterator it = argList.begin(); it != argList.end(); it++) + if ( !xorHandler.contains( (*it) ) ) + printShortArg((*it)); + + std::cout << "" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << std::endl; + std::cout << "Description" << std::endl; + std::cout << "" << std::endl; + std::cout << _cmd.getMessage() << std::endl; + std::cout << "" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << std::endl; + std::cout << "Options" << std::endl; + + std::cout << "" << std::endl; + + for (ArgListIterator it = argList.begin(); it != argList.end(); it++) + printLongArg((*it)); + + std::cout << "" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << std::endl; + std::cout << "Version" << std::endl; + std::cout << "" << std::endl; + std::cout << xversion << std::endl; + std::cout << "" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << std::endl; + +} + +inline void DocBookOutput::failure( CmdLineInterface& _cmd, + ArgException& e ) +{ + static_cast(_cmd); // unused + std::cout << e.what() << std::endl; + throw ExitException(1); +} + +inline void DocBookOutput::substituteSpecialChars( std::string& s, + char r, + std::string& x ) +{ + size_t p; + while ( (p = s.find_first_of(r)) != std::string::npos ) + { + s.erase(p,1); + s.insert(p,x); + } +} + +inline void DocBookOutput::removeChar( std::string& s, char r) +{ + size_t p; + while ( (p = s.find_first_of(r)) != std::string::npos ) + { + s.erase(p,1); + } +} + +inline void DocBookOutput::basename( std::string& s ) +{ + size_t p = s.find_last_of('/'); + if ( p != std::string::npos ) + { + s.erase(0, p + 1); + } +} + +inline void DocBookOutput::printShortArg(Arg* a) +{ + std::string lt = "<"; + std::string gt = ">"; + + std::string id = a->shortID(); + substituteSpecialChars(id,'<',lt); + substituteSpecialChars(id,'>',gt); + removeChar(id,'['); + removeChar(id,']'); + + std::string choice = "opt"; + if ( a->isRequired() ) + choice = "plain"; + + std::cout << "acceptsMultipleValues() ) + std::cout << " rep='repeat'"; + + + std::cout << '>'; + if ( !a->getFlag().empty() ) + std::cout << a->flagStartChar() << a->getFlag(); + else + std::cout << a->nameStartString() << a->getName(); + if ( a->isValueRequired() ) + { + std::string arg = a->shortID(); + removeChar(arg,'['); + removeChar(arg,']'); + removeChar(arg,'<'); + removeChar(arg,'>'); + arg.erase(0, arg.find_last_of(theDelimiter) + 1); + std::cout << theDelimiter; + std::cout << "" << arg << ""; + } + std::cout << "" << std::endl; + +} + +inline void DocBookOutput::printLongArg(Arg* a) +{ + std::string lt = "<"; + std::string gt = ">"; + + std::string desc = a->getDescription(); + substituteSpecialChars(desc,'<',lt); + substituteSpecialChars(desc,'>',gt); + + std::cout << "" << std::endl; + + if ( !a->getFlag().empty() ) + { + std::cout << "" << std::endl; + std::cout << "" << std::endl; + std::cout << "" << std::endl; + } + + std::cout << "" << std::endl; + std::cout << "" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << std::endl; + std::cout << "" << std::endl; + std::cout << desc << std::endl; + std::cout << "" << std::endl; + std::cout << "" << std::endl; + + std::cout << "" << std::endl; +} + +} //namespace TCLAP +#endif diff --git a/components/mkspiffs/src/tclap/HelpVisitor.h b/components/mkspiffs/src/tclap/HelpVisitor.h new file mode 100644 index 0000000..076c640 --- /dev/null +++ b/components/mkspiffs/src/tclap/HelpVisitor.h @@ -0,0 +1,76 @@ + +/****************************************************************************** + * + * file: HelpVisitor.h + * + * Copyright (c) 2003, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_HELP_VISITOR_H +#define TCLAP_HELP_VISITOR_H + +#include "CmdLineInterface.h" +#include "CmdLineOutput.h" +#include "Visitor.h" + +namespace TCLAP { + +/** + * A Visitor object that calls the usage method of the given CmdLineOutput + * object for the specified CmdLine object. + */ +class HelpVisitor: public Visitor +{ + private: + /** + * Prevent accidental copying. + */ + HelpVisitor(const HelpVisitor& rhs); + HelpVisitor& operator=(const HelpVisitor& rhs); + + protected: + + /** + * The CmdLine the output will be generated for. + */ + CmdLineInterface* _cmd; + + /** + * The output object. + */ + CmdLineOutput** _out; + + public: + + /** + * Constructor. + * \param cmd - The CmdLine the output will be generated for. + * \param out - The type of output. + */ + HelpVisitor(CmdLineInterface* cmd, CmdLineOutput** out) + : Visitor(), _cmd( cmd ), _out( out ) { } + + /** + * Calls the usage method of the CmdLineOutput for the + * specified CmdLine. + */ + void visit() { (*_out)->usage(*_cmd); throw ExitException(0); } + +}; + +} + +#endif diff --git a/components/mkspiffs/src/tclap/IgnoreRestVisitor.h b/components/mkspiffs/src/tclap/IgnoreRestVisitor.h new file mode 100644 index 0000000..09bdba7 --- /dev/null +++ b/components/mkspiffs/src/tclap/IgnoreRestVisitor.h @@ -0,0 +1,52 @@ + +/****************************************************************************** + * + * file: IgnoreRestVisitor.h + * + * Copyright (c) 2003, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_IGNORE_REST_VISITOR_H +#define TCLAP_IGNORE_REST_VISITOR_H + +#include "Visitor.h" +#include "Arg.h" + +namespace TCLAP { + +/** + * A Vistor that tells the CmdLine to begin ignoring arguments after + * this one is parsed. + */ +class IgnoreRestVisitor: public Visitor +{ + public: + + /** + * Constructor. + */ + IgnoreRestVisitor() : Visitor() {} + + /** + * Sets Arg::_ignoreRest. + */ + void visit() { Arg::beginIgnoring(); } +}; + +} + +#endif diff --git a/components/mkspiffs/src/tclap/MultiArg.h b/components/mkspiffs/src/tclap/MultiArg.h new file mode 100644 index 0000000..63c88c7 --- /dev/null +++ b/components/mkspiffs/src/tclap/MultiArg.h @@ -0,0 +1,433 @@ +/****************************************************************************** + * + * file: MultiArg.h + * + * Copyright (c) 2003, Michael E. Smoot . + * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_MULTIPLE_ARGUMENT_H +#define TCLAP_MULTIPLE_ARGUMENT_H + +#include +#include + +#include "Arg.h" +#include "Constraint.h" + +namespace TCLAP { +/** + * An argument that allows multiple values of type T to be specified. Very + * similar to a ValueArg, except a vector of values will be returned + * instead of just one. + */ +template +class MultiArg : public Arg +{ +public: + typedef std::vector container_type; + typedef typename container_type::iterator iterator; + typedef typename container_type::const_iterator const_iterator; + +protected: + + /** + * The list of values parsed from the CmdLine. + */ + std::vector _values; + + /** + * The description of type T to be used in the usage. + */ + std::string _typeDesc; + + /** + * A list of constraint on this Arg. + */ + Constraint* _constraint; + + /** + * Extracts the value from the string. + * Attempts to parse string as type T, if this fails an exception + * is thrown. + * \param val - The string to be read. + */ + void _extractValue( const std::string& val ); + + /** + * Used by XorHandler to decide whether to keep parsing for this arg. + */ + bool _allowMore; + +public: + + /** + * Constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param typeDesc - A short, human readable description of the + * type that this object expects. This is used in the generation + * of the USAGE statement. The goal is to be helpful to the end user + * of the program. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + MultiArg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + const std::string& typeDesc, + Visitor* v = NULL); + + /** + * Constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param typeDesc - A short, human readable description of the + * type that this object expects. This is used in the generation + * of the USAGE statement. The goal is to be helpful to the end user + * of the program. + * \param parser - A CmdLine parser object to add this Arg to + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + MultiArg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + const std::string& typeDesc, + CmdLineInterface& parser, + Visitor* v = NULL ); + + /** + * Constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param constraint - A pointer to a Constraint object used + * to constrain this Arg. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + MultiArg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + Constraint* constraint, + Visitor* v = NULL ); + + /** + * Constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param constraint - A pointer to a Constraint object used + * to constrain this Arg. + * \param parser - A CmdLine parser object to add this Arg to + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + MultiArg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + Constraint* constraint, + CmdLineInterface& parser, + Visitor* v = NULL ); + + /** + * Handles the processing of the argument. + * This re-implements the Arg version of this method to set the + * _value of the argument appropriately. It knows the difference + * between labeled and unlabeled. + * \param i - Pointer the the current argument in the list. + * \param args - Mutable list of strings. Passed from main(). + */ + virtual bool processArg(int* i, std::vector& args); + + /** + * Returns a vector of type T containing the values parsed from + * the command line. + */ + const std::vector& getValue(); + + /** + * Returns an iterator over the values parsed from the command + * line. + */ + const_iterator begin() const { return _values.begin(); } + + /** + * Returns the end of the values parsed from the command + * line. + */ + const_iterator end() const { return _values.end(); } + + /** + * Returns the a short id string. Used in the usage. + * \param val - value to be used. + */ + virtual std::string shortID(const std::string& val="val") const; + + /** + * Returns the a long id string. Used in the usage. + * \param val - value to be used. + */ + virtual std::string longID(const std::string& val="val") const; + + /** + * Once we've matched the first value, then the arg is no longer + * required. + */ + virtual bool isRequired() const; + + virtual bool allowMore(); + + virtual void reset(); + +private: + /** + * Prevent accidental copying + */ + MultiArg(const MultiArg& rhs); + MultiArg& operator=(const MultiArg& rhs); + +}; + +template +MultiArg::MultiArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + const std::string& typeDesc, + Visitor* v) : + Arg( flag, name, desc, req, true, v ), + _values(std::vector()), + _typeDesc( typeDesc ), + _constraint( NULL ), + _allowMore(false) +{ + _acceptsMultipleValues = true; +} + +template +MultiArg::MultiArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + const std::string& typeDesc, + CmdLineInterface& parser, + Visitor* v) +: Arg( flag, name, desc, req, true, v ), + _values(std::vector()), + _typeDesc( typeDesc ), + _constraint( NULL ), + _allowMore(false) +{ + parser.add( this ); + _acceptsMultipleValues = true; +} + +/** + * + */ +template +MultiArg::MultiArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + Constraint* constraint, + Visitor* v) +: Arg( flag, name, desc, req, true, v ), + _values(std::vector()), + _typeDesc( constraint->shortID() ), + _constraint( constraint ), + _allowMore(false) +{ + _acceptsMultipleValues = true; +} + +template +MultiArg::MultiArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + Constraint* constraint, + CmdLineInterface& parser, + Visitor* v) +: Arg( flag, name, desc, req, true, v ), + _values(std::vector()), + _typeDesc( constraint->shortID() ), + _constraint( constraint ), + _allowMore(false) +{ + parser.add( this ); + _acceptsMultipleValues = true; +} + +template +const std::vector& MultiArg::getValue() { return _values; } + +template +bool MultiArg::processArg(int *i, std::vector& args) +{ + if ( _ignoreable && Arg::ignoreRest() ) + return false; + + if ( _hasBlanks( args[*i] ) ) + return false; + + std::string flag = args[*i]; + std::string value = ""; + + trimFlag( flag, value ); + + if ( argMatches( flag ) ) + { + if ( Arg::delimiter() != ' ' && value == "" ) + throw( ArgParseException( + "Couldn't find delimiter for this argument!", + toString() ) ); + + // always take the first one, regardless of start string + if ( value == "" ) + { + (*i)++; + if ( static_cast(*i) < args.size() ) + _extractValue( args[*i] ); + else + throw( ArgParseException("Missing a value for this argument!", + toString() ) ); + } + else + _extractValue( value ); + + /* + // continuing taking the args until we hit one with a start string + while ( (unsigned int)(*i)+1 < args.size() && + args[(*i)+1].find_first_of( Arg::flagStartString() ) != 0 && + args[(*i)+1].find_first_of( Arg::nameStartString() ) != 0 ) + _extractValue( args[++(*i)] ); + */ + + _alreadySet = true; + _checkWithVisitor(); + + return true; + } + else + return false; +} + +/** + * + */ +template +std::string MultiArg::shortID(const std::string& val) const +{ + static_cast(val); // Ignore input, don't warn + return Arg::shortID(_typeDesc) + " ... "; +} + +/** + * + */ +template +std::string MultiArg::longID(const std::string& val) const +{ + static_cast(val); // Ignore input, don't warn + return Arg::longID(_typeDesc) + " (accepted multiple times)"; +} + +/** + * Once we've matched the first value, then the arg is no longer + * required. + */ +template +bool MultiArg::isRequired() const +{ + if ( _required ) + { + if ( _values.size() > 1 ) + return false; + else + return true; + } + else + return false; + +} + +template +void MultiArg::_extractValue( const std::string& val ) +{ + try { + T tmp; + ExtractValue(tmp, val, typename ArgTraits::ValueCategory()); + _values.push_back(tmp); + } catch( ArgParseException &e) { + throw ArgParseException(e.error(), toString()); + } + + if ( _constraint != NULL ) + if ( ! _constraint->check( _values.back() ) ) + throw( CmdLineParseException( "Value '" + val + + "' does not meet constraint: " + + _constraint->description(), + toString() ) ); +} + +template +bool MultiArg::allowMore() +{ + bool am = _allowMore; + _allowMore = true; + return am; +} + +template +void MultiArg::reset() +{ + Arg::reset(); + _values.clear(); +} + +} // namespace TCLAP + +#endif diff --git a/components/mkspiffs/src/tclap/MultiSwitchArg.h b/components/mkspiffs/src/tclap/MultiSwitchArg.h new file mode 100644 index 0000000..7661d85 --- /dev/null +++ b/components/mkspiffs/src/tclap/MultiSwitchArg.h @@ -0,0 +1,216 @@ + +/****************************************************************************** +* +* file: MultiSwitchArg.h +* +* Copyright (c) 2003, Michael E. Smoot . +* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. +* Copyright (c) 2005, Michael E. Smoot, Daniel Aarno, Erik Zeek. +* All rights reverved. +* +* See the file COPYING in the top directory of this distribution for +* more information. +* +* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +*****************************************************************************/ + + +#ifndef TCLAP_MULTI_SWITCH_ARG_H +#define TCLAP_MULTI_SWITCH_ARG_H + +#include +#include + +#include "SwitchArg.h" + +namespace TCLAP { + +/** +* A multiple switch argument. If the switch is set on the command line, then +* the getValue method will return the number of times the switch appears. +*/ +class MultiSwitchArg : public SwitchArg +{ + protected: + + /** + * The value of the switch. + */ + int _value; + + /** + * Used to support the reset() method so that ValueArg can be + * reset to their constructed value. + */ + int _default; + + public: + + /** + * MultiSwitchArg constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param init - Optional. The initial/default value of this Arg. + * Defaults to 0. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + MultiSwitchArg(const std::string& flag, + const std::string& name, + const std::string& desc, + int init = 0, + Visitor* v = NULL); + + + /** + * MultiSwitchArg constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param parser - A CmdLine parser object to add this Arg to + * \param init - Optional. The initial/default value of this Arg. + * Defaults to 0. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + MultiSwitchArg(const std::string& flag, + const std::string& name, + const std::string& desc, + CmdLineInterface& parser, + int init = 0, + Visitor* v = NULL); + + + /** + * Handles the processing of the argument. + * This re-implements the SwitchArg version of this method to set the + * _value of the argument appropriately. + * \param i - Pointer the the current argument in the list. + * \param args - Mutable list of strings. Passed + * in from main(). + */ + virtual bool processArg(int* i, std::vector& args); + + /** + * Returns int, the number of times the switch has been set. + */ + int getValue(); + + /** + * Returns the shortID for this Arg. + */ + std::string shortID(const std::string& val) const; + + /** + * Returns the longID for this Arg. + */ + std::string longID(const std::string& val) const; + + void reset(); + +}; + +////////////////////////////////////////////////////////////////////// +//BEGIN MultiSwitchArg.cpp +////////////////////////////////////////////////////////////////////// +inline MultiSwitchArg::MultiSwitchArg(const std::string& flag, + const std::string& name, + const std::string& desc, + int init, + Visitor* v ) +: SwitchArg(flag, name, desc, false, v), +_value( init ), +_default( init ) +{ } + +inline MultiSwitchArg::MultiSwitchArg(const std::string& flag, + const std::string& name, + const std::string& desc, + CmdLineInterface& parser, + int init, + Visitor* v ) +: SwitchArg(flag, name, desc, false, v), +_value( init ), +_default( init ) +{ + parser.add( this ); +} + +inline int MultiSwitchArg::getValue() { return _value; } + +inline bool MultiSwitchArg::processArg(int *i, std::vector& args) +{ + if ( _ignoreable && Arg::ignoreRest() ) + return false; + + if ( argMatches( args[*i] )) + { + // so the isSet() method will work + _alreadySet = true; + + // Matched argument: increment value. + ++_value; + + _checkWithVisitor(); + + return true; + } + else if ( combinedSwitchesMatch( args[*i] ) ) + { + // so the isSet() method will work + _alreadySet = true; + + // Matched argument: increment value. + ++_value; + + // Check for more in argument and increment value. + while ( combinedSwitchesMatch( args[*i] ) ) + ++_value; + + _checkWithVisitor(); + + return false; + } + else + return false; +} + +inline std::string +MultiSwitchArg::shortID(const std::string& val) const +{ + return Arg::shortID(val) + " ... "; +} + +inline std::string +MultiSwitchArg::longID(const std::string& val) const +{ + return Arg::longID(val) + " (accepted multiple times)"; +} + +inline void +MultiSwitchArg::reset() +{ + MultiSwitchArg::_value = MultiSwitchArg::_default; +} + +////////////////////////////////////////////////////////////////////// +//END MultiSwitchArg.cpp +////////////////////////////////////////////////////////////////////// + +} //namespace TCLAP + +#endif diff --git a/components/mkspiffs/src/tclap/OptionalUnlabeledTracker.h b/components/mkspiffs/src/tclap/OptionalUnlabeledTracker.h new file mode 100644 index 0000000..8174c5f --- /dev/null +++ b/components/mkspiffs/src/tclap/OptionalUnlabeledTracker.h @@ -0,0 +1,62 @@ + + +/****************************************************************************** + * + * file: OptionalUnlabeledTracker.h + * + * Copyright (c) 2005, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_OPTIONAL_UNLABELED_TRACKER_H +#define TCLAP_OPTIONAL_UNLABELED_TRACKER_H + +#include + +namespace TCLAP { + +class OptionalUnlabeledTracker +{ + + public: + + static void check( bool req, const std::string& argName ); + + static void gotOptional() { alreadyOptionalRef() = true; } + + static bool& alreadyOptional() { return alreadyOptionalRef(); } + + private: + + static bool& alreadyOptionalRef() { static bool ct = false; return ct; } +}; + + +inline void OptionalUnlabeledTracker::check( bool req, const std::string& argName ) +{ + if ( OptionalUnlabeledTracker::alreadyOptional() ) + throw( SpecificationException( + "You can't specify ANY Unlabeled Arg following an optional Unlabeled Arg", + argName ) ); + + if ( !req ) + OptionalUnlabeledTracker::gotOptional(); +} + + +} // namespace TCLAP + +#endif diff --git a/components/mkspiffs/src/tclap/StandardTraits.h b/components/mkspiffs/src/tclap/StandardTraits.h new file mode 100644 index 0000000..46d7f6f --- /dev/null +++ b/components/mkspiffs/src/tclap/StandardTraits.h @@ -0,0 +1,208 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: StandardTraits.h + * + * Copyright (c) 2007, Daniel Aarno, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +// This is an internal tclap file, you should probably not have to +// include this directly + +#ifndef TCLAP_STANDARD_TRAITS_H +#define TCLAP_STANDARD_TRAITS_H + +#ifdef HAVE_CONFIG_H +#include // To check for long long +#endif + +// If Microsoft has already typedef'd wchar_t as an unsigned +// short, then compiles will break because it's as if we're +// creating ArgTraits twice for unsigned short. Thus... +#ifdef _MSC_VER +#ifndef _NATIVE_WCHAR_T_DEFINED +#define TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS +#endif +#endif + +namespace TCLAP { + +// ====================================================================== +// Integer types +// ====================================================================== + +/** + * longs have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +/** + * ints have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +/** + * shorts have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +/** + * chars have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +#ifdef HAVE_LONG_LONG +/** + * long longs have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; +#endif + +// ====================================================================== +// Unsigned integer types +// ====================================================================== + +/** + * unsigned longs have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +/** + * unsigned ints have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +/** + * unsigned shorts have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +/** + * unsigned chars have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +// Microsoft implements size_t awkwardly. +#if defined(_MSC_VER) && defined(_M_X64) +/** + * size_ts have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; +#endif + + +#ifdef HAVE_LONG_LONG +/** + * unsigned long longs have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; +#endif + +// ====================================================================== +// Float types +// ====================================================================== + +/** + * floats have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +/** + * doubles have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + +// ====================================================================== +// Other types +// ====================================================================== + +/** + * bools have value-like semantics. + */ +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; + + +/** + * wchar_ts have value-like semantics. + */ +#ifndef TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS +template<> +struct ArgTraits { + typedef ValueLike ValueCategory; +}; +#endif + +/** + * Strings have string like argument traits. + */ +template<> +struct ArgTraits { + typedef StringLike ValueCategory; +}; + +template +void SetString(T &dst, const std::string &src) +{ + dst = src; +} + +} // namespace + +#endif + diff --git a/components/mkspiffs/src/tclap/StdOutput.h b/components/mkspiffs/src/tclap/StdOutput.h new file mode 100644 index 0000000..ec1cb93 --- /dev/null +++ b/components/mkspiffs/src/tclap/StdOutput.h @@ -0,0 +1,298 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: StdOutput.h + * + * Copyright (c) 2004, Michael E. Smoot + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_STDCMDLINEOUTPUT_H +#define TCLAP_STDCMDLINEOUTPUT_H + +#include +#include +#include +#include +#include + +#include "CmdLineInterface.h" +#include "CmdLineOutput.h" +#include "XorHandler.h" +#include "Arg.h" + +namespace TCLAP { + +/** + * A class that isolates any output from the CmdLine object so that it + * may be easily modified. + */ +class StdOutput : public CmdLineOutput +{ + + public: + + /** + * Prints the usage to stdout. Can be overridden to + * produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + */ + virtual void usage(CmdLineInterface& c); + + /** + * Prints the version to stdout. Can be overridden + * to produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + */ + virtual void version(CmdLineInterface& c); + + /** + * Prints (to stderr) an error message, short usage + * Can be overridden to produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + * \param e - The ArgException that caused the failure. + */ + virtual void failure(CmdLineInterface& c, + ArgException& e ); + + protected: + + /** + * Writes a brief usage message with short args. + * \param c - The CmdLine object the output is generated for. + * \param os - The stream to write the message to. + */ + void _shortUsage( CmdLineInterface& c, std::ostream& os ) const; + + /** + * Writes a longer usage message with long and short args, + * provides descriptions and prints message. + * \param c - The CmdLine object the output is generated for. + * \param os - The stream to write the message to. + */ + void _longUsage( CmdLineInterface& c, std::ostream& os ) const; + + /** + * This function inserts line breaks and indents long strings + * according the params input. It will only break lines at spaces, + * commas and pipes. + * \param os - The stream to be printed to. + * \param s - The string to be printed. + * \param maxWidth - The maxWidth allowed for the output line. + * \param indentSpaces - The number of spaces to indent the first line. + * \param secondLineOffset - The number of spaces to indent the second + * and all subsequent lines in addition to indentSpaces. + */ + void spacePrint( std::ostream& os, + const std::string& s, + int maxWidth, + int indentSpaces, + int secondLineOffset ) const; + +}; + + +inline void StdOutput::version(CmdLineInterface& _cmd) +{ + std::string progName = _cmd.getProgramName(); + std::string xversion = _cmd.getVersion(); + + std::cout << std::endl << progName << " version: " + << xversion << std::endl << std::endl; +} + +inline void StdOutput::usage(CmdLineInterface& _cmd ) +{ + std::cout << std::endl << "USAGE: " << std::endl << std::endl; + + _shortUsage( _cmd, std::cout ); + + std::cout << std::endl << std::endl << "Where: " << std::endl << std::endl; + + _longUsage( _cmd, std::cout ); + + std::cout << std::endl; + +} + +inline void StdOutput::failure( CmdLineInterface& _cmd, + ArgException& e ) +{ + std::string progName = _cmd.getProgramName(); + + std::cerr << "PARSE ERROR: " << e.argId() << std::endl + << " " << e.error() << std::endl << std::endl; + + if ( _cmd.hasHelpAndVersion() ) + { + std::cerr << "Brief USAGE: " << std::endl; + + _shortUsage( _cmd, std::cerr ); + + std::cerr << std::endl << "For complete USAGE and HELP type: " + << std::endl << " " << progName << " --help" + << std::endl << std::endl; + } + else + usage(_cmd); + + throw ExitException(1); +} + +inline void +StdOutput::_shortUsage( CmdLineInterface& _cmd, + std::ostream& os ) const +{ + std::list argList = _cmd.getArgList(); + std::string progName = _cmd.getProgramName(); + XorHandler xorHandler = _cmd.getXorHandler(); + std::vector< std::vector > xorList = xorHandler.getXorList(); + + std::string s = progName + " "; + + // first the xor + for ( int i = 0; static_cast(i) < xorList.size(); i++ ) + { + s += " {"; + for ( ArgVectorIterator it = xorList[i].begin(); + it != xorList[i].end(); it++ ) + s += (*it)->shortID() + "|"; + + s[s.length()-1] = '}'; + } + + // then the rest + for (ArgListIterator it = argList.begin(); it != argList.end(); it++) + if ( !xorHandler.contains( (*it) ) ) + s += " " + (*it)->shortID(); + + // if the program name is too long, then adjust the second line offset + int secondLineOffset = static_cast(progName.length()) + 2; + if ( secondLineOffset > 75/2 ) + secondLineOffset = static_cast(75/2); + + spacePrint( os, s, 75, 3, secondLineOffset ); +} + +inline void +StdOutput::_longUsage( CmdLineInterface& _cmd, + std::ostream& os ) const +{ + std::list argList = _cmd.getArgList(); + std::string message = _cmd.getMessage(); + XorHandler xorHandler = _cmd.getXorHandler(); + std::vector< std::vector > xorList = xorHandler.getXorList(); + + // first the xor + for ( int i = 0; static_cast(i) < xorList.size(); i++ ) + { + for ( ArgVectorIterator it = xorList[i].begin(); + it != xorList[i].end(); + it++ ) + { + spacePrint( os, (*it)->longID(), 75, 3, 3 ); + spacePrint( os, (*it)->getDescription(), 75, 5, 0 ); + + if ( it+1 != xorList[i].end() ) + spacePrint(os, "-- OR --", 75, 9, 0); + } + os << std::endl << std::endl; + } + + // then the rest + for (ArgListIterator it = argList.begin(); it != argList.end(); it++) + if ( !xorHandler.contains( (*it) ) ) + { + spacePrint( os, (*it)->longID(), 75, 3, 3 ); + spacePrint( os, (*it)->getDescription(), 75, 5, 0 ); + os << std::endl; + } + + os << std::endl; + + spacePrint( os, message, 75, 3, 0 ); +} + +inline void StdOutput::spacePrint( std::ostream& os, + const std::string& s, + int maxWidth, + int indentSpaces, + int secondLineOffset ) const +{ + int len = static_cast(s.length()); + + if ( (len + indentSpaces > maxWidth) && maxWidth > 0 ) + { + int allowedLen = maxWidth - indentSpaces; + int start = 0; + while ( start < len ) + { + // find the substring length + // int stringLen = std::min( len - start, allowedLen ); + // doing it this way to support a VisualC++ 2005 bug + using namespace std; + int stringLen = min( len - start, allowedLen ); + + // trim the length so it doesn't end in middle of a word + if ( stringLen == allowedLen ) + while ( stringLen >= 0 && + s[stringLen+start] != ' ' && + s[stringLen+start] != ',' && + s[stringLen+start] != '|' ) + stringLen--; + + // ok, the word is longer than the line, so just split + // wherever the line ends + if ( stringLen <= 0 ) + stringLen = allowedLen; + + // check for newlines + for ( int i = 0; i < stringLen; i++ ) + if ( s[start+i] == '\n' ) + stringLen = i+1; + + // print the indent + for ( int i = 0; i < indentSpaces; i++ ) + os << " "; + + if ( start == 0 ) + { + // handle second line offsets + indentSpaces += secondLineOffset; + + // adjust allowed len + allowedLen -= secondLineOffset; + } + + os << s.substr(start,stringLen) << std::endl; + + // so we don't start a line with a space + while ( s[stringLen+start] == ' ' && start < len ) + start++; + + start += stringLen; + } + } + else + { + for ( int i = 0; i < indentSpaces; i++ ) + os << " "; + os << s << std::endl; + } +} + +} //namespace TCLAP +#endif diff --git a/components/mkspiffs/src/tclap/SwitchArg.h b/components/mkspiffs/src/tclap/SwitchArg.h new file mode 100644 index 0000000..3a93a93 --- /dev/null +++ b/components/mkspiffs/src/tclap/SwitchArg.h @@ -0,0 +1,266 @@ + +/****************************************************************************** + * + * file: SwitchArg.h + * + * Copyright (c) 2003, Michael E. Smoot . + * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_SWITCH_ARG_H +#define TCLAP_SWITCH_ARG_H + +#include +#include + +#include "Arg.h" + +namespace TCLAP { + +/** + * A simple switch argument. If the switch is set on the command line, then + * the getValue method will return the opposite of the default value for the + * switch. + */ +class SwitchArg : public Arg +{ + protected: + + /** + * The value of the switch. + */ + bool _value; + + /** + * Used to support the reset() method so that ValueArg can be + * reset to their constructed value. + */ + bool _default; + + public: + + /** + * SwitchArg constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param def - The default value for this Switch. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + SwitchArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool def = false, + Visitor* v = NULL); + + + /** + * SwitchArg constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param parser - A CmdLine parser object to add this Arg to + * \param def - The default value for this Switch. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + SwitchArg(const std::string& flag, + const std::string& name, + const std::string& desc, + CmdLineInterface& parser, + bool def = false, + Visitor* v = NULL); + + + /** + * Handles the processing of the argument. + * This re-implements the Arg version of this method to set the + * _value of the argument appropriately. + * \param i - Pointer the the current argument in the list. + * \param args - Mutable list of strings. Passed + * in from main(). + */ + virtual bool processArg(int* i, std::vector& args); + + /** + * Checks a string to see if any of the chars in the string + * match the flag for this Switch. + */ + bool combinedSwitchesMatch(std::string& combined); + + /** + * Returns bool, whether or not the switch has been set. + */ + bool getValue(); + + virtual void reset(); + + private: + /** + * Checks to see if we've found the last match in + * a combined string. + */ + bool lastCombined(std::string& combined); + + /** + * Does the common processing of processArg. + */ + void commonProcessing(); +}; + +////////////////////////////////////////////////////////////////////// +//BEGIN SwitchArg.cpp +////////////////////////////////////////////////////////////////////// +inline SwitchArg::SwitchArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool default_val, + Visitor* v ) +: Arg(flag, name, desc, false, false, v), + _value( default_val ), + _default( default_val ) +{ } + +inline SwitchArg::SwitchArg(const std::string& flag, + const std::string& name, + const std::string& desc, + CmdLineInterface& parser, + bool default_val, + Visitor* v ) +: Arg(flag, name, desc, false, false, v), + _value( default_val ), + _default(default_val) +{ + parser.add( this ); +} + +inline bool SwitchArg::getValue() { return _value; } + +inline bool SwitchArg::lastCombined(std::string& combinedSwitches ) +{ + for ( unsigned int i = 1; i < combinedSwitches.length(); i++ ) + if ( combinedSwitches[i] != Arg::blankChar() ) + return false; + + return true; +} + +inline bool SwitchArg::combinedSwitchesMatch(std::string& combinedSwitches ) +{ + // make sure this is actually a combined switch + if ( combinedSwitches.length() > 0 && + combinedSwitches[0] != Arg::flagStartString()[0] ) + return false; + + // make sure it isn't a long name + if ( combinedSwitches.substr( 0, Arg::nameStartString().length() ) == + Arg::nameStartString() ) + return false; + + // make sure the delimiter isn't in the string + if ( combinedSwitches.find_first_of( Arg::delimiter() ) != std::string::npos ) + return false; + + // ok, we're not specifying a ValueArg, so we know that we have + // a combined switch list. + for ( unsigned int i = 1; i < combinedSwitches.length(); i++ ) + if ( _flag.length() > 0 && + combinedSwitches[i] == _flag[0] && + _flag[0] != Arg::flagStartString()[0] ) + { + // update the combined switches so this one is no longer present + // this is necessary so that no unlabeled args are matched + // later in the processing. + //combinedSwitches.erase(i,1); + combinedSwitches[i] = Arg::blankChar(); + return true; + } + + // none of the switches passed in the list match. + return false; +} + +inline void SwitchArg::commonProcessing() +{ + if ( _xorSet ) + throw(CmdLineParseException( + "Mutually exclusive argument already set!", toString())); + + if ( _alreadySet ) + throw(CmdLineParseException("Argument already set!", toString())); + + _alreadySet = true; + + if ( _value == true ) + _value = false; + else + _value = true; + + _checkWithVisitor(); +} + +inline bool SwitchArg::processArg(int *i, std::vector& args) +{ + if ( _ignoreable && Arg::ignoreRest() ) + return false; + + // if the whole string matches the flag or name string + if ( argMatches( args[*i] ) ) + { + commonProcessing(); + + return true; + } + // if a substring matches the flag as part of a combination + else if ( combinedSwitchesMatch( args[*i] ) ) + { + // check again to ensure we don't misinterpret + // this as a MultiSwitchArg + if ( combinedSwitchesMatch( args[*i] ) ) + throw(CmdLineParseException("Argument already set!", + toString())); + + commonProcessing(); + + // We only want to return true if we've found the last combined + // match in the string, otherwise we return true so that other + // switches in the combination will have a chance to match. + return lastCombined( args[*i] ); + } + else + return false; +} + +inline void SwitchArg::reset() +{ + Arg::reset(); + _value = _default; +} +////////////////////////////////////////////////////////////////////// +//End SwitchArg.cpp +////////////////////////////////////////////////////////////////////// + +} //namespace TCLAP + +#endif diff --git a/components/mkspiffs/src/tclap/UnlabeledMultiArg.h b/components/mkspiffs/src/tclap/UnlabeledMultiArg.h new file mode 100644 index 0000000..d38ce91 --- /dev/null +++ b/components/mkspiffs/src/tclap/UnlabeledMultiArg.h @@ -0,0 +1,301 @@ + +/****************************************************************************** + * + * file: UnlabeledMultiArg.h + * + * Copyright (c) 2003, Michael E. Smoot. + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_MULTIPLE_UNLABELED_ARGUMENT_H +#define TCLAP_MULTIPLE_UNLABELED_ARGUMENT_H + +#include +#include + +#include "MultiArg.h" +#include "OptionalUnlabeledTracker.h" + +namespace TCLAP { + +/** + * Just like a MultiArg, except that the arguments are unlabeled. Basically, + * this Arg will slurp up everything that hasn't been matched to another + * Arg. + */ +template +class UnlabeledMultiArg : public MultiArg +{ + + // If compiler has two stage name lookup (as gcc >= 3.4 does) + // this is requried to prevent undef. symbols + using MultiArg::_ignoreable; + using MultiArg::_hasBlanks; + using MultiArg::_extractValue; + using MultiArg::_typeDesc; + using MultiArg::_name; + using MultiArg::_description; + using MultiArg::_alreadySet; + using MultiArg::toString; + + public: + + /** + * Constructor. + * \param name - The name of the Arg. Note that this is used for + * identification, not as a long flag. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param typeDesc - A short, human readable description of the + * type that this object expects. This is used in the generation + * of the USAGE statement. The goal is to be helpful to the end user + * of the program. + * \param ignoreable - Whether or not this argument can be ignored + * using the "--" flag. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + UnlabeledMultiArg( const std::string& name, + const std::string& desc, + bool req, + const std::string& typeDesc, + bool ignoreable = false, + Visitor* v = NULL ); + /** + * Constructor. + * \param name - The name of the Arg. Note that this is used for + * identification, not as a long flag. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param typeDesc - A short, human readable description of the + * type that this object expects. This is used in the generation + * of the USAGE statement. The goal is to be helpful to the end user + * of the program. + * \param parser - A CmdLine parser object to add this Arg to + * \param ignoreable - Whether or not this argument can be ignored + * using the "--" flag. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + UnlabeledMultiArg( const std::string& name, + const std::string& desc, + bool req, + const std::string& typeDesc, + CmdLineInterface& parser, + bool ignoreable = false, + Visitor* v = NULL ); + + /** + * Constructor. + * \param name - The name of the Arg. Note that this is used for + * identification, not as a long flag. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param constraint - A pointer to a Constraint object used + * to constrain this Arg. + * \param ignoreable - Whether or not this argument can be ignored + * using the "--" flag. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + UnlabeledMultiArg( const std::string& name, + const std::string& desc, + bool req, + Constraint* constraint, + bool ignoreable = false, + Visitor* v = NULL ); + + /** + * Constructor. + * \param name - The name of the Arg. Note that this is used for + * identification, not as a long flag. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param constraint - A pointer to a Constraint object used + * to constrain this Arg. + * \param parser - A CmdLine parser object to add this Arg to + * \param ignoreable - Whether or not this argument can be ignored + * using the "--" flag. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + UnlabeledMultiArg( const std::string& name, + const std::string& desc, + bool req, + Constraint* constraint, + CmdLineInterface& parser, + bool ignoreable = false, + Visitor* v = NULL ); + + /** + * Handles the processing of the argument. + * This re-implements the Arg version of this method to set the + * _value of the argument appropriately. It knows the difference + * between labeled and unlabeled. + * \param i - Pointer the the current argument in the list. + * \param args - Mutable list of strings. Passed from main(). + */ + virtual bool processArg(int* i, std::vector& args); + + /** + * Returns the a short id string. Used in the usage. + * \param val - value to be used. + */ + virtual std::string shortID(const std::string& val="val") const; + + /** + * Returns the a long id string. Used in the usage. + * \param val - value to be used. + */ + virtual std::string longID(const std::string& val="val") const; + + /** + * Opertor ==. + * \param a - The Arg to be compared to this. + */ + virtual bool operator==(const Arg& a) const; + + /** + * Pushes this to back of list rather than front. + * \param argList - The list this should be added to. + */ + virtual void addToList( std::list& argList ) const; +}; + +template +UnlabeledMultiArg::UnlabeledMultiArg(const std::string& name, + const std::string& desc, + bool req, + const std::string& typeDesc, + bool ignoreable, + Visitor* v) +: MultiArg("", name, desc, req, typeDesc, v) +{ + _ignoreable = ignoreable; + OptionalUnlabeledTracker::check(true, toString()); +} + +template +UnlabeledMultiArg::UnlabeledMultiArg(const std::string& name, + const std::string& desc, + bool req, + const std::string& typeDesc, + CmdLineInterface& parser, + bool ignoreable, + Visitor* v) +: MultiArg("", name, desc, req, typeDesc, v) +{ + _ignoreable = ignoreable; + OptionalUnlabeledTracker::check(true, toString()); + parser.add( this ); +} + + +template +UnlabeledMultiArg::UnlabeledMultiArg(const std::string& name, + const std::string& desc, + bool req, + Constraint* constraint, + bool ignoreable, + Visitor* v) +: MultiArg("", name, desc, req, constraint, v) +{ + _ignoreable = ignoreable; + OptionalUnlabeledTracker::check(true, toString()); +} + +template +UnlabeledMultiArg::UnlabeledMultiArg(const std::string& name, + const std::string& desc, + bool req, + Constraint* constraint, + CmdLineInterface& parser, + bool ignoreable, + Visitor* v) +: MultiArg("", name, desc, req, constraint, v) +{ + _ignoreable = ignoreable; + OptionalUnlabeledTracker::check(true, toString()); + parser.add( this ); +} + + +template +bool UnlabeledMultiArg::processArg(int *i, std::vector& args) +{ + + if ( _hasBlanks( args[*i] ) ) + return false; + + // never ignore an unlabeled multi arg + + + // always take the first value, regardless of the start string + _extractValue( args[(*i)] ); + + /* + // continue taking args until we hit the end or a start string + while ( (unsigned int)(*i)+1 < args.size() && + args[(*i)+1].find_first_of( Arg::flagStartString() ) != 0 && + args[(*i)+1].find_first_of( Arg::nameStartString() ) != 0 ) + _extractValue( args[++(*i)] ); + */ + + _alreadySet = true; + + return true; +} + +template +std::string UnlabeledMultiArg::shortID(const std::string& val) const +{ + static_cast(val); // Ignore input, don't warn + return std::string("<") + _typeDesc + "> ..."; +} + +template +std::string UnlabeledMultiArg::longID(const std::string& val) const +{ + static_cast(val); // Ignore input, don't warn + return std::string("<") + _typeDesc + "> (accepted multiple times)"; +} + +template +bool UnlabeledMultiArg::operator==(const Arg& a) const +{ + if ( _name == a.getName() || _description == a.getDescription() ) + return true; + else + return false; +} + +template +void UnlabeledMultiArg::addToList( std::list& argList ) const +{ + argList.push_back( const_cast(static_cast(this)) ); +} + +} + +#endif diff --git a/components/mkspiffs/src/tclap/UnlabeledValueArg.h b/components/mkspiffs/src/tclap/UnlabeledValueArg.h new file mode 100644 index 0000000..8debba7 --- /dev/null +++ b/components/mkspiffs/src/tclap/UnlabeledValueArg.h @@ -0,0 +1,340 @@ + +/****************************************************************************** + * + * file: UnlabeledValueArg.h + * + * Copyright (c) 2003, Michael E. Smoot . + * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_UNLABELED_VALUE_ARGUMENT_H +#define TCLAP_UNLABELED_VALUE_ARGUMENT_H + +#include +#include + +#include "ValueArg.h" +#include "OptionalUnlabeledTracker.h" + + +namespace TCLAP { + +/** + * The basic unlabeled argument that parses a value. + * This is a template class, which means the type T defines the type + * that a given object will attempt to parse when an UnlabeledValueArg + * is reached in the list of args that the CmdLine iterates over. + */ +template +class UnlabeledValueArg : public ValueArg +{ + + // If compiler has two stage name lookup (as gcc >= 3.4 does) + // this is requried to prevent undef. symbols + using ValueArg::_ignoreable; + using ValueArg::_hasBlanks; + using ValueArg::_extractValue; + using ValueArg::_typeDesc; + using ValueArg::_name; + using ValueArg::_description; + using ValueArg::_alreadySet; + using ValueArg::toString; + + public: + + /** + * UnlabeledValueArg constructor. + * \param name - A one word name for the argument. Note that this is used for + * identification, not as a long flag. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param value - The default value assigned to this argument if it + * is not present on the command line. + * \param typeDesc - A short, human readable description of the + * type that this object expects. This is used in the generation + * of the USAGE statement. The goal is to be helpful to the end user + * of the program. + * \param ignoreable - Allows you to specify that this argument can be + * ignored if the '--' flag is set. This defaults to false (cannot + * be ignored) and should generally stay that way unless you have + * some special need for certain arguments to be ignored. + * \param v - Optional Vistor. You should leave this blank unless + * you have a very good reason. + */ + UnlabeledValueArg( const std::string& name, + const std::string& desc, + bool req, + T value, + const std::string& typeDesc, + bool ignoreable = false, + Visitor* v = NULL); + + /** + * UnlabeledValueArg constructor. + * \param name - A one word name for the argument. Note that this is used for + * identification, not as a long flag. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param value - The default value assigned to this argument if it + * is not present on the command line. + * \param typeDesc - A short, human readable description of the + * type that this object expects. This is used in the generation + * of the USAGE statement. The goal is to be helpful to the end user + * of the program. + * \param parser - A CmdLine parser object to add this Arg to + * \param ignoreable - Allows you to specify that this argument can be + * ignored if the '--' flag is set. This defaults to false (cannot + * be ignored) and should generally stay that way unless you have + * some special need for certain arguments to be ignored. + * \param v - Optional Vistor. You should leave this blank unless + * you have a very good reason. + */ + UnlabeledValueArg( const std::string& name, + const std::string& desc, + bool req, + T value, + const std::string& typeDesc, + CmdLineInterface& parser, + bool ignoreable = false, + Visitor* v = NULL ); + + /** + * UnlabeledValueArg constructor. + * \param name - A one word name for the argument. Note that this is used for + * identification, not as a long flag. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param value - The default value assigned to this argument if it + * is not present on the command line. + * \param constraint - A pointer to a Constraint object used + * to constrain this Arg. + * \param ignoreable - Allows you to specify that this argument can be + * ignored if the '--' flag is set. This defaults to false (cannot + * be ignored) and should generally stay that way unless you have + * some special need for certain arguments to be ignored. + * \param v - Optional Vistor. You should leave this blank unless + * you have a very good reason. + */ + UnlabeledValueArg( const std::string& name, + const std::string& desc, + bool req, + T value, + Constraint* constraint, + bool ignoreable = false, + Visitor* v = NULL ); + + + /** + * UnlabeledValueArg constructor. + * \param name - A one word name for the argument. Note that this is used for + * identification, not as a long flag. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param value - The default value assigned to this argument if it + * is not present on the command line. + * \param constraint - A pointer to a Constraint object used + * to constrain this Arg. + * \param parser - A CmdLine parser object to add this Arg to + * \param ignoreable - Allows you to specify that this argument can be + * ignored if the '--' flag is set. This defaults to false (cannot + * be ignored) and should generally stay that way unless you have + * some special need for certain arguments to be ignored. + * \param v - Optional Vistor. You should leave this blank unless + * you have a very good reason. + */ + UnlabeledValueArg( const std::string& name, + const std::string& desc, + bool req, + T value, + Constraint* constraint, + CmdLineInterface& parser, + bool ignoreable = false, + Visitor* v = NULL); + + /** + * Handles the processing of the argument. + * This re-implements the Arg version of this method to set the + * _value of the argument appropriately. Handling specific to + * unlabled arguments. + * \param i - Pointer the the current argument in the list. + * \param args - Mutable list of strings. + */ + virtual bool processArg(int* i, std::vector& args); + + /** + * Overrides shortID for specific behavior. + */ + virtual std::string shortID(const std::string& val="val") const; + + /** + * Overrides longID for specific behavior. + */ + virtual std::string longID(const std::string& val="val") const; + + /** + * Overrides operator== for specific behavior. + */ + virtual bool operator==(const Arg& a ) const; + + /** + * Instead of pushing to the front of list, push to the back. + * \param argList - The list to add this to. + */ + virtual void addToList( std::list& argList ) const; + +}; + +/** + * Constructor implemenation. + */ +template +UnlabeledValueArg::UnlabeledValueArg(const std::string& name, + const std::string& desc, + bool req, + T val, + const std::string& typeDesc, + bool ignoreable, + Visitor* v) +: ValueArg("", name, desc, req, val, typeDesc, v) +{ + _ignoreable = ignoreable; + + OptionalUnlabeledTracker::check(req, toString()); + +} + +template +UnlabeledValueArg::UnlabeledValueArg(const std::string& name, + const std::string& desc, + bool req, + T val, + const std::string& typeDesc, + CmdLineInterface& parser, + bool ignoreable, + Visitor* v) +: ValueArg("", name, desc, req, val, typeDesc, v) +{ + _ignoreable = ignoreable; + OptionalUnlabeledTracker::check(req, toString()); + parser.add( this ); +} + +/** + * Constructor implemenation. + */ +template +UnlabeledValueArg::UnlabeledValueArg(const std::string& name, + const std::string& desc, + bool req, + T val, + Constraint* constraint, + bool ignoreable, + Visitor* v) +: ValueArg("", name, desc, req, val, constraint, v) +{ + _ignoreable = ignoreable; + OptionalUnlabeledTracker::check(req, toString()); +} + +template +UnlabeledValueArg::UnlabeledValueArg(const std::string& name, + const std::string& desc, + bool req, + T val, + Constraint* constraint, + CmdLineInterface& parser, + bool ignoreable, + Visitor* v) +: ValueArg("", name, desc, req, val, constraint, v) +{ + _ignoreable = ignoreable; + OptionalUnlabeledTracker::check(req, toString()); + parser.add( this ); +} + +/** + * Implementation of processArg(). + */ +template +bool UnlabeledValueArg::processArg(int *i, std::vector& args) +{ + + if ( _alreadySet ) + return false; + + if ( _hasBlanks( args[*i] ) ) + return false; + + // never ignore an unlabeled arg + + _extractValue( args[*i] ); + _alreadySet = true; + return true; +} + +/** + * Overriding shortID for specific output. + */ +template +std::string UnlabeledValueArg::shortID(const std::string& val) const +{ + static_cast(val); // Ignore input, don't warn + return std::string("<") + _typeDesc + ">"; +} + +/** + * Overriding longID for specific output. + */ +template +std::string UnlabeledValueArg::longID(const std::string& val) const +{ + static_cast(val); // Ignore input, don't warn + + // Ideally we would like to be able to use RTTI to return the name + // of the type required for this argument. However, g++ at least, + // doesn't appear to return terribly useful "names" of the types. + return std::string("<") + _typeDesc + ">"; +} + +/** + * Overriding operator== for specific behavior. + */ +template +bool UnlabeledValueArg::operator==(const Arg& a ) const +{ + if ( _name == a.getName() || _description == a.getDescription() ) + return true; + else + return false; +} + +template +void UnlabeledValueArg::addToList( std::list& argList ) const +{ + argList.push_back( const_cast(static_cast(this)) ); +} + +} +#endif diff --git a/components/mkspiffs/src/tclap/ValueArg.h b/components/mkspiffs/src/tclap/ValueArg.h new file mode 100644 index 0000000..95f9bb7 --- /dev/null +++ b/components/mkspiffs/src/tclap/ValueArg.h @@ -0,0 +1,425 @@ +/****************************************************************************** + * + * file: ValueArg.h + * + * Copyright (c) 2003, Michael E. Smoot . + * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_VALUE_ARGUMENT_H +#define TCLAP_VALUE_ARGUMENT_H + +#include +#include + +#include "Arg.h" +#include "Constraint.h" + +namespace TCLAP { + +/** + * The basic labeled argument that parses a value. + * This is a template class, which means the type T defines the type + * that a given object will attempt to parse when the flag/name is matched + * on the command line. While there is nothing stopping you from creating + * an unflagged ValueArg, it is unwise and would cause significant problems. + * Instead use an UnlabeledValueArg. + */ +template +class ValueArg : public Arg +{ + protected: + + /** + * The value parsed from the command line. + * Can be of any type, as long as the >> operator for the type + * is defined. + */ + T _value; + + /** + * Used to support the reset() method so that ValueArg can be + * reset to their constructed value. + */ + T _default; + + /** + * A human readable description of the type to be parsed. + * This is a hack, plain and simple. Ideally we would use RTTI to + * return the name of type T, but until there is some sort of + * consistent support for human readable names, we are left to our + * own devices. + */ + std::string _typeDesc; + + /** + * A Constraint this Arg must conform to. + */ + Constraint* _constraint; + + /** + * Extracts the value from the string. + * Attempts to parse string as type T, if this fails an exception + * is thrown. + * \param val - value to be parsed. + */ + void _extractValue( const std::string& val ); + + public: + + /** + * Labeled ValueArg constructor. + * You could conceivably call this constructor with a blank flag, + * but that would make you a bad person. It would also cause + * an exception to be thrown. If you want an unlabeled argument, + * use the other constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param value - The default value assigned to this argument if it + * is not present on the command line. + * \param typeDesc - A short, human readable description of the + * type that this object expects. This is used in the generation + * of the USAGE statement. The goal is to be helpful to the end user + * of the program. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + ValueArg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + T value, + const std::string& typeDesc, + Visitor* v = NULL); + + + /** + * Labeled ValueArg constructor. + * You could conceivably call this constructor with a blank flag, + * but that would make you a bad person. It would also cause + * an exception to be thrown. If you want an unlabeled argument, + * use the other constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param value - The default value assigned to this argument if it + * is not present on the command line. + * \param typeDesc - A short, human readable description of the + * type that this object expects. This is used in the generation + * of the USAGE statement. The goal is to be helpful to the end user + * of the program. + * \param parser - A CmdLine parser object to add this Arg to + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + ValueArg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + T value, + const std::string& typeDesc, + CmdLineInterface& parser, + Visitor* v = NULL ); + + /** + * Labeled ValueArg constructor. + * You could conceivably call this constructor with a blank flag, + * but that would make you a bad person. It would also cause + * an exception to be thrown. If you want an unlabeled argument, + * use the other constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param value - The default value assigned to this argument if it + * is not present on the command line. + * \param constraint - A pointer to a Constraint object used + * to constrain this Arg. + * \param parser - A CmdLine parser object to add this Arg to. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + ValueArg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + T value, + Constraint* constraint, + CmdLineInterface& parser, + Visitor* v = NULL ); + + /** + * Labeled ValueArg constructor. + * You could conceivably call this constructor with a blank flag, + * but that would make you a bad person. It would also cause + * an exception to be thrown. If you want an unlabeled argument, + * use the other constructor. + * \param flag - The one character flag that identifies this + * argument on the command line. + * \param name - A one word name for the argument. Can be + * used as a long flag on the command line. + * \param desc - A description of what the argument is for or + * does. + * \param req - Whether the argument is required on the command + * line. + * \param value - The default value assigned to this argument if it + * is not present on the command line. + * \param constraint - A pointer to a Constraint object used + * to constrain this Arg. + * \param v - An optional visitor. You probably should not + * use this unless you have a very good reason. + */ + ValueArg( const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + T value, + Constraint* constraint, + Visitor* v = NULL ); + + /** + * Handles the processing of the argument. + * This re-implements the Arg version of this method to set the + * _value of the argument appropriately. It knows the difference + * between labeled and unlabeled. + * \param i - Pointer the the current argument in the list. + * \param args - Mutable list of strings. Passed + * in from main(). + */ + virtual bool processArg(int* i, std::vector& args); + + /** + * Returns the value of the argument. + */ + T& getValue() ; + + /** + * Specialization of shortID. + * \param val - value to be used. + */ + virtual std::string shortID(const std::string& val = "val") const; + + /** + * Specialization of longID. + * \param val - value to be used. + */ + virtual std::string longID(const std::string& val = "val") const; + + virtual void reset() ; + +private: + /** + * Prevent accidental copying + */ + ValueArg(const ValueArg& rhs); + ValueArg& operator=(const ValueArg& rhs); +}; + + +/** + * Constructor implementation. + */ +template +ValueArg::ValueArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + T val, + const std::string& typeDesc, + Visitor* v) +: Arg(flag, name, desc, req, true, v), + _value( val ), + _default( val ), + _typeDesc( typeDesc ), + _constraint( NULL ) +{ } + +template +ValueArg::ValueArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + T val, + const std::string& typeDesc, + CmdLineInterface& parser, + Visitor* v) +: Arg(flag, name, desc, req, true, v), + _value( val ), + _default( val ), + _typeDesc( typeDesc ), + _constraint( NULL ) +{ + parser.add( this ); +} + +template +ValueArg::ValueArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + T val, + Constraint* constraint, + Visitor* v) +: Arg(flag, name, desc, req, true, v), + _value( val ), + _default( val ), + _typeDesc( constraint->shortID() ), + _constraint( constraint ) +{ } + +template +ValueArg::ValueArg(const std::string& flag, + const std::string& name, + const std::string& desc, + bool req, + T val, + Constraint* constraint, + CmdLineInterface& parser, + Visitor* v) +: Arg(flag, name, desc, req, true, v), + _value( val ), + _default( val ), + _typeDesc( constraint->shortID() ), + _constraint( constraint ) +{ + parser.add( this ); +} + + +/** + * Implementation of getValue(). + */ +template +T& ValueArg::getValue() { return _value; } + +/** + * Implementation of processArg(). + */ +template +bool ValueArg::processArg(int *i, std::vector& args) +{ + if ( _ignoreable && Arg::ignoreRest() ) + return false; + + if ( _hasBlanks( args[*i] ) ) + return false; + + std::string flag = args[*i]; + + std::string value = ""; + trimFlag( flag, value ); + + if ( argMatches( flag ) ) + { + if ( _alreadySet ) + { + if ( _xorSet ) + throw( CmdLineParseException( + "Mutually exclusive argument already set!", + toString()) ); + else + throw( CmdLineParseException("Argument already set!", + toString()) ); + } + + if ( Arg::delimiter() != ' ' && value == "" ) + throw( ArgParseException( + "Couldn't find delimiter for this argument!", + toString() ) ); + + if ( value == "" ) + { + (*i)++; + if ( static_cast(*i) < args.size() ) + _extractValue( args[*i] ); + else + throw( ArgParseException("Missing a value for this argument!", + toString() ) ); + } + else + _extractValue( value ); + + _alreadySet = true; + _checkWithVisitor(); + return true; + } + else + return false; +} + +/** + * Implementation of shortID. + */ +template +std::string ValueArg::shortID(const std::string& val) const +{ + static_cast(val); // Ignore input, don't warn + return Arg::shortID( _typeDesc ); +} + +/** + * Implementation of longID. + */ +template +std::string ValueArg::longID(const std::string& val) const +{ + static_cast(val); // Ignore input, don't warn + return Arg::longID( _typeDesc ); +} + +template +void ValueArg::_extractValue( const std::string& val ) +{ + try { + ExtractValue(_value, val, typename ArgTraits::ValueCategory()); + } catch( ArgParseException &e) { + throw ArgParseException(e.error(), toString()); + } + + if ( _constraint != NULL ) + if ( ! _constraint->check( _value ) ) + throw( CmdLineParseException( "Value '" + val + + + "' does not meet constraint: " + + _constraint->description(), + toString() ) ); +} + +template +void ValueArg::reset() +{ + Arg::reset(); + _value = _default; +} + +} // namespace TCLAP + +#endif diff --git a/components/mkspiffs/src/tclap/ValuesConstraint.h b/components/mkspiffs/src/tclap/ValuesConstraint.h new file mode 100644 index 0000000..27d3a6d --- /dev/null +++ b/components/mkspiffs/src/tclap/ValuesConstraint.h @@ -0,0 +1,148 @@ + + +/****************************************************************************** + * + * file: ValuesConstraint.h + * + * Copyright (c) 2005, Michael E. Smoot + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_VALUESCONSTRAINT_H +#define TCLAP_VALUESCONSTRAINT_H + +#include +#include +#include "Constraint.h" + +#ifdef HAVE_CONFIG_H +#include +#else +#define HAVE_SSTREAM +#endif + +#if defined(HAVE_SSTREAM) +#include +#elif defined(HAVE_STRSTREAM) +#include +#else +#error "Need a stringstream (sstream or strstream) to compile!" +#endif + +namespace TCLAP { + +/** + * A Constraint that constrains the Arg to only those values specified + * in the constraint. + */ +template +class ValuesConstraint : public Constraint +{ + + public: + + /** + * Constructor. + * \param allowed - vector of allowed values. + */ + ValuesConstraint(std::vector& allowed); + + /** + * Virtual destructor. + */ + virtual ~ValuesConstraint() {} + + /** + * Returns a description of the Constraint. + */ + virtual std::string description() const; + + /** + * Returns the short ID for the Constraint. + */ + virtual std::string shortID() const; + + /** + * The method used to verify that the value parsed from the command + * line meets the constraint. + * \param value - The value that will be checked. + */ + virtual bool check(const T& value) const; + + protected: + + /** + * The list of valid values. + */ + std::vector _allowed; + + /** + * The string used to describe the allowed values of this constraint. + */ + std::string _typeDesc; + +}; + +template +ValuesConstraint::ValuesConstraint(std::vector& allowed) +: _allowed(allowed), + _typeDesc("") +{ + for ( unsigned int i = 0; i < _allowed.size(); i++ ) + { + +#if defined(HAVE_SSTREAM) + std::ostringstream os; +#elif defined(HAVE_STRSTREAM) + std::ostrstream os; +#else +#error "Need a stringstream (sstream or strstream) to compile!" +#endif + + os << _allowed[i]; + + std::string temp( os.str() ); + + if ( i > 0 ) + _typeDesc += "|"; + _typeDesc += temp; + } +} + +template +bool ValuesConstraint::check( const T& val ) const +{ + if ( std::find(_allowed.begin(),_allowed.end(),val) == _allowed.end() ) + return false; + else + return true; +} + +template +std::string ValuesConstraint::shortID() const +{ + return _typeDesc; +} + +template +std::string ValuesConstraint::description() const +{ + return _typeDesc; +} + + +} //namespace TCLAP +#endif + diff --git a/components/mkspiffs/src/tclap/VersionVisitor.h b/components/mkspiffs/src/tclap/VersionVisitor.h new file mode 100644 index 0000000..d7e67d8 --- /dev/null +++ b/components/mkspiffs/src/tclap/VersionVisitor.h @@ -0,0 +1,81 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: VersionVisitor.h + * + * Copyright (c) 2003, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_VERSION_VISITOR_H +#define TCLAP_VERSION_VISITOR_H + +#include "CmdLineInterface.h" +#include "CmdLineOutput.h" +#include "Visitor.h" + +namespace TCLAP { + +/** + * A Vistor that will call the version method of the given CmdLineOutput + * for the specified CmdLine object and then exit. + */ +class VersionVisitor: public Visitor +{ + private: + /** + * Prevent accidental copying + */ + VersionVisitor(const VersionVisitor& rhs); + VersionVisitor& operator=(const VersionVisitor& rhs); + + protected: + + /** + * The CmdLine of interest. + */ + CmdLineInterface* _cmd; + + /** + * The output object. + */ + CmdLineOutput** _out; + + public: + + /** + * Constructor. + * \param cmd - The CmdLine the output is generated for. + * \param out - The type of output. + */ + VersionVisitor( CmdLineInterface* cmd, CmdLineOutput** out ) + : Visitor(), _cmd( cmd ), _out( out ) { } + + /** + * Calls the version method of the output object using the + * specified CmdLine. + */ + void visit() { + (*_out)->version(*_cmd); + throw ExitException(0); + } + +}; + +} + +#endif diff --git a/components/mkspiffs/src/tclap/Visitor.h b/components/mkspiffs/src/tclap/Visitor.h new file mode 100644 index 0000000..38ddcbd --- /dev/null +++ b/components/mkspiffs/src/tclap/Visitor.h @@ -0,0 +1,53 @@ + +/****************************************************************************** + * + * file: Visitor.h + * + * Copyright (c) 2003, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#ifndef TCLAP_VISITOR_H +#define TCLAP_VISITOR_H + +namespace TCLAP { + +/** + * A base class that defines the interface for visitors. + */ +class Visitor +{ + public: + + /** + * Constructor. Does nothing. + */ + Visitor() { } + + /** + * Destructor. Does nothing. + */ + virtual ~Visitor() { } + + /** + * Does nothing. Should be overridden by child. + */ + virtual void visit() { } +}; + +} + +#endif diff --git a/components/mkspiffs/src/tclap/XorHandler.h b/components/mkspiffs/src/tclap/XorHandler.h new file mode 100644 index 0000000..13551fb --- /dev/null +++ b/components/mkspiffs/src/tclap/XorHandler.h @@ -0,0 +1,166 @@ + +/****************************************************************************** + * + * file: XorHandler.h + * + * Copyright (c) 2003, Michael E. Smoot . + * Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_XORHANDLER_H +#define TCLAP_XORHANDLER_H + +#include "Arg.h" +#include +#include +#include +#include + +namespace TCLAP { + +/** + * This class handles lists of Arg's that are to be XOR'd on the command + * line. This is used by CmdLine and you shouldn't ever use it. + */ +class XorHandler +{ + protected: + + /** + * The list of of lists of Arg's to be or'd together. + */ + std::vector< std::vector > _orList; + + public: + + /** + * Constructor. Does nothing. + */ + XorHandler( ) : _orList(std::vector< std::vector >()) {} + + /** + * Add a list of Arg*'s that will be orred together. + * \param ors - list of Arg* that will be xor'd. + */ + void add( std::vector& ors ); + + /** + * Checks whether the specified Arg is in one of the xor lists and + * if it does match one, returns the size of the xor list that the + * Arg matched. If the Arg matches, then it also sets the rest of + * the Arg's in the list. You shouldn't use this. + * \param a - The Arg to be checked. + */ + int check( const Arg* a ); + + /** + * Returns the XOR specific short usage. + */ + std::string shortUsage(); + + /** + * Prints the XOR specific long usage. + * \param os - Stream to print to. + */ + void printLongUsage(std::ostream& os); + + /** + * Simply checks whether the Arg is contained in one of the arg + * lists. + * \param a - The Arg to be checked. + */ + bool contains( const Arg* a ); + + std::vector< std::vector >& getXorList(); + +}; + + +////////////////////////////////////////////////////////////////////// +//BEGIN XOR.cpp +////////////////////////////////////////////////////////////////////// +inline void XorHandler::add( std::vector& ors ) +{ + _orList.push_back( ors ); +} + +inline int XorHandler::check( const Arg* a ) +{ + // iterate over each XOR list + for ( int i = 0; static_cast(i) < _orList.size(); i++ ) + { + // if the XOR list contains the arg.. + ArgVectorIterator ait = std::find( _orList[i].begin(), + _orList[i].end(), a ); + if ( ait != _orList[i].end() ) + { + // first check to see if a mutually exclusive switch + // has not already been set + for ( ArgVectorIterator it = _orList[i].begin(); + it != _orList[i].end(); + it++ ) + if ( a != (*it) && (*it)->isSet() ) + throw(CmdLineParseException( + "Mutually exclusive argument already set!", + (*it)->toString())); + + // go through and set each arg that is not a + for ( ArgVectorIterator it = _orList[i].begin(); + it != _orList[i].end(); + it++ ) + if ( a != (*it) ) + (*it)->xorSet(); + + // return the number of required args that have now been set + if ( (*ait)->allowMore() ) + return 0; + else + return static_cast(_orList[i].size()); + } + } + + if ( a->isRequired() ) + return 1; + else + return 0; +} + +inline bool XorHandler::contains( const Arg* a ) +{ + for ( int i = 0; static_cast(i) < _orList.size(); i++ ) + for ( ArgVectorIterator it = _orList[i].begin(); + it != _orList[i].end(); + it++ ) + if ( a == (*it) ) + return true; + + return false; +} + +inline std::vector< std::vector >& XorHandler::getXorList() +{ + return _orList; +} + + + +////////////////////////////////////////////////////////////////////// +//END XOR.cpp +////////////////////////////////////////////////////////////////////// + +} //namespace TCLAP + +#endif diff --git a/components/mkspiffs/src/tclap/ZshCompletionOutput.h b/components/mkspiffs/src/tclap/ZshCompletionOutput.h new file mode 100644 index 0000000..69dc099 --- /dev/null +++ b/components/mkspiffs/src/tclap/ZshCompletionOutput.h @@ -0,0 +1,323 @@ +// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- + +/****************************************************************************** + * + * file: ZshCompletionOutput.h + * + * Copyright (c) 2006, Oliver Kiddle + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef TCLAP_ZSHCOMPLETIONOUTPUT_H +#define TCLAP_ZSHCOMPLETIONOUTPUT_H + +#include +#include +#include +#include +#include + +#include "CmdLineInterface.h" +#include "CmdLineOutput.h" +#include "XorHandler.h" +#include "Arg.h" + +namespace TCLAP { + +/** + * A class that generates a Zsh completion function as output from the usage() + * method for the given CmdLine and its Args. + */ +class ZshCompletionOutput : public CmdLineOutput +{ + + public: + + ZshCompletionOutput(); + + /** + * Prints the usage to stdout. Can be overridden to + * produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + */ + virtual void usage(CmdLineInterface& c); + + /** + * Prints the version to stdout. Can be overridden + * to produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + */ + virtual void version(CmdLineInterface& c); + + /** + * Prints (to stderr) an error message, short usage + * Can be overridden to produce alternative behavior. + * \param c - The CmdLine object the output is generated for. + * \param e - The ArgException that caused the failure. + */ + virtual void failure(CmdLineInterface& c, + ArgException& e ); + + protected: + + void basename( std::string& s ); + void quoteSpecialChars( std::string& s ); + + std::string getMutexList( CmdLineInterface& _cmd, Arg* a ); + void printOption( Arg* it, std::string mutex ); + void printArg( Arg* it ); + + std::map common; + char theDelimiter; +}; + +ZshCompletionOutput::ZshCompletionOutput() +: common(std::map()), + theDelimiter('=') +{ + common["host"] = "_hosts"; + common["hostname"] = "_hosts"; + common["file"] = "_files"; + common["filename"] = "_files"; + common["user"] = "_users"; + common["username"] = "_users"; + common["directory"] = "_directories"; + common["path"] = "_directories"; + common["url"] = "_urls"; +} + +inline void ZshCompletionOutput::version(CmdLineInterface& _cmd) +{ + std::cout << _cmd.getVersion() << std::endl; +} + +inline void ZshCompletionOutput::usage(CmdLineInterface& _cmd ) +{ + std::list argList = _cmd.getArgList(); + std::string progName = _cmd.getProgramName(); + std::string xversion = _cmd.getVersion(); + theDelimiter = _cmd.getDelimiter(); + basename(progName); + + std::cout << "#compdef " << progName << std::endl << std::endl << + "# " << progName << " version " << _cmd.getVersion() << std::endl << std::endl << + "_arguments -s -S"; + + for (ArgListIterator it = argList.begin(); it != argList.end(); it++) + { + if ( (*it)->shortID().at(0) == '<' ) + printArg((*it)); + else if ( (*it)->getFlag() != "-" ) + printOption((*it), getMutexList(_cmd, *it)); + } + + std::cout << std::endl; +} + +inline void ZshCompletionOutput::failure( CmdLineInterface& _cmd, + ArgException& e ) +{ + static_cast(_cmd); // unused + std::cout << e.what() << std::endl; +} + +inline void ZshCompletionOutput::quoteSpecialChars( std::string& s ) +{ + size_t idx = s.find_last_of(':'); + while ( idx != std::string::npos ) + { + s.insert(idx, 1, '\\'); + idx = s.find_last_of(':', idx); + } + idx = s.find_last_of('\''); + while ( idx != std::string::npos ) + { + s.insert(idx, "'\\'"); + if (idx == 0) + idx = std::string::npos; + else + idx = s.find_last_of('\'', --idx); + } +} + +inline void ZshCompletionOutput::basename( std::string& s ) +{ + size_t p = s.find_last_of('/'); + if ( p != std::string::npos ) + { + s.erase(0, p + 1); + } +} + +inline void ZshCompletionOutput::printArg(Arg* a) +{ + static int count = 1; + + std::cout << " \\" << std::endl << " '"; + if ( a->acceptsMultipleValues() ) + std::cout << '*'; + else + std::cout << count++; + std::cout << ':'; + if ( !a->isRequired() ) + std::cout << ':'; + + std::cout << a->getName() << ':'; + std::map::iterator compArg = common.find(a->getName()); + if ( compArg != common.end() ) + { + std::cout << compArg->second; + } + else + { + std::cout << "_guard \"^-*\" " << a->getName(); + } + std::cout << '\''; +} + +inline void ZshCompletionOutput::printOption(Arg* a, std::string mutex) +{ + std::string flag = a->flagStartChar() + a->getFlag(); + std::string name = a->nameStartString() + a->getName(); + std::string desc = a->getDescription(); + + // remove full stop and capitalisation from description as + // this is the convention for zsh function + if (!desc.compare(0, 12, "(required) ")) + { + desc.erase(0, 12); + } + if (!desc.compare(0, 15, "(OR required) ")) + { + desc.erase(0, 15); + } + size_t len = desc.length(); + if (len && desc.at(--len) == '.') + { + desc.erase(len); + } + if (len) + { + desc.replace(0, 1, 1, tolower(desc.at(0))); + } + + std::cout << " \\" << std::endl << " '" << mutex; + + if ( a->getFlag().empty() ) + { + std::cout << name; + } + else + { + std::cout << "'{" << flag << ',' << name << "}'"; + } + if ( theDelimiter == '=' && a->isValueRequired() ) + std::cout << "=-"; + quoteSpecialChars(desc); + std::cout << '[' << desc << ']'; + + if ( a->isValueRequired() ) + { + std::string arg = a->shortID(); + arg.erase(0, arg.find_last_of(theDelimiter) + 1); + if ( arg.at(arg.length()-1) == ']' ) + arg.erase(arg.length()-1); + if ( arg.at(arg.length()-1) == ']' ) + { + arg.erase(arg.length()-1); + } + if ( arg.at(0) == '<' ) + { + arg.erase(arg.length()-1); + arg.erase(0, 1); + } + size_t p = arg.find('|'); + if ( p != std::string::npos ) + { + do + { + arg.replace(p, 1, 1, ' '); + } + while ( (p = arg.find_first_of('|', p)) != std::string::npos ); + quoteSpecialChars(arg); + std::cout << ": :(" << arg << ')'; + } + else + { + std::cout << ':' << arg; + std::map::iterator compArg = common.find(arg); + if ( compArg != common.end() ) + { + std::cout << ':' << compArg->second; + } + } + } + + std::cout << '\''; +} + +inline std::string ZshCompletionOutput::getMutexList( CmdLineInterface& _cmd, Arg* a) +{ + XorHandler xorHandler = _cmd.getXorHandler(); + std::vector< std::vector > xorList = xorHandler.getXorList(); + + if (a->getName() == "help" || a->getName() == "version") + { + return "(-)"; + } + + std::ostringstream list; + if ( a->acceptsMultipleValues() ) + { + list << '*'; + } + + for ( int i = 0; static_cast(i) < xorList.size(); i++ ) + { + for ( ArgVectorIterator it = xorList[i].begin(); + it != xorList[i].end(); + it++) + if ( a == (*it) ) + { + list << '('; + for ( ArgVectorIterator iu = xorList[i].begin(); + iu != xorList[i].end(); + iu++ ) + { + bool notCur = (*iu) != a; + bool hasFlag = !(*iu)->getFlag().empty(); + if ( iu != xorList[i].begin() && (notCur || hasFlag) ) + list << ' '; + if (hasFlag) + list << (*iu)->flagStartChar() << (*iu)->getFlag() << ' '; + if ( notCur || hasFlag ) + list << (*iu)->nameStartString() << (*iu)->getName(); + } + list << ')'; + return list.str(); + } + } + + // wasn't found in xor list + if (!a->getFlag().empty()) { + list << "(" << a->flagStartChar() << a->getFlag() << ' ' << + a->nameStartString() << a->getName() << ')'; + } + + return list.str(); +} + +} //namespace TCLAP +#endif diff --git a/components/spidriver/component.mk b/components/spidriver/component.mk new file mode 100644 index 0000000..0f76ee5 --- /dev/null +++ b/components/spidriver/component.mk @@ -0,0 +1,7 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/components/spidriver/spi_master_lobo.c b/components/spidriver/spi_master_lobo.c new file mode 100644 index 0000000..5fb267a --- /dev/null +++ b/components/spidriver/spi_master_lobo.c @@ -0,0 +1,1153 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/* +---------------------------------------- +Non DMA version of the spi_master driver +---------------------------------------- +------------------------------------------------------------------------------------ +Based on esp-idf 'spi_master', modified by LoBo (https://github.com/loboris) 03/2017 +------------------------------------------------------------------------------------ + +* Transfers data to SPI device in direct mode, not using DMA +* All configuration options (bus, device, transaction) are the same as in spi_master driver +* Transfers uses the semaphore (taken in select function & given in deselect function) to protect the transfer +* Number of the devices attached to the bus which uses hardware CS can be 3 ('NO_CS') +* Additional devices which uses software CS can be attached to the bus, up to 'NO_DEV' +* 'spi_bus_initialize' & 'spi_bus_remove' functions are removed, spi bus is initiated/removed in spi_lobo_bus_add_device/spi_lobo_bus_remove_device when needed +* 'spi_lobo_bus_add_device' function has added parameter 'bus_config' and automatically initializes spi bus device if not already initialized +* 'spi_lobo_bus_remove_device' automatically removes spi bus device if no other devices are attached to it. +* Devices can have individual bus_configs, so different mosi, miso, sck pins can be configured for each device + Reconfiguring the bus is done automaticaly in 'spi_lobo_device_select' function +* 'spi_lobo_device_select' & 'spi_lobo_device_deselect' functions handles devices configuration changes and software CS +* Some helper functions are added ('spi_lobo_get_speed', 'spi_lobo_set_speed', ...) +* All structures are available in header file for easy creation of user low level spi functions. See **tftfunc.c** source for examples. +* Transimt and receive lenghts are limited only by available memory + + +Main driver's function is 'spi_lobo_transfer_data()' + + * TRANSMIT 8-bit data to spi device from 'trans->tx_buffer' or 'trans->tx_data' (trans->lenght/8 bytes) + * and RECEIVE data to 'trans->rx_buffer' or 'trans->rx_data' (trans->rx_length/8 bytes) + * Lengths must be 8-bit multiples! + * If trans->rx_buffer is NULL or trans->rx_length is 0, only transmits data + * If trans->tx_buffer is NULL or trans->length is 0, only receives data + * If the device is in duplex mode (SPI_DEVICE_HALFDUPLEX flag NOT set), data are transmitted and received simultaneously. + * If the device is in half duplex mode (SPI_DEVICE_HALFDUPLEX flag IS set), data are received after transmission + * 'address', 'command' and 'dummy bits' are transmitted before data phase IF set in device's configuration + * and IF 'trans->length' and 'trans->rx_length' are NOT both 0 + * If configured, devices 'pre_cb' callback is called before and 'post_cb' after the transmission + * If device was not previously selected, it will be selected before transmission and deselected after transmission. + +*/ + +/* + Replace this include with + #include "driver/spi_master_lobo.h" + if the driver is located in esp-isf/components +*/ +#include "freertos/FreeRTOS.h" +#include +#include +#include "soc/gpio_sig_map.h" +#include "soc/spi_reg.h" +#include "soc/dport_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "rom/ets_sys.h" +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_err.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/soc.h" +#include "soc/dport_reg.h" +#include "soc/uart_struct.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" +#include "esp_heap_caps.h" +#include "driver/periph_ctrl.h" +#include "spi_master_lobo.h" + + +static spi_lobo_host_t *spihost[3] = {NULL}; + + +static const char *SPI_TAG = "spi_lobo_master"; +#define SPI_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } + +/* + Stores a bunch of per-spi-peripheral data. +*/ +typedef struct { + const uint8_t spiclk_out; //GPIO mux output signals + const uint8_t spid_out; + const uint8_t spiq_out; + const uint8_t spiwp_out; + const uint8_t spihd_out; + const uint8_t spid_in; //GPIO mux input signals + const uint8_t spiq_in; + const uint8_t spiwp_in; + const uint8_t spihd_in; + const uint8_t spics_out[3]; // /CS GPIO output mux signals + const uint8_t spiclk_native; //IO pins of IO_MUX muxed signals + const uint8_t spid_native; + const uint8_t spiq_native; + const uint8_t spiwp_native; + const uint8_t spihd_native; + const uint8_t spics0_native; + const uint8_t irq; //irq source for interrupt mux + const uint8_t irq_dma; //dma irq source for interrupt mux + const periph_module_t module; //peripheral module, for enabling clock etc + spi_dev_t *hw; //Pointer to the hardware registers +} spi_signal_conn_t; + +/* + Bunch of constants for every SPI peripheral: GPIO signals, irqs, hw addr of registers etc +*/ +static const spi_signal_conn_t io_signal[3]={ + { + .spiclk_out=SPICLK_OUT_IDX, + .spid_out=SPID_OUT_IDX, + .spiq_out=SPIQ_OUT_IDX, + .spiwp_out=SPIWP_OUT_IDX, + .spihd_out=SPIHD_OUT_IDX, + .spid_in=SPID_IN_IDX, + .spiq_in=SPIQ_IN_IDX, + .spiwp_in=SPIWP_IN_IDX, + .spihd_in=SPIHD_IN_IDX, + .spics_out={SPICS0_OUT_IDX, SPICS1_OUT_IDX, SPICS2_OUT_IDX}, + .spiclk_native=6, + .spid_native=8, + .spiq_native=7, + .spiwp_native=10, + .spihd_native=9, + .spics0_native=11, + .irq=ETS_SPI1_INTR_SOURCE, + .irq_dma=ETS_SPI1_DMA_INTR_SOURCE, + .module=PERIPH_SPI_MODULE, + .hw=&SPI1 + }, { + .spiclk_out=HSPICLK_OUT_IDX, + .spid_out=HSPID_OUT_IDX, + .spiq_out=HSPIQ_OUT_IDX, + .spiwp_out=HSPIWP_OUT_IDX, + .spihd_out=HSPIHD_OUT_IDX, + .spid_in=HSPID_IN_IDX, + .spiq_in=HSPIQ_IN_IDX, + .spiwp_in=HSPIWP_IN_IDX, + .spihd_in=HSPIHD_IN_IDX, + .spics_out={HSPICS0_OUT_IDX, HSPICS1_OUT_IDX, HSPICS2_OUT_IDX}, + .spiclk_native=14, + .spid_native=13, + .spiq_native=12, + .spiwp_native=2, + .spihd_native=4, + .spics0_native=15, + .irq=ETS_SPI2_INTR_SOURCE, + .irq_dma=ETS_SPI2_DMA_INTR_SOURCE, + .module=PERIPH_HSPI_MODULE, + .hw=&SPI2 + }, { + .spiclk_out=VSPICLK_OUT_IDX, + .spid_out=VSPID_OUT_IDX, + .spiq_out=VSPIQ_OUT_IDX, + .spiwp_out=VSPIWP_OUT_IDX, + .spihd_out=VSPIHD_OUT_IDX, + .spid_in=VSPID_IN_IDX, + .spiq_in=VSPIQ_IN_IDX, + .spiwp_in=VSPIWP_IN_IDX, + .spihd_in=VSPIHD_IN_IDX, + .spics_out={VSPICS0_OUT_IDX, VSPICS1_OUT_IDX, VSPICS2_OUT_IDX}, + .spiclk_native=18, + .spid_native=23, + .spiq_native=19, + .spiwp_native=22, + .spihd_native=21, + .spics0_native=5, + .irq=ETS_SPI3_INTR_SOURCE, + .irq_dma=ETS_SPI3_DMA_INTR_SOURCE, + .module=PERIPH_VSPI_MODULE, + .hw=&SPI3 + } +}; + + +//====================================================================================================== + +#define DMA_CHANNEL_ENABLED(dma_chan) (BIT(dma_chan-1)) + +typedef void(*dmaworkaround_cb_t)(void *arg); + +//Set up a list of dma descriptors. dmadesc is an array of descriptors. Data is the buffer to point to. +//-------------------------------------------------------------------------------------------- +void spi_lobo_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx) +{ + int n = 0; + while (len) { + int dmachunklen = len; + if (dmachunklen > SPI_MAX_DMA_LEN) dmachunklen = SPI_MAX_DMA_LEN; + if (isrx) { + //Receive needs DMA length rounded to next 32-bit boundary + dmadesc[n].size = (dmachunklen + 3) & (~3); + dmadesc[n].length = (dmachunklen + 3) & (~3); + } else { + dmadesc[n].size = dmachunklen; + dmadesc[n].length = dmachunklen; + } + dmadesc[n].buf = (uint8_t *)data; + dmadesc[n].eof = 0; + dmadesc[n].sosf = 0; + dmadesc[n].owner = 1; + dmadesc[n].qe.stqe_next = &dmadesc[n + 1]; + len -= dmachunklen; + data += dmachunklen; + n++; + } + dmadesc[n - 1].eof = 1; //Mark last DMA desc as end of stream. + dmadesc[n - 1].qe.stqe_next = NULL; +} + + +/* +Code for workaround for DMA issue in ESP32 v0/v1 silicon +*/ + + +static volatile int dmaworkaround_channels_busy[2] = {0, 0}; +static dmaworkaround_cb_t dmaworkaround_cb; +static void *dmaworkaround_cb_arg; +static portMUX_TYPE dmaworkaround_mux = portMUX_INITIALIZER_UNLOCKED; +static int dmaworkaround_waiting_for_chan = 0; +static bool spi_periph_claimed[3] = {true, false, false}; +static uint8_t spi_dma_chan_enabled = 0; +static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED; + +//-------------------------------------------------------------------------------------------- +bool IRAM_ATTR spi_lobo_dmaworkaround_req_reset(int dmachan, dmaworkaround_cb_t cb, void *arg) +{ + int otherchan = (dmachan == 1) ? 2 : 1; + bool ret; + portENTER_CRITICAL(&dmaworkaround_mux); + if (dmaworkaround_channels_busy[otherchan-1]) { + //Other channel is busy. Call back when it's done. + dmaworkaround_cb = cb; + dmaworkaround_cb_arg = arg; + dmaworkaround_waiting_for_chan = otherchan; + ret = false; + } else { + //Reset DMA + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST); + ret = true; + } + portEXIT_CRITICAL(&dmaworkaround_mux); + return ret; +} + +//------------------------------------------------------- +bool IRAM_ATTR spi_lobo_dmaworkaround_reset_in_progress() +{ + return (dmaworkaround_waiting_for_chan != 0); +} + +//----------------------------------------------------- +void IRAM_ATTR spi_lobo_dmaworkaround_idle(int dmachan) +{ + portENTER_CRITICAL(&dmaworkaround_mux); + dmaworkaround_channels_busy[dmachan-1] = 0; + if (dmaworkaround_waiting_for_chan == dmachan) { + //Reset DMA + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST); + dmaworkaround_waiting_for_chan = 0; + //Call callback + dmaworkaround_cb(dmaworkaround_cb_arg); + + } + portEXIT_CRITICAL(&dmaworkaround_mux); +} + +//---------------------------------------------------------------- +void IRAM_ATTR spi_lobo_dmaworkaround_transfer_active(int dmachan) +{ + portENTER_CRITICAL(&dmaworkaround_mux); + dmaworkaround_channels_busy[dmachan-1] = 1; + portEXIT_CRITICAL(&dmaworkaround_mux); +} + +//Returns true if this peripheral is successfully claimed, false if otherwise. +//----------------------------------------------------- +bool spi_lobo_periph_claim(spi_lobo_host_device_t host) +{ + bool ret = __sync_bool_compare_and_swap(&spi_periph_claimed[host], false, true); + if (ret) periph_module_enable(io_signal[host].module); + return ret; +} + +//Returns true if this peripheral is successfully freed, false if otherwise. +//----------------------------------------------- +bool spi_lobo_periph_free(spi_lobo_host_device_t host) +{ + bool ret = __sync_bool_compare_and_swap(&spi_periph_claimed[host], true, false); + if (ret) periph_module_disable(io_signal[host].module); + return ret; +} + +//----------------------------------------- +bool spi_lobo_dma_chan_claim (int dma_chan) +{ + bool ret = false; + assert( dma_chan == 1 || dma_chan == 2 ); + + portENTER_CRITICAL(&spi_dma_spinlock); + if ( !(spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan)) ) { + // get the channel only when it's not claimed yet. + spi_dma_chan_enabled |= DMA_CHANNEL_ENABLED(dma_chan); + ret = true; + } + periph_module_enable( PERIPH_SPI_DMA_MODULE ); + portEXIT_CRITICAL(&spi_dma_spinlock); + + return ret; +} + +//--------------------------------------- +bool spi_lobo_dma_chan_free(int dma_chan) +{ + assert( dma_chan == 1 || dma_chan == 2 ); + assert( spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan) ); + + portENTER_CRITICAL(&spi_dma_spinlock); + spi_dma_chan_enabled &= ~DMA_CHANNEL_ENABLED(dma_chan); + if ( spi_dma_chan_enabled == 0 ) { + //disable the DMA only when all the channels are freed. + periph_module_disable( PERIPH_SPI_DMA_MODULE ); + } + portEXIT_CRITICAL(&spi_dma_spinlock); + + return true; +} + + +//====================================================================================================== + + +//---------------------------------------------------------------------------------------------------------------- +static esp_err_t spi_lobo_bus_initialize(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, int init) +{ + bool native=true, spi_chan_claimed, dma_chan_claimed; + + if (init > 0) { + /* ToDo: remove this when we have flash operations cooperating with this */ + SPI_CHECK(host!=SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED); + + SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG); + SPI_CHECK(spihost[host]==NULL, "host already in use", ESP_ERR_INVALID_STATE); + } + else { + SPI_CHECK(spihost[host]!=NULL, "host not in use", ESP_ERR_INVALID_STATE); + } + + SPI_CHECK(bus_config->mosi_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->sclk_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->sclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->miso_io_num<0 || GPIO_IS_VALID_GPIO(bus_config->miso_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->quadwp_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG); + SPI_CHECK(bus_config->quadhd_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadhd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG); + + if (init > 0) { + spi_chan_claimed=spi_lobo_periph_claim(host); + SPI_CHECK(spi_chan_claimed, "host already in use", ESP_ERR_INVALID_STATE); + + //spihost[host]=malloc(sizeof(spi_lobo_host_t)); + spihost[host]=heap_caps_malloc(sizeof(spi_lobo_host_t), MALLOC_CAP_DMA); + if (spihost[host]==NULL) return ESP_ERR_NO_MEM; + memset(spihost[host], 0, sizeof(spi_lobo_host_t)); + // Create semaphore + spihost[host]->spi_lobo_bus_mutex = xSemaphoreCreateMutex(); + if (!spihost[host]->spi_lobo_bus_mutex) return ESP_ERR_NO_MEM; + } + + spihost[host]->cur_device = -1; + memcpy(&spihost[host]->cur_bus_config, bus_config, sizeof(spi_lobo_bus_config_t)); + + //Check if the selected pins correspond to the native pins of the peripheral + if (bus_config->mosi_io_num >= 0 && bus_config->mosi_io_num!=io_signal[host].spid_native) native=false; + if (bus_config->miso_io_num >= 0 && bus_config->miso_io_num!=io_signal[host].spiq_native) native=false; + if (bus_config->sclk_io_num >= 0 && bus_config->sclk_io_num!=io_signal[host].spiclk_native) native=false; + if (bus_config->quadwp_io_num >= 0 && bus_config->quadwp_io_num!=io_signal[host].spiwp_native) native=false; + if (bus_config->quadhd_io_num >= 0 && bus_config->quadhd_io_num!=io_signal[host].spihd_native) native=false; + + spihost[host]->no_gpio_matrix=native; + if (native) { + //All SPI native pin selections resolve to 1, so we put that here instead of trying to figure + //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway. + if (bus_config->mosi_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], 1); + if (bus_config->miso_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], 1); + if (bus_config->quadwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], 1); + if (bus_config->quadhd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], 1); + if (bus_config->sclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], 1); + } else { + //Use GPIO + if (bus_config->mosi_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->mosi_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->mosi_io_num, io_signal[host].spid_out, false, false); + gpio_matrix_in(bus_config->mosi_io_num, io_signal[host].spid_in, false); + } + if (bus_config->miso_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT); + gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false); + gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false); + } + if (bus_config->quadwp_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->quadwp_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->quadwp_io_num, io_signal[host].spiwp_out, false, false); + gpio_matrix_in(bus_config->quadwp_io_num, io_signal[host].spiwp_in, false); + } + if (bus_config->quadhd_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->quadhd_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->quadhd_io_num, io_signal[host].spihd_out, false, false); + gpio_matrix_in(bus_config->quadhd_io_num, io_signal[host].spihd_in, false); + } + if (bus_config->sclk_io_num>0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->sclk_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(bus_config->sclk_io_num, io_signal[host].spiclk_out, false, false); + } + } + periph_module_enable(io_signal[host].module); + spihost[host]->hw=io_signal[host].hw; + + if (init > 0) { + dma_chan_claimed=spi_lobo_dma_chan_claim(init); + if ( !dma_chan_claimed ) { + spi_lobo_periph_free( host ); + SPI_CHECK(dma_chan_claimed, "dma channel already in use", ESP_ERR_INVALID_STATE); + } + spihost[host]->dma_chan = init; + //See how many dma descriptors we need and allocate them + int dma_desc_ct=(bus_config->max_transfer_sz+SPI_MAX_DMA_LEN-1)/SPI_MAX_DMA_LEN; + if (dma_desc_ct==0) dma_desc_ct=1; //default to 4k when max is not given + spihost[host]->max_transfer_sz = dma_desc_ct*SPI_MAX_DMA_LEN; + + spihost[host]->dmadesc_tx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA); + spihost[host]->dmadesc_rx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA); + if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) goto nomem; + + //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. + spi_lobo_dmaworkaround_idle(spihost[host]->dma_chan); + + // Reset DMA + spihost[host]->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + spihost[host]->hw->dma_out_link.start=0; + spihost[host]->hw->dma_in_link.start=0; + spihost[host]->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + spihost[host]->hw->dma_conf.out_data_burst_en=1; + + //Reset timing + spihost[host]->hw->ctrl2.val=0; + + //Disable unneeded ints + spihost[host]->hw->slave.rd_buf_done=0; + spihost[host]->hw->slave.wr_buf_done=0; + spihost[host]->hw->slave.rd_sta_done=0; + spihost[host]->hw->slave.wr_sta_done=0; + spihost[host]->hw->slave.rd_buf_inten=0; + spihost[host]->hw->slave.wr_buf_inten=0; + spihost[host]->hw->slave.rd_sta_inten=0; + spihost[host]->hw->slave.wr_sta_inten=0; + + //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as + //disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling + //any transactions that are queued. + spihost[host]->hw->slave.trans_inten=1; + spihost[host]->hw->slave.trans_done=1; + + //Select DMA channel. + DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, init, (host * 2)); + } + return ESP_OK; + +nomem: + if (spihost[host]) { + free(spihost[host]->dmadesc_tx); + free(spihost[host]->dmadesc_rx); + } + free(spihost[host]); + spi_lobo_periph_free(host); + return ESP_ERR_NO_MEM; +} + +//--------------------------------------------------------------------------- +static esp_err_t spi_lobo_bus_free(spi_lobo_host_device_t host, int dofree) +{ + if ((host == SPI_HOST) || (host >VSPI_HOST)) return ESP_ERR_NOT_SUPPORTED; // invalid host + + if (spihost[host] == NULL) return ESP_ERR_INVALID_STATE; // host not in use + + if (dofree) { + for (int x=0; xdevice[x] != NULL) return ESP_ERR_INVALID_STATE; // not all devices freed + } + } + if ( spihost[host]->dma_chan > 0 ) { + spi_lobo_dma_chan_free ( spihost[host]->dma_chan ); + } + spihost[host]->hw->slave.trans_inten=0; + spihost[host]->hw->slave.trans_done=0; + spi_lobo_periph_free(host); + + if (dofree) { + vSemaphoreDelete(spihost[host]->spi_lobo_bus_mutex); + free(spihost[host]->dmadesc_tx); + free(spihost[host]->dmadesc_rx); + free(spihost[host]); + spihost[host] = NULL; + } + return ESP_OK; +} + +//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +esp_err_t spi_lobo_bus_add_device(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, spi_lobo_device_interface_config_t *dev_config, spi_lobo_device_handle_t *handle) +{ + if ((host == SPI_HOST) || (host >VSPI_HOST)) return ESP_ERR_NOT_SUPPORTED; // invalid host + + if (spihost[host] == NULL) { + esp_err_t ret = spi_lobo_bus_initialize(host, bus_config, 1); + if (ret) return ret; + } + + int freecs, maxdev; + int apbclk=APB_CLK_FREQ; + + if (spihost[host] == NULL) return ESP_ERR_INVALID_STATE; + + if (dev_config->spics_io_num >= 0) { + if (!GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num)) return ESP_ERR_INVALID_ARG; + if (dev_config->spics_ext_io_num > 0) dev_config->spics_ext_io_num = -1; + } + else { + //if ((dev_config->spics_ext_io_num <= 0) || (!GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_ext_io_num))) return ESP_ERR_INVALID_ARG; + } + + //ToDo: Check if some other device uses the same 'spics_ext_io_num' + + if (dev_config->clock_speed_hz == 0) return ESP_ERR_INVALID_ARG; + if (dev_config->spics_io_num > 0) maxdev = NO_CS; + else maxdev = NO_DEV; + + for (freecs=0; freecsdevice[freecs], NULL, (spi_lobo_device_t *)1)) break; + } + if (freecs == maxdev) return ESP_ERR_NOT_FOUND; + + // The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full + // duplex mode does absolutely nothing on the ESP32. + if ((dev_config->cs_ena_pretrans != 0) && (dev_config->flags & SPI_DEVICE_HALFDUPLEX)) return ESP_ERR_INVALID_ARG; + + // Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections. + if (((dev_config->flags & SPI_DEVICE_HALFDUPLEX)==0) && (dev_config->clock_speed_hz > ((apbclk*2)/5)) && (!spihost[host]->no_gpio_matrix)) return ESP_ERR_INVALID_ARG; + + //Allocate memory for device + spi_lobo_device_t *dev=malloc(sizeof(spi_lobo_device_t)); + if (dev==NULL) return ESP_ERR_NO_MEM; + + memset(dev, 0, sizeof(spi_lobo_device_t)); + spihost[host]->device[freecs]=dev; + + if (dev_config->duty_cycle_pos==0) dev_config->duty_cycle_pos=128; + dev->host=spihost[host]; + dev->host_dev = host; + + //We want to save a copy of the dev config in the dev struct. + memcpy(&dev->cfg, dev_config, sizeof(spi_lobo_device_interface_config_t)); + //We want to save a copy of the bus config in the dev struct. + memcpy(&dev->bus_config, bus_config, sizeof(spi_lobo_bus_config_t)); + + //Set CS pin, CS options + if (dev_config->spics_io_num > 0) { + if (spihost[host]->no_gpio_matrix &&dev_config->spics_io_num == io_signal[host].spics0_native && freecs==0) { + //Again, the cs0s for all SPI peripherals map to pin mux source 1, so we use that instead of a define. + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], 1); + } else { + //Use GPIO matrix + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], PIN_FUNC_GPIO); + gpio_set_direction(dev_config->spics_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(dev_config->spics_io_num, io_signal[host].spics_out[freecs], false, false); + } + } + else if (dev_config->spics_ext_io_num >= 0) { + gpio_set_direction(dev_config->spics_ext_io_num, GPIO_MODE_OUTPUT); + gpio_set_level(dev_config->spics_ext_io_num, 1); + } + if (dev_config->flags & SPI_DEVICE_CLK_AS_CS) { + spihost[host]->hw->pin.master_ck_sel |= (1<hw->pin.master_ck_sel &= (1<flags & SPI_DEVICE_POSITIVE_CS) { + spihost[host]->hw->pin.master_cs_pol |= (1<hw->pin.master_cs_pol &= (1<host->device[x] == handle) handle->host->device[x]=NULL; + } + + // Check if all devices are removed from this host and free the bus if yes + for (x=0; xhost_dev]->device[x] !=NULL) break; + } + if (x == NO_DEV) { + free(handle); + spi_lobo_bus_free(handle->host_dev, 1); + } + else free(handle); + + return ESP_OK; +} + +//----------------------------------------------------------------- +static int IRAM_ATTR spi_freq_for_pre_n(int fapb, int pre, int n) { + return (fapb / (pre * n)); +} + +/* + * Set the SPI clock to a certain frequency. Returns the effective frequency set, which may be slightly + * different from the requested frequency. + */ +//----------------------------------------------------------------------------------- +static int IRAM_ATTR spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) { + int pre, n, h, l, eff_clk; + + //In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value. + if (hz>((fapb/4)*3)) { + //Using Fapb directly will give us the best result here. + hw->clock.clkcnt_l=0; + hw->clock.clkcnt_h=0; + hw->clock.clkcnt_n=0; + hw->clock.clkdiv_pre=0; + hw->clock.clk_equ_sysclk=1; + eff_clk=fapb; + } else { + //For best duty cycle resolution, we want n to be as close to 32 as possible, but + //we also need a pre/n combo that gets us as close as possible to the intended freq. + //To do this, we bruteforce n and calculate the best pre to go along with that. + //If there's a choice between pre/n combos that give the same result, use the one + //with the higher n. + int bestn=-1; + int bestpre=-1; + int besterr=0; + int errval; + for (n=1; n<=64; n++) { + //Effectively, this does pre=round((fapb/n)/hz). + pre=((fapb/n)+(hz/2))/hz; + if (pre<=0) pre=1; + if (pre>8192) pre=8192; + errval=abs(spi_freq_for_pre_n(fapb, pre, n)-hz); + if (bestn==-1 || errval<=besterr) { + besterr=errval; + bestn=n; + bestpre=pre; + } + } + + n=bestn; + pre=bestpre; + l=n; + //This effectively does round((duty_cycle*n)/256) + h=(duty_cycle*n+127)/256; + if (h<=0) h=1; + + hw->clock.clk_equ_sysclk=0; + hw->clock.clkcnt_n=n-1; + hw->clock.clkdiv_pre=pre-1; + hw->clock.clkcnt_h=h-1; + hw->clock.clkcnt_l=l-1; + eff_clk=spi_freq_for_pre_n(fapb, pre, n); + } + return eff_clk; +} + + + +//------------------------------------------------------------------------------------ +esp_err_t IRAM_ATTR spi_lobo_device_select(spi_lobo_device_handle_t handle, int force) +{ + if (handle == NULL) return ESP_ERR_INVALID_ARG; + + if ((handle->cfg.selected == 1) && (!force)) return ESP_OK; // already selected + + int i; + spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host; + + // find device's host bus + for (i=0; idevice[i] == handle) break; + } + if (i == NO_DEV) return ESP_ERR_INVALID_ARG; + + if (!(xSemaphoreTake(host->spi_lobo_bus_mutex, SPI_SEMAPHORE_WAIT))) return ESP_ERR_INVALID_STATE; + + // Check if previously used device's bus device is the same + if (memcmp(&host->cur_bus_config, &handle->bus_config, sizeof(spi_lobo_bus_config_t)) != 0) { + // device has different bus configuration, we need to reconfigure the bus + esp_err_t err = spi_lobo_bus_free(1, 0); + if (err) { + xSemaphoreGive(host->spi_lobo_bus_mutex); + return err; + } + err = spi_lobo_bus_initialize(i, &handle->bus_config, -1); + if (err) { + xSemaphoreGive(host->spi_lobo_bus_mutex); + return err; + } + } + + //Reconfigure according to device settings, but only if the device changed or forced. + if ((force) || (host->device[host->cur_device] != handle)) { + //Assumes a hardcoded 80MHz Fapb for now. ToDo: figure out something better once we have clock scaling working. + int apbclk=APB_CLK_FREQ; + + //Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections. + if (((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) == 0) && (handle->cfg.clock_speed_hz > ((apbclk*2)/5)) && (!host->no_gpio_matrix)) { + // set speed to 32 MHz + handle->cfg.clock_speed_hz = (apbclk*2)/5; + } + + int effclk=spi_set_clock(host->hw, apbclk, handle->cfg.clock_speed_hz, handle->cfg.duty_cycle_pos); + //Configure bit order + host->hw->ctrl.rd_bit_order=(handle->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0; + host->hw->ctrl.wr_bit_order=(handle->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0; + + //Configure polarity + //SPI iface needs to be configured for a delay in some cases. + int nodelay=0; + int extra_dummy=0; + if (host->no_gpio_matrix) { + if (effclk >= apbclk/2) { + nodelay=1; + } + } else { + if (effclk >= apbclk/2) { + nodelay=1; + extra_dummy=1; //Note: This only works on half-duplex connections. spi_lobo_bus_add_device checks for this. + } else if (effclk >= apbclk/4) { + nodelay=1; + } + } + if (handle->cfg.mode==0) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } else if (handle->cfg.mode==1) { + host->hw->pin.ck_idle_edge=0; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (handle->cfg.mode==2) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=1; + host->hw->ctrl2.miso_delay_mode=nodelay?0:1; + } else if (handle->cfg.mode==3) { + host->hw->pin.ck_idle_edge=1; + host->hw->user.ck_out_edge=0; + host->hw->ctrl2.miso_delay_mode=nodelay?0:2; + } + + //Configure bit sizes, load addr and command + host->hw->user.usr_dummy=(handle->cfg.dummy_bits+extra_dummy)?1:0; + host->hw->user.usr_addr=(handle->cfg.address_bits)?1:0; + host->hw->user.usr_command=(handle->cfg.command_bits)?1:0; + host->hw->user1.usr_addr_bitlen=handle->cfg.address_bits-1; + host->hw->user1.usr_dummy_cyclelen=handle->cfg.dummy_bits+extra_dummy-1; + host->hw->user2.usr_command_bitlen=handle->cfg.command_bits-1; + //Configure misc stuff + host->hw->user.doutdin=(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1; + host->hw->user.sio=(handle->cfg.flags & SPI_DEVICE_3WIRE)?1:0; + + host->hw->ctrl2.setup_time=handle->cfg.cs_ena_pretrans-1; + host->hw->user.cs_setup=handle->cfg.cs_ena_pretrans?1:0; + host->hw->ctrl2.hold_time=handle->cfg.cs_ena_posttrans-1; + host->hw->user.cs_hold=(handle->cfg.cs_ena_posttrans)?1:0; + + //Configure CS pin + host->hw->pin.cs0_dis=(i==0)?0:1; + host->hw->pin.cs1_dis=(i==1)?0:1; + host->hw->pin.cs2_dis=(i==2)?0:1; + + host->cur_device = i; + } + + if ((handle->cfg.spics_io_num < 0) && (handle->cfg.spics_ext_io_num > 0)) { + gpio_set_level(handle->cfg.spics_ext_io_num, 0); + } + + handle->cfg.selected = 1; + + return ESP_OK; +} + +//--------------------------------------------------------------------------- +esp_err_t IRAM_ATTR spi_lobo_device_deselect(spi_lobo_device_handle_t handle) +{ + if (handle == NULL) return ESP_ERR_INVALID_ARG; + + if (handle->cfg.selected == 0) return ESP_OK; // already deselected + + int i; + spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host; + + for (i=0; idevice[i] == handle) break; + } + if (i == NO_DEV) return ESP_ERR_INVALID_ARG; + + if (host->device[host->cur_device] == handle) { + if ((handle->cfg.spics_io_num < 0) && (handle->cfg.spics_ext_io_num > 0)) { + gpio_set_level(handle->cfg.spics_ext_io_num, 1); + } + } + + handle->cfg.selected = 0; + xSemaphoreGive(host->spi_lobo_bus_mutex); + + return ESP_OK; +} + +//-------------------------------------------------------------------------------- +esp_err_t IRAM_ATTR spi_lobo_device_TakeSemaphore(spi_lobo_device_handle_t handle) +{ + if (!(xSemaphoreTake(handle->host->spi_lobo_bus_mutex, SPI_SEMAPHORE_WAIT))) return ESP_ERR_INVALID_STATE; + else return ESP_OK; +} + +//--------------------------------------------------------------------------- +void IRAM_ATTR spi_lobo_device_GiveSemaphore(spi_lobo_device_handle_t handle) +{ + xSemaphoreTake(handle->host->spi_lobo_bus_mutex, portMAX_DELAY); +} + +//---------------------------------------------------------- +uint32_t spi_lobo_get_speed(spi_lobo_device_handle_t handle) +{ + spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host; + uint32_t speed = 0; + if (spi_lobo_device_select(handle, 0) == ESP_OK) { + if (host->hw->clock.clk_equ_sysclk == 1) speed = 80000000; + else speed = 80000000/(host->hw->clock.clkdiv_pre+1)/(host->hw->clock.clkcnt_n+1); + } + spi_lobo_device_deselect(handle); + return speed; +} + +//-------------------------------------------------------------------------- +uint32_t spi_lobo_set_speed(spi_lobo_device_handle_t handle, uint32_t speed) +{ + spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host; + uint32_t newspeed = 0; + if (spi_lobo_device_select(handle, 0) == ESP_OK) { + spi_lobo_device_deselect(handle); + handle->cfg.clock_speed_hz = speed; + if (spi_lobo_device_select(handle, 1) == ESP_OK) { + if (host->hw->clock.clk_equ_sysclk == 1) newspeed = 80000000; + else newspeed = 80000000/(host->hw->clock.clkdiv_pre+1)/(host->hw->clock.clkcnt_n+1); + } + } + spi_lobo_device_deselect(handle); + + return newspeed; +} + +//------------------------------------------------------------- +bool spi_lobo_uses_native_pins(spi_lobo_device_handle_t handle) +{ + return handle->host->no_gpio_matrix; +} + +//------------------------------------------------------------------- +void spi_lobo_get_native_pins(int host, int *sdi, int *sdo, int *sck) +{ + *sdo = io_signal[host].spid_native; + *sdi = io_signal[host].spiq_native; + *sck = io_signal[host].spiclk_native; +} + +/* +When using 'spi_lobo_transfer_data' function we can have several scenarios: + +A: Send only (trans->rxlength = 0) +B: Receive only (trans->txlength = 0) +C: Send & receive (trans->txlength > 0 & trans->rxlength > 0) +D: No operation (trans->txlength = 0 & trans->rxlength = 0) + +*/ +//---------------------------------------------------------------------------------------------------------- +esp_err_t IRAM_ATTR spi_lobo_transfer_data(spi_lobo_device_handle_t handle, spi_lobo_transaction_t *trans) { + if (!handle) return ESP_ERR_INVALID_ARG; + + // *** For now we can only handle 8-bit bytes transmission + if (((trans->length % 8) != 0) || ((trans->rxlength % 8) != 0)) return ESP_ERR_INVALID_ARG; + + spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host; + esp_err_t ret; + uint8_t do_deselect = 0; + const uint8_t *txbuffer = NULL; + uint8_t *rxbuffer = NULL; + + if (trans->flags & SPI_TRANS_USE_TXDATA) { + // Send data from 'trans->tx_data' + txbuffer=(uint8_t*)&trans->tx_data[0]; + } else { + // Send data from 'trans->tx_buffer' + txbuffer=(uint8_t*)trans->tx_buffer; + } + if (trans->flags & SPI_TRANS_USE_RXDATA) { + // Receive data to 'trans->rx_data' + rxbuffer=(uint8_t*)&trans->rx_data[0]; + } else { + // Receive data to 'trans->rx_buffer' + rxbuffer=(uint8_t*)trans->rx_buffer; + } + + // ** Set transmit & receive length in bytes + uint32_t txlen = trans->length / 8; + uint32_t rxlen = trans->rxlength / 8; + + if (txbuffer == NULL) txlen = 0; + if (rxbuffer == NULL) rxlen = 0; + if ((rxlen == 0) && (txlen == 0)) { + // ** NOTHING TO SEND or RECEIVE, return + return ESP_ERR_INVALID_ARG; + } + + // If using 'trans->tx_data' and/or 'trans->rx_data', maximum 4 bytes can be sent/received + if ((txbuffer == &trans->tx_data[0]) && (txlen > 4)) return ESP_ERR_INVALID_ARG; + if ((rxbuffer == &trans->rx_data[0]) && (rxlen > 4)) return ESP_ERR_INVALID_ARG; + + // --- Wait for SPI bus ready --- + while (host->hw->cmd.usr); + + // ** If the device was not selected, select it + if (handle->cfg.selected == 0) { + ret = spi_lobo_device_select(handle, 0); + if (ret) return ret; + do_deselect = 1; // We will deselect the device after the operation ! + } + + // ** Call pre-transmission callback, if any + if (handle->cfg.pre_cb) handle->cfg.pre_cb(trans); + + // Test if operating in full duplex mode + uint8_t duplex = 1; + if (handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) duplex = 0; // Half duplex mode ! + + uint32_t bits, rdbits; + uint32_t wd; + uint8_t bc, rdidx; + uint32_t rdcount = rxlen; // Total number of bytes to read + uint32_t count = 0; // number of bytes transmitted + uint32_t rd_read = 0; // Number of bytes read so far + + host->hw->user.usr_mosi_highpart = 0; // use the whole spi buffer + + // ** Check if address phase will be used + host->hw->user2.usr_command_value=trans->command; + if (handle->cfg.address_bits>32) { + host->hw->addr=trans->address >> 32; + host->hw->slv_wr_status=trans->address & 0xffffffff; + } else { + host->hw->addr=trans->address & 0xffffffff; + } + + // Check if we have to transmit some data + if (txlen > 0) { + host->hw->user.usr_mosi = 1; + uint8_t idx; + bits = 0; // remaining bits to send + idx = 0; // index to spi hw data_buf (16 32-bit words, 64 bytes, 512 bits) + + // ** Transmit 'txlen' bytes + while (count < txlen) { + wd = 0; + for (bc=0;bc<32;bc+=8) { + wd |= (uint32_t)txbuffer[count] << bc; + count++; // Increment sent data count + bits += 8; // Increment bits count + if (count == txlen) break; // If all transmit data pushed to hw spi buffer break from the loop + } + host->hw->data_buf[idx] = wd; + idx++; + if (idx == 16) { + // hw SPI buffer full (all 64 bytes filled, START THE TRANSSACTION + host->hw->mosi_dlen.usr_mosi_dbitlen=bits-1; // Set mosi dbitlen + + if ((duplex) && (rdcount > 0)) { + // In full duplex mode we are receiving while sending ! + host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen + host->hw->user.usr_miso = 1; + } + else { + host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received + host->hw->user.usr_miso = 0; + } + + // ** Start the transaction *** + host->hw->cmd.usr=1; + // Wait the transaction to finish + while (host->hw->cmd.usr); + + if ((duplex) && (rdcount > 0)) { + // *** in full duplex mode transfer received data to input buffer *** + rdidx = 0; + while (bits > 0) { + wd = host->hw->data_buf[rdidx]; + rdidx++; + for (bc=0;bc<32;bc+=8) { // get max 4 bytes + rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF); + rdcount--; + bits -= 8; + if (rdcount == 0) { + bits = 0; + break; // Finished reading data + } + } + } + } + bits = 0; // nothing in hw spi buffer yet + idx = 0; // start from the beginning of the hw spi buffer + } + } + // *** All transmit data are sent or pushed to hw spi buffer + // bits > 0 IF THERE ARE SOME DATA STILL WAITING IN THE HW SPI TRANSMIT BUFFER + if (bits > 0) { + // ** WE HAVE SOME DATA IN THE HW SPI TRANSMIT BUFFER + host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // Set mosi dbitlen + + if ((duplex) && (rdcount > 0)) { + // In full duplex mode we are receiving while sending ! + host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen + host->hw->user.usr_miso = 1; + } + else { + host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received + host->hw->user.usr_miso = 0; + } + + // ** Start the transaction *** + host->hw->cmd.usr=1; + // Wait the transaction to finish + while (host->hw->cmd.usr); + + if ((duplex) && (rdcount > 0)) { + // *** in full duplex mode transfer received data to input buffer *** + rdidx = 0; + while (bits > 0) { + wd = host->hw->data_buf[rdidx]; + rdidx++; + for (bc=0;bc<32;bc+=8) { // get max 4 bytes + rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF); + rdcount--; + bits -= 8; + if (bits == 0) break; + if (rdcount == 0) { + bits = 0; + break; // Finished reading data + } + } + } + } + } + //if (duplex) rdcount = 0; // In duplex mode receive only as many bytes as was transmitted + } + + // ------------------------------------------------------------------------ + // *** If rdcount = 0 we have nothing to receive and we exit the function + // This is true if no data receive was requested, + // or all the data was received in Full duplex mode during the transmission + // ------------------------------------------------------------------------ + if (rdcount > 0) { + // ---------------------------------------------------------------------------------------------------------------- + // *** rdcount > 0, we have to receive some data + // This is true if we operate in Half duplex mode when receiving after transmission is done, + // or not all data was received in Full duplex mode during the transmission (trans->rxlength > trans->txlength) + // ---------------------------------------------------------------------------------------------------------------- + host->hw->user.usr_mosi = 0; // do not send + host->hw->user.usr_miso = 1; // do receive + while (rdcount > 0) { + if (rdcount <= 64) rdbits = rdcount * 8; + else rdbits = 64 * 8; + + // Load receive buffer + host->hw->mosi_dlen.usr_mosi_dbitlen=0; + host->hw->miso_dlen.usr_miso_dbitlen=rdbits-1; + + // ** Start the transaction *** + host->hw->cmd.usr=1; + // Wait the transaction to finish + while (host->hw->cmd.usr); + + // *** transfer received data to input buffer *** + rdidx = 0; + while (rdbits > 0) { + wd = host->hw->data_buf[rdidx]; + rdidx++; + for (bc=0;bc<32;bc+=8) { + rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF); + rdcount--; + rdbits -= 8; + if (rdcount == 0) { + rdbits = 0; + break; + } + } + } + } + } + + // ** Call post-transmission callback, if any + if (handle->cfg.post_cb) handle->cfg.post_cb(trans); + + if (do_deselect) { + // Spi device was selected in this function, we have to deselect it now + ret = spi_lobo_device_deselect(handle); + if (ret) return ret; + } + + return ESP_OK; +} diff --git a/components/spidriver/spi_master_lobo.h b/components/spidriver/spi_master_lobo.h new file mode 100644 index 0000000..863ba82 --- /dev/null +++ b/components/spidriver/spi_master_lobo.h @@ -0,0 +1,355 @@ +// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#ifndef _DRIVER_SPI_MASTER_LOBO_H_ +#define _DRIVER_SPI_MASTER_LOBO_H_ + +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "soc/spi_struct.h" + +#include "esp_intr.h" +#include "esp_intr_alloc.h" +#include "rom/lldesc.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +//Maximum amount of bytes that can be put in one DMA descriptor +#define SPI_MAX_DMA_LEN (4096-4) + +/** + * @brief Enum with the three SPI peripherals that are software-accessible in it + */ +typedef enum { + SPI_HOST=0, ///< SPI1, SPI; Cannot be used in this driver! + HSPI_HOST=1, ///< SPI2, HSPI + VSPI_HOST=2 ///< SPI3, VSPI +} spi_lobo_host_device_t; + + +/** + * @brief This is a configuration structure for a SPI bus. + * + * You can use this structure to specify the GPIO pins of the bus. Normally, the driver will use the + * GPIO matrix to route the signals. An exception is made when all signals either can be routed through + * the IO_MUX or are -1. In that case, the IO_MUX is used, allowing for >40MHz speeds. + */ +typedef struct { + int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used. + int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used. + int sclk_io_num; ///< GPIO pin for Spi CLocK signal, or -1 if not used. + int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used. + int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used. + int max_transfer_sz; ///< Maximum transfer size, in bytes. Defaults to 4094 if 0. +} spi_lobo_bus_config_t; + + +#define SPI_DEVICE_TXBIT_LSBFIRST (1<<0) ///< Transmit command/address/data LSB first instead of the default MSB first +#define SPI_DEVICE_RXBIT_LSBFIRST (1<<1) ///< Receive data LSB first instead of the default MSB first +#define SPI_DEVICE_BIT_LSBFIRST (SPI_TXBIT_LSBFIRST|SPI_RXBIT_LSBFIRST); ///< Transmit and receive LSB first +#define SPI_DEVICE_3WIRE (1<<2) ///< Use spiq for both sending and receiving data +#define SPI_DEVICE_POSITIVE_CS (1<<3) ///< Make CS positive during a transaction instead of negative +#define SPI_DEVICE_HALFDUPLEX (1<<4) ///< Transmit data before receiving it, instead of simultaneously +#define SPI_DEVICE_CLK_AS_CS (1<<5) ///< Output clock on CS line if CS is active + +#define SPI_ERR_OTHER_CONFIG 7001 + +typedef struct spi_lobo_transaction_t spi_lobo_transaction_t; +typedef void(*transaction_cb_t)(spi_lobo_transaction_t *trans); + +/** + * @brief This is a configuration for a SPI slave device that is connected to one of the SPI buses. + */ +typedef struct { + uint8_t command_bits; ///< Amount of bits in command phase (0-16) + uint8_t address_bits; ///< Amount of bits in address phase (0-64) + uint8_t dummy_bits; ///< Amount of dummy bits to insert between address and data phase + uint8_t mode; ///< SPI mode (0-3) + uint8_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128. + uint8_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions. + uint8_t cs_ena_posttrans; ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16) + int clock_speed_hz; ///< Clock speed, in Hz + int spics_io_num; ///< CS GPIO pin for this device, handled by hardware; set to -1 if not used + int spics_ext_io_num; ///< CS GPIO pin for this device, handled by software (spi_lobo_device_select/spi_lobo_device_deselect); only used if spics_io_num=-1 + uint32_t flags; ///< Bitwise OR of SPI_DEVICE_* flags + transaction_cb_t pre_cb; ///< Callback to be called before a transmission is started. This callback from 'spi_lobo_transfer_data' function. + transaction_cb_t post_cb; ///< Callback to be called after a transmission has completed. This callback from 'spi_lobo_transfer_data' function. + uint8_t selected; ///< **INTERNAL** 1 if the device's CS pin is active +} spi_lobo_device_interface_config_t; + + +#define SPI_TRANS_MODE_DIO (1<<0) ///< Transmit/receive data in 2-bit mode +#define SPI_TRANS_MODE_QIO (1<<1) ///< Transmit/receive data in 4-bit mode +#define SPI_TRANS_MODE_DIOQIO_ADDR (1<<2) ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO +#define SPI_TRANS_USE_RXDATA (1<<3) ///< Receive into rx_data member of spi_lobo_transaction_t instead into memory at rx_buffer. +#define SPI_TRANS_USE_TXDATA (1<<4) ///< Transmit tx_data member of spi_lobo_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this. + +/** + * This structure describes one SPI transmission + */ +struct spi_lobo_transaction_t { + uint32_t flags; ///< Bitwise OR of SPI_TRANS_* flags + uint16_t command; ///< Command data. Specific length was given when device was added to the bus. + uint64_t address; ///< Address. Specific length was given when device was added to the bus. + size_t length; ///< Total data length to be transmitted to the device, in bits; if 0, no data is transmitted + size_t rxlength; ///< Total data length to be received from the device, in bits; if 0, no data is received + void *user; ///< User-defined variable. Can be used to store eg transaction ID or data to be used by pre_cb and/or post_cb callbacks. + union { + const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase + uint8_t tx_data[4]; ///< If SPI_USE_TXDATA is set, data set here is sent directly from this variable. + }; + union { + void *rx_buffer; ///< Pointer to receive buffer, or NULL for no MISO phase + uint8_t rx_data[4]; ///< If SPI_USE_RXDATA is set, data is received directly to this variable + }; +}; + +#define NO_CS 3 // Number of CS pins per SPI host +#define NO_DEV 6 // Number of spi devices per SPI host; more than 3 devices can be attached to the same bus if using software CS's +#define SPI_SEMAPHORE_WAIT 2000 // Time in ms to wait for SPI mutex + +typedef struct spi_lobo_device_t spi_lobo_device_t; + +typedef struct { + spi_lobo_device_t *device[NO_DEV]; + intr_handle_t intr; + spi_dev_t *hw; + //spi_lobo_transaction_t *cur_trans; + int cur_device; + lldesc_t *dmadesc_tx; + lldesc_t *dmadesc_rx; + bool no_gpio_matrix; + int dma_chan; + int max_transfer_sz; + QueueHandle_t spi_lobo_bus_mutex; + spi_lobo_bus_config_t cur_bus_config; +} spi_lobo_host_t; + +struct spi_lobo_device_t { + spi_lobo_device_interface_config_t cfg; + spi_lobo_host_t *host; + spi_lobo_bus_config_t bus_config; + spi_lobo_host_device_t host_dev; +}; + +typedef spi_lobo_device_t* spi_lobo_device_handle_t; ///< Handle for a device on a SPI bus +typedef spi_lobo_host_t* spi_lobo_host_handle_t; +typedef spi_lobo_device_interface_config_t* spi_lobo_device_interface_config_handle_t; + + +/** + * @brief Add a device. This allocates a CS line for the device, allocates memory for the device structure and hooks + * up the CS pin to whatever is specified. + * + * This initializes the internal structures for a device, plus allocates a CS pin on the indicated SPI master + * peripheral and routes it to the indicated GPIO. All SPI master devices have three hw CS pins and can thus control + * up to three devices. Software handled CS pin can also be used for additional devices on the same SPI bus. + * + * ### If selected SPI host device bus is not yet initialized, it is initialized first with 'bus_config' function ### + * + * @note While in general, speeds up to 80MHz on the dedicated SPI pins and 40MHz on GPIO-matrix-routed pins are + * supported, full-duplex transfers routed over the GPIO matrix only support speeds up to 26MHz. + * + * @param host SPI peripheral to allocate device on (HSPI or VSPI) + * @param dev_config SPI interface protocol config for the device + * @param bus_config Pointer to a spi_lobo_bus_config_t struct specifying how the host device bus should be initialized + * @param handle Pointer to variable to hold the device handle + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_NOT_FOUND if host doesn't have any free CS slots + * - ESP_ERR_NO_MEM if out of memory + * - ESP_OK on success + */ +esp_err_t spi_lobo_bus_add_device(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, spi_lobo_device_interface_config_t *dev_config, spi_lobo_device_handle_t *handle); + +/** + * @brief Remove a device from the SPI bus. If after removal no other device is attached to the spi bus device, it is freed. + * + * @param handle Device handle to free + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_INVALID_STATE if device already is freed + * - ESP_OK on success + */ +esp_err_t spi_lobo_bus_remove_device(spi_lobo_device_handle_t handle); + + +/** + * @brief Return the actuall SPI bus speed for the spi device in Hz + * + * Some frequencies cannot be set, for example 30000000 will actually set SPI clock to 26666666 Hz + * + * @param handle Device handle obtained using spi_lobo_bus_add_device + * + * @return + * - actuall SPI clock + */ +uint32_t spi_lobo_get_speed(spi_lobo_device_handle_t handle); + +/** + * @brief Set the new clock speed for the device, return the actuall SPI bus speed set, in Hz + * This function can be used after the device is initialized + * + * Some frequencies cannot be set, for example 30000000 will actually set SPI clock to 26666666 Hz + * + * @param handle Device handle obtained using spi_lobo_bus_add_device + * @param speed New device spi clock to be set in Hz + * + * @return + * - actuall SPI clock + * - 0 if speed cannot be set + */ +uint32_t spi_lobo_set_speed(spi_lobo_device_handle_t handle, uint32_t speed); + +/** + * @brief Select spi device for transmission + * + * It configures spi bus with selected spi device parameters if previously selected device was different than the current + * If device's spics_io_num=-1 and spics_ext_io_num > 0 'spics_ext_io_num' pin is set to active state (low) + * + * spi bus device's semaphore is taken before selecting the device + * + * @param handle Device handle obtained using spi_lobo_bus_add_device + * @param force configure spi bus even if the previous device was the same + * + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_OK on success + */ +esp_err_t spi_lobo_device_select(spi_lobo_device_handle_t handle, int force); + +/** + * @brief De-select spi device + * + * If device's spics_io_num=-1 and spics_ext_io_num > 0 'spics_ext_io_num' pin is set to inactive state (high) + * + * spi bus device's semaphore is given after selecting the device + * + * @param handle Device handle obtained using spi_lobo_bus_add_device + * + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_OK on success + */ +esp_err_t spi_lobo_device_deselect(spi_lobo_device_handle_t handle); + + +/** + * @brief Check if spi bus uses native spi pins + * + * @param handle Device handle obtained using spi_lobo_bus_add_device + * + * @return + * - true if native spi pins are used + * - false if spi pins are routed through gpio matrix + */ +bool spi_lobo_uses_native_pins(spi_lobo_device_handle_t handle); + +/** + * @brief Get spi bus native spi pins + * + * @param handle Device handle obtained using spi_lobo_bus_add_device + * + * @return + * places spi bus native pins in provided pointers + */ +void spi_lobo_get_native_pins(int host, int *sdi, int *sdo, int *sck); + +/** + * @brief Transimit and receive data to/from spi device based on transaction data + * + * TRANSMIT 8-bit data to spi device from 'trans->tx_buffer' or 'trans->tx_data' (trans->lenght/8 bytes) + * and RECEIVE data to 'trans->rx_buffer' or 'trans->rx_data' (trans->rx_length/8 bytes) + * Lengths must be 8-bit multiples! + * If trans->rx_buffer is NULL or trans->rx_length is 0, only transmits data + * If trans->tx_buffer is NULL or trans->length is 0, only receives data + * If the device is in duplex mode (SPI_DEVICE_HALFDUPLEX flag NOT set), data are transmitted and received simultaneously. + * If the device is in half duplex mode (SPI_DEVICE_HALFDUPLEX flag IS set), data are received after transmission + * 'address', 'command' and 'dummy bits' are transmitted before data phase IF set in device's configuration + * and IF 'trans->length' and 'trans->rx_length' are NOT both 0 + * If device was not previously selected, it will be selected before transmission and deselected after transmission. + * + * @param handle Device handle obtained using spi_lobo_bus_add_device + * + * @param trans Pointer to variable containing the description of the transaction that is executed + * + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP error code if device cannot be selected + * - ESP_OK on success + * + */ +esp_err_t spi_lobo_transfer_data(spi_lobo_device_handle_t handle, spi_lobo_transaction_t *trans); + + +/* + * SPI transactions uses the semaphore (taken in select function) to protect the transfer + */ +esp_err_t spi_lobo_device_TakeSemaphore(spi_lobo_device_handle_t handle); +void spi_lobo_device_GiveSemaphore(spi_lobo_device_handle_t handle); + + +/** + * @brief Setup a DMA link chain + * + * This routine will set up a chain of linked DMA descriptors in the array pointed to by + * ``dmadesc``. Enough DMA descriptors will be used to fit the buffer of ``len`` bytes in, and the + * descriptors will point to the corresponding positions in ``buffer`` and linked together. The + * end result is that feeding ``dmadesc[0]`` into DMA hardware results in the entirety ``len`` bytes + * of ``data`` being read or written. + * + * @param dmadesc Pointer to array of DMA descriptors big enough to be able to convey ``len`` bytes + * @param len Length of buffer + * @param data Data buffer to use for DMA transfer + * @param isrx True if data is to be written into ``data``, false if it's to be read from ``data``. + */ +void spi_lobo_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx); + +/** + * @brief Check if a DMA reset is requested but has not completed yet + * + * @return True when a DMA reset is requested but hasn't completed yet. False otherwise. + */ +bool spi_lobo_dmaworkaround_reset_in_progress(); + + +/** + * @brief Mark a DMA channel as idle. + * + * A call to this function tells the workaround logic that this channel will + * not be affected by a global SPI DMA reset. + */ +void spi_lobo_dmaworkaround_idle(int dmachan); + +/** + * @brief Mark a DMA channel as active. + * + * A call to this function tells the workaround logic that this channel will + * be affected by a global SPI DMA reset, and a reset like that should not be attempted. + */ +void spi_lobo_dmaworkaround_transfer_active(int dmachan); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/spiffs/component.mk b/components/spiffs/component.mk new file mode 100644 index 0000000..3d6fbce --- /dev/null +++ b/components/spiffs/component.mk @@ -0,0 +1,7 @@ +# +# Component Makefile +# + +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . +COMPONENT_PRIV_INCLUDEDIRS := \ No newline at end of file diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c new file mode 100644 index 0000000..57b8aa0 --- /dev/null +++ b/components/spiffs/esp_spiffs.c @@ -0,0 +1,138 @@ +/* + * Lua RTOS, SPIFFS low access + * + * Copyright (C) 2015 - 2017 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * + * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + */ + +#include + +#include "esp_spiffs.h" +#include "esp_attr.h" + +#include "spiffs.h" + +#include + +s32_t IRAM_ATTR esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) { + u32_t aaddr; + u8_t *buff = NULL; + u8_t *abuff = NULL; + u32_t asize; + + asize = size; + + // Align address to 4 byte + aaddr = (addr + (4 - 1)) & (u32_t)-4; + if (aaddr != addr) { + aaddr -= 4; + asize += (addr - aaddr); + } + + // Align size to 4 byte + asize = (asize + (4 - 1)) & (u32_t)-4; + + if ((aaddr != addr) || (asize != size)) { + // Align buffer + buff = malloc(asize + 4); + if (!buff) { + return SPIFFS_ERR_INTERNAL; + } + + abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & (u32_t)-4); + + if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) { + free(buff); + return SPIFFS_ERR_INTERNAL; + } + + memcpy(dst, abuff + (addr - aaddr), size); + + free(buff); + } else { + if (spi_flash_read(addr, (void *)dst, size) != 0) { + return SPIFFS_ERR_INTERNAL; + } + } + + return SPIFFS_OK; +} + +s32_t IRAM_ATTR esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) { + u32_t aaddr; + u8_t *buff = NULL; + u8_t *abuff = NULL; + u32_t asize; + + asize = size; + + // Align address to 4 byte + aaddr = (addr + (4 - 1)) & -4; + if (aaddr != addr) { + aaddr -= 4; + asize += (addr - aaddr); + } + + // Align size to 4 byte + asize = (asize + (4 - 1)) & -4; + + if ((aaddr != addr) || (asize != size)) { + // Align buffer + buff = malloc(asize + 4); + if (!buff) { + return SPIFFS_ERR_INTERNAL; + } + + abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & -4); + + if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) { + free(buff); + return SPIFFS_ERR_INTERNAL; + } + + memcpy(abuff + (addr - aaddr), src, size); + + if (spi_flash_write(aaddr, (uint32_t *)abuff, asize) != 0) { + free(buff); + return SPIFFS_ERR_INTERNAL; + } + + free(buff); + } else { + if (spi_flash_write(addr, (uint32_t *)src, size) != 0) { + return SPIFFS_ERR_INTERNAL; + } + } + + return SPIFFS_OK; +} + +s32_t IRAM_ATTR esp32_spi_flash_erase(u32_t addr, u32_t size) { + if (spi_flash_erase_sector(addr >> 12) != 0) { + return SPIFFS_ERR_INTERNAL; + } + + return SPIFFS_OK; +} diff --git a/components/spiffs/esp_spiffs.h b/components/spiffs/esp_spiffs.h new file mode 100644 index 0000000..c68e652 --- /dev/null +++ b/components/spiffs/esp_spiffs.h @@ -0,0 +1,43 @@ +/* + * Lua RTOS, write syscall implementation + * + * Copyright (C) 2015 - 2017 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * + * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + */ + +#ifndef __ESP_SPIFFS_H__ +#define __ESP_SPIFFS_H__ + +#include "spiffs.h" + +s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst); +s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src); +s32_t esp32_spi_flash_erase(u32_t addr, u32_t size); + +#define low_spiffs_read (spiffs_read *)esp32_spi_flash_read +#define low_spiffs_write (spiffs_write *)esp32_spi_flash_write +#define low_spiffs_erase (spiffs_erase *)esp32_spi_flash_erase + +#endif // __ESP_SPIFFS_H__ diff --git a/components/spiffs/list.c b/components/spiffs/list.c new file mode 100644 index 0000000..adf2fcb --- /dev/null +++ b/components/spiffs/list.c @@ -0,0 +1,251 @@ +/* + * Lua RTOS, list data structure + * + * Copyright (C) 2015 - 2017 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * + * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + */ + +#include "esp_attr.h" + +#include +#include +#include + +#include "list.h" +#include "mutex.h" + +void list_init(struct list *list, int first_index) { + // Create the mutex + mtx_init(&list->mutex, NULL, NULL, 0); + + mtx_lock(&list->mutex); + + list->indexes = 0; + list->free = NULL; + list->index = NULL; + list->first_index = first_index; + + mtx_unlock(&list->mutex); +} + +int list_add(struct list *list, void *item, int *item_index) { + struct list_index *index = NULL; + struct list_index *indexa = NULL; + int grow = 0; + + mtx_lock(&list->mutex); + + // Get an index + if (list->free) { + // Get first free element + index = list->free; + list->free = index->next; + } else { + // Must grow index array + grow = 1; + } + + if (grow) { + // Increment index count + list->indexes++; + + // Create a new index array for allocate new index + indexa = (struct list_index *)malloc(sizeof(struct list_index) * list->indexes); + if (!indexa) { + mtx_unlock(&list->mutex); + return ENOMEM; + } + + if (list->index) { + // Copy current index array to new created + bcopy(list->index, indexa, sizeof(struct list_index) * (list->indexes - 1)); + + // Free current index array + free(list->index); + } + + // Store new index array + list->index = indexa; + + // Current index + index = list->index + list->indexes - 1; + + // Initialize new index + index->index = list->indexes - 1; + + } + + index->next = NULL; + index->item = item; + index->deleted = 0; + + // Return index + *item_index = index->index + list->first_index; + + mtx_unlock(&list->mutex); + + return 0; +} + +int IRAM_ATTR list_get(struct list *list, int index, void **item) { + struct list_index *cindex = NULL; + int iindex; + + mtx_lock(&list->mutex); + + if (!list->indexes) { + mtx_unlock(&list->mutex); + return EINVAL; + } + + // Check index + if (index < list->first_index) { + mtx_unlock(&list->mutex); + return EINVAL; + } + + // Get new internal index + iindex = index - list->first_index; + + // Test for a valid index + if (iindex > list->indexes) { + mtx_unlock(&list->mutex); + return EINVAL; + } + + cindex = list->index + iindex; + + if (cindex->deleted) { + mtx_unlock(&list->mutex); + return EINVAL; + } + + *item = cindex->item; + + mtx_unlock(&list->mutex); + + return 0; +} + +int list_remove(struct list *list, int index, int destroy) { + struct list_index *cindex = NULL; + int iindex; + + mtx_lock(&list->mutex); + + // Check index + if (index < list->first_index) { + mtx_unlock(&list->mutex); + return EINVAL; + } + + // Get new internal index + iindex = index - list->first_index; + + // Test for a valid index + if ((iindex < 0) || (iindex > list->indexes)) { + mtx_unlock(&list->mutex); + return EINVAL; + } + + cindex = &list->index[iindex]; + + if (destroy) { + free(cindex->item); + } + + cindex->next = list->free; + cindex->deleted = 1; + list->free = cindex; + + mtx_unlock(&list->mutex); + + return 0; +} + +int IRAM_ATTR list_first(struct list *list) { + int index; + int res = -1; + + mtx_lock(&list->mutex); + + for(index=0;index < list->indexes;index++) { + if (!list->index[index].deleted) { + res = index + list->first_index; + break; + } + } + + mtx_unlock(&list->mutex); + + return res; +} + +int IRAM_ATTR list_next(struct list *list, int index) { + int res = -1; + int iindex; + + mtx_lock(&list->mutex); + + // Check index + if (index < list->first_index) { + mtx_unlock(&list->mutex); + return -1; + } + + // Get new internal index + iindex = index - list->first_index + 1; + + // Get next non deleted item on list + for(;iindex < list->indexes;iindex++) { + if (!list->index[iindex].deleted) { + res = iindex + list->first_index; + break; + } + } + + mtx_unlock(&list->mutex); + + return res; +} + +void list_destroy(struct list *list, int items) { + int index; + + mtx_lock(&list->mutex); + + if (items) { + for(index=0;index < list->indexes;index++) { + if (!list->index[index].deleted) { + free(list->index[index].item); + } + } + } + + free(list->index); + + mtx_unlock(&list->mutex); + mtx_destroy(&list->mutex); +} diff --git a/components/spiffs/list.h b/components/spiffs/list.h new file mode 100644 index 0000000..4f9c5b1 --- /dev/null +++ b/components/spiffs/list.h @@ -0,0 +1,60 @@ +/* + * Lua RTOS, list data structure + * + * Copyright (C) 2015 - 2017 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * + * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + */ + +#ifndef _LIST_H +#define _LIST_H + +#include +#include "mutex.h" + +struct list { + struct mtx mutex; + struct list_index *index; + struct list_index *free; + uint8_t indexes; + uint8_t first_index; +}; + +struct list_index { + void *item; + uint8_t index; + uint8_t deleted; + struct list_index *next; +}; + +void list_init(struct list *list, int first_index); +int list_add(struct list *list, void *item, int *item_index); +int list_get(struct list *list, int index, void **item); +int list_remove(struct list *list, int index, int destroy); +int list_first(struct list *list); +int list_next(struct list *list, int index); +void list_destroy(struct list *list, int items); + +#endif /* LIST_H */ + diff --git a/components/spiffs/mutex.c b/components/spiffs/mutex.c new file mode 100644 index 0000000..84aea9c --- /dev/null +++ b/components/spiffs/mutex.c @@ -0,0 +1,102 @@ +/* + * Lua RTOS, mutex api implementation over FreeRTOS + * + * Copyright (C) 2015 - 2017 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * + * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + * + * Modified by: LoBo (loboris@gmail.com / https://github.com/loboris) + * + */ + +#include "freertos/FreeRTOS.h" +#include "esp_attr.h" +#include "mutex.h" + + +#define portEND_SWITCHING_ISR(xSwitchRequired) \ +if (xSwitchRequired) { \ + _frxt_setup_switch(); \ +} + +extern unsigned port_interruptNesting[portNUM_PROCESSORS]; + +void _mtx_init() { +} + +void mtx_init(struct mtx *mutex, const char *name, const char *type, int opts) { + mutex->sem = xSemaphoreCreateBinary(); + + if (mutex->sem) { + if (port_interruptNesting[xPortGetCoreID()] != 0) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken); + portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); + } else { + xSemaphoreGive( mutex->sem ); + } + } +} + +void IRAM_ATTR mtx_lock(struct mtx *mutex) { + if (port_interruptNesting[xPortGetCoreID()] != 0) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xSemaphoreTakeFromISR( mutex->sem, &xHigherPriorityTaskWoken ); + portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); + } else { + xSemaphoreTake( mutex->sem, portMAX_DELAY ); + } +} + +int mtx_trylock(struct mtx *mutex) { + if (xSemaphoreTake( mutex->sem, 0 ) == pdTRUE) { + return 1; + } else { + return 0; + } +} + +void IRAM_ATTR mtx_unlock(struct mtx *mutex) { + if (port_interruptNesting[xPortGetCoreID()] != 0) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken ); + portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); + } else { + xSemaphoreGive( mutex->sem ); + } +} + +void mtx_destroy(struct mtx *mutex) { + if (port_interruptNesting[xPortGetCoreID()] != 0) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken ); + portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); + } else { + xSemaphoreGive( mutex->sem ); + } + + vSemaphoreDelete( mutex->sem ); + + mutex->sem = 0; +} diff --git a/components/spiffs/mutex.h b/components/spiffs/mutex.h new file mode 100644 index 0000000..0871e23 --- /dev/null +++ b/components/spiffs/mutex.h @@ -0,0 +1,53 @@ +/* + * Lua RTOS, mutex api implementation over FreeRTOS + * + * Copyright (C) 2015 - 2017 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * + * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + * + * Modified by: LoBo (loboris@gmail.com / https://github.com/loboris) + * + */ + +#ifndef MUTEX_H_H +#define MUTEX_H_H + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +#define MUTEX_INITIALIZER {.sem = 0} + +struct mtx { + SemaphoreHandle_t sem; +}; + + +void mtx_init(struct mtx *mutex, const char *name, const char *type, int opts); +void mtx_lock(struct mtx *mutex); +int mtx_trylock(struct mtx *mutex); +void mtx_unlock(struct mtx *mutex); +void mtx_destroy(struct mtx *mutex); + +#endif /* MUTEX_H_H */ + diff --git a/components/spiffs/spiffs.h b/components/spiffs/spiffs.h new file mode 100644 index 0000000..d87422d --- /dev/null +++ b/components/spiffs/spiffs.h @@ -0,0 +1,813 @@ +/* + * spiffs.h + * + * Created on: May 26, 2013 + * Author: petera + */ + +#ifndef SPIFFS_H_ +#define SPIFFS_H_ +#if defined(__cplusplus) +extern "C" { +#endif + +#include "spiffs_config.h" + +#define SPIFFS_OK 0 +#define SPIFFS_ERR_NOT_MOUNTED -10000 +#define SPIFFS_ERR_FULL -10001 +#define SPIFFS_ERR_NOT_FOUND -10002 +#define SPIFFS_ERR_END_OF_OBJECT -10003 +#define SPIFFS_ERR_DELETED -10004 +#define SPIFFS_ERR_NOT_FINALIZED -10005 +#define SPIFFS_ERR_NOT_INDEX -10006 +#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007 +#define SPIFFS_ERR_FILE_CLOSED -10008 +#define SPIFFS_ERR_FILE_DELETED -10009 +#define SPIFFS_ERR_BAD_DESCRIPTOR -10010 +#define SPIFFS_ERR_IS_INDEX -10011 +#define SPIFFS_ERR_IS_FREE -10012 +#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013 +#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014 +#define SPIFFS_ERR_INDEX_REF_FREE -10015 +#define SPIFFS_ERR_INDEX_REF_LU -10016 +#define SPIFFS_ERR_INDEX_REF_INVALID -10017 +#define SPIFFS_ERR_INDEX_FREE -10018 +#define SPIFFS_ERR_INDEX_LU -10019 +#define SPIFFS_ERR_INDEX_INVALID -10020 +#define SPIFFS_ERR_NOT_WRITABLE -10021 +#define SPIFFS_ERR_NOT_READABLE -10022 +#define SPIFFS_ERR_CONFLICTING_NAME -10023 +#define SPIFFS_ERR_NOT_CONFIGURED -10024 + +#define SPIFFS_ERR_NOT_A_FS -10025 +#define SPIFFS_ERR_MOUNTED -10026 +#define SPIFFS_ERR_ERASE_FAIL -10027 +#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028 + +#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 + +#define SPIFFS_ERR_FILE_EXISTS -10030 + +#define SPIFFS_ERR_NOT_A_FILE -10031 +#define SPIFFS_ERR_RO_NOT_IMPL -10032 +#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033 +#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034 +#define SPIFFS_ERR_PROBE_NOT_A_FS -10035 +#define SPIFFS_ERR_NAME_TOO_LONG -10036 + +#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037 +#define SPIFFS_ERR_IX_MAP_MAPPED -10038 +#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039 + +#define SPIFFS_ERR_INTERNAL -10050 + +#define SPIFFS_ERR_TEST -10100 + + +// spiffs file descriptor index type. must be signed +typedef s16_t spiffs_file; +// spiffs file descriptor flags +typedef u16_t spiffs_flags; +// spiffs file mode +typedef u16_t spiffs_mode; +// object type +typedef u8_t spiffs_obj_type; + +struct spiffs_t; + +#if SPIFFS_HAL_CALLBACK_EXTRA + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); + +#else // SPIFFS_HAL_CALLBACK_EXTRA + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +/* file system check callback report operation */ +typedef enum { + SPIFFS_CHECK_LOOKUP = 0, + SPIFFS_CHECK_INDEX, + SPIFFS_CHECK_PAGE +} spiffs_check_type; + +/* file system check callback report type */ +typedef enum { + SPIFFS_CHECK_PROGRESS = 0, + SPIFFS_CHECK_ERROR, + SPIFFS_CHECK_FIX_INDEX, + SPIFFS_CHECK_FIX_LOOKUP, + SPIFFS_CHECK_DELETE_ORPHANED_INDEX, + SPIFFS_CHECK_DELETE_PAGE, + SPIFFS_CHECK_DELETE_BAD_FILE +} spiffs_check_report; + +/* file system check callback function */ +#if SPIFFS_HAL_CALLBACK_EXTRA +typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); +#else // SPIFFS_HAL_CALLBACK_EXTRA +typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +/* file system listener callback operation */ +typedef enum { + /* the file has been created */ + SPIFFS_CB_CREATED = 0, + /* the file has been updated or moved to another page */ + SPIFFS_CB_UPDATED, + /* the file has been deleted */ + SPIFFS_CB_DELETED +} spiffs_fileop_type; + +/* file system listener callback function */ +typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix); + +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) \ + printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) +#endif + +/* Any write to the filehandle is appended to end of the file */ +#define SPIFFS_APPEND (1<<0) +#define SPIFFS_O_APPEND SPIFFS_APPEND +/* If the opened file exists, it will be truncated to zero length before opened */ +#define SPIFFS_TRUNC (1<<1) +#define SPIFFS_O_TRUNC SPIFFS_TRUNC +/* If the opened file does not exist, it will be created before opened */ +#define SPIFFS_CREAT (1<<2) +#define SPIFFS_O_CREAT SPIFFS_CREAT +/* The opened file may only be read */ +#define SPIFFS_RDONLY (1<<3) +#define SPIFFS_O_RDONLY SPIFFS_RDONLY +/* The opened file may only be written */ +#define SPIFFS_WRONLY (1<<4) +#define SPIFFS_O_WRONLY SPIFFS_WRONLY +/* The opened file may be both read and written */ +#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) +#define SPIFFS_O_RDWR SPIFFS_RDWR +/* Any writes to the filehandle will never be cached but flushed directly */ +#define SPIFFS_DIRECT (1<<5) +#define SPIFFS_O_DIRECT SPIFFS_DIRECT +/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */ +#define SPIFFS_EXCL (1<<6) +#define SPIFFS_O_EXCL SPIFFS_EXCL + +#define SPIFFS_SEEK_SET (0) +#define SPIFFS_SEEK_CUR (1) +#define SPIFFS_SEEK_END (2) + +#define SPIFFS_TYPE_FILE (1) +#define SPIFFS_TYPE_DIR (2) +#define SPIFFS_TYPE_HARD_LINK (3) +#define SPIFFS_TYPE_SOFT_LINK (4) + +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) +#endif + +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#endif + +// phys structs + +// spiffs spi configuration struct +typedef struct { + // physical read function + spiffs_read hal_read_f; + // physical write function + spiffs_write hal_write_f; + // physical erase function + spiffs_erase hal_erase_f; +#if SPIFFS_SINGLETON == 0 + // physical size of the spi flash + u32_t phys_size; + // physical offset in spi flash used for spiffs, + // must be on block boundary + u32_t phys_addr; + // physical size when erasing a block + u32_t phys_erase_block; + + // logical size of a block, must be on physical + // block size boundary and must never be less than + // a physical block + u32_t log_block_size; + // logical size of a page, must be at least + // log_block_size / 8 + u32_t log_page_size; + +#endif +#if SPIFFS_FILEHDL_OFFSET + // an integer offset added to each file handle + u16_t fh_ix_offset; +#endif +} spiffs_config; + +typedef struct spiffs_t { + // file system configuration + spiffs_config cfg; + // number of logical blocks + u32_t block_count; + + // cursor for free blocks, block index + spiffs_block_ix free_cursor_block_ix; + // cursor for free blocks, entry index + int free_cursor_obj_lu_entry; + // cursor when searching, block index + spiffs_block_ix cursor_block_ix; + // cursor when searching, entry index + int cursor_obj_lu_entry; + + // primary work buffer, size of a logical page + u8_t *lu_work; + // secondary work buffer, size of a logical page + u8_t *work; + // file descriptor memory area + u8_t *fd_space; + // available file descriptors + u32_t fd_count; + + // last error + s32_t err_code; + + // current number of free blocks + u32_t free_blocks; + // current number of busy pages + u32_t stats_p_allocated; + // current number of deleted pages + u32_t stats_p_deleted; + // flag indicating that garbage collector is cleaning + u8_t cleaning; + // max erase count amongst all blocks + spiffs_obj_id max_erase_count; + +#if SPIFFS_GC_STATS + u32_t stats_gc_runs; +#endif + +#if SPIFFS_CACHE + // cache memory + void *cache; + // cache size + u32_t cache_size; +#if SPIFFS_CACHE_STATS + u32_t cache_hits; + u32_t cache_misses; +#endif +#endif + + // check callback function + spiffs_check_callback check_cb_f; + // file callback function + spiffs_file_callback file_cb_f; + // mounted flag + u8_t mounted; + // user data + void *user_data; + // config magic + u32_t config_magic; +} spiffs; + +/* spiffs file status struct */ +typedef struct { + spiffs_obj_id obj_id; + u32_t size; + spiffs_obj_type type; + spiffs_page_ix pix; + u8_t name[SPIFFS_OBJ_NAME_LEN]; +#if SPIFFS_OBJ_META_LEN + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +} spiffs_stat; + +struct spiffs_dirent { + spiffs_obj_id obj_id; + u8_t name[SPIFFS_OBJ_NAME_LEN]; + spiffs_obj_type type; + u32_t size; + spiffs_page_ix pix; +#if SPIFFS_OBJ_META_LEN + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +}; + +typedef struct { + spiffs *fs; + spiffs_block_ix block; + int entry; +} spiffs_DIR; + +#if SPIFFS_IX_MAP + +typedef struct { + // buffer with looked up data pixes + spiffs_page_ix *map_buf; + // precise file byte offset + u32_t offset; + // start data span index of lookup buffer + spiffs_span_ix start_spix; + // end data span index of lookup buffer + spiffs_span_ix end_spix; +} spiffs_ix_map; + +#endif + +// functions + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +/** + * Special function. This takes a spiffs config struct and returns the number + * of blocks this file system was formatted with. This function relies on + * that following info is set correctly in given config struct: + * + * phys_addr, log_page_size, and log_block_size. + * + * Also, hal_read_f must be set in the config struct. + * + * One must be sure of the correct page size and that the physical address is + * correct in the probed file system when calling this function. It is not + * checked if the phys_addr actually points to the start of the file system, + * so one might get a false positive if entering a phys_addr somewhere in the + * middle of the file system at block boundary. In addition, it is not checked + * if the page size is actually correct. If it is not, weird file system sizes + * will be returned. + * + * If this function detects a file system it returns the assumed file system + * size, which can be used to set the phys_size. + * + * Otherwise, it returns an error indicating why it is not regarded as a file + * system. + * + * Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK + * macros. It returns the error code directly, instead of as read by + * SPIFFS_errno. + * + * @param config essential parts of the physical and logical + * configuration of the file system. + */ +s32_t SPIFFS_probe_fs(spiffs_config *config); +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +/** + * Initializes the file system dynamic parameters and mounts the filesystem. + * If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS + * if the flash does not contain a recognizable file system. + * In this case, SPIFFS_format must be called prior to remounting. + * @param fs the file system struct + * @param config the physical and logical configuration of the file system + * @param work a memory work buffer comprising 2*config->log_page_size + * bytes used throughout all file system operations + * @param fd_space memory for file descriptors + * @param fd_space_size memory size of file descriptors + * @param cache memory for cache, may be null + * @param cache_size memory size of cache + * @param check_cb_f callback function for reporting during consistency checks + */ +s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f); + +/** + * Unmounts the file system. All file handles will be flushed of any + * cached writes and closed. + * @param fs the file system struct + */ +void SPIFFS_unmount(spiffs *fs); + +/** + * Creates a new file. + * @param fs the file system struct + * @param path the path of the new file + * @param mode ignored, for posix compliance + */ +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); + +/** + * Opens/creates a file. + * @param fs the file system struct + * @param path the path of the new file + * @param flags the flags for the open command, can be combinations of + * SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, + * SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); + +/** + * Opens a file by given dir entry. + * Optimization purposes, when traversing a file system with SPIFFS_readdir + * a normal SPIFFS_open would need to traverse the filesystem again to find + * the file, whilst SPIFFS_open_by_dirent already knows where the file resides. + * @param fs the file system struct + * @param e the dir entry to the file + * @param flags the flags for the open command, can be combinations of + * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + * SPIFFS_CREAT will have no effect in this case. + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode); + +/** + * Opens a file by given page index. + * Optimization purposes, opens a file by directly pointing to the page + * index in the spi flash. + * If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE + * is returned. + * @param fs the file system struct + * @param page_ix the page index + * @param flags the flags for the open command, can be combinations of + * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + * SPIFFS_CREAT will have no effect in this case. + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode); + +/** + * Reads from given filehandle. + * @param fs the file system struct + * @param fh the filehandle + * @param buf where to put read data + * @param len how much to read + * @returns number of bytes read, or -1 if error + */ +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); + +/** + * Writes to given filehandle. + * @param fs the file system struct + * @param fh the filehandle + * @param buf the data to write + * @param len how much to write + * @returns number of bytes written, or -1 if error + */ +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); + +/** + * Moves the read/write file offset. Resulting offset is returned or negative if error. + * lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. + * @param fs the file system struct + * @param fh the filehandle + * @param offs how much/where to move the offset + * @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes + * if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset + * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative + */ +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); + +/** + * Removes a file by path + * @param fs the file system struct + * @param path the path of the file to remove + */ +s32_t SPIFFS_remove(spiffs *fs, const char *path); + +/** + * Removes a file by filehandle + * @param fs the file system struct + * @param fh the filehandle of the file to remove + */ +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); + +/** + * Gets file status by path + * @param fs the file system struct + * @param path the path of the file to stat + * @param s the stat struct to populate + */ +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); + +/** + * Gets file status by filehandle + * @param fs the file system struct + * @param fh the filehandle of the file to stat + * @param s the stat struct to populate + */ +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); + +/** + * Flushes all pending write operations from cache for given file + * @param fs the file system struct + * @param fh the filehandle of the file to flush + */ +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); + +/** + * Closes a filehandle. If there are pending write operations, these are finalized before closing. + * @param fs the file system struct + * @param fh the filehandle of the file to close + */ +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); + +/** + * Renames a file + * @param fs the file system struct + * @param old path of file to rename + * @param newPath new path of file + */ +s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath); + +#if SPIFFS_OBJ_META_LEN +/** + * Updates file's metadata + * @param fs the file system struct + * @param path path to the file + * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. + */ +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); + +/** + * Updates file's metadata + * @param fs the file system struct + * @param fh file handle of the file + * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. + */ +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); +#endif + +/** + * Returns last error of last file operation. + * @param fs the file system struct + */ +s32_t SPIFFS_errno(spiffs *fs); + +/** + * Clears last error. + * @param fs the file system struct + */ +void SPIFFS_clearerr(spiffs *fs); + +/** + * Opens a directory stream corresponding to the given name. + * The stream is positioned at the first entry in the directory. + * On hydrogen builds the name argument is ignored as hydrogen builds always correspond + * to a flat file structure - no directories. + * @param fs the file system struct + * @param name the name of the directory + * @param d pointer the directory stream to be populated + */ +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); + +/** + * Closes a directory stream + * @param d the directory stream to close + */ +s32_t SPIFFS_closedir(spiffs_DIR *d); + +/** + * Reads a directory into given spifs_dirent struct. + * @param d pointer to the directory stream + * @param e the dirent struct to be populated + * @returns null if error or end of stream, else given dirent is returned + */ +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); + +/** + * Runs a consistency check on given filesystem. + * @param fs the file system struct + */ +s32_t SPIFFS_check(spiffs *fs); + +/** + * Returns number of total bytes available and number of used bytes. + * This is an estimation, and depends on if there a many files with little + * data or few files with much data. + * NB: If used number of bytes exceeds total bytes, a SPIFFS_check should + * run. This indicates a power loss in midst of things. In worst case + * (repeated powerlosses in mending or gc) you might have to delete some files. + * + * @param fs the file system struct + * @param total total number of bytes in filesystem + * @param used used number of bytes in filesystem + */ +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); + +/** + * Formats the entire file system. All data will be lost. + * The filesystem must not be mounted when calling this. + * + * NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount + * MUST be called prior to formatting in order to configure the filesystem. + * If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling + * SPIFFS_format. + * If SPIFFS_mount fails, SPIFFS_format can be called directly without calling + * SPIFFS_unmount first. + * + * @param fs the file system struct + */ +s32_t SPIFFS_format(spiffs *fs); + +/** + * Returns nonzero if spiffs is mounted, or zero if unmounted. + * @param fs the file system struct + */ +u8_t SPIFFS_mounted(spiffs *fs); + +/** + * Tries to find a block where most or all pages are deleted, and erase that + * block if found. Does not care for wear levelling. Will not move pages + * around. + * If parameter max_free_pages are set to 0, only blocks with only deleted + * pages will be selected. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * Setting max_free_pages to anything larger than zero will eventually wear + * flash more as a block containing free pages can be erased. + * + * Will set err_no to SPIFFS_OK if a block was found and erased, + * SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, + * or other error. + * + * @param fs the file system struct + * @param max_free_pages maximum number allowed free pages in block + */ +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); + +/** + * Will try to make room for given amount of bytes in the filesystem by moving + * pages and erasing blocks. + * If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If + * there already is this amount (or more) of free space, SPIFFS_gc will + * silently return. It is recommended to call SPIFFS_info before invoking + * this method in order to determine what amount of bytes to give. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * @param fs the file system struct + * @param size amount of bytes that should be freed + */ +s32_t SPIFFS_gc(spiffs *fs, u32_t size); + +/** + * Check if EOF reached. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); + +/** + * Get position in file. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); + +/** + * Registers a callback function that keeps track on operations on file + * headers. Do note, that this callback is called from within internal spiffs + * mechanisms. Any operations on the actual file system being callbacked from + * in this callback will mess things up for sure - do not do this. + * This can be used to track where files are and move around during garbage + * collection, which in turn can be used to build location tables in ram. + * Used in conjuction with SPIFFS_open_by_page this may improve performance + * when opening a lot of files. + * Must be invoked after mount. + * + * @param fs the file system struct + * @param cb_func the callback on file operations + */ +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); + +#if SPIFFS_IX_MAP + +/** + * Maps the first level index lookup to a given memory map. + * This will make reading big files faster, as the memory map will be used for + * looking up data pages instead of searching for the indices on the physical + * medium. When mapping, all affected indicies are found and the information is + * copied to the array. + * Whole file or only parts of it may be mapped. The index map will cover file + * contents from argument offset until and including arguments (offset+len). + * It is valid to map a longer range than the current file size. The map will + * then be populated when the file grows. + * On garbage collections and file data page movements, the map array will be + * automatically updated. Do not tamper with the map array, as this contains + * the references to the data pages. Modifying it from outside will corrupt any + * future readings using this file descriptor. + * The map will no longer be used when the file descriptor closed or the file + * is unmapped. + * This can be useful to get faster and more deterministic timing when reading + * large files, or when seeking and reading a lot within a file. + * @param fs the file system struct + * @param fh the file handle of the file to map + * @param map a spiffs_ix_map struct, describing the index map + * @param offset absolute file offset where to start the index map + * @param len length of the mapping in actual file bytes + * @param map_buf the array buffer for the look up data - number of required + * elements in the array can be derived from function + * SPIFFS_bytes_to_ix_map_entries given the length + */ +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf); + +/** + * Unmaps the index lookup from this filehandle. All future readings will + * proceed as normal, requiring reading of the first level indices from + * physical media. + * The map and map buffer given in function SPIFFS_ix_map will no longer be + * referenced by spiffs. + * It is not strictly necessary to unmap a file before closing it, as closing + * a file will automatically unmap it. + * @param fs the file system struct + * @param fh the file handle of the file to unmap + */ +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh); + +/** + * Moves the offset for the index map given in function SPIFFS_ix_map. Parts or + * all of the map buffer will repopulated. + * @param fs the file system struct + * @param fh the mapped file handle of the file to remap + * @param offset new absolute file offset where to start the index map + */ +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs); + +/** + * Utility function to get number of spiffs_page_ix entries a map buffer must + * contain on order to map given amount of file data in bytes. + * See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. + * @param fs the file system struct + * @param bytes number of file data bytes to map + * @return needed number of elements in a spiffs_page_ix array needed to + * map given amount of bytes in a file + */ +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes); + +/** + * Utility function to amount of file data bytes that can be mapped when + * mapping a file with buffer having given number of spiffs_page_ix entries. + * See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. + * @param fs the file system struct + * @param map_page_ix_entries number of entries in a spiffs_page_ix array + * @return amount of file data in bytes that can be mapped given a map + * buffer having given amount of spiffs_page_ix entries + */ +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); + +#endif // SPIFFS_IX_MAP + + +#if SPIFFS_TEST_VISUALISATION +/** + * Prints out a visualization of the filesystem. + * @param fs the file system struct + */ +s32_t SPIFFS_vis(spiffs *fs); +#endif + +#if SPIFFS_BUFFER_HELP +/** + * Returns number of bytes needed for the filedescriptor buffer given + * amount of file descriptors. + */ +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs); + +#if SPIFFS_CACHE +/** + * Returns number of bytes needed for the cache buffer given + * amount of cache pages. + */ +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); +#endif +#endif + +#if SPIFFS_CACHE +#endif +#if defined(__cplusplus) +} +#endif + +#endif /* SPIFFS_H_ */ diff --git a/components/spiffs/spiffs_cache.c b/components/spiffs/spiffs_cache.c new file mode 100644 index 0000000..018f763 --- /dev/null +++ b/components/spiffs/spiffs_cache.c @@ -0,0 +1,314 @@ +/* + * spiffs_cache.c + * + * Created on: Jun 23, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if SPIFFS_CACHE + +// returns cached page for give page index, or null if no such cached page +static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache *cache = spiffs_get_cache(fs); + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + cp->pix == pix ) { + SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix); + cp->last_access = cache->last_access; + return cp; + } + } + //SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix); + return 0; +} + +// frees cached page +static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix); + if (cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { + u8_t *mem = spiffs_get_cache_page(fs, cache, ix); + res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + } + + cp->flags = 0; + cache->cpage_use_map &= ~(1 << ix); + + if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id); + } else { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + } + } + + return res; +} + +// removes the oldest accessed cached page +static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) { + // at least one free cpage + return SPIFFS_OK; + } + + // all busy, scan thru all to find the cpage which has oldest access + int i; + int cand_ix = -1; + u32_t oldest_val = 0; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->last_access - cp->last_access) > oldest_val && + (cp->flags & flag_mask) == flags) { + oldest_val = cache->last_access - cp->last_access; + cand_ix = i; + } + } + + if (cand_ix >= 0) { + res = spiffs_cache_page_free(fs, cand_ix, 1); + } + + return res; +} + +// allocates a new cached page and returns it, or null if all cache pages are busy +static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) { + spiffs_cache *cache = spiffs_get_cache(fs); + if (cache->cpage_use_map == 0xffffffff) { + // out of cache memory + return 0; + } + int i; + for (i = 0; i < cache->cpage_count; i++) { + if ((cache->cpage_use_map & (1<cpage_use_map |= (1<last_access = cache->last_access; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i); + return cp; + } + } + // out of cache entries + return 0; +} + +// drops the cache page for give page index +void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + if (cp) { + spiffs_cache_page_free(fs, cp->ix, 0); + } +} + +// ------------------------------ + +// reads from spi flash or the cache +s32_t spiffs_phys_rd( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *dst) { + (void)fh; + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); + cache->last_access++; + if (cp) { + // we've already got one, you see +#if SPIFFS_CACHE_STATS + fs->cache_hits++; +#endif + cp->last_access = cache->last_access; + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } else { + if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { + // for second layer lookup functions, we do not cache in order to prevent shredding + return SPIFFS_HAL_READ(fs, addr, len, dst); + } +#if SPIFFS_CACHE_STATS + fs->cache_misses++; +#endif + // this operation will always free one cache page (unless all already free), + // the result code stems from the write operation of the possibly freed cache page + res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + + cp = spiffs_cache_page_allocate(fs); + if (cp) { + cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; + cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + + s32_t res2 = SPIFFS_HAL_READ(fs, + addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + spiffs_get_cache_page(fs, cache, cp->ix)); + if (res2 != SPIFFS_OK) { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } else { + // this will never happen, last resort for sake of symmetry + s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst); + if (res2 != SPIFFS_OK) { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + } + } + return res; +} + +// writes to spi flash and/or the cache +s32_t spiffs_phys_wr( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *src) { + (void)fh; + spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + + if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) { + // have a cache page + // copy in data to cache page + + if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE && + (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { + // page is being deleted, wipe from cache - unless it is a lookup page + spiffs_cache_page_free(fs, cp->ix, 0); + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } + + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + + cache->last_access++; + cp->last_access = cache->last_access; + + if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) { + // page is being updated, no write-cache, just pass thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } else { + return SPIFFS_OK; + } + } else { + // no cache page, no write cache - just write thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } +} + +#if SPIFFS_CACHE_WR +// returns the cache page that this fd refers, or null if no cache page +spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) { + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) { + // all cpages free, no cpage cannot be assigned to obj_id + return 0; + } + + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) && + cp->obj_id == fd->obj_id) { + return cp; + } + } + + return 0; +} + +// allocates a new cache page and refers this to given fd - flushes an old cache +// page if all cache is busy +spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) { + // before this function is called, it is ensured that there is no already existing + // cache page with same object id + spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + spiffs_cache_page *cp = spiffs_cache_page_allocate(fs); + if (cp == 0) { + // could not get cache page + return 0; + } + + cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; + cp->obj_id = fd->obj_id; + fd->cache_page = cp; + return cp; +} + +// unrefers all fds that this cache page refers to and releases the cache page +void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) { + if (cp == 0) return; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) { + cur_fd->cache_page = 0; + } + } + spiffs_cache_page_free(fs, cp->ix, 0); + + cp->obj_id = 0; +} + +#endif + +// initializes the cache +void spiffs_cache_init(spiffs *fs) { + if (fs->cache == 0) return; + u32_t sz = fs->cache_size; + u32_t cache_mask = 0; + int i; + int cache_entries = + (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs)); + if (cache_entries <= 0) return; + + for (i = 0; i < cache_entries; i++) { + cache_mask <<= 1; + cache_mask |= 1; + } + + spiffs_cache cache; + memset(&cache, 0, sizeof(spiffs_cache)); + cache.cpage_count = cache_entries; + cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache)); + + cache.cpage_use_map = 0xffffffff; + cache.cpage_use_mask = cache_mask; + memcpy(fs->cache, &cache, sizeof(spiffs_cache)); + + spiffs_cache *c = spiffs_get_cache(fs); + + memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); + + c->cpage_use_map &= ~(c->cpage_use_mask); + for (i = 0; i < cache.cpage_count; i++) { + spiffs_get_cache_page_hdr(fs, c, i)->ix = i; + } +} + +#endif // SPIFFS_CACHE diff --git a/components/spiffs/spiffs_check.c b/components/spiffs/spiffs_check.c new file mode 100644 index 0000000..dde85ef --- /dev/null +++ b/components/spiffs/spiffs_check.c @@ -0,0 +1,995 @@ +/* + * spiffs_check.c + * + * Contains functionality for checking file system consistency + * and mending problems. + * Three levels of consistency checks are implemented: + * + * Look up consistency + * Checks if indices in lookup pages are coherent with page headers + * Object index consistency + * Checks if there are any orphaned object indices (missing object index headers). + * If an object index is found but not its header, the object index is deleted. + * This is critical for the following page consistency check. + * Page consistency + * Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed + * + * + * Created on: Jul 7, 2013 + * Author: petera + */ + + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if !SPIFFS_READ_ONLY + +#if SPIFFS_HAL_CALLBACK_EXTRA +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#else +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#endif + +//--------------------------------------- +// Look up consistency + +// searches in the object indices and returns the referenced page index given +// the object id and the data span index +// destroys fs->lu_work +static s32_t spiffs_object_get_data_page_index_reference( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix data_spix, + spiffs_page_ix *pix, + spiffs_page_ix *objix_pix) { + s32_t res; + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // find obj index for obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix); + SPIFFS_CHECK_RES(res); + + // load obj index entry + u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); + if (objix_spix == 0) { + // get referenced page from object index header + addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); + } else { + // get referenced page from object index + addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); + } + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); + + return res; +} + +// copies page contents to a new page +static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) { + s32_t res; + res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + return res; +} + +// rewrites the object index for given object id and replaces the +// data page index to a new page index +static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (objix_spix == 0) { + // calc index in index header + entry = data_spix; + } else { + // calc entry in index + entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); + + } + // load index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + // be ultra safe, double check header against provided data + if (objix_p_hdr->obj_id != obj_id) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_OBJ_ID_MISM; + } + if (objix_p_hdr->span_ix != objix_spix) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_SPIX_MISM; + } + if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX | + SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) != + (SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_FLAGS_BAD; + } + + // rewrite in mem + if (objix_spix == 0) { + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + } else { + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + res = spiffs_page_delete(fs, objix_pix); + + return res; +} + +// deletes an object just by marking object index header as deleted +static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) { + spiffs_page_ix objix_hdr_pix; + s32_t res; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + return SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + return res; +} + +// validates the given look up entry +static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr, + spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) { + (void)cur_block; + (void)cur_entry; + u8_t delete_page = 0; + s32_t res = SPIFFS_OK; + spiffs_page_ix objix_pix; + spiffs_page_ix ref_pix; + // check validity, take actions + if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || + ((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) { + // look up entry deleted / free but used in page header + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix); + *reload_lu = 1; + delete_page = 1; + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { + // header says data page + // data page can be removed if not referenced by some object index + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } else { + SPIFFS_CHECK_RES(res); + if (ref_pix == cur_pix) { + // data page referenced by object index but deleted in lu + // copy page to new place and re-write the object index to new place + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } else { + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + } + } + } else { + // header says index page + // index page can be removed if other index with same obj_id and spanix is found + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no such index page found, check for a data page amongst page headers + // lu cannot be trusted + res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0); + if (res == SPIFFS_OK) { // ignore other errors + // got a data page also, assume lu corruption only, rewrite to new page + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + } + } else { + SPIFFS_CHECK_RES(res); + } + } + } + if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) { + // look up entry used + if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id); + delete_page = 1; + if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || + (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || + (p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) { + // page deleted or not finalized, just remove it + } else { + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { + // if data page, check for reference to this page + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } else { + SPIFFS_CHECK_RES(res); + // if found, rewrite page with object id, update index, and delete current + if (ref_pix == cur_pix) { + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + *reload_lu = 1; + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } + SPIFFS_CHECK_RES(res); + } + } + } else { + // else if index, check for other pages with both obj_id's and spanix + spiffs_page_ix objix_pix_lu, objix_pix_ph; + // see if other object index page exists for lookup obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + // if both obj_id's found, just delete current + if (objix_pix_ph == 0 || objix_pix_lu == 0) { + // otherwise try finding first corresponding data pages + spiffs_page_ix data_pix_lu, data_pix_ph; + // see if other data page exists for look up obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other data page exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL); + new_ph.span_ix = p_hdr->span_ix; + spiffs_page_ix new_pix; + if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) || + (objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) { + // got a data page for page header obj id + // rewrite as obj_id_ph + new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || + (objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) { + // got a data page for look up obj id + // rewrite as obj_id_lu + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } else { + // cannot safely do anything + SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n"); + } + } + } + } + } else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || + ((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) { + SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix); + spiffs_page_ix data_pix, objix_pix_d; + // see if other data page exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_d = 0; + } + SPIFFS_CHECK_RES(res); + + delete_page = 1; + // if other data page exists and object index exists, just delete page + if (data_pix && objix_pix_d) { + SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n"); + } else + // if only data page exists, make this page index + if (data_pix && objix_pix_d == 0) { + SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } else + // if only index exists, make data page + if (data_pix == 0 && objix_pix_d) { + SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } else { + // if nothing exists, we cannot safely make a decision - delete + } + } + else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix); + delete_page = 1; + } else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix); + // page can be removed if not referenced by object index + *reload_lu = 1; + res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + delete_page = 1; + } else { + SPIFFS_CHECK_RES(res); + if (ref_pix != cur_pix) { + SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n"); + delete_page = 1; + } else { + // page referenced by object index but not final + // just finalize + SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), (u8_t*)&flags); + } + } + } + } + + if (delete_page) { + SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + + return res; +} + +static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, + const void *user_const_p, void *user_var_p) { + (void)user_const_p; + (void)user_var_p; + s32_t res = SPIFFS_OK; + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, + (cur_block * 256)/fs->block_count, 0); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + int reload_lu = 0; + + res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu); + SPIFFS_CHECK_RES(res); + + if (res == SPIFFS_OK) { + return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; + } + return res; +} + + +// Scans all object look up. For each entry, corresponding page header is checked for validity. +// If an object index header page is found, this is also checked +s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { + (void)check_all_objects; + s32_t res = SPIFFS_OK; + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); + + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); + } + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); + + return res; +} + +//--------------------------------------- +// Page consistency + +// Scans all pages (except lu pages), reserves 4 bits in working memory for each page +// bit 0: 0 == FREE|DELETED, 1 == USED +// bit 1: 0 == UNREFERENCED, 1 == REFERENCED +// bit 2: 0 == NOT_INDEX, 1 == INDEX +// bit 3: unused +// A consistent file system will have only pages being +// * x000 free, unreferenced, not index +// * x011 used, referenced only once, not index +// * x101 used, unreferenced, index +// The working memory might not fit all pages so several scans might be needed +static s32_t spiffs_page_consistency_check_i(spiffs *fs) { + const u32_t bits = 4; + const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits; + + s32_t res = SPIFFS_OK; + spiffs_page_ix pix_offset = 0; + + // for each range of pages fitting into work memory + while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { + // set this flag to abort all checks and rescan the page range + u8_t restart = 0; + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + spiffs_block_ix cur_block = 0; + // build consistency bitmap for id range traversing all blocks + while (!restart && cur_block < fs->block_count) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, + (pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + + ((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), + 0); + // traverse each page except for lookup pages + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; + while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { + //if ((cur_pix & 0xff) == 0) + // SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n", + // cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); + + // read header + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan); + const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits); + const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits; + + if (within_range && + (p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) { + // used + fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0)); + } + if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) && + (p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) { + // found non-deleted index + if (within_range) { + fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2)); + } + + // load non-deleted index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + + // traverse index for referenced pages + spiffs_page_ix *object_page_index; + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + int entries; + int i; + spiffs_span_ix data_spix_offset; + if (p_hdr.span_ix == 0) { + // object header page index + entries = SPIFFS_OBJ_HDR_IX_LEN(fs); + data_spix_offset = 0; + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); + } else { + // object page index + entries = SPIFFS_OBJ_IX_LEN(fs); + data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1); + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)); + } + + // for all entries in index + for (i = 0; !restart && i < entries; i++) { + spiffs_page_ix rpix = object_page_index[i]; + u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan; + + if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs)) + || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) { + + // bad reference + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n", + rpix, cur_pix); + // check for data page elsewhere + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, 0, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) { + // if not, allocate free page + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = data_spix_offset + i; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); + SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix); + } + // remap index + SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix); + res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); + // delete file + res = spiffs_page_delete(fs, cur_pix); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + + } else if (rpix_within_range) { + + // valid reference + // read referenced page header + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + SPIFFS_CHECK_RES(res); + + // cross reference page header check + if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) || + rp_hdr.span_ix != data_spix_offset + i || + (rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) { + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n", + rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, + rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); + // try finding correct page + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, rpix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) { + // not found, this index is badly borked + SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + break; + } else { + // found it, so rewrite index + SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n", + data_pix, cur_pix, p_hdr.obj_id); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + } + } + else { + // mark rpix as referenced + const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits); + const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits; + if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) { + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n", + rpix, cur_pix); + // Here, we should have fixed all broken references - getting this means there + // must be multiple files with same object id. Only solution is to delete + // the object which is referring to this page + SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\n", + p_hdr.obj_id, cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + // extra precaution, delete this page also + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + restart = 1; + } + fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1)); + } + } + } // for all index entries + } // found index + + // next page + cur_pix++; + } + // next block + cur_block++; + } + // check consistency bitmap + if (!restart) { + spiffs_page_ix objix_pix; + spiffs_page_ix rpix; + + u32_t byte_ix; + u8_t bit_ix; + for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) { + for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) { + u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7; + spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix; + + // 000 ok - free, unreferenced, not index + + if (bitmask == 0x1) { + + // 001 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix); + + u8_t rewrite_ix_to_this = 0; + u8_t delete_page = 0; + // check corresponding object index entry + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix, + &rpix, &objix_pix); + if (res == SPIFFS_OK) { + if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) { + // pointing to a bad page altogether, rewrite index to this + rewrite_ix_to_this = 1; + SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix); + } else { + // pointing to something else, check what + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + SPIFFS_CHECK_RES(res); + if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) && + ((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == + (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) { + // pointing to something else valid, just delete this page then + SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix); + delete_page = 1; + } else { + // pointing to something weird, update index to point to this page instead + if (rpix != cur_pix) { + SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix, + (rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", + (rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", + (rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", + (rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "", + cur_pix); + rewrite_ix_to_this = 1; + } else { + // should not happen, destined for fubar + } + } + } + } else if (res == SPIFFS_ERR_NOT_FOUND) { + SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix); + delete_page = 1; + res = SPIFFS_OK; + } + + if (rewrite_ix_to_this) { + // if pointing to invalid page, redirect index to this page + SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n", + p_hdr.obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + continue; + } else if (delete_page) { + SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + } + SPIFFS_CHECK_RES(res); + } + if (bitmask == 0x2) { + + // 010 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + + // 011 ok - busy, referenced, not index + + if (bitmask == 0x4) { + + // 100 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix); + + // this should never happen, major fubar + } + + // 101 ok - busy, unreferenced, index + + if (bitmask == 0x6) { + + // 110 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + if (bitmask == 0x7) { + + // 111 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + } + } + } + + SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\n", pix_offset, restart); + // next page range + if (!restart) { + pix_offset += pages_per_scan; + } + } // while page range not reached end + return res; +} + +// Checks consistency amongst all pages and fixes irregularities +s32_t spiffs_page_consistency_check(spiffs *fs) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); + s32_t res = spiffs_page_consistency_check_i(fs); + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); + } + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; +} + +//--------------------------------------- +// Object index consistency + +// searches for given object id in temporary object id index, +// returns the index or -1 +static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) { + u32_t i; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) { + if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) { + return i; + } + } + return -1; +} + +static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, + int cur_entry, const void *user_const_p, void *user_var_p) { + (void)user_const_p; + s32_t res_c = SPIFFS_VIS_COUNTINUE; + s32_t res = SPIFFS_OK; + u32_t *log_ix = (u32_t*)user_var_p; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, + (cur_block * 256)/fs->block_count, 0); + + if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + if (p_hdr.span_ix == 0 && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET)) { + SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + return res_c; + } + + if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + return res_c; + } + + if (p_hdr.span_ix == 0) { + // objix header page, register objid as reachable + int r = spiffs_object_index_search(fs, obj_id); + if (r == -1) { + // not registered, do it + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { + *log_ix = 0; + } + } + } else { // span index + // objix page, see if header can be found + int r = spiffs_object_index_search(fs, obj_id); + u8_t delete = 0; + if (r == -1) { + // not in temporary index, try finding it + spiffs_page_ix objix_hdr_pix; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix); + res_c = SPIFFS_VIS_COUNTINUE_RELOAD; + if (res == SPIFFS_OK) { + // found, register as reachable + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + } else if (res == SPIFFS_ERR_NOT_FOUND) { + // not found, register as unreachable + delete = 1; + obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG; + } else { + SPIFFS_CHECK_RES(res); + } + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { + *log_ix = 0; + } + } else { + // in temporary index, check reachable flag + if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) { + // registered as unreachable + delete = 1; + } + } + + if (delete) { + SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" is orphan index - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + } // span index + } // valid object index id + + return res_c; +} + +// Removes orphaned and partially deleted index pages. +// Scans for index pages. When an index page is found, corresponding index header is searched for. +// If no such page exists, the index page cannot be reached as no index header exists and must be +// deleted. +s32_t spiffs_object_index_consistency_check(spiffs *fs) { + s32_t res = SPIFFS_OK; + // impl note: + // fs->work is used for a temporary object index memory, listing found object ids and + // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. + // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate + // a reachable/unreachable object id. + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + u32_t obj_id_log_ix = 0; + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, + 0, 0); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); + } + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; +} + +#endif // !SPIFFS_READ_ONLY diff --git a/components/spiffs/spiffs_config.h b/components/spiffs/spiffs_config.h new file mode 100644 index 0000000..b4d3b9b --- /dev/null +++ b/components/spiffs/spiffs_config.h @@ -0,0 +1,367 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +// ----------- 8< ------------ +// Following includes are for the linux test build of spiffs +// These may/should/must be removed/altered/replaced in your target +#include +#include +#include +#include +#include +#include +#include +// ----------- >8 ------------ + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" + +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + +QueueHandle_t spiffs_mutex; + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) +#endif + + +// Defines spiffs debug print formatters +// some general signed number +#ifndef _SPIPRIi +#define _SPIPRIi "%d" +#endif +// address +#ifndef _SPIPRIad +#define _SPIPRIad "%08x" +#endif +// block +#ifndef _SPIPRIbl +#define _SPIPRIbl "%04x" +#endif +// page +#ifndef _SPIPRIpg +#define _SPIPRIpg "%04x" +#endif +// span index +#ifndef _SPIPRIsp +#define _SPIPRIsp "%04x" +#endif +// file descriptor +#ifndef _SPIPRIfd +#define _SPIPRIfd "%d" +#endif +// file object id +#ifndef _SPIPRIid +#define _SPIPRIid "%04x" +#endif +// file flags +#ifndef _SPIPRIfl +#define _SPIPRIfl "%02x" +#endif + +// Enable/disable API functions to determine exact number of bytes +// for filedescriptor and cache buffers. Once decided for a configuration, +// this can be disabled to reduce flash. +#ifndef SPIFFS_BUFFER_HELP +#define SPIFFS_BUFFER_HELP 0 +#endif + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifndef SPIFFS_CACHE +#define SPIFFS_CACHE 1 +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifndef SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR 1 +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifndef SPIFFS_CACHE_STATS +#define SPIFFS_CACHE_STATS 0 +#endif +#endif + +// Always check header of each accessed page to ensure consistent state. +// If enabled it will increase number of reads, will increase flash. +#ifndef SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK 0 +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#ifndef SPIFFS_GC_MAX_RUNS +#define SPIFFS_GC_MAX_RUNS 5 +#endif + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifndef SPIFFS_GC_STATS +#define SPIFFS_GC_STATS 0 +#endif + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// To have a fair wear-leveling, the erase age is also included in score, +// whose factor normally is the most positive. +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// Garbage collecting heuristics - weight used for deleted pages. +#ifndef SPIFFS_GC_HEUR_W_DELET +#define SPIFFS_GC_HEUR_W_DELET (5) +#endif +// Garbage collecting heuristics - weight used for used pages. +#ifndef SPIFFS_GC_HEUR_W_USED +#define SPIFFS_GC_HEUR_W_USED (-1) +#endif +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) +#endif + +// Object name maximum length. Note that this length include the +// zero-termination character, meaning maximum string of characters +// can at most be SPIFFS_OBJ_NAME_LEN - 1. +#ifndef SPIFFS_OBJ_NAME_LEN +#define SPIFFS_OBJ_NAME_LEN (64) +#endif + +// Maximum length of the metadata associated with an object. +// Setting to non-zero value enables metadata-related API but also +// changes the on-disk format, so the change is not backward-compatible. +// +// Do note: the meta length must never exceed +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64) +// +// This is derived from following: +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + +// spiffs_object_ix_header fields + at least some LUT entries) +#ifndef SPIFFS_OBJ_META_LEN +#define SPIFFS_OBJ_META_LEN (64) +#endif + +// Size of buffer allocated on stack used when copying data. +// Lower value generates more read/writes. No meaning having it bigger +// than logical page size. +#ifndef SPIFFS_COPY_BUFFER_STACK +#define SPIFFS_COPY_BUFFER_STACK (256) +#endif + +// Enable this to have an identifiable spiffs filesystem. This will look for +// a magic in all sectors to determine if this is a valid spiffs system or +// not on mount point. If not, SPIFFS_format must be called prior to mounting +// again. +#ifndef SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC (1) +#endif + +#if SPIFFS_USE_MAGIC +// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is +// enabled, the magic will also be dependent on the length of the filesystem. +// For example, a filesystem configured and formatted for 4 megabytes will not +// be accepted for mounting with a configuration defining the filesystem as 2 +// megabytes. +#ifndef SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH (1) +#endif +#endif + +// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level +// These should be defined on a multithreaded system + +// define this to enter a mutex if you're running on a multithreaded system +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) xSemaphoreTake(spiffs_mutex, portMAX_DELAY) +#endif +// define this to exit a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) xSemaphoreGive(spiffs_mutex) +#endif + +// Enable if only one spiffs instance with constant configuration will exist +// on the target. This will reduce calculations, flash and memory accesses. +// Parts of configuration must be defined below instead of at time of mount. +#ifndef SPIFFS_SINGLETON +#define SPIFFS_SINGLETON 0 +#endif + +#if SPIFFS_SINGLETON +// Instead of giving parameters in config struct, singleton build must +// give parameters in defines below. +#ifndef SPIFFS_CFG_PHYS_SZ +#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536) +#endif +#ifndef SPIFFS_CFG_PHYS_ADDR +#define SPIFFS_CFG_PHYS_ADDR(ignore) (0) +#endif +#ifndef SPIFFS_CFG_LOG_PAGE_SZ +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#endif +#ifndef SPIFFS_CFG_LOG_BLOCK_SZ +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536) +#endif +#endif + +// Enable this if your target needs aligned data for index tables +#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 +#endif + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 0 +#endif + +// Enable this if you want to add an integer offset to all file handles +// (spiffs_file). This is useful if running multiple instances of spiffs on +// same target, in order to recognise to what spiffs instance a file handle +// belongs. +// NB: This adds config field fh_ix_offset in the configuration struct when +// mounting, which must be defined. +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 0 +#endif + +// Enable this to compile a read only version of spiffs. +// This will reduce binary size of spiffs. All code comprising modification +// of the file system will not be compiled. Some config will be ignored. +// HAL functions for erasing and writing to spi-flash may be null. Cache +// can be disabled for even further binary size reduction (and ram savings). +// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. +// If the file system cannot be mounted due to aborted erase operation and +// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be +// returned. +// Might be useful for e.g. bootloaders and such. +#ifndef SPIFFS_READ_ONLY +#define SPIFFS_READ_ONLY 0 +#endif + +// Enable this to add a temporal file cache using the fd buffer. +// The effects of the cache is that SPIFFS_open will find the file faster in +// certain cases. It will make it a lot easier for spiffs to find files +// opened frequently, reducing number of readings from the spi flash for +// finding those files. +// This will grow each fd by 6 bytes. If your files are opened in patterns +// with a degree of temporal locality, the system is optimized. +// Examples can be letting spiffs serve web content, where one file is the css. +// The css is accessed for each html file that is opened, meaning it is +// accessed almost every second time a file is opened. Another example could be +// a log file that is often opened, written, and closed. +// The size of the cache is number of given file descriptors, as it piggybacks +// on the fd update mechanism. The cache lives in the closed file descriptors. +// When closed, the fd know the whereabouts of the file. Instead of forgetting +// this, the temporal cache will keep handling updates to that file even if the +// fd is closed. If the file is opened again, the location of the file is found +// directly. If all available descriptors become opened, all cache memory is +// lost. +#ifndef SPIFFS_TEMPORAL_FD_CACHE +#define SPIFFS_TEMPORAL_FD_CACHE 1 +#endif + +// Temporal file cache hit score. Each time a file is opened, all cached files +// will lose one point. If the opened file is found in cache, that entry will +// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this +// value for the specific access patterns of the application. However, it must +// be between 1 (no gain for hitting a cached entry often) and 255. +#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE +#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 8 +#endif + +// Enable to be able to map object indices to memory. +// This allows for faster and more deterministic reading if cases of reading +// large files and when changing file offset by seeking around a lot. +// When mapping a file's index, the file system will be scanned for index pages +// and the info will be put in memory provided by user. When reading, the +// memory map can be looked up instead of searching for index pages on the +// medium. This way, user can trade memory against performance. +// Whole, parts of, or future parts not being written yet can be mapped. The +// memory array will be owned by spiffs and updated accordingly during garbage +// collecting or when modifying the indices. The latter is invoked by when the +// file is modified in some way. The index buffer is tied to the file +// descriptor. +#ifndef SPIFFS_IX_MAP +#define SPIFFS_IX_MAP 1 +#endif + +// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function +// in the api. This function will visualize all filesystem using given printf +// function. +#ifndef SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 0 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) printf(__VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#ifndef SPIFFS_TEST_VIS_FREE_STR +#define SPIFFS_TEST_VIS_FREE_STR "_" +#endif +// spiffs_printf argument for a deleted page +#ifndef SPIFFS_TEST_VIS_DELE_STR +#define SPIFFS_TEST_VIS_DELE_STR "/" +#endif +// spiffs_printf argument for an index page for given object id +#ifndef SPIFFS_TEST_VIS_INDX_STR +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +#endif +// spiffs_printf argument for a data page for given object id +#ifndef SPIFFS_TEST_VIS_DATA_STR +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif +#endif + +// Types depending on configuration such as the amount of flash bytes +// given to spiffs file system in total (spiffs_file_system_size), +// the logical block size (log_block_size), and the logical page size +// (log_page_size) + +// Block index type. Make sure the size of this type can hold +// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size +typedef u16_t spiffs_block_ix; +// Page index type. Make sure the size of this type can hold +// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size +typedef u16_t spiffs_page_ix; +// Object id type - most significant bit is reserved for index flag. Make sure the +// size of this type can hold the highest object id on a full system, +// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 +typedef u16_t spiffs_obj_id; +// Object span index type. Make sure the size of this type can +// hold the largest possible span index on the system - +// i.e. (spiffs_file_system_size / log_page_size) - 1 +typedef u16_t spiffs_span_ix; + +#endif /* SPIFFS_CONFIG_H_ */ diff --git a/components/spiffs/spiffs_gc.c b/components/spiffs/spiffs_gc.c new file mode 100644 index 0000000..db1af4c --- /dev/null +++ b/components/spiffs/spiffs_gc.c @@ -0,0 +1,606 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if !SPIFFS_READ_ONLY + +// Erases a logical block and updates the erase counter. +// If cache is enabled, all pages that might be cached in this block +// is dropped. +static s32_t spiffs_gc_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + + SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix); + res = spiffs_erase_block(fs, bix); + SPIFFS_CHECK_RES(res); + +#if SPIFFS_CACHE + { + u32_t i; + for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { + spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); + } + } +#endif + return res; +} + +// Searches for blocks where all entries are deleted - if one is found, +// the block is erased. Compared to the non-quick gc, the quick one ensures +// that no updates are needed on existing objects on pages that are erased. +s32_t spiffs_gc_quick( + spiffs *fs, u16_t max_free_pages) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + + SPIFFS_GC_DBG("gc_quick: running\n"); +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // find fully deleted blocks + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + u16_t free_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && + cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_DELETED) { + deleted_pages_in_block++; + } else if (obj_id == SPIFFS_OBJ_ID_FREE) { + // kill scan, go for next block + free_pages_in_block++; + if (free_pages_in_block > max_free_pages) { + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } + } else { + // kill scan, go for next block + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + if (res == 1) res = SPIFFS_OK; + + if (res == SPIFFS_OK && + deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) && + free_pages_in_block <= max_free_pages) { + // found a fully deleted block + fs->stats_p_deleted -= deleted_pages_in_block; + res = spiffs_gc_erase_block(fs, cur_block); + return res; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + if (res == SPIFFS_OK) { + res = SPIFFS_ERR_NO_DELETED_BLOCKS; + } + return res; +} + +// Checks if garbage collecting is necessary. If so a candidate block is found, +// cleansed and erased +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len) { + s32_t res; + s32_t free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2) + - fs->stats_p_allocated - fs->stats_p_deleted; + int tries = 0; + + if (fs->free_blocks > 3 && + (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { + return SPIFFS_OK; + } + + u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); +// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { +// SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); +// return SPIFFS_ERR_FULL; +// } + if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { + SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); + return SPIFFS_ERR_FULL; + } + + do { + SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n", + tries, + fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), + len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); + + spiffs_block_ix *cands; + int count; + spiffs_block_ix cand; + s32_t prev_free_pages = free_pages; + // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state + res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); + SPIFFS_CHECK_RES(res); + if (count == 0) { + SPIFFS_GC_DBG("gc_check: no candidates, return\n"); + return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; + } +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + cand = cands[0]; + fs->cleaning = 1; + //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand); + res = spiffs_gc_clean(fs, cand); + fs->cleaning = 0; + if (res < 0) { + SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); + } else { + SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); + } + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_page_stats(fs, cand); + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_block(fs, cand); + SPIFFS_CHECK_RES(res); + + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + + if (prev_free_pages <= 0 && prev_free_pages == free_pages) { + // abort early to reduce wear, at least tried once + SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n"); + break; + } + + } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || + (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); + + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { + res = SPIFFS_ERR_FULL; + } + + SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n", + fs->stats_p_allocated + fs->stats_p_deleted, + fs->free_blocks, free_pages, tries, res); + + return res; +} + +// Updates page statistics for a block that is about to be erased +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + int obj_lookup_page = 0; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + u32_t dele = 0; + u32_t allo = 0; + + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + dele++; + } else { + allo++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele); + fs->stats_p_allocated -= allo; + fs->stats_p_deleted -= dele; + return res; +} + +// Finds block candidates to erase +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidates, + int *candidate_count, + char fs_crammed) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + + // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score + int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t))); + *candidate_count = 0; + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + // divide up work area into block indices and scores + spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; + s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); + + // align cand_scores on s32_t boundary + cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); + + *block_candidates = cand_blocks; + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + u16_t used_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && + cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + // when a free entry is encountered, scan logic ensures that all following entries are free also + res = 1; // kill object lu loop + break; + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + deleted_pages_in_block++; + } else { + used_pages_in_block++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + if (res == 1) res = SPIFFS_OK; + + // calculate score and insert into candidate table + // stoneage sort, but probably not so many blocks + if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) { + // read erase count + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + spiffs_obj_id erase_age; + if (fs->max_erase_count > erase_count) { + erase_age = fs->max_erase_count - erase_count; + } else { + erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); + } + + s32_t score = + deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + + used_pages_in_block * SPIFFS_GC_HEUR_W_USED + + erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); + int cand_ix = 0; + SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); + while (cand_ix < max_candidates) { + if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } else if (cand_scores[cand_ix] < score) { + int reorder_cand_ix = max_candidates - 2; + while (reorder_cand_ix >= cand_ix) { + cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; + cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; + reorder_cand_ix--; + } + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } + cand_ix++; + } + (*candidate_count)++; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + return res; +} + +typedef enum { + FIND_OBJ_DATA, + MOVE_OBJ_DATA, + MOVE_OBJ_IX, + FINISHED +} spiffs_gc_clean_state; + +typedef struct { + spiffs_gc_clean_state state; + spiffs_obj_id cur_obj_id; + spiffs_span_ix cur_objix_spix; + spiffs_page_ix cur_objix_pix; + spiffs_page_ix cur_data_pix; + int stored_scan_entry_index; + u8_t obj_id_found; +} spiffs_gc; + +// Empties given block by moving all data into free pages of another block +// Strategy: +// loop: +// scan object lookup for object data pages +// for first found id, check spix and load corresponding object index page to memory +// push object scan lookup entry index +// rescan object lookup, find data pages with same id and referenced by same object index +// move data page, update object index in memory +// when reached end of lookup, store updated object index +// pop object scan lookup entry index +// repeat loop until end of object lookup +// scan object lookup again for remaining object index pages, move to new page in other block +// +s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + // this is the global localizer being pushed and popped + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_gc gc; // our stack frame/state + spiffs_page_ix cur_pix = 0; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix); + + memset(&gc, 0, sizeof(spiffs_gc)); + gc.state = FIND_OBJ_DATA; + + if (fs->free_cursor_block_ix == bix) { + // move free cursor to next block, cannot use free pages from the block we want to clean + fs->free_cursor_block_ix = (bix+1)%fs->block_count; + fs->free_cursor_obj_lu_entry = 0; + SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix); + } + + while (res == SPIFFS_OK && gc.state != FINISHED) { + SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry); + gc.obj_id_found = 0; // reset (to no found data page) + + // scan through lookup pages + int obj_lookup_page = cur_entry / entries_per_page; + u8_t scan = 1; + // check each object lookup page + while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each object lookup entry + while (scan && res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); + + // act upon object id depending on gc state + switch (gc.state) { + case FIND_OBJ_DATA: + // find a data page + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { + // found a data page, stop scanning and handle in switch case below + SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id); + gc.obj_id_found = 1; + gc.cur_obj_id = obj_id; + gc.cur_data_pix = cur_pix; + scan = 0; + } + break; + case MOVE_OBJ_DATA: + // evacuate found data pages for corresponding object index we have in memory, + // update memory representation + if (obj_id == gc.cur_obj_id) { + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); + if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { + SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); + } else { + spiffs_page_ix new_data_pix; + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); + SPIFFS_CHECK_RES(res); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + new_data_pix = SPIFFS_OBJ_ID_FREE; + } + // update memory representation of object index page with new data page + if (gc.cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } + } + } + break; + case MOVE_OBJ_IX: + // find and evacuate object index pages + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + // found an index object id + spiffs_page_header p_hdr; + spiffs_page_ix new_pix; + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, + SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + if (res == SPIFFS_OK) { + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); + } + } + SPIFFS_CHECK_RES(res); + } + break; + default: + scan = 0; + break; + } // switch gc state + cur_entry++; + } // per entry + obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop + } // per object lookup page + if (res != SPIFFS_OK) break; + + // state finalization and switch + switch (gc.state) { + case FIND_OBJ_DATA: + if (gc.obj_id_found) { + // handle found data page - + // find out corresponding obj ix page and load it to memory + spiffs_page_header p_hdr; + spiffs_page_ix objix_pix; + gc.stored_scan_entry_index = cur_entry; // push cursor + cur_entry = 0; // restart scan from start + gc.state = MOVE_OBJ_DATA; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); + SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix); + res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // on borked systems we might get an ERR_NOT_FOUND here - + // this is handled by simply deleting the page as it is not referenced + // from anywhere + SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix); + res = spiffs_page_delete(fs, gc.cur_data_pix); + SPIFFS_CHECK_RES(res); + // then we restore states and continue scanning for data pages + cur_entry = gc.stored_scan_entry_index; // pop cursor + gc.state = FIND_OBJ_DATA; + break; // done + } + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + // cannot allow a gc if the presumed index in fact is no index, a + // check must run or lot of data may be lost + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); + gc.cur_objix_pix = objix_pix; + } else { + // no more data pages found, passed thru all block, start evacuating object indices + gc.state = MOVE_OBJ_IX; + cur_entry = 0; // restart entry scan index + } + break; + case MOVE_OBJ_DATA: { + // store modified objix (hdr) page residing in memory now that all + // data pages belonging to this object index and residing in the block + // we want to evacuate + spiffs_page_ix new_objix_pix; + gc.state = FIND_OBJ_DATA; + cur_entry = gc.stored_scan_entry_index; // pop cursor + if (gc.cur_objix_spix == 0) { + // store object index header page + res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0); + SPIFFS_CHECK_RES(res); + } else { + // store object index page + res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + break; + case MOVE_OBJ_IX: + // scanned thru all block, no more object indices found - our work here is done + gc.state = FINISHED; + break; + default: + cur_entry = 0; + break; + } // switch gc.state + SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state); + } // while state != FINISHED + + + return res; +} + +#endif // !SPIFFS_READ_ONLY diff --git a/components/spiffs/spiffs_hydrogen.c b/components/spiffs/spiffs_hydrogen.c new file mode 100644 index 0000000..9ff3e7a --- /dev/null +++ b/components/spiffs/spiffs_hydrogen.c @@ -0,0 +1,1405 @@ +/* + * spiffs_hydrogen.c + * + * Created on: Jun 16, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0) +#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0) +#else +#define SPIFFS_FH_OFFS(fs, fh) (fh) +#define SPIFFS_FH_UNOFFS(fs, fh) (fh) +#endif + +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); +#endif + +#if SPIFFS_BUFFER_HELP +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { + return num_descs * sizeof(spiffs_fd); +} +#if SPIFFS_CACHE +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { + return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); +} +#endif +#endif + +u8_t SPIFFS_mounted(spiffs *fs) { + return SPIFFS_CHECK_MOUNT(fs); +} + +s32_t SPIFFS_format(spiffs *fs) { +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + if (SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_MOUNTED; + return -1; + } + + s32_t res; + SPIFFS_LOCK(fs); + + spiffs_block_ix bix = 0; + while (bix < fs->block_count) { + fs->max_erase_count = 0; + res = spiffs_erase_block(fs, bix); + if (res != SPIFFS_OK) { + res = SPIFFS_ERR_ERASE_FAIL; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + bix++; + } + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_probe_fs(spiffs_config *config) { + s32_t res = spiffs_probe(config); + return res; +} + +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f) { + void *user_data; + SPIFFS_LOCK(fs); + user_data = fs->user_data; + memset(fs, 0, sizeof(spiffs)); + memcpy(&fs->cfg, config, sizeof(spiffs_config)); + fs->user_data = user_data; + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary + u8_t ptr_size = sizeof(void*); + u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1); + if (addr_lsb) { + fd_space += (ptr_size-addr_lsb); + fd_space_size -= (ptr_size-addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); + + // align cache pointer to 4 byte boundary + addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1); + if (addr_lsb) { + u8_t *cache_8 = (u8_t *)cache; + cache_8 += (ptr_size-addr_lsb); + cache = cache_8; + cache_size -= (ptr_size-addr_lsb); + } + if (cache_size & (ptr_size-1)) { + cache_size -= (cache_size & (ptr_size-1)); + } + +#if SPIFFS_CACHE + fs->cache = cache; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size; + spiffs_cache_init(fs); +#endif + + s32_t res; + +#if SPIFFS_USE_MAGIC + res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + fs->config_magic = SPIFFS_CONFIG_MAGIC; + + res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count); + SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks); + + fs->check_cb_f = check_cb_f; + + fs->mounted = 1; + + SPIFFS_UNLOCK(fs); + + return 0; +} + +void SPIFFS_unmount(spiffs *fs) { + if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; + SPIFFS_LOCK(fs); + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0) { +#if SPIFFS_CACHE + (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); +#endif + spiffs_fd_return(fs, cur_fd->file_nbr); + } + } + fs->mounted = 0; + + SPIFFS_UNLOCK(fs); +} + +s32_t SPIFFS_errno(spiffs *fs) { + return fs->err_code; +} + +void SPIFFS_clearerr(spiffs *fs) { + fs->err_code = SPIFFS_OK; +} + +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { +#if SPIFFS_READ_ONLY + (void)fs; (void)path; (void)mode; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; + + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + +#if SPIFFS_READ_ONLY + // not valid flags in read only mode + flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); +#endif // SPIFFS_READ_ONLY + + s32_t res = spiffs_fd_find_new(fs, &fd, path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if ((flags & SPIFFS_O_CREAT) == 0) { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (res == SPIFFS_OK && + (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { +#if !SPIFFS_READ_ONLY + spiffs_obj_id obj_id; + // no need to enter conflicting name here, already looked for it above + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + flags &= ~SPIFFS_O_TRUNC; +#endif // !SPIFFS_READ_ONLY + } else { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { + res = SPIFFS_ERR_NOT_A_FILE; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); + if (res == SPIFFS_ERR_IS_FREE || + res == SPIFFS_ERR_DELETED || + res == SPIFFS_ERR_NOT_FINALIZED || + res == SPIFFS_ERR_NOT_INDEX || + res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { + res = SPIFFS_ERR_NOT_A_FILE; + } + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_RDONLY) == 0) { + res = SPIFFS_ERR_NOT_READABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { + // special case for zero sized files + res = SPIFFS_ERR_END_OF_OBJECT; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + if (fd->fdoffset + len >= fd->size) { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } else { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + len = avail; + } + } else { + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return len; +} + +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + s32_t res = spiffs_hydro_read(fs, fh, buf, len); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + res = 0; + } + return res; +} + + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { + (void)fs; + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN((s32_t)(fd->size - offset), len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + u8_t *buf_8 = (u8_t *)buf; + buf_8 += m_len; + buf = buf_8; + offset += m_len; + } + if (remaining > 0) { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; + +} +#endif // !SPIFFS_READ_ONLY + +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)buf; (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + u32_t offset; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((fd->flags & SPIFFS_O_APPEND)) { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + } + + offset = fd->fdoffset; + +#if SPIFFS_CACHE_WR + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } +#endif + if (fd->flags & SPIFFS_O_APPEND) { + if (fd->size == SPIFFS_UNDEFINED_LEN) { + offset = 0; + } else { + offset = fd->size; + } +#if SPIFFS_CACHE_WR + if (fd->cache_page) { + offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); + } +#endif + } + +#if SPIFFS_CACHE_WR + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->offset || // writing before cache + offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache + offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page + { + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) { + fd->cache_page->offset = offset; + fd->cache_page->size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) { + u32_t offset_in_cpage = offset - fd->cache_page->offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = spiffs_get_cache(fs); + u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); + memcpy(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } else { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // data written below + } + } + } +#endif + + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + s32_t fileSize = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + + switch (whence) { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset+offs; + break; + case SPIFFS_SEEK_END: + offs = fileSize + offs; + break; + } + + if ((offs > fileSize)) { + fd->fdoffset = fileSize; + res = SPIFFS_ERR_END_OF_OBJECT; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; + + SPIFFS_UNLOCK(fs); + + return offs; +} + +s32_t SPIFFS_remove(spiffs *fs, const char *path) { +#if SPIFFS_READ_ONLY + (void)fs; (void)path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t res; + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0,0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_truncate(fd, 0, 1); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_cache_fd_release(fs, fd->cache_page); +#endif + + res = spiffs_object_truncate(fd, 0, 1); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { + (void)fh; + spiffs_page_object_ix_header objix_hdr; + spiffs_obj_id obj_id; + s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES(fs, res); + + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); + res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, + obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_API_CHECK_RES(fs, res); + + s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + s->pix = pix; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + memcpy(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + + return res; +} + +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_page_ix pix; + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_stat_pix(fs, pix, 0, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +// Checks if there are any cached writes for the object id associated with +// given filehandle. If so, these writes are flushed. +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + (void)fs; + (void)fh; + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + if (res < SPIFFS_OK) { + fs->err_code = res; + } + spiffs_cache_fd_release(fs, fd->cache_page); + } + } +#endif + + return res; +} +#endif + +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + (void)fh; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + SPIFFS_LOCK(fs); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs,res); + SPIFFS_UNLOCK(fs); +#endif + + return res; +} + +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + + s32_t res = SPIFFS_OK; + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); +#if SPIFFS_CACHE + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { +#if SPIFFS_READ_ONLY + (void)fs; (void)old_path; (void)new_path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || + strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_page_ix pix_old, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + } else if (res == SPIFFS_OK) { + res = SPIFFS_ERR_CONFLICTING_NAME; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, + 0, 0, &pix_dummy); +#if SPIFFS_TEMPORAL_FD_CACHE + if (res == SPIFFS_OK) { + spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); + } +#endif + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_OBJ_META_LEN +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)name; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_page_ix pix, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} +#endif // SPIFFS_OBJ_META_LEN + +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { + (void)name; + + if (!SPIFFS_CHECK_CFG((fs))) { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return 0; + } + + if (!SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; +} + +static s32_t spiffs_read_dir_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) return res; + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; + e->obj_id = obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + e->pix = pix; +#if SPIFFS_OBJ_META_LEN + memcpy(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + return SPIFFS_OK; + } + return SPIFFS_VIS_COUNTINUE; +} + +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { + if (!SPIFFS_CHECK_MOUNT(d->fs)) { + d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + SPIFFS_LOCK(d->fs); + + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; + + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) { + d->block = bix; + d->entry = entry + 1; + e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + ret = e; + } else { + d->fs->err_code = res; + } + SPIFFS_UNLOCK(d->fs); + return ret; +} + +s32_t SPIFFS_closedir(spiffs_DIR *d) { + SPIFFS_API_CHECK_CFG(d->fs); + SPIFFS_API_CHECK_MOUNT(d->fs); + return 0; +} + +s32_t SPIFFS_check(spiffs *fs) { +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_lookup_consistency_check(fs, 0); + + res = spiffs_object_index_consistency_check(fs); + + res = spiffs_page_consistency_check(fs); + + res = spiffs_obj_lu_scan(fs); + + SPIFFS_UNLOCK(fs); + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); + u32_t blocks = fs->block_count; + u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); + u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); + u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page + + if (total) { + *total = total_data_pages * data_page_size; + } + + if (used) { + *used = fs->stats_p_allocated * data_page_size; + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { +#if SPIFFS_READ_ONLY + (void)fs; (void)max_free_pages; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_quick(fs, max_free_pages); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + + +s32_t SPIFFS_gc(spiffs *fs, u32_t size) { +#if SPIFFS_READ_ONLY + (void)fs; (void)size; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_check(fs, size); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = fd->fdoffset; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) { + SPIFFS_LOCK(fs); + fs->file_cb_f = cb_func; + SPIFFS_UNLOCK(fs); + return 0; +} + +#if SPIFFS_IX_MAP + +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); + } + + map->map_buf = map_buf; + map->offset = offset; + // nb: spix range includes last + map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); + memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); + fd->ix_map = map; + + // scan for pixes + res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + fd->ix_map = 0; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + spiffs_ix_map *map = fd->ix_map; + + s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; + map->offset = offset; + + // move existing pixes if within map offs + if (spix_diff != 0) { + // move vector + int i; + const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last + map->start_spix += spix_diff; + map->end_spix += spix_diff; + if (spix_diff >= vec_len) { + // moving beyond range + memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else if (spix_diff > 0) { + // diff positive + for (i = 0; i < vec_len - spix_diff; i++) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // diff negative + for (i = vec_len - 1; i >= -spix_diff; i--) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) { + SPIFFS_API_CHECK_CFG(fs); + // always add one extra page, the offset might change to the middle of a page + return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs); +} + +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) { + SPIFFS_API_CHECK_CFG(fs); + return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); +} + +#endif // SPIFFS_IX_MAP + +#if SPIFFS_TEST_VISUALISATION +s32_t SPIFFS_vis(spiffs *fs) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_block_ix bix = 0; + + while (bix < fs->block_count) { + // check each object lookup page + int obj_lookup_page = 0; + int cur_entry = 0; + + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (cur_entry == 0) { + spiffs_printf(_SPIPRIbl" ", bix); + } else if ((cur_entry & 0x3f) == 0) { + spiffs_printf(" "); + } + if (obj_id == SPIFFS_OBJ_ID_FREE) { + spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); + } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){ + spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + } else { + spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + } + cur_entry++; + if ((cur_entry & 0x3f) == 0) { + spiffs_printf("\n"); + } + } // per entry + obj_lookup_page++; + } // per object lookup page + + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + if (erase_count != (spiffs_obj_id)-1) { + spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count); + } else { + spiffs_printf("\tera_cnt: N/A\n"); + } + + bix++; + } // per block + + spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count); + spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code); + spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count); + spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks); + spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated); + spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted); + SPIFFS_UNLOCK(fs); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total); + return res; +} +#endif diff --git a/components/spiffs/spiffs_nucleus.c b/components/spiffs/spiffs_nucleus.c new file mode 100644 index 0000000..44ba711 --- /dev/null +++ b/components/spiffs/spiffs_nucleus.c @@ -0,0 +1,2327 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" + +static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix)-1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_REF_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_REF_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_REF_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); +#endif + return res; +} + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix)-1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); +#endif + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_CACHE + +s32_t spiffs_phys_rd( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *dst) { + return SPIFFS_HAL_READ(fs, addr, len, dst); +} + +s32_t spiffs_phys_wr( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *src) { + return SPIFFS_HAL_WRITE(fs, addr, len, src); +} + +#endif + +#if !SPIFFS_READ_ONLY +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len) { + (void)fh; + s32_t res; + u8_t b[SPIFFS_COPY_BUFFER_STACK]; + while (len > 0) { + u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); + SPIFFS_CHECK_RES(res); + len -= chunk_size; + src += chunk_size; + dst += chunk_size; + } + return SPIFFS_OK; +} +#endif // !SPIFFS_READ_ONLY + +// Find object lookup entry containing given id with visitor. +// Iterate over object lookup pages in each block until a given object id entry is found. +// When found, the visitor function is called with block index, entry index and user data. +// If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be +// ended and visitor's return code is returned to caller. +// If no visitor is given (0) the search returns on first entry with matching object id. +// If no match is found in all look up, SPIFFS_VIS_END is returned. +// @param fs the file system +// @param starting_block the starting block to start search in +// @param starting_lu_entry the look up index entry to start search in +// @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH, +// SPIFFS_VIS_NO_WRAP +// @param obj_id argument object id +// @param v visitor callback function +// @param user_const_p any const pointer, passed to the callback visitor function +// @param user_var_p any pointer, passed to the callback visitor function +// @param block_ix reported block index where match was found +// @param lu_entry reported look up index where match was found +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + const void *user_const_p, + void *user_var_p, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res = SPIFFS_OK; + s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); + spiffs_block_ix cur_block = starting_block; + u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = starting_lu_entry; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // wrap initial + if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + cur_entry = 0; + cur_block++; + cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } + + // check each block + while (res == SPIFFS_OK && entry_count > 0) { + int obj_lookup_page = cur_entry / entries_per_page; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages + cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page + { + if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) { + if (block_ix) *block_ix = cur_block; + if (lu_entry) *lu_entry = cur_entry; + if (v) { + res = v( + fs, + (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset], + cur_block, + cur_entry, + user_const_p, + user_var_p); + if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { + if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } + res = SPIFFS_OK; + cur_entry++; + entry_count--; + continue; + } else { + return res; + } + } else { + return SPIFFS_OK; + } + } + entry_count--; + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } // per block + + SPIFFS_CHECK_RES(res); + + return SPIFFS_VIS_END; +} + +#if !SPIFFS_READ_ONLY +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + // here we ignore res, just try erasing the block + while (size > 0) { + SPIFFS_DBG("erase "_SPIPRIad":"_SPIPRIi"\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); + size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); + } + fs->free_blocks++; + + // register erase count for this block + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + SPIFFS_CHECK_RES(res); + +#if SPIFFS_USE_MAGIC + // finally, write magic + spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + SPIFFS_CHECK_RES(res); +#endif + + fs->max_erase_count++; + if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { + fs->max_erase_count = 0; + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +s32_t spiffs_probe( + spiffs_config *cfg) { + s32_t res; + u32_t paddr; + spiffs dummy_fs; // create a dummy fs struct just to be able to use macros + memcpy(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); + dummy_fs.block_count = 0; + + // Read three magics, as one block may be in an aborted erase state. + // At least two of these must contain magic and be in decreasing order. + spiffs_obj_id magic[3]; + spiffs_obj_id bix_count[3]; + + spiffs_block_ix bix; + for (bix = 0; bix < 3; bix++) { + paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); +#if SPIFFS_HAL_CALLBACK_EXTRA + // not any proper fs to report here, so callback with null + // (cross fingers that no-one gets angry) + res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#else + res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#endif + bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); + SPIFFS_CHECK_RES(res); + } + + // check that we have sane number of blocks + if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; + // check that the order is correct, take aborted erases in calculation + // first block aborted erase + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { + return (bix_count[1]+1) * cfg->log_block_size; + } + // second block aborted erase + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { + return bix_count[0] * cfg->log_block_size; + } + // third block aborted erase + if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { + return bix_count[0] * cfg->log_block_size; + } + // no block has aborted erase + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { + return bix_count[0] * cfg->log_block_size; + } + + return SPIFFS_ERR_PROBE_NOT_A_FS; +} +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + + +static s32_t spiffs_obj_lu_scan_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)bix; + (void)user_const_p; + (void)user_var_p; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + if (ix_entry == 0) { + fs->free_blocks++; + // todo optimize further, return SPIFFS_NEXT_BLOCK + } + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + fs->stats_p_deleted++; + } else { + fs->stats_p_allocated++; + } + + return SPIFFS_VIS_COUNTINUE; +} + + +// Scans thru all obj lu and counts free, deleted and used pages +// Find the maximum block erase count +// Checks magic if enabled +s32_t spiffs_obj_lu_scan( + spiffs *fs) { + s32_t res; + spiffs_block_ix bix; + int entry; +#if SPIFFS_USE_MAGIC + spiffs_block_ix unerased_bix = (spiffs_block_ix)-1; +#endif + + // find out erase count + // if enabled, check magic + bix = 0; + spiffs_obj_id erase_count_final; + spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; + spiffs_obj_id erase_count_max = 0; + while (bix < fs->block_count) { +#if SPIFFS_USE_MAGIC + spiffs_obj_id magic; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_MAGIC_PADDR(fs, bix) , + sizeof(spiffs_obj_id), (u8_t *)&magic); + + SPIFFS_CHECK_RES(res); + if (magic != SPIFFS_MAGIC(fs, bix)) { + if (unerased_bix == (spiffs_block_ix)-1) { + // allow one unerased block as it might be powered down during an erase + unerased_bix = bix; + } else { + // more than one unerased block, bail out + SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); + } + } +#endif + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix) , + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + if (erase_count != SPIFFS_OBJ_ID_FREE) { + erase_count_min = MIN(erase_count_min, erase_count); + erase_count_max = MAX(erase_count_max, erase_count); + } + bix++; + } + + if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) { + // clean system, set counter to zero + erase_count_final = 0; + } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE)/2) { + // wrap, take min + erase_count_final = erase_count_min+1; + } else { + erase_count_final = erase_count_max+1; + } + + fs->max_erase_count = erase_count_final; + +#if SPIFFS_USE_MAGIC + if (unerased_bix != (spiffs_block_ix)-1) { + // found one unerased block, remedy + SPIFFS_DBG("mount: erase block "_SPIPRIbl"\n", bix); +#if SPIFFS_READ_ONLY + res = SPIFFS_ERR_RO_ABORTED_OPERATION; +#else + res = spiffs_erase_block(fs, unerased_bix); +#endif // SPIFFS_READ_ONLY + SPIFFS_CHECK_RES(res); + } +#endif + + // count blocks + + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; + + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + SPIFFS_CHECK_RES(res); + + return res; +} + +#if !SPIFFS_READ_ONLY +// Find free object lookup entry +// Iterate over object lookup pages in each block until a free object id entry is found +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res; + if (!fs->cleaning && fs->free_blocks < 2) { + res = spiffs_gc_quick(fs, 0); + if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { + res = SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + if (fs->free_blocks < 2) { + return SPIFFS_ERR_FULL; + } + } + res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, + SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); + if (res == SPIFFS_OK) { + fs->free_cursor_block_ix = *block_ix; + fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; + if (*lu_entry == 0) { + fs->free_blocks--; + } + } + if (res == SPIFFS_ERR_FULL) { + SPIFFS_DBG("fs full\n"); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +// Find object lookup entry containing given id +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res = spiffs_obj_lu_find_entry_visitor( + fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + return res; +} + + +static s32_t spiffs_obj_lu_find_id_and_span_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + s32_t res; + spiffs_page_header ph; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + if (ph.obj_id == obj_id && + ph.span_ix == *((spiffs_span_ix*)user_var_p) && + (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && + !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && + (user_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) { + return SPIFFS_OK; + } else { + return SPIFFS_VIS_COUNTINUE; + } +} + +// Find object lookup entry containing given id and span index +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_ID, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Find object lookup entry containing given id and span index in page headers only +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_PH, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +#if SPIFFS_IX_MAP + +// update index map of given fd with given object index data +static void spiffs_update_ix_map(spiffs *fs, + spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) { +#if SPIFFS_SINGLETON + (void)fs; +#endif + spiffs_ix_map *map = fd->ix_map; + spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); + spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); + + // check if updated ix is within map range + if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) { + return; + } + + // update memory mapped page index buffer to new pages + + // get range of updated object index map data span indices + spiffs_span_ix objix_data_spix_start = + SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); + spiffs_span_ix objix_data_spix_end = objix_data_spix_start + + (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); + + // calc union of object index range and index map range array + spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); + spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); + + while (map_spix < map_spix_end) { + spiffs_page_ix objix_data_pix; + if (objix_spix == 0) { + // get data page from object index header page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; + } else { + // get data page from object index page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + } + + if (objix_data_pix == (spiffs_page_ix)-1) { + // reached end of object, abort + break; + } + + map->map_buf[map_spix - map->start_spix] = objix_data_pix; + SPIFFS_DBG("map "_SPIPRIid":"_SPIPRIsp" ("_SPIPRIsp"--"_SPIPRIsp") objix.spix:"_SPIPRIsp" to pix "_SPIPRIpg"\n", + fd->obj_id, map_spix - map->start_spix, + map->start_spix, map->end_spix, + objix->p_hdr.span_ix, + objix_data_pix); + + map_spix++; + } +} + +typedef struct { + spiffs_fd *fd; + u32_t remaining_objix_pages_to_visit; + spiffs_span_ix map_objix_start_spix; + spiffs_span_ix map_objix_end_spix; +} spiffs_ix_map_populate_state; + +static s32_t spiffs_populate_ix_map_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + + // load header to check it + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); + + // check if hdr is ok, and if objix range overlap with ix map range + if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && + objix->p_hdr.span_ix >= state->map_objix_start_spix && + objix->p_hdr.span_ix <= state->map_objix_end_spix) { + // ok, load rest of object index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), + (u8_t *)objix + sizeof(spiffs_page_object_ix)); + SPIFFS_CHECK_RES(res); + + spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); + + state->remaining_objix_pages_to_visit--; + SPIFFS_DBG("map "_SPIPRIid" ("_SPIPRIsp"--"_SPIPRIsp") remaining objix pages "_SPIPRIi"\n", + state->fd->obj_id, + state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, + state->remaining_objix_pages_to_visit); + } + + if (res == SPIFFS_OK) { + res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; + } + return res; +} + +// populates index map, from vector entry start to vector entry end, inclusive +s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) { + s32_t res; + spiffs_ix_map *map = fd->ix_map; + spiffs_ix_map_populate_state state; + vec_entry_start = MIN((map->end_spix - map->start_spix + 1) - 1, (s32_t)vec_entry_start); + vec_entry_end = MAX((map->end_spix - map->start_spix + 1) - 1, (s32_t)vec_entry_end); + if (vec_entry_start > vec_entry_end) { + return SPIFFS_ERR_IX_MAP_BAD_RANGE; + } + state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); + state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); + state.remaining_objix_pages_to_visit = + state.map_objix_end_spix - state.map_objix_start_spix + 1; + state.fd = fd; + + res = spiffs_obj_lu_find_entry_visitor( + fs, + SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_VIS_CHECK_ID, + fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + spiffs_populate_ix_map_v, + 0, + &state, + 0, + 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + return res; +} + +#endif + + +#if !SPIFFS_READ_ONLY +// Allocates a free defined page with given obj_id +// Occupies object lookup entry and page +// data may be NULL; where only page header is stored, len and page_offs is ignored +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + int entry; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write page header + ph->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); + SPIFFS_CHECK_RES(res); + + // write page data + if (data) { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0,SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + SPIFFS_CHECK_RES(res); + } + + // finalize header if necessary + if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { + ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&ph->flags); + SPIFFS_CHECK_RES(res); + } + + // return written page + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. +// If page data is null, provided header is used for metainfo and page data is physically copied. +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix) { + s32_t res; + u8_t was_final = 0; + spiffs_page_header *p_hdr; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + if (dst_pix) *dst_pix = free_pix; + + p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; + if (page_data) { + // got page data + was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; + // write unfinalized page + p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; + p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data); + } else { + // copy page data + res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } + SPIFFS_CHECK_RES(res); + + // mark entry in destination object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + if (was_final) { + // mark finalized in destination page + p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fh, + SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr->flags); + SPIFFS_CHECK_RES(res); + } + // mark source deleted + res = spiffs_page_delete(fs, src_pix); + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Deletes a page and removes it from object lookup. +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix) { + s32_t res; + spiffs_page_header hdr; + hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); + // mark deleted entry in source object lookup + spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, + 0, + SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&d_obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_deleted++; + fs->stats_p_allocated--; + + // mark deleted in source page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&hdr.flags); + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Create an object index header page with empty index and undefined length +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + const u8_t name[], + const u8_t meta[], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + spiffs_page_object_ix_header oix_hdr; + int entry; + + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("create: found free page @ "_SPIPRIpg" bix:"_SPIPRIbl" entry:"_SPIPRIsp"\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write empty object index page + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); + oix_hdr.type = type; + oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page + strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + if (meta) { + memcpy(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + } else { + memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + + // update page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); + + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, + SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + + if (objix_hdr_pix) { + *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// update object index header with any combination of name/size/index +// new_objix_hdr_data may be null, if so the object index header page is loaded +// name may be null, if so name is not changed +// size may be null, if so size is not changed +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + const u8_t name[], + const u8_t meta[], + u32_t size, + spiffs_page_ix *new_pix) { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header *objix_hdr; + spiffs_page_ix new_objix_hdr_pix; + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + if (new_objix_hdr_data) { + // object index header page already given to us, no need to load it + objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; + } else { + // read object index header page + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + objix_hdr = (spiffs_page_object_ix_header *)fs->work; + } + + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); + + // change name + if (name) { + strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); + } +#if SPIFFS_OBJ_META_LEN + if (meta) { + memcpy(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + if (size) { + objix_hdr->size = size; + } + + // move and update page + res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); + + if (res == SPIFFS_OK) { + if (new_pix) { + *new_pix = new_objix_hdr_pix; + } + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, + obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, + int ev, + spiffs_obj_id obj_id_raw, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size) { +#if SPIFFS_IX_MAP == 0 + (void)objix; +#endif + // update index caches in all file descriptors + spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; +#if SPIFFS_TEMPORAL_FD_CACHE + if (cur_fd->score == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; +#else + if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; +#endif + if (spix == 0) { + if (ev != SPIFFS_EV_IX_DEL) { + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size); + cur_fd->objix_hdr_pix = new_pix; + if (new_size != 0) { + cur_fd->size = new_size; + } + } else { + cur_fd->file_nbr = 0; + cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; + } + } + if (cur_fd->cursor_objix_spix == spix) { + if (ev != SPIFFS_EV_IX_DEL) { + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix); + cur_fd->cursor_objix_pix = new_pix; + } else { + cur_fd->cursor_objix_pix = 0; + } + } + } + +#if SPIFFS_IX_MAP + + // update index maps + if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) { + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + // check fd opened, having ix map, match obj id + if (cur_fd->file_nbr == 0 || + cur_fd->ix_map == 0 || + (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; + SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", cur_fd->file_nbr, cur_fd->obj_id, spix); + spiffs_update_ix_map(fs, cur_fd, spix, objix); + } + } + +#endif + + // callback to user if object index header + if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_fileop_type op; + if (ev == SPIFFS_EV_IX_NEW) { + op = SPIFFS_CB_CREATED; + } else if (ev == SPIFFS_EV_IX_UPD || + ev == SPIFFS_EV_IX_MOV || + ev == SPIFFS_EV_IX_UPD_HDR) { + op = SPIFFS_CB_UPDATED; + } else if (ev == SPIFFS_EV_IX_DEL) { + op = SPIFFS_CB_DELETED; + } else { + SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi"\n", ev); + return; // bail out + } + fs->file_cb_f(fs, op, obj_id, new_pix); + } +} + +// Open object by id +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + s32_t res = SPIFFS_OK; + spiffs_page_ix pix; + + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + + return res; +} + +// Open object by page index +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + (void)mode; + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header oix_hdr; + spiffs_obj_id obj_id; + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + SPIFFS_CHECK_RES(res); + + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + + fd->fs = fs; + fd->objix_hdr_pix = pix; + fd->size = oix_hdr.size; + fd->offset = 0; + fd->cursor_objix_pix = pix; + fd->cursor_objix_spix = 0; + fd->obj_id = obj_id; + fd->flags = flags; + + SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); + + SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", fd->file_nbr, fd->obj_id); + + return res; +} + +#if !SPIFFS_READ_ONLY +// Append to object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + SPIFFS_DBG("append: "_SPIPRIi" bytes @ offs "_SPIPRIi" of size "_SPIPRIi"\n", len, offset, fd->size); + + if (offset > fd->size) { + SPIFFS_DBG("append: offset reversed to size\n"); + offset = fd->size; + } + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta + if (res != SPIFFS_OK) { + SPIFFS_DBG("append: gc check fail "_SPIPRIi"\n", res); + } + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_page; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_page; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index page, unless first pass + SPIFFS_DBG("append: "_SPIPRIid" store objix "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, prev_objix_spix, written); + if (prev_objix_spix == 0) { + // this is an update to object index header page + objix_hdr->size = offset+written; + if (offset == 0) { + // was an empty object, update same page (size was 0xffffffff) + res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + } else { + // was a nonempty object, update to new page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + new_objix_hdr_page, 0, written); + } + } else { + // this is an update to an object index page + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + // update length in object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new size I "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + offset+written, new_objix_hdr_page, 0, written); + } + fd->size = offset+written; + fd->offset = offset+written; + } + + // create or load new object index page + if (cur_objix_spix == 0) { + // load object index header page, must always exist + SPIFFS_DBG("append: "_SPIPRIid" load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs)); + // on subsequent passes, create a new object index page + if (written > 0 || cur_objix_spix > len_objix_spix) { + p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = cur_objix_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 1, &cur_objix_pix); + SPIFFS_CHECK_RES(res); + // quick "load" of new object index page + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header)); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + } else { + // on first pass, we load existing object index page + spiffs_page_ix pix; + SPIFFS_DBG("append: "_SPIPRIid" find objix span_ix:"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("append: "_SPIPRIid" found object index at page "_SPIPRIpg" [fd size "_SPIPRIi"]\n", fd->obj_id, pix, fd->size); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + fd->size = offset+written; + } + prev_objix_spix = cur_objix_spix; + } + + // write data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + if (page_offs == 0) { + // at beginning of a page, allocate and write a new page of data + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_page); + SPIFFS_DBG("append: "_SPIPRIid" store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id, + data_page, data_spix, page_offs, to_write, written); + } else { + // append to existing page, fill out free data in existing page + if (cur_objix_spix == 0) { + // get data page from object index header page + data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + res = spiffs_page_data_check(fs, fd, data_page, data_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + SPIFFS_DBG("append: "_SPIPRIid" store to existing data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id + , data_page, data_spix, page_offs, to_write, written); + } + + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, data_spix); + objix_hdr->size = offset+written; + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->size = offset+written; + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page, unless object header index page + SPIFFS_DBG("append: "_SPIPRIid" store objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + + // update size in object header index page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store new size II "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi", res "_SPIPRIi"\n", fd->obj_id + , offset+written, new_objix_hdr_page, 0, written, res2); + SPIFFS_CHECK_RES(res2); + } else { + // wrote within object index header page + if (offset == 0) { + // wrote to empty object - simply update size and write whole page + objix_hdr->size = offset+written; + SPIFFS_DBG("append: "_SPIPRIid" store fresh objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + } else { + // modifying object index header page, update size and make new copy + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } + } + + return res; +} // spiffs_object_append +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Modify object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_pix; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_pix; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index (header) page, unless first pass + if (prev_objix_spix == 0) { + // store previous object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res); + } else { + // store new version of previous object index page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + + // load next object index page + if (cur_objix_spix == 0) { + // load object index header page, must exist + SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + // load existing object index page on first pass + spiffs_page_ix pix; + SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp"\n", cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("modify: found object index at page "_SPIPRIpg"\n", pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + prev_objix_spix = cur_objix_spix; + } + + // write partial data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + spiffs_page_ix orig_data_pix; + if (cur_objix_spix == 0) { + // get data page from object index header page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { + // a full page, allocate and write a new page of data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); + SPIFFS_DBG("modify: store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", data_pix, data_spix, page_offs, to_write, written); + } else { + // write to existing page, allocate new and copy unmodified data + + res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &data_pix); + if (res != SPIFFS_OK) break; + + // copy unmodified data + if (page_offs > 0) { + // before modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), + page_offs); + if (res != SPIFFS_OK) break; + } + if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { + // after modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); + if (res != SPIFFS_OK) break; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg", dst:"_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + } + + // delete original data page + res = spiffs_page_delete(fs, orig_data_pix); + if (res != SPIFFS_OK) break; + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", data_pix, data_spix); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page + // move and update page + spiffs_page_ix new_objix_pix; + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, cur_objix_spix, written); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + + } else { + // wrote within object index header page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res2); + } + + return res; +} // spiffs_object_modify +#endif // !SPIFFS_READ_ONLY + +static s32_t spiffs_object_find_object_index_header_by_name_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_var_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { + return SPIFFS_OK; + } + } + + return SPIFFS_VIS_COUNTINUE; +} + +// Finds object index header page by name +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + const u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + 0, + 0, + spiffs_object_find_object_index_header_by_name_v, + name, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +#if !SPIFFS_READ_ONLY +// Truncates object to new size. If new size is null, object may be removed totally +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_size, + u8_t remove_full) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + + if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) { + // no op + return res; + } + + // need 2 pages if not removing: object index page + possibly chopped data page + if (remove_full == 0) { + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); + SPIFFS_CHECK_RES(res); + } + + spiffs_page_ix objix_pix = fd->objix_hdr_pix; + spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_ix data_pix; + spiffs_page_ix new_objix_hdr_pix; + + // before truncating, check if object is to be fully removed and mark this + if (remove_full && new_size == 0) { + u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + SPIFFS_CHECK_RES(res); + } + + // delete from end of object until desired len is reached + while (cur_size > new_size) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // put object index for current data span index in work buffer + if (prev_objix_spix != cur_objix_spix) { + if (prev_objix_spix != (spiffs_span_ix)-1) { + // remove previous object index page + SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg":"_SPIPRIsp"\n", objix_pix, prev_objix_spix); + + res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); + if (prev_objix_spix > 0) { + // Update object index header page, unless we totally want to remove the file. + // If fully removing, we're not keeping consistency as good as when storing the header between chunks, + // would we be aborted. But when removing full files, a crammed system may otherwise + // report ERR_FULL a la windows. We cannot have that. + // Hence, take the risk - if aborted, a file check would free the lost pages and mend things + // as the file is marked as fully deleted in the beginning. + if (remove_full == 0) { + SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg":"_SPIPRIsp" to size "_SPIPRIi"\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + } + } + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + + SPIFFS_DBG("truncate: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + } + + SPIFFS_DBG("truncate: got data pix "_SPIPRIpg"\n", data_pix); + + if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) { + // delete full data page + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { + SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi"\n", res); + break; + } + + if (res == SPIFFS_OK) { + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) { + SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi"\n", res); + break; + } + } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { + res = SPIFFS_OK; + } + + // update current size + if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { + cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); + } else { + cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); + } + fd->size = cur_size; + fd->offset = cur_size; + SPIFFS_DBG("truncate: delete data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", data_pix, data_spix, cur_size); + } else { + // delete last page, partially + spiffs_page_header p_hdr; + spiffs_page_ix new_data_pix; + u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_DBG("truncate: delete "_SPIPRIi" bytes from data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", bytes_to_remove, data_pix, data_spix, cur_size); + + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + // allocate new page and copy unmodified data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &new_data_pix); + if (res != SPIFFS_OK) break; + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); + if (res != SPIFFS_OK) break; + // delete original data page + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + cur_size = new_size; + fd->size = new_size; + fd->offset = cur_size; + break; + } + data_spix--; + } // while all data + + // update object indices + if (cur_objix_spix == 0) { + // update object index header page + if (cur_size == 0) { + if (remove_full) { + // remove object altogether + SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg"\n", objix_pix); + + res = spiffs_page_index_check(fs, fd, objix_pix, 0); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + } else { + // make uninitialized object + SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg"\n", objix_pix); + memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update object index header page + SPIFFS_DBG("truncate: update object index header page with indices and size\n"); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update both current object index page and object index header page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res); + + // move and update object index page + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, cur_objix_spix); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + // update object index header page with new size + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + + return res; +} // spiffs_object_truncate +#endif // !SPIFFS_READ_ONLY + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + spiffs_page_ix objix_pix; + spiffs_page_ix data_pix; + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_offset = offset; + spiffs_span_ix cur_objix_spix; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + while (cur_offset < offset + len) { +#if SPIFFS_IX_MAP + // check if we have a memory, index map and if so, if we're within index map's range + // and if so, if the entry is populated + if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix + && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) { + data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; + } else { +#endif + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) { + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + SPIFFS_DBG("read: find objix "_SPIPRIid":"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + objix_pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + } + SPIFFS_DBG("read: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); + + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } +#if SPIFFS_IX_MAP + } +#endif + // all remaining data + u32_t len_to_read = offset + len - cur_offset; + // remaining data in page + len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + // remaining data in file + len_to_read = MIN(len_to_read, fd->size); + SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix, + (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); + if (len_to_read <= 0) { + res = SPIFFS_ERR_END_OF_OBJECT; + break; + } + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + SPIFFS_CHECK_RES(res); + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), + len_to_read, + dst); + SPIFFS_CHECK_RES(res); + dst += len_to_read; + cur_offset += len_to_read; + fd->offset = cur_offset; + data_spix++; + } + + return res; +} + +#if !SPIFFS_READ_ONLY +typedef struct { + spiffs_obj_id min_obj_id; + spiffs_obj_id max_obj_id; + u32_t compaction; + const u8_t *conflicting_name; +} spiffs_free_obj_id_state; + +static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p) { + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { + spiffs_obj_id min_obj_id = *((spiffs_obj_id*)user_var_p); + const u8_t *conflicting_name = (const u8_t*)user_const_p; + + // if conflicting name parameter is given, also check if this name is found in object index hdrs + if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + int res; + spiffs_page_object_ix_header objix_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + } + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t bit_ix = (id-min_obj_id) & 7; + int byte_ix = (id-min_obj_id) >> 3; + if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + fs->work[byte_ix] |= (1<conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + if (id >= state->min_obj_id && id <= state->max_obj_id) { + u8_t *map = (u8_t *)fs->work; + int ix = (id - state->min_obj_id) / state->compaction; + //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + map[ix]++; + } + } + } + return SPIFFS_VIS_COUNTINUE; +} + +// Scans thru all object lookup for object index header pages. If total possible number of +// object ids cannot fit into a work buffer, these are grouped. When a group containing free +// object ids is found, the object lu is again scanned for object ids within group and bitmasked. +// Finally, the bitmask is searched for a free id +s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) { + s32_t res = SPIFFS_OK; + u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; + spiffs_free_obj_id_state state; + spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; + state.min_obj_id = 1; + state.max_obj_id = max_objects + 1; + if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG; + } + state.compaction = 0; + state.conflicting_name = conflicting_name; + while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { + if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) { + // possible to represent in bitmap + u32_t i, j; + SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid" max:"_SPIPRIid"\n", state.min_obj_id, state.max_obj_id); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, + conflicting_name, &state.min_obj_id, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + // traverse bitmask until found free obj_id + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { + u8_t mask = fs->work[i]; + if (mask == 0xff) { + continue; + } + for (j = 0; j < 8; j++) { + if ((mask & (1<work; + u8_t min_count = 0xff; + + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) { + if (map[i] < min_count) { + min_count = map[i]; + min_i = i; + if (min_count == 0) { + break; + } + } + } + + if (min_count == state.compaction) { + // there are no free objids! + SPIFFS_DBG("free_obj_id: compacted table is full\n"); + return SPIFFS_ERR_FULL; + } + + SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi" min_count:"_SPIPRIi" min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + + if (min_count == 0) { + // no id in this range, skip compacting and use directly + *obj_id = min_i * state.compaction + state.min_obj_id; + return SPIFFS_OK; + } else { + SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi" min:"_SPIPRIid" -> "_SPIPRIid"\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + state.min_obj_id += min_i * state.compaction; + state.max_obj_id = state.min_obj_id + state.compaction; + // decrease compaction + } + if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) { + // no need for compacting, use bitmap + continue; + } + } + // in a work memory of log_page_size bytes, we may fit in log_page_size ids + // todo what if compaction is > 255 - then we cannot fit it in a byte + state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); + SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", state.min_obj_id, state.max_obj_id, state.compaction); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + state.conflicting_name = 0; // searched for conflicting name once, no need to do it again + } + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_TEMPORAL_FD_CACHE +// djb2 hash +static u32_t spiffs_hash(spiffs *fs, const u8_t *name) { + (void)fs; + u32_t hash = 5381; + u8_t c; + int i = 0; + while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) { + hash = (hash * 33) ^ c; + } + return hash; +} +#endif + +s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) { +#if SPIFFS_TEMPORAL_FD_CACHE + u32_t i; + u16_t min_score = 0xffff; + u32_t cand_ix = (u32_t)-1; + u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + + if (name) { + // first, decrease score of all closed descriptors + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (cur_fd->score > 1) { // score == 0 indicates never used fd + cur_fd->score--; + } + } + } + } + + // find the free fd with least score + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (name && cur_fd->name_hash == name_hash) { + cand_ix = i; + break; + } + if (cur_fd->score < min_score) { + min_score = cur_fd->score; + cand_ix = i; + } + } + } + + if (cand_ix != (u32_t)-1) { + spiffs_fd *cur_fd = &fds[cand_ix]; + if (name) { + if (cur_fd->name_hash == name_hash && cur_fd->score > 0) { + // opened an fd with same name hash, assume same file + // set search point to saved obj index page and hope we have a correct match directly + // when start searching - if not, we will just keep searching until it is found + fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + // update score + if (cur_fd->score < 0xffff-SPIFFS_TEMPORAL_CACHE_HIT_SCORE) { + cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + } else { + cur_fd->score = 0xffff; + } + } else { + // no hash hit, restore this fd to initial state + cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + cur_fd->name_hash = name_hash; + } + } + cur_fd->file_nbr = cand_ix+1; + *fd = cur_fd; + return SPIFFS_OK; + } else { + return SPIFFS_ERR_OUT_OF_FILE_DESCS; + } +#else + (void)name; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + cur_fd->file_nbr = i+1; + *fd = cur_fd; + return SPIFFS_OK; + } + } + return SPIFFS_ERR_OUT_OF_FILE_DESCS; +#endif +} + +s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + spiffs_fd *fd = &fds[f-1]; + if (fd->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + fd->file_nbr = 0; +#if SPIFFS_IX_MAP + fd->ix_map = 0; +#endif + return SPIFFS_OK; +} + +s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + *fd = &fds[f-1]; + if ((*fd)->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + return SPIFFS_OK; +} + +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path) { + u32_t i; + u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); + u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->score > 0 && cur_fd->name_hash == old_hash) { + cur_fd->name_hash = new_hash; + } + } +} +#endif diff --git a/components/spiffs/spiffs_nucleus.h b/components/spiffs/spiffs_nucleus.h new file mode 100644 index 0000000..7d676ee --- /dev/null +++ b/components/spiffs/spiffs_nucleus.h @@ -0,0 +1,797 @@ +/* + * spiffs_nucleus.h + * + * Created on: Jun 15, 2013 + * Author: petera + */ + +/* SPIFFS layout + * + * spiffs is designed for following spi flash characteristics: + * - only big areas of data (blocks) can be erased + * - erasing resets all bits in a block to ones + * - writing pulls ones to zeroes + * - zeroes cannot be pulled to ones, without erase + * - wear leveling + * + * spiffs is also meant to be run on embedded, memory constraint devices. + * + * Entire area is divided in blocks. Entire area is also divided in pages. + * Each block contains same number of pages. A page cannot be erased, but a + * block can be erased. + * + * Entire area must be block_size * x + * page_size must be block_size / (2^y) where y > 2 + * + * ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes + * + * BLOCK 0 PAGE 0 object lookup 1 + * PAGE 1 object lookup 2 + * ... + * PAGE n-1 object lookup n + * PAGE n object data 1 + * PAGE n+1 object data 2 + * ... + * PAGE n+m-1 object data m + * + * BLOCK 1 PAGE n+m object lookup 1 + * PAGE n+m+1 object lookup 2 + * ... + * PAGE 2n+m-1 object lookup n + * PAGE 2n+m object data 1 + * PAGE 2n+m object data 2 + * ... + * PAGE 2n+2m-1 object data m + * ... + * + * n is number of object lookup pages, which is number of pages needed to index all pages + * in a block by object id + * : block_size / page_size * sizeof(obj_id) / page_size + * m is number data pages, which is number of pages in block minus number of lookup pages + * : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size + * thus, n+m is total number of pages in a block + * : block_size / page_size + * + * ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 + * + * Object lookup pages contain object id entries. Each entry represent the corresponding + * data page. + * Assuming a 16 bit object id, an object id being 0xffff represents a free page. + * An object id being 0x0000 represents a deleted page. + * + * ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. + * page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. + * page 2 : data : data for object id 0008 + * page 3 : data : data for object id 0001 + * page 4 : data : data for object id 0aaa + * ... + * + * + * Object data pages can be either object index pages or object content. + * All object data pages contains a data page header, containing object id and span index. + * The span index denotes the object page ordering amongst data pages with same object id. + * This applies to both object index pages (when index spans more than one page of entries), + * and object data pages. + * An object index page contains page entries pointing to object content page. The entry index + * in a object index page correlates to the span index in the actual object data page. + * The first object index page (span index 0) is called object index header page, and also + * contains object flags (directory/file), size, object name etc. + * + * ex: + * BLOCK 1 + * PAGE 256: objectl lookup page 1 + * [*123] [ 123] [ 123] [ 123] + * [ 123] [*123] [ 123] [ 123] + * [free] [free] [free] [free] ... + * PAGE 257: objectl lookup page 2 + * [free] [free] [free] [free] ... + * PAGE 258: object index page (header) + * obj.id:0123 span.ix:0000 flags:INDEX + * size:1600 name:ex.txt type:file + * [259] [260] [261] [262] + * PAGE 259: object data page + * obj.id:0123 span.ix:0000 flags:DATA + * PAGE 260: object data page + * obj.id:0123 span.ix:0001 flags:DATA + * PAGE 261: object data page + * obj.id:0123 span.ix:0002 flags:DATA + * PAGE 262: object data page + * obj.id:0123 span.ix:0003 flags:DATA + * PAGE 263: object index page + * obj.id:0123 span.ix:0001 flags:INDEX + * [264] [265] [fre] [fre] + * [fre] [fre] [fre] [fre] + * PAGE 264: object data page + * obj.id:0123 span.ix:0004 flags:DATA + * PAGE 265: object data page + * obj.id:0123 span.ix:0005 flags:DATA + * + */ +#ifndef SPIFFS_NUCLEUS_H_ +#define SPIFFS_NUCLEUS_H_ + +#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1) +#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1) +#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2) +#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3) +#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4) + +// visitor result, continue searching +#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20) +// visitor result, continue searching after reloading lu buffer +#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21) +// visitor result, stop searching +#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22) + +// updating an object index contents +#define SPIFFS_EV_IX_UPD (0) +// creating a new object index +#define SPIFFS_EV_IX_NEW (1) +// deleting an object index +#define SPIFFS_EV_IX_DEL (2) +// moving an object index without updating contents +#define SPIFFS_EV_IX_MOV (3) +// updating an object index header data only, not the table itself +#define SPIFFS_EV_IX_UPD_HDR (4) + +#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) + +#define SPIFFS_UNDEFINED_LEN (u32_t)(-1) + +#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) +#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) + +#if SPIFFS_USE_MAGIC +#if !SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_MAGIC(fs, bix) \ + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) +#else // SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_MAGIC(fs, bix) \ + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) +#endif // SPIFFS_USE_MAGIC_LENGTH +#endif // SPIFFS_USE_MAGIC + +#define SPIFFS_CONFIG_MAGIC (0x20090315) + +#if SPIFFS_SINGLETON == 0 +#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ + ((fs)->cfg.log_page_size) +#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ + ((fs)->cfg.log_block_size) +#define SPIFFS_CFG_PHYS_SZ(fs) \ + ((fs)->cfg.phys_size) +#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ + ((fs)->cfg.phys_erase_block) +#define SPIFFS_CFG_PHYS_ADDR(fs) \ + ((fs)->cfg.phys_addr) +#endif + +// total number of pages +#define SPIFFS_MAX_PAGES(fs) \ + ( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// total number of pages per block, including object lookup pages +#define SPIFFS_PAGES_PER_BLOCK(fs) \ + ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// number of object lookup pages per block +#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ + (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) +// checks if page index belongs to object lookup +#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ + (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// number of object lookup entries in all object lookup pages +#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ + (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// converts a block to physical address +#define SPIFFS_BLOCK_TO_PADDR(fs, block) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) +// converts a object lookup entry to page index +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ + ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) +// converts a object lookup entry to physical address of corresponding page +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ + (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a page to physical address +#define SPIFFS_PAGE_TO_PADDR(fs, page) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a physical address to page +#define SPIFFS_PADDR_TO_PAGE(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// gives index in page for a physical address +#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// returns containing block for given page +#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ + ( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) +// returns starting page for block +#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ + ( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) +// converts page to entry in object lookup page +#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ + ( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) +// returns data size in a data page +#define SPIFFS_DATA_PAGE_SIZE(fs) \ + ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) +// returns physical address for block's erase count, +// always in the physical last entry of the last object lookup page +#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) +// returns physical address for block's magic, +// always in the physical second last entry of the last object lookup page +#define SPIFFS_MAGIC_PADDR(fs, bix) \ + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) +// checks if there is any room for magic in the object luts +#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \ + ( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ + <= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) + +// define helpers object + +// entries in an object header page index +#define SPIFFS_OBJ_HDR_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) +// entries in an object page index +#define SPIFFS_OBJ_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) +// object index entry for given data span index +#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) +// object index span index number for given data span index or entry +#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) +// get data span index for object index span index +#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ + ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) + +#define SPIFFS_OP_T_OBJ_LU (0<<0) +#define SPIFFS_OP_T_OBJ_LU2 (1<<0) +#define SPIFFS_OP_T_OBJ_IX (2<<0) +#define SPIFFS_OP_T_OBJ_DA (3<<0) +#define SPIFFS_OP_C_DELE (0<<2) +#define SPIFFS_OP_C_UPDT (1<<2) +#define SPIFFS_OP_C_MOVS (2<<2) +#define SPIFFS_OP_C_MOVD (3<<2) +#define SPIFFS_OP_C_FLSH (4<<2) +#define SPIFFS_OP_C_READ (5<<2) +#define SPIFFS_OP_C_WRTHRU (6<<2) + +#define SPIFFS_OP_TYPE_MASK (3<<0) +#define SPIFFS_OP_COM_MASK (7<<2) + + +// if 0, this page is written to, else clean +#define SPIFFS_PH_FLAG_USED (1<<0) +// if 0, writing is finalized, else under modification +#define SPIFFS_PH_FLAG_FINAL (1<<1) +// if 0, this is an index page, else a data page +#define SPIFFS_PH_FLAG_INDEX (1<<2) +// if 0, page is deleted, else valid +#define SPIFFS_PH_FLAG_DELET (1<<7) +// if 0, this index header is being deleted +#define SPIFFS_PH_FLAG_IXDELE (1<<6) + + +#define SPIFFS_CHECK_MOUNT(fs) \ + ((fs)->mounted != 0) + +#define SPIFFS_CHECK_CFG(fs) \ + ((fs)->config_magic == SPIFFS_CONFIG_MAGIC) + +#define SPIFFS_CHECK_RES(res) \ + do { \ + if ((res) < SPIFFS_OK) return (res); \ + } while (0); + +#define SPIFFS_API_CHECK_MOUNT(fs) \ + if (!SPIFFS_CHECK_MOUNT((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ + return SPIFFS_ERR_NOT_MOUNTED; \ + } + +#define SPIFFS_API_CHECK_CFG(fs) \ + if (!SPIFFS_CHECK_CFG((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ + return SPIFFS_ERR_NOT_CONFIGURED; \ + } + +#define SPIFFS_API_CHECK_RES(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->err_code = (res); \ + return (res); \ + } + +#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->err_code = (res); \ + SPIFFS_UNLOCK(fs); \ + return (res); \ + } + +#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ + if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH; + //if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; + +#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \ + if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; + + +// check id, only visit matching objec ids +#define SPIFFS_VIS_CHECK_ID (1<<0) +// report argument object id to visitor - else object lookup id is reported +#define SPIFFS_VIS_CHECK_PH (1<<1) +// stop searching at end of all look up pages +#define SPIFFS_VIS_NO_WRAP (1<<2) + +#if SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) + +#else // SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_paddr), (_len)) + +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +#if SPIFFS_CACHE + +#define SPIFFS_CACHE_FLAG_DIRTY (1<<0) +#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1) +#define SPIFFS_CACHE_FLAG_OBJLU (1<<2) +#define SPIFFS_CACHE_FLAG_OBJIX (1<<3) +#define SPIFFS_CACHE_FLAG_DATA (1<<4) +#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) + +#define SPIFFS_CACHE_PAGE_SIZE(fs) \ + (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) + +#define spiffs_get_cache(fs) \ + ((spiffs_cache *)((fs)->cache)) + +#define spiffs_get_cache_page_hdr(fs, c, ix) \ + ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) + +#define spiffs_get_cache_page(fs, c, ix) \ + ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) + +// cache page struct +typedef struct { + // cache flags + u8_t flags; + // cache page index + u8_t ix; + // last access of this cache page + u32_t last_access; + union { + // type read cache + struct { + // read cache page index + spiffs_page_ix pix; + }; +#if SPIFFS_CACHE_WR + // type write cache + struct { + // write cache + spiffs_obj_id obj_id; + // offset in cache page + u32_t offset; + // size of cache page + u16_t size; + }; +#endif + }; +} spiffs_cache_page; + +// cache struct +typedef struct { + u8_t cpage_count; + u32_t last_access; + u32_t cpage_use_map; + u32_t cpage_use_mask; + u8_t *cpages; +} spiffs_cache; + +#endif + + +// spiffs nucleus file descriptor +typedef struct { + // the filesystem of this descriptor + spiffs *fs; + // number of file descriptor - if 0, the file descriptor is closed + spiffs_file file_nbr; + // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted + spiffs_obj_id obj_id; + // size of the file + u32_t size; + // cached object index header page index + spiffs_page_ix objix_hdr_pix; + // cached offset object index page index + spiffs_page_ix cursor_objix_pix; + // cached offset object index span index + spiffs_span_ix cursor_objix_spix; + // current absolute offset + u32_t offset; + // current file descriptor offset + u32_t fdoffset; + // fd flags + spiffs_flags flags; +#if SPIFFS_CACHE_WR + spiffs_cache_page *cache_page; +#endif +#if SPIFFS_TEMPORAL_FD_CACHE + // djb2 hash of filename + u32_t name_hash; + // hit score (score == 0 indicates never used fd) + u16_t score; +#endif +#if SPIFFS_IX_MAP + // spiffs index map, if 0 it means unmapped + spiffs_ix_map *ix_map; +#endif +} spiffs_fd; + + +// object structs + +// page header, part of each page except object lookup pages +// NB: this is always aligned when the data page is an object index, +// as in this case struct spiffs_page_object_ix is used +typedef struct __attribute(( packed )) { + // object id + spiffs_obj_id obj_id; + // object span index + spiffs_span_ix span_ix; + // flags + u8_t flags; +} spiffs_page_header; + +// object index header page header +typedef struct __attribute(( packed )) +#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES + __attribute(( aligned(sizeof(spiffs_page_ix)) )) +#endif +{ + // common page header + spiffs_page_header p_hdr; + // alignment + u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; + // size of object + u32_t size; + // type of object + spiffs_obj_type type; + // name of object + u8_t name[SPIFFS_OBJ_NAME_LEN]; +#if SPIFFS_OBJ_META_LEN + // metadata. not interpreted by SPIFFS in any way. + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +} spiffs_page_object_ix_header; + +// object index page header +typedef struct __attribute(( packed )) { + spiffs_page_header p_hdr; + u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; +} spiffs_page_object_ix; + +// callback func for object lookup visitor +typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p); + + +#if SPIFFS_CACHE +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src)) +#else +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (addr), (len), (src)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +// --------------- + +s32_t spiffs_phys_rd( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *dst); + +s32_t spiffs_phys_wr( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *src); + +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len); + +s32_t spiffs_phys_count_free_blocks( + spiffs *fs); + +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + const void *user_const_p, + void *user_var_p, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix); + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH +s32_t spiffs_probe( + spiffs_config *cfg); +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH + +// --------------- + +s32_t spiffs_obj_lu_scan( + spiffs *fs); + +s32_t spiffs_obj_lu_find_free_obj_id( + spiffs *fs, + spiffs_obj_id *obj_id, + const u8_t *conflicting_name); + +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix); + +s32_t spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix); + +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix); + +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix); + +// --------------- + +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + const u8_t name[], + const u8_t meta[], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix); + +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + const u8_t name[], + const u8_t meta[], + u32_t size, + spiffs_page_ix *new_pix); + +#if SPIFFS_IX_MAP + +s32_t spiffs_populate_ix_map( + spiffs *fs, + spiffs_fd *fd, + u32_t vec_entry_start, + u32_t vec_entry_end); + +#endif + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, + int ev, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size); + +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *f, + spiffs_flags flags, + spiffs_mode mode); + +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *f, + spiffs_flags flags, + spiffs_mode mode); + +s32_t spiffs_object_append( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_modify( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst); + +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_len, + u8_t remove_object); + +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + const u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len); + +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidate, + int *candidate_count, + char fs_crammed); + +s32_t spiffs_gc_clean( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_quick( + spiffs *fs, u16_t max_free_pages); + +// --------------- + +s32_t spiffs_fd_find_new( + spiffs *fs, + spiffs_fd **fd, + const char *name); + +s32_t spiffs_fd_return( + spiffs *fs, + spiffs_file f); + +s32_t spiffs_fd_get( + spiffs *fs, + spiffs_file f, + spiffs_fd **fd); + +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path); +#endif + +#if SPIFFS_CACHE +void spiffs_cache_init( + spiffs *fs); + +void spiffs_cache_drop_page( + spiffs *fs, + spiffs_page_ix pix); + +#if SPIFFS_CACHE_WR +spiffs_cache_page *spiffs_cache_page_allocate_by_fd( + spiffs *fs, + spiffs_fd *fd); + +void spiffs_cache_fd_release( + spiffs *fs, + spiffs_cache_page *cp); + +spiffs_cache_page *spiffs_cache_page_get_by_fd( + spiffs *fs, + spiffs_fd *fd); +#endif +#endif + +s32_t spiffs_lookup_consistency_check( + spiffs *fs, + u8_t check_all_objects); + +s32_t spiffs_page_consistency_check( + spiffs *fs); + +s32_t spiffs_object_index_consistency_check( + spiffs *fs); + +#endif /* SPIFFS_NUCLEUS_H_ */ diff --git a/components/spiffs/spiffs_vfs.c b/components/spiffs/spiffs_vfs.c new file mode 100644 index 0000000..0e52cf4 --- /dev/null +++ b/components/spiffs/spiffs_vfs.c @@ -0,0 +1,878 @@ +/* + * spiffs VFS operations + * + * Author: LoBo (loboris@gmail.com / https://github.com/loboris) + * + * Part of this code is copied from or inspired by LUA-RTOS_ESP32 project: + * + * https://github.com/whitecatboard/Lua-RTOS-ESP32 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + */ + + +#include + +#include +#include +#include +#include "esp_log.h" + +#include + +#include "esp_vfs.h" +#include "esp_attr.h" +#include + +#include +#include +#include +#include +#include "list.h" +#include +#include +#include "sdkconfig.h" + + +#ifdef PATH_MAX +#undef PATH_MAX +#endif +#define PATH_MAX MAXNAMLEN+8 + +#define SPIFFS_ERASE_SIZE 4096 + +int spiffs_is_registered = 0; +int spiffs_is_mounted = 0; + +QueueHandle_t spiffs_mutex = NULL; + +static int IRAM_ATTR vfs_spiffs_open(const char *path, int flags, int mode); +static ssize_t IRAM_ATTR vfs_spiffs_write(int fd, const void *data, size_t size); +static ssize_t IRAM_ATTR vfs_spiffs_read(int fd, void * dst, size_t size); +static int IRAM_ATTR vfs_spiffs_fstat(int fd, struct stat * st); +static int IRAM_ATTR vfs_spiffs_close(int fd); +static off_t IRAM_ATTR vfs_spiffs_lseek(int fd, off_t size, int mode); + +typedef struct { + DIR dir; + spiffs_DIR spiffs_dir; + char path[MAXNAMLEN + 1]; + struct dirent ent; + uint8_t read_mount; +} vfs_spiffs_dir_t; + +typedef struct { + spiffs_file spiffs_file; + char path[MAXNAMLEN + 1]; + uint8_t is_dir; +} vfs_spiffs_file_t; + +typedef struct { + time_t mtime; + time_t ctime; + time_t atime; + uint8_t spare[SPIFFS_OBJ_META_LEN - (sizeof(time_t)*3)]; +} spiffs_metadata_t; + +static spiffs fs; +static struct list files; + +static u8_t *my_spiffs_work_buf; +static u8_t *my_spiffs_fds; +static u8_t *my_spiffs_cache; + + +/* + * ######################################## + * file names/paths passed to the functions + * do not contain '/spiffs' prefix + * ######################################## + */ + +//---------------------------------------------------- +void spiffs_fs_stat(uint32_t *total, uint32_t *used) { + if (SPIFFS_info(&fs, total, used) != SPIFFS_OK) { + *total = 0; + *used = 0; + } +} + +/* + * Test if path corresponds to a directory. Return 0 if is not a directory, + * 1 if it's a directory. + * + */ +//----------------------------------- +static int is_dir(const char *path) { + spiffs_DIR d; + char npath[PATH_MAX + 1]; + int res = 0; + + struct spiffs_dirent e; + + // Add /. to path + strlcpy(npath, path, PATH_MAX); + if (strcmp(path,"/") != 0) { + strlcat(npath,"/.", PATH_MAX); + } else { + strlcat(npath,".", PATH_MAX); + } + + SPIFFS_opendir(&fs, "/", &d); + while (SPIFFS_readdir(&d, &e)) { + if (strncmp(npath, (const char *)e.name, strlen(npath)) == 0) { + res = 1; + break; + } + } + + SPIFFS_closedir(&d); + + return res; +} + +/* + * This function translate error codes from SPIFFS to errno error codes + * + */ +//------------------------------- +static int spiffs_result(int res) { + switch (res) { + case SPIFFS_OK: + case SPIFFS_ERR_END_OF_OBJECT: + return 0; + + case SPIFFS_ERR_NOT_FOUND: + case SPIFFS_ERR_CONFLICTING_NAME: + return ENOENT; + + case SPIFFS_ERR_NOT_WRITABLE: + case SPIFFS_ERR_NOT_READABLE: + return EACCES; + + case SPIFFS_ERR_FILE_EXISTS: + return EEXIST; + + default: + return res; + } +} + +//----------------------------------------------------------------------------------------------------- +static int IRAM_ATTR vfs_spiffs_getstat(spiffs_file fd, spiffs_stat *st, spiffs_metadata_t *metadata) { + int res = SPIFFS_fstat(&fs, fd, st); + if (res == SPIFFS_OK) { + // Get file's time information from metadata + memcpy(metadata, st->meta, sizeof(spiffs_metadata_t)); + } + return res; +} + +// ## path does not contain '/spiffs' prefix ! +//--------------------------------------------------------------------------- +static int IRAM_ATTR vfs_spiffs_open(const char *path, int flags, int mode) { + int fd, result = 0, exists = 0; + spiffs_stat stat; + spiffs_metadata_t meta; + + // Allocate new file + vfs_spiffs_file_t *file = calloc(1, sizeof(vfs_spiffs_file_t)); + if (!file) { + errno = ENOMEM; + return -1; + } + + // Add file to file list. List index is file descriptor. + int res = list_add(&files, file, &fd); + if (res) { + free(file); + errno = res; + return -1; + } + + // Check if file exists + if (SPIFFS_stat(&fs, path, &stat) == SPIFFS_OK) exists = 1; + + // Make a copy of path + strlcpy(file->path, path, MAXNAMLEN); + + // Open file + spiffs_flags spiffs_mode = 0; + + // Translate flags to SPIFFS flags + if (flags == O_RDONLY) + spiffs_mode |= SPIFFS_RDONLY; + + if (flags & O_WRONLY) + spiffs_mode |= SPIFFS_WRONLY; + + if (flags & O_RDWR) + spiffs_mode = SPIFFS_RDWR; + + if (flags & O_EXCL) + spiffs_mode |= SPIFFS_EXCL; + + if (flags & O_CREAT) + spiffs_mode |= SPIFFS_CREAT; + + if (flags & O_TRUNC) + spiffs_mode |= SPIFFS_TRUNC; + + if (is_dir(path)) { + char npath[PATH_MAX + 1]; + + // Add /. to path + strlcpy(npath, path, PATH_MAX); + if (strcmp(path,"/") != 0) { + strlcat(npath,"/.", PATH_MAX); + } else { + strlcat(npath,".", PATH_MAX); + } + + // Open SPIFFS file + file->spiffs_file = SPIFFS_open(&fs, npath, spiffs_mode, 0); + if (file->spiffs_file < 0) { + result = spiffs_result(fs.err_code); + } + + file->is_dir = 1; + } else { + // Open SPIFFS file + file->spiffs_file = SPIFFS_open(&fs, path, spiffs_mode, 0); + if (file->spiffs_file < 0) { + result = spiffs_result(fs.err_code); + } + } + + if (result != 0) { + list_remove(&files, fd, 1); + errno = result; + return -1; + } + + res = vfs_spiffs_getstat(file->spiffs_file, &stat, &meta); + if (res == SPIFFS_OK) { + // update file's time information + meta.atime = time(NULL); // Get the system time to access time + if (!exists) meta.ctime = meta.atime; + if (spiffs_mode != SPIFFS_RDONLY) meta.mtime = meta.atime; + SPIFFS_fupdate_meta(&fs, file->spiffs_file, &meta); + } + + return fd; +} + +//-------------------------------------------------------------------------------- +static ssize_t IRAM_ATTR vfs_spiffs_write(int fd, const void *data, size_t size) { + vfs_spiffs_file_t *file; + int res; + + res = list_get(&files, fd, (void **)&file); + if (res) { + errno = EBADF; + return -1; + } + + if (file->is_dir) { + errno = EBADF; + return -1; + } + + // Write SPIFFS file + res = SPIFFS_write(&fs, file->spiffs_file, (void *)data, size); + if (res >= 0) { + return res; + } else { + res = spiffs_result(fs.err_code); + if (res != 0) { + errno = res; + return -1; + } + } + + return -1; +} + +//------------------------------------------------------------------------- +static ssize_t IRAM_ATTR vfs_spiffs_read(int fd, void * dst, size_t size) { + vfs_spiffs_file_t *file; + int res; + + res = list_get(&files, fd, (void **)&file); + if (res) { + errno = EBADF; + return -1; + } + + if (file->is_dir) { + errno = EBADF; + return -1; + } + + // Read SPIFFS file + res = SPIFFS_read(&fs, file->spiffs_file, dst, size); + if (res >= 0) { + return res; + } else { + res = spiffs_result(fs.err_code); + if (res != 0) { + errno = res; + return -1; + } + + // EOF + return 0; + } + + return -1; +} + +//--------------------------------------------------------------- +static int IRAM_ATTR vfs_spiffs_fstat(int fd, struct stat * st) { + vfs_spiffs_file_t *file; + spiffs_stat stat; + int res; + spiffs_metadata_t meta; + + res = list_get(&files, fd, (void **)&file); + if (res) { + errno = EBADF; + return -1; + } + + // Set block size for this file system + st->st_blksize = CONFIG_SPIFFS_LOG_PAGE_SIZE; + + // Get file/directory statistics + res = vfs_spiffs_getstat(file->spiffs_file, &stat, &meta); + if (res == SPIFFS_OK) { + // Set file's time information from metadata + st->st_mtime = meta.mtime; + st->st_ctime = meta.ctime; + st->st_atime = meta.atime; + + st->st_size = stat.size; + + } else { + st->st_mtime = 0; + st->st_ctime = 0; + st->st_atime = 0; + st->st_size = 0; + errno = spiffs_result(fs.err_code); + //printf("SPIFFS_STAT: error %d\r\n", res); + return -1; + } + + // Test if it's a directory entry + if (file->is_dir) st->st_mode = S_IFDIR; + else st->st_mode = S_IFREG; + + return 0; +} + +//--------------------------------------------- +static int IRAM_ATTR vfs_spiffs_close(int fd) { + vfs_spiffs_file_t *file; + int res; + + res = list_get(&files, fd, (void **)&file); + if (res) { + errno = EBADF; + return -1; + } + + res = SPIFFS_close(&fs, file->spiffs_file); + if (res) { + res = spiffs_result(fs.err_code); + } + + if (res < 0) { + errno = res; + return -1; + } + + list_remove(&files, fd, 1); + + return 0; +} + +//--------------------------------------------------------------------- +static off_t IRAM_ATTR vfs_spiffs_lseek(int fd, off_t size, int mode) { + vfs_spiffs_file_t *file; + int res; + + res = list_get(&files, fd, (void **)&file); + if (res) { + errno = EBADF; + return -1; + } + + if (file->is_dir) { + errno = EBADF; + return -1; + } + + int whence = SPIFFS_SEEK_CUR; + + switch (mode) { + case SEEK_SET: whence = SPIFFS_SEEK_SET;break; + case SEEK_CUR: whence = SPIFFS_SEEK_CUR;break; + case SEEK_END: whence = SPIFFS_SEEK_END;break; + } + + res = SPIFFS_lseek(&fs, file->spiffs_file, size, whence); + if (res < 0) { + res = spiffs_result(fs.err_code); + errno = res; + return -1; + } + + return res; +} + +//------------------------------------------------------------------------- +static int IRAM_ATTR vfs_spiffs_stat(const char * path, struct stat * st) { + int fd; + int res; + fd = vfs_spiffs_open(path, 0, 0); + res = vfs_spiffs_fstat(fd, st); + vfs_spiffs_close(fd); + + return res; +} + +//-------------------------------------------------------- +static int IRAM_ATTR vfs_spiffs_unlink(const char *path) { + char npath[PATH_MAX + 1]; + + strlcpy(npath, path, PATH_MAX); + + if (is_dir(path)) { + // Check if directory is empty + int nument = 0; + sprintf(npath, "/spiffs"); + strlcat(npath, path, PATH_MAX); + + DIR *dir = opendir(npath); + if (dir) { + struct dirent *ent; + // Read directory entries + while ((ent = readdir(dir)) != NULL) { + nument++; + } + } + else { + errno = ENOTEMPTY; + return -1; + } + closedir(dir); + + if (nument > 0) { + // Directory not empty, cannot remove + errno = ENOTEMPTY; + return -1; + } + + strlcpy(npath, path, PATH_MAX); + // Add /. to path + if (strcmp(path,"/") != 0) { + strlcat(npath,"/.", PATH_MAX); + } + } + + // Open SPIFFS file + spiffs_file FP = SPIFFS_open(&fs, npath, SPIFFS_RDWR, 0); + if (FP < 0) { + errno = spiffs_result(fs.err_code); + return -1; + } + + // Remove SPIFSS file + if (SPIFFS_fremove(&fs, FP) < 0) { + errno = spiffs_result(fs.err_code); + SPIFFS_close(&fs, FP); + return -1; + } + + SPIFFS_close(&fs, FP); + + return 0; +} + +//------------------------------------------------------------------------ +static int IRAM_ATTR vfs_spiffs_rename(const char *src, const char *dst) { + if (SPIFFS_rename(&fs, src, dst) < 0) { + errno = spiffs_result(fs.err_code); + return -1; + } + + return 0; +} + +//------------------------------------------------ +static DIR* vfs_spiffs_opendir(const char* name) { + struct stat st; + + if (strcmp(name, "/") != 0) { + // Not on root + if (vfs_spiffs_stat(name, &st)) { + // Not found + errno = ENOENT; + return NULL; + } + if (!S_ISDIR(st.st_mode)) { + // Not a directory + errno = ENOTDIR; + return NULL; + } + } + + vfs_spiffs_dir_t *dir = calloc(1, sizeof(vfs_spiffs_dir_t)); + + if (!dir) { + errno = ENOMEM; + return NULL; + } + + if (!SPIFFS_opendir(&fs, name, &dir->spiffs_dir)) { + free(dir); + errno = spiffs_result(fs.err_code); + return NULL; + } + + strlcpy(dir->path, name, MAXNAMLEN); + + return (DIR *)dir; +} + +//--------------------------------------------------- +static struct dirent* vfs_spiffs_readdir(DIR* pdir) { + int res = 0, len = 0, entries = 0; + vfs_spiffs_dir_t* dir = (vfs_spiffs_dir_t*) pdir; + + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + struct dirent *ent = &dir->ent; + + char *fn; + + // Clear current dirent + memset(ent,0,sizeof(struct dirent)); + + // If this is the first call to readdir for pdir, and + // directory is the root path, return the mounted point if any + if (!dir->read_mount) { + if (strcmp(dir->path,"/") == 0) { + strlcpy(ent->d_name, "/spiffs", PATH_MAX); + ent->d_type = DT_DIR; + dir->read_mount = 1; + + return ent; + } + + dir->read_mount = 1; + } + + // Search for next entry + for(;;) { + // Read directory + pe = SPIFFS_readdir(&dir->spiffs_dir, pe); + if (!pe) { + res = spiffs_result(fs.err_code); + errno = res; + break; + } + + // Break condition + if (pe->name[0] == 0) break; + + // Get name and length + fn = (char *)pe->name; + len = strlen(fn); + + // Get entry type and size + ent->d_type = DT_REG; + + if (len >= 2) { + if (fn[len - 1] == '.') { + if (fn[len - 2] == '/') { + ent->d_type = DT_DIR; + + fn[len - 2] = '\0'; + + len = strlen(fn); + + // Skip root dir + if (len == 0) { + continue; + } + } + } + } + + // Skip entries not belonged to path + if (strncmp(fn, dir->path, strlen(dir->path)) != 0) { + continue; + } + + if (strlen(dir->path) > 1) { + if (*(fn + strlen(dir->path)) != '/') { + continue; + } + } + + // Skip root directory + fn = fn + strlen(dir->path); + len = strlen(fn); + if (len == 0) { + continue; + } + + // Skip initial / + if (len > 1) { + if (*fn == '/') { + fn = fn + 1; + len--; + } + } + + // Skip subdirectories + if (strchr(fn,'/')) { + continue; + } + + //ent->d_fsize = pe->size; + + strlcpy(ent->d_name, fn, MAXNAMLEN); + + entries++; + + break; + } + + if (entries > 0) { + return ent; + } else { + return NULL; + } +} + +//-------------------------------------------------- +static int IRAM_ATTR vfs_piffs_closedir(DIR* pdir) { + vfs_spiffs_dir_t* dir = (vfs_spiffs_dir_t*) pdir; + int res; + + if (!pdir) { + errno = EBADF; + return -1; + } + + if ((res = SPIFFS_closedir(&dir->spiffs_dir)) < 0) { + errno = spiffs_result(fs.err_code);; + return -1; + } + + free(dir); + + return 0; +} + +//-------------------------------------------------------------------- +static int IRAM_ATTR vfs_spiffs_mkdir(const char *path, mode_t mode) { + char npath[PATH_MAX + 1]; + int res; + + // Add /. to path + strlcpy(npath, path, PATH_MAX); + if ((strcmp(path,"/") != 0) && (strcmp(path,"/.") != 0)) { + strlcat(npath,"/.", PATH_MAX); + } + + spiffs_file fd = SPIFFS_open(&fs, npath, SPIFFS_CREAT, 0); + if (fd < 0) { + res = spiffs_result(fs.err_code); + errno = res; + return -1; + } + + if (SPIFFS_close(&fs, fd) < 0) { + res = spiffs_result(fs.err_code); + errno = res; + return -1; + } + + spiffs_metadata_t meta; + meta.atime = time(NULL); // Get the system time to access time + meta.ctime = meta.atime; + meta.mtime = meta.atime; + SPIFFS_update_meta(&fs, npath, &meta); + + return 0; +} + + +static const char tag[] = "[SPIFFS]"; + +//================== +int spiffs_mount() { + + if (!spiffs_is_registered) return 0; + if (spiffs_is_mounted) return 1; + + spiffs_config cfg; + int res = 0; + int retries = 0; + int err = 0; + + ESP_LOGI(tag, "Mounting SPIFFS files system"); + + cfg.phys_addr = CONFIG_SPIFFS_BASE_ADDR; + cfg.phys_size = CONFIG_SPIFFS_SIZE; + cfg.phys_erase_block = SPIFFS_ERASE_SIZE; + cfg.log_page_size = CONFIG_SPIFFS_LOG_PAGE_SIZE; + cfg.log_block_size = CONFIG_SPIFFS_LOG_BLOCK_SIZE; + + cfg.hal_read_f = (spiffs_read)low_spiffs_read; + cfg.hal_write_f = (spiffs_write)low_spiffs_write; + cfg.hal_erase_f = (spiffs_erase)low_spiffs_erase; + + my_spiffs_work_buf = malloc(cfg.log_page_size * 8); + if (!my_spiffs_work_buf) { + err = 1; + goto err_exit; + } + + int fds_len = sizeof(spiffs_fd) * SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + my_spiffs_fds = malloc(fds_len); + if (!my_spiffs_fds) { + free(my_spiffs_work_buf); + err = 2; + goto err_exit; + } + + int cache_len = cfg.log_page_size * SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + my_spiffs_cache = malloc(cache_len); + if (!my_spiffs_cache) { + free(my_spiffs_work_buf); + free(my_spiffs_fds); + err = 3; + goto err_exit; + } + + ESP_LOGI(tag, "Start address: 0x%x; Size %d KB", cfg.phys_addr, cfg.phys_size / 1024); + ESP_LOGI(tag, " Work buffer: %d B", cfg.log_page_size * 8); + ESP_LOGI(tag, " FDS buffer: %d B", sizeof(spiffs_fd) * SPIFFS_TEMPORAL_CACHE_HIT_SCORE); + ESP_LOGI(tag, " Cache size: %d B", cfg.log_page_size * SPIFFS_TEMPORAL_CACHE_HIT_SCORE); + while (retries < 2) { + res = SPIFFS_mount( + &fs, &cfg, my_spiffs_work_buf, my_spiffs_fds, + fds_len, my_spiffs_cache, cache_len, NULL + ); + + if (res < 0) { + if (fs.err_code == SPIFFS_ERR_NOT_A_FS) { + ESP_LOGW(tag, "No file system detected, formating..."); + SPIFFS_unmount(&fs); + res = SPIFFS_format(&fs); + if (res < 0) { + free(my_spiffs_work_buf); + free(my_spiffs_fds); + free(my_spiffs_cache); + ESP_LOGE(tag, "Format error"); + goto exit; + } + } + else { + free(my_spiffs_work_buf); + free(my_spiffs_fds); + free(my_spiffs_cache); + ESP_LOGE(tag, "Error mounting fs (%d)", res); + goto exit; + } + } + else break; + retries++; + } + + if (retries > 1) { + free(my_spiffs_work_buf); + free(my_spiffs_fds); + free(my_spiffs_cache); + ESP_LOGE(tag, "Can't mount"); + goto exit; + } + + list_init(&files, 0); + + ESP_LOGI(tag, "Mounted"); + + spiffs_is_mounted = 1; + return 1; + +err_exit: + ESP_LOGE(tag, "Error allocating fs structures (%d)", err); +exit: + esp_vfs_unregister("/spiffs"); + spiffs_is_registered = 0; + return 0; +} + +//========================== +void vfs_spiffs_register() { + + if (spiffs_is_registered) return; + + if (spiffs_mutex == NULL) { + spiffs_mutex = xSemaphoreCreateMutex(); + if (spiffs_mutex == NULL) { + ESP_LOGE(tag, "Error creating SPIFFS mutex"); + return; + } + } + + esp_vfs_t vfs = { + //.fd_offset = 0, // not available in latest esp-idf + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &vfs_spiffs_write, + .open = &vfs_spiffs_open, + .fstat = &vfs_spiffs_fstat, + .close = &vfs_spiffs_close, + .read = &vfs_spiffs_read, + .lseek = &vfs_spiffs_lseek, + .stat = &vfs_spiffs_stat, + .link = NULL, + .unlink = &vfs_spiffs_unlink, + .rename = &vfs_spiffs_rename, + .mkdir = &vfs_spiffs_mkdir, + .opendir = &vfs_spiffs_opendir, + .readdir = &vfs_spiffs_readdir, + .closedir = &vfs_piffs_closedir, + }; + + ESP_LOGI(tag, "Registering SPIFFS file system"); + esp_err_t res = esp_vfs_register(SPIFFS_BASE_PATH, &vfs, NULL); + if (res != ESP_OK) { + ESP_LOGE(tag, "Error, SPIFFS file system not registered"); + return; + } + spiffs_is_registered = 1; + + spiffs_mount(); +} + +//============================= +int spiffs_unmount(int unreg) { + + if (!spiffs_is_mounted) return 0; + + SPIFFS_unmount(&fs); + spiffs_is_mounted = 0; + + if (unreg) { + esp_vfs_unregister("/spiffs"); + spiffs_is_registered = 0; + } + return 1; +} diff --git a/components/spiffs/spiffs_vfs.h b/components/spiffs/spiffs_vfs.h new file mode 100644 index 0000000..1c627c3 --- /dev/null +++ b/components/spiffs/spiffs_vfs.h @@ -0,0 +1,23 @@ +/* + * spiffs VFS public function + * + * Author: LoBo (loboris@gmail.com / https://github.com/loboris) + * + * Part of this code is copied from or inspired by LUA-RTOS_ESP32 project: + * + * https://github.com/whitecatboard/Lua-RTOS-ESP32 + * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. + * Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) + * + */ + +#define SPIFFS_BASE_PATH "/spiffs" + + +int spiffs_is_registered; +int spiffs_is_mounted; + +void vfs_spiffs_register(); +int spiffs_mount(); +int spiffs_unmount(int unreg); +void spiffs_fs_stat(uint32_t *total, uint32_t *used); diff --git a/components/spiffs_image/Makefile.projbuild b/components/spiffs_image/Makefile.projbuild new file mode 100644 index 0000000..0ed7f7f --- /dev/null +++ b/components/spiffs_image/Makefile.projbuild @@ -0,0 +1,26 @@ +SPIFFS_IMAGE_COMPONENT_PATH := $(COMPONENT_PATH) +ifeq ($(OS),Windows_NT) + MKSPIFFS_BIN="mkspiffs.exe" +else + MKSPIFFS_BIN="mkspiffs" +endif + +.PHONY: flashfs +.PHONY: makefs +.PHONY: copyfs + +flashfs: $(SDKCONFIG_MAKEFILE) mkspiffs + @echo "Making spiffs image ..." + @echo "$(ESPTOOLPY_WRITE_FLASH)" + $(MKSPIFFS_COMPONENT_PATH)/../mkspiffs/src/$(MKSPIFFS_BIN) -c $(SPIFFS_IMAGE_COMPONENT_PATH)/image -b $(CONFIG_SPIFFS_LOG_BLOCK_SIZE) -p $(CONFIG_SPIFFS_LOG_PAGE_SIZE) -s $(CONFIG_SPIFFS_SIZE) $(BUILD_DIR_BASE)/spiffs_image.img + $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_SPIFFS_BASE_ADDR) $(BUILD_DIR_BASE)/spiffs_image.img + +makefs: $(SDKCONFIG_MAKEFILE) mkspiffs + @echo "Making spiffs image ..." + @echo "$(ESPTOOLPY_WRITE_FLASH)" + $(MKSPIFFS_COMPONENT_PATH)/../mkspiffs/src/$(MKSPIFFS_BIN) -c $(SPIFFS_IMAGE_COMPONENT_PATH)/image -b $(CONFIG_SPIFFS_LOG_BLOCK_SIZE) -p $(CONFIG_SPIFFS_LOG_PAGE_SIZE) -s $(CONFIG_SPIFFS_SIZE) $(BUILD_DIR_BASE)/spiffs_image.img + +copyfs: + @echo "Flashing spiffs image ..." + @echo "$(ESPTOOLPY_WRITE_FLASH)" + $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_SPIFFS_BASE_ADDR) $(SPIFFS_IMAGE_COMPONENT_PATH)/spiffs_image.img diff --git a/components/spiffs_image/component.mk b/components/spiffs_image/component.mk new file mode 100644 index 0000000..12f5e46 --- /dev/null +++ b/components/spiffs_image/component.mk @@ -0,0 +1,6 @@ +# +# Component Makefile +# + +COMPONENT_SRCDIRS := +COMPONENT_ADD_INCLUDEDIRS := diff --git a/components/spiffs_image/image/fonts/BigFont.fon b/components/spiffs_image/image/fonts/BigFont.fon new file mode 100644 index 0000000..c7b3c48 Binary files /dev/null and b/components/spiffs_image/image/fonts/BigFont.fon differ diff --git a/components/spiffs_image/image/fonts/DejaVuSans12.fon b/components/spiffs_image/image/fonts/DejaVuSans12.fon new file mode 100644 index 0000000..36e76bf Binary files /dev/null and b/components/spiffs_image/image/fonts/DejaVuSans12.fon differ diff --git a/components/spiffs_image/image/fonts/DejaVuSans18.fon b/components/spiffs_image/image/fonts/DejaVuSans18.fon new file mode 100644 index 0000000..0d609fb Binary files /dev/null and b/components/spiffs_image/image/fonts/DejaVuSans18.fon differ diff --git a/components/spiffs_image/image/fonts/DejaVuSans24.fon b/components/spiffs_image/image/fonts/DejaVuSans24.fon new file mode 100644 index 0000000..5220bfc Binary files /dev/null and b/components/spiffs_image/image/fonts/DejaVuSans24.fon differ diff --git a/components/spiffs_image/image/fonts/DotMatrix_M.fon b/components/spiffs_image/image/fonts/DotMatrix_M.fon new file mode 100644 index 0000000..a932f51 Binary files /dev/null and b/components/spiffs_image/image/fonts/DotMatrix_M.fon differ diff --git a/components/spiffs_image/image/fonts/Grotesk24x48.fon b/components/spiffs_image/image/fonts/Grotesk24x48.fon new file mode 100644 index 0000000..ee59c36 Binary files /dev/null and b/components/spiffs_image/image/fonts/Grotesk24x48.fon differ diff --git a/components/spiffs_image/image/fonts/SmallFont.fon b/components/spiffs_image/image/fonts/SmallFont.fon new file mode 100644 index 0000000..bf3f5f6 Binary files /dev/null and b/components/spiffs_image/image/fonts/SmallFont.fon differ diff --git a/components/spiffs_image/image/fonts/Ubuntu.fon b/components/spiffs_image/image/fonts/Ubuntu.fon new file mode 100644 index 0000000..fcc4b5b Binary files /dev/null and b/components/spiffs_image/image/fonts/Ubuntu.fon differ diff --git a/components/spiffs_image/image/fonts/arial_bold.fon b/components/spiffs_image/image/fonts/arial_bold.fon new file mode 100644 index 0000000..9cf4ee6 Binary files /dev/null and b/components/spiffs_image/image/fonts/arial_bold.fon differ diff --git a/components/spiffs_image/image/fonts/ocrfont.c b/components/spiffs_image/image/fonts/ocrfont.c new file mode 100644 index 0000000..1104238 --- /dev/null +++ b/components/spiffs_image/image/fonts/ocrfont.c @@ -0,0 +1,119 @@ +// OCR_A_Extended_M.c +// Font type : Full (95 characters) +// Font size : 16x24 pixels +// Memory usage : 4564 bytes + +#if defined(__AVR__) + #include + #define fontdatatype const uint8_t +#elif defined(__PIC32MX__) + #define PROGMEM + #define fontdatatype const unsigned char +#elif defined(__arm__) + #define PROGMEM + #define fontdatatype const unsigned char +#endif + +fontdatatype OCR_A_Extended_M[4564] PROGMEM={ +0x10,0x18,0x20,0x5F, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ! +0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x1E,0x78,0x1E,0x78,0x0E,0x70,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // " +0x00,0x00,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x1F,0xF8,0x1F,0xF8,0x06,0x60,0x06,0x60,0x06,0x60,0x1F,0xF8,0x1F,0xF8,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // # +0x00,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x1F,0xF8,0x1F,0xF8,0x18,0x00,0x18,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x18,0x00,0x18,0x1F,0xF8,0x1F,0xF8,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // $ +0x00,0x00,0x1C,0x00,0x1C,0x18,0x1C,0x18,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0xE0,0x00,0xC0,0x01,0x80,0x01,0x80,0x03,0x00,0x07,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x38,0x18,0x38,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // % +0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x0F,0xC0,0x18,0x60,0x18,0x60,0x18,0x60,0x18,0x60,0x0C,0xC0,0x07,0x80,0x07,0x00,0x0F,0x80,0x1D,0xC8,0x18,0xF8,0x18,0x70,0x18,0xF0,0x0F,0xD8,0x0F,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // & +0x00,0x00,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x80,0x03,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ' +0x00,0x00,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x80,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x00,0xC0,0x00,0x60,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ( +0x00,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x01,0x80,0x01,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x01,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ) +0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x19,0x88,0x19,0x98,0x0F,0xF0,0x07,0xE0,0x03,0xC0,0x03,0xC0,0x07,0xE0,0x0F,0xF0,0x1D,0xB8,0x11,0x98,0x01,0x80,0x01,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,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x1F,0xF8,0x1F,0xF8,0x01,0x80,0x01,0x80,0x01,0x80,0x01,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,0x07,0xE0,0x07,0xE0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // , +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // - +0x00,0x00,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,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // . +0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0x60,0x00,0xC0,0x00,0xC0,0x01,0x80,0x01,0x80,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // / + +0x00,0x00,0x0F,0xF0,0x1F,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0xF8,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0 +0x00,0x00,0x1F,0x80,0x1F,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x98,0x01,0x98,0x01,0x98,0x01,0x98,0x01,0x98,0x01,0x98,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 1 +0x00,0x00,0x1F,0xF0,0x1F,0xF8,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x0F,0xF8,0x1F,0xF0,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 2 +0x00,0x00,0x1F,0xF8,0x1F,0xFC,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x07,0xF8,0x07,0xF8,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x1F,0xFC,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 3 +0x00,0x00,0x18,0x00,0x18,0x00,0x18,0x30,0x18,0x30,0x18,0x30,0x18,0x30,0x18,0x30,0x18,0x30,0x18,0x30,0x18,0x30,0x1F,0xF8,0x1F,0xF8,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 4 +0x00,0x00,0x07,0xF8,0x07,0xF8,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x07,0xF0,0x07,0xF8,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x18,0x18,0x1F,0xF8,0x07,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 5 +0x00,0x00,0x1C,0x00,0x1C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1F,0xF8,0x1F,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 6 +0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x18,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 7 +0x00,0x00,0x07,0xE0,0x07,0xE0,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x0F,0xF0,0x1F,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0xF8,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 8 +0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0xF8,0x1F,0xF8,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x38,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 9 +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x03,0x80,0x03,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,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x07,0xE0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ; +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x78,0x00,0xE0,0x01,0xC0,0x07,0x00,0x0E,0x00,0x1C,0x00,0x0E,0x00,0x07,0x00,0x01,0xC0,0x00,0xE0,0x00,0x78,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // < +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // = +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x1E,0x00,0x07,0x00,0x03,0x80,0x00,0xE0,0x00,0x70,0x00,0x38,0x00,0x70,0x00,0xE0,0x03,0x80,0x07,0x00,0x1E,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // > +0x00,0x00,0x00,0x20,0x00,0x60,0x01,0xF0,0x03,0x98,0x07,0x18,0x0E,0x18,0x18,0x70,0x10,0xE0,0x01,0xC0,0x03,0x80,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ? + +0x00,0x00,0x07,0xE0,0x0F,0xF0,0x18,0x18,0x18,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x0F,0x98,0x0F,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x0F,0xF0,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // @ +0x00,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x0C,0x30,0x0F,0xF0,0x0F,0xF0,0x0C,0x30,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // A +0x00,0x00,0x1F,0xF0,0x1F,0xF8,0x18,0x1C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x1C,0x1F,0xF8,0x1F,0xF8,0x18,0x1C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x1C,0x1F,0xF8,0x1F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // B +0x00,0x00,0x01,0xFC,0x03,0xFC,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1C,0x00,0x0C,0x00,0x0E,0x00,0x06,0x00,0x07,0x00,0x03,0xFC,0x01,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // C +0x00,0x00,0x1F,0xC0,0x1F,0xE0,0x06,0x70,0x06,0x30,0x06,0x30,0x06,0x18,0x06,0x18,0x06,0x0C,0x06,0x0C,0x06,0x0C,0x06,0x0C,0x06,0x1C,0x06,0x18,0x06,0x38,0x06,0x30,0x06,0x70,0x1F,0xE0,0x1F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // D +0x00,0x00,0x1F,0xFC,0x1F,0xFC,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1F,0xC0,0x1F,0xC0,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1F,0xFC,0x1F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // E +0x00,0x00,0x1F,0xFC,0x1F,0xFC,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1F,0xF0,0x1F,0xF0,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // F +0x00,0x00,0x01,0xF8,0x03,0xF8,0x03,0x00,0x06,0x00,0x0E,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0xF8,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x0F,0xF8,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // G +0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0xF8,0x1F,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // H +0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // I +0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x07,0xF0,0x03,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // J +0x00,0x00,0x18,0x0C,0x18,0x18,0x18,0x30,0x18,0x60,0x18,0xC0,0x19,0x80,0x1B,0x00,0x1E,0x00,0x1C,0x00,0x1E,0x00,0x1F,0x00,0x1B,0x80,0x19,0xC0,0x18,0xE0,0x18,0x70,0x18,0x30,0x18,0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // K +0x00,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // L +0x00,0x00,0x1C,0x38,0x1C,0x38,0x1E,0x78,0x1E,0xF8,0x1B,0xD8,0x19,0x98,0x19,0x98,0x19,0x98,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // M +0x00,0x00,0x1C,0x18,0x1C,0x18,0x1E,0x18,0x1E,0x18,0x1E,0x18,0x1B,0x18,0x1B,0x18,0x1B,0x18,0x19,0x98,0x19,0x98,0x18,0xD8,0x18,0xD8,0x18,0xD8,0x18,0x78,0x18,0x78,0x18,0x78,0x18,0x38,0x18,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // N +0x00,0x00,0x03,0xC0,0x03,0xC0,0x06,0x60,0x06,0x60,0x0E,0x70,0x0C,0x30,0x0C,0x30,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x0C,0x30,0x0C,0x30,0x0E,0x70,0x06,0x60,0x06,0x60,0x03,0xC0,0x01,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // O + +0x00,0x00,0x1F,0xF0,0x1F,0xF0,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0xF0,0x1F,0xF0,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // P +0x00,0x00,0x00,0x70,0x00,0xF8,0x01,0xD8,0x03,0x98,0x07,0x18,0x0C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x19,0x98,0x19,0x98,0x19,0xF0,0x18,0xE0,0x19,0xC0,0x1B,0xE0,0x1F,0x78,0x0E,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Q +0x00,0x00,0x1F,0xF0,0x1F,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0xF8,0x1F,0xF0,0x19,0x80,0x19,0x80,0x18,0xC0,0x18,0xC0,0x18,0x60,0x18,0x60,0x18,0x30,0x18,0x30,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // R +0x00,0x00,0x0F,0xF0,0x1F,0xF8,0x18,0x18,0x18,0x18,0x0C,0x00,0x06,0x00,0x06,0x00,0x03,0x00,0x01,0x80,0x01,0x80,0x00,0xC0,0x00,0x60,0x00,0x60,0x00,0x30,0x18,0x18,0x18,0x18,0x1F,0xF8,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // S +0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x19,0x98,0x19,0x98,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // T +0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x0F,0xF0,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // U +0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x0C,0x30,0x0C,0x30,0x0E,0x70,0x06,0x60,0x06,0x60,0x07,0xE0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // V +0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x19,0x98,0x0F,0xF0,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // W +0x00,0x00,0x18,0x18,0x18,0x18,0x0C,0x30,0x0C,0x30,0x06,0x60,0x06,0x60,0x03,0xC0,0x03,0xC0,0x01,0x80,0x01,0x80,0x03,0xC0,0x03,0xC0,0x06,0x60,0x06,0x60,0x0C,0x30,0x0C,0x30,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // X +0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x0C,0x30,0x0E,0x60,0x07,0xE0,0x03,0xC0,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Y +0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0x60,0x00,0xC0,0x00,0xC0,0x01,0x80,0x01,0x80,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Z +0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // [ +0x00,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x07,0x00,0x03,0x00,0x01,0x80,0x01,0x80,0x00,0xC0,0x00,0xC0,0x00,0x60,0x00,0x70,0x00,0x30,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // +0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x00,0xD8,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ] +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x03,0xC0,0x03,0xC0,0x07,0xE0,0x07,0xE0,0x0E,0x70,0x0E,0x70,0x1C,0x30,0x1C,0x38,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ^ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00, // _ + +0x00,0x00,0x06,0x00,0x07,0xC0,0x03,0xE0,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ` +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF0,0x07,0xF0,0x00,0x18,0x00,0x18,0x00,0x18,0x0F,0xF8,0x0F,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x38,0x1F,0xF8,0x0F,0xD8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // a +0x00,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1B,0xE0,0x1F,0xF0,0x1E,0x38,0x1C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1C,0x18,0x1E,0x38,0x1F,0xF0,0x1B,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // b +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC,0x07,0xFC,0x0E,0x00,0x1C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1C,0x00,0x0E,0x00,0x07,0xFC,0x03,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // c +0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x07,0xD8,0x0F,0xF8,0x1C,0x78,0x18,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x38,0x1C,0x78,0x0F,0xF8,0x07,0xD8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // d +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x0F,0xF0,0x1C,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0xF8,0x1F,0xF8,0x18,0x00,0x18,0x00,0x1C,0x00,0x0F,0xF8,0x07,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // e +0x00,0x00,0x00,0xFC,0x01,0xFC,0x03,0x80,0x03,0x00,0x03,0x00,0x0F,0xF0,0x0F,0xF0,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // f +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xD8,0x0F,0xF8,0x1C,0x78,0x18,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x38,0x1C,0x78,0x0F,0xF8,0x07,0xD8,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x38,0x0F,0xF0,0x0F,0xE0, // g +0x00,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x19,0xF0,0x1B,0xF0,0x1F,0x18,0x1C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // h +0x03,0x80,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x0F,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x0F,0xF0,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // i +0x00,0x38,0x00,0x38,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xF8,0x03,0xF8,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x0C,0x18,0x0C,0x18,0x07,0xF0,0x03,0xE0, // j +0x00,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x30,0x18,0x60,0x18,0xC0,0x19,0x80,0x1B,0x80,0x1F,0x00,0x1E,0x00,0x1F,0x00,0x19,0x80,0x18,0xC0,0x18,0x60,0x18,0x30,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // k +0x00,0x00,0x0F,0x80,0x0F,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x0F,0xF0,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // l +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x78,0x3F,0xFC,0x39,0xCC,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // m +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0xF0,0x1B,0xF0,0x1F,0x18,0x1C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // n +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x0F,0xF0,0x1C,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1C,0x38,0x0F,0xF0,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // o + +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0xC0,0x1F,0xE0,0x1E,0x70,0x1C,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1C,0x38,0x1E,0x70,0x1F,0xE0,0x1B,0xC0,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00, // p +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xD8,0x0F,0xF8,0x1C,0x78,0x18,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x38,0x1C,0x78,0x0F,0xF8,0x07,0xD8,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18, // q +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0xF0,0x1B,0xF8,0x1F,0x0C,0x1C,0x0C,0x18,0x0C,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // r +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x1F,0xF8,0x18,0x18,0x18,0x00,0x1E,0x00,0x07,0x80,0x01,0xF0,0x00,0x70,0x00,0x18,0x00,0x18,0x18,0x18,0x1F,0xF0,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // s +0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x1F,0xF8,0x1F,0xF8,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x0C,0x06,0x1C,0x03,0xF8,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // t +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x38,0x18,0xF8,0x0F,0xD8,0x07,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // u +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x18,0x30,0x18,0x30,0x18,0x30,0x18,0x38,0x38,0x18,0x30,0x18,0x30,0x0C,0x60,0x0C,0x60,0x06,0xC0,0x06,0xC0,0x03,0x80,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // v +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x31,0x8C,0x33,0xCC,0x1F,0xF8,0x1E,0x78,0x1C,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // w +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x1C,0x38,0x0C,0x30,0x06,0x60,0x03,0xC0,0x03,0xC0,0x01,0x80,0x03,0xC0,0x06,0xE0,0x0E,0x70,0x0C,0x30,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // x +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x18,0x30,0x18,0x30,0x18,0x30,0x18,0x38,0x38,0x18,0x30,0x18,0x30,0x0C,0x60,0x0C,0x60,0x06,0xC0,0x07,0xC0,0x07,0x80,0x01,0x80,0x03,0x80,0x03,0x00,0x03,0x00,0x3E,0x00,0x3E,0x00, // y +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x38,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x1C,0x00,0x1F,0xF8,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // z +0x00,0x00,0x00,0xF8,0x01,0xF8,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x1F,0x00,0x1F,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0xF8,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // { +0x00,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // | +0x00,0x00,0x1F,0x00,0x1F,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0xF8,0x00,0xF8,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x1F,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // } +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x8C,0x1F,0xFC,0x18,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ~ +}; diff --git a/components/spiffs_image/image/fonts/swiss721_outline.fon b/components/spiffs_image/image/fonts/swiss721_outline.fon new file mode 100644 index 0000000..2ece05e Binary files /dev/null and b/components/spiffs_image/image/fonts/swiss721_outline.fon differ diff --git a/components/spiffs_image/image/images/Flintstones.jpg b/components/spiffs_image/image/images/Flintstones.jpg new file mode 100644 index 0000000..b0c2514 Binary files /dev/null and b/components/spiffs_image/image/images/Flintstones.jpg differ diff --git a/components/spiffs_image/image/images/animal-silhouettes.jpg b/components/spiffs_image/image/images/animal-silhouettes.jpg new file mode 100644 index 0000000..99cece5 Binary files /dev/null and b/components/spiffs_image/image/images/animal-silhouettes.jpg differ diff --git a/components/spiffs_image/image/images/evolution-of-human.jpg b/components/spiffs_image/image/images/evolution-of-human.jpg new file mode 100644 index 0000000..154128e Binary files /dev/null and b/components/spiffs_image/image/images/evolution-of-human.jpg differ diff --git a/components/spiffs_image/image/images/girl_silhouettes.jpg b/components/spiffs_image/image/images/girl_silhouettes.jpg new file mode 100644 index 0000000..daa05b6 Binary files /dev/null and b/components/spiffs_image/image/images/girl_silhouettes.jpg differ diff --git a/components/spiffs_image/image/images/people_silhouettes.jpg b/components/spiffs_image/image/images/people_silhouettes.jpg new file mode 100644 index 0000000..f1fc55b Binary files /dev/null and b/components/spiffs_image/image/images/people_silhouettes.jpg differ diff --git a/components/spiffs_image/image/images/silhouettes-dancing.jpg b/components/spiffs_image/image/images/silhouettes-dancing.jpg new file mode 100644 index 0000000..5c662ba Binary files /dev/null and b/components/spiffs_image/image/images/silhouettes-dancing.jpg differ diff --git a/components/spiffs_image/image/spiffs.info b/components/spiffs_image/image/spiffs.info new file mode 100644 index 0000000..68946a2 --- /dev/null +++ b/components/spiffs_image/image/spiffs.info @@ -0,0 +1,11 @@ +INTRODUCTION + +Spiffs is a file system intended for SPI NOR flash devices on embedded targets. +Spiffs is designed with following characteristics in mind: + + * Small (embedded) targets, sparse RAM without heap + * Only big areas of data (blocks) can be erased + * An erase will reset all bits in block to ones + * Writing pulls one to zeroes + * Zeroes can only be pulled to ones by erase + * Wear leveling diff --git a/components/spiffs_image/spiffs_image.img b/components/spiffs_image/spiffs_image.img new file mode 100644 index 0000000..0787bee Binary files /dev/null and b/components/spiffs_image/spiffs_image.img differ diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild new file mode 100644 index 0000000..bce409f --- /dev/null +++ b/main/Kconfig.projbuild @@ -0,0 +1,49 @@ +menu "ePaper Display DEMO Configuration" + +config SPIFFS_BASE_ADDR + hex "SPIFFS Base address" + range 0x100000 0x1FFE000 + default 0x180000 + help + Starting address of the SPIFFS area in ESP32 Flash + Write the address in hex format, 0x180000 + +config SPIFFS_SIZE + int "SPIFFS Size in bytes" + range 262144 2097152 + default 1048576 + +config SPIFFS_LOG_BLOCK_SIZE + int "SPIFFS Logical block size" + range 4098 65536 + default 8192 + +config SPIFFS_LOG_PAGE_SIZE + int "SPIFFS Logical page size" + range 256 2048 + default 256 + help + Set it to the phisycal page size og the used SPI Flash chip. + +config EXAMPLE_USE_WIFI + bool "Use wifi in TFT Demo" + default n + help + If WiFi is used ntp server will be used to provide the exact time + and file timestamps will be correct. +config WIFI_SSID + string "WiFi SSID" + depends on EXAMPLE_USE_WIFI + default "myssid" + help + SSID (network name) for the demo to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + depends on EXAMPLE_USE_WIFI + default "mypassword" + help + WiFi password (WPA or WPA2) for the demo to use. + + +endmenu diff --git a/main/component.mk b/main/component.mk new file mode 100644 index 0000000..0f76ee5 --- /dev/null +++ b/main/component.mk @@ -0,0 +1,7 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/main/ePaper.c b/main/ePaper.c new file mode 100644 index 0000000..b6d7cea --- /dev/null +++ b/main/ePaper.c @@ -0,0 +1,689 @@ +/* ePaper demo + * + * Author: LoBo (loboris@gmail.com, loboris.github) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "driver/gpio.h" +#include "esp_system.h" +#include "esp_heap_alloc_caps.h" +#include "spiffs_vfs.h" +#include "esp_log.h" + +#ifdef CONFIG_EXAMPLE_USE_WIFI + +#include "esp_wifi.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_event_loop.h" +#include "freertos/event_groups.h" +#include "esp_attr.h" +#include +#include +#include "lwip/err.h" +#include "apps/sntp/sntp.h" +#include "nvs_flash.h" + +#endif + + +#include "spi_master_lobo.h" +#include "img1.h" +#include "img2.h" +#include "img3.h" +#include "img_hacking.c" +#include "EPD.h" +//#include "EPDspi.h" + +#define DELAYTIME 1500 + + + +static struct tm* tm_info; +static char tmp_buff[128]; +static time_t time_now, time_last = 0; +static const char *file_fonts[3] = {"/spiffs/fonts/DotMatrix_M.fon", "/spiffs/fonts/Ubuntu.fon", "/spiffs/fonts/Grotesk24x48.fon"}; +static const char tag[] = "[Eink Demo]"; + +//================================================================================== +#ifdef CONFIG_EXAMPLE_USE_WIFI + + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = 0x00000001; + +//------------------------------------------------------------ +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +//------------------------------- +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_WIFI_SSID, + .password = CONFIG_WIFI_PASSWORD, + }, + }; + ESP_LOGI(tag, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +//------------------------------- +static void initialize_sntp(void) +{ + ESP_LOGI(tag, "Initializing SNTP"); + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, "pool.ntp.org"); + sntp_init(); +} + +//-------------------------- +static int obtain_time(void) +{ + int res = 1; + initialise_wifi(); + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); + + initialize_sntp(); + + // wait for time to be set + int retry = 0; + const int retry_count = 20; + + time(&time_now); + tm_info = localtime(&time_now); + + while(tm_info->tm_year < (2016 - 1900) && ++retry < retry_count) { + //ESP_LOGI(tag, "Waiting for system time to be set... (%d/%d)", retry, retry_count); + vTaskDelay(500 / portTICK_RATE_MS); + time(&time_now); + tm_info = localtime(&time_now); + } + if (tm_info->tm_year < (2016 - 1900)) { + ESP_LOGI(tag, "System time NOT set."); + res = 0; + } + else { + ESP_LOGI(tag, "System time is set."); + } + + ESP_ERROR_CHECK( esp_wifi_stop() ); + return res; +} + +#endif //CONFIG_EXAMPLE_USE_WIFI +//================================================================================== + + +//============= +void app_main() +{ + + // ======== PREPARE DISPLAY INITIALIZATION ========= + + esp_err_t ret; + + disp_buffer = pvPortMallocCaps(EPD_DISPLAY_WIDTH * (EPD_DISPLAY_HEIGHT/8), MALLOC_CAP_DMA); + assert(disp_buffer); + drawBuff = disp_buffer; + + gs_disp_buffer = pvPortMallocCaps(EPD_DISPLAY_WIDTH * EPD_DISPLAY_HEIGHT, MALLOC_CAP_DMA); + assert(gs_disp_buffer); + gs_drawBuff = gs_disp_buffer; + + // ==== CONFIGURE SPI DEVICES(s) ==================================================================================== + + gpio_set_direction(DC_Pin, GPIO_MODE_OUTPUT); + gpio_set_level(DC_Pin, 1); + gpio_set_direction(RST_Pin, GPIO_MODE_OUTPUT); + gpio_set_level(RST_Pin, 0); + gpio_set_direction(BUSY_Pin, GPIO_MODE_INPUT); + gpio_set_pull_mode(BUSY_Pin, GPIO_PULLUP_ONLY); + +#if POWER_Pin + gpio_set_direction(POWER_Pin, GPIO_MODE_OUTPUT); + gpio_set_level(POWER_Pin, 1); +#endif + + spi_lobo_bus_config_t buscfg={ + .miso_io_num = -1, // set SPI MISO pin + .mosi_io_num = MOSI_Pin, // set SPI MOSI pin + .sclk_io_num = SCK_Pin, // set SPI CLK pin + .quadwp_io_num=-1, + .quadhd_io_num=-1, + .max_transfer_sz = 5*1024, // max transfer size is 4736 bytes + }; + spi_lobo_device_interface_config_t devcfg={ + .clock_speed_hz=40000000, // SPI clock is 40 MHz + .mode=0, // SPI mode 0 + .spics_io_num=-1, // we will use external CS pin + .spics_ext_io_num = CS_Pin, // external CS pin + .flags=SPI_DEVICE_HALFDUPLEX, // ALWAYS SET to HALF DUPLEX MODE for display spi !! + }; + + // ==================================================================================================================== + + + vTaskDelay(500 / portTICK_RATE_MS); + printf("\r\n=================================\r\n"); + printf("ePaper display DEMO, LoBo 06/2017\r\n"); + printf("=================================\r\n\r\n"); + + // ================================================================== + // ==== Initialize the SPI bus and attach the EPD to the SPI bus ==== + + ret=spi_lobo_bus_add_device(SPI_BUS, &buscfg, &devcfg, &disp_spi); + assert(ret==ESP_OK); + printf("SPI: display device added to spi bus\r\n"); + + // ==== Test select/deselect ==== + ret = spi_lobo_device_select(disp_spi, 1); + assert(ret==ESP_OK); + ret = spi_lobo_device_deselect(disp_spi); + assert(ret==ESP_OK); + + printf("SPI: attached display device, speed=%u\r\n", spi_lobo_get_speed(disp_spi)); + printf("SPI: bus uses native pins: %s\r\n", spi_lobo_uses_native_pins(disp_spi) ? "true" : "false"); + + printf("\r\n-------------------\r\n"); + printf("ePaper demo started\r\n"); + printf("-------------------\r\n"); + + + EPD_DisplayClearFull(); + +#ifdef CONFIG_EXAMPLE_USE_WIFI + + ESP_ERROR_CHECK( nvs_flash_init() ); + + EPD_DisplayClearPart(); + EPD_fillScreen(_bg); + EPD_setFont(DEFAULT_FONT, NULL); + sprintf(tmp_buff, "Waiting for NTP time..."); + EPD_print(tmp_buff, CENTER, CENTER); + EPD_drawRect(10,10,274,108, EPD_BLACK); + EPD_drawRect(12,12,270,104, EPD_BLACK); + EPD_UpdateScreen(); + + // ===== Set time zone ====== + setenv("TZ", "CET-1CEST", 0); + tzset(); + // ========================== + + time(&time_now); + tm_info = localtime(&time_now); + + // Is time set? If not, tm_year will be (1970 - 1900). + if (tm_info->tm_year < (2016 - 1900)) { + ESP_LOGI(tag, "Time is not set yet. Connecting to WiFi and getting time over NTP."); + if (obtain_time()) { + } + else { + } + time(&time_now); + } +#endif + + // ==== Initialize the file system ==== + printf("\r\n\n"); + vfs_spiffs_register(); + if (spiffs_is_mounted) { + ESP_LOGI(tag, "File system mounted."); + } + else { + ESP_LOGE(tag, "Error mounting file system."); + } + + //========= + // Run demo + //========= + + /* + EPD_DisplayClearFull(); + EPD_DisplayClearPart(); + EPD_fillScreen(_bg); + //EPD_DisplaySetPart(0x00); + //EPD_DisplaySetPart(0xFF); + + + uint8_t LUTTest1[31] = {0x32, 0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t LUTTest2[31] = {0x32, 0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + _gs = 0; + _fg = 1; + _bg = 0; + int n = 0; + while (1) { + //EPD_DisplayClearFull(); + EPD_fillRect(14, 14, 100, 100, ((n&1) ? 0 : 1)); + EPD_fillRect(_width/2+14, 14, 100, 100, ((n&1) ? 1 : 0)); + //LUT_part = LUTTest1; + EPD_DisplayPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, disp_buffer); + //EPD_wait(2000); + //LUT_part = LUTTest2; + //EPD_DisplayFull(disp_buffer); + printf("Updated\r\n"); + EPD_wait(4000); + n++; + + + n = 0; + printf("\r\n==== FULL UPDATE TEST ====\r\n\n"); + EPD_DisplayClearFull(); + while (n < 2) { + EPD_fillScreen(_bg); + printf("Black\r\n"); + EPD_fillRect(0,0,_width/2,_height-1, EPD_BLACK); + + EPD_fillRect(20,20,_width/2-40,_height-1-40, EPD_WHITE); + EPD_DisplayFull(disp_buffer); + EPD_wait(4000); + + printf("White\r\n"); + EPD_fillRect(0,0,_width/2,_height-1, EPD_WHITE); + EPD_DisplayFull(disp_buffer); + EPD_wait(2000); + n++; + } + + printf("\r\n==== PARTIAL UPDATE TEST ====\r\n\n"); + EPD_DisplayClearFull(); + n = 0; + while (n < 2) { + EPD_fillScreen(_bg); + printf("Black\r\n"); + EPD_fillRect(0,0,_width/2,_height-1, EPD_BLACK); + + EPD_fillRect(20,20,_width/2-40,_height-1-40, EPD_WHITE); + EPD_DisplayPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, disp_buffer); + EPD_wait(4000); + + printf("White\r\n"); + EPD_fillRect(0,0,_width/2,_height-1, EPD_WHITE); + EPD_DisplayPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, disp_buffer); + EPD_wait(2000); + n++; + } + + printf("\r\n==== PARTIAL UPDATE TEST - gray scale ====\r\n\n"); + EPD_DisplayClearFull(); + n = 0; + while (n < 3) { + EPD_fillScreen(_bg); + LUT_part = LUT_gs; + for (uint8_t sh=1; sh<16; sh++) { + LUT_gs[21] = sh; + printf("Black (%d)\r\n", LUT_gs[21]); + EPD_fillRect((sh-1)*19,0,19,_height, EPD_BLACK); + EPD_DisplayPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, disp_buffer); + } + EPD_wait(4000); + + //LUT_part = LUTDefault_part; + printf("White\r\n"); + //EPD_DisplayPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, disp_buffer); + //EPD_DisplaySetPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, 0xFF); + LUT_gs[21] = 15; + LUT_gs[1] = 0x28; + EPD_fillRect(190,0,76,_height, EPD_WHITE); + EPD_DisplayPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, disp_buffer); + EPD_wait(2000); + + EPD_fillRect(0,0,_width,_height, EPD_WHITE); + EPD_DisplayPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, disp_buffer); + LUT_gs[1] = 0x18; + EPD_wait(2000); + n++; + } + LUT_part = LUTDefault_part; + } +*/ + + + printf("==== START ====\r\n\n"); + + _gs = 1; + uint32_t tstart; + int pass = 0, ftype = 9; + while (1) { + ftype++; + if (ftype > 10) { + ftype = 1; + for (int t=40; t>0; t--) { + printf("Wait %d seconds ... \r", t); + fflush(stdout); + EPD_wait(100); + } + printf(" \r"); + fflush(stdout); + _gs ^= 1; + } + printf("\r\n-- Test %d\r\n", ftype); + EPD_DisplayClearPart(); + + //EPD_Cls(0); + EPD_fillScreen(_bg); + _fg = 15; + _bg = 0; + + EPD_drawRect(1,1,294,126, EPD_BLACK); + + int y = 4; + tstart = clock(); + if (ftype == 1) { + for (int f=0; f<4; f++) { + if (f == 0) _fg = 15; + else if (f == 1) _fg = 9; + else if (f == 2) _fg = 5; + else if (f == 2) _fg = 3; + EPD_setFont(f, NULL); + if (f == 3) { + EPD_print("Welcome to ", 4, y); + font_rotate = 90; + EPD_print("ESP32", EPD_getStringWidth("Welcome to ")+EPD_getfontheight()+4, y); + font_rotate = 0; + } + else if (f == 1) { + EPD_print("HR chars: \xA6\xA8\xB4\xB5\xB0", 4, y); + } + else { + EPD_print("Welcome to ESP32", 4, y); + } + y += EPD_getfontheight() + 2; + } + font_rotate = 45; + EPD_print("ESP32", LASTX+8, LASTY); + font_rotate = 0; + _fg = 15; + + EPD_setFont(DEFAULT_FONT, NULL); + sprintf(tmp_buff, "Pass: %d", pass+1); + EPD_print(tmp_buff, 4, 128-EPD_getfontheight()-2); + EPD_UpdateScreen(); + } + else if (ftype == 2) { + orientation = LANDSCAPE_180; + for (int f=4; ftm_sec != _sec) { + _sec = tm_info->tm_sec; + sprintf(tmp_buff, "%02d:%02d:%02d", tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec); + _fg = 15; // fill = 15 + set_7seg_font_atrib(10, 2, 0, 15); // outline = 15 + EPD_print(tmp_buff, CENTER, y); + _fg = 15; + if (tm_info->tm_mday != _day) { + sprintf(tmp_buff, "%02d.%02d.%04d", tm_info->tm_mday, tm_info->tm_mon + 1, tm_info->tm_year+1900); + _fg = 7; // fill = 7 + set_7seg_font_atrib(8, 2, 1, 15); // outline = 15 + EPD_print(tmp_buff, CENTER, y2); + _fg = 15; + } + EPD_UpdateScreen(); + } + EPD_wait(100); + } + tstart = clock(); + _fg = 15; + EPD_setFont(DEFAULT_FONT, NULL); + font_rotate = 90; + sprintf(tmp_buff, "%02d:%02d:%02d %02d/%02d", tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec, tm_info->tm_mday, tm_info->tm_mon + 1); + EPD_print(tmp_buff, 20, 4); + font_rotate = 0; + sprintf(tmp_buff, "Pass: %d", pass+1); + EPD_print(tmp_buff, 4, 128-EPD_getfontheight()-2); + EPD_UpdateScreen(); + } + else if (ftype == 5) { + uint8_t old_gs = _gs; + _gs = 1; + EPD_drawRect(4,4,20,20, 15); + + EPD_fillRect(27,5,18,18, 1); + EPD_drawRect(26,4,20,20, 15); + + EPD_drawCircle(66,16,10, 15); + + EPD_fillCircle(92,16,10, 2); + EPD_drawCircle(92,16,11, 15); + + EPD_fillRect(185,4,80,80, 3); + EPD_drawRect(185,4,80,80, 15); + + EPD_fillCircle(225,44,35, 0); + EPD_drawCircle(225,44,35, 15); + EPD_fillCircle(225,44,35, 5); + + EPD_fillCircle(225,44,20, 0); + EPD_drawCircle(225,44,20, 15); + + orientation = LANDSCAPE_180; + EPD_drawRect(4,4,20,20, 15); + + EPD_fillRect(27,5,18,18, 1); + EPD_drawRect(26,4,20,20, 15); + + EPD_drawCircle(66,16,10, 15); + + EPD_fillCircle(92,16,10, 2); + EPD_drawCircle(92,16,11, 15); + + EPD_fillRect(185,4,80,80, 3); + EPD_drawRect(185,4,80,80, 15); + + EPD_fillCircle(225,44,35, 0); + EPD_drawCircle(225,44,35, 15); + EPD_fillCircle(225,44,35, 5); + + EPD_fillCircle(225,44,20, 0); + EPD_drawCircle(225,44,20, 15); + orientation = LANDSCAPE_0; + + EPD_setFont(DEFAULT_FONT, NULL); + font_rotate = 90; + sprintf(tmp_buff, "Pass: %d", pass+1); + EPD_print("Gray scale demo", _width/2+EPD_getfontheight()+2, 4); + EPD_print(tmp_buff, _width/2, 4); + font_rotate = 0; + + EPD_UpdateScreen(); + _gs = old_gs; + } + else if (ftype == 6) { + uint8_t old_gs = _gs; + _gs = 0; + memcpy(disp_buffer, (unsigned char *)gImage_img1, sizeof(gImage_img1)); + + EPD_setFont(DEFAULT_FONT, NULL); + sprintf(tmp_buff, "Pass: %d", pass+1); + EPD_print(tmp_buff, 4, 128-EPD_getfontheight()-2); + + EPD_UpdateScreen(); + _gs = old_gs; + } + else if (ftype == 7) { + uint8_t old_gs = _gs; + _gs = 0; + memcpy(disp_buffer, (unsigned char *)gImage_img3, sizeof(gImage_img3)); + + EPD_setFont(DEFAULT_FONT, NULL); + _fg = 0; + _bg = 1; + sprintf(tmp_buff, "Pass: %d", pass+1); + EPD_print(tmp_buff, 4, 128-EPD_getfontheight()-2); + + EPD_UpdateScreen(); + _fg = 15; + _bg = 0; + _gs = old_gs; + } + else if (ftype == 8) { + uint8_t old_gs = _gs; + _gs = 1; + int i, x, y; + uint8_t last_lvl = 0; + for (i=0; i<16; i++) { + for (x = 0; x < EPD_DISPLAY_WIDTH; x++) { + for (y = 0; y < EPD_DISPLAY_HEIGHT; y++) { + uint8_t pix = img_hacking[(x * EPD_DISPLAY_HEIGHT) + (EPD_DISPLAY_HEIGHT-y-1)]; + if ((pix > last_lvl) && (pix <= lvl_buf[i])) { + gs_disp_buffer[(y * EPD_DISPLAY_WIDTH) + x] = i; + gs_used_shades |= (1 << i); + } + } + } + last_lvl = lvl_buf[i]; + } + + EPD_setFont(DEFAULT_FONT, NULL); + sprintf(tmp_buff, "Pass: %d (Gray scale image)", pass+1); + EPD_print(tmp_buff, 4, 128-EPD_getfontheight()-2); + + EPD_UpdateScreen(); + _gs = old_gs; + } + else if (ftype == 9) { + uint8_t old_gs = _gs; + _gs = 0; + memcpy(disp_buffer, gImage_img2, sizeof(gImage_img2)); + + EPD_setFont(DEFAULT_FONT, NULL); + sprintf(tmp_buff, "Pass: %d", pass+1); + EPD_print(tmp_buff, 4, 4); + + EPD_UpdateScreen(); + _gs = old_gs; + } + else if (ftype == 10) { + if (spiffs_is_mounted) { + // ** Show scaled (1/8, 1/4, 1/2 size) JPG images + uint8_t old_gs = _gs; + _gs = 1; + EPD_Cls(); + EPD_jpg_image(CENTER, CENTER, 0, SPIFFS_BASE_PATH"/images/evolution-of-human.jpg", NULL, 0); + EPD_UpdateScreen(); + EPD_wait(5000); + + EPD_Cls(); + EPD_jpg_image(CENTER, CENTER, 0, SPIFFS_BASE_PATH"/images/people_silhouettes.jpg", NULL, 0); + EPD_UpdateScreen(); + EPD_wait(5000); + + EPD_Cls(); + EPD_jpg_image(CENTER, CENTER, 0, SPIFFS_BASE_PATH"/images/silhouettes-dancing.jpg", NULL, 0); + EPD_UpdateScreen(); + EPD_wait(5000); + + EPD_Cls(); + EPD_jpg_image(CENTER, CENTER, 0, SPIFFS_BASE_PATH"/images/girl_silhouettes.jpg", NULL, 0); + EPD_UpdateScreen(); + EPD_wait(5000); + + EPD_Cls(); + EPD_jpg_image(CENTER, CENTER, 0, SPIFFS_BASE_PATH"/images/animal-silhouettes.jpg", NULL, 0); + EPD_UpdateScreen(); + EPD_wait(5000); + + EPD_Cls(); + EPD_jpg_image(CENTER, CENTER, 0, SPIFFS_BASE_PATH"/images/Flintstones.jpg", NULL, 0); + EPD_UpdateScreen(); + EPD_wait(5000); + + _gs = old_gs; + } + } + + //EPD_DisplayPart(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1, disp_buffer); + tstart = clock() - tstart; + pass++; + printf("-- Type: %d Pass: %d Time: %u ms\r\n", ftype, pass, tstart); + + EPD_PowerOff(); + EPD_wait(8000); + } + +} diff --git a/main/ePaper.h b/main/ePaper.h new file mode 100644 index 0000000..e266961 --- /dev/null +++ b/main/ePaper.h @@ -0,0 +1,73 @@ +/** + ****************************************************************************** + * File Name : main.h + * Description : This file contains the common defines of the application + ****************************************************************************** + * + * COPYRIGHT(c) 2017 STMicroelectronics + * + * 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. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __EPAPER_H +#define __EPAPER_H + /* Includes ------------------------------------------------------------------*/ + +/* USER CODE BEGIN Includes */ +#include "spi_master_lobo.h" + +/* USER CODE END Includes */ + +/* Private define ------------------------------------------------------------*/ + +#define SCK_Pin 18 +#define MOSI_Pin 23 +#define MISO_Pin 19 +#define DC_Pin 17//26 +#define BUSY_Pin 4//32 +#define RST_Pin 16//27 +#define CS_Pin 5 + +spi_lobo_device_handle_t disp_spi; + +/* USER CODE BEGIN Private defines */ +//cs +//dc +//rst +//busy + + +/* USER CODE END Private defines */ + +/** + * @} + */ + +/** + * @} +*/ + +#endif /* __EPAPER_H */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/main/img1.h b/main/img1.h new file mode 100644 index 0000000..c597015 --- /dev/null +++ b/main/img1.h @@ -0,0 +1,298 @@ +const unsigned char gImage_img1[4736] = { /* 0X01,0X01,0X28,0X01,0X80,0X00, */ +0XCC,0XCE,0XEC,0XBA,0XCC,0XB3,0XCD,0X10,0X01,0X15,0XEC,0X50,0X01,0X31,0X34,0XA2, +0X33,0X31,0X17,0X45,0X33,0X4C,0X72,0X4C,0X00,0X4A,0X10,0X00,0X00,0X4E,0X41,0X18, +0XCC,0XCE,0XE8,0XBA,0XCC,0XB3,0XAC,0X30,0X01,0X15,0XE0,0X00,0X01,0X10,0XBC,0XC7, +0X33,0X35,0X37,0XED,0X33,0X4C,0XD2,0X84,0X00,0X43,0X10,0X00,0X00,0XEA,0X00,0X18, +0XCD,0XCA,0XCC,0X16,0XCC,0XB3,0X3D,0X52,0X01,0X3C,0XC8,0X00,0X00,0X0D,0XDF,0X20, +0X32,0X35,0X73,0XE9,0X33,0X4D,0XD4,0X28,0X00,0X43,0X20,0X00,0X00,0XA2,0X20,0XDA, +0XCD,0XEE,0X8E,0XB6,0XCC,0XB2,0X6A,0X80,0X01,0X15,0XC8,0X00,0X00,0X58,0X1B,0X01, +0X33,0X11,0X71,0X59,0X33,0X5D,0XB4,0X38,0X00,0X4A,0XA0,0X00,0X00,0X87,0XE4,0XAC, +0XEC,0XEE,0XDE,0XAE,0XCC,0XA3,0X5A,0XC4,0X01,0X35,0X50,0X00,0X00,0X30,0X13,0X42, +0X13,0X33,0X23,0XF1,0X37,0X5C,0XEC,0X28,0X00,0X83,0XA0,0X00,0X00,0XCE,0XCC,0X3C, +0XED,0XCC,0XDC,0X0E,0XC8,0XA3,0X32,0X90,0X00,0X5D,0X40,0X00,0X01,0X21,0X23,0XD3, +0X3A,0X7B,0X6B,0XF1,0X37,0X5D,0XDC,0X68,0X01,0X22,0XA8,0X00,0X00,0X5E,0XCA,0X0C, +0XC5,0X86,0X95,0X5E,0XC9,0XA2,0XB2,0X90,0X00,0X8F,0X44,0X00,0X00,0X20,0X30,0XD3, +0X3B,0X79,0XEE,0XA1,0X36,0X5D,0X5D,0X28,0X22,0X31,0XB8,0X00,0X00,0X1F,0XCF,0X28, +0XCC,0XD6,0X31,0X7E,0XE9,0XA3,0XE0,0XD0,0X00,0X8E,0X44,0X00,0X00,0XA0,0X20,0X97, +0X77,0X2D,0XDF,0X89,0X3E,0X5C,0X3E,0X2C,0X02,0X23,0XB0,0X00,0X00,0X1D,0X2F,0X28, +0X89,0XD2,0XA4,0XF6,0XC3,0XA3,0XD0,0XD0,0X04,0XDE,0XC0,0X00,0X00,0XE2,0XA0,0XC7, +0X7E,0XBF,0X5B,0X29,0X3C,0XDD,0X6E,0X28,0X01,0X03,0X28,0X00,0X00,0X1D,0X0D,0X34, +0XC3,0X40,0XEE,0XDE,0XE3,0X22,0XB0,0XD0,0X04,0X6D,0XD2,0X00,0X00,0XE2,0XF2,0X53, +0X3C,0XFF,0X31,0X63,0X1E,0XDF,0XEF,0X28,0X01,0X16,0XE8,0X00,0X00,0X1D,0X09,0X0C, +0XD7,0X2A,0XCF,0XBC,0XF1,0X20,0X30,0XD0,0X04,0X4B,0X3A,0X00,0X00,0XC2,0XB6,0XA2, +0X69,0XD5,0X74,0XD3,0X8E,0XDF,0XDA,0X28,0X01,0X35,0XCC,0X20,0X00,0X35,0X49,0X59, +0XBE,0XAE,0X9B,0X6E,0XF3,0X22,0XED,0XD0,0X04,0X8E,0XF4,0X00,0X00,0XCA,0XB6,0XA6, +0X45,0X73,0XE5,0XBB,0X4D,0XDD,0X32,0X2C,0X01,0X73,0X3A,0X00,0X00,0X35,0X49,0X49, +0XFB,0X9C,0X3E,0XCD,0XF2,0X26,0XED,0XD0,0X04,0X0D,0XD4,0X00,0X00,0XC2,0XB4,0X35, +0X2C,0XE3,0XD3,0X37,0X2D,0XDB,0XB0,0X20,0X02,0XB7,0X7B,0X00,0X00,0X3D,0X4A,0XC0, +0XD7,0X3E,0XAC,0XDD,0XD6,0X2C,0XDF,0XD0,0X05,0X4D,0XDC,0X80,0X00,0XC4,0XB4,0X3E, +0XB9,0XC9,0X7B,0X62,0X6B,0XD3,0X60,0X20,0X00,0X37,0X77,0X00,0X00,0X33,0X4B,0XC1, +0X4E,0XB7,0X8D,0XBD,0X95,0X2D,0XBD,0XD0,0X04,0X8D,0XBC,0XB0,0X00,0XCC,0XB4,0X34, +0XF5,0X6C,0XF6,0XD2,0X6A,0XD6,0XD2,0X20,0X03,0X76,0XF3,0X40,0X00,0X32,0X4A,0XC3, +0X2B,0X93,0X5B,0X6D,0XB5,0X3B,0X6D,0XD0,0X04,0X0F,0XFC,0X90,0X00,0XCB,0XB5,0X28, +0XDC,0XFE,0XED,0XBA,0XCE,0XCD,0XB2,0X20,0X01,0XB3,0X35,0X40,0X00,0X2C,0X42,0XD7, +0X67,0X4B,0X36,0XE5,0X73,0X36,0XDD,0XD0,0X04,0X4D,0XFE,0X00,0X10,0X93,0X9D,0X28, +0XBA,0XB5,0XDB,0X3E,0X8C,0XDB,0X72,0X20,0X03,0X3E,0XF1,0X40,0X0B,0X6C,0X62,0XD3, +0X4D,0XDB,0X6D,0XD3,0XF7,0X2D,0XCD,0XC0,0X04,0XC7,0XFC,0X00,0X00,0X93,0X94,0X0D, +0XF6,0X6C,0XB6,0XED,0X29,0XFB,0X32,0X30,0X01,0X3B,0XF0,0X10,0X03,0X6C,0X4B,0X60, +0X2B,0XB7,0XDB,0X32,0XDE,0X0C,0XED,0XC0,0X04,0X8F,0XEC,0X00,0X00,0X91,0XB0,0X9E, +0XDD,0X4D,0X6D,0XDF,0XE5,0XF7,0XB2,0X10,0X02,0X75,0XF0,0X00,0X02,0X6E,0X4F,0X61, +0X62,0XF2,0XB6,0XE1,0X3E,0X2A,0XDD,0XC0,0X05,0X0F,0XFC,0X03,0X04,0X91,0X30,0X9C, +0XBD,0X5F,0XDB,0X3E,0XD3,0XFF,0X62,0X2C,0X02,0XFB,0XF2,0X04,0X43,0X6E,0X0F,0X63, +0X57,0XB5,0X6D,0XC3,0XEC,0X33,0X9D,0XC0,0X08,0X0F,0XFC,0X01,0X00,0X91,0XC0,0X9C, +0XEC,0XCA,0XB6,0XFD,0X3B,0XCC,0XE2,0X10,0X03,0XBB,0XF3,0X04,0X02,0X6E,0X3F,0X63, +0X37,0X7F,0XDB,0X23,0XD4,0X7B,0XAD,0XC0,0X08,0X4F,0XEC,0XC2,0X05,0X11,0XC0,0X9C, +0XDB,0XB5,0X6D,0XDD,0XFF,0XAE,0XF6,0X10,0X02,0XBF,0XFB,0X28,0X10,0XCE,0X4B,0X63, +0X6C,0XCA,0XB6,0XE3,0XD2,0XFB,0X51,0XC0,0X0D,0X4F,0XE4,0XD2,0X00,0X31,0X34,0X9C, +0XB7,0X7F,0XDB,0X3D,0XBD,0X2D,0XFE,0X20,0X02,0XB7,0XFB,0X28,0X10,0X4C,0XD3,0X65, +0XDB,0XB5,0X6F,0XF2,0XD6,0XF7,0X51,0X80,0X09,0X5F,0XED,0XD3,0X00,0XB3,0X04,0X12, +0X6C,0XCA,0XB4,0XCF,0X6B,0XBD,0XEE,0X20,0X04,0XAF,0XF2,0X2C,0X00,0X4C,0X2B,0XED, +0XB7,0X7F,0XFB,0X3B,0XBC,0XD6,0XB1,0XD0,0X13,0X5F,0XED,0XC1,0X00,0XB3,0XD5,0X12, +0XDB,0XB5,0X4F,0XE4,0XD3,0X6B,0XEE,0X04,0X04,0XBF,0XF2,0X24,0X00,0X0C,0X21,0X6C, +0X6C,0XDA,0XB4,0X9B,0XAD,0XBD,0X51,0XC2,0X11,0X5F,0XED,0X92,0X00,0XF3,0X4C,0X92, +0XB7,0X6F,0XFF,0XE6,0XFA,0XF7,0XEE,0X30,0X0E,0XBF,0XF2,0X48,0X00,0X0C,0XB3,0X6D, +0XDB,0XBB,0X56,0XBB,0X2F,0X5D,0X31,0X82,0X10,0X5F,0XCD,0X22,0X80,0XF3,0X4C,0X12, +0X6C,0XED,0XAB,0XC6,0XF0,0XAB,0XEE,0X40,0X07,0XBF,0XF2,0X8D,0X00,0X0C,0XB3,0XA9, +0XB7,0XB6,0XFE,0XBB,0XBF,0XFE,0XB5,0X2B,0X10,0X4F,0XC9,0X72,0X21,0X73,0X4C,0X56, +0XDA,0XDB,0XAB,0XEE,0XCA,0XAB,0XDA,0X80,0X0B,0XBF,0XF4,0X88,0X80,0X8C,0XB5,0XA8, +0XEF,0X6E,0XFF,0XFB,0X75,0XFF,0XED,0X28,0X14,0X7F,0XCA,0X27,0X40,0X71,0X42,0X57, +0X3B,0XBB,0XAB,0XFD,0XBE,0XEB,0XB2,0X84,0X03,0X9F,0XF1,0XD0,0X01,0X0E,0XB5,0X08, +0XEC,0XEE,0XFF,0XFF,0XD3,0X7F,0XDD,0X30,0X18,0X6F,0XCC,0X2A,0X80,0XB1,0X40,0X97, +0XB7,0XBB,0XAA,0XFC,0XED,0XEF,0XA2,0XC4,0X07,0XBF,0XF3,0X45,0X00,0XCE,0XBA,0X68, +0XDA,0XEE,0XFF,0XFF,0X3B,0XFF,0XFD,0X10,0X10,0X7F,0XCC,0XB0,0X00,0X31,0X45,0X97, +0X6F,0XBB,0XAD,0XF7,0XD4,0XEF,0XD4,0X42,0X0B,0XBF,0XF3,0X4A,0X00,0XCE,0XB1,0X68, +0XBA,0XEE,0XF3,0X5A,0XFF,0XFF,0XEF,0X0C,0X14,0X7F,0XCC,0XB0,0XA1,0X31,0X4C,0X06, +0XEF,0XBB,0XBE,0XEF,0X53,0XEF,0XB0,0XB0,0X0B,0XBF,0XF3,0X4A,0X00,0XCE,0X83,0XF1, +0XBA,0XEF,0XDB,0XBF,0XAC,0XFF,0XEF,0X42,0X24,0X7F,0XDC,0XB4,0XC0,0X31,0X38,0X1E, +0XEF,0XBA,0XEF,0XD6,0XFB,0XFF,0XB4,0X0C,0X13,0XFF,0XF3,0X6A,0X01,0XCE,0X87,0XC1, +0XBA,0XDF,0XFF,0XEF,0XAF,0XFF,0XDB,0XA0,0X2C,0X3F,0XCC,0X95,0X40,0X31,0X70,0X34, +0XEF,0XFF,0XFF,0X3E,0XF5,0XFF,0X74,0X54,0X03,0XFF,0XFB,0X60,0X01,0XCE,0X8D,0X8B, +0XBB,0X7F,0XFF,0XEF,0X5B,0XFF,0XDB,0X00,0X34,0X7F,0XC4,0X9A,0X80,0X31,0X72,0X74, +0XFC,0XFF,0XFF,0XFD,0XEF,0XFF,0X6C,0XAA,0X0B,0XFF,0XFF,0X45,0X01,0XEE,0X8C,0X8B, +0XFF,0XFF,0XFF,0XF3,0X7B,0XFF,0XB3,0X54,0X24,0X7F,0XD2,0XA8,0X01,0X15,0X73,0X35, +0XFF,0XFF,0XFF,0XFE,0XAF,0XFF,0XEC,0X00,0X1B,0XFF,0XED,0X46,0X00,0XEA,0X8C,0XC8, +0XFF,0XFF,0XFF,0XFF,0XF7,0XFF,0XFB,0X2A,0X44,0X7F,0XDE,0XB9,0X03,0X15,0X72,0X12, +0XFF,0XFF,0XFF,0XD7,0XBB,0XFF,0X64,0XC0,0X33,0XBF,0XF3,0X46,0X08,0XFE,0X8D,0X4C, +0XFF,0XFF,0XFF,0XBF,0XDF,0XFF,0XFB,0X28,0X4C,0XFF,0XCC,0XB9,0XC3,0X01,0X70,0X03, +0XFF,0XFF,0XFF,0XD7,0XFF,0XFD,0X24,0X14,0X13,0X7F,0XF7,0X44,0X28,0XFE,0X8E,0XB4, +0XFF,0XFF,0XFF,0X7F,0XDF,0XFE,0XFA,0XC2,0X6C,0XBF,0XD8,0X32,0X55,0X01,0X70,0X4B, +0XFF,0XFF,0XFF,0XCF,0XBF,0XFF,0XB5,0X28,0X13,0XFF,0XE7,0X4C,0X02,0XFE,0X8B,0X94, +0XFF,0XFF,0XFF,0X3F,0XFF,0XFF,0XDA,0X42,0X4C,0XFF,0XD8,0X12,0X05,0X09,0X74,0X53, +0XFF,0XFF,0XFD,0XF4,0XFF,0XFF,0X65,0X28,0X33,0X7F,0XF7,0X4C,0X00,0XF6,0X83,0X00, +0XFF,0XFF,0XFB,0X5F,0XBF,0XFF,0XBA,0X94,0X4C,0XFF,0XD8,0X12,0X37,0X0B,0X74,0XBB, +0XFF,0XFF,0XFE,0XAA,0XFF,0XFF,0XD4,0X42,0X23,0XFF,0XAF,0X0C,0X80,0XF4,0X8B,0X45, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XEB,0X28,0X5C,0XFF,0XF0,0X22,0X43,0X0B,0XF4,0XB8, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XD4,0X93,0X23,0X7F,0XBE,0X9C,0XAC,0XF4,0X0B,0X47, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFA,0X68,0X1D,0XFF,0XC9,0X42,0X03,0X0B,0XF4,0XB8, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XCD,0X04,0XC2,0XFF,0X76,0X39,0XAC,0XFC,0X4B,0X47, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF6,0XB3,0X3D,0XFF,0XD9,0X44,0X53,0X07,0XB4,0XA8, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XD0,0X48,0X03,0X7F,0X6E,0XB2,0XAC,0XF8,0XDB,0X57, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X24,0XDC,0XFF,0XD1,0X4C,0X53,0X0F,0X64,0XA8, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X92,0X23,0XFF,0X2E,0XB3,0X94,0XF2,0XAA,0X57, +0XFF,0XFF,0XFF,0XFF,0XBF,0XFF,0XFE,0X49,0X5D,0XFF,0XD1,0X44,0X43,0X0D,0X6B,0XA8, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X69,0X24,0XA3,0XFF,0X24,0X51,0XA2,0XF2,0XFC,0X57, +0XFF,0XFF,0XFF,0XFF,0X5F,0XFF,0XF6,0X92,0X5D,0XFF,0XDB,0X0C,0X01,0X5F,0X53,0XA8, +0XFF,0XFF,0XFE,0XFF,0XFF,0XFF,0XE8,0X49,0X23,0XFF,0X20,0XA2,0X86,0XE9,0XEC,0X57, +0XFF,0XFF,0XFF,0XFF,0X3F,0XFF,0XFF,0X24,0XDC,0XFF,0XCA,0X49,0X41,0X1F,0X53,0XAA, +0XFF,0XFF,0XFE,0XFE,0XFF,0XFF,0XE0,0X92,0X23,0XFF,0X35,0X24,0X0A,0XE4,0XBC,0X55, +0XFF,0XFF,0XFF,0XFF,0XBF,0XFF,0XFE,0X41,0X5F,0XFF,0XC8,0XDB,0X45,0X5F,0XC3,0XAA, +0XFF,0XFF,0XFF,0X7E,0XFF,0XFF,0XE9,0X3C,0XA0,0XFF,0X32,0X20,0X2A,0XA1,0X7E,0XD5, +0XFF,0XFF,0XFF,0XFF,0XBF,0XFF,0XF6,0XC3,0X5F,0XFF,0XC9,0X49,0X15,0X5E,0XB3,0X58, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE9,0X14,0X23,0XFF,0X34,0X34,0XCA,0XA5,0XDD,0X47, +0XFF,0XFF,0XFF,0XFF,0XBF,0XFF,0XFE,0X49,0XDD,0XFF,0XCB,0X82,0X05,0X5A,0X67,0XF8, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE1,0XA6,0X23,0XFF,0X74,0XB8,0XAA,0XA5,0XB9,0X57, +0XFF,0XFF,0XFF,0XFF,0XBF,0XFF,0XFE,0X10,0XDF,0XFF,0XC3,0XC2,0X2F,0XFB,0X47,0XAC, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE9,0XCB,0X23,0XFF,0XFD,0X7F,0XFF,0XFC,0XB8,0XF7, +0XFF,0XFF,0XFF,0XFF,0X7F,0XFF,0XF6,0X34,0XBF,0XFF,0XC3,0XFF,0XFF,0XFF,0XE7,0X08, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE9,0X82,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X98,0XF7, +0XFF,0XFF,0XFF,0X7F,0X7F,0XFF,0XFE,0X5B,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X67,0X48, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE1,0XA7,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XD0,0XBF, +0XFF,0XFF,0XFF,0XFF,0X7F,0XFF,0XFE,0X5B,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X3E,0XF4, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF1,0X45,0X54,0XF7,0XFF,0XFF,0XFF,0XFF,0XCB,0X8B, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XCC,0X3A,0XAB,0XF7,0XFF,0XFF,0XFF,0XF4,0XFE,0XFE, +0XFF,0XFF,0XFE,0XFF,0XFF,0XFF,0XF0,0X85,0X3A,0XFF,0XFF,0XFF,0XFF,0XDB,0X33,0X53, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X0A,0XA2,0XFF,0XC7,0XFF,0XFF,0XF5,0X6F,0XCF,0XED, +0XFF,0XFF,0XFD,0X7F,0XFF,0XFF,0X37,0XE9,0X5F,0XFF,0XFF,0XFB,0X5F,0XBD,0X7B,0X3F, +0XFF,0XFF,0XFE,0XFF,0XFF,0XFF,0X0F,0XF0,0X3F,0X8F,0XFF,0XFD,0XF4,0XFF,0XAE,0XD2, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X0F,0X48,0XD7,0XF7,0XFF,0XEE,0X0B,0X3D,0XF7,0XBF, +0XFF,0XFF,0XFE,0XFF,0XFF,0XFF,0X15,0XB4,0X0F,0X3F,0XFC,0XF3,0XFF,0XD2,0XDA,0XE9, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X8C,0XD0,0X16,0XF7,0XF3,0XFD,0X57,0XFF,0X6F,0XBF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X82,0XC0,0X0F,0XFA,0XDF,0X4A,0XBB,0XCD,0XBA,0XD4, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X1D,0XFF,0X34,0XFF,0XDF,0X76,0X4F,0XEB, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XBC,0X00,0X3F,0XFE,0XEB,0XB7,0X57,0XBB,0XF5,0X3E, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XD2,0X02,0XBB,0XFF,0X1E,0XD9,0XCC,0XCD,0X2E,0XCB, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XAD,0XCC,0X7E,0X7C,0XF3,0X76,0X33,0X76,0XD3,0XFC, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFA,0X23,0XD7,0X9A,0X1C,0XC9,0X8A,0XAB,0XAC,0X9F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XAF,0XDC,0XE4,0X34,0X6B,0X36,0X45,0XD6,0XF7,0XF5, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X23,0X12,0XCB,0X3E,0XC9,0XBA,0X3B,0X1B,0XAA, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XB3,0XD4,0XD1,0X38,0XCD,0X36,0X47,0XCE,0XF4,0XD5, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XDE,0X2B,0X1E,0XCF,0X73,0XC9,0XBD,0X7B,0X0F,0X6A, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFB,0XD4,0X00,0X31,0X8C,0X36,0X52,0X8D,0XF2,0X97, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X2B,0XA2,0XFE,0XF3,0XC9,0X4F,0XFB,0X3F,0XF9, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X7B,0XC4,0X0F,0XFB,0X4C,0X34,0X3D,0X2D,0XDB,0X4E, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X3B,0XF3,0XFC,0XB3,0XC0,0X42,0XD7,0X7D,0XB7, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X7B,0XC4,0X0F,0XFF,0X5C,0X38,0X3F,0XA8,0X8A,0XCB, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XBC,0X33,0XFF,0XFC,0X83,0XC4,0X44,0XF7,0XF5,0X74, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0XDB,0XCC,0X1F,0XFB,0X7E,0X20,0X3F,0X5A,0XAA,0XAF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X7E,0X33,0XEF,0XFC,0X81,0X90,0X81,0XAF,0X7F,0XF2, +0XFF,0XFF,0XF7,0XFF,0XFF,0XFE,0XDB,0XCC,0X3F,0XF7,0X7E,0X40,0X1E,0XF9,0XD4,0X4D, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X3D,0X33,0XDF,0XF8,0X01,0X80,0X03,0X4E,0X2B,0X3E, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFA,0XCC,0X3E,0XE7,0XFE,0X00,0X2D,0XB7,0XFE,0XE3, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X5F,0X22,0XEF,0X38,0X01,0XCB,0XEA,0XF9,0X75,0X3C, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XBC,0XBB,0X3F,0XEA,0XBE,0X3F,0XFF,0XFF,0XBF,0XCB, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF3,0XBC,0XF3,0XFF,0XCA,0XFF,0XFF,0XFC,0XD4,0XFE, +0XFF,0XFF,0XFB,0XFF,0XFF,0XFF,0XDF,0XFF,0XFF,0XFF,0XFF,0XFF,0XDF,0XFF,0X7F,0X35, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XBF,0XFF,0X3F,0XFF,0XFF,0XFF,0XFF,0XF0,0X95,0XCA, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFD,0XCF,0XFF,0XFF,0XFF,0XDF,0XFF,0XEE,0XF7, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XBF,0XF5,0X70,0X0E,0XFF,0XFF,0XFF,0XFE,0XF5,0X2D, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X58,0XEF,0XFF,0XFF,0XFF,0XDD,0X7F,0X1B,0XF6, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XBE,0XA7,0XF0,0X2C,0XFF,0XFF,0XEF,0XD5,0XEC,0XA9, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XDF,0XCB,0XEC,0XFF,0X7F,0XFF,0XB5,0XBF,0X53,0XFE, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XBF,0XF5,0X73,0XFC,0XFF,0XFF,0XEA,0XD4,0XEC,0XD3, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC4,0X0A,0XCC,0XFF,0XFF,0XFF,0X7D,0X6F,0XBB,0X2C, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE8,0XD5,0X33,0XFC,0XFF,0XFF,0XAB,0XBE,0XFD,0XD7, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF7,0X20,0XCC,0XFF,0XFF,0XFF,0XEC,0XFB,0XBE,0XAA, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0XCB,0X33,0XFC,0XFE,0XFF,0XF7,0XFC,0XD3,0X6B, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X34,0XCD,0XFF,0XFF,0XFF,0XCB,0XBF,0XFD,0XAF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF4,0XCF,0X33,0XFD,0X3F,0XFF,0XFF,0XD2,0X0E,0X78, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X32,0XCD,0XFF,0XF4,0XFF,0XCC,0XFD,0XF3,0XDF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF4,0XCD,0X33,0XFD,0X1F,0XFF,0XFF,0XDF,0XFD,0X32, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X32,0XC4,0XFE,0XF3,0X7F,0XDF,0XF5,0X57,0XDF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFB,0XF4,0XCD,0X3B,0XF9,0X0D,0XFF,0XFD,0X7B,0XDD,0X6A, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFD,0XFF,0X36,0XC0,0X7E,0X32,0X7F,0XBF,0XDF,0X62,0X95, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFB,0XF4,0XCB,0X28,0X3C,0X8D,0X7F,0XFC,0XF4,0XDF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X34,0XD6,0X92,0X20,0X5F,0XFF,0X4F,0X33,0X01, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFB,0XF2,0XCB,0X29,0X25,0XD2,0X17,0XFC,0XBD,0XCF,0XFE, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFD,0XFE,0XB5,0XD6,0XD8,0X0C,0X0F,0XFF,0XE6,0XFC,0XA1, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF7,0XFB,0X4A,0X69,0X21,0XE0,0X05,0XFF,0XFB,0XAB,0XDB, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFD,0XFC,0XB5,0XBA,0XDA,0XEB,0XB3,0X3F,0XFE,0X77,0X6D, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF3,0XFF,0X4E,0XCD,0X65,0XF4,0X48,0XDF,0XFB,0XDC,0XBD, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF2,0XB3,0X72,0X9B,0XFB,0X36,0X37,0XCF,0X23,0XE7, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF1,0X3D,0X4D,0XBF,0X6C,0XCC,0X81,0XC9,0X7D,0XFF,0X5C, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF6,0XDA,0XB3,0X53,0X93,0X73,0X6A,0X16,0XB7,0X54,0XA3, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFB,0XFF,0X4C,0XBC,0X6C,0X8C,0X94,0X0B,0XCA,0XAF,0XDC, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF5,0X38,0XB3,0X4F,0X93,0X73,0X40,0X34,0XFF,0XF5,0X73, +0XFF,0XFB,0XFF,0XFF,0XFF,0XFB,0XCF,0X4D,0XB5,0X6C,0X8C,0XBA,0XAF,0X3C,0X2A,0XCE, +0XFF,0XFE,0XFF,0XFF,0XFF,0XE5,0X7A,0XB6,0XEA,0X93,0X73,0X45,0X11,0XC3,0XFB,0X39, +0XFF,0XFF,0X7F,0XFF,0XFF,0XFB,0XAD,0X4B,0X3D,0X4C,0X8C,0XB0,0XEE,0XFF,0X5D,0XD7, +0XFF,0XF3,0XFF,0XFF,0XFF,0XED,0XFA,0XBC,0XC2,0XA3,0X73,0X4E,0X13,0X5D,0XEA,0X2C, +0XFF,0XFF,0X3F,0XFF,0XFF,0XF3,0XFF,0XD7,0X7E,0X5C,0X84,0XA1,0XEE,0XEE,0XDF,0XD7, +0XFF,0XFD,0XFF,0XFF,0XFF,0XCD,0XFA,0XE9,0XA3,0X83,0X7B,0X56,0X13,0X13,0XEB,0X68, +0XFF,0XFF,0X3F,0XFF,0XFF,0XF7,0XFF,0X36,0X5F,0X7C,0X84,0XA9,0XEE,0XFF,0XBB,0XBE, +0XFF,0XFF,0XDF,0XFF,0XFF,0XEB,0XF9,0XD9,0XAC,0X82,0X51,0X56,0X13,0X8C,0XCC,0X4B, +0XFF,0XFC,0X3D,0XF7,0XFF,0XFF,0XFE,0XE6,0XF7,0X5C,0X0E,0XAB,0XEE,0XF7,0XF7,0XA9, +0XFF,0XFF,0XDF,0X0D,0XDF,0XE5,0XFB,0X1B,0XAA,0XA2,0X01,0X55,0X13,0XAB,0X2A,0XEA, +0XFF,0XFC,0X4C,0XF2,0XFF,0XFB,0XFD,0XEC,0XFD,0X55,0X06,0XAA,0XEE,0XDC,0XFF,0X3D, +0XFF,0XFE,0XF7,0XFD,0X2F,0XEF,0XFA,0X57,0X33,0XA8,0X01,0X55,0X17,0X37,0XCD,0XD7, +0XFF,0XFB,0X09,0X52,0XDF,0XF5,0XFD,0XAA,0XFC,0X56,0X0E,0XAE,0XAB,0XCD,0X72,0X7C, +0XFF,0XFC,0XF4,0X8D,0X7F,0XFB,0XFA,0X7D,0X93,0XA8,0X21,0X51,0X17,0X7A,0XAF,0X83, +0XFF,0XFF,0X0B,0X6E,0XFF,0XF7,0XFD,0X86,0X6C,0X54,0X8E,0X2C,0XEC,0XCF,0XFD,0X7C, +0XFF,0XFC,0XD0,0X39,0XFF,0XFB,0XFA,0XF9,0X93,0X00,0X21,0X50,0X17,0XB5,0XF2,0X83, +0XFF,0XFA,0X01,0XF7,0XFF,0XEF,0XFD,0X06,0X6C,0X80,0XDE,0X84,0XAD,0X5A,0X2F,0X7C, +0XFF,0XFE,0XC0,0X9B,0XEF,0XF3,0XFA,0XFB,0X92,0X2A,0X00,0X52,0X52,0XD7,0XDD,0XD3, +0XFF,0XF9,0X03,0X67,0X7F,0XDF,0XFF,0XA4,0X6C,0X80,0XAB,0X05,0X0F,0X6B,0X6B,0X2C, +0XFF,0XFE,0X00,0XDF,0XDF,0XF3,0XFA,0XDF,0XD3,0X2A,0X54,0X00,0XF1,0XB4,0XBC,0XF3, +0XFF,0XF4,0X03,0X38,0X7F,0XCF,0XFF,0X62,0X2C,0X14,0X00,0X01,0X0E,0XCB,0XD7,0XAC, +0XFF,0XF0,0X04,0X47,0XCF,0X7D,0XFA,0XBF,0XF3,0XC0,0X40,0X00,0X53,0X34,0XAA,0XFF, +0X5D,0XFC,0X00,0XB9,0X35,0X83,0XFF,0XFF,0XFA,0X2B,0X0B,0X00,0X0F,0XEB,0XFF,0XFF, +0X86,0XF4,0X84,0X44,0XC8,0XFF,0XFF,0XFF,0XF9,0X50,0XE4,0X20,0XA0,0XBF,0XFF,0XFF, +0XB9,0X72,0X00,0X42,0X34,0X2F,0XFF,0XFF,0XF4,0XAF,0X1B,0X90,0X1B,0XFF,0XFF,0XFF, +0XC2,0X08,0X00,0X05,0X13,0XFF,0XFF,0XFF,0XFA,0X50,0XE4,0X48,0XAF,0XFF,0XFF,0XFF, +0XF9,0XB2,0X00,0X08,0X0D,0XFF,0XFF,0XFF,0XFD,0XAF,0X13,0X23,0XFF,0XFF,0XFF,0XFF, +0XE9,0X44,0X00,0X03,0XB3,0XFF,0XFF,0XFD,0XFA,0X50,0XCC,0XBF,0XFF,0XFF,0XFF,0XFE, +0XF8,0X00,0X00,0X0C,0X07,0XFF,0XF5,0X07,0XFD,0XAF,0X23,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFE,0X00,0X00,0X10,0X1F,0XFF,0XFF,0XF9,0XFE,0X50,0X9F,0XFF,0XFF,0XFF,0XFF,0XFF, +0XF4,0X00,0X00,0X04,0X3F,0XFF,0XF4,0X07,0XFF,0XAE,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE, +0XF8,0X00,0X00,0X00,0XFF,0XFF,0XFB,0XF8,0XFF,0X53,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XE4,0X00,0X00,0X00,0XFF,0XFF,0XFC,0X3F,0X7C,0XAF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XF8,0X00,0X08,0X03,0XFF,0XFF,0XFF,0XFE,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XE0,0X00,0XBC,0X0F,0XFF,0XFF,0XFD,0XFF,0X7E,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XF0,0X0B,0XFE,0X0F,0XFF,0XFF,0XEA,0XFF,0XBF,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XC0,0X0F,0XFF,0XFF,0XFF,0XFC,0XBF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XF0,0X6F,0X7F,0XFF,0XFF,0X3F,0XEF,0XFF,0XFF,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XC0,0X3F,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XF0,0XFC,0X3D,0XFE,0XFF,0XFF,0XFF,0XFF,0XFF,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XC0,0XEE,0XF2,0XFF,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XA0,0XF4,0X7F,0XF1,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFD, +0XC0,0XDF,0XF5,0XFE,0XBF,0XFF,0XFF,0XFF,0XFF,0XBF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XA0,0X75,0XFF,0X31,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFD, +0XC0,0X54,0X55,0XEA,0XDF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0X80,0X54,0X21,0X14,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF5, +0XC0,0X00,0X48,0XC2,0X9F,0XFF,0XFF,0XFF,0XFF,0XDF,0XFF,0XFF,0XFF,0XFF,0XFE,0X35, +0X80,0X00,0XB2,0X3E,0X87,0XFF,0XFF,0XDF,0XFF,0XBF,0XFF,0XFF,0XFF,0XFF,0XFF,0XDA, +0XE0,0X00,0X4C,0XFF,0X67,0XFF,0X34,0XAF,0XFF,0XDF,0XFF,0XFF,0XFF,0XFF,0XFE,0XFB, +0X00,0X00,0X23,0XFF,0XBB,0XFF,0XCF,0XFF,0XFF,0X5F,0XFF,0XFF,0XFF,0XFF,0XFF,0X3E, +0XC0,0X00,0X5F,0XFF,0X7F,0XFF,0XB0,0XD7,0XFF,0X2F,0XFF,0XBF,0XFF,0XFF,0XFF,0XF7, +0X00,0X00,0X7F,0XFF,0XDF,0XFD,0XCB,0X7F,0XFC,0XB7,0XFF,0XDF,0XFF,0XFF,0XE4,0XB1, +0XC2,0X00,0X3F,0XFF,0X3F,0XFB,0X20,0XF5,0X5E,0X0B,0XFD,0X7F,0XFF,0XFF,0XFF,0XDC, +0X00,0X00,0X7F,0XFF,0XEF,0XFD,0XDC,0XEE,0XB1,0X7D,0X57,0XDF,0XFF,0XF5,0XF7,0X57, +0X84,0X00,0X3F,0XFE,0X17,0XFF,0X20,0X7F,0XF4,0X07,0XFD,0X77,0XFF,0XF3,0XDC,0XA8, +0X00,0X80,0XBF,0XFF,0XFF,0XFE,0XD8,0XBF,0XE2,0X79,0X02,0X88,0X7F,0XDC,0X67,0XF7, +0X20,0X00,0XFF,0XFE,0X0F,0XFD,0X23,0X47,0XC2,0X8F,0XFD,0X55,0X8C,0XF3,0XDA,0X1E, +0X00,0X00,0X7F,0XFF,0XFF,0XEB,0XD8,0X39,0X42,0X74,0X4A,0X80,0X73,0X4C,0XAF,0XF3, +0XC6,0X02,0XFF,0XFE,0XBF,0XEC,0X20,0X2F,0XE3,0X8B,0X25,0X6A,0X9E,0XAB,0X78,0X5E, +0X0A,0X47,0X3F,0XFD,0X7F,0X37,0XB8,0X31,0X22,0X74,0XBA,0X85,0X6B,0X77,0X47,0XA3, +0X86,0X54,0XFF,0XFF,0XBF,0XDC,0XA6,0X76,0XC5,0X8B,0XE4,0X60,0X55,0X9C,0XFC,0XFC, +0X2F,0X0F,0X5F,0XFC,0XFE,0XE2,0XD8,0X7B,0XB4,0X7E,0XFB,0X13,0X3E,0XE3,0X27,0X0B, +0X8C,0X83,0XBF,0XFF,0X3F,0X3F,0X44,0X24,0X42,0XB3,0X04,0XC0,0XC3,0X5E,0XDB,0XF4, +0X2E,0XDF,0XFF,0XFC,0XFE,0XF9,0X34,0XFB,0X02,0XCD,0XF2,0X3D,0X3D,0XA5,0X34,0XCF, +0XCC,0X3B,0XFF,0XFF,0XFF,0XBE,0XC2,0X14,0XA3,0X32,0X0C,0X80,0XD6,0XFD,0XCF,0X31, +0X1E,0X5F,0XFF,0XF8,0XFE,0XF5,0X68,0XA3,0X5A,0XDD,0XE3,0X75,0X69,0X52,0X74,0XDE, +0XED,0XBD,0XFF,0XFF,0X7F,0X5B,0X12,0XC8,0X2D,0X62,0X18,0X0A,0XB7,0XAD,0XAB,0X61, +0X1A,0X7F,0XFF,0XF9,0XFD,0XAD,0XE3,0X30,0X82,0XDD,0X45,0XF1,0X58,0XF3,0X5E,0X9E, +0XEF,0XFB,0XFF,0XFE,0XFB,0XF3,0X18,0X8D,0X3D,0X6A,0X3A,0X0E,0XCF,0XDD,0XE3,0XE1, +0X17,0X7F,0XFF,0XFB,0XFC,0XFC,0XE2,0XA2,0X07,0XB5,0X85,0XE1,0X74,0XAD,0X1D,0X5B, +0XBF,0X7B,0XFF,0X7C,0XFB,0XFB,0X1D,0X19,0XF8,0XCE,0X5A,0X1C,0XAB,0XF7,0XF3,0XAC, +0XDF,0XBF,0XFF,0XBB,0XFE,0XFC,0XE2,0XC2,0X07,0X31,0XA5,0XE3,0XFE,0X8C,0X1C,0XB3, +0XBF,0XEB,0XFF,0XFF,0XDF,0XFB,0X39,0X3D,0XC8,0XD6,0X5A,0X1C,0X15,0XF3,0XEF,0XCE, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0XC6,0XC2,0X0F,0X49,0XA5,0XE3,0XEB,0X5C,0XDA,0X3B, +0XBF,0XFB,0XFF,0XFF,0XFF,0XFF,0X79,0X3D,0XAA,0XB6,0X5A,0X1C,0X3E,0XE3,0X25,0XDC, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X86,0XCA,0X5D,0X49,0XA5,0XE3,0XD3,0X3E,0XDE,0X27, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X79,0X35,0X0F,0XB6,0X52,0X1C,0X6D,0XFF,0X77,0XD8, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X8E,0XCA,0XF4,0X69,0XA9,0XE3,0X12,0X11,0XCD,0X67, +0XFF,0XFF,0XFE,0XFF,0XFF,0XFF,0XF1,0X35,0X0B,0X96,0X56,0X1C,0XEF,0XEE,0X76,0XB8, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X8E,0XCA,0XFC,0X6B,0XA9,0XE3,0X11,0X51,0X89,0X4F, +0XFF,0XFF,0XFE,0XBF,0XFF,0XFF,0XF1,0X35,0X37,0XB4,0X56,0X1C,0XEE,0XAE,0X7B,0XFA, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XCC,0XAA,0XD8,0X4B,0X91,0XE2,0X1B,0XF1,0XAD,0X2D, +0XFF,0XFF,0XFE,0XDF,0XFF,0XFF,0XF3,0X55,0X6F,0XB0,0X4A,0X1D,0XA5,0X1E,0X72,0XF2, +0XFF,0XF7,0XFF,0X2F,0XFF,0XFF,0XCC,0XBA,0X90,0X4D,0X15,0XE2,0XDA,0XE5,0X8F,0X8D, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF2,0X45,0XEF,0XB2,0X82,0X14,0X6F,0XEA,0XF0,0X72, +0XFF,0XF7,0XFD,0X57,0X7F,0XFF,0XCD,0XBA,0X30,0X48,0X09,0XEB,0X11,0X35,0X0F,0XAD, +0XFF,0XFF,0XFF,0XEF,0XFF,0XFF,0XF2,0X65,0XCD,0XB2,0XB4,0X10,0X4E,0XCA,0XFB,0XD2, +0XFF,0XF7,0XFD,0X3F,0XFF,0XFF,0XDD,0X9A,0X30,0X4D,0X4B,0X6C,0X0B,0XFF,0X0C,0XAD, +0XFF,0XFF,0XF3,0XDF,0XFF,0XFF,0XE2,0X65,0XC3,0X32,0XB0,0X80,0X01,0X10,0XF7,0X52, +0XFF,0XF7,0XFD,0X7F,0XFF,0XFF,0XFD,0XBA,0X78,0XC9,0X4C,0X72,0XA0,0XAB,0X0B,0XAD, +0XFF,0XFB,0XF6,0XDF,0XFF,0XFF,0XF2,0X45,0X84,0X36,0XB2,0X8D,0X0E,0XDC,0XF4,0X57, +0XFF,0XEF,0XFB,0X3F,0XFF,0XFF,0XFD,0XBB,0X5B,0X41,0X45,0X60,0XB3,0XA7,0X3F,0XA8, +0XFF,0XFF,0XFD,0XFF,0XFF,0XFF,0XF2,0X4D,0X80,0X0A,0XB2,0X96,0X1C,0X58,0XC4,0XF7, +0XFF,0XEF,0XFA,0X5F,0XFF,0XFF,0XFE,0XB2,0X6D,0X01,0X49,0X48,0XA3,0XEB,0X3B,0X50, +0XFF,0XFF,0XFF,0XBF,0XFF,0XFF,0XF3,0X6D,0X90,0XCA,0X84,0X37,0X0D,0X3D,0XEB,0XAF, +0XFF,0XEF,0XFA,0XFF,0XFF,0XFF,0XFC,0XD2,0X44,0X25,0X22,0X88,0XA2,0XC3,0X3C,0XD0, +0XFF,0XFF,0XFD,0X3D,0XFF,0XFF,0XEF,0X2D,0X03,0X12,0XC9,0X77,0X5D,0X3D,0XD3,0X2F, +0XFF,0XEF,0XFB,0XFF,0XFF,0XFF,0XF2,0XF6,0XA8,0XE9,0X04,0X08,0XA6,0XC0,0XBD,0XF0, +0XFF,0XFF,0XFC,0X7C,0XFF,0XFF,0XEE,0X89,0X53,0X14,0X32,0XB6,0X59,0X3F,0X46,0XDE, +0XFF,0XEF,0XF7,0XB7,0XFF,0XFF,0XFB,0X76,0XAC,0X41,0X0C,0X49,0XA6,0XC2,0XBB,0X31, +0XFF,0XF7,0XFC,0XFB,0XFF,0XFF,0XED,0X89,0X51,0X20,0XD3,0XB4,0X5B,0X3F,0XD4,0XDE, +0XFF,0XDF,0XF3,0X3E,0XFF,0XFF,0XFA,0XF6,0XAE,0X80,0X2C,0X02,0XAC,0XD5,0X4F,0X31, +0XFF,0XF7,0XDC,0XFB,0XFF,0XFF,0XEF,0XF9,0X41,0X20,0XC3,0XF8,0XD3,0X0B,0X70,0XCE, +0XFF,0XCF,0XAB,0XFE,0XDF,0XFF,0XF5,0X76,0XB4,0XD0,0X3C,0X02,0X2E,0XF4,0X8F,0X31, +0XFF,0XFF,0XFC,0X7B,0X7F,0XFF,0XFE,0XFB,0X41,0X00,0X81,0XFC,0XD3,0XBB,0XF0,0XCE, +0XFF,0XEF,0XEB,0XFD,0XDF,0XFF,0XEB,0XEC,0X80,0X03,0X7C,0X03,0X2C,0X4E,0X2F,0X71, +0XFF,0XFF,0XF4,0XFB,0X3F,0XFF,0XFE,0XF7,0X41,0X0C,0X02,0XBC,0X93,0XF1,0XD0,0X8D, +0XFF,0XFF,0XFB,0XFD,0XCF,0XFF,0XEB,0XFA,0X80,0XA0,0X28,0X43,0X6C,0X1E,0X2F,0X70, +0XFF,0XFF,0XCD,0X7A,0X3F,0XFF,0XFE,0XF5,0X4A,0X48,0XD7,0X3C,0X93,0XE9,0XD0,0X8A, +0XFF,0XFF,0X72,0XFD,0XCF,0XFF,0XEB,0XFE,0XB4,0XA2,0X28,0XC3,0X6D,0X37,0X2F,0X71, +0XFF,0XFF,0XED,0XF2,0X3F,0XFF,0XFE,0XF3,0X4B,0X1C,0X97,0X3C,0XD2,0XE8,0XD0,0X8E, +0XFF,0XDF,0X37,0XFD,0XCF,0XFF,0XEB,0XFD,0XB4,0XE3,0X68,0XCB,0X2D,0X17,0X6E,0X71, +0XFF,0XBF,0XD0,0XF2,0X3F,0XFF,0XFE,0XF6,0XCB,0X1C,0X97,0X34,0X52,0XE8,0X91,0X8E, +0XFF,0XDF,0XFF,0XFE,0XDF,0XFF,0XEB,0XFB,0X34,0XC3,0X68,0XCB,0X8B,0X97,0X6A,0X71, +0XFF,0XEF,0XC3,0XF3,0XAF,0XFF,0XFD,0XF5,0XCB,0X3C,0X97,0X34,0X4C,0X68,0X95,0X8E, +0XFF,0X3F,0XFD,0XFF,0XFF,0XFF,0XF7,0XEA,0XB4,0XC1,0X48,0XCB,0X13,0XB7,0X6A,0X71, +0XFF,0XDF,0XE7,0XEF,0XFF,0XFF,0XFC,0XF5,0X4B,0XBC,0X37,0X34,0XCC,0X4E,0X95,0X8E, +0XFF,0X3D,0XFB,0XF3,0XDF,0XFF,0XCB,0XEA,0XB4,0X42,0XC8,0XCB,0X13,0XB3,0X68,0X71, +0XFF,0XDF,0XEF,0XEF,0XFF,0XFF,0XFF,0XF5,0X5B,0XB9,0X27,0X34,0X0D,0X5D,0X92,0X8E, +0XFE,0XBD,0XF3,0XFF,0XBF,0XFF,0XF5,0XDA,0XA4,0X44,0XD0,0XC5,0X92,0XAE,0X6C,0X71, +0XFF,0X5F,0XCD,0XD7,0XFF,0XFF,0XCF,0XB5,0X5B,0XB3,0X2F,0X32,0X4D,0X75,0X92,0X8A, +0XFE,0XBC,0XB6,0XAB,0XBF,0XFF,0XF0,0XCA,0XA4,0X04,0XD0,0XC1,0X32,0X8A,0X6D,0X75, +0XFF,0X5F,0XDF,0XFF,0XDF,0XFF,0X5F,0X75,0X5A,0X81,0X2F,0X00,0X8D,0X75,0X92,0X8A, +}; diff --git a/main/img2.h b/main/img2.h new file mode 100644 index 0000000..744ccdf --- /dev/null +++ b/main/img2.h @@ -0,0 +1,298 @@ +const unsigned char gImage_img2[4736] = { /* 0X01,0X01,0X28,0X01,0X80,0X00, */ +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X7F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X7F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X01,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X00,0X00,0X01,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X03,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X03,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X07,0XFF,0XF6,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X04,0X14,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XD0,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XDF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0X1F,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XF0,0X01,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X07,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X07,0XC0,0X02,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0X88,0X00,0X00,0X03,0XC0,0X38,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0X80,0X00,0X00,0X07,0X80,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0X00,0X00,0X00,0X03,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0X00,0X00,0X00,0X07,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFE,0X00,0X00,0X00,0X01,0X40,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X02,0X22,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X07,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X3F,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X3F,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X01,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFC,0X00,0X00,0X00,0X03,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFE,0X00,0X00,0X00,0X03,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFE,0X00,0X00,0X00,0X03,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFE,0X00,0X00,0X00,0X07,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0X00,0X00,0X00,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0X80,0X00,0X0F,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XC0,0X00,0X0F,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XE0,0X00,0X3F,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XF8,0X00,0XFF,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFE,0X1F,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X03, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X0F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X7F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XA2,0XAF,0X80,0X00,0X02,0XAF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF7,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X9F,0XFF,0XFF,0XE7,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X0F,0XFF,0XFF,0XE3,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X0F,0XFF,0XFF,0XE1,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X07,0XFF,0XFF,0XE1,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X0F,0XFF,0XFF,0XF1,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X07,0XFF,0XFF,0XF0,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X07,0XFF,0XFF,0XF0,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X03,0XFF,0XFF,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X03,0XFF,0XFF,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X03,0XFF,0XFC,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X01,0XFF,0XF0,0X00,0X7F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X01,0XFF,0XE0,0X00,0X3F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X01,0XFF,0XC0,0X00,0X7F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0XFF,0X80,0X00,0X3F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF2,0X00,0X00,0XFF,0X80,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0XF0,0X00,0X0F,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X00,0X00,0X0F,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X07,0X00,0X00,0X00,0X00,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X0F,0X80,0X00,0X00,0X03,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XF5,0XFF,0XFF,0XFF,0XC0,0X04,0X00,0X00,0X00,0X03,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFC,0X40,0X7F,0XFF,0X55,0X40,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFC,0X00,0X1F,0XF4,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFC,0X00,0X1F,0X40,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFC,0X00,0X1C,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFC,0XFF, +0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFC,0XFF, +0XFF,0XFF,0XFF,0XFF,0X00,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFC,0X7F, +0XFF,0XFF,0XFF,0XFF,0XAB,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFC,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X7F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X1B,0X00,0X00,0X00,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X07,0XFF,0XE8,0X00,0X00,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X80,0X00,0X00,0X5F,0XFE,0X00,0X00,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X07,0XFF,0X80,0X00,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X80,0X00,0X07,0XFF,0XF0,0X00,0X00,0X7F, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X07,0XFF,0XFF,0X00,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X8F,0XFF,0XFF,0XE8,0X00,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFD,0XFF,0XFF,0XFF,0XFF,0XAA,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0X9F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0X8F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XE5,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XF3,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XC1,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XC0,0X01,0XF7,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0X80,0X00,0XC3,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X3F,0XFF,0XFF,0X37,0XFF,0XFF,0X7F, +0X80,0X00,0X01,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X75, +0X00,0X00,0X01,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40, +0X00,0X00,0X00,0X7F,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X20, +0X00,0X00,0X00,0X0F,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40, +0X00,0X00,0X00,0X07,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X60, +0X00,0X00,0X00,0X03,0XF0,0X00,0X00,0X2D,0X30,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X00,0X00,0X00,0X03,0XE0,0X00,0X0B,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X00,0X00,0X00,0X03,0XC0,0X0B,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X20, +0X00,0X00,0X00,0X03,0X83,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X60, +0X00,0X00,0X00,0X03,0XEF,0XFF,0X40,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X60, +0X00,0X00,0X00,0X00,0XF8,0XD0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X00,0X00,0X00,0X00,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40, +0X00,0X00,0X00,0X00,0X78,0X00,0X00,0X22,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X60, +0X00,0X00,0X00,0X00,0XF8,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X60, +0X00,0X00,0X00,0X00,0XFE,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X00,0X00,0X00,0X03,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40, +0X00,0X00,0X00,0X03,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X60, +0X00,0X00,0X00,0X0F,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X60, +0X00,0X00,0X00,0X0F,0XFD,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X80,0X00,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XC0,0X00,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XE0,0X00,0X00,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XF0,0X00,0X20,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFE,0XCB,0XFB,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XA0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0X80, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X02,0XFF,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X00,0X2A,0XAF,0XFF,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XAA,0XAA,0XAF,0XFF,0XFF,0XFE,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00, +0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFC,0X00,0X00,0X00,0X00, +}; diff --git a/main/img3.h b/main/img3.h new file mode 100644 index 0000000..af57b05 --- /dev/null +++ b/main/img3.h @@ -0,0 +1,298 @@ +const unsigned char gImage_img3[4736] = { /* 0X01,0X01,0X28,0X01,0X80,0X00, */ +0X20,0XC4,0X34,0X40,0X44,0X40,0X00,0X00,0X0F,0XFF,0XCC,0XAC,0X00,0X00,0X00,0X00, +0X9E,0X3B,0X8B,0XBF,0XBB,0X10,0X00,0X00,0X03,0XF0,0X37,0X50,0X00,0X00,0X00,0X00, +0X61,0X84,0X70,0X40,0X44,0XC0,0X00,0X00,0X0F,0XEF,0XC8,0XA8,0X00,0X00,0X00,0X00, +0X0A,0X72,0X8F,0X15,0X12,0X00,0X00,0X00,0X03,0XFE,0XBF,0X44,0X00,0X00,0X00,0X00, +0XB4,0X8D,0X60,0XEA,0XE9,0X00,0X00,0X00,0X0F,0XD3,0XE0,0XB2,0X00,0X00,0X00,0X00, +0X4B,0X32,0X1D,0X14,0X16,0XC0,0X00,0X00,0X03,0XEE,0X9F,0X48,0X00,0X00,0X00,0X00, +0X24,0XC9,0XA2,0XC3,0XC8,0X00,0X00,0X00,0X0F,0XFB,0X60,0XA0,0X00,0X00,0X00,0X00, +0X91,0X16,0X4C,0X3C,0X33,0X00,0X00,0X00,0X03,0XFF,0XDE,0X58,0X00,0X00,0X00,0X00, +0X6E,0XE9,0XB3,0XC3,0X4C,0X00,0X00,0X00,0X0F,0XFD,0X63,0XA0,0X00,0X00,0X00,0X00, +0X00,0X04,0X4C,0X28,0X92,0X80,0X00,0X00,0X07,0XF2,0X9D,0X4C,0X00,0X00,0X00,0X00, +0XB7,0XFB,0X23,0X56,0X69,0X00,0X00,0X00,0X0B,0XFF,0XF2,0XB0,0X00,0X00,0X00,0X00, +0X48,0X04,0XD8,0XA9,0X94,0X00,0X00,0X00,0X0F,0XFA,0XCF,0X44,0X00,0X00,0X00,0X00, +0X23,0X6A,0X27,0X12,0X42,0X00,0X00,0X00,0X03,0XFF,0XBD,0XF0,0X00,0X00,0X00,0X00, +0X9C,0X95,0XC8,0XED,0X38,0X00,0X00,0X00,0X0F,0XFF,0XF2,0X00,0X00,0X00,0X00,0X00, +0X61,0X2A,0X35,0X12,0XC4,0X00,0X00,0X00,0X0F,0XFF,0X4F,0XE8,0X00,0X00,0X00,0X00, +0X0E,0XD1,0X4A,0XC9,0X30,0X00,0X00,0X00,0X03,0XFF,0XF8,0X30,0X00,0X00,0X00,0X00, +0XB1,0X2E,0XA4,0X36,0X8A,0X00,0X00,0X00,0X0F,0XFF,0X2F,0XDC,0X00,0X00,0X00,0X00, +0X4A,0XD1,0X5B,0XC9,0X70,0X00,0X00,0X00,0X0B,0XFF,0XF2,0XA0,0X00,0X00,0X00,0X00, +0X14,0X0C,0XA4,0X24,0X8C,0X00,0X00,0X00,0X0F,0XFF,0XAF,0XD0,0X00,0X00,0X00,0X00, +0XEB,0XF3,0X13,0X5B,0X60,0X00,0X00,0X00,0X0B,0XFF,0XDA,0X28,0X00,0X00,0X00,0X00, +0X04,0X0C,0XEC,0XA4,0X1A,0X00,0X00,0X00,0X0F,0XFF,0XED,0XD0,0X00,0X00,0X00,0X00, +0XB3,0XD3,0X13,0X13,0XE0,0X00,0X00,0X00,0X0B,0XFF,0X33,0X28,0X00,0X00,0X00,0X00, +0X48,0X28,0XA8,0XEC,0X0C,0X00,0X00,0X00,0X0F,0XF2,0XCC,0XD2,0X80,0X00,0X00,0X00, +0X36,0XD7,0X57,0X01,0X70,0X00,0X00,0X00,0X0B,0XFF,0XB3,0X2C,0X00,0X00,0X00,0X00, +0X89,0X28,0XA8,0XFE,0X85,0X00,0X00,0X00,0X0F,0XFF,0XDD,0XF2,0X00,0X00,0X00,0X00, +0X66,0XC6,0X46,0X01,0X38,0X00,0X00,0X00,0X0F,0XFF,0XF7,0X0A,0XC0,0X00,0X00,0X00, +0X11,0X39,0XB9,0XBA,0XC4,0X00,0X00,0X00,0X0F,0XFD,0X5D,0XF5,0X00,0X00,0X00,0X00, +0XAC,0XC6,0X46,0X45,0X33,0X00,0X00,0X00,0X0F,0XFA,0XA2,0X08,0X80,0X00,0X00,0X00, +0X52,0X31,0XB1,0XAA,0XC8,0X00,0X00,0X00,0X0F,0XFF,0XFD,0XB6,0X00,0X00,0X00,0X00, +0X0D,0XCC,0X4C,0X54,0X34,0X00,0X00,0X00,0X0F,0XFF,0XEE,0XEB,0X80,0X00,0X00,0X00, +0XE2,0X33,0X33,0X2B,0XC3,0X00,0X00,0X00,0X0F,0XFF,0XB3,0X14,0X00,0X00,0X00,0X00, +0X1C,0XCC,0XCC,0XC4,0X38,0X00,0X00,0X00,0X0F,0XFF,0XDA,0XEB,0X00,0X00,0X00,0X00, +0X43,0X33,0X33,0X3B,0X44,0X00,0X00,0X00,0X0F,0XFF,0XED,0X34,0X00,0X00,0X00,0X00, +0XB4,0XCC,0XCC,0XC4,0XAA,0X00,0X00,0X00,0X0F,0XFF,0XF6,0XCA,0X00,0X00,0X00,0X00, +0X0B,0X33,0X33,0X33,0X51,0X00,0X00,0X00,0X0F,0XFF,0XEA,0XB5,0X80,0X00,0X00,0X00, +0XF0,0XCC,0XCC,0XCC,0XAC,0X80,0X00,0X00,0X0F,0XFF,0XFD,0X48,0X00,0X00,0X00,0X00, +0X0F,0X33,0X33,0X33,0X12,0X00,0X00,0X00,0X0F,0XFF,0XEE,0XB6,0XC0,0X00,0X00,0X00, +0X50,0XCC,0XCC,0XCC,0XE8,0X80,0X00,0X00,0X0F,0XFB,0XFD,0X40,0X00,0X00,0X00,0X00, +0XAB,0X33,0X33,0X33,0X16,0X00,0X00,0X00,0X0F,0XFC,0XD6,0XBB,0X00,0X00,0X00,0X00, +0X14,0XCC,0XCC,0XCC,0XC9,0X00,0X00,0X00,0X0F,0XFF,0XBB,0X44,0X80,0X00,0X00,0X00, +0XCA,0X32,0X33,0X33,0X32,0X80,0X00,0X00,0X0F,0XFF,0XE5,0XB0,0X00,0X00,0X00,0X00, +0X35,0XCD,0XCC,0XCC,0XCC,0X40,0X00,0X00,0X0F,0XFF,0XFE,0X48,0X00,0X00,0X00,0X00, +0X82,0X32,0X33,0X33,0X33,0X00,0X00,0X00,0X0F,0XFF,0XF3,0XB6,0X00,0X00,0X00,0X00, +0X7C,0XCD,0XCC,0XCC,0XCC,0X80,0X00,0X00,0X0F,0XFF,0XFF,0X40,0X00,0X00,0X00,0X00, +0X83,0X22,0X33,0X33,0X32,0X40,0X00,0X00,0X0F,0XFF,0XFD,0X1A,0X00,0X00,0X00,0X00, +0X2C,0XDD,0XCC,0XCC,0XCD,0X00,0X00,0X00,0X0F,0XFF,0XE4,0XE4,0X00,0X00,0X00,0X00, +0XD3,0X22,0X33,0X33,0X32,0X80,0X00,0X00,0X0F,0XFF,0XDA,0X12,0X00,0X00,0X00,0X00, +0X28,0XD9,0XCC,0XCC,0XCC,0X40,0X00,0X00,0X0F,0XFF,0XAB,0XA8,0X00,0X00,0X00,0X00, +0X97,0X26,0X23,0X33,0X33,0X20,0X00,0X00,0X0F,0XFC,0XFC,0XD4,0X00,0X00,0X00,0X00, +0X48,0XD9,0XDC,0XCC,0XCC,0X80,0X00,0X00,0X0F,0XFF,0XFF,0X4A,0X00,0X00,0X00,0X00, +0XB7,0X26,0X23,0X33,0X33,0X40,0X00,0X00,0X07,0XFF,0XF3,0X21,0X00,0X00,0X00,0X00, +0X00,0XD9,0XDC,0XCC,0XCC,0X20,0X00,0X00,0X0F,0XFF,0XFC,0XBC,0X00,0X00,0X00,0X00, +0XFF,0X26,0X23,0X33,0X33,0X90,0X00,0X00,0X0B,0XFF,0XC2,0XC1,0X00,0X00,0X00,0X00, +0X00,0XD9,0XDC,0XCC,0XCC,0X48,0X00,0X00,0X0F,0XFF,0XFF,0X5C,0X00,0X00,0X00,0X00, +0XB7,0X26,0X23,0X33,0X33,0X23,0X00,0X00,0X0F,0XFF,0XEB,0XA0,0X00,0X00,0X00,0X00, +0X48,0XD9,0XDC,0XCC,0XCC,0XD0,0X00,0X00,0X03,0XFF,0X15,0X5A,0X00,0X00,0X00,0X00, +0XB7,0X26,0X23,0X33,0X33,0X0C,0X80,0X00,0X0F,0XFF,0XEA,0X84,0X00,0X00,0X00,0X00, +0X00,0X99,0XDC,0XCC,0XCC,0XE2,0X40,0X00,0X0F,0XFF,0X3B,0X7A,0X00,0X00,0X00,0X00, +0XFF,0X66,0X23,0X33,0X33,0X19,0X00,0X00,0X03,0XFE,0XDE,0X84,0X00,0X00,0X00,0X00, +0X00,0X99,0XDC,0XCC,0XCC,0XC4,0XB0,0X00,0X0F,0XFF,0XEB,0XFB,0X00,0X00,0X00,0X00, +0XB7,0X66,0X23,0X33,0X33,0X33,0X40,0X00,0X0F,0XFF,0XF4,0X10,0X00,0X00,0X00,0X00, +0X48,0X99,0XDC,0XCC,0XCC,0XCC,0X00,0X00,0X03,0XFF,0XFB,0XCC,0X00,0X00,0X00,0X00, +0XB6,0X66,0X23,0X33,0X33,0X21,0XB0,0X00,0X0F,0XFF,0XDC,0XA3,0X00,0X00,0X00,0X00, +0X01,0X99,0XDC,0XCC,0XCC,0XDE,0X40,0X00,0X0F,0XFA,0XF7,0X7C,0X00,0X00,0X00,0X00, +0XFE,0X66,0X23,0X33,0X33,0X21,0X10,0X00,0X07,0XFF,0X3D,0X90,0X00,0X00,0X00,0X00, +0X01,0X99,0XDC,0XCC,0XCC,0XCC,0XC8,0X00,0X0B,0XFF,0XCF,0XC5,0X00,0X00,0X00,0X00, +0XB6,0X66,0X23,0X33,0X33,0X32,0X24,0X00,0X0F,0XFF,0XF1,0X40,0X00,0X00,0X00,0X00, +0X49,0X99,0XDC,0XCC,0XCC,0XC5,0X90,0X00,0X07,0XFF,0X2E,0X20,0X00,0X00,0X00,0X00, +0XB4,0X66,0X23,0X33,0X33,0X3A,0X4A,0X00,0X0F,0XFE,0XF9,0XC0,0X00,0X00,0X00,0X00, +0X0B,0X99,0XDC,0XCC,0XCC,0XC4,0XA4,0X00,0X17,0XFF,0XBF,0X24,0X00,0X00,0X00,0X00, +0XE4,0X66,0X23,0X33,0X33,0X33,0X11,0X00,0X0F,0XFF,0XC4,0XB8,0X00,0X00,0X00,0X00, +0X1B,0X99,0XDC,0XCC,0XCC,0XC8,0XC4,0X00,0X07,0XFF,0XFA,0X80,0X00,0X00,0X00,0X00, +0XA4,0X66,0X23,0X33,0X33,0X36,0X32,0X80,0X1F,0XFF,0XDA,0XFA,0X00,0X00,0X00,0X00, +0X5B,0X99,0XDC,0XCC,0XCC,0XC9,0X48,0X20,0X03,0XFF,0X3F,0X40,0X00,0X00,0X00,0X00, +0XA4,0X66,0X23,0X33,0X33,0X32,0X92,0XC8,0X0F,0XFF,0XFC,0XAE,0XC0,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCC,0X69,0X00,0X33,0XFD,0XF3,0XF1,0X00,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X33,0X04,0X34,0X8F,0XFF,0XDF,0X4C,0X60,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XC8,0XB2,0XC2,0X37,0XFF,0XED,0XF3,0X80,0X00,0X00,0X00, +0XAC,0X66,0X23,0X33,0X33,0X37,0X49,0X18,0XCB,0XFF,0XF6,0X0C,0X40,0X00,0X00,0X00, +0X53,0X99,0XDC,0XCC,0XCC,0XC8,0X24,0X43,0X3F,0XFF,0X2B,0XE3,0X20,0X00,0X00,0X00, +0XAC,0X66,0X23,0X33,0X33,0X33,0X93,0X28,0XCF,0XFC,0XFD,0X18,0X80,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCC,0X6C,0XD2,0X33,0XFF,0XD2,0XA2,0X00,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X31,0X01,0X09,0XEF,0XFF,0X6F,0XDC,0X00,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCE,0XB4,0X64,0X3F,0XFD,0XFE,0XE3,0X80,0X00,0X00,0X00, +0XAC,0X66,0X23,0X33,0X33,0X31,0X4B,0X12,0XFF,0XFA,0XFB,0X3C,0X40,0X00,0X00,0X00, +0X53,0X99,0XDC,0XCC,0XCC,0XCC,0X24,0XCD,0X3F,0XFF,0XFF,0XD2,0X00,0X00,0X00,0X00, +0XAC,0X66,0X23,0X33,0X33,0X33,0X91,0X20,0XFF,0XFF,0XFC,0XAF,0XC0,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCC,0X4C,0X56,0X3F,0XFF,0XBB,0XF5,0X00,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X31,0X33,0X09,0XFF,0XFF,0XFF,0XFC,0X30,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCE,0XC8,0XE2,0X2F,0XFF,0XEF,0X02,0X40,0X00,0X00,0X00, +0XAC,0X66,0X23,0X33,0X33,0X31,0X26,0X0C,0XF7,0XFF,0XFD,0XFA,0X80,0X00,0X00,0X00, +0X53,0X99,0XDC,0XCC,0XCC,0XCC,0X90,0XB3,0X3F,0XFF,0XEA,0XAD,0X48,0X00,0X00,0X00, +0XAC,0X66,0X23,0X33,0X33,0X32,0X4B,0X48,0XD7,0XFD,0XFD,0X52,0XB0,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCD,0X30,0X25,0X0D,0X40,0XFE,0XAD,0X40,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X32,0XCB,0X98,0X00,0X00,0XFB,0X52,0XA8,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCC,0X24,0X40,0X00,0X02,0X7D,0XCD,0X30,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X33,0X91,0X00,0X00,0X20,0X7E,0XEA,0XE8,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCC,0X4C,0X00,0X00,0X00,0X15,0X3F,0XB0,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X31,0X32,0X00,0X00,0X00,0X0E,0XD0,0X50,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCE,0XC8,0X00,0X00,0X00,0X0B,0X3D,0XF0,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X31,0X20,0X00,0X00,0X00,0X0C,0XCA,0X08,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCC,0X9C,0X00,0X00,0X00,0X03,0XE3,0XF0,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X33,0X40,0X00,0X00,0X00,0X00,0X7D,0X40,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCC,0XA8,0X00,0X00,0X00,0X00,0X00,0X70,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X32,0X50,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCD,0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X30,0X10,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XD0,0X00,0X00,0X00,0X00,0X00,0X35,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X30,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XC8,0X30,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X37,0X08,0X00,0X00,0X00,0X00,0X0C,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC8,0XA0,0X00,0X00,0X00,0X0C,0XB3,0X80,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X37,0X5C,0X00,0X00,0X00,0X3E,0XDC,0X40,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC8,0X80,0X00,0X00,0X00,0XCF,0XB7,0XB0,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X36,0X70,0X00,0X00,0X00,0XF3,0XDD,0X40,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC9,0X8C,0X00,0X00,0X23,0XFD,0X40,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X36,0X62,0X00,0X00,0X35,0X56,0XAB,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC9,0X19,0X00,0X00,0X02,0XBB,0XFC,0X20,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X36,0XA4,0X00,0X00,0X01,0XFF,0X10,0XC8,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC9,0X4B,0XA0,0X00,0X00,0X54,0XE3,0X20,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X36,0XB0,0X0C,0X3B,0X81,0XEB,0X0C,0XD0,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC9,0X0E,0XD2,0XCF,0XFC,0X7C,0X3F,0X48,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X36,0XE1,0X29,0X35,0XF1,0X10,0XD1,0X20,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC9,0X14,0X94,0X00,0X00,0X03,0X6F,0XD8,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X36,0XAB,0X40,0X00,0X00,0X03,0XD0,0X02,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC9,0X40,0X00,0X00,0X00,0X0D,0X6F,0XEC,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X36,0XBC,0X00,0X00,0X00,0X1A,0XDB,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC1,0X00,0X00,0X00,0X00,0X0F,0X2C,0XE0,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X26,0XD0,0X00,0X00,0X00,0X0D,0XF7,0X50,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC1,0X20,0X00,0X00,0X00,0X03,0X50,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X26,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC1,0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X20,0X40,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XC0,0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0X00,0X00,0X00,0X00,0X00,0X00,0X0D,0X70,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X20,0X80,0X00,0X00,0X00,0X00,0XBA,0X0C,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X00,0X00,0X00,0X00,0X02,0XED,0XF0,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0XA0,0X00,0X00,0X00,0X07,0X96,0X8C,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X40,0X00,0X00,0X00,0X04,0XFB,0X72,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0XA0,0X00,0X00,0X00,0X07,0X44,0XC4,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X00,0X00,0X00,0X00,0X0C,0XFB,0X35,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0XF0,0X00,0X01,0XA8,0X03,0X4D,0XD2,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X00,0X00,0X01,0X7F,0XBE,0XB2,0X00,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0XD0,0X00,0X00,0XF5,0XF3,0XCD,0XCF,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCF,0X28,0X00,0X03,0XFC,0X1E,0XB2,0X50,0X80,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X30,0XC0,0X00,0X00,0X00,0X0B,0X5F,0X2E,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCF,0X3C,0X00,0X00,0X00,0X1D,0XA0,0XB3,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0X82,0X80,0X00,0X00,0X0A,0XDF,0X4C,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X69,0X48,0X00,0X00,0X37,0X68,0XB3,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0X94,0X32,0X00,0XB2,0XBD,0XB7,0X4C,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCF,0X2B,0XCD,0X0D,0XFF,0XD6,0XC8,0XB2,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X30,0XC4,0X20,0XD6,0XCC,0XEB,0X3F,0X4C,0X80,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCF,0X32,0X97,0X2B,0XB3,0X3D,0XC5,0XB3,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0X89,0X48,0XD4,0XFF,0XD6,0X7A,0XCC,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X64,0XB2,0X2F,0X6C,0XEB,0XAD,0X32,0X80,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0X1A,0X4C,0XD2,0XB3,0X3D,0X56,0XCC,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCF,0XC1,0X13,0X2D,0XDE,0XD6,0XFB,0X32,0X00,0X00,0X00, +0XEC,0X66,0X23,0X33,0X33,0X30,0X2C,0XC8,0XD7,0X7B,0XAB,0X4D,0XCC,0X00,0X00,0X00, +0X13,0X99,0XDC,0XCC,0XCC,0XCF,0X53,0X26,0X28,0XCC,0XFD,0XB2,0X32,0X80,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0X88,0X91,0XD7,0X77,0X56,0XDD,0XCD,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X66,0X6C,0X2D,0XBD,0XBB,0X62,0X30,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0X11,0X02,0X92,0XD6,0XCD,0XBF,0X8E,0X80,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0XCA,0XB5,0X6F,0X6B,0X76,0XC8,0XE1,0X00,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X30,0X24,0X48,0X91,0XBC,0XBB,0X37,0X1C,0X00,0X00,0X00, +0X73,0X99,0XDC,0XCC,0XCC,0XCF,0X53,0X26,0X2E,0XD7,0XCD,0XEC,0XE3,0X00,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X30,0XA8,0X91,0XD3,0X6A,0X72,0X93,0X1C,0X80,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X06,0X4C,0X2D,0XBD,0XDD,0X6C,0XE2,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0XF0,0XB2,0XD2,0XD7,0X37,0XBB,0X1D,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X0B,0X04,0X2F,0X6C,0XD8,0XC4,0XE0,0X00,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X30,0X54,0XDB,0X51,0XB3,0X6F,0X3B,0X1E,0X00,0X00,0X00, +0X73,0X99,0XDC,0XCC,0XCC,0XCF,0XA2,0X20,0XAE,0XDD,0XB5,0XEC,0XE1,0X00,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X30,0X59,0X8D,0X13,0X76,0XDA,0X13,0X1E,0X80,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X04,0X32,0XEC,0XDB,0X2F,0XFE,0XE1,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0XF2,0XC8,0X13,0X2C,0XF1,0X41,0X14,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X09,0X27,0X6D,0XF7,0X8E,0XBF,0XEB,0X00,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X30,0XD6,0X90,0X92,0XDA,0X75,0X50,0X10,0X00,0X00,0X00, +0X73,0X99,0XDC,0XCC,0XCC,0XCF,0X20,0X4A,0X6F,0X2D,0XDB,0XAF,0XE8,0X00,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X30,0XDB,0X25,0X10,0XF7,0X34,0XF4,0X14,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X04,0X92,0XAF,0XB8,0XCF,0X0B,0XE0,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X30,0XF1,0X28,0X52,0XCF,0X72,0XF5,0X1A,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCF,0X0E,0X93,0X2D,0X7A,0X9D,0X5A,0XE4,0X80,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X30,0XD0,0X4C,0X92,0XAF,0XEA,0XA5,0X00,0X00,0X00,0X00, +0X73,0X99,0XDC,0XCC,0XCC,0XCF,0X2B,0X21,0X6F,0XD2,0X57,0X5A,0XF3,0X00,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X30,0XC4,0X94,0X10,0X7D,0XB9,0XAD,0X0C,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCE,0X3A,0X49,0XAF,0XD3,0X4E,0XF3,0X71,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X31,0X81,0XB0,0X51,0X2C,0XF5,0X0C,0X8A,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCE,0X6C,0X00,0X00,0XFB,0X2B,0XF7,0X74,0X80,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X31,0X92,0XC0,0X00,0X4E,0XD4,0X28,0X8B,0X00,0X00,0X00, +0X73,0X99,0XDC,0XCC,0XCC,0XCC,0X29,0X20,0X00,0X73,0X7F,0XD7,0X70,0X00,0X00,0X00, +0X8C,0X66,0X23,0X33,0X33,0X33,0XC4,0X40,0X00,0X5D,0X01,0X28,0X88,0X00,0X00,0X00, +0X33,0X99,0XDC,0XCC,0XCC,0XCC,0X33,0X30,0X00,0XB0,0X00,0X17,0X72,0X00,0X00,0X00, +0XCC,0X66,0X23,0X33,0X33,0X32,0X8C,0X80,0X01,0X50,0X00,0X28,0X89,0X00,0X00,0X00, +0X33,0X19,0XDC,0XCC,0XCC,0XCD,0X62,0X50,0X06,0XC0,0X00,0X17,0X64,0X00,0X00,0X00, +0X8C,0XE6,0X23,0X33,0X33,0X32,0X99,0X00,0X01,0X40,0X00,0X00,0X12,0X00,0X00,0X00, +0X73,0X19,0XDC,0XCC,0XCC,0XCC,0X44,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X8C,0XE6,0X23,0X33,0X33,0X33,0X31,0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X19,0XDC,0XCC,0XCC,0XCC,0XC0,0X40,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XC4,0XE6,0X23,0X33,0X33,0X33,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X3B,0X19,0XDC,0XCC,0XCC,0XCC,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X84,0XE6,0X23,0X33,0X33,0X32,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X73,0X19,0XDC,0XCC,0XCC,0XCD,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X8C,0XC6,0X23,0X33,0X33,0X32,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X33,0X39,0XDC,0XCC,0XCC,0XCC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XC4,0XC6,0X23,0X33,0X33,0X33,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X20,0X00,0X00, +0X3B,0X39,0XDC,0XCC,0XCC,0XCC,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X80,0X00,0X00, +0X84,0XC6,0X23,0X33,0X33,0X32,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0X40,0X00,0X00, +0X73,0X39,0XDC,0XCC,0XCC,0XCD,0X00,0X00,0X00,0X00,0X00,0X00,0X0C,0X20,0X00,0X00, +0X8C,0XC6,0X23,0X33,0X33,0X32,0X80,0X00,0X00,0X00,0X00,0X01,0X43,0X00,0X00,0X00, +0X33,0X39,0X9C,0XCC,0XCC,0XCC,0X00,0X00,0X00,0X00,0X00,0X06,0XBC,0X00,0X00,0X00, +0XCC,0XC6,0X63,0X33,0X33,0X33,0XA0,0X00,0X00,0X00,0X00,0X19,0X43,0X00,0X00,0X00, +0X32,0X39,0X9C,0XCC,0XCC,0XCC,0X50,0X00,0X00,0X00,0X00,0X06,0XBC,0X80,0X00,0X00, +0X8D,0XC6,0X43,0X33,0X33,0X32,0XAB,0X20,0X00,0X80,0X00,0X19,0X43,0X00,0X00,0X00, +0X52,0X39,0XBC,0XCC,0XCC,0XCD,0X10,0X80,0X00,0X00,0X00,0X06,0XBC,0X80,0X00,0X00, +0XAC,0XC6,0X42,0X33,0X33,0X32,0XCA,0X40,0X00,0X00,0X00,0X19,0X43,0X00,0X00,0X00, +0X13,0X39,0XBD,0XCC,0XCC,0XCC,0X25,0X20,0X00,0X03,0X1D,0X66,0XBC,0XC0,0X00,0X00, +0XCC,0XC6,0X42,0X33,0X33,0X33,0XD0,0X10,0X00,0X00,0XE1,0X99,0X43,0X00,0X00,0X00, +0X33,0X31,0XBD,0XCC,0XCC,0XCC,0X0A,0XC0,0X00,0X00,0X04,0X66,0XA8,0X00,0X00,0X00, +0X88,0XCE,0X42,0X33,0X33,0X31,0X65,0X10,0X08,0X00,0X01,0XB9,0X53,0XC0,0X00,0X00, +0X57,0X31,0XB9,0X8C,0XCC,0XCE,0X90,0X4B,0X27,0X80,0X00,0X46,0X80,0X00,0X00,0X00, +0XA8,0XCC,0X46,0X73,0X33,0X31,0X4D,0X24,0XD8,0X7A,0XF3,0XB9,0X6B,0X00,0X00,0X00, +0X13,0X33,0XB9,0X8C,0XCC,0XCC,0X30,0X90,0X07,0XAD,0X0C,0X46,0X90,0X80,0X00,0X00, +0XCC,0XCC,0X46,0X73,0X33,0X33,0XC4,0X43,0X38,0X56,0XF3,0XB9,0X6B,0X00,0X00,0X00, +0X33,0X33,0X31,0X8C,0XCC,0XCC,0X1B,0X2C,0XC7,0XA9,0X0D,0X46,0X84,0X40,0X00,0X00, +0X88,0XCC,0XCC,0X73,0X33,0X31,0X60,0X90,0X18,0XF6,0XF2,0XB9,0X71,0X00,0X00,0X00, +0X57,0X33,0X33,0X0C,0XCC,0XCE,0X8A,0X43,0X47,0X1B,0X0D,0X46,0X80,0X00,0X00,0X00, +0XA8,0XCC,0XCC,0XD3,0X33,0X31,0X35,0X2C,0X28,0XE4,0XF2,0XB9,0X50,0X00,0X00,0X00, +0X13,0X33,0X33,0X2C,0XCC,0XCC,0XC8,0X81,0X57,0X3B,0X0D,0X46,0XA8,0X00,0X00,0X00, +0XCC,0XCC,0XCC,0XD3,0X33,0X33,0X22,0X34,0X08,0XCC,0XF2,0XB9,0X54,0X00,0X00,0X00, +0X31,0X33,0X33,0X2C,0XCC,0XCC,0X9C,0XC0,0X37,0X73,0X4D,0X46,0XA0,0X00,0X00,0X00, +0X8E,0XCC,0XCC,0X93,0X33,0X31,0X43,0X00,0X00,0X1D,0X72,0XB9,0X5A,0X80,0X00,0X00, +0X61,0X33,0X33,0X6C,0XC8,0XCE,0XA8,0X00,0X00,0X01,0X0D,0X46,0XA4,0X00,0X00,0X00, +0X1C,0XCC,0XCC,0X93,0X37,0X30,0X52,0X00,0X00,0X00,0X06,0XB9,0X53,0X00,0X00,0X00, +0XA3,0X33,0X32,0X4C,0XC8,0XCF,0X0C,0X00,0X00,0X00,0X01,0X46,0XA8,0X80,0X00,0X00, +0X4C,0XCC,0XCD,0XB3,0X37,0X30,0XE0,0X00,0X00,0X00,0X06,0XB9,0X53,0X40,0X00,0X00, +0X32,0X31,0X32,0X4C,0XC0,0XCB,0X18,0X00,0X00,0X70,0X12,0XC6,0XAC,0X00,0X00,0X00, +0X85,0X8E,0XCD,0XA3,0X3F,0X34,0X40,0X00,0X00,0X00,0X03,0X39,0X51,0X00,0X00,0X00, +0X7A,0X71,0X10,0X58,0XC0,0X8B,0XA8,0X00,0X00,0X00,0X05,0XC6,0XAC,0X80,0X00,0X00, +0X04,0X8C,0XEF,0X27,0X37,0X70,0X10,0X00,0X00,0X00,0X00,0X79,0X52,0X00,0X00,0X00, +0XB3,0X33,0X10,0XD8,0X88,0X8F,0XC0,0X00,0X00,0X00,0X00,0X16,0XAD,0X00,0X00,0X00, +0X4C,0XCC,0XCF,0X07,0X77,0X30,0X30,0X00,0X00,0X00,0X00,0X01,0X00,0X00,0X00,0X00, +0X23,0X31,0X30,0XF0,0X80,0XCD,0X40,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X98,0XCE,0XCB,0X0D,0X3B,0X20,0X10,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X66,0X11,0X14,0XB2,0XC4,0X50,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X09,0XEC,0XEB,0X4D,0X3B,0XA0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XB2,0X13,0X10,0XB2,0XC4,0X40,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X4C,0XCC,0XCF,0X04,0X12,0XA0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X23,0X31,0X30,0XFB,0XED,0X40,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X94,0XCE,0XCD,0X04,0X12,0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X6A,0X11,0X22,0XB2,0XC9,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X05,0XEA,0X5D,0X4D,0X36,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XB2,0X15,0XA0,0XB2,0XC9,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X4C,0XCA,0X4F,0X0C,0X24,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X23,0X31,0X30,0XE3,0XDA,0XB8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X94,0XCE,0XCD,0X18,0X25,0X46,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X6A,0X11,0X12,0XA6,0X92,0XB1,0X30,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0X05,0XEA,0XE9,0X59,0X6D,0X0A,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, +0XB0,0X14,0X16,0X82,0X90,0XF4,0X68,0X00,0X00,0X00,0X00,0X00,0X0A,0X00,0X00,0X00, +0X4E,0XCB,0XC8,0X7C,0X4F,0X0B,0X10,0X00,0X00,0X00,0X00,0X00,0X20,0X00,0X00,0X00, +0X21,0X34,0X37,0X83,0XA0,0X50,0XA0,0X00,0X00,0X00,0X00,0X00,0X0A,0X00,0X00,0X00, +0X9A,0XC3,0X40,0X54,0X5B,0XAF,0X48,0X00,0X00,0X00,0X00,0X03,0XA4,0X00,0X00,0X00, +0X44,0X3C,0XBD,0X2B,0X24,0X50,0X20,0X00,0X00,0X60,0X0A,0X3C,0X40,0X00,0X00,0X00, +0X33,0X81,0X02,0XD0,0XCB,0X05,0X90,0X00,0X00,0X00,0X0B,0X81,0X00,0X00,0X00,0X00, +0X88,0X6E,0XED,0X0E,0X30,0XFA,0X48,0X00,0X00,0X00,0X04,0X68,0X00,0X00,0X00,0X00, +0X56,0X91,0X10,0XF1,0X8E,0X05,0X12,0X00,0X00,0X00,0X01,0X94,0X00,0X00,0X00,0X00, +0X21,0X2C,0XAE,0X0A,0X51,0XD0,0XCC,0X00,0X20,0X00,0X01,0X41,0X00,0X00,0X00,0X00, +0X9A,0XC3,0X41,0XD4,0XAA,0X2F,0X20,0XC0,0X0A,0X00,0X00,0X28,0X00,0X00,0X00,0X00, +0X44,0X38,0X3A,0X2B,0X55,0X40,0X4B,0X10,0X31,0XB0,0X6D,0X54,0X00,0X00,0X00,0X00, +0X33,0X47,0XC4,0XD0,0X00,0XBD,0X24,0X02,0X4E,0X4A,0X92,0X00,0X00,0X00,0X00,0X00, +0X88,0XA8,0X2B,0X0F,0XFF,0X02,0X90,0XC8,0X01,0XB5,0X6D,0XCB,0X00,0X00,0X00,0X00, +0X57,0X12,0X90,0XE0,0X00,0XEC,0X4B,0X22,0X96,0X4A,0X92,0X30,0X00,0X00,0X00,0X00, +0X20,0XED,0X6E,0X15,0X55,0X13,0X14,0X08,0X09,0XB5,0X6D,0X40,0X00,0X00,0X00,0X00, +0X9A,0X12,0X91,0XAA,0XAA,0XA8,0XC0,0XA2,0XA6,0X4A,0X90,0X88,0X00,0X00,0X00,0X00, +0X45,0X44,0X44,0X55,0X55,0X56,0X2A,0X08,0X49,0XB5,0X6F,0X70,0X00,0X00,0X00,0X00, +0X2A,0XBB,0X3B,0X00,0X00,0X01,0X40,0XA2,0X15,0X4A,0X90,0X00,0X00,0X00,0X00,0X00, +0X91,0X04,0XC4,0XFF,0XFF,0XFC,0XAA,0X09,0X44,0XB5,0X6A,0XC0,0X00,0X00,0X00,0X00, +0X4C,0XF3,0X33,0X00,0X00,0X03,0X00,0XA0,0X13,0X4A,0X95,0X20,0X00,0X00,0X00,0X00, +0X22,0X08,0X88,0XAA,0XAA,0XA8,0XAA,0X08,0X0A,0XB5,0X6A,0X88,0X00,0X00,0X00,0X00, +}; diff --git a/main/img_hacking.c b/main/img_hacking.c new file mode 100644 index 0000000..8ce5312 --- /dev/null +++ b/main/img_hacking.c @@ -0,0 +1,2382 @@ +#include + +/* + * original image found on https://www.flickr.com/photos/adulau/9464930917/ + * (Alexandre Dulaunoy) + * + * "Feel free to reuse, use and abuse my pictures under the CC-SA license, + * the GNU Free Documentation License or the GNU General Public License." + * + * image is cropped and resized to 296x128 pixels. + */ + +const uint8_t img_hacking[37888] = { + 0x83,0x8e,0x85,0x78,0x69,0x5f,0x4c,0x3e,0x33,0x37,0x3c,0x41,0x40,0x43,0x52,0x5e, + 0x6b,0x75,0x77,0x72,0x6c,0x6b,0x6d,0x75,0x89,0x90,0x8f,0x8d,0x7c,0x7d,0x7c,0x74, + 0x6b,0x61,0x5b,0x59,0x5a,0x5d,0x5e,0x5d,0x5c,0x5b,0x58,0x58,0x5a,0x5d,0x5d,0x5f, + 0x60,0x60,0x5f,0x66,0x70,0x72,0x6e,0x69,0x62,0x57,0x4e,0x4f,0x53,0x5a,0x5f,0x6d, + 0x8d,0xa7,0xad,0xa9,0xa0,0x8e,0x65,0x3f,0x32,0x2e,0x2d,0x2d,0x30,0x33,0x36,0x3c, + 0x3c,0x3b,0x3a,0x38,0x3a,0x3e,0x43,0x46,0x46,0x47,0x49,0x4c,0x4e,0x4d,0x4f,0x4c, + 0x48,0x44,0x42,0x43,0x41,0x3d,0x3b,0x38,0x33,0x2f,0x34,0x39,0x3d,0x44,0x44,0x3b, + 0x31,0x2f,0x2d,0x34,0x3c,0x3c,0x3a,0x37,0x2e,0x26,0x28,0x2b,0x30,0x3a,0x43,0x49, + 0x88,0x8d,0x86,0x78,0x6a,0x62,0x50,0x3d,0x31,0x32,0x3b,0x3e,0x43,0x41,0x44,0x51, + 0x5d,0x69,0x72,0x75,0x76,0x71,0x71,0x80,0x97,0x9c,0x99,0x93,0x7f,0x7f,0x7e,0x74, + 0x6a,0x60,0x58,0x5a,0x5e,0x61,0x63,0x65,0x63,0x62,0x60,0x5f,0x5f,0x62,0x63,0x65, + 0x65,0x65,0x62,0x62,0x68,0x71,0x72,0x6c,0x65,0x5e,0x55,0x52,0x56,0x5b,0x61,0x6d, + 0x78,0x8f,0xac,0xb0,0xab,0x9e,0x87,0x59,0x37,0x2e,0x31,0x2e,0x2f,0x34,0x36,0x3c, + 0x3f,0x40,0x3f,0x3e,0x3e,0x42,0x47,0x49,0x4b,0x4c,0x4c,0x4d,0x4d,0x50,0x4f,0x4d, + 0x4b,0x47,0x45,0x42,0x3f,0x3d,0x3c,0x38,0x32,0x31,0x32,0x35,0x37,0x39,0x39,0x33, + 0x2e,0x30,0x34,0x39,0x3d,0x3c,0x36,0x2e,0x2a,0x27,0x25,0x2d,0x35,0x3b,0x3f,0x4a, + 0x86,0x89,0x84,0x76,0x6b,0x65,0x50,0x3f,0x30,0x2f,0x38,0x3c,0x41,0x44,0x40,0x42, + 0x4f,0x5a,0x67,0x72,0x77,0x7a,0x75,0x7e,0x9e,0x9d,0x99,0x92,0x7f,0x7e,0x7b,0x72, + 0x64,0x5d,0x58,0x59,0x5d,0x63,0x67,0x69,0x68,0x67,0x66,0x65,0x64,0x66,0x67,0x68, + 0x68,0x68,0x66,0x67,0x66,0x69,0x72,0x73,0x71,0x6a,0x5a,0x54,0x59,0x5d,0x65,0x6b, + 0x7a,0x86,0x9a,0xaf,0xb1,0xa8,0x97,0x7e,0x4d,0x38,0x36,0x37,0x37,0x39,0x3b,0x40, + 0x43,0x45,0x42,0x42,0x43,0x48,0x4b,0x4c,0x4b,0x4a,0x47,0x48,0x4a,0x4c,0x4b,0x4d, + 0x50,0x48,0x42,0x3c,0x38,0x35,0x34,0x31,0x2d,0x2b,0x2e,0x2f,0x32,0x32,0x33,0x31, + 0x30,0x33,0x38,0x3e,0x3d,0x3b,0x38,0x32,0x2f,0x2c,0x29,0x2f,0x38,0x3d,0x42,0x49, + 0x80,0x89,0x85,0x79,0x75,0x68,0x53,0x3c,0x31,0x30,0x35,0x3a,0x40,0x42,0x40,0x40, + 0x43,0x4f,0x5c,0x6c,0x75,0x78,0x76,0x7f,0x94,0x92,0x90,0x8b,0x7d,0x7b,0x77,0x6f, + 0x69,0x5e,0x5a,0x58,0x5e,0x63,0x67,0x6c,0x6e,0x6d,0x6a,0x6b,0x6a,0x6b,0x6c,0x6c, + 0x6d,0x6d,0x6b,0x6b,0x69,0x69,0x74,0x7c,0x7b,0x73,0x6d,0x5b,0x5b,0x60,0x66,0x72, + 0x80,0x89,0x90,0xa2,0xb2,0xaf,0xa6,0x92,0x70,0x45,0x3d,0x3f,0x3f,0x41,0x43,0x49, + 0x48,0x4a,0x48,0x47,0x48,0x4b,0x4b,0x4a,0x49,0x49,0x45,0x45,0x44,0x49,0x4a,0x4a, + 0x4d,0x4a,0x40,0x36,0x30,0x2a,0x27,0x24,0x23,0x24,0x28,0x2d,0x32,0x34,0x30,0x33, + 0x37,0x3b,0x41,0x47,0x48,0x49,0x47,0x44,0x41,0x3f,0x3e,0x3f,0x41,0x41,0x46,0x4a, + 0x85,0x8b,0x82,0x7b,0x73,0x65,0x4d,0x38,0x30,0x30,0x33,0x37,0x3d,0x41,0x3f,0x40, + 0x3f,0x45,0x52,0x62,0x6e,0x72,0x75,0x79,0x88,0x8a,0x8a,0x83,0x7d,0x7c,0x77,0x73, + 0x6f,0x63,0x5e,0x5b,0x5b,0x5f,0x6a,0x7d,0x86,0x80,0x73,0x70,0x6f,0x6f,0x6e,0x6e, + 0x70,0x70,0x6d,0x6d,0x6e,0x6d,0x78,0x83,0x7c,0x76,0x72,0x64,0x5e,0x62,0x6a,0x76, + 0x83,0x8f,0x95,0x97,0xac,0xb4,0xae,0x9e,0x86,0x5d,0x45,0x46,0x47,0x47,0x48,0x4d, + 0x4b,0x4b,0x4b,0x4b,0x4d,0x4c,0x4c,0x4a,0x48,0x48,0x46,0x44,0x43,0x45,0x48,0x4a, + 0x48,0x44,0x3b,0x2f,0x26,0x20,0x1d,0x1b,0x1f,0x27,0x2f,0x34,0x38,0x38,0x38,0x35, + 0x37,0x3a,0x3f,0x43,0x46,0x45,0x47,0x4b,0x4c,0x4b,0x4a,0x48,0x48,0x48,0x4b,0x4d, + 0x89,0x8b,0x87,0x7f,0x77,0x66,0x50,0x39,0x30,0x2f,0x33,0x37,0x39,0x3e,0x40,0x3b, + 0x3f,0x44,0x4a,0x52,0x5f,0x68,0x71,0x75,0x7e,0x8d,0x94,0x8f,0x82,0x7d,0x7b,0x7a, + 0x75,0x6c,0x65,0x5d,0x58,0x62,0x86,0x9e,0xa6,0xa8,0x98,0x7b,0x72,0x70,0x6f,0x6f, + 0x6f,0x70,0x6f,0x6e,0x6f,0x70,0x71,0x84,0x80,0x7a,0x76,0x6d,0x67,0x67,0x6f,0x7c, + 0x89,0x94,0x97,0x96,0xa1,0xb4,0xaf,0xa3,0x8e,0x71,0x4a,0x47,0x4b,0x4b,0x4d,0x51, + 0x51,0x4f,0x4c,0x4d,0x4e,0x4e,0x4d,0x4d,0x49,0x47,0x48,0x49,0x48,0x48,0x48,0x4c, + 0x4c,0x4a,0x41,0x36,0x2f,0x26,0x23,0x26,0x2d,0x32,0x35,0x38,0x37,0x35,0x35,0x34, + 0x36,0x39,0x3a,0x3a,0x3a,0x3b,0x43,0x4c,0x4f,0x4f,0x4b,0x45,0x45,0x49,0x4c,0x4d, + 0x82,0x83,0x86,0x83,0x7b,0x6d,0x53,0x3d,0x31,0x30,0x2f,0x33,0x36,0x3c,0x3d,0x3e, + 0x3e,0x3f,0x43,0x43,0x4c,0x5c,0x66,0x6e,0x8f,0xa9,0xa5,0xa2,0x8a,0x7f,0x80,0x80, + 0x7d,0x75,0x68,0x5f,0x59,0x6d,0x92,0x9e,0xa1,0xa2,0x93,0x7d,0x6e,0x6a,0x6a,0x6a, + 0x68,0x69,0x68,0x68,0x69,0x6d,0x6d,0x71,0x76,0x78,0x76,0x70,0x6e,0x6f,0x73,0x81, + 0x8d,0x95,0x9a,0x9a,0x9a,0xa7,0xb3,0xab,0x9e,0x8c,0x6b,0x5d,0x55,0x4f,0x4f,0x53, + 0x52,0x4e,0x4e,0x4e,0x4f,0x4e,0x4e,0x4b,0x4b,0x48,0x4a,0x4c,0x49,0x47,0x48,0x47, + 0x47,0x47,0x42,0x3b,0x39,0x37,0x36,0x38,0x3e,0x3e,0x3b,0x37,0x37,0x34,0x31,0x30, + 0x34,0x35,0x33,0x31,0x2e,0x36,0x3f,0x47,0x46,0x43,0x3d,0x38,0x37,0x38,0x3f,0x43, + 0x74,0x7b,0x83,0x85,0x80,0x72,0x59,0x3e,0x32,0x32,0x31,0x33,0x35,0x39,0x3d,0x3d, + 0x3c,0x3e,0x3d,0x3f,0x41,0x49,0x56,0x67,0x9b,0xa8,0xab,0xa8,0x93,0x86,0x85,0x84, + 0x83,0x76,0x69,0x60,0x59,0x62,0x85,0x9a,0x99,0x8d,0x7c,0x6b,0x63,0x60,0x5f,0x5e, + 0x5e,0x5d,0x5f,0x60,0x62,0x64,0x65,0x69,0x6d,0x75,0x78,0x77,0x77,0x78,0x7c,0x87, + 0x91,0x97,0x9c,0x9d,0x9a,0xa1,0xb3,0xb4,0xb2,0xb7,0xb1,0x97,0x7a,0x5f,0x54,0x52, + 0x50,0x4f,0x4d,0x4e,0x4f,0x4e,0x4e,0x4b,0x4b,0x48,0x4a,0x4b,0x48,0x44,0x46,0x44, + 0x42,0x45,0x42,0x3e,0x3e,0x3c,0x3b,0x3c,0x41,0x3f,0x3e,0x3e,0x36,0x31,0x2c,0x2b, + 0x2b,0x2b,0x29,0x27,0x2b,0x35,0x3d,0x45,0x44,0x3c,0x32,0x2e,0x2c,0x34,0x3a,0x3b, + 0x68,0x78,0x82,0x83,0x7c,0x71,0x5b,0x3d,0x2e,0x31,0x31,0x30,0x32,0x36,0x3c,0x3f, + 0x39,0x3b,0x3a,0x3c,0x3f,0x3f,0x42,0x54,0x8f,0x9b,0xa2,0x9f,0x98,0x93,0x8e,0x87, + 0x7f,0x71,0x67,0x5e,0x56,0x54,0x57,0x62,0x66,0x63,0x5c,0x55,0x55,0x54,0x55,0x54, + 0x54,0x54,0x57,0x59,0x5b,0x5e,0x5f,0x63,0x69,0x73,0x78,0x7c,0x7e,0x80,0x84,0x8b, + 0x95,0x98,0x9d,0x9b,0x9d,0x9d,0xb2,0xb8,0xc2,0xd1,0xcb,0xb7,0x9f,0x75,0x53,0x4f, + 0x4e,0x4d,0x4c,0x4c,0x4e,0x4e,0x4a,0x49,0x4b,0x48,0x49,0x4b,0x47,0x43,0x45,0x45, + 0x46,0x45,0x46,0x45,0x41,0x3f,0x3e,0x3e,0x3c,0x3b,0x39,0x38,0x33,0x2f,0x2c,0x2e, + 0x2d,0x2b,0x2c,0x2e,0x34,0x3b,0x44,0x4d,0x47,0x3e,0x35,0x2d,0x2c,0x33,0x39,0x38, + 0x6a,0x77,0x80,0x82,0x7d,0x74,0x60,0x43,0x31,0x30,0x33,0x30,0x30,0x34,0x39,0x3c, + 0x39,0x39,0x39,0x3a,0x3d,0x3e,0x3b,0x3a,0x5a,0x74,0x82,0x9c,0xa2,0x9e,0x97,0x88, + 0x79,0x6d,0x63,0x5c,0x57,0x53,0x51,0x4f,0x4d,0x4d,0x4d,0x4d,0x4d,0x4e,0x4f,0x50, + 0x51,0x54,0x55,0x59,0x5b,0x5f,0x61,0x64,0x68,0x71,0x7b,0x80,0x83,0x85,0x8a,0x8f, + 0x96,0x9b,0x9e,0x9d,0x9c,0x9d,0xa8,0xb4,0xce,0xdb,0xdb,0xcd,0xb4,0x7d,0x4e,0x4a, + 0x49,0x4a,0x4a,0x4b,0x50,0x4f,0x4e,0x4d,0x4e,0x4a,0x48,0x46,0x42,0x3e,0x3f,0x40, + 0x42,0x41,0x41,0x3f,0x3f,0x3d,0x3d,0x3d,0x3f,0x3c,0x3b,0x3c,0x3f,0x3a,0x38,0x35, + 0x36,0x35,0x36,0x38,0x3f,0x44,0x4e,0x56,0x55,0x49,0x40,0x38,0x35,0x3b,0x40,0x3f, + 0x72,0x7e,0x84,0x83,0x7c,0x73,0x62,0x46,0x31,0x2f,0x35,0x32,0x2f,0x31,0x34,0x3b, + 0x3b,0x38,0x38,0x38,0x3c,0x3c,0x3b,0x34,0x34,0x48,0x6e,0x8f,0x96,0x9c,0x95,0x7e, + 0x72,0x67,0x61,0x5a,0x57,0x54,0x52,0x50,0x4f,0x4d,0x4d,0x4a,0x4b,0x4e,0x50,0x53, + 0x55,0x59,0x5c,0x5f,0x63,0x66,0x67,0x6a,0x6f,0x73,0x7d,0x84,0x86,0x89,0x8e,0x93, + 0x99,0x9d,0x9e,0x9d,0x9f,0x9f,0x9b,0xa8,0xcc,0xde,0xde,0xd3,0xb5,0x79,0x47,0x43, + 0x43,0x45,0x4a,0x4d,0x52,0x52,0x53,0x50,0x52,0x4f,0x4b,0x46,0x42,0x3e,0x3e,0x3f, + 0x3e,0x3f,0x3f,0x3f,0x3d,0x3b,0x39,0x35,0x34,0x38,0x3d,0x42,0x47,0x45,0x42,0x3f, + 0x3e,0x3e,0x3f,0x42,0x45,0x49,0x52,0x59,0x58,0x4d,0x44,0x38,0x34,0x3b,0x40,0x40, + 0x7a,0x85,0x8a,0x86,0x7e,0x76,0x63,0x47,0x30,0x2e,0x33,0x34,0x2e,0x2f,0x31,0x36, + 0x3a,0x38,0x37,0x37,0x38,0x3a,0x3a,0x38,0x32,0x34,0x58,0x70,0x84,0x8c,0x7e,0x75, + 0x6f,0x65,0x61,0x5a,0x57,0x54,0x53,0x52,0x51,0x4f,0x4f,0x4f,0x51,0x53,0x57,0x59, + 0x5d,0x61,0x63,0x69,0x6e,0x72,0x75,0x78,0x7a,0x7d,0x80,0x88,0x8b,0x8d,0x92,0x97, + 0x9a,0x9d,0x9f,0x9f,0xa0,0x9e,0x99,0xa5,0xcc,0xdd,0xdc,0xd4,0xb3,0x74,0x43,0x40, + 0x41,0x44,0x49,0x4e,0x51,0x54,0x56,0x54,0x53,0x50,0x4b,0x46,0x40,0x3c,0x3c,0x3c, + 0x3b,0x39,0x39,0x36,0x35,0x35,0x33,0x2f,0x30,0x36,0x3a,0x3d,0x42,0x43,0x45,0x45, + 0x42,0x42,0x41,0x41,0x45,0x4b,0x4e,0x51,0x50,0x48,0x41,0x37,0x33,0x35,0x3b,0x3c, + 0x81,0x8d,0x8e,0x87,0x7e,0x71,0x5f,0x45,0x30,0x2d,0x33,0x34,0x31,0x2c,0x2f,0x31, + 0x36,0x38,0x35,0x36,0x36,0x37,0x3a,0x39,0x39,0x34,0x36,0x4b,0x5e,0x61,0x69,0x6f, + 0x6a,0x64,0x60,0x5b,0x57,0x55,0x56,0x56,0x54,0x53,0x53,0x57,0x58,0x5a,0x5d,0x5f, + 0x64,0x69,0x6c,0x75,0x7a,0x7f,0x83,0x86,0x87,0x88,0x8a,0x8d,0x8e,0x93,0x95,0x98, + 0x9b,0x9e,0x9f,0x9f,0x9f,0x9e,0x9a,0xa7,0xcd,0xde,0xdd,0xd2,0xae,0x6a,0x3e,0x3c, + 0x3e,0x42,0x47,0x4b,0x50,0x54,0x56,0x55,0x53,0x51,0x4b,0x45,0x3e,0x3a,0x38,0x39, + 0x3b,0x37,0x34,0x2f,0x2b,0x2a,0x2c,0x2c,0x2e,0x32,0x34,0x36,0x3a,0x3e,0x3e,0x40, + 0x41,0x41,0x3f,0x3c,0x3f,0x45,0x46,0x47,0x45,0x40,0x38,0x35,0x2d,0x2d,0x37,0x3c, + 0x83,0x8f,0x8f,0x87,0x7e,0x76,0x63,0x49,0x35,0x2f,0x33,0x33,0x32,0x2d,0x2e,0x30, + 0x34,0x36,0x34,0x35,0x36,0x37,0x38,0x38,0x38,0x37,0x32,0x33,0x3e,0x4b,0x56,0x61, + 0x67,0x63,0x5e,0x5b,0x57,0x56,0x57,0x57,0x57,0x57,0x5a,0x5b,0x5e,0x61,0x66,0x6b, + 0x72,0x77,0x7b,0x81,0x85,0x89,0x8c,0x8d,0x8f,0x90,0x90,0x93,0x92,0x95,0x98,0x9a, + 0x9d,0x9e,0x9f,0x9f,0x9f,0x9c,0x9a,0xab,0xcf,0xdd,0xda,0xcd,0xa3,0x5d,0x3c,0x3b, + 0x3f,0x43,0x45,0x49,0x50,0x54,0x56,0x53,0x50,0x4e,0x46,0x41,0x3e,0x40,0x40,0x43, + 0x46,0x44,0x3d,0x3a,0x36,0x2d,0x27,0x27,0x2a,0x2b,0x2a,0x2b,0x2e,0x30,0x31,0x36, + 0x37,0x39,0x3a,0x37,0x34,0x38,0x3a,0x3c,0x3a,0x38,0x31,0x2c,0x26,0x2a,0x32,0x37, + 0x83,0x8c,0x8d,0x87,0x80,0x76,0x65,0x4a,0x36,0x2d,0x30,0x32,0x34,0x30,0x2d,0x30, + 0x30,0x36,0x36,0x33,0x35,0x35,0x35,0x37,0x37,0x37,0x37,0x34,0x33,0x39,0x45,0x51, + 0x58,0x60,0x60,0x5b,0x56,0x56,0x57,0x58,0x5a,0x5c,0x5e,0x61,0x69,0x6d,0x73,0x79, + 0x7d,0x83,0x86,0x8b,0x8e,0x91,0x91,0x92,0x94,0x95,0x95,0x98,0x99,0x99,0x9a,0x9c, + 0x9d,0x9d,0x9d,0x9d,0x9d,0x9c,0x96,0xac,0xce,0xd9,0xd5,0xc3,0x96,0x50,0x38,0x3b, + 0x41,0x45,0x46,0x49,0x4d,0x53,0x54,0x54,0x4f,0x48,0x3d,0x38,0x38,0x3f,0x45,0x4b, + 0x4d,0x4f,0x4b,0x45,0x3f,0x39,0x33,0x30,0x2e,0x2d,0x29,0x24,0x21,0x24,0x25,0x2f, + 0x32,0x36,0x35,0x36,0x38,0x37,0x32,0x31,0x33,0x31,0x2b,0x2a,0x24,0x27,0x30,0x34, + 0x80,0x88,0x89,0x84,0x7c,0x73,0x61,0x49,0x36,0x29,0x2c,0x30,0x33,0x32,0x2e,0x2f, + 0x31,0x34,0x35,0x33,0x34,0x33,0x32,0x34,0x35,0x35,0x35,0x33,0x33,0x2f,0x38,0x42, + 0x49,0x50,0x54,0x59,0x58,0x57,0x57,0x5a,0x5f,0x64,0x68,0x6e,0x75,0x7b,0x80,0x85, + 0x89,0x8c,0x8e,0x8f,0x93,0x91,0x92,0x93,0x97,0x97,0x98,0x9a,0x9b,0x9b,0x9b,0x9c, + 0x9c,0x9d,0x9d,0x9d,0x9d,0x9d,0x95,0xab,0xcd,0xd1,0xcb,0xb5,0x84,0x43,0x3b,0x3e, + 0x44,0x46,0x49,0x4c,0x4e,0x52,0x54,0x55,0x4f,0x45,0x3c,0x3a,0x39,0x3b,0x48,0x4f, + 0x54,0x57,0x56,0x50,0x46,0x41,0x3d,0x37,0x32,0x30,0x2b,0x26,0x23,0x22,0x25,0x2b, + 0x2f,0x37,0x39,0x39,0x3b,0x3a,0x35,0x30,0x32,0x2e,0x29,0x27,0x27,0x24,0x29,0x2f, + 0x7c,0x84,0x84,0x80,0x79,0x71,0x5d,0x48,0x36,0x28,0x28,0x2f,0x32,0x32,0x2d,0x2f, + 0x31,0x31,0x35,0x34,0x33,0x32,0x30,0x32,0x31,0x32,0x31,0x31,0x31,0x32,0x2f,0x33, + 0x39,0x42,0x47,0x4c,0x54,0x59,0x5e,0x62,0x6a,0x71,0x76,0x7c,0x82,0x87,0x89,0x8d, + 0x8f,0x91,0x91,0x91,0x92,0x93,0x94,0x94,0x98,0x99,0x9a,0x9c,0x9e,0x9e,0x9e,0x9e, + 0x9e,0x9d,0x9c,0x9c,0x9c,0x9c,0x96,0xb0,0xcc,0xc8,0xbc,0xa4,0x6d,0x3f,0x3f,0x44, + 0x48,0x4a,0x4c,0x4e,0x50,0x54,0x59,0x53,0x4d,0x44,0x3c,0x3a,0x3b,0x3e,0x49,0x51, + 0x55,0x58,0x59,0x55,0x4b,0x43,0x3f,0x3a,0x36,0x31,0x2a,0x25,0x24,0x24,0x27,0x2a, + 0x32,0x3b,0x3f,0x40,0x3b,0x3c,0x38,0x33,0x31,0x2b,0x28,0x26,0x25,0x23,0x23,0x2a, + 0x7a,0x83,0x86,0x81,0x7b,0x71,0x60,0x4a,0x35,0x28,0x25,0x2e,0x33,0x34,0x30,0x34, + 0x36,0x35,0x36,0x37,0x35,0x34,0x33,0x32,0x33,0x31,0x30,0x2f,0x2f,0x2f,0x30,0x31, + 0x30,0x31,0x38,0x41,0x4a,0x58,0x66,0x71,0x79,0x7f,0x83,0x87,0x89,0x8c,0x90,0x91, + 0x8f,0x8f,0x90,0x92,0x91,0x91,0x94,0x95,0x98,0x9b,0x9d,0x9e,0xa0,0xa0,0xa0,0xa1, + 0xa0,0x9f,0x9f,0x9f,0x9f,0x9a,0x9c,0xb9,0xbf,0xb8,0xa9,0x91,0x57,0x40,0x45,0x4b, + 0x4d,0x4e,0x51,0x52,0x52,0x52,0x52,0x52,0x4b,0x45,0x3f,0x3d,0x3b,0x41,0x49,0x51, + 0x55,0x57,0x55,0x52,0x4d,0x47,0x41,0x3a,0x38,0x32,0x2c,0x25,0x27,0x29,0x2e,0x34, + 0x3d,0x45,0x44,0x44,0x41,0x3f,0x3a,0x35,0x2e,0x2a,0x25,0x21,0x1f,0x21,0x23,0x28, + 0x78,0x82,0x89,0x8a,0x83,0x78,0x75,0x81,0x84,0x7b,0x76,0x77,0x6c,0x5a,0x50,0x51, + 0x53,0x54,0x52,0x52,0x52,0x52,0x52,0x52,0x4f,0x4d,0x49,0x46,0x43,0x40,0x3c,0x3a, + 0x37,0x35,0x34,0x38,0x41,0x52,0x64,0x77,0x85,0x8a,0x8c,0x8c,0x90,0x91,0x8f,0x93, + 0x97,0x9d,0xa3,0xa4,0x9f,0x99,0x96,0x96,0x9b,0x9d,0xa0,0xa0,0xa1,0xa2,0xa3,0xa3, + 0xa2,0xa0,0xa0,0x9f,0x9e,0x9b,0xa5,0xaf,0xaa,0xa3,0x96,0x71,0x4b,0x4b,0x4d,0x50, + 0x53,0x55,0x57,0x55,0x53,0x52,0x4e,0x4f,0x4b,0x48,0x44,0x3f,0x3e,0x43,0x47,0x4d, + 0x50,0x53,0x53,0x52,0x4d,0x4b,0x44,0x3e,0x35,0x31,0x2d,0x29,0x28,0x2c,0x35,0x3f, + 0x46,0x4b,0x49,0x44,0x43,0x3f,0x3a,0x37,0x33,0x2c,0x26,0x1f,0x1d,0x23,0x28,0x32, + 0x7a,0x84,0x89,0x8d,0x89,0x88,0xa2,0xb7,0xc4,0xbf,0xb2,0xa3,0x93,0x83,0x79,0x78, + 0x7a,0x7d,0x7c,0x7c,0x7e,0x7e,0x7f,0x7f,0x7d,0x7b,0x7c,0x79,0x76,0x73,0x71,0x6d, + 0x67,0x62,0x58,0x4d,0x4c,0x53,0x5d,0x70,0x81,0x8c,0x8f,0x8d,0x8f,0x90,0x9d,0xad, + 0xb8,0xbe,0xc2,0xc0,0xb3,0xa5,0x9c,0x99,0x9c,0x9d,0xa0,0x9f,0xa1,0xa5,0xa6,0xa5, + 0xa3,0xa1,0x9f,0x9e,0x9b,0x9c,0x9d,0x9b,0x92,0x89,0x77,0x50,0x4a,0x4d,0x51,0x54, + 0x58,0x5a,0x5b,0x5a,0x56,0x54,0x51,0x4d,0x4b,0x48,0x46,0x41,0x3f,0x43,0x45,0x46, + 0x47,0x4a,0x4c,0x4c,0x49,0x48,0x44,0x40,0x39,0x31,0x31,0x2e,0x2b,0x2e,0x39,0x44, + 0x48,0x4a,0x47,0x42,0x3f,0x39,0x3a,0x3a,0x39,0x33,0x2c,0x23,0x21,0x2a,0x2f,0x37, + 0x7c,0x83,0x88,0x8e,0x8a,0x90,0xad,0xc1,0xc8,0xbb,0xae,0xa1,0x93,0x89,0x87,0x8a, + 0x8c,0x8f,0x8f,0x92,0x94,0x94,0x96,0x96,0x96,0x94,0x94,0x95,0x96,0x93,0x90,0x90, + 0x8f,0x89,0x84,0x7d,0x76,0x6e,0x6e,0x72,0x7a,0x83,0x89,0x8f,0x92,0xa0,0xb5,0xca, + 0xd7,0xde,0xde,0xd4,0xc6,0xb1,0x9d,0x99,0x9b,0x9d,0x9e,0x9e,0xa4,0xa8,0xa9,0xa8, + 0xa6,0xa1,0x9d,0x9c,0x9a,0x97,0x8f,0x84,0x77,0x68,0x55,0x4c,0x4f,0x52,0x55,0x5a, + 0x5e,0x60,0x5f,0x5d,0x5a,0x57,0x55,0x52,0x4f,0x4d,0x49,0x46,0x45,0x46,0x46,0x44, + 0x42,0x40,0x43,0x45,0x45,0x42,0x42,0x41,0x3e,0x39,0x38,0x35,0x30,0x2e,0x35,0x3c, + 0x41,0x43,0x41,0x3e,0x36,0x34,0x36,0x3b,0x3d,0x3b,0x36,0x31,0x32,0x37,0x3a,0x3f, + 0x7d,0x81,0x86,0x8d,0x88,0x77,0x75,0x7c,0x7b,0x6f,0x70,0x74,0x7a,0x81,0x83,0x89, + 0x8b,0x8e,0x8f,0x90,0x92,0x94,0x96,0x96,0x97,0x94,0x95,0x98,0x9a,0x9c,0x9e,0x9f, + 0x9d,0x9e,0x9a,0x94,0x8f,0x84,0x7a,0x79,0x7d,0x82,0x88,0x8d,0x8f,0xaf,0xcd,0xdf, + 0xe9,0xed,0xea,0xe2,0xd1,0xb8,0x9c,0x9a,0x9a,0x9d,0x9e,0x9f,0xa9,0xaa,0xab,0xaa, + 0xa6,0xa2,0x9e,0x9a,0x98,0x91,0x81,0x6f,0x5d,0x53,0x4b,0x4e,0x50,0x54,0x58,0x60, + 0x63,0x63,0x63,0x61,0x5e,0x5b,0x57,0x55,0x52,0x50,0x4c,0x4a,0x4a,0x49,0x45,0x45, + 0x42,0x39,0x39,0x3b,0x3d,0x42,0x3e,0x3e,0x40,0x3d,0x3b,0x3b,0x36,0x2f,0x34,0x33, + 0x37,0x3a,0x3a,0x3a,0x34,0x31,0x36,0x3b,0x3e,0x42,0x40,0x3e,0x3f,0x41,0x42,0x46, + 0x7f,0x80,0x83,0x88,0x83,0x73,0x6b,0x68,0x6e,0x74,0x78,0x78,0x7d,0x7c,0x81,0x86, + 0x89,0x8b,0x8c,0x8e,0x8f,0x90,0x90,0x91,0x91,0x90,0x91,0x91,0x93,0x93,0x96,0x96, + 0x97,0x97,0x98,0x99,0x99,0x91,0x8c,0x81,0x7d,0x80,0x85,0x86,0x8e,0xbe,0xdc,0xe9, + 0xf0,0xf2,0xee,0xe5,0xd4,0xb8,0x9e,0x9a,0x9d,0x9e,0xa1,0xa4,0xac,0xac,0xad,0xab, + 0xa6,0xa1,0x9e,0x9a,0x95,0x89,0x79,0x66,0x56,0x4e,0x4c,0x4b,0x4f,0x54,0x5a,0x5c, + 0x61,0x63,0x62,0x61,0x5f,0x5c,0x5a,0x57,0x54,0x52,0x4f,0x4d,0x4c,0x4c,0x4a,0x45, + 0x42,0x3d,0x35,0x33,0x35,0x3b,0x3d,0x3c,0x40,0x3f,0x41,0x42,0x3b,0x34,0x34,0x34, + 0x33,0x34,0x37,0x36,0x32,0x33,0x36,0x3a,0x3c,0x46,0x4c,0x4a,0x4b,0x49,0x46,0x48, + 0x7f,0x7e,0x7f,0x7f,0x7f,0x76,0x6b,0x6d,0x73,0x7b,0x7c,0x7c,0x7b,0x7d,0x83,0x87, + 0x8a,0x8c,0x8d,0x8d,0x8e,0x91,0x90,0x90,0x90,0x91,0x91,0x92,0x92,0x93,0x96,0x95, + 0x94,0x95,0x95,0x94,0x91,0x8d,0x88,0x83,0x7f,0x7d,0x7d,0x7e,0x90,0xc1,0xdc,0xeb, + 0xf1,0xf3,0xf0,0xe8,0xd5,0xb7,0x9e,0x9b,0x9f,0xa1,0xa3,0xa6,0xad,0xad,0xaf,0xac, + 0xa6,0xa0,0x9b,0x98,0x91,0x80,0x72,0x5f,0x50,0x4d,0x49,0x48,0x4d,0x53,0x57,0x5d, + 0x60,0x63,0x61,0x5e,0x5c,0x59,0x56,0x55,0x52,0x52,0x4e,0x4b,0x4b,0x4b,0x4b,0x47, + 0x42,0x3b,0x38,0x34,0x30,0x34,0x37,0x3a,0x41,0x44,0x48,0x4a,0x44,0x3c,0x38,0x35, + 0x32,0x33,0x34,0x34,0x35,0x37,0x35,0x38,0x3d,0x47,0x4e,0x51,0x51,0x4e,0x48,0x45, + 0x7d,0x7b,0x7b,0x7e,0x7d,0x77,0x6c,0x72,0x75,0x7e,0x7d,0x7b,0x7b,0x80,0x84,0x87, + 0x8a,0x8d,0x8f,0x8e,0x91,0x94,0x91,0x90,0x90,0x90,0x90,0x91,0x93,0x94,0x95,0x93, + 0x94,0x95,0x96,0x96,0x8f,0x8a,0x86,0x81,0x81,0x78,0x78,0x78,0x89,0xb4,0xd5,0xea, + 0xf1,0xf3,0xf0,0xe7,0xd5,0xb8,0xa0,0x9c,0xa3,0xa5,0xa8,0xac,0xb0,0xb0,0xb0,0xab, + 0xa6,0x9f,0x9c,0x92,0x83,0x75,0x68,0x56,0x4c,0x4a,0x49,0x48,0x4d,0x50,0x55,0x59, + 0x5a,0x5e,0x5b,0x59,0x54,0x52,0x50,0x52,0x51,0x4e,0x4c,0x4b,0x4a,0x48,0x4b,0x4d, + 0x49,0x41,0x3a,0x34,0x2f,0x30,0x31,0x3a,0x47,0x4c,0x4c,0x4d,0x48,0x44,0x41,0x38, + 0x32,0x32,0x35,0x35,0x36,0x39,0x3b,0x3d,0x43,0x4a,0x4f,0x54,0x53,0x4e,0x48,0x43, + 0x7b,0x79,0x7c,0x82,0x80,0x7a,0x70,0x74,0x77,0x7c,0x7b,0x7a,0x7c,0x7f,0x84,0x87, + 0x8a,0x8d,0x90,0x91,0x94,0x93,0x91,0x91,0x8e,0x8f,0x91,0x8f,0x94,0x94,0x93,0x92, + 0x95,0x95,0x97,0x92,0x8a,0x88,0x85,0x84,0x81,0x7b,0x77,0x73,0x77,0x93,0xc1,0xde, + 0xef,0xf3,0xf1,0xe8,0xd6,0xb9,0x9f,0x9f,0xa3,0xa6,0xaa,0xb1,0xb2,0xb2,0xb1,0xad, + 0xa3,0xa0,0x94,0x87,0x76,0x66,0x5a,0x52,0x4e,0x4a,0x49,0x49,0x4a,0x4d,0x51,0x53, + 0x53,0x54,0x52,0x52,0x4b,0x4d,0x50,0x50,0x4f,0x4d,0x4b,0x48,0x46,0x47,0x4b,0x4e, + 0x4d,0x47,0x41,0x34,0x2c,0x2d,0x33,0x38,0x42,0x49,0x4e,0x4e,0x49,0x47,0x45,0x3d, + 0x39,0x36,0x38,0x36,0x37,0x3a,0x3f,0x40,0x46,0x4b,0x4d,0x54,0x56,0x50,0x4b,0x45, + 0x7e,0x81,0x83,0x87,0x83,0x78,0x71,0x73,0x7a,0x7c,0x7e,0x7f,0x81,0x84,0x84,0x87, + 0x8a,0x8d,0x91,0x90,0x94,0x95,0x92,0x91,0x8f,0x92,0x90,0x90,0x95,0x92,0x92,0x91, + 0x92,0x96,0x94,0x8c,0x87,0x87,0x87,0x89,0x82,0x7d,0x78,0x72,0x72,0x75,0x99,0xce, + 0xe6,0xef,0xf1,0xe7,0xd5,0xb8,0x9f,0xa1,0xa5,0xa9,0xac,0xb3,0xb4,0xb3,0xb1,0xaa, + 0xa2,0x9c,0x8a,0x76,0x67,0x5a,0x4d,0x50,0x4f,0x4f,0x4e,0x4e,0x4f,0x52,0x54,0x52, + 0x52,0x4f,0x4f,0x4e,0x4b,0x4c,0x4e,0x50,0x4e,0x4e,0x4c,0x47,0x47,0x47,0x4b,0x4e, + 0x4f,0x4c,0x45,0x38,0x2c,0x2a,0x33,0x3c,0x3e,0x48,0x4f,0x51,0x4c,0x4a,0x45,0x40, + 0x3e,0x3b,0x3b,0x39,0x3c,0x41,0x43,0x44,0x43,0x4a,0x4f,0x55,0x54,0x4f,0x49,0x45, + 0x7f,0x83,0x87,0x85,0x7f,0x72,0x70,0x71,0x78,0x7c,0x83,0x86,0x8a,0x8e,0x8c,0x8c, + 0x89,0x8d,0x90,0x92,0x94,0x95,0x94,0x92,0x92,0x90,0x90,0x91,0x92,0x8f,0x90,0x8f, + 0x94,0x94,0x8e,0x89,0x86,0x86,0x8a,0x89,0x86,0x7d,0x78,0x72,0x70,0x72,0x7d,0xb5, + 0xdb,0xea,0xea,0xe1,0xcd,0xb2,0xa1,0xa3,0xa7,0xa9,0xb0,0xb5,0xb4,0xb5,0xaf,0xa6, + 0xa0,0x93,0x7c,0x65,0x5a,0x4d,0x4c,0x4d,0x4f,0x51,0x53,0x54,0x54,0x53,0x52,0x53, + 0x53,0x4e,0x4e,0x4d,0x4c,0x4e,0x4e,0x51,0x50,0x50,0x4e,0x4a,0x46,0x47,0x48,0x4a, + 0x49,0x46,0x42,0x39,0x30,0x2d,0x32,0x3d,0x44,0x4e,0x52,0x50,0x4c,0x4b,0x48,0x44, + 0x45,0x43,0x3f,0x3f,0x45,0x42,0x45,0x43,0x41,0x48,0x4d,0x50,0x53,0x50,0x4a,0x43, + 0x7f,0x81,0x81,0x80,0x79,0x6d,0x6d,0x71,0x79,0x82,0x8b,0x8c,0x8f,0x90,0x94,0x96, + 0x92,0x92,0x90,0x92,0x95,0x96,0x95,0x94,0x90,0x8f,0x92,0x93,0x90,0x8f,0x8e,0x91, + 0x94,0x92,0x8d,0x87,0x86,0x89,0x8d,0x8b,0x87,0x80,0x7b,0x76,0x71,0x6f,0x74,0x98, + 0xc7,0xdc,0xde,0xd3,0xc0,0xab,0xa3,0xa5,0xa8,0xaa,0xaf,0xb4,0xb5,0xb3,0xab,0xa1, + 0x95,0x83,0x6f,0x5e,0x4e,0x4c,0x4e,0x4e,0x51,0x53,0x54,0x57,0x59,0x58,0x55,0x56, + 0x55,0x52,0x52,0x52,0x53,0x53,0x54,0x56,0x55,0x55,0x51,0x4e,0x4c,0x49,0x45,0x47, + 0x47,0x45,0x41,0x3c,0x3a,0x39,0x3c,0x43,0x4a,0x50,0x52,0x4f,0x50,0x4d,0x4c,0x4a, + 0x49,0x49,0x48,0x4a,0x4b,0x46,0x43,0x40,0x3f,0x45,0x4d,0x50,0x51,0x51,0x4e,0x46, + 0x7e,0x7d,0x7e,0x7a,0x75,0x6d,0x6b,0x70,0x7b,0x8d,0x8a,0x88,0x8c,0x90,0x96,0x98, + 0x98,0x97,0x93,0x91,0x93,0x94,0x93,0x92,0x91,0x91,0x92,0x91,0x8f,0x8f,0x8f,0x92, + 0x93,0x90,0x89,0x85,0x86,0x8a,0x8f,0x8c,0x88,0x81,0x7b,0x77,0x73,0x72,0x74,0x84, + 0xa9,0xc1,0xc7,0xc0,0xae,0xa2,0xa4,0xa6,0xa9,0xac,0xb0,0xb2,0xaf,0xa9,0xa2,0x90, + 0x81,0x6f,0x61,0x58,0x52,0x51,0x51,0x50,0x50,0x51,0x53,0x56,0x5a,0x5c,0x5c,0x5a, + 0x58,0x56,0x55,0x56,0x57,0x56,0x57,0x57,0x57,0x55,0x52,0x4f,0x4c,0x4a,0x43,0x43, + 0x44,0x44,0x43,0x42,0x43,0x43,0x45,0x49,0x4e,0x50,0x4e,0x4f,0x51,0x4c,0x4a,0x4b, + 0x4b,0x4d,0x4f,0x4d,0x4a,0x46,0x41,0x3d,0x3a,0x40,0x4a,0x4f,0x54,0x52,0x4e,0x45, + 0x7f,0x7f,0x86,0x81,0x7a,0x71,0x6b,0x73,0x89,0x91,0x88,0x7f,0x8f,0xa0,0xa4,0x9a, + 0x97,0x96,0x97,0x97,0x91,0x90,0x92,0x91,0x91,0x92,0x91,0x91,0x8f,0x8d,0x90,0x91, + 0x91,0x8d,0x88,0x86,0x88,0x8b,0x8f,0x8c,0x87,0x81,0x7a,0x77,0x73,0x72,0x73,0x78, + 0x89,0x9d,0xa2,0xa0,0xa1,0xa2,0xa5,0xa6,0xa9,0xac,0xaf,0xac,0xa7,0x9d,0x8c,0x77, + 0x6e,0x63,0x5b,0x57,0x5d,0x5d,0x5a,0x56,0x54,0x54,0x54,0x59,0x5c,0x5d,0x61,0x5f, + 0x5d,0x5b,0x59,0x59,0x5b,0x5b,0x5d,0x5c,0x5b,0x5b,0x57,0x54,0x50,0x4c,0x4a,0x48, + 0x47,0x46,0x48,0x49,0x4a,0x4b,0x4e,0x51,0x52,0x52,0x51,0x4c,0x4d,0x49,0x4a,0x4b, + 0x4b,0x4c,0x51,0x51,0x4c,0x48,0x44,0x3f,0x38,0x3f,0x49,0x50,0x54,0x54,0x50,0x48, + 0x83,0x83,0x8c,0x89,0x81,0x72,0x6d,0x7d,0x8c,0x8e,0x80,0x88,0xa5,0xab,0xaa,0x97, + 0x8a,0x8c,0x91,0x9a,0x9c,0x99,0x94,0x91,0x91,0x92,0x93,0x93,0x90,0x8f,0x90,0x8f, + 0x8e,0x89,0x87,0x85,0x88,0x8c,0x8e,0x8c,0x85,0x81,0x7b,0x78,0x74,0x73,0x74,0x75, + 0x75,0x7f,0x87,0x91,0x9c,0xa2,0xa5,0xa8,0xaa,0xad,0xad,0xa7,0x9c,0x8d,0x75,0x66, + 0x61,0x5a,0x59,0x60,0x65,0x63,0x60,0x5c,0x58,0x55,0x57,0x59,0x5c,0x63,0x65,0x62, + 0x61,0x5f,0x5a,0x59,0x5c,0x5c,0x61,0x62,0x60,0x5f,0x5b,0x57,0x50,0x51,0x4e,0x4b, + 0x4a,0x49,0x4b,0x4c,0x4e,0x4f,0x52,0x51,0x4f,0x4f,0x4e,0x48,0x47,0x47,0x46,0x4c, + 0x4d,0x4e,0x52,0x53,0x4f,0x4a,0x44,0x41,0x3e,0x44,0x49,0x50,0x54,0x54,0x4f,0x49, + 0x82,0x87,0x8c,0x89,0x82,0x77,0x73,0x87,0x8c,0x8a,0x78,0x98,0xb0,0xb3,0xa7,0x82, + 0x6e,0x82,0x8f,0x9e,0xa1,0x9f,0x9b,0x97,0x90,0x94,0x94,0x94,0x94,0x91,0x90,0x8d, + 0x8c,0x87,0x88,0x89,0x8a,0x8b,0x8f,0x8d,0x86,0x80,0x7c,0x77,0x75,0x73,0x75,0x74, + 0x72,0x70,0x7a,0x88,0x96,0xa1,0xa6,0xa7,0xaa,0xac,0xad,0xa1,0x90,0x7b,0x65,0x59, + 0x58,0x55,0x5b,0x64,0x67,0x69,0x66,0x62,0x5c,0x5c,0x5a,0x5b,0x62,0x66,0x69,0x68, + 0x65,0x62,0x5d,0x5a,0x5e,0x63,0x66,0x67,0x62,0x60,0x5d,0x58,0x55,0x55,0x53,0x4e, + 0x4d,0x4d,0x4b,0x4b,0x51,0x54,0x53,0x50,0x4b,0x49,0x4a,0x47,0x44,0x42,0x45,0x4e, + 0x4f,0x51,0x55,0x52,0x53,0x50,0x4d,0x4b,0x47,0x4d,0x50,0x52,0x55,0x55,0x51,0x4b, + 0x82,0x86,0x8d,0x8f,0x86,0x77,0x7a,0x87,0x8a,0x81,0x74,0x95,0xa9,0xb0,0x9a,0x72, + 0x5f,0x75,0x8d,0xa0,0xa0,0xa0,0x9d,0x9b,0x97,0x94,0x95,0x95,0x93,0x93,0x93,0x8f, + 0x8c,0x89,0x8a,0x8a,0x8b,0x8d,0x8f,0x8b,0x86,0x81,0x7d,0x78,0x75,0x73,0x74,0x76, + 0x72,0x6c,0x6f,0x7c,0x8e,0x9f,0xa9,0xa8,0xa7,0xad,0xa8,0x9b,0x86,0x6e,0x5c,0x53, + 0x53,0x59,0x61,0x66,0x68,0x67,0x66,0x63,0x5f,0x5e,0x60,0x61,0x64,0x6a,0x6d,0x6c, + 0x67,0x62,0x61,0x60,0x5f,0x63,0x67,0x68,0x66,0x64,0x5f,0x5a,0x58,0x55,0x52,0x4f, + 0x4e,0x4d,0x49,0x4a,0x4b,0x4e,0x4e,0x4c,0x4a,0x4a,0x46,0x44,0x44,0x41,0x45,0x4d, + 0x51,0x50,0x53,0x55,0x53,0x55,0x53,0x50,0x4f,0x52,0x56,0x55,0x55,0x56,0x52,0x4a, + 0x7b,0x82,0x8a,0x8d,0x81,0x77,0x7e,0x84,0x88,0x79,0x76,0x91,0xa6,0xa9,0x94,0x73, + 0x67,0x7e,0x91,0x97,0x9e,0x9e,0x9b,0x9b,0x9b,0x9a,0x9a,0x98,0x96,0x96,0x97,0x94, + 0x91,0x90,0x8f,0x8d,0x8c,0x8c,0x8f,0x8a,0x85,0x81,0x7e,0x78,0x75,0x73,0x74,0x76, + 0x71,0x69,0x65,0x72,0x88,0x9a,0xa7,0xab,0xae,0xb2,0xad,0x9f,0x8a,0x6d,0x59,0x57, + 0x58,0x5f,0x62,0x63,0x64,0x62,0x64,0x64,0x63,0x63,0x65,0x67,0x69,0x6c,0x6f,0x70, + 0x6c,0x66,0x61,0x60,0x63,0x64,0x66,0x67,0x67,0x66,0x5e,0x59,0x58,0x55,0x4f,0x4f, + 0x4c,0x49,0x46,0x4b,0x49,0x49,0x49,0x47,0x48,0x48,0x42,0x3e,0x41,0x41,0x46,0x4b, + 0x53,0x55,0x56,0x56,0x57,0x54,0x53,0x54,0x59,0x5a,0x5e,0x5c,0x5a,0x5b,0x56,0x4e, + 0x74,0x7f,0x8a,0x86,0x7a,0x71,0x7a,0x81,0x83,0x74,0x79,0x8a,0xa9,0xb0,0x99,0x7d, + 0x6f,0x86,0x94,0x98,0x96,0x96,0x96,0x9a,0x9b,0x9c,0x9d,0x9c,0x9c,0x9b,0x98,0x9a, + 0x99,0x97,0x95,0x91,0x8d,0x8d,0x8c,0x89,0x85,0x81,0x7e,0x78,0x74,0x73,0x75,0x75, + 0x71,0x69,0x66,0x6b,0x7e,0x92,0xa1,0xad,0xba,0xbc,0xb7,0xab,0x96,0x76,0x64,0x62, + 0x5d,0x5e,0x5f,0x60,0x5f,0x5d,0x5d,0x5f,0x62,0x64,0x68,0x6a,0x6e,0x6f,0x70,0x71, + 0x6f,0x68,0x60,0x5f,0x61,0x64,0x67,0x66,0x67,0x63,0x5b,0x57,0x53,0x54,0x4e,0x4c, + 0x47,0x46,0x45,0x49,0x48,0x47,0x46,0x47,0x46,0x3f,0x3e,0x39,0x3b,0x42,0x44,0x4a, + 0x55,0x59,0x59,0x58,0x55,0x55,0x56,0x58,0x5e,0x60,0x63,0x5f,0x5e,0x5a,0x57,0x4c, + 0x74,0x80,0x8b,0x86,0x7a,0x6f,0x78,0x7c,0x7c,0x73,0x7b,0x8a,0xb0,0xb5,0xa7,0x8b, + 0x7b,0x8b,0x93,0x99,0x96,0x94,0x94,0x97,0x9b,0x9d,0x9d,0xa0,0xa2,0xa3,0xa1,0xa1, + 0xa0,0x9e,0x9a,0x93,0x91,0x8f,0x8c,0x88,0x85,0x81,0x7d,0x78,0x74,0x73,0x75,0x75, + 0x72,0x6a,0x65,0x64,0x77,0x8f,0xa8,0xbc,0xcb,0xce,0xc6,0xb9,0xa5,0x86,0x6b,0x63, + 0x60,0x5d,0x5c,0x5c,0x5b,0x58,0x59,0x5b,0x5f,0x62,0x67,0x6a,0x6d,0x6f,0x6e,0x6e, + 0x6d,0x65,0x5e,0x5a,0x59,0x5e,0x62,0x67,0x66,0x61,0x5a,0x53,0x50,0x4f,0x4a,0x49, + 0x46,0x45,0x48,0x4c,0x4c,0x49,0x4a,0x48,0x3e,0x3c,0x38,0x36,0x38,0x40,0x46,0x4d, + 0x55,0x59,0x5b,0x5b,0x5a,0x58,0x57,0x59,0x5c,0x5f,0x5f,0x62,0x5c,0x57,0x52,0x4b, + 0x78,0x88,0x89,0x85,0x74,0x6e,0x75,0x7a,0x79,0x76,0x7a,0x8a,0xac,0xae,0xa2,0x8b, + 0x83,0x8e,0x94,0x97,0x95,0x95,0x94,0x95,0x97,0x99,0x9e,0xa2,0xa3,0xa5,0xa9,0xa8, + 0xa5,0xa1,0x9b,0x98,0x95,0x94,0x8d,0x86,0x83,0x7f,0x7d,0x77,0x74,0x74,0x74,0x74, + 0x72,0x6a,0x65,0x62,0x68,0x8f,0xae,0xc8,0xd8,0xda,0xd3,0xc6,0xac,0x8e,0x6e,0x61, + 0x5c,0x58,0x57,0x56,0x53,0x55,0x57,0x5a,0x5d,0x60,0x61,0x66,0x6a,0x6c,0x6d,0x6f, + 0x6b,0x63,0x5d,0x56,0x56,0x59,0x5f,0x67,0x64,0x5e,0x58,0x52,0x4a,0x45,0x41,0x3f, + 0x3e,0x40,0x47,0x4f,0x4e,0x4d,0x4b,0x44,0x3c,0x39,0x33,0x33,0x36,0x3b,0x41,0x4d, + 0x55,0x58,0x5c,0x5f,0x60,0x59,0x57,0x59,0x5a,0x60,0x64,0x62,0x5f,0x57,0x50,0x4a, + 0x7f,0x8b,0x8a,0x83,0x73,0x70,0x73,0x76,0x75,0x77,0x7a,0x85,0x9d,0xa2,0x9c,0x89, + 0x8d,0x93,0x98,0x99,0x96,0x95,0x95,0x96,0x9a,0x9b,0x9c,0x9f,0xa5,0xa7,0xaa,0xa9, + 0xa9,0xa4,0x9d,0x9e,0x9e,0x9b,0x95,0x8b,0x86,0x81,0x7e,0x78,0x74,0x73,0x75,0x76, + 0x75,0x6d,0x67,0x63,0x66,0x8c,0xb1,0xcf,0xdd,0xe1,0xd9,0xc8,0xaf,0x8d,0x6c,0x5d, + 0x57,0x57,0x56,0x56,0x57,0x57,0x58,0x59,0x5c,0x61,0x62,0x64,0x68,0x6c,0x70,0x72, + 0x70,0x69,0x61,0x57,0x53,0x57,0x60,0x66,0x66,0x5e,0x55,0x4d,0x43,0x40,0x3c,0x3b, + 0x39,0x40,0x49,0x4f,0x51,0x53,0x51,0x47,0x3e,0x38,0x31,0x31,0x37,0x3c,0x42,0x4b, + 0x52,0x57,0x5b,0x62,0x63,0x5e,0x59,0x57,0x57,0x5d,0x61,0x63,0x5d,0x54,0x4b,0x48, + 0x82,0x8e,0x8c,0x84,0x74,0x74,0x72,0x72,0x71,0x77,0x77,0x7e,0x85,0x8f,0x93,0x94, + 0x94,0x98,0x98,0x97,0x96,0x96,0x95,0x97,0x99,0x9b,0x9a,0x9e,0x9f,0xa5,0xa8,0xa8, + 0xa6,0xa4,0xa5,0xa6,0xa3,0xa0,0x98,0x8f,0x8a,0x84,0x7f,0x79,0x76,0x74,0x77,0x78, + 0x77,0x71,0x6d,0x68,0x66,0x84,0xb3,0xd2,0xe1,0xe2,0xd7,0xc5,0xae,0x8e,0x6b,0x5b, + 0x54,0x52,0x54,0x57,0x5a,0x5c,0x5b,0x5b,0x5d,0x60,0x62,0x64,0x6b,0x6e,0x76,0x79, + 0x75,0x6c,0x64,0x5a,0x56,0x59,0x5f,0x65,0x66,0x5d,0x55,0x48,0x3e,0x38,0x35,0x33, + 0x36,0x43,0x4c,0x51,0x54,0x56,0x4f,0x47,0x40,0x38,0x31,0x33,0x3c,0x41,0x43,0x4a, + 0x50,0x53,0x59,0x60,0x62,0x5e,0x5a,0x53,0x53,0x56,0x5c,0x5c,0x58,0x50,0x49,0x45, + 0x89,0x91,0x8b,0x88,0x7e,0x77,0x70,0x6e,0x71,0x78,0x76,0x80,0x87,0x90,0x95,0x99, + 0x99,0x9a,0x98,0x97,0x96,0x95,0x96,0x97,0x9a,0x9c,0x9d,0xa0,0x9e,0xa1,0xa4,0xa7, + 0xa6,0xa5,0xa5,0xa7,0xa6,0xa4,0x95,0x90,0x8c,0x87,0x83,0x79,0x75,0x75,0x77,0x79, + 0x79,0x75,0x71,0x6c,0x6a,0x7d,0xb1,0xce,0xde,0xe0,0xd6,0xc1,0xa8,0x89,0x6f,0x62, + 0x56,0x55,0x56,0x5a,0x5e,0x62,0x65,0x62,0x62,0x62,0x62,0x67,0x6d,0x74,0x79,0x7d, + 0x78,0x71,0x67,0x60,0x5d,0x5f,0x62,0x67,0x66,0x5e,0x55,0x49,0x3c,0x34,0x33,0x31, + 0x36,0x42,0x4a,0x4e,0x51,0x53,0x4d,0x46,0x43,0x40,0x3b,0x3c,0x43,0x46,0x46,0x4d, + 0x4f,0x4e,0x55,0x62,0x65,0x62,0x5e,0x56,0x52,0x57,0x5b,0x5c,0x5b,0x56,0x4f,0x4b, + 0x88,0x8f,0x8d,0x89,0x80,0x77,0x70,0x6b,0x70,0x77,0x77,0x82,0x8a,0x92,0x96,0x99, + 0x99,0x9a,0x97,0x97,0x97,0x96,0x97,0x97,0x99,0x9c,0x9f,0x9f,0x9f,0xa1,0xa0,0xa1, + 0xa0,0xa2,0xa2,0xa5,0xa4,0xa1,0x93,0x91,0x8d,0x8a,0x87,0x7b,0x75,0x75,0x77,0x79, + 0x79,0x77,0x72,0x6e,0x6e,0x7f,0xae,0xcb,0xd9,0xdb,0xcf,0xba,0xa1,0x85,0x6e,0x6a, + 0x63,0x5e,0x5c,0x5e,0x61,0x63,0x68,0x68,0x68,0x66,0x66,0x6a,0x6f,0x76,0x7a,0x7c, + 0x7a,0x73,0x68,0x64,0x60,0x61,0x65,0x69,0x65,0x5d,0x54,0x48,0x3c,0x33,0x31,0x31, + 0x35,0x3d,0x43,0x49,0x4d,0x4f,0x4d,0x4b,0x4b,0x4a,0x47,0x44,0x47,0x48,0x46,0x4b, + 0x4c,0x4d,0x54,0x5f,0x64,0x61,0x5d,0x55,0x50,0x53,0x57,0x58,0x59,0x57,0x50,0x49, + 0x82,0x8a,0x8e,0x8a,0x82,0x76,0x6e,0x6a,0x72,0x75,0x7b,0x83,0x8e,0x95,0x97,0x98, + 0x98,0x98,0x98,0x98,0x96,0x96,0x98,0x99,0x9b,0x9d,0x9f,0x9f,0x9f,0xa1,0xa0,0xa0, + 0x9d,0x9e,0x9f,0xa2,0xa1,0x9d,0x94,0x98,0x9a,0x9a,0x90,0x83,0x78,0x74,0x77,0x7b, + 0x7a,0x79,0x77,0x72,0x6f,0x7e,0xab,0xc6,0xd3,0xd2,0xc2,0xb0,0x98,0x81,0x76,0x72, + 0x70,0x6b,0x64,0x63,0x65,0x66,0x68,0x6c,0x69,0x67,0x68,0x6b,0x70,0x75,0x78,0x78, + 0x74,0x6e,0x65,0x62,0x5f,0x5f,0x63,0x67,0x62,0x59,0x53,0x49,0x3a,0x33,0x31,0x33, + 0x35,0x39,0x42,0x49,0x4c,0x4f,0x52,0x52,0x51,0x52,0x52,0x4a,0x49,0x48,0x45,0x47, + 0x49,0x4e,0x55,0x5f,0x60,0x5d,0x59,0x56,0x52,0x54,0x55,0x53,0x50,0x4e,0x46,0x44, + 0x7e,0x8c,0x8f,0x8d,0x85,0x74,0x6c,0x6b,0x75,0x76,0x7f,0x85,0x8f,0x95,0x97,0x99, + 0x99,0x99,0x98,0x98,0x98,0x98,0x97,0x99,0x9b,0x9e,0x9f,0xa0,0xa0,0xa0,0xa0,0x9f, + 0x9d,0x9e,0x9e,0xa0,0xa1,0x9e,0x9b,0xa0,0xa4,0xa6,0x9c,0x8a,0x81,0x76,0x77,0x7b, + 0x79,0x76,0x73,0x70,0x6f,0x7c,0x9e,0xb5,0xbc,0xba,0xac,0x96,0x83,0x79,0x77,0x77, + 0x74,0x71,0x6f,0x66,0x67,0x66,0x66,0x65,0x60,0x5e,0x61,0x68,0x6e,0x6f,0x72,0x71, + 0x6d,0x62,0x5e,0x5e,0x5b,0x5a,0x5d,0x60,0x59,0x56,0x4e,0x45,0x3b,0x35,0x34,0x34, + 0x34,0x38,0x42,0x49,0x49,0x4a,0x4c,0x50,0x51,0x53,0x54,0x4c,0x46,0x46,0x46,0x43, + 0x48,0x4d,0x53,0x56,0x5a,0x5a,0x57,0x56,0x55,0x55,0x57,0x52,0x4f,0x48,0x3f,0x3f, + 0x80,0x8f,0x8e,0x8d,0x83,0x73,0x6a,0x6e,0x76,0x78,0x81,0x88,0x91,0x97,0x99,0x99, + 0x99,0x99,0x99,0x99,0x98,0x98,0x9a,0x9b,0x9d,0x9f,0xa0,0xa0,0xa0,0xa0,0xa0,0x9e, + 0x9d,0x9d,0xa0,0xa0,0xa2,0x9e,0x9f,0xa5,0xa9,0xaa,0xa4,0x97,0x8d,0x81,0x79,0x77, + 0x78,0x75,0x71,0x70,0x70,0x75,0x86,0x9b,0xa3,0x9d,0x90,0x7d,0x6a,0x69,0x71,0x72, + 0x73,0x73,0x6f,0x6d,0x64,0x60,0x5d,0x59,0x55,0x54,0x59,0x62,0x67,0x6a,0x6d,0x6a, + 0x64,0x5d,0x59,0x55,0x51,0x50,0x53,0x57,0x54,0x53,0x4a,0x43,0x41,0x3b,0x39,0x3b, + 0x3b,0x3c,0x42,0x47,0x45,0x46,0x48,0x4c,0x4e,0x4f,0x4e,0x4b,0x48,0x44,0x45,0x3f, + 0x41,0x49,0x4d,0x4d,0x52,0x51,0x50,0x4e,0x4d,0x57,0x5a,0x58,0x54,0x4d,0x3c,0x33, + 0x8a,0x8f,0x8d,0x8c,0x84,0x74,0x6b,0x71,0x76,0x7b,0x82,0x8b,0x94,0x96,0x98,0x9a, + 0x9a,0x9a,0x99,0x98,0x98,0x9a,0x9b,0x9c,0x9f,0xa0,0xa1,0xa1,0xa1,0xa0,0x9f,0x9e, + 0x9d,0x9e,0xa1,0xa2,0xa4,0x9e,0x9f,0xa3,0xaa,0xac,0xa9,0xa4,0x99,0x8e,0x80,0x79, + 0x76,0x74,0x6f,0x6e,0x6f,0x70,0x75,0x80,0x86,0x7e,0x72,0x66,0x61,0x5f,0x64,0x6d, + 0x6f,0x70,0x6d,0x69,0x67,0x5b,0x51,0x4c,0x48,0x4a,0x51,0x5d,0x64,0x68,0x6a,0x64, + 0x5e,0x53,0x4d,0x45,0x42,0x45,0x49,0x4c,0x4f,0x50,0x4b,0x47,0x43,0x41,0x40,0x41, + 0x44,0x44,0x45,0x47,0x45,0x44,0x44,0x46,0x4b,0x4b,0x4c,0x4e,0x4c,0x48,0x45,0x3e, + 0x3e,0x45,0x48,0x46,0x48,0x4a,0x48,0x48,0x4a,0x56,0x5d,0x5d,0x56,0x4a,0x3a,0x2b, + 0x8d,0x8c,0x8b,0x8a,0x83,0x76,0x6e,0x71,0x76,0x7f,0x84,0x8f,0x95,0x97,0x98,0x9a, + 0x9a,0x9a,0x99,0x99,0x9a,0x9b,0x9d,0x9e,0x9f,0xa0,0xa1,0xa1,0xa1,0xa0,0x9f,0xa0, + 0xa0,0xa0,0xa1,0xa2,0xa3,0xa0,0xa0,0xa3,0xa8,0xae,0xb0,0xae,0xa5,0x9a,0x8d,0x81, + 0x75,0x72,0x70,0x6d,0x6d,0x71,0x77,0x7c,0x77,0x6e,0x65,0x5e,0x5f,0x5c,0x5a,0x5f, + 0x6a,0x6c,0x69,0x67,0x68,0x63,0x52,0x43,0x3c,0x3f,0x48,0x56,0x62,0x68,0x65,0x5b, + 0x51,0x49,0x3e,0x32,0x2e,0x37,0x40,0x3f,0x46,0x4b,0x4b,0x45,0x45,0x47,0x45,0x48, + 0x4a,0x47,0x42,0x41,0x3e,0x3b,0x3d,0x40,0x45,0x4b,0x4f,0x53,0x51,0x4d,0x48,0x41, + 0x3d,0x40,0x3d,0x3d,0x3c,0x3a,0x3e,0x4a,0x51,0x59,0x61,0x5f,0x58,0x4d,0x3c,0x2e, + 0x8f,0x8c,0x8a,0x8c,0x86,0x7b,0x74,0x74,0x78,0x80,0x87,0x92,0x97,0x99,0x9a,0x9b, + 0x9a,0x9a,0x99,0x9b,0x9c,0x9d,0x9e,0x9d,0x9e,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0, + 0x9f,0x9f,0xa1,0xa2,0xa4,0xa2,0xa1,0xa4,0xa9,0xb0,0xb2,0xb3,0xaf,0xa5,0x99,0x8c, + 0x7e,0x73,0x6f,0x6d,0x6e,0x77,0x83,0x85,0x85,0x7f,0x70,0x67,0x65,0x62,0x5f,0x5e, + 0x5f,0x67,0x6a,0x68,0x67,0x64,0x5f,0x4f,0x3e,0x3f,0x48,0x57,0x63,0x66,0x65,0x57, + 0x4a,0x40,0x33,0x29,0x25,0x2b,0x38,0x3e,0x47,0x4b,0x4b,0x45,0x45,0x46,0x43,0x46, + 0x47,0x45,0x3e,0x3c,0x39,0x37,0x3b,0x3f,0x47,0x4f,0x57,0x55,0x51,0x4f,0x4a,0x44, + 0x3e,0x3b,0x3a,0x37,0x36,0x37,0x3e,0x49,0x55,0x56,0x5b,0x5a,0x52,0x4c,0x3d,0x30, + 0x8e,0x89,0x8b,0x8b,0x8a,0x80,0x7c,0x77,0x79,0x82,0x8a,0x93,0x97,0x99,0x9a,0x9a, + 0x9b,0x9c,0x9b,0x9b,0x9c,0x9d,0x9d,0x9e,0x9e,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa1, + 0xa2,0xa1,0xa1,0xa2,0xa3,0xa3,0xa2,0xa6,0xab,0xb0,0xb4,0xb3,0xb3,0xaf,0xa6,0x98, + 0x89,0x79,0x70,0x6e,0x6e,0x7a,0x87,0x89,0x85,0x83,0x7d,0x71,0x6b,0x69,0x66,0x64, + 0x63,0x62,0x69,0x6d,0x6b,0x65,0x5f,0x58,0x4e,0x42,0x47,0x58,0x62,0x67,0x61,0x58, + 0x4b,0x38,0x2b,0x27,0x24,0x2a,0x36,0x44,0x4b,0x4e,0x4d,0x48,0x46,0x44,0x42,0x43, + 0x43,0x41,0x3c,0x3a,0x38,0x36,0x3b,0x48,0x50,0x57,0x5e,0x60,0x5c,0x55,0x4f,0x48, + 0x42,0x3e,0x3b,0x38,0x35,0x34,0x3e,0x49,0x53,0x55,0x56,0x52,0x49,0x40,0x36,0x2a, + 0x8d,0x8c,0x8a,0x8d,0x93,0x84,0x7b,0x78,0x7a,0x85,0x8e,0x93,0x98,0x99,0x9a,0x9b, + 0x9a,0x9b,0x9b,0x9c,0x9d,0x9d,0x9e,0x9e,0x9f,0xa0,0xa0,0xa0,0xa2,0xa2,0xa1,0xa1, + 0xa1,0xa1,0xa0,0xa0,0xa1,0xa3,0xa2,0xa7,0xac,0xaf,0xb4,0xb5,0xb5,0xb5,0xb0,0xa3, + 0x94,0x85,0x74,0x6e,0x6e,0x75,0x83,0x83,0x81,0x7b,0x77,0x71,0x70,0x6c,0x69,0x67, + 0x67,0x64,0x63,0x67,0x6e,0x6a,0x6a,0x62,0x56,0x4e,0x47,0x51,0x5f,0x65,0x63,0x5b, + 0x4d,0x3d,0x2c,0x26,0x25,0x29,0x36,0x43,0x4c,0x4d,0x4c,0x49,0x45,0x3f,0x41,0x3e, + 0x3c,0x39,0x39,0x38,0x35,0x36,0x3d,0x4c,0x56,0x5d,0x63,0x64,0x64,0x57,0x4d,0x48, + 0x45,0x44,0x3f,0x3e,0x39,0x38,0x44,0x4c,0x50,0x50,0x50,0x48,0x3d,0x31,0x2a,0x23, + 0x8d,0x8e,0x8e,0x90,0x93,0x7f,0x76,0x73,0x7f,0x88,0x8f,0x95,0x97,0x99,0x9a,0x9a, + 0x9a,0x9b,0x9c,0x9d,0x9e,0x9e,0x9e,0x9f,0x9f,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1,0xa1, + 0xa2,0xa2,0xa2,0xa2,0xa2,0xa3,0xa2,0xa6,0xac,0xb0,0xb4,0xb6,0xb6,0xb5,0xb3,0xad, + 0xa1,0x90,0x7e,0x70,0x6d,0x70,0x75,0x7a,0x76,0x74,0x75,0x78,0x7b,0x7c,0x7d,0x7e, + 0x80,0x82,0x84,0x87,0x8e,0x98,0x9c,0x93,0x7c,0x63,0x53,0x4f,0x5b,0x64,0x64,0x5d, + 0x54,0x43,0x32,0x29,0x26,0x2e,0x35,0x41,0x4d,0x4d,0x4c,0x49,0x45,0x40,0x3b,0x39, + 0x38,0x37,0x37,0x39,0x38,0x3c,0x45,0x4e,0x59,0x62,0x65,0x67,0x63,0x58,0x4f,0x4c, + 0x4b,0x48,0x47,0x44,0x41,0x41,0x47,0x4e,0x50,0x50,0x49,0x3e,0x31,0x27,0x21,0x1f, + 0x8f,0x90,0x92,0x8f,0x8c,0x76,0x70,0x71,0x82,0x8c,0x91,0x95,0x97,0x99,0x9a,0x9a, + 0x9a,0x9c,0x9d,0x9d,0x9e,0x9e,0x9e,0x9f,0x9f,0xa0,0xa0,0xa0,0xa1,0xa1,0xa0,0xa1, + 0xa2,0xa2,0xa2,0xa2,0xa3,0xa4,0xa4,0xa7,0xac,0xaf,0xb4,0xb6,0xb6,0xb5,0xb4,0xb1, + 0xab,0x9d,0x8b,0x78,0x6f,0x72,0x7a,0x8e,0x9b,0xa2,0xa6,0xa7,0xab,0xae,0xb1,0xb4, + 0xb5,0xb7,0xb9,0xbb,0xba,0xc1,0xc3,0xb4,0x9d,0x82,0x65,0x51,0x5b,0x64,0x65,0x5c, + 0x53,0x46,0x37,0x28,0x24,0x2b,0x2f,0x3e,0x49,0x4b,0x4a,0x45,0x43,0x40,0x3d,0x3c, + 0x3c,0x3a,0x3a,0x3b,0x3d,0x43,0x4d,0x53,0x59,0x62,0x69,0x6b,0x64,0x5b,0x54,0x50, + 0x4f,0x50,0x51,0x4e,0x4b,0x4a,0x4b,0x4d,0x4e,0x49,0x40,0x37,0x2d,0x23,0x1e,0x1e, + 0x8f,0x92,0x91,0x8e,0x88,0x70,0x6b,0x71,0x85,0x8e,0x93,0x96,0x98,0x9a,0x9b,0x9c, + 0x9b,0x9c,0x9d,0x9e,0x9e,0x9e,0x9e,0x9f,0x9f,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1,0xa0, + 0xa1,0xa2,0xa2,0xa2,0xa3,0xa4,0xa5,0xa7,0xab,0xaf,0xb3,0xb6,0xb6,0xb6,0xb6,0xb4, + 0xb1,0xa6,0x94,0x82,0x73,0x7c,0x99,0xb1,0xc0,0xc9,0xca,0xcb,0xcd,0xd0,0xd2,0xd3, + 0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xcd,0xb6,0x95,0x6f,0x57,0x5c,0x62,0x62,0x5b, + 0x52,0x43,0x33,0x26,0x1d,0x22,0x29,0x36,0x41,0x46,0x43,0x41,0x40,0x3e,0x3e,0x3e, + 0x3f,0x3e,0x3e,0x41,0x43,0x4a,0x4e,0x52,0x57,0x5b,0x63,0x66,0x64,0x5b,0x56,0x54, + 0x54,0x56,0x59,0x55,0x54,0x56,0x54,0x4f,0x4d,0x44,0x39,0x30,0x2b,0x21,0x1d,0x1d, + 0x91,0x8f,0x8d,0x8d,0x83,0x6d,0x6c,0x74,0x8b,0x8f,0x95,0x97,0x99,0x99,0x9b,0x9c, + 0x9c,0x9c,0x9c,0x9c,0x9e,0x9e,0x9f,0x9f,0x9f,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1,0xa1, + 0xa1,0xa1,0xa2,0xa2,0xa4,0xa4,0xa5,0xa7,0xaa,0xae,0xb2,0xb4,0xb5,0xb7,0xb6,0xb6, + 0xb5,0xae,0x9e,0x8c,0x7a,0x8e,0xb5,0xca,0xd9,0xdd,0xdc,0xdd,0xdf,0xe0,0xe3,0xe2, + 0xe4,0xe5,0xe5,0xe5,0xe6,0xe7,0xe4,0xd8,0xc2,0xa6,0x77,0x5c,0x60,0x61,0x5d,0x59, + 0x4f,0x40,0x31,0x26,0x1d,0x21,0x2a,0x35,0x3e,0x40,0x3d,0x3e,0x39,0x39,0x3d,0x3f, + 0x3e,0x42,0x42,0x46,0x4c,0x4f,0x4e,0x4e,0x53,0x57,0x5b,0x5e,0x5d,0x57,0x58,0x57, + 0x58,0x5a,0x5d,0x5a,0x5c,0x5f,0x5c,0x54,0x4c,0x42,0x33,0x2b,0x26,0x1f,0x1c,0x1b, + 0x92,0x8f,0x88,0x8a,0x80,0x6f,0x71,0x7d,0x91,0x94,0x97,0x98,0x99,0x9a,0x9c,0x9a, + 0x9c,0x9d,0x9f,0x9e,0x9f,0x9f,0x9f,0x9f,0xa0,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1,0xa1, + 0xa1,0xa1,0xa2,0xa2,0xa4,0xa4,0xa5,0xa6,0xaa,0xad,0xb1,0xb3,0xb4,0xb5,0xb8,0xb7, + 0xb5,0xb2,0xa5,0x95,0x86,0x98,0xba,0xd1,0xdb,0xde,0xdf,0xdd,0xe1,0xe2,0xe5,0xe5, + 0xe7,0xe7,0xe7,0xe6,0xe5,0xe2,0xe1,0xd5,0xc2,0xa7,0x7e,0x65,0x63,0x56,0x52,0x4f, + 0x46,0x37,0x2b,0x25,0x1f,0x21,0x29,0x34,0x3b,0x3b,0x36,0x36,0x35,0x37,0x39,0x3e, + 0x43,0x44,0x45,0x47,0x4b,0x4d,0x4d,0x4d,0x4e,0x53,0x52,0x52,0x55,0x54,0x55,0x57, + 0x5d,0x60,0x62,0x60,0x60,0x61,0x5e,0x55,0x4b,0x41,0x31,0x27,0x23,0x1d,0x1c,0x1c, + 0x95,0x8d,0x84,0x88,0x81,0x84,0x89,0x90,0x97,0x99,0x9a,0x9c,0x9d,0x9d,0x9d,0x9e, + 0x9e,0x9e,0x9e,0x9f,0x9f,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1, + 0xa1,0xa1,0xa2,0xa2,0xa4,0xa4,0xa4,0xa6,0xa9,0xad,0xaf,0xb3,0xb3,0xb4,0xb6,0xb9, + 0xb6,0xb4,0xad,0x9f,0x90,0x9e,0xb6,0xc7,0xcf,0xcb,0xc6,0xc6,0xc9,0xd1,0xd5,0xd8, + 0xda,0xdb,0xda,0xd7,0xd4,0xd4,0xd5,0xc8,0xb8,0xa3,0x85,0x68,0x62,0x51,0x45,0x41, + 0x38,0x2c,0x23,0x23,0x1e,0x22,0x29,0x32,0x37,0x37,0x2f,0x2d,0x29,0x2e,0x34,0x3a, + 0x3f,0x45,0x47,0x48,0x49,0x4b,0x4c,0x4c,0x4c,0x4e,0x51,0x4e,0x55,0x53,0x55,0x58, + 0x5e,0x61,0x61,0x61,0x62,0x62,0x61,0x59,0x4a,0x3d,0x2f,0x24,0x1e,0x1c,0x1b,0x20, + 0x96,0x8b,0x82,0x86,0x8c,0x88,0x8a,0x98,0x9d,0x9c,0x9d,0x9d,0x9e,0x9f,0x9f,0xa0, + 0xa0,0xa0,0xa0,0xa1,0xa0,0xa0,0x9f,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1, + 0xa1,0xa1,0xa2,0xa2,0xa4,0xa4,0xa5,0xa7,0xab,0xac,0xae,0xb1,0xb2,0xb2,0xb4,0xb7, + 0xb7,0xb4,0xb0,0xa3,0x95,0x9f,0xae,0xb6,0xb1,0xa1,0x95,0x91,0x98,0xa1,0xab,0xb5, + 0xb8,0xba,0xb7,0xb2,0xad,0xad,0xaf,0xa1,0x9c,0x91,0x7c,0x68,0x5e,0x49,0x3b,0x30, + 0x27,0x22,0x1f,0x1d,0x1a,0x20,0x27,0x2c,0x2d,0x2e,0x29,0x22,0x21,0x27,0x2e,0x34, + 0x3c,0x42,0x47,0x49,0x4d,0x4d,0x4a,0x49,0x49,0x4a,0x4d,0x50,0x52,0x50,0x4f,0x55, + 0x5b,0x5c,0x5d,0x5d,0x5e,0x5b,0x5e,0x5a,0x4f,0x3d,0x31,0x29,0x1f,0x1d,0x1c,0x22, + 0x95,0x8b,0x85,0x8a,0x8e,0x8c,0x91,0x97,0x9d,0x9e,0x9d,0x9f,0xa0,0x9f,0x9f,0xa0, + 0xa0,0xa0,0xa1,0xa1,0xa0,0xa1,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1, + 0xa1,0xa1,0xa2,0xa2,0xa4,0xa4,0xa5,0xa7,0xaa,0xac,0xaf,0xb1,0xb1,0xb2,0xb3,0xb6, + 0xb6,0xb4,0xb1,0xa9,0x9f,0xa4,0xa8,0xa2,0x96,0x7b,0x5e,0x50,0x59,0x62,0x71,0x82, + 0x89,0x88,0x80,0x7a,0x72,0x73,0x7b,0x79,0x7e,0x7b,0x74,0x69,0x5c,0x42,0x31,0x22, + 0x1b,0x1b,0x1b,0x18,0x16,0x1c,0x23,0x25,0x27,0x26,0x22,0x1e,0x1f,0x26,0x2f,0x38, + 0x40,0x46,0x4c,0x4e,0x4f,0x49,0x44,0x46,0x49,0x49,0x4d,0x4f,0x52,0x4e,0x4b,0x50, + 0x5c,0x5a,0x5a,0x5b,0x5a,0x53,0x54,0x57,0x50,0x41,0x36,0x2c,0x23,0x21,0x20,0x27, + 0x90,0x88,0x84,0x88,0x82,0x84,0x89,0x94,0x9d,0x9c,0x9c,0x9f,0x9f,0x9f,0xa0,0xa0, + 0xa0,0xa0,0xa0,0xa0,0xa0,0xa1,0xa2,0xa1,0xa1,0xa1,0xa0,0xa2,0xa3,0xa3,0xa2,0xa2, + 0xa2,0xa2,0xa2,0xa3,0xa3,0xa5,0xa5,0xa6,0xaa,0xad,0xae,0xaf,0xb0,0xb0,0xb3,0xb3, + 0xb4,0xb4,0xb1,0xaf,0xa6,0xa3,0x99,0x85,0x72,0x58,0x3b,0x29,0x27,0x29,0x33,0x40, + 0x4a,0x4b,0x44,0x36,0x2f,0x2c,0x33,0x44,0x5d,0x6b,0x6e,0x6c,0x5c,0x40,0x2e,0x1e, + 0x16,0x16,0x15,0x14,0x13,0x1c,0x21,0x25,0x25,0x23,0x20,0x1f,0x21,0x26,0x2d,0x3a, + 0x48,0x50,0x51,0x52,0x51,0x4a,0x41,0x3e,0x45,0x43,0x46,0x48,0x49,0x46,0x48,0x4d, + 0x58,0x59,0x55,0x53,0x53,0x4e,0x4e,0x50,0x4f,0x43,0x39,0x2c,0x25,0x22,0x22,0x26, + 0x8a,0x87,0x88,0x8b,0x83,0x81,0x89,0x94,0x9b,0x9c,0x9b,0x9d,0x9f,0x9f,0xa0,0xa0, + 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa1,0xa1,0xa1,0xa1,0xa0,0xa3,0xa4,0xa3,0xa3,0xa2, + 0xa2,0xa2,0xa2,0xa3,0xa3,0xa5,0xa5,0xa6,0xaa,0xac,0xae,0xae,0xae,0xaf,0xb2,0xb3, + 0xb3,0xb4,0xb2,0xaf,0xa8,0x9b,0x87,0x6c,0x53,0x3d,0x2e,0x27,0x22,0x20,0x2b,0x36, + 0x41,0x43,0x3d,0x30,0x27,0x22,0x23,0x30,0x55,0x69,0x74,0x73,0x63,0x45,0x32,0x21, + 0x16,0x13,0x13,0x15,0x14,0x1c,0x24,0x27,0x25,0x25,0x24,0x22,0x24,0x2b,0x33,0x3f, + 0x4b,0x52,0x56,0x56,0x55,0x4d,0x41,0x3a,0x3d,0x3a,0x3c,0x3e,0x3d,0x3b,0x41,0x47, + 0x50,0x57,0x53,0x4e,0x4d,0x4a,0x4c,0x4b,0x47,0x41,0x38,0x2c,0x22,0x21,0x23,0x24, + 0x86,0x83,0x82,0x8b,0x84,0x83,0x8a,0x95,0x9c,0x9b,0x9a,0x9c,0x9f,0xa0,0xa0,0x9f, + 0x9f,0x9f,0x9f,0x9f,0x9f,0xa0,0xa0,0xa1,0xa2,0xa1,0xa1,0xa1,0xa2,0xa2,0xa2,0xa2, + 0xa2,0xa2,0xa2,0xa2,0xa4,0xa4,0xa5,0xa7,0xa9,0xac,0xac,0xad,0xae,0xb0,0xb2,0xb2, + 0xb3,0xb1,0xaf,0xaa,0xa1,0x8b,0x76,0x56,0x36,0x2e,0x2b,0x27,0x1e,0x1b,0x27,0x33, + 0x37,0x38,0x35,0x2d,0x26,0x21,0x27,0x34,0x5b,0x70,0x79,0x76,0x69,0x48,0x36,0x25, + 0x1a,0x14,0x14,0x15,0x15,0x1a,0x20,0x25,0x25,0x26,0x25,0x23,0x28,0x31,0x3a,0x44, + 0x4c,0x50,0x56,0x52,0x4e,0x45,0x3a,0x36,0x37,0x38,0x3b,0x3b,0x32,0x33,0x3a,0x41, + 0x4c,0x51,0x52,0x4e,0x46,0x43,0x42,0x42,0x42,0x3e,0x37,0x2c,0x22,0x20,0x23,0x22, + 0x88,0x86,0x85,0x87,0x82,0x82,0x8c,0x97,0x9d,0x9b,0x9a,0x9c,0x9e,0x9f,0xa0,0x9f, + 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0xa0,0xa1,0xa1,0xa1,0xa1,0xa1,0xa2,0xa2,0xa2,0xa2, + 0xa2,0xa2,0xa2,0xa2,0xa4,0xa4,0xa5,0xa7,0xa9,0xac,0xac,0xad,0xae,0xb0,0xb1,0xb2, + 0xb0,0xac,0xa7,0xa1,0x9a,0x82,0x65,0x3f,0x2a,0x2a,0x2b,0x26,0x20,0x1a,0x20,0x2b, + 0x2f,0x31,0x29,0x24,0x22,0x22,0x2a,0x39,0x60,0x71,0x7a,0x7a,0x6c,0x4d,0x3b,0x2b, + 0x1e,0x15,0x13,0x14,0x15,0x19,0x20,0x23,0x25,0x27,0x25,0x24,0x28,0x33,0x3c,0x45, + 0x4c,0x51,0x54,0x50,0x47,0x3e,0x37,0x33,0x31,0x37,0x36,0x35,0x32,0x33,0x3b,0x46, + 0x4f,0x52,0x54,0x47,0x3d,0x38,0x37,0x38,0x3a,0x3a,0x37,0x2c,0x24,0x1f,0x20,0x21, + 0x87,0x88,0x88,0x8d,0x82,0x85,0x8d,0x98,0x9f,0x9b,0x9c,0x9b,0x9d,0x9e,0xa0,0xa0, + 0xa0,0xa0,0x9f,0x9f,0x9e,0x9f,0x9f,0xa0,0xa1,0xa0,0xa0,0xa0,0xa1,0xa1,0xa2,0xa2, + 0xa2,0xa2,0xa2,0xa2,0xa4,0xa4,0xa5,0xa7,0xa9,0xab,0xac,0xad,0xae,0xb0,0xb1,0xb1, + 0xb0,0xab,0xa0,0x9a,0x94,0x83,0x6c,0x48,0x31,0x27,0x27,0x20,0x1b,0x19,0x17,0x20, + 0x29,0x28,0x22,0x1c,0x1d,0x1e,0x27,0x38,0x5f,0x70,0x77,0x75,0x6c,0x51,0x42,0x32, + 0x23,0x19,0x15,0x17,0x16,0x19,0x21,0x24,0x26,0x26,0x24,0x22,0x28,0x30,0x3a,0x43, + 0x49,0x4e,0x4c,0x47,0x3c,0x38,0x34,0x35,0x35,0x37,0x36,0x36,0x37,0x39,0x3f,0x4a, + 0x51,0x55,0x4f,0x46,0x3a,0x32,0x2e,0x31,0x34,0x35,0x32,0x2c,0x29,0x22,0x21,0x20, + 0x8c,0x8f,0x8d,0x8b,0x7f,0x82,0x8a,0x99,0xa1,0x9e,0x9c,0x9b,0x9c,0x9f,0x9f,0xa2, + 0xa2,0xa2,0xa2,0xa0,0xa0,0xa0,0x9f,0xa0,0xa0,0xa0,0xa0,0xa0,0xa1,0xa2,0xa2,0xa2, + 0xa2,0xa2,0xa2,0xa3,0xa3,0xa5,0xa5,0xa7,0xa9,0xab,0xad,0xae,0xae,0xaf,0xb0,0xb1, + 0xb0,0xab,0x9e,0x94,0x8f,0x88,0x72,0x55,0x39,0x27,0x21,0x1a,0x13,0x10,0x15,0x1b, + 0x20,0x21,0x1c,0x1a,0x1e,0x1f,0x28,0x3b,0x5b,0x6a,0x71,0x70,0x68,0x57,0x48,0x3c, + 0x2c,0x20,0x1a,0x19,0x19,0x20,0x28,0x28,0x28,0x27,0x27,0x28,0x2d,0x32,0x3d,0x46, + 0x49,0x4b,0x4a,0x42,0x39,0x33,0x2f,0x30,0x37,0x38,0x3a,0x3b,0x44,0x43,0x47,0x4e, + 0x54,0x53,0x4a,0x42,0x35,0x29,0x26,0x28,0x2e,0x2f,0x2e,0x2e,0x2c,0x29,0x26,0x24, + 0x8f,0x92,0x8d,0x8b,0x7e,0x81,0x89,0x9a,0xa0,0xa0,0x9b,0x9b,0x9b,0x9e,0xa0,0xa1, + 0xa1,0xa0,0xa1,0xa1,0xa0,0x9f,0x9f,0xa0,0xa0,0xa0,0xa0,0xa0,0xa1,0xa2,0xa2,0xa2, + 0xa2,0xa2,0xa2,0xa3,0xa3,0xa5,0xa5,0xa7,0xa9,0xab,0xad,0xae,0xae,0xaf,0xb0,0xb1, + 0xaf,0xaa,0x9e,0x92,0x8d,0x89,0x79,0x61,0x50,0x40,0x39,0x25,0x11,0x0d,0x12,0x19, + 0x1f,0x21,0x21,0x1f,0x21,0x22,0x2a,0x3c,0x56,0x63,0x6e,0x6a,0x61,0x55,0x4b,0x3f, + 0x31,0x25,0x1d,0x1c,0x1e,0x22,0x27,0x28,0x28,0x28,0x2c,0x2e,0x30,0x39,0x42,0x49, + 0x4b,0x4d,0x4c,0x46,0x3d,0x36,0x2e,0x30,0x38,0x38,0x3a,0x3c,0x43,0x47,0x47,0x4f, + 0x50,0x4e,0x46,0x3d,0x30,0x26,0x22,0x25,0x29,0x2a,0x2c,0x2d,0x30,0x2c,0x29,0x26, + 0x93,0x92,0x8c,0x88,0x7b,0x82,0x8d,0x9a,0xa0,0xa0,0x9d,0x9c,0x9c,0x9d,0x9e,0xa1, + 0xa1,0xa2,0xa1,0xa1,0xa1,0xa0,0x9f,0x9f,0x9f,0xa0,0xa0,0xa0,0xa1,0xa2,0xa2,0xa2, + 0xa2,0xa1,0xa2,0xa2,0xa3,0xa4,0xa5,0xa6,0xa9,0xab,0xac,0xad,0xaf,0xb0,0xb2,0xb3, + 0xb1,0xab,0x9e,0x92,0x8b,0x88,0x83,0x78,0x78,0x75,0x61,0x44,0x23,0x11,0x15,0x1d, + 0x24,0x27,0x29,0x25,0x25,0x25,0x2b,0x3a,0x52,0x60,0x67,0x65,0x5e,0x59,0x51,0x46, + 0x38,0x2c,0x24,0x22,0x23,0x26,0x27,0x2a,0x2c,0x2f,0x33,0x33,0x36,0x3f,0x45,0x4c, + 0x51,0x52,0x50,0x4b,0x42,0x3d,0x38,0x34,0x36,0x39,0x37,0x36,0x3b,0x41,0x43,0x46, + 0x46,0x43,0x3c,0x35,0x2c,0x25,0x21,0x24,0x28,0x2e,0x2f,0x32,0x32,0x2e,0x30,0x2e, + 0x90,0x8d,0x88,0x83,0x79,0x83,0x8e,0x9b,0xa0,0xa0,0x9e,0x9c,0x9b,0x9b,0x9c,0xa0, + 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0x9f,0x9f,0x9f,0xa0,0xa0,0xa0,0xa1,0xa2,0xa1,0xa1, + 0xa1,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xaa,0xac,0xac,0xad,0xaf,0xb0,0xb3,0xb3, + 0xb1,0xab,0x9d,0x8f,0x87,0x89,0x8d,0x91,0x9d,0x97,0x7d,0x5d,0x38,0x1e,0x1b,0x26, + 0x2c,0x31,0x31,0x2b,0x2a,0x25,0x29,0x3b,0x51,0x5d,0x62,0x62,0x5c,0x5a,0x54,0x4b, + 0x3e,0x35,0x2d,0x26,0x26,0x29,0x29,0x2b,0x2e,0x31,0x31,0x35,0x3a,0x47,0x4c,0x51, + 0x53,0x57,0x56,0x4d,0x47,0x40,0x3b,0x33,0x33,0x34,0x33,0x33,0x35,0x37,0x3c,0x3a, + 0x37,0x31,0x2e,0x29,0x26,0x24,0x21,0x25,0x29,0x2d,0x30,0x35,0x37,0x33,0x33,0x32, + 0x8c,0x87,0x7f,0x7e,0x7b,0x85,0x90,0x9c,0x9f,0x9e,0x9e,0x9c,0x9b,0x9a,0x9d,0x9e, + 0x9f,0xa0,0xa1,0xa1,0xa1,0xa1,0x9f,0x9f,0x9f,0xa0,0xa0,0xa0,0xa1,0xa2,0xa1,0xa1, + 0xa1,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xaa,0xac,0xac,0xad,0xaf,0xb0,0xb3,0xb2, + 0xb3,0xad,0x9d,0x8e,0x88,0x87,0x93,0xa4,0xb1,0xa9,0x90,0x6d,0x4c,0x2f,0x2c,0x30, + 0x38,0x3f,0x3a,0x32,0x2c,0x26,0x29,0x3b,0x50,0x5c,0x62,0x61,0x5b,0x5b,0x57,0x4f, + 0x45,0x3d,0x32,0x2b,0x2a,0x2e,0x2e,0x2c,0x2a,0x2e,0x34,0x3a,0x40,0x4a,0x50,0x53, + 0x55,0x58,0x5a,0x51,0x4b,0x45,0x40,0x37,0x34,0x30,0x30,0x31,0x31,0x33,0x34,0x30, + 0x2c,0x27,0x25,0x21,0x20,0x22,0x24,0x28,0x2e,0x31,0x36,0x39,0x3b,0x39,0x36,0x37, + 0x8b,0x86,0x7a,0x79,0x7b,0x87,0x90,0x9a,0x9d,0x9e,0x9e,0x9c,0x9b,0x9b,0x9b,0x9e, + 0xa0,0x9f,0xa0,0xa1,0xa1,0xa0,0x9e,0x9f,0x9f,0xa0,0xa0,0xa2,0xa2,0xa2,0xa1,0xa1, + 0xa1,0xa1,0xa1,0xa1,0xa4,0xa5,0xa7,0xa8,0xab,0xac,0xac,0xad,0xaf,0xb0,0xb3,0xb3, + 0xb2,0xae,0xa2,0x97,0x8d,0x89,0x90,0xa6,0xb1,0xad,0x99,0x73,0x4f,0x3a,0x3a,0x3d, + 0x3f,0x43,0x3d,0x34,0x2a,0x24,0x27,0x3a,0x4f,0x5b,0x61,0x60,0x5b,0x5a,0x5a,0x53, + 0x4b,0x43,0x3b,0x35,0x32,0x34,0x2e,0x2b,0x28,0x2c,0x31,0x37,0x42,0x48,0x4f,0x53, + 0x56,0x57,0x56,0x51,0x4c,0x46,0x41,0x39,0x37,0x30,0x2c,0x2b,0x2b,0x2d,0x2b,0x2a, + 0x26,0x21,0x1d,0x1a,0x1c,0x1e,0x25,0x2a,0x31,0x35,0x39,0x3b,0x3e,0x3c,0x3a,0x3a, + 0x8e,0x86,0x77,0x77,0x78,0x89,0x90,0x98,0x9c,0x9c,0x9e,0x9d,0x9d,0x9c,0x9b,0x9c, + 0x9c,0x9d,0x9f,0x9f,0xa0,0x9f,0x9f,0x9f,0x9f,0xa0,0x9f,0xa1,0xa1,0xa1,0xa1,0xa1, + 0xa1,0xa1,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xab,0xac,0xac,0xad,0xaf,0xb0,0xb3,0xb3, + 0xb2,0xb1,0xa9,0x9a,0x8e,0x8b,0x8f,0xa1,0xa8,0xa7,0x96,0x6c,0x46,0x45,0x49,0x47, + 0x47,0x43,0x3b,0x34,0x29,0x24,0x26,0x3b,0x4e,0x5a,0x5f,0x5f,0x5a,0x59,0x5d,0x59, + 0x54,0x4a,0x43,0x3e,0x39,0x38,0x33,0x2f,0x2d,0x31,0x37,0x3b,0x43,0x47,0x4d,0x53, + 0x57,0x58,0x54,0x4f,0x4d,0x44,0x41,0x3a,0x35,0x2f,0x29,0x27,0x28,0x28,0x2a,0x2b, + 0x28,0x24,0x1e,0x18,0x19,0x1d,0x21,0x26,0x2c,0x34,0x39,0x3d,0x3e,0x40,0x3e,0x3e, + 0x92,0x88,0x79,0x75,0x7c,0x89,0x90,0x97,0x9b,0x9c,0x9c,0x9d,0x9d,0x9d,0x9c,0x9a, + 0x9b,0x9d,0x9e,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0xa0,0xa0,0xa0,0xa0,0xa1, + 0xa1,0xa1,0xa1,0xa1,0xa4,0xa5,0xa7,0xa8,0xab,0xac,0xac,0xad,0xaf,0xb0,0xb3,0xb3, + 0xb4,0xb2,0xa9,0x9c,0x92,0x88,0x8a,0x9b,0xa7,0xa7,0xa1,0x80,0x63,0x5a,0x54,0x4b, + 0x46,0x43,0x3c,0x36,0x2e,0x22,0x25,0x3c,0x4f,0x5a,0x5e,0x5c,0x57,0x5a,0x5d,0x5c, + 0x55,0x4f,0x48,0x44,0x3b,0x38,0x36,0x35,0x32,0x36,0x3b,0x3e,0x46,0x49,0x4c,0x51, + 0x53,0x51,0x51,0x4f,0x4a,0x42,0x3c,0x38,0x34,0x2a,0x27,0x28,0x27,0x2a,0x2d,0x2e, + 0x2a,0x27,0x22,0x1a,0x16,0x19,0x1f,0x24,0x28,0x2f,0x33,0x38,0x3b,0x3a,0x3b,0x3c, + 0x94,0x8a,0x76,0x7a,0x85,0x90,0x93,0x97,0x9a,0x9b,0x9c,0x9d,0x9d,0x9f,0x9d,0x9c, + 0x9a,0x9c,0x9d,0x9e,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0xa0,0xa0,0xa0,0xa0, + 0xa1,0xa1,0xa1,0xa1,0xa3,0xa4,0xa6,0xa8,0xaa,0xab,0xad,0xae,0xaf,0xb0,0xb3,0xb3, + 0xb3,0xb1,0xa6,0x99,0x8d,0x85,0x8b,0x9f,0xac,0xae,0xae,0x9e,0x8b,0x74,0x60,0x4f, + 0x48,0x45,0x3f,0x35,0x2c,0x24,0x2a,0x3e,0x50,0x59,0x5e,0x5c,0x59,0x5b,0x5d,0x5d, + 0x58,0x53,0x4d,0x46,0x3e,0x38,0x35,0x35,0x36,0x39,0x3d,0x41,0x47,0x4a,0x4c,0x4d, + 0x50,0x4e,0x4e,0x4d,0x44,0x3d,0x3b,0x37,0x33,0x28,0x26,0x26,0x25,0x29,0x30,0x33, + 0x32,0x31,0x2b,0x20,0x1b,0x1d,0x21,0x24,0x26,0x2c,0x30,0x33,0x34,0x37,0x37,0x35, + 0x95,0x85,0x74,0x7e,0x8b,0x96,0x97,0x96,0x99,0x99,0x9a,0x9d,0x9d,0x9f,0x9f,0x9e, + 0x9b,0x9b,0x9a,0x9d,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0xa0,0xa0,0xa0,0xa0, + 0xa1,0xa1,0xa1,0xa1,0xa3,0xa4,0xa6,0xa7,0xaa,0xab,0xad,0xae,0xaf,0xb0,0xb3,0xb3, + 0xb3,0xb0,0xa3,0x97,0x8b,0x85,0x8b,0xa2,0xb6,0xbd,0xc0,0xaf,0x9e,0x87,0x6b,0x50, + 0x48,0x46,0x3e,0x33,0x2b,0x25,0x2a,0x41,0x51,0x5b,0x5f,0x5d,0x58,0x5b,0x5d,0x5d, + 0x5b,0x55,0x4d,0x44,0x3b,0x38,0x34,0x33,0x35,0x38,0x3c,0x41,0x49,0x4a,0x4a,0x48, + 0x49,0x4a,0x4b,0x48,0x40,0x3c,0x37,0x35,0x30,0x2d,0x2b,0x29,0x25,0x2a,0x35,0x3b, + 0x39,0x37,0x33,0x2d,0x28,0x27,0x2a,0x2a,0x28,0x29,0x2c,0x2f,0x32,0x36,0x35,0x33, + 0x8f,0x7e,0x77,0x87,0x96,0x9a,0x96,0x96,0x99,0x99,0x99,0x9b,0x9e,0xa0,0xa1,0xa0, + 0x9d,0x9a,0x99,0x9b,0x9e,0x9e,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0xa0, + 0xa1,0xa1,0xa1,0xa2,0xa4,0xa4,0xa6,0xa6,0xa9,0xab,0xad,0xae,0xaf,0xb0,0xb3,0xb3, + 0xb4,0xaf,0xa4,0x96,0x8c,0x89,0x92,0xa6,0xbb,0xc9,0xcb,0xbf,0xac,0x95,0x72,0x53, + 0x4b,0x47,0x3f,0x34,0x2f,0x29,0x2e,0x46,0x57,0x5e,0x62,0x5e,0x57,0x5b,0x5d,0x5f, + 0x5d,0x57,0x4e,0x47,0x3d,0x38,0x33,0x33,0x37,0x39,0x3e,0x42,0x48,0x47,0x48,0x48, + 0x49,0x47,0x49,0x46,0x41,0x3f,0x3e,0x37,0x33,0x33,0x33,0x32,0x30,0x35,0x3d,0x47, + 0x44,0x41,0x3e,0x39,0x37,0x34,0x32,0x32,0x31,0x2e,0x2c,0x2e,0x32,0x35,0x35,0x31, + 0x82,0x77,0x7a,0x8b,0x96,0x96,0x94,0x94,0x96,0x96,0x98,0x98,0x9b,0x9f,0xa1,0xa0, + 0x9e,0x9c,0x99,0x97,0x9c,0x9d,0x9e,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0xa0, + 0xa1,0xa1,0xa1,0xa2,0xa4,0xa4,0xa6,0xa6,0xa9,0xab,0xad,0xae,0xaf,0xb0,0xb3,0xb3, + 0xb3,0xaf,0xa4,0x97,0x8d,0x8d,0x99,0xab,0xbe,0xc9,0xcc,0xc4,0xb3,0x97,0x76,0x56, + 0x4b,0x49,0x40,0x34,0x2e,0x29,0x30,0x49,0x59,0x61,0x63,0x5f,0x58,0x5b,0x5e,0x5f, + 0x5e,0x59,0x50,0x49,0x3d,0x37,0x34,0x38,0x39,0x3b,0x3d,0x40,0x42,0x42,0x42,0x42, + 0x43,0x42,0x43,0x43,0x42,0x41,0x41,0x3c,0x37,0x36,0x36,0x34,0x37,0x3b,0x43,0x48, + 0x48,0x49,0x48,0x44,0x42,0x41,0x3c,0x3b,0x3c,0x36,0x31,0x32,0x33,0x34,0x33,0x2e, + 0x7d,0x7a,0x7b,0x87,0x88,0x88,0x8a,0x8f,0x93,0x96,0x97,0x96,0x98,0x9c,0xa0,0x9f, + 0x9f,0x9d,0x9a,0x98,0x99,0x9c,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9f,0x9f,0x9f,0xa0, + 0xa1,0xa1,0xa1,0xa2,0xa4,0xa4,0xa6,0xa6,0xa9,0xaa,0xac,0xae,0xaf,0xb0,0xb3,0xb3, + 0xb3,0xaf,0xa5,0x9a,0x94,0x98,0xa2,0xb0,0xbe,0xc6,0xc9,0xc2,0xaf,0x95,0x71,0x55, + 0x49,0x44,0x3c,0x33,0x2f,0x2a,0x32,0x4d,0x5c,0x64,0x64,0x5f,0x59,0x5c,0x5e,0x5f, + 0x5e,0x59,0x52,0x4b,0x40,0x3a,0x3a,0x39,0x38,0x39,0x3b,0x3e,0x41,0x40,0x3d,0x3f, + 0x3c,0x3f,0x40,0x42,0x45,0x46,0x45,0x41,0x3c,0x3a,0x38,0x38,0x3a,0x3d,0x45,0x48, + 0x4c,0x4f,0x50,0x4e,0x4d,0x4d,0x47,0x46,0x49,0x42,0x3e,0x39,0x3a,0x3a,0x38,0x33, + 0x7c,0x81,0x7e,0x7f,0x7b,0x7e,0x80,0x89,0x90,0x94,0x95,0x95,0x97,0x99,0x9b,0x9f, + 0xa1,0x9d,0x99,0x98,0x96,0x99,0x9a,0x9c,0x9d,0x9d,0x9d,0x9d,0x9e,0x9f,0x9f,0x9f, + 0x9f,0xa1,0xa2,0xa2,0xa3,0xa4,0xa5,0xa7,0xa8,0xa9,0xac,0xad,0xaf,0xb0,0xb2,0xb2, + 0xb3,0xaf,0xa4,0x9c,0x9a,0x9f,0xa8,0xb3,0xbc,0xc4,0xc7,0xbd,0xab,0x8e,0x6a,0x49, + 0x42,0x3e,0x3a,0x36,0x30,0x2b,0x34,0x4e,0x5f,0x65,0x65,0x5e,0x59,0x5c,0x5e,0x5f, + 0x5f,0x5d,0x56,0x4d,0x45,0x40,0x41,0x3f,0x3e,0x3e,0x3f,0x40,0x42,0x44,0x40,0x39, + 0x35,0x37,0x3b,0x41,0x46,0x45,0x43,0x3d,0x3b,0x38,0x34,0x33,0x38,0x40,0x44,0x48, + 0x4d,0x50,0x51,0x4e,0x4f,0x4f,0x4c,0x4c,0x4b,0x44,0x3f,0x3c,0x3b,0x3a,0x39,0x37, + 0x81,0x85,0x84,0x7e,0x79,0x79,0x7c,0x83,0x88,0x8d,0x91,0x92,0x97,0x99,0x9a,0x9d, + 0xa0,0x9f,0x9c,0x9a,0x98,0x98,0x99,0x9c,0x9e,0x9d,0x9d,0x9d,0x9f,0x9f,0x9f,0x9f, + 0x9f,0xa0,0xa1,0xa2,0xa2,0xa3,0xa5,0xa6,0xa7,0xa8,0xaa,0xac,0xae,0xaf,0xb2,0xb3, + 0xb3,0xaf,0xaa,0xa6,0xa1,0xa4,0xab,0xb6,0xc0,0xc8,0xc7,0xbc,0xa6,0x85,0x5e,0x41, + 0x3f,0x3e,0x3e,0x38,0x30,0x2b,0x38,0x52,0x62,0x68,0x67,0x5e,0x59,0x5c,0x5e,0x60, + 0x5f,0x5d,0x57,0x4e,0x47,0x45,0x45,0x43,0x42,0x43,0x45,0x42,0x41,0x42,0x41,0x3a, + 0x32,0x32,0x36,0x39,0x3f,0x3e,0x3d,0x39,0x37,0x35,0x2f,0x2f,0x35,0x3b,0x42,0x4a, + 0x4e,0x50,0x50,0x50,0x50,0x4f,0x49,0x49,0x49,0x43,0x3e,0x3d,0x3b,0x37,0x39,0x3b, + 0x84,0x90,0x8e,0x85,0x7f,0x7d,0x80,0x82,0x84,0x89,0x8b,0x8e,0x94,0x97,0x99,0x9c, + 0x9f,0x9f,0x9f,0x9c,0x99,0x97,0x97,0x99,0x9d,0x9f,0x9d,0x9d,0x9d,0x9d,0x9e,0x9f, + 0x9f,0x9f,0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa7,0xa8,0xa9,0xac,0xad,0xaf,0xb1,0xb0, + 0xb2,0xb1,0xb2,0xae,0xa8,0xa6,0xad,0xb9,0xc3,0xce,0xc9,0xb9,0xa0,0x79,0x51,0x3d, + 0x3e,0x3d,0x3e,0x37,0x30,0x2d,0x3e,0x57,0x66,0x6b,0x68,0x5e,0x57,0x5c,0x5f,0x5f, + 0x60,0x5b,0x53,0x4c,0x43,0x40,0x41,0x43,0x41,0x3f,0x42,0x42,0x3f,0x3d,0x3b,0x35, + 0x2d,0x2b,0x2f,0x31,0x34,0x35,0x36,0x34,0x2e,0x2d,0x28,0x27,0x2e,0x34,0x3b,0x43, + 0x4b,0x50,0x4f,0x51,0x52,0x4f,0x4a,0x46,0x45,0x41,0x3e,0x3b,0x3c,0x37,0x37,0x3c, + 0x99,0x9b,0x99,0x90,0x8a,0x86,0x83,0x83,0x85,0x88,0x8b,0x8e,0x90,0x91,0x97,0x9d, + 0x9f,0xa0,0x9e,0x9c,0x9c,0x9b,0x98,0x98,0x9c,0x9d,0x9f,0x9f,0x9d,0x9d,0x9e,0x9f, + 0x9f,0x9f,0xa0,0xa1,0xa2,0xa3,0xa4,0xa4,0xa7,0xa7,0xa9,0xab,0xad,0xae,0xb0,0xb2, + 0xb6,0xbb,0xbb,0xb5,0xaf,0xac,0xb1,0xbe,0xcb,0xd2,0xcb,0xb8,0x98,0x6e,0x46,0x3d, + 0x3c,0x3b,0x3a,0x34,0x30,0x2f,0x42,0x5c,0x6a,0x71,0x6b,0x5f,0x58,0x5c,0x5f,0x5f, + 0x5f,0x58,0x4e,0x46,0x3e,0x3c,0x3f,0x41,0x40,0x3e,0x3d,0x40,0x3c,0x39,0x35,0x30, + 0x29,0x28,0x29,0x29,0x2a,0x2c,0x2f,0x2a,0x26,0x25,0x24,0x23,0x2a,0x34,0x3a,0x3f, + 0x47,0x4e,0x50,0x53,0x53,0x50,0x4b,0x47,0x42,0x3d,0x3a,0x39,0x3a,0x37,0x39,0x3b, + 0x9d,0xa3,0xa3,0x98,0x8c,0x89,0x8a,0x8b,0x88,0x88,0x8a,0x8d,0x8d,0x8b,0x8e,0x92, + 0x99,0x9c,0x9a,0x9c,0x9c,0x9a,0x93,0x93,0x95,0x9a,0x9e,0x9f,0x9d,0x9e,0x9e,0x9f, + 0x9f,0x9f,0xa0,0xa1,0xa2,0xa3,0xa4,0xa4,0xa7,0xa7,0xa8,0xab,0xac,0xae,0xb2,0xb8, + 0xbe,0xc0,0xbe,0xba,0xb0,0xac,0xb4,0xc1,0xce,0xd1,0xca,0xb3,0x91,0x61,0x41,0x3e, + 0x3f,0x3d,0x3a,0x32,0x2c,0x2b,0x43,0x61,0x70,0x76,0x6e,0x60,0x5a,0x5c,0x5f,0x60, + 0x5f,0x55,0x49,0x3d,0x37,0x36,0x39,0x41,0x41,0x3c,0x35,0x35,0x33,0x32,0x2e,0x2b, + 0x26,0x23,0x26,0x25,0x27,0x26,0x26,0x25,0x21,0x23,0x22,0x22,0x2b,0x35,0x3a,0x42, + 0x49,0x4f,0x51,0x55,0x52,0x4e,0x4f,0x46,0x40,0x39,0x37,0x37,0x36,0x3a,0x3b,0x39, + 0x9f,0xaa,0xa9,0x9c,0x91,0x8b,0x8b,0x8d,0x8d,0x8c,0x8c,0x8c,0x87,0x81,0x7f,0x7f, + 0x85,0x89,0x8e,0x91,0x8d,0x84,0x7a,0x78,0x83,0x91,0x9d,0x9e,0x9e,0x9e,0x9e,0x9e, + 0x9e,0x9f,0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa5,0xa6,0xa8,0xab,0xae,0xb3,0xbb,0xc0, + 0xc4,0xc6,0xc2,0xbb,0xb4,0xb0,0xb7,0xc6,0xd2,0xd0,0xc4,0xa8,0x83,0x55,0x43,0x46, + 0x3f,0x3d,0x39,0x30,0x27,0x27,0x45,0x67,0x77,0x7a,0x73,0x62,0x59,0x5c,0x5f,0x60, + 0x5c,0x50,0x42,0x35,0x2f,0x32,0x36,0x39,0x3c,0x39,0x31,0x30,0x2f,0x2d,0x28,0x23, + 0x21,0x20,0x20,0x23,0x23,0x22,0x21,0x21,0x23,0x24,0x25,0x27,0x2c,0x39,0x41,0x45, + 0x49,0x4e,0x50,0x52,0x51,0x4e,0x4d,0x47,0x40,0x39,0x38,0x37,0x37,0x3a,0x3a,0x38, + 0x98,0xa9,0xaa,0xa1,0x95,0x8d,0x8a,0x8a,0x8d,0x8e,0x8d,0x8a,0x83,0x76,0x6a,0x65, + 0x6c,0x74,0x77,0x75,0x6f,0x65,0x5d,0x5c,0x6e,0x86,0x98,0x9c,0x9c,0x9e,0x9e,0x9e, + 0x9e,0x9f,0xa0,0xa1,0xa2,0xa3,0xa3,0xa4,0xa5,0xa5,0xa8,0xad,0xb4,0xbc,0xc3,0xc6, + 0xc8,0xc8,0xc2,0xbc,0xb7,0xb5,0xbe,0xcc,0xd0,0xc9,0xbc,0x9c,0x73,0x4e,0x4c,0x4b, + 0x43,0x3b,0x34,0x29,0x1f,0x25,0x45,0x6b,0x7a,0x7d,0x75,0x64,0x58,0x5c,0x5f,0x5d, + 0x5a,0x4d,0x3d,0x30,0x2a,0x2e,0x32,0x38,0x3b,0x38,0x31,0x2d,0x2a,0x26,0x22,0x1f, + 0x20,0x1f,0x1e,0x20,0x20,0x20,0x1f,0x1f,0x22,0x23,0x26,0x27,0x2d,0x3b,0x40,0x47, + 0x4b,0x4e,0x4e,0x4f,0x4e,0x4d,0x4b,0x45,0x3f,0x39,0x36,0x37,0x39,0x37,0x39,0x37, + 0x92,0xa6,0xad,0xa5,0x9c,0x92,0x8e,0x8c,0x8d,0x8c,0x8c,0x8a,0x80,0x70,0x61,0x55, + 0x56,0x5a,0x5d,0x5d,0x56,0x49,0x41,0x43,0x64,0x84,0x97,0x9d,0x9c,0x9d,0x9e,0x9e, + 0x9e,0x9f,0xa0,0xa2,0xa3,0xa3,0xa3,0xa5,0xa7,0xac,0xb2,0xba,0xbf,0xc4,0xc7,0xca, + 0xc8,0xc8,0xc4,0xbd,0xb9,0xbc,0xc8,0xd1,0xce,0xc3,0xae,0x8c,0x60,0x4d,0x51,0x4e, + 0x45,0x39,0x2e,0x26,0x1d,0x25,0x46,0x6f,0x7d,0x7f,0x79,0x66,0x58,0x5d,0x5f,0x5f, + 0x57,0x48,0x39,0x2d,0x26,0x2e,0x39,0x3d,0x3e,0x3c,0x36,0x30,0x2a,0x28,0x25,0x23, + 0x21,0x1f,0x1e,0x1e,0x1e,0x1e,0x1c,0x1f,0x21,0x22,0x27,0x2c,0x36,0x3e,0x45,0x49, + 0x4c,0x4e,0x4c,0x4b,0x4a,0x49,0x43,0x3f,0x3c,0x39,0x34,0x35,0x37,0x3b,0x3b,0x3c, + 0x95,0xa8,0xaf,0xa7,0x9d,0x98,0x8d,0x8f,0x8c,0x8b,0x8b,0x8c,0x7f,0x70,0x61,0x58, + 0x52,0x51,0x53,0x52,0x50,0x4c,0x47,0x47,0x73,0x91,0x9f,0x9f,0x9e,0x9c,0x9d,0x9e, + 0x9e,0x9f,0xa1,0xa1,0xa3,0xa4,0xa8,0xaa,0xb2,0xbb,0xc0,0xc6,0xc8,0xc9,0xc9,0xc8, + 0xc8,0xc8,0xc4,0xc0,0xbf,0xc6,0xd1,0xd4,0xc9,0xbc,0x9e,0x78,0x56,0x51,0x53,0x4e, + 0x45,0x3a,0x2e,0x21,0x1a,0x22,0x47,0x72,0x81,0x83,0x7b,0x67,0x59,0x5d,0x5e,0x5d, + 0x54,0x44,0x39,0x2e,0x29,0x32,0x3b,0x3e,0x3e,0x3c,0x38,0x34,0x30,0x2c,0x28,0x27, + 0x24,0x1f,0x1d,0x1d,0x1e,0x1d,0x1d,0x1d,0x21,0x22,0x28,0x2f,0x38,0x3f,0x47,0x4b, + 0x4c,0x4c,0x4a,0x49,0x44,0x41,0x3b,0x39,0x38,0x3a,0x38,0x36,0x38,0x3c,0x40,0x41, + 0x95,0xa7,0xab,0xa8,0xa1,0x9c,0x92,0x8f,0x8c,0x89,0x8a,0x89,0x80,0x71,0x5e,0x58, + 0x54,0x52,0x52,0x52,0x51,0x4d,0x4d,0x5f,0x8c,0xa4,0xab,0xa4,0x9e,0x9c,0x9c,0x9d, + 0x9e,0x9e,0xa0,0xa0,0x9f,0xa7,0xb1,0xb8,0xbf,0xc7,0xcc,0xcf,0xce,0xcc,0xc9,0xc6, + 0xc6,0xc9,0xc7,0xc4,0xc4,0xcf,0xd9,0xd8,0xcb,0xb2,0x8b,0x60,0x53,0x52,0x52,0x4d, + 0x46,0x3c,0x2c,0x22,0x1e,0x25,0x48,0x74,0x83,0x85,0x7d,0x6b,0x5b,0x5d,0x5f,0x5b, + 0x50,0x42,0x36,0x2d,0x2b,0x31,0x3a,0x3f,0x3d,0x39,0x38,0x34,0x32,0x30,0x2e,0x2b, + 0x28,0x21,0x1e,0x1d,0x1b,0x19,0x1a,0x19,0x1d,0x22,0x27,0x30,0x37,0x3f,0x47,0x4b, + 0x4d,0x4b,0x4b,0x48,0x44,0x3f,0x3a,0x35,0x36,0x37,0x37,0x38,0x40,0x42,0x45,0x4a, + 0x93,0xa2,0xa6,0xa5,0xa1,0x9c,0x99,0x90,0x8c,0x89,0x88,0x88,0x80,0x73,0x65,0x59, + 0x56,0x53,0x52,0x52,0x52,0x4e,0x4c,0x77,0xa3,0xb5,0xb4,0xa8,0xa0,0x9c,0x9c,0x9c, + 0x9e,0x9d,0x9f,0xa0,0xa3,0xb0,0xbb,0xc6,0xcc,0xce,0xd2,0xd3,0xcf,0xcd,0xc8,0xc4, + 0xc6,0xc8,0xc8,0xc6,0xcb,0xd8,0xdb,0xdb,0xc9,0xad,0x7e,0x55,0x52,0x52,0x50,0x4a, + 0x47,0x3f,0x2d,0x25,0x21,0x26,0x4c,0x72,0x84,0x86,0x7d,0x6b,0x5d,0x5d,0x5e,0x58, + 0x4c,0x3e,0x33,0x2b,0x2f,0x35,0x3b,0x3e,0x3e,0x3b,0x34,0x32,0x30,0x32,0x2f,0x2b, + 0x29,0x25,0x20,0x1f,0x1c,0x1d,0x1c,0x1b,0x1e,0x23,0x27,0x2f,0x38,0x3e,0x45,0x49, + 0x4a,0x48,0x49,0x48,0x46,0x42,0x3a,0x35,0x33,0x37,0x3a,0x3f,0x45,0x48,0x49,0x4f, + 0xb8,0x9c,0xa1,0xa3,0xa2,0x9c,0x99,0x94,0x8e,0x8a,0x88,0x84,0x7f,0x75,0x67,0x5c, + 0x55,0x52,0x52,0x51,0x50,0x4c,0x5c,0x96,0xb6,0xc1,0xbb,0xaf,0xa5,0x9d,0x9e,0x9e, + 0xa2,0xa7,0xa9,0xaa,0xb0,0xbc,0xc4,0xcc,0xd0,0xd1,0xd1,0xd2,0xd0,0xcb,0xc6,0xc4, + 0xc6,0xc8,0xc7,0xcb,0xd1,0xdb,0xde,0xd6,0xc0,0xa7,0x7a,0x58,0x50,0x4d,0x48,0x46, + 0x44,0x3b,0x31,0x2b,0x24,0x28,0x4e,0x72,0x83,0x86,0x7d,0x69,0x5d,0x5e,0x5d,0x57, + 0x4c,0x3f,0x34,0x2d,0x31,0x35,0x39,0x3a,0x3b,0x38,0x30,0x2e,0x2f,0x31,0x2f,0x2a, + 0x28,0x25,0x1f,0x22,0x21,0x22,0x22,0x22,0x23,0x26,0x2b,0x31,0x3d,0x45,0x46,0x46, + 0x45,0x45,0x47,0x4a,0x4a,0x45,0x3e,0x37,0x37,0x3a,0x3f,0x45,0x4b,0x4c,0x4f,0x55, + 0xdd,0xb9,0xa1,0xa2,0xa1,0x9c,0x99,0x97,0x90,0x8b,0x85,0x84,0x7f,0x77,0x6a,0x5d, + 0x53,0x51,0x51,0x4f,0x4d,0x50,0x80,0xad,0xc2,0xc9,0xc1,0xb6,0xab,0xa3,0xa5,0xad, + 0xb2,0xb6,0xb9,0xba,0xbe,0xc5,0xcb,0xcf,0xcf,0xd1,0xd1,0xd0,0xd0,0xcb,0xc5,0xc5, + 0xc7,0xca,0xca,0xcb,0xd2,0xda,0xda,0xcd,0xb9,0x9e,0x78,0x5c,0x51,0x4c,0x45,0x46, + 0x45,0x3d,0x36,0x30,0x28,0x2c,0x50,0x70,0x83,0x85,0x7b,0x68,0x5d,0x5d,0x5c,0x56, + 0x4b,0x3f,0x34,0x2e,0x32,0x36,0x38,0x37,0x34,0x32,0x32,0x30,0x30,0x30,0x2c,0x28, + 0x25,0x25,0x23,0x22,0x25,0x23,0x24,0x26,0x28,0x2a,0x2c,0x32,0x3d,0x43,0x44,0x44, + 0x42,0x42,0x45,0x4b,0x4d,0x49,0x44,0x3b,0x3c,0x3e,0x45,0x4a,0x4d,0x4e,0x52,0x56, + 0xe1,0xdb,0xb6,0xa1,0xa2,0x9e,0x99,0x97,0x92,0x8e,0x86,0x85,0x81,0x79,0x6c,0x60, + 0x55,0x50,0x50,0x4f,0x4e,0x6d,0xa0,0xbd,0xca,0xcb,0xc4,0xba,0xb5,0xb3,0xb6,0xbd, + 0xc2,0xc4,0xc5,0xc6,0xc7,0xca,0xcc,0xcf,0xd0,0xd0,0xd0,0xd0,0xcf,0xca,0xc7,0xc8, + 0xca,0xc9,0xc7,0xc5,0xc6,0xcc,0xce,0xc2,0xb2,0x99,0x78,0x63,0x54,0x4b,0x47,0x49, + 0x47,0x3e,0x38,0x31,0x2a,0x2d,0x51,0x70,0x81,0x83,0x7a,0x67,0x5d,0x5c,0x5b,0x54, + 0x49,0x3d,0x33,0x2d,0x31,0x36,0x39,0x3a,0x36,0x35,0x38,0x34,0x31,0x2f,0x29,0x25, + 0x22,0x20,0x1e,0x21,0x25,0x24,0x25,0x28,0x2b,0x2e,0x32,0x36,0x3d,0x41,0x42,0x41, + 0x3e,0x3c,0x40,0x47,0x4a,0x4b,0x47,0x42,0x43,0x42,0x47,0x4e,0x50,0x51,0x53,0x56, + 0xde,0xe3,0xda,0xb5,0xa4,0xa1,0x9c,0x99,0x94,0x91,0x8a,0x87,0x82,0x7b,0x6c,0x5f, + 0x56,0x51,0x4f,0x4d,0x5d,0x91,0xb4,0xc9,0xcd,0xcb,0xc6,0xbf,0xbd,0xc0,0xc4,0xc9, + 0xcb,0xcc,0xcb,0xcb,0xcc,0xcd,0xcf,0xd0,0xd0,0xd0,0xd0,0xd1,0xcd,0xcb,0xca,0xc9, + 0xc9,0xc3,0xbc,0xb6,0xb6,0xb6,0xb9,0xb0,0xa1,0x92,0x81,0x6c,0x5c,0x4f,0x48,0x46, + 0x44,0x3c,0x36,0x2f,0x28,0x2c,0x52,0x70,0x81,0x84,0x7a,0x67,0x5d,0x5a,0x58,0x51, + 0x47,0x3c,0x31,0x2f,0x32,0x33,0x37,0x3c,0x39,0x37,0x37,0x33,0x2e,0x2a,0x24,0x1e, + 0x1c,0x1a,0x1a,0x1d,0x21,0x23,0x26,0x2c,0x34,0x36,0x39,0x3b,0x42,0x42,0x41,0x3d, + 0x3a,0x37,0x39,0x43,0x49,0x4d,0x49,0x48,0x48,0x49,0x4a,0x50,0x53,0x53,0x51,0x53, + 0xdc,0xde,0xe5,0xe0,0xb6,0xa4,0x9f,0x9d,0x99,0x95,0x8e,0x8a,0x83,0x7b,0x69,0x5d, + 0x55,0x4e,0x4d,0x58,0x85,0xad,0xc3,0xcd,0xce,0xcb,0xc6,0xc2,0xc2,0xc6,0xc5,0xc3, + 0xc1,0xc6,0xca,0xcb,0xcd,0xce,0xcf,0xd0,0xd1,0xd0,0xd0,0xd0,0xcf,0xcc,0xcd,0xca, + 0xc5,0xb7,0xab,0xa4,0xa2,0xa3,0xa7,0x9e,0x9a,0x90,0x86,0x72,0x66,0x58,0x49,0x43, + 0x3e,0x37,0x2f,0x2a,0x22,0x2b,0x51,0x73,0x82,0x84,0x7a,0x67,0x5d,0x5a,0x56,0x4e, + 0x45,0x38,0x31,0x30,0x30,0x30,0x32,0x36,0x38,0x37,0x34,0x30,0x2b,0x27,0x20,0x1b, + 0x17,0x17,0x17,0x1a,0x1d,0x22,0x27,0x31,0x3a,0x40,0x3f,0x3d,0x42,0x40,0x3c,0x37, + 0x34,0x34,0x39,0x3f,0x4a,0x4e,0x4c,0x49,0x4b,0x4f,0x4d,0x51,0x55,0x51,0x51,0x53, + 0xdf,0xdd,0xdf,0xe4,0xdf,0xbb,0xa5,0xa0,0x9b,0x98,0x93,0x8f,0x84,0x76,0x64,0x59, + 0x51,0x4c,0x50,0x7a,0xa5,0xbd,0xcb,0xcd,0xcd,0xca,0xc6,0xc5,0xc3,0xc4,0xbc,0xaf, + 0xa7,0xb3,0xbe,0xc8,0xcd,0xcd,0xce,0xcf,0xd0,0xcf,0xcf,0xd1,0xd0,0xcd,0xcb,0xc6, + 0xbb,0xa9,0x96,0x8b,0x8d,0x96,0x9c,0x97,0x96,0x96,0x8b,0x79,0x70,0x60,0x48,0x41, + 0x3a,0x31,0x28,0x21,0x1d,0x27,0x54,0x72,0x81,0x82,0x78,0x66,0x5e,0x5a,0x52,0x49, + 0x3f,0x34,0x30,0x2e,0x2c,0x2c,0x2e,0x31,0x33,0x31,0x2e,0x2e,0x28,0x22,0x1c,0x16, + 0x15,0x16,0x17,0x18,0x1a,0x1e,0x26,0x32,0x3e,0x44,0x44,0x42,0x41,0x3f,0x3b,0x35, + 0x32,0x35,0x3c,0x40,0x48,0x4e,0x4f,0x4a,0x4a,0x4f,0x4c,0x4d,0x50,0x54,0x53,0x52, + 0xdf,0xdf,0xdc,0xe0,0xe4,0xde,0xb7,0xa5,0x9e,0x9c,0x97,0x91,0x85,0x71,0x5f,0x51, + 0x4c,0x4f,0x72,0x9b,0xb7,0xc8,0xcb,0xcb,0xcd,0xc9,0xc5,0xc4,0xc3,0xbe,0xad,0x90, + 0x82,0x97,0xb3,0xc5,0xce,0xce,0xcd,0xcd,0xce,0xcf,0xcf,0xd0,0xcf,0xcc,0xca,0xbf, + 0xb1,0xa1,0x8d,0x86,0x88,0x93,0x98,0x99,0x9c,0x9c,0x8f,0x83,0x78,0x65,0x4a,0x3c, + 0x35,0x29,0x1e,0x18,0x18,0x29,0x52,0x72,0x80,0x80,0x77,0x65,0x5d,0x59,0x4f,0x45, + 0x3b,0x31,0x2f,0x2b,0x29,0x29,0x29,0x29,0x2a,0x2a,0x27,0x23,0x1f,0x1c,0x17,0x14, + 0x14,0x16,0x17,0x15,0x19,0x1d,0x26,0x31,0x3c,0x42,0x46,0x46,0x45,0x41,0x3a,0x35, + 0x34,0x38,0x40,0x44,0x48,0x4d,0x4f,0x49,0x49,0x49,0x4b,0x4a,0x4e,0x56,0x56,0x53, + 0xdf,0xdf,0xdd,0xe0,0xe0,0xe4,0xdf,0xba,0xa4,0x9f,0x9a,0x96,0x84,0x6c,0x57,0x50, + 0x4c,0x69,0x96,0xb4,0xc3,0xc9,0xcb,0xcc,0xcc,0xc8,0xc6,0xc3,0xc5,0xbc,0x98,0x6a, + 0x54,0x7c,0xa8,0xc0,0xcb,0xce,0xcc,0xcc,0xcb,0xcd,0xcf,0xcf,0xd0,0xcb,0xc3,0xb8, + 0xaa,0xa1,0x96,0x92,0x93,0x96,0x97,0x98,0x9a,0x9d,0x9a,0x8e,0x82,0x6e,0x53,0x40, + 0x37,0x2d,0x26,0x24,0x24,0x32,0x58,0x73,0x7f,0x7f,0x76,0x64,0x5b,0x55,0x4c,0x41, + 0x38,0x32,0x2f,0x2b,0x28,0x25,0x22,0x24,0x22,0x22,0x1e,0x1c,0x17,0x16,0x13,0x12, + 0x13,0x16,0x17,0x1a,0x1c,0x20,0x25,0x2e,0x38,0x40,0x46,0x49,0x46,0x40,0x37,0x34, + 0x37,0x3c,0x43,0x49,0x4e,0x4f,0x4e,0x48,0x44,0x45,0x47,0x46,0x4b,0x54,0x53,0x4f, + 0xdd,0xe0,0xdf,0xdf,0xe0,0xe2,0xe5,0xe0,0xb7,0xa1,0x9c,0x97,0x81,0x68,0x53,0x4b, + 0x5e,0x8c,0xad,0xc1,0xc7,0xc8,0xcb,0xcd,0xca,0xc9,0xc7,0xc3,0xc3,0xb2,0x92,0x63, + 0x4d,0x6d,0x9d,0xb9,0xc8,0xcd,0xcb,0xca,0xca,0xcc,0xce,0xcf,0xce,0xc7,0xbf,0xb4, + 0xa8,0xa2,0x9e,0x9b,0x99,0x97,0x97,0x9a,0x9d,0x9b,0x9a,0x90,0x83,0x73,0x5d,0x49, + 0x41,0x3c,0x37,0x36,0x36,0x40,0x61,0x77,0x7f,0x7f,0x74,0x62,0x5a,0x4e,0x45,0x3d, + 0x38,0x30,0x2d,0x2b,0x28,0x24,0x1e,0x1e,0x1c,0x1a,0x17,0x16,0x12,0x12,0x11,0x11, + 0x14,0x16,0x19,0x1b,0x1e,0x21,0x25,0x2a,0x34,0x3b,0x3e,0x41,0x41,0x3c,0x35,0x33, + 0x36,0x3c,0x45,0x4b,0x4f,0x4e,0x48,0x44,0x3e,0x3d,0x3f,0x40,0x47,0x4e,0x52,0x4f, + 0xdf,0xde,0xe2,0xdf,0xdf,0xdf,0xe4,0xe5,0xe0,0xb3,0x9d,0x94,0x7c,0x60,0x4f,0x59, + 0x85,0xa9,0xbf,0xc9,0xc6,0xc7,0xcb,0xcf,0xcc,0xc9,0xc7,0xc4,0xc2,0xb0,0x90,0x64, + 0x4d,0x62,0x8e,0xb0,0xc4,0xcd,0xcb,0xca,0xca,0xcc,0xce,0xcf,0xcc,0xc3,0xb8,0xb2, + 0xaa,0xa9,0xa7,0xa5,0xa1,0x9a,0x97,0x97,0x96,0x96,0x97,0x8d,0x81,0x75,0x65,0x56, + 0x51,0x4d,0x4c,0x4b,0x4a,0x4f,0x6c,0x7b,0x7f,0x7f,0x75,0x61,0x57,0x4a,0x42,0x38, + 0x30,0x2d,0x2c,0x2a,0x27,0x23,0x20,0x1b,0x1c,0x18,0x14,0x13,0x12,0x10,0x10,0x12, + 0x13,0x14,0x18,0x1b,0x1d,0x1f,0x21,0x25,0x2e,0x35,0x38,0x39,0x3b,0x3a,0x35,0x31, + 0x35,0x3b,0x44,0x4a,0x4e,0x4b,0x42,0x3d,0x39,0x36,0x38,0x3e,0x45,0x4c,0x51,0x50, + 0xdf,0xe1,0xe1,0xe0,0xe2,0xe5,0xe5,0xe5,0xe6,0xdf,0xae,0x8b,0x70,0x57,0x53,0x7d, + 0xa6,0xbb,0xc5,0xc9,0xc6,0xc7,0xca,0xcd,0xcd,0xc9,0xc7,0xc4,0xbf,0xb3,0x9b,0x7b, + 0x62,0x59,0x80,0xa4,0xbc,0xca,0xcd,0xcb,0xcd,0xce,0xcf,0xcc,0xc6,0xbc,0xb3,0xac, + 0xad,0xae,0xab,0xab,0xa7,0x9e,0x98,0x93,0x91,0x91,0x92,0x89,0x7e,0x75,0x67,0x61, + 0x5f,0x5b,0x59,0x59,0x55,0x58,0x70,0x7d,0x7f,0x7c,0x71,0x5e,0x51,0x47,0x3d,0x2f, + 0x27,0x2a,0x2a,0x28,0x25,0x20,0x1c,0x1a,0x19,0x16,0x13,0x11,0x11,0x10,0x0f,0x11, + 0x12,0x12,0x16,0x19,0x1c,0x1f,0x20,0x25,0x29,0x2c,0x30,0x32,0x34,0x35,0x32,0x2d, + 0x2e,0x36,0x3f,0x44,0x48,0x44,0x3d,0x37,0x33,0x33,0x38,0x3e,0x44,0x4c,0x50,0x50, + 0xdd,0xe3,0xe0,0xe5,0xde,0xde,0xe2,0xe5,0xe7,0xec,0xdd,0x8e,0x62,0x52,0x74,0xa0, + 0xba,0xc5,0xc5,0xc9,0xc7,0xc6,0xc8,0xcc,0xcf,0xca,0xc7,0xc4,0xc1,0xbe,0xac,0x98, + 0x7e,0x68,0x73,0x98,0xae,0xbf,0xc7,0xcf,0xd0,0xd0,0xcc,0xc8,0xbf,0xb5,0xad,0xa7, + 0xab,0xad,0xaf,0xae,0xad,0xa5,0x9c,0x93,0x8d,0x89,0x88,0x84,0x7c,0x6f,0x6a,0x68, + 0x69,0x67,0x66,0x64,0x5e,0x5e,0x73,0x7c,0x7c,0x7a,0x70,0x5d,0x4d,0x42,0x36,0x29, + 0x24,0x26,0x26,0x25,0x22,0x20,0x1e,0x19,0x18,0x15,0x13,0x11,0x10,0x10,0x10,0x11, + 0x12,0x13,0x17,0x1a,0x1c,0x1e,0x20,0x26,0x29,0x2a,0x2e,0x32,0x2f,0x2e,0x2c,0x2a, + 0x2a,0x31,0x39,0x3b,0x3f,0x3c,0x37,0x32,0x34,0x38,0x3c,0x3f,0x45,0x4c,0x50,0x50, + 0xde,0xdd,0xdf,0xdc,0xdb,0xe0,0xe5,0xe5,0xe5,0xe9,0xeb,0xd5,0x6d,0x6c,0x99,0xb6, + 0xc6,0xc8,0xc6,0xc7,0xc8,0xc6,0xc7,0xc9,0xcc,0xcb,0xc6,0xc3,0xbf,0xc1,0xb8,0xaa, + 0x95,0x7e,0x72,0x82,0x9b,0xae,0xbd,0xca,0xd0,0xcf,0xc9,0xbf,0xb6,0xac,0xa7,0xa6, + 0xa7,0xaa,0xad,0xae,0xaf,0xa8,0xa2,0x98,0x8c,0x86,0x85,0x83,0x82,0x78,0x6d,0x66, + 0x66,0x66,0x64,0x64,0x5f,0x5d,0x6e,0x77,0x7b,0x78,0x6e,0x58,0x4a,0x3e,0x30,0x26, + 0x1f,0x24,0x24,0x21,0x23,0x22,0x1e,0x1b,0x18,0x14,0x12,0x0f,0x12,0x12,0x12,0x11, + 0x10,0x15,0x18,0x1b,0x1c,0x1d,0x1d,0x27,0x2a,0x2d,0x30,0x31,0x2e,0x2a,0x28,0x28, + 0x2c,0x2e,0x34,0x33,0x36,0x36,0x31,0x31,0x33,0x3c,0x40,0x42,0x47,0x4b,0x50,0x4e, + 0xdf,0xde,0xe1,0xda,0xda,0xe2,0xe6,0xe8,0xe8,0xe9,0xea,0xe9,0xce,0x98,0xb4,0xc5, + 0xc9,0xc6,0xc5,0xc6,0xc9,0xc7,0xc7,0xc9,0xcb,0xcb,0xc6,0xc3,0xbf,0xbe,0xbf,0xb8, + 0xa9,0x94,0x85,0x7b,0x84,0x96,0xad,0xc1,0xcc,0xc9,0xc1,0xb5,0xac,0xa6,0xa5,0xa1, + 0xa3,0xa6,0xab,0xb0,0xb2,0xaf,0xa8,0x9b,0x92,0x8c,0x8e,0x90,0x92,0x8c,0x7c,0x6d, + 0x63,0x61,0x61,0x60,0x5b,0x59,0x6b,0x72,0x76,0x72,0x69,0x55,0x49,0x3a,0x2d,0x20, + 0x1d,0x21,0x21,0x23,0x28,0x25,0x1f,0x1a,0x17,0x14,0x11,0x10,0x11,0x13,0x14,0x12, + 0x13,0x17,0x1c,0x1b,0x1b,0x1c,0x1d,0x25,0x2c,0x30,0x31,0x32,0x2e,0x2a,0x29,0x2c, + 0x2d,0x30,0x34,0x36,0x34,0x34,0x30,0x32,0x36,0x3f,0x45,0x45,0x47,0x4a,0x4f,0x4c, + 0xdf,0xe1,0xde,0xd8,0xd9,0xe1,0xea,0xee,0xec,0xea,0xed,0xeb,0xea,0xd5,0xc4,0xc8, + 0xc7,0xc7,0xc7,0xc7,0xca,0xc8,0xc6,0xc9,0xca,0xcb,0xc7,0xc3,0xc0,0xbf,0xc0,0xbd, + 0xb3,0xa2,0x93,0x83,0x70,0x72,0x96,0xb3,0xbd,0xbd,0xb4,0xa9,0xa0,0x9f,0x9f,0x9f, + 0xa0,0xa3,0xaa,0xad,0xb3,0xb1,0xab,0x9c,0x97,0x94,0x98,0x9c,0x9d,0x99,0x87,0x74, + 0x5e,0x54,0x53,0x54,0x50,0x4e,0x60,0x6a,0x6e,0x6b,0x65,0x53,0x43,0x34,0x26,0x1b, + 0x1a,0x1d,0x21,0x25,0x28,0x25,0x1f,0x1a,0x14,0x13,0x10,0x0f,0x0f,0x11,0x12,0x13, + 0x15,0x1b,0x1d,0x1b,0x1b,0x1c,0x1d,0x25,0x2b,0x2f,0x2e,0x2f,0x2e,0x2c,0x2a,0x29, + 0x2c,0x30,0x35,0x39,0x3a,0x37,0x32,0x37,0x39,0x42,0x49,0x47,0x47,0x4a,0x4b,0x4b, + 0xdd,0xdf,0xde,0xdc,0xe0,0xe2,0xeb,0xef,0xed,0xed,0xef,0xe5,0xe9,0xeb,0xd9,0xca, + 0xc9,0xc7,0xc7,0xc7,0xc9,0xc9,0xc6,0xc9,0xc9,0xcb,0xc8,0xc2,0xc0,0xbe,0xbf,0xbb, + 0xb7,0xad,0x9e,0x8b,0x6d,0x5c,0x81,0xa2,0xae,0xb0,0xa5,0x9b,0x9a,0x99,0x99,0x9b, + 0x9d,0xa0,0xa7,0xac,0xb3,0xb4,0xb1,0xa4,0x99,0x95,0x9a,0x9e,0xa0,0x9a,0x8b,0x75, + 0x59,0x46,0x43,0x47,0x43,0x4d,0x5f,0x67,0x66,0x66,0x5f,0x4e,0x3f,0x30,0x23,0x18, + 0x18,0x18,0x20,0x23,0x24,0x22,0x20,0x19,0x14,0x12,0x10,0x10,0x0f,0x11,0x12,0x15, + 0x17,0x1b,0x1e,0x1c,0x1b,0x1b,0x1c,0x22,0x29,0x2f,0x2d,0x2c,0x2d,0x2a,0x2a,0x2c, + 0x2f,0x32,0x37,0x3c,0x3d,0x3d,0x3d,0x43,0x43,0x47,0x4b,0x4a,0x49,0x49,0x48,0x46, + 0xe0,0xe0,0xdd,0xdc,0xe2,0xe7,0xed,0xef,0xf1,0xf0,0xed,0xe1,0xe6,0xe9,0xea,0xd3, + 0xc9,0xc7,0xc8,0xc8,0xc8,0xc9,0xc7,0xc8,0xc8,0xc9,0xc8,0xc0,0xc0,0xbc,0xbb,0xb8, + 0xb2,0xac,0x9f,0x83,0x63,0x59,0x79,0x97,0xa3,0xa2,0x99,0x97,0x96,0x95,0x9a,0x98, + 0x9b,0x9e,0xa5,0xac,0xb2,0xb5,0xb0,0xa7,0x97,0x8b,0x90,0x99,0x9c,0x94,0x83,0x71, + 0x57,0x47,0x49,0x57,0x68,0x7c,0x82,0x7b,0x6f,0x63,0x56,0x49,0x3c,0x2e,0x23,0x19, + 0x17,0x18,0x1d,0x20,0x20,0x1e,0x1c,0x18,0x13,0x0f,0x0f,0x0f,0x0e,0x10,0x11,0x15, + 0x19,0x1c,0x1f,0x1c,0x1b,0x1a,0x1b,0x1f,0x26,0x2d,0x2e,0x2c,0x2c,0x2b,0x2d,0x30, + 0x35,0x38,0x3c,0x40,0x3d,0x3d,0x41,0x46,0x47,0x4b,0x4e,0x4b,0x49,0x49,0x47,0x43, + 0xe0,0xe2,0xdf,0xe1,0xe4,0xe5,0xee,0xf0,0xf0,0xeb,0xf1,0xd5,0xec,0xf0,0xf0,0xea, + 0xd1,0xca,0xcb,0xc9,0xc8,0xc8,0xc8,0xc7,0xc7,0xc6,0xc9,0xc5,0xbf,0xbe,0xb9,0xb5, + 0xad,0x9f,0x92,0x75,0x62,0x6c,0x87,0x93,0x9b,0x99,0x96,0x95,0x94,0x95,0x97,0x98, + 0x97,0x9a,0x9f,0xab,0xb3,0xb6,0xb4,0xab,0x98,0x83,0x7d,0x83,0x8e,0x8e,0x88,0x80, + 0x7f,0x80,0x89,0x99,0xa4,0xac,0xa4,0x97,0x83,0x6a,0x4e,0x42,0x38,0x2a,0x1d,0x17, + 0x15,0x17,0x1a,0x1c,0x1d,0x1b,0x18,0x12,0x0f,0x10,0x0f,0x10,0x0f,0x10,0x11,0x15, + 0x19,0x1a,0x1c,0x1d,0x1b,0x1a,0x18,0x1a,0x23,0x2b,0x2c,0x2c,0x2d,0x2d,0x31,0x37, + 0x3b,0x3c,0x41,0x43,0x40,0x3d,0x41,0x43,0x46,0x4b,0x4d,0x4f,0x4b,0x47,0x46,0x42, + 0xe3,0xe5,0xe6,0xe3,0xe5,0xdd,0xea,0xf0,0xeb,0xec,0xe5,0xd7,0xe5,0xeb,0xec,0xf0, + 0xea,0xcf,0xc9,0xca,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc7,0xc6,0xbc,0xbd,0xb5,0xac, + 0x9a,0x85,0x78,0x6d,0x70,0x82,0x91,0x96,0x98,0x96,0x95,0x93,0x94,0x96,0x96,0x94, + 0x93,0x95,0x9a,0xa6,0xb2,0xb6,0xb3,0xa6,0x96,0x7d,0x72,0x7a,0x8f,0xa0,0xa7,0xac, + 0xb1,0xb7,0xbd,0xc6,0xcd,0xce,0xc5,0xb1,0x97,0x70,0x4a,0x3e,0x34,0x26,0x1b,0x18, + 0x15,0x15,0x17,0x18,0x18,0x16,0x13,0x0f,0x0f,0x0f,0x0f,0x10,0x10,0x11,0x11,0x15, + 0x18,0x19,0x1a,0x18,0x19,0x18,0x18,0x1c,0x22,0x26,0x29,0x2c,0x2f,0x32,0x35,0x39, + 0x3c,0x3f,0x40,0x3f,0x3e,0x3d,0x42,0x43,0x46,0x4a,0x4e,0x4e,0x4b,0x47,0x45,0x40, + 0xe4,0xe6,0xeb,0xea,0xde,0xdd,0xe0,0xee,0xeb,0xed,0xe9,0xe0,0xe8,0xf0,0xee,0xf0, + 0xe5,0xe0,0xcc,0xc9,0xc8,0xc8,0xc8,0xc9,0xc8,0xc9,0xc6,0xc5,0xb9,0xaf,0xa2,0x95, + 0x86,0x78,0x72,0x76,0x87,0x94,0x9a,0x98,0x96,0x95,0x93,0x95,0x96,0x95,0x95,0x90, + 0x8d,0x8c,0x93,0x9f,0xae,0xb3,0xb2,0xab,0xa3,0xa2,0xa4,0xaa,0xb5,0xc1,0xc8,0xcc, + 0xce,0xd2,0xd8,0xdd,0xe0,0xe1,0xd6,0xc1,0xa5,0x79,0x48,0x39,0x2f,0x22,0x1a,0x18, + 0x15,0x14,0x15,0x15,0x14,0x12,0x10,0x0e,0x0f,0x0f,0x0f,0x10,0x10,0x10,0x11,0x14, + 0x17,0x1a,0x19,0x18,0x17,0x18,0x1a,0x1f,0x24,0x28,0x2b,0x2f,0x32,0x34,0x37,0x3b, + 0x3f,0x40,0x3e,0x3d,0x3c,0x3e,0x42,0x46,0x4b,0x4c,0x4e,0x4f,0x4f,0x4c,0x46,0x40, + 0xe6,0xe9,0xe9,0xed,0xea,0xe1,0xe1,0xe8,0xed,0xf0,0xe3,0xd8,0xf0,0xf1,0xef,0xe0, + 0xe5,0xe2,0xd3,0xc9,0xca,0xc7,0xc8,0xc6,0xc5,0xc3,0xbb,0xb5,0xa3,0x9a,0x94,0x8d, + 0x86,0x7d,0x7f,0x89,0x97,0x9c,0x9e,0x98,0x95,0x93,0x92,0x94,0x95,0x94,0x95,0x86, + 0x7d,0x7f,0x8b,0x9a,0xaa,0xb1,0xb3,0xb4,0xb7,0xb9,0xbe,0xc6,0xce,0xd7,0xdb,0xdf, + 0xe2,0xe4,0xe6,0xe9,0xec,0xea,0xe0,0xca,0xa9,0x7a,0x48,0x36,0x2b,0x1f,0x18,0x16, + 0x14,0x12,0x14,0x14,0x13,0x11,0x10,0x0f,0x0f,0x0f,0x0f,0x10,0x10,0x10,0x10,0x12, + 0x14,0x16,0x15,0x16,0x18,0x18,0x1b,0x1f,0x25,0x2a,0x2f,0x35,0x37,0x37,0x37,0x3a, + 0x3e,0x43,0x40,0x3a,0x39,0x3d,0x41,0x45,0x49,0x4f,0x4d,0x4d,0x4f,0x4d,0x48,0x43, + 0xdc,0xe1,0xe4,0xe9,0xeb,0xe7,0xe9,0xed,0xee,0xea,0xe3,0xdf,0xee,0xef,0xe2,0xea, + 0xe8,0xcf,0xe6,0xd3,0xcb,0xc8,0xc2,0xbc,0xb3,0xae,0xab,0xaa,0xa4,0x9b,0x94,0x8d, + 0x8c,0x8d,0x91,0x97,0x9d,0xa1,0x9f,0x97,0x92,0x90,0x93,0x92,0x90,0x92,0x8d,0x7d, + 0x73,0x78,0x84,0x99,0xa9,0xb0,0xb6,0xc1,0xc6,0xce,0xd8,0xde,0xe2,0xe4,0xe7,0xe7, + 0xe9,0xe8,0xe8,0xe9,0xe8,0xe6,0xdd,0xc6,0xaa,0x79,0x44,0x31,0x27,0x1b,0x16,0x13, + 0x12,0x11,0x13,0x14,0x12,0x11,0x10,0x0f,0x0f,0x0f,0x0f,0x10,0x10,0x10,0x10,0x12, + 0x13,0x13,0x13,0x15,0x16,0x18,0x1c,0x1f,0x25,0x2b,0x34,0x3b,0x3c,0x3c,0x3a,0x3a, + 0x3c,0x3e,0x3d,0x37,0x38,0x3d,0x42,0x45,0x49,0x4e,0x50,0x4f,0x4c,0x4c,0x4d,0x47, + 0xe1,0xdf,0xe5,0xe1,0xe5,0xe1,0xe8,0xeb,0xea,0xe9,0xe9,0xd7,0xe7,0xe5,0xeb,0xe3, + 0xb5,0xa4,0xf0,0xe5,0xc4,0xb6,0xb1,0xad,0xac,0xae,0xae,0xac,0xa4,0x9a,0x97,0x94, + 0x95,0x97,0x97,0x98,0xa0,0xa0,0x9e,0x95,0x8e,0x91,0x91,0x91,0x92,0x92,0x87,0x76, + 0x73,0x79,0x86,0x9b,0xa8,0xad,0xb7,0xc7,0xd3,0xda,0xe2,0xe5,0xe9,0xe7,0xe8,0xe8, + 0xe4,0xe4,0xe3,0xe0,0xe0,0xdd,0xd2,0xbc,0x9c,0x70,0x3f,0x2c,0x23,0x1a,0x16,0x14, + 0x13,0x11,0x11,0x12,0x12,0x0f,0x0f,0x0f,0x0f,0x10,0x11,0x11,0x12,0x10,0x10,0x10, + 0x10,0x13,0x13,0x13,0x15,0x17,0x1b,0x1f,0x21,0x2b,0x36,0x3d,0x3f,0x40,0x3e,0x3b, + 0x3a,0x39,0x38,0x37,0x36,0x3c,0x44,0x48,0x4c,0x50,0x53,0x51,0x4d,0x4c,0x4d,0x4c, + 0xdf,0xdf,0xe6,0xe1,0xe3,0xe8,0xec,0xea,0xed,0xe9,0xe4,0xda,0xe3,0xe8,0xde,0xd9, + 0x94,0xb0,0xe6,0xdd,0xcb,0xb3,0xb2,0xb3,0xaf,0xad,0xad,0xab,0xa8,0xa7,0xa0,0x97, + 0x94,0x93,0x95,0x98,0xa0,0x9f,0x9b,0x93,0x8d,0x8d,0x8e,0x8e,0x90,0x8f,0x83,0x77, + 0x78,0x80,0x8b,0x9a,0xa6,0xab,0xb8,0xcd,0xda,0xe3,0xe9,0xe6,0xe8,0xe6,0xe3,0xdd, + 0xd9,0xd6,0xd2,0xcd,0xca,0xc9,0xbb,0xa4,0x85,0x5e,0x37,0x28,0x20,0x19,0x17,0x14, + 0x14,0x14,0x14,0x12,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x10,0x11,0x13,0x14,0x13, + 0x13,0x12,0x12,0x12,0x14,0x15,0x19,0x1d,0x22,0x2e,0x3a,0x43,0x46,0x46,0x43,0x40, + 0x3b,0x38,0x37,0x36,0x35,0x38,0x42,0x49,0x4e,0x51,0x55,0x54,0x52,0x50,0x51,0x50, + 0xe1,0xdb,0xe0,0xe2,0xe2,0xe8,0xe4,0xe6,0xec,0xe2,0xea,0xe1,0xd9,0xde,0xe2,0xb9, + 0xa2,0xbd,0xdd,0xd7,0xd6,0xbf,0xb5,0xb0,0xb2,0xb3,0xb1,0xad,0xa7,0x9a,0x92,0x90, + 0x90,0x91,0x93,0x97,0x9f,0x9f,0x99,0x8f,0x87,0x88,0x87,0x8a,0x8f,0x8c,0x86,0x80, + 0x81,0x8a,0x93,0x9e,0xa3,0xaa,0xbc,0xc9,0xd7,0xde,0xdf,0xdd,0xdd,0xd7,0xd2,0xcb, + 0xc3,0xba,0xb1,0xab,0xa4,0xa1,0x93,0x7c,0x5e,0x43,0x2f,0x25,0x1d,0x17,0x16,0x14, + 0x14,0x13,0x12,0x12,0x12,0x13,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x13,0x14,0x14, + 0x14,0x12,0x12,0x12,0x12,0x12,0x16,0x19,0x20,0x2b,0x3a,0x47,0x4b,0x4b,0x47,0x40, + 0x3a,0x33,0x33,0x34,0x35,0x36,0x3f,0x47,0x4b,0x50,0x55,0x56,0x54,0x54,0x52,0x4f, + 0xe4,0xde,0xe0,0xe0,0xe9,0xe4,0xe7,0xee,0xef,0xea,0xe9,0xd9,0xd6,0xe1,0xd4,0xae, + 0xb1,0xc0,0xdc,0xd4,0xd5,0xcc,0xbc,0xb7,0xb3,0xaa,0xa5,0xa0,0x9b,0x96,0x93,0x91, + 0x90,0x91,0x93,0x98,0x9f,0x9d,0x98,0x89,0x81,0x81,0x84,0x8b,0x90,0x92,0x8e,0x8d, + 0x8f,0x94,0x9c,0xa0,0xa1,0xae,0xbb,0xc7,0xd1,0xd2,0xce,0xc7,0xc2,0xb6,0xab,0xa0, + 0x94,0x86,0x79,0x6f,0x67,0x62,0x5e,0x50,0x3f,0x2b,0x27,0x20,0x1b,0x1a,0x17,0x17, + 0x17,0x16,0x15,0x15,0x14,0x14,0x15,0x15,0x15,0x14,0x14,0x15,0x14,0x14,0x14,0x14, + 0x14,0x14,0x14,0x12,0x10,0x11,0x13,0x16,0x1c,0x25,0x34,0x3f,0x46,0x47,0x43,0x3c, + 0x37,0x31,0x32,0x34,0x36,0x37,0x3e,0x46,0x4a,0x4e,0x56,0x59,0x59,0x5a,0x57,0x53, + 0xd9,0xde,0xdd,0xe2,0xe4,0xe8,0xe5,0xe9,0xee,0xeb,0xe4,0xd1,0xd0,0xe4,0xb3,0xa9, + 0xb2,0xbf,0xc8,0xbd,0xd7,0xd0,0xca,0xa5,0xa0,0x9f,0x9e,0x9d,0x9a,0x96,0x95,0x93, + 0x91,0x91,0x94,0x99,0x9f,0x9b,0x92,0x82,0x7a,0x80,0x87,0x8c,0x92,0x97,0x98,0x98, + 0x99,0x9a,0x9c,0xa0,0xa1,0xad,0xbb,0xc5,0xc9,0xc9,0xbf,0xb2,0xa9,0x99,0x87,0x72, + 0x5f,0x52,0x42,0x39,0x31,0x30,0x2d,0x31,0x2c,0x25,0x23,0x1f,0x1d,0x1b,0x18,0x17, + 0x17,0x15,0x15,0x16,0x15,0x14,0x15,0x15,0x15,0x15,0x14,0x14,0x15,0x15,0x15,0x15, + 0x15,0x15,0x15,0x12,0x11,0x10,0x10,0x14,0x18,0x1f,0x2d,0x38,0x3e,0x3f,0x3a,0x36, + 0x30,0x2c,0x30,0x35,0x38,0x3e,0x3d,0x41,0x47,0x4e,0x55,0x5c,0x5d,0x5b,0x58,0x52, + 0xd2,0xde,0xde,0xe4,0xd8,0xe4,0xf0,0xe6,0xeb,0xef,0xe4,0xcf,0xd0,0xe5,0xae,0xa1, + 0xaf,0x9d,0x90,0xc4,0xdb,0xd0,0xd8,0xb2,0x9f,0xa1,0xa2,0xa0,0x9c,0x99,0x95,0x92, + 0x91,0x92,0x94,0x99,0x9f,0x9a,0x8d,0x7f,0x7a,0x84,0x8a,0x93,0x97,0x9c,0x9d,0x9e, + 0x9f,0x9e,0x9c,0x9e,0xa8,0xb3,0xbe,0xc8,0xca,0xc5,0xb7,0xa7,0x90,0x76,0x5b,0x45, + 0x35,0x2c,0x24,0x26,0x2c,0x2d,0x2c,0x2d,0x2c,0x26,0x25,0x21,0x1f,0x1f,0x1c,0x1b, + 0x1b,0x1b,0x19,0x16,0x16,0x1b,0x1f,0x20,0x20,0x1b,0x18,0x17,0x18,0x18,0x18,0x17, + 0x17,0x15,0x14,0x14,0x14,0x12,0x13,0x13,0x18,0x1e,0x29,0x31,0x36,0x33,0x30,0x2e, + 0x2a,0x28,0x2c,0x30,0x3a,0x3e,0x3e,0x3e,0x42,0x4c,0x52,0x5a,0x5c,0x5c,0x5a,0x54, + 0xd4,0xdf,0xe0,0xd7,0xd6,0xdd,0xed,0xed,0xed,0xea,0xbe,0xc0,0xd0,0xe8,0xb7,0xa2, + 0x9c,0x86,0x9f,0xc2,0xda,0xd3,0xd8,0xce,0xaa,0x9e,0xa1,0x9e,0x9c,0x98,0x95,0x92, + 0x91,0x92,0x95,0x99,0xa0,0x9a,0x8d,0x81,0x7f,0x86,0x8e,0x97,0x9c,0x9f,0xa1,0xa1, + 0xa0,0x9d,0x9c,0xa0,0xaf,0xbb,0xc3,0xc5,0xcc,0xcb,0xc0,0xb2,0x99,0x82,0x6d,0x5a, + 0x46,0x39,0x30,0x2c,0x32,0x31,0x2f,0x2c,0x2c,0x27,0x25,0x23,0x21,0x20,0x1f,0x1f, + 0x1e,0x1e,0x1c,0x1b,0x1f,0x24,0x26,0x2a,0x2a,0x26,0x1d,0x1a,0x18,0x17,0x16,0x17, + 0x18,0x15,0x14,0x13,0x14,0x12,0x13,0x16,0x1c,0x20,0x26,0x2a,0x2e,0x2b,0x2a,0x26, + 0x22,0x26,0x2a,0x30,0x39,0x3d,0x3d,0x3d,0x3f,0x49,0x4c,0x55,0x5b,0x5b,0x5a,0x56, + 0xd5,0xde,0xda,0xd3,0xda,0xdc,0xea,0xed,0xee,0xc2,0xa6,0xa7,0xdc,0xdc,0xbc,0xae, + 0x9a,0xa2,0xaa,0xc5,0xd7,0xd6,0xd0,0xdb,0xc9,0xa7,0xa0,0x9f,0x9b,0x98,0x95,0x93, + 0x92,0x92,0x95,0x98,0x9f,0x9c,0x90,0x89,0x85,0x8e,0x96,0x9c,0x9e,0xa1,0xa0,0x9f, + 0x9e,0x9c,0x9c,0xa6,0xb9,0xc5,0xd0,0xd7,0xe1,0xe1,0xd9,0xcf,0xc1,0xad,0x9d,0x8c, + 0x72,0x63,0x50,0x44,0x3f,0x37,0x3b,0x37,0x31,0x2b,0x2a,0x29,0x26,0x25,0x24,0x25, + 0x22,0x22,0x22,0x20,0x24,0x29,0x2b,0x2e,0x31,0x2c,0x24,0x21,0x1a,0x19,0x17,0x17, + 0x17,0x16,0x15,0x15,0x13,0x14,0x14,0x18,0x20,0x21,0x24,0x29,0x2c,0x29,0x26,0x22, + 0x21,0x23,0x27,0x2d,0x39,0x3f,0x3e,0x3e,0x3e,0x44,0x48,0x4d,0x52,0x57,0x58,0x56, + 0xd6,0xd8,0xce,0xd0,0xd1,0xe0,0xeb,0xeb,0xe8,0xc9,0xb1,0xba,0xe0,0xd0,0xc4,0xc2, + 0x9b,0x9c,0xa6,0xaa,0xd1,0xd9,0xd0,0xdc,0xd5,0xb6,0xa0,0x9f,0x9c,0x98,0x95,0x94, + 0x94,0x93,0x95,0x99,0xa0,0x9e,0x96,0x8e,0x8e,0x94,0x9a,0x9d,0x9d,0x9e,0x9d,0x9b, + 0x9a,0x9b,0x9f,0xb3,0xc6,0xd5,0xde,0xe7,0xec,0xef,0xec,0xe3,0xdb,0xc5,0xbc,0xaf, + 0x9a,0x81,0x6d,0x5b,0x4c,0x43,0x45,0x43,0x39,0x31,0x2c,0x2d,0x2a,0x29,0x29,0x29, + 0x27,0x24,0x22,0x25,0x29,0x2c,0x2f,0x36,0x38,0x35,0x2c,0x25,0x1f,0x1a,0x18,0x18, + 0x17,0x16,0x16,0x15,0x14,0x14,0x14,0x1b,0x1d,0x20,0x27,0x29,0x29,0x27,0x28,0x26, + 0x21,0x1f,0x23,0x2b,0x34,0x3b,0x3d,0x3d,0x3d,0x3d,0x40,0x44,0x4d,0x52,0x55,0x54, + 0xd4,0xcc,0xce,0xd4,0xd6,0xdc,0xee,0xea,0xe1,0xcd,0xba,0xbf,0xc2,0xcd,0xca,0xc5, + 0xa1,0x88,0x9a,0xa4,0xbe,0xdb,0xd1,0xd9,0xda,0xcf,0xaa,0xa0,0x9c,0x98,0x95,0x95, + 0x94,0x93,0x95,0x99,0xa0,0xa0,0x98,0x91,0x93,0x98,0x9b,0x9c,0x9c,0x9c,0x9a,0x98, + 0x98,0x9c,0xb5,0xd8,0xe7,0xec,0xef,0xf1,0xf5,0xf4,0xf2,0xea,0xe5,0xda,0xc8,0xc1, + 0xb3,0x97,0x7f,0x6e,0x61,0x5e,0x5a,0x53,0x48,0x3c,0x31,0x32,0x31,0x32,0x30,0x2d, + 0x2c,0x29,0x27,0x27,0x29,0x2d,0x37,0x3d,0x3f,0x3e,0x36,0x2e,0x27,0x1f,0x19,0x18, + 0x17,0x16,0x16,0x15,0x15,0x14,0x15,0x1b,0x20,0x23,0x28,0x2b,0x2a,0x2b,0x30,0x2e, + 0x2b,0x25,0x22,0x27,0x2d,0x35,0x39,0x39,0x38,0x39,0x3d,0x3f,0x44,0x4a,0x50,0x51, + 0xda,0xda,0xd5,0xd5,0xd6,0xd8,0xe7,0xe8,0xdb,0xba,0xc6,0xa7,0xad,0xc2,0xd3,0xcf, + 0xa9,0x8b,0x8e,0x9c,0xa9,0xd6,0xd2,0xd7,0xdd,0xda,0xc0,0xa0,0x9c,0x98,0x96,0x95, + 0x94,0x94,0x93,0x98,0x9d,0x9e,0x9e,0x9c,0x99,0x9a,0x9b,0x9b,0x9b,0x9a,0x98,0x97, + 0x9e,0xb3,0xd2,0xed,0xf4,0xf3,0xf5,0xf4,0xf4,0xf3,0xf1,0xec,0xe2,0xd8,0xcd,0xc5, + 0xbd,0xaa,0x9f,0x91,0x88,0x7f,0x76,0x67,0x57,0x47,0x37,0x38,0x37,0x36,0x36,0x34, + 0x30,0x2e,0x2c,0x28,0x2a,0x2f,0x39,0x41,0x40,0x40,0x3a,0x34,0x29,0x22,0x1d,0x1a, + 0x18,0x17,0x17,0x15,0x14,0x15,0x17,0x1d,0x22,0x26,0x29,0x2c,0x2c,0x2e,0x35,0x36, + 0x35,0x2f,0x27,0x24,0x28,0x30,0x33,0x34,0x35,0x34,0x34,0x38,0x3c,0x42,0x49,0x4c, + 0xcd,0xcf,0xd1,0xd6,0xd4,0xd9,0xe7,0xe1,0xdb,0xae,0xb0,0xb1,0xbf,0xc8,0xe1,0xd1, + 0xa0,0x8b,0x87,0x90,0xa0,0xcc,0xd6,0xd1,0xe0,0xd7,0xd5,0xa7,0x9b,0x97,0x96,0x95, + 0x94,0x95,0x92,0x98,0x9e,0xa3,0xa7,0xa5,0xa0,0x9c,0x9a,0x9b,0x9c,0x9a,0x9a,0x9b, + 0xa6,0xc3,0xe4,0xf1,0xf6,0xf8,0xf5,0xf2,0xf1,0xef,0xe8,0xe1,0xdb,0xd2,0xd0,0xcc, + 0xcc,0xc8,0xbc,0xaf,0xa8,0xa0,0x8d,0x7d,0x6a,0x50,0x39,0x3a,0x3a,0x38,0x3a,0x3c, + 0x38,0x34,0x32,0x2d,0x2e,0x31,0x37,0x3b,0x3f,0x3f,0x3a,0x33,0x2c,0x26,0x20,0x1c, + 0x1c,0x1a,0x19,0x16,0x17,0x19,0x1a,0x1e,0x23,0x25,0x27,0x2b,0x2f,0x3a,0x39,0x3c, + 0x3c,0x35,0x2b,0x23,0x22,0x27,0x2d,0x30,0x31,0x33,0x32,0x34,0x37,0x3b,0x3e,0x43, + 0xc7,0xca,0xd0,0xd8,0xd6,0xda,0xdf,0xd9,0xdd,0xad,0xaf,0xd5,0xcf,0xd7,0xe9,0xd5, + 0x8f,0x8e,0x8a,0x7d,0x91,0xcd,0xd9,0xcd,0xe1,0xd5,0xdb,0xbb,0x9c,0x99,0x96,0x96, + 0x95,0x94,0x92,0x98,0xa0,0xa8,0xa9,0xa9,0xa7,0x9f,0x9d,0x9c,0x9d,0x9d,0x9d,0xa1, + 0xad,0xc4,0xe5,0xed,0xf4,0xf5,0xef,0xe9,0xe3,0xe1,0xde,0xdb,0xd3,0xd2,0xd3,0xd4, + 0xd1,0xcf,0xc9,0xc0,0xb9,0xb3,0xa2,0x8e,0x77,0x52,0x39,0x3c,0x37,0x37,0x3d,0x40, + 0x40,0x3d,0x37,0x31,0x30,0x33,0x36,0x3a,0x40,0x40,0x3b,0x34,0x2f,0x2b,0x25,0x20, + 0x1d,0x1d,0x1b,0x18,0x18,0x1c,0x1f,0x20,0x22,0x23,0x2a,0x2d,0x32,0x3a,0x40,0x40, + 0x3f,0x38,0x2b,0x20,0x1e,0x21,0x24,0x2a,0x2e,0x30,0x2f,0x30,0x32,0x35,0x37,0x3c, + 0xc6,0xc8,0xcc,0xd1,0xd8,0xdb,0xdd,0xd9,0xdc,0xab,0xbc,0xca,0xc8,0xe1,0xe1,0xc8, + 0x80,0x8f,0x8d,0x71,0x98,0xc4,0xdc,0xcf,0xdb,0xd6,0xde,0xcf,0xaa,0x99,0x98,0x98, + 0x96,0x96,0x94,0x98,0xa0,0xab,0xad,0xac,0xa8,0xa0,0x9d,0x9f,0xa1,0xa1,0xa1,0xa2, + 0xaf,0xbd,0xd8,0xe9,0xec,0xea,0xe5,0xd7,0xd7,0xd1,0xd3,0xd2,0xce,0xd1,0xd4,0xd8, + 0xd7,0xd7,0xd5,0xce,0xc9,0xc1,0xb0,0x9d,0x7e,0x57,0x3d,0x3a,0x38,0x39,0x40,0x45, + 0x45,0x3f,0x3b,0x37,0x32,0x34,0x36,0x3c,0x41,0x44,0x3e,0x37,0x30,0x2e,0x28,0x25, + 0x22,0x21,0x1e,0x1b,0x1b,0x1e,0x22,0x27,0x2b,0x30,0x35,0x34,0x37,0x3f,0x46,0x49, + 0x43,0x3a,0x2f,0x22,0x1c,0x1f,0x25,0x29,0x2e,0x31,0x31,0x33,0x34,0x33,0x33,0x37, + 0xcb,0xcd,0xd2,0xce,0xe3,0xde,0xcb,0xb7,0xd8,0xb1,0xd3,0xd0,0xdc,0xdb,0xcb,0xad, + 0x87,0x8c,0xa3,0x6f,0x9e,0xb6,0xdf,0xd3,0xd8,0xdb,0xda,0xd4,0xbd,0x9b,0x99,0x98, + 0x96,0x95,0x93,0x97,0x9c,0xa5,0xa9,0xa9,0xa5,0x9f,0xa2,0xa3,0xa5,0xa4,0xa0,0x9f, + 0xa3,0xb2,0xbf,0xcd,0xd3,0xcf,0xc6,0xbe,0xc4,0xc9,0xcd,0xce,0xd1,0xd4,0xd7,0xdb, + 0xdd,0xde,0xdb,0xd8,0xd3,0xc7,0xb6,0xa2,0x83,0x5b,0x3c,0x36,0x37,0x3d,0x43,0x45, + 0x44,0x41,0x3f,0x3a,0x36,0x36,0x3b,0x41,0x43,0x42,0x3e,0x39,0x32,0x2d,0x28,0x28, + 0x26,0x23,0x20,0x1f,0x1e,0x22,0x2a,0x33,0x3b,0x3f,0x44,0x42,0x41,0x45,0x4d,0x49, + 0x46,0x3d,0x32,0x25,0x21,0x24,0x29,0x2d,0x33,0x36,0x36,0x35,0x34,0x32,0x34,0x38, + 0xc7,0xc6,0xc9,0xcb,0xd9,0xe1,0xbe,0xac,0xcf,0xbf,0xcd,0xc9,0xc9,0xc9,0xbe,0xad, + 0x93,0x78,0xb6,0x6e,0x8a,0xb5,0xd8,0xd3,0xd4,0xdb,0xda,0xd5,0xcf,0xa6,0x98,0x98, + 0x97,0x95,0x93,0x97,0x9b,0xa1,0xa3,0xa2,0x9f,0xa2,0xa7,0xaa,0xa9,0xa6,0xa6,0xa7, + 0xa9,0xb0,0xb2,0xb5,0xb8,0xba,0xb7,0xbb,0xc2,0xc9,0xcf,0xd2,0xd6,0xda,0xdc,0xdf, + 0xdf,0xde,0xdb,0xd6,0xd0,0xc9,0xbb,0xa4,0x87,0x64,0x40,0x38,0x3b,0x3f,0x43,0x45, + 0x45,0x47,0x42,0x3d,0x3a,0x3b,0x45,0x48,0x48,0x46,0x40,0x3b,0x33,0x30,0x2d,0x2d, + 0x2a,0x29,0x26,0x27,0x26,0x2d,0x37,0x40,0x49,0x4c,0x4f,0x50,0x4f,0x4e,0x4e,0x47, + 0x45,0x3d,0x35,0x2e,0x2b,0x30,0x32,0x37,0x3c,0x3c,0x3a,0x39,0x36,0x33,0x36,0x39, + 0xc2,0xbc,0xc2,0xc8,0xcd,0xdf,0xd4,0xcb,0xc5,0xc2,0xbe,0xb2,0xc4,0xc9,0xcc,0xb1, + 0x98,0x83,0xab,0x7b,0x80,0xb7,0xc9,0xd9,0xd0,0xde,0xd7,0xd4,0xd5,0xbf,0x98,0x99, + 0x97,0x95,0x94,0x98,0x9c,0x9f,0x9f,0xa0,0xa1,0xa7,0xac,0xb1,0xb1,0xb0,0xaf,0xab, + 0xac,0xad,0xaf,0xb1,0xb4,0xb7,0xbc,0xc3,0xcb,0xd0,0xd4,0xd9,0xdc,0xdc,0xdd,0xdd, + 0xdd,0xdb,0xd8,0xd4,0xcf,0xcb,0xbe,0xab,0x90,0x6f,0x50,0x40,0x43,0x46,0x48,0x4a, + 0x48,0x48,0x44,0x40,0x3c,0x41,0x4e,0x51,0x51,0x4e,0x47,0x3e,0x38,0x37,0x35,0x35, + 0x33,0x31,0x30,0x30,0x31,0x39,0x43,0x4c,0x54,0x54,0x58,0x58,0x58,0x53,0x4b,0x45, + 0x40,0x39,0x36,0x33,0x31,0x35,0x38,0x3d,0x3f,0x40,0x3e,0x3d,0x3a,0x3a,0x3a,0x3d, + 0x67,0x94,0xc4,0xca,0xcf,0xd3,0xde,0xd6,0xcc,0xcc,0xcb,0xc8,0xcd,0xd7,0xd6,0xc4, + 0x8f,0x84,0x98,0x81,0x7d,0xa0,0xc7,0xdc,0xd2,0xdf,0xd3,0xd8,0xd1,0xce,0xa7,0x97, + 0x97,0x95,0x95,0x97,0x9b,0x9e,0x9e,0xa2,0xa6,0xab,0xb2,0xb7,0xb7,0xb7,0xb6,0xb1, + 0xaf,0xb0,0xb3,0xb4,0xb8,0xbc,0xc3,0xcb,0xcf,0xd2,0xd5,0xd6,0xd8,0xd8,0xd9,0xdb, + 0xdb,0xdb,0xd9,0xd4,0xd0,0xca,0xc1,0xae,0x97,0x79,0x5d,0x4e,0x50,0x4f,0x4e,0x4f, + 0x4e,0x4a,0x45,0x44,0x47,0x4d,0x58,0x59,0x58,0x55,0x4d,0x44,0x3c,0x3d,0x3d,0x3b, + 0x3d,0x40,0x3e,0x3d,0x3d,0x43,0x4a,0x4f,0x56,0x59,0x5c,0x59,0x55,0x4d,0x47,0x44, + 0x41,0x3c,0x38,0x35,0x32,0x36,0x3a,0x40,0x42,0x45,0x45,0x44,0x43,0x40,0x40,0x40, + 0x54,0x3d,0x57,0x7f,0xb6,0xc3,0xd2,0xdc,0xd3,0xca,0xbf,0xcf,0xb4,0xbc,0xda,0xd3, + 0x91,0x78,0x85,0x76,0x7a,0xa7,0xc6,0xd9,0xd4,0xdf,0xda,0xdc,0xd3,0xd7,0xbd,0x97, + 0x96,0x95,0x93,0x96,0x9a,0xa0,0xa2,0xa6,0xa7,0xad,0xb4,0xb9,0xb9,0xba,0xbc,0xb7, + 0xb6,0xb8,0xb7,0xb9,0xbb,0xc2,0xc4,0xc9,0xce,0xd0,0xd2,0xd4,0xd7,0xda,0xdb,0xdd, + 0xdb,0xdb,0xd7,0xd3,0xcf,0xcc,0xc4,0xb3,0xa0,0x86,0x6e,0x5e,0x5c,0x59,0x57,0x57, + 0x53,0x4f,0x4a,0x4d,0x54,0x5d,0x61,0x63,0x63,0x5d,0x54,0x4c,0x43,0x46,0x47,0x47, + 0x4a,0x4f,0x4e,0x4a,0x47,0x48,0x4e,0x51,0x53,0x59,0x59,0x54,0x4f,0x49,0x44,0x44, + 0x3e,0x3c,0x3b,0x38,0x34,0x33,0x38,0x3c,0x41,0x46,0x48,0x4a,0x4b,0x47,0x45,0x46, + 0x9b,0x40,0x3c,0x40,0x4a,0x68,0x98,0xc3,0xcd,0xb2,0x8a,0x7c,0x76,0xa1,0xe7,0xdd, + 0x9c,0x71,0x77,0x78,0xaa,0xa4,0xba,0xd3,0xd6,0xdc,0xdc,0xdc,0xd7,0xd6,0xcb,0xa2, + 0x97,0x95,0x93,0x97,0x9c,0xa0,0xa2,0xa3,0xa5,0xaa,0xb2,0xb6,0xb9,0xbb,0xbe,0xbd, + 0xbc,0xb9,0xb9,0xbb,0xbc,0xc1,0xc7,0xcd,0xd1,0xd3,0xd5,0xd5,0xd8,0xda,0xdb,0xdd, + 0xdc,0xdb,0xd9,0xd5,0xd1,0xcd,0xc6,0xb9,0xa7,0x92,0x7b,0x67,0x63,0x64,0x60,0x5f, + 0x5c,0x55,0x53,0x57,0x5d,0x65,0x6c,0x6b,0x69,0x63,0x58,0x50,0x50,0x52,0x55,0x58, + 0x5b,0x5d,0x5c,0x56,0x51,0x4f,0x50,0x51,0x4f,0x4d,0x4b,0x45,0x41,0x41,0x3f,0x3f, + 0x3b,0x3c,0x3f,0x3c,0x37,0x33,0x35,0x37,0x3d,0x44,0x49,0x4f,0x51,0x4b,0x49,0x49, + 0xb7,0x85,0x43,0x36,0x3b,0x3b,0x41,0x54,0xa1,0x7a,0x5d,0x62,0x93,0xca,0xe6,0xd2, + 0xb1,0x8d,0x7d,0x84,0xae,0xa6,0xb1,0xca,0xe2,0xdb,0xde,0xdc,0xd5,0xdc,0xcf,0xb9, + 0x98,0x97,0x94,0x98,0x9e,0xa1,0xa0,0x9e,0xa0,0xa4,0xaa,0xb0,0xb5,0xb9,0xb8,0xb4, + 0xb3,0xb3,0xb8,0xbf,0xc7,0xcd,0xce,0xd3,0xd4,0xd5,0xd7,0xd6,0xd8,0xda,0xdc,0xdd, + 0xdb,0xdb,0xd9,0xd7,0xd3,0xcd,0xc8,0xbd,0xaf,0x9b,0x86,0x70,0x6a,0x68,0x65,0x65, + 0x63,0x5f,0x5b,0x61,0x66,0x6b,0x71,0x71,0x6d,0x67,0x5e,0x5a,0x57,0x5b,0x62,0x66, + 0x68,0x6a,0x68,0x62,0x59,0x53,0x52,0x4f,0x49,0x45,0x40,0x39,0x34,0x35,0x37,0x3a, + 0x3a,0x3e,0x40,0x40,0x39,0x35,0x31,0x35,0x3c,0x41,0x49,0x52,0x55,0x51,0x4f,0x4a, + 0xb4,0xb3,0x9d,0x8a,0x71,0x49,0x36,0x44,0x86,0x95,0x63,0x5d,0xb0,0xc5,0xda,0xcc, + 0xaf,0xbb,0x7c,0x7a,0xb1,0xaa,0xad,0xcc,0xde,0xd4,0xe1,0xdc,0xd3,0xdc,0xce,0xc6, + 0xa0,0x98,0x96,0x98,0x9f,0x9e,0x9c,0x98,0x9a,0x9c,0xa0,0xa6,0xac,0xb1,0xb0,0xaf, + 0xb1,0xb4,0xb9,0xc1,0xcb,0xd3,0xd4,0xd7,0xd8,0xd5,0xd5,0xd8,0xd7,0xdb,0xdd,0xdc, + 0xdd,0xdb,0xda,0xd7,0xd5,0xcf,0xca,0xc0,0xb3,0xa3,0x8d,0x77,0x6e,0x6c,0x68,0x65, + 0x65,0x63,0x65,0x67,0x6c,0x72,0x74,0x73,0x6a,0x67,0x60,0x5c,0x58,0x5e,0x67,0x6c, + 0x6d,0x6e,0x6b,0x65,0x5b,0x55,0x4f,0x4b,0x46,0x3e,0x38,0x32,0x2d,0x2f,0x33,0x3a, + 0x3c,0x40,0x42,0x3f,0x3c,0x37,0x33,0x35,0x39,0x3e,0x44,0x50,0x51,0x4f,0x4e,0x49, + 0xc4,0xb1,0xb3,0xba,0xcb,0xbf,0x98,0x65,0x8a,0xb4,0x73,0x61,0x71,0xb4,0xb2,0xad, + 0xac,0xdb,0x84,0x7e,0xa1,0xb9,0xb8,0xb9,0xe0,0xd7,0xdc,0xdc,0xd4,0xdb,0xd1,0xcd, + 0xb0,0x98,0x98,0x9a,0x9f,0x9b,0x96,0x91,0x93,0x97,0x9b,0xa1,0xa8,0xa9,0xa6,0xa7, + 0xad,0xb2,0xb8,0xc2,0xcc,0xd4,0xd5,0xd8,0xd8,0xd5,0xd4,0xd6,0xd8,0xdb,0xdd,0xdc, + 0xdd,0xdc,0xdc,0xd9,0xd6,0xd2,0xcd,0xc4,0xb8,0xaa,0x98,0x7f,0x75,0x6e,0x6a,0x68, + 0x67,0x68,0x69,0x6c,0x70,0x75,0x77,0x71,0x68,0x62,0x5c,0x58,0x57,0x5f,0x68,0x6c, + 0x6e,0x6b,0x67,0x63,0x59,0x54,0x50,0x4e,0x46,0x3c,0x32,0x2b,0x28,0x2c,0x30,0x3e, + 0x45,0x47,0x48,0x47,0x45,0x40,0x3b,0x38,0x3b,0x3c,0x42,0x4b,0x4e,0x4d,0x4b,0x4a, + 0xc8,0xc0,0xac,0xa5,0xa3,0xc6,0xd5,0xba,0x92,0xae,0xa0,0x62,0x5f,0x93,0xaf,0xaf, + 0xb8,0xe1,0x9d,0x72,0x9b,0xb7,0xaa,0x99,0xe6,0xe1,0xd5,0xdb,0xd6,0xd7,0xd3,0xce, + 0xc1,0x9e,0x99,0x9b,0xa0,0x99,0x94,0x8e,0x8f,0x99,0xa0,0xa1,0xa1,0x9f,0x9c,0x97, + 0x9d,0xab,0xb5,0xc0,0xc8,0xce,0xd0,0xd1,0xce,0xcf,0xd0,0xd2,0xd7,0xd9,0xdc,0xdd, + 0xdd,0xdc,0xdd,0xdc,0xd8,0xd5,0xcf,0xc8,0xbc,0xaf,0x9e,0x8e,0x79,0x6e,0x69,0x6d, + 0x6a,0x6b,0x6a,0x69,0x6b,0x6f,0x6e,0x67,0x5f,0x58,0x52,0x4c,0x50,0x56,0x5f,0x66, + 0x6a,0x68,0x60,0x58,0x54,0x56,0x55,0x53,0x4a,0x40,0x35,0x2c,0x28,0x2c,0x31,0x3b, + 0x47,0x4b,0x4d,0x51,0x50,0x4b,0x46,0x3f,0x3d,0x3b,0x40,0x47,0x49,0x4d,0x4c,0x4e, + 0xb7,0xca,0xb5,0xb2,0xa1,0xa1,0xbc,0xcf,0x9b,0x99,0xca,0x62,0x5f,0x72,0xaa,0xa8, + 0x8e,0xc5,0xe7,0x83,0x93,0xc0,0x9a,0x93,0xe7,0xe7,0xd5,0xd8,0xd6,0xd4,0xd3,0xcf, + 0xcc,0xac,0x9a,0x9b,0x9f,0x99,0x93,0x8c,0x8e,0x9d,0xa2,0x9d,0x9a,0x97,0x94,0x90, + 0x97,0x9b,0xa9,0xb4,0xbd,0xc4,0xc7,0xc7,0xc8,0xc8,0xca,0xcd,0xd1,0xd7,0xdb,0xdd, + 0xdf,0xdd,0xde,0xdc,0xda,0xd7,0xd3,0xcc,0xc3,0xb6,0xa6,0x96,0x80,0x70,0x6b,0x69, + 0x6b,0x68,0x68,0x68,0x65,0x67,0x62,0x5a,0x51,0x4c,0x43,0x3f,0x49,0x4e,0x54,0x59, + 0x60,0x60,0x58,0x52,0x50,0x54,0x57,0x55,0x4f,0x46,0x3a,0x2e,0x28,0x2b,0x33,0x39, + 0x42,0x49,0x4d,0x56,0x56,0x53,0x4e,0x4b,0x43,0x3c,0x41,0x42,0x44,0x4a,0x4d,0x50, + 0xbb,0xbb,0xcc,0xb7,0xad,0xa0,0x93,0x8d,0xa7,0xcd,0xd3,0x63,0x5e,0x5e,0x82,0x94, + 0x81,0xac,0xfd,0xae,0x8a,0xc7,0x7f,0x9c,0xe0,0xe7,0xd6,0xd6,0xd5,0xd1,0xd4,0xd0, + 0xce,0xbe,0x9c,0x9b,0xa1,0x9b,0x93,0x8b,0x8e,0x9b,0x9f,0x9a,0x97,0x96,0x95,0x95, + 0x9c,0xa7,0xb0,0xb2,0xb8,0xba,0xc0,0xbf,0xbb,0xbb,0xc1,0xc5,0xca,0xd1,0xd7,0xd9, + 0xdc,0xde,0xde,0xde,0xdc,0xd8,0xd5,0xce,0xc8,0xbd,0xb0,0x9f,0x8a,0x74,0x6b,0x64, + 0x65,0x64,0x64,0x62,0x60,0x5e,0x57,0x4b,0x45,0x41,0x37,0x35,0x3d,0x45,0x4a,0x4d, + 0x54,0x58,0x51,0x4f,0x52,0x54,0x59,0x5b,0x56,0x4c,0x3f,0x32,0x2c,0x2d,0x34,0x38, + 0x40,0x44,0x4c,0x53,0x56,0x54,0x55,0x52,0x46,0x40,0x3e,0x3e,0x41,0x49,0x4f,0x54, + 0xc6,0xb9,0xc6,0xc4,0xaf,0xa8,0x97,0x9b,0xab,0xcb,0xd5,0x62,0x5e,0x5e,0x67,0x7e, + 0x7d,0x9e,0xf8,0xd1,0x9d,0xc2,0x85,0x9b,0xde,0xe4,0xdf,0xd2,0xd3,0xcf,0xd4,0xcd, + 0xcd,0xca,0xa5,0x9f,0xa1,0x9b,0x93,0x8b,0x8c,0x98,0x9b,0x98,0x94,0x94,0x93,0x9c, + 0xb0,0xbf,0xc7,0xc9,0xc8,0xce,0xcd,0xcb,0xc6,0xb7,0xb6,0xba,0xc2,0xca,0xce,0xd4, + 0xd6,0xd9,0xda,0xdd,0xde,0xdb,0xd9,0xd1,0xcb,0xc2,0xb5,0xa8,0x95,0x7c,0x6b,0x5e, + 0x5e,0x5c,0x59,0x56,0x54,0x51,0x49,0x3f,0x3c,0x39,0x2f,0x2b,0x33,0x3e,0x47,0x49, + 0x50,0x54,0x53,0x52,0x58,0x5e,0x63,0x63,0x5e,0x52,0x44,0x38,0x32,0x34,0x39,0x3d, + 0x41,0x41,0x48,0x4e,0x51,0x54,0x57,0x52,0x48,0x43,0x3d,0x3f,0x44,0x4d,0x59,0x61, + 0xc6,0xc4,0xbc,0xcc,0xba,0xb0,0xa3,0x9c,0x90,0xa3,0xc3,0x9e,0x5d,0x60,0x61,0x67, + 0x73,0x8b,0xea,0xee,0xca,0xbb,0x84,0x8f,0xe3,0xde,0xe2,0xd0,0xd1,0xcd,0xd2,0xcd, + 0xcd,0xcc,0xb3,0x9e,0xa3,0x9c,0x94,0x8c,0x8c,0x95,0x98,0x95,0x92,0x94,0x94,0xab, + 0xc2,0xd1,0xd8,0xdb,0xdc,0xdf,0xdb,0xd7,0xd4,0xce,0xc2,0xc8,0xca,0xcc,0xc9,0xcc, + 0xd0,0xd3,0xd5,0xd7,0xd8,0xdb,0xd7,0xd3,0xcd,0xc6,0xbc,0xae,0x9b,0x7f,0x6a,0x57, + 0x56,0x53,0x50,0x48,0x43,0x3f,0x39,0x36,0x36,0x37,0x2e,0x2c,0x33,0x3b,0x44,0x4c, + 0x53,0x55,0x56,0x59,0x62,0x69,0x6c,0x6b,0x64,0x5a,0x4f,0x43,0x40,0x3e,0x3f,0x42, + 0x43,0x40,0x43,0x49,0x4c,0x51,0x53,0x4e,0x44,0x41,0x3f,0x3f,0x44,0x4f,0x5a,0x66, + 0xc4,0xc4,0xc6,0xcc,0xca,0xb5,0xae,0xa8,0x9a,0x95,0xb5,0xe0,0x7e,0x5f,0x5e,0x5f, + 0x63,0x78,0xcc,0xfc,0xf3,0xc0,0xa8,0xa3,0xda,0xda,0xde,0xd1,0xd1,0xcc,0xd0,0xcd, + 0xcc,0xcd,0xc0,0xa1,0xa3,0x9c,0x94,0x8b,0x8c,0x96,0x97,0x92,0x90,0x95,0xa2,0xbf, + 0xd3,0xdc,0xe3,0xe6,0xe9,0xea,0xe7,0xe7,0xe7,0xe0,0xdd,0xdb,0xd2,0xcf,0xcf,0xc9, + 0xcf,0xd2,0xd3,0xd3,0xd4,0xd7,0xd5,0xd3,0xd0,0xca,0xc2,0xb5,0xa2,0x8a,0x6a,0x4f, + 0x4a,0x48,0x41,0x3b,0x36,0x31,0x30,0x33,0x35,0x33,0x30,0x30,0x35,0x3b,0x45,0x50, + 0x57,0x59,0x5b,0x60,0x69,0x72,0x75,0x73,0x6b,0x61,0x59,0x51,0x4e,0x4a,0x47,0x45, + 0x44,0x42,0x42,0x45,0x44,0x48,0x4a,0x45,0x40,0x40,0x42,0x43,0x48,0x4f,0x55,0x63, + 0xbd,0xc3,0xc8,0xcc,0xd1,0xc6,0xb2,0xaf,0xa4,0x9d,0x90,0xa2,0x7f,0x65,0x5c,0x5d, + 0x5c,0x67,0x95,0xee,0xf8,0xe3,0xc0,0xb1,0xc6,0xd9,0xe2,0xd4,0xd1,0xce,0xce,0xcd, + 0xcc,0xcc,0xca,0xa9,0xa2,0x9e,0x95,0x8d,0x8f,0x98,0x99,0x94,0x8f,0x95,0xab,0xc7, + 0xdc,0xe5,0xeb,0xee,0xf0,0xf0,0xef,0xef,0xee,0xea,0xec,0xe6,0xdd,0xd6,0xd3,0xcf, + 0xd2,0xd5,0xd4,0xd2,0xd1,0xd0,0xd0,0xcf,0xce,0xc9,0xc7,0xbc,0xa9,0x90,0x6c,0x4e, + 0x40,0x3a,0x34,0x32,0x2e,0x2a,0x2d,0x31,0x32,0x32,0x33,0x37,0x38,0x3a,0x42,0x4d, + 0x54,0x56,0x5b,0x63,0x6e,0x78,0x7b,0x7b,0x74,0x6b,0x64,0x5e,0x5b,0x5a,0x56,0x4f, + 0x4a,0x47,0x44,0x43,0x42,0x41,0x41,0x40,0x3f,0x43,0x44,0x45,0x4a,0x50,0x55,0x61, + 0xb4,0xb8,0xc1,0xcb,0xd0,0xd1,0xc3,0xb1,0xab,0xa2,0x9d,0x93,0x79,0x71,0x78,0x5e, + 0x5d,0x5f,0x83,0xda,0xf6,0xf2,0xd9,0xcc,0xbb,0xd2,0xe5,0xdf,0xd2,0xcd,0xcc,0xce, + 0xcd,0xcc,0xcd,0xb4,0xa0,0x9d,0x95,0x90,0x91,0x99,0x98,0x93,0x8f,0x99,0xb1,0xca, + 0xe0,0xe9,0xf0,0xf0,0xf1,0xf5,0xf6,0xf6,0xf5,0xf3,0xf5,0xef,0xe6,0xdd,0xdb,0xd4, + 0xd9,0xdb,0xd6,0xd1,0xcd,0xca,0xc7,0xc8,0xca,0xc9,0xc6,0xc3,0xb2,0x98,0x72,0x50, + 0x39,0x30,0x2b,0x29,0x26,0x26,0x2c,0x32,0x33,0x35,0x36,0x39,0x36,0x37,0x3d,0x48, + 0x4c,0x50,0x57,0x65,0x6e,0x76,0x7f,0x80,0x78,0x70,0x6b,0x62,0x62,0x63,0x63,0x5d, + 0x59,0x55,0x4d,0x46,0x40,0x3c,0x3b,0x3f,0x43,0x4b,0x49,0x48,0x4e,0x54,0x56,0x5f, + 0xad,0xb1,0xb5,0xc5,0xcc,0xd1,0xd5,0xb9,0xae,0xa8,0xa1,0xa6,0x94,0x95,0xaa,0x71, + 0x63,0x5f,0x6d,0xb9,0xeb,0xf8,0xeb,0xd0,0xcd,0xad,0xe3,0xe3,0xd8,0xcb,0xcb,0xcc, + 0xcd,0xcc,0xce,0xbf,0xa1,0x9b,0x96,0x90,0x91,0x98,0x98,0x92,0x90,0x9b,0xb2,0xcc, + 0xe1,0xeb,0xef,0xf0,0xf1,0xf1,0xf2,0xf6,0xf7,0xf4,0xf6,0xf4,0xea,0xe4,0xde,0xda, + 0xe0,0xe2,0xd9,0xd2,0xca,0xc1,0xbd,0xbe,0xbf,0xc2,0xc2,0xc0,0xb2,0x9b,0x75,0x50, + 0x36,0x2d,0x29,0x29,0x26,0x27,0x2c,0x2f,0x31,0x34,0x37,0x38,0x36,0x36,0x3b,0x41, + 0x42,0x46,0x4e,0x5c,0x64,0x6f,0x76,0x7a,0x74,0x70,0x6a,0x67,0x64,0x68,0x67,0x67, + 0x65,0x5e,0x56,0x4d,0x47,0x43,0x3f,0x43,0x4c,0x51,0x51,0x4f,0x4e,0x51,0x53,0x5d, + 0xaf,0xae,0xb7,0xb9,0xc9,0xd2,0xd5,0xce,0xb6,0xac,0xa8,0xa4,0xa4,0xa3,0x98,0x92, + 0x76,0x64,0x6a,0x7e,0xd1,0xf5,0xef,0xdb,0xe2,0xb8,0xe6,0xea,0xd9,0xcc,0xcb,0xcb, + 0xcc,0xcd,0xcb,0xc8,0xa3,0x9a,0x95,0x93,0x92,0x97,0x96,0x91,0x8f,0x9a,0xb3,0xcc, + 0xe0,0xea,0xef,0xf0,0xf1,0xf1,0xf4,0xf7,0xf6,0xf8,0xf7,0xf2,0xe9,0xe0,0xda,0xdd, + 0xe3,0xe7,0xe0,0xd4,0xc8,0xba,0xb3,0xb3,0xb5,0xb8,0xba,0xba,0xad,0x99,0x79,0x52, + 0x36,0x30,0x2c,0x2d,0x2a,0x2b,0x2e,0x33,0x32,0x32,0x33,0x31,0x33,0x32,0x39,0x3e, + 0x3a,0x39,0x40,0x4f,0x5a,0x63,0x6b,0x6c,0x68,0x67,0x66,0x68,0x66,0x69,0x69,0x6a, + 0x68,0x60,0x5b,0x56,0x4f,0x47,0x46,0x4a,0x50,0x56,0x57,0x59,0x57,0x55,0x56,0x5e, + 0xb1,0xac,0xaf,0xb4,0xb9,0xc3,0xcc,0xd3,0xc8,0xb4,0xab,0xa8,0x9d,0xa1,0x9a,0x99, + 0x94,0x70,0x60,0x64,0x86,0xdc,0xf5,0xed,0xe6,0xce,0xe4,0xec,0xda,0xcc,0xcb,0xcc, + 0xcc,0xcb,0xcc,0xcb,0xaa,0x96,0x94,0x94,0x92,0x94,0x93,0x8d,0x8c,0x99,0xb5,0xcd, + 0xe0,0xe9,0xee,0xf0,0xf0,0xf0,0xf2,0xf4,0xf5,0xf7,0xf7,0xf3,0xe7,0xdf,0xdb,0xdc, + 0xe4,0xe8,0xe0,0xd6,0xc6,0xb6,0xa8,0xa5,0xa5,0xa9,0xad,0xaf,0xa5,0x94,0x77,0x54, + 0x37,0x2e,0x2d,0x2d,0x2c,0x2c,0x2e,0x31,0x30,0x30,0x2d,0x2b,0x2c,0x2b,0x31,0x36, + 0x2f,0x2c,0x30,0x3f,0x45,0x51,0x5a,0x5f,0x5c,0x5b,0x5e,0x64,0x65,0x66,0x67,0x67, + 0x63,0x5d,0x5b,0x57,0x52,0x4a,0x4b,0x4d,0x50,0x58,0x5c,0x5e,0x5c,0x5c,0x5c,0x60, + 0xb1,0xae,0xae,0xae,0xb0,0xb8,0xc5,0xc6,0xd3,0xc4,0xb2,0xab,0xa7,0xa2,0x9f,0x9c, + 0x89,0x85,0x7a,0x63,0x66,0x9d,0xf4,0xf4,0xee,0xe0,0xe2,0xec,0xdd,0xcd,0xcb,0xcb, + 0xcb,0xcb,0xcc,0xcd,0xb7,0x99,0x96,0x91,0x93,0x92,0x90,0x89,0x89,0x99,0xb3,0xcb, + 0xdf,0xe8,0xee,0xef,0xf0,0xef,0xf1,0xf4,0xf5,0xf6,0xf7,0xf2,0xe7,0xde,0xd8,0xd9, + 0xe1,0xe6,0xdf,0xd4,0xc3,0xb0,0xa1,0x9b,0x95,0x96,0x9d,0xa4,0x9c,0x89,0x74,0x57, + 0x3c,0x31,0x31,0x2f,0x2e,0x2d,0x2d,0x2e,0x2e,0x2d,0x2b,0x29,0x29,0x27,0x29,0x2d, + 0x2b,0x28,0x2b,0x32,0x36,0x3f,0x4a,0x50,0x51,0x51,0x53,0x58,0x5d,0x5f,0x5f,0x60, + 0x5b,0x57,0x55,0x56,0x51,0x4d,0x4e,0x50,0x51,0x56,0x5c,0x62,0x64,0x65,0x66,0x6a, + 0xb8,0xab,0xac,0xae,0xac,0xaf,0xb1,0xbc,0xc6,0xcc,0xc1,0xb2,0xa9,0xac,0xa4,0x9f, + 0xa0,0x9b,0x95,0x80,0x6b,0x6d,0xcd,0xf6,0xf2,0xda,0xd0,0xe8,0xe1,0xcd,0xcc,0xcb, + 0xcb,0xcb,0xcc,0xcc,0xc0,0x9c,0x95,0x8f,0x90,0x90,0x8e,0x89,0x88,0x96,0xb2,0xca, + 0xdd,0xe6,0xee,0xee,0xf0,0xf0,0xf1,0xf3,0xf4,0xf5,0xf5,0xf1,0xe7,0xdb,0xd3,0xd6, + 0xdf,0xe3,0xdd,0xd1,0xc0,0xaa,0x9d,0x95,0x8c,0x86,0x89,0x94,0x8f,0x82,0x6e,0x57, + 0x3d,0x34,0x31,0x30,0x2b,0x29,0x28,0x2b,0x2b,0x2b,0x2a,0x29,0x28,0x27,0x28,0x2b, + 0x2b,0x28,0x27,0x29,0x2c,0x2e,0x36,0x3d,0x40,0x43,0x47,0x4d,0x53,0x55,0x56,0x57, + 0x55,0x52,0x54,0x53,0x50,0x4f,0x51,0x4f,0x4e,0x54,0x59,0x60,0x65,0x6b,0x6f,0x72, + 0xe2,0xcf,0xd1,0xc8,0xc4,0xcb,0xc2,0xb9,0xbf,0xc6,0xc7,0xc1,0xb1,0xac,0xa9,0xa3, + 0x9b,0xa2,0xaa,0x88,0x7f,0x74,0x95,0xf6,0xf9,0xdb,0xd5,0xe4,0xe3,0xcc,0xcb,0xcb, + 0xca,0xca,0xcc,0xcc,0xc7,0x9c,0x8d,0x89,0x8b,0x8d,0x8b,0x86,0x86,0x95,0xb1,0xc9, + 0xdc,0xe7,0xec,0xec,0xef,0xef,0xf0,0xf2,0xf3,0xf5,0xf5,0xef,0xe3,0xd7,0xcc,0xd3, + 0xdc,0xe1,0xda,0xce,0xbc,0xa6,0x95,0x8d,0x85,0x7e,0x7e,0x88,0x8b,0x80,0x6f,0x5c, + 0x41,0x37,0x33,0x30,0x2c,0x2b,0x2b,0x2e,0x2d,0x2b,0x29,0x29,0x28,0x28,0x28,0x2b, + 0x29,0x25,0x23,0x23,0x25,0x28,0x2b,0x31,0x38,0x3f,0x44,0x48,0x4d,0x4d,0x4e,0x4e, + 0x4f,0x4e,0x4f,0x50,0x50,0x52,0x4d,0x4c,0x4c,0x4f,0x55,0x5e,0x68,0x70,0x74,0x77, + 0xf0,0xeb,0xef,0xf0,0xe6,0xec,0xef,0xe3,0xe2,0xe2,0xdf,0xdd,0xd5,0xcb,0xc3,0xb7, + 0xa8,0xa1,0x9e,0xa2,0x97,0x8f,0x89,0xe0,0xf5,0xec,0xe8,0xe5,0xde,0xce,0xcc,0xcb, + 0xca,0xca,0xca,0xcb,0xcb,0x9c,0x80,0x80,0x85,0x8a,0x89,0x83,0x83,0x92,0xae,0xc6, + 0xda,0xe5,0xeb,0xec,0xed,0xed,0xee,0xf0,0xf0,0xf3,0xf2,0xec,0xdd,0xcf,0xc8,0xce, + 0xd8,0xdf,0xd8,0xcb,0xb8,0xa0,0x8f,0x87,0x7f,0x7b,0x7c,0x88,0x89,0x80,0x70,0x5c, + 0x43,0x37,0x33,0x2e,0x2b,0x2c,0x2e,0x32,0x31,0x2d,0x2a,0x29,0x28,0x28,0x28,0x28, + 0x25,0x24,0x22,0x22,0x22,0x27,0x2d,0x32,0x36,0x3b,0x3e,0x42,0x48,0x49,0x4a,0x4b, + 0x4b,0x4c,0x4c,0x4e,0x4c,0x4b,0x4b,0x4b,0x4c,0x50,0x56,0x5d,0x68,0x6e,0x72,0x75, + 0xed,0xeb,0xef,0xef,0xe5,0xe6,0xe5,0xdf,0xe0,0xe5,0xe6,0xe4,0xe4,0xe4,0xe4,0xe7, + 0xe6,0xde,0xda,0xd3,0xc7,0xbb,0xd6,0xe9,0xf6,0xf5,0xf2,0xed,0xdd,0xcf,0xcc,0xcb, + 0xca,0xcb,0xca,0xc9,0xca,0xa2,0x6d,0x72,0x7c,0x84,0x83,0x80,0x7f,0x8f,0xa9,0xc2, + 0xd5,0xe0,0xe6,0xe7,0xea,0xea,0xeb,0xeb,0xed,0xee,0xee,0xe5,0xd5,0xc8,0xc4,0xcb, + 0xd5,0xdd,0xd6,0xc8,0xb5,0x9e,0x8c,0x83,0x7d,0x7d,0x83,0x90,0x8f,0x86,0x75,0x5f, + 0x47,0x3a,0x33,0x30,0x2f,0x32,0x34,0x35,0x34,0x2e,0x2b,0x29,0x28,0x28,0x28,0x28, + 0x27,0x27,0x25,0x27,0x27,0x29,0x2f,0x36,0x3a,0x3e,0x41,0x44,0x49,0x4a,0x4c,0x4e, + 0x4f,0x50,0x4d,0x4c,0x4e,0x4f,0x4e,0x4c,0x4b,0x50,0x58,0x5e,0x68,0x6d,0x72,0x76, + 0xf0,0xec,0xe8,0xef,0xe9,0xe4,0xea,0xdf,0xe3,0xe7,0xe4,0xe4,0xe1,0xe0,0xe0,0xe1, + 0xe0,0xde,0xe3,0xe2,0xe8,0xe7,0xe9,0xeb,0xee,0xf4,0xf2,0xf0,0xe2,0xd2,0xcb,0xcd, + 0xcc,0xca,0xc9,0xca,0xca,0xb3,0x68,0x6a,0x71,0x78,0x7a,0x79,0x7a,0x87,0x9f,0xb7, + 0xcb,0xd7,0xdc,0xdc,0xe1,0xe0,0xe0,0xe0,0xe3,0xe6,0xe1,0xd8,0xc7,0xbf,0xbe,0xc6, + 0xd4,0xda,0xd1,0xc0,0xaf,0x9a,0x86,0x7f,0x7e,0x85,0x8e,0x98,0x96,0x8b,0x78,0x5f, + 0x47,0x3b,0x34,0x32,0x35,0x38,0x37,0x36,0x37,0x31,0x2d,0x2a,0x29,0x28,0x27,0x2b, + 0x2c,0x2d,0x2c,0x2c,0x2c,0x2d,0x37,0x3c,0x3e,0x43,0x45,0x47,0x4a,0x4c,0x4f,0x54, + 0x54,0x53,0x50,0x4f,0x4f,0x50,0x4e,0x4b,0x4b,0x4f,0x5c,0x5f,0x66,0x6b,0x71,0x75, + 0xec,0xef,0xe9,0xeb,0xee,0xe5,0xeb,0xe3,0xe7,0xe5,0xe2,0xe3,0xe0,0xe0,0xe0,0xde, + 0xe3,0xe2,0xe6,0xe0,0xe9,0xe0,0xe7,0xe7,0xe9,0xea,0xed,0xef,0xee,0xe1,0xd9,0xd2, + 0xcc,0xca,0xc8,0xc8,0xc9,0xc0,0x70,0x67,0x6f,0x6f,0x6e,0x6f,0x74,0x7f,0x91,0xa8, + 0xb9,0xc3,0xca,0xce,0xce,0xcd,0xce,0xce,0xd0,0xd1,0xcc,0xc3,0xb5,0xb1,0xb2,0xbd, + 0xc7,0xcd,0xc4,0xb6,0xa7,0x93,0x84,0x85,0x8a,0x91,0x9a,0x9e,0x9b,0x90,0x76,0x5b, + 0x45,0x3c,0x35,0x37,0x3d,0x3e,0x3d,0x3b,0x3b,0x36,0x30,0x2c,0x27,0x27,0x2a,0x2f, + 0x2f,0x30,0x34,0x37,0x36,0x3a,0x3e,0x43,0x47,0x4c,0x4b,0x4b,0x4f,0x51,0x52,0x58, + 0x57,0x54,0x4f,0x4f,0x4c,0x48,0x4a,0x49,0x4c,0x55,0x62,0x64,0x67,0x6b,0x6f,0x72, + 0xe7,0xed,0xeb,0xe7,0xec,0xea,0xe7,0xe4,0xe0,0xe6,0xe4,0xe8,0xe1,0xde,0xde,0xde, + 0xe2,0xe5,0xe3,0xeb,0xe2,0xe3,0xe8,0xe9,0xea,0xec,0xf0,0xf0,0xed,0xea,0xea,0xe9, + 0xe4,0xd9,0xcf,0xca,0xc9,0xc3,0x78,0x63,0x65,0x66,0x64,0x65,0x69,0x75,0x86,0x9b, + 0xa4,0xae,0xb2,0xb3,0xb6,0xb5,0xb5,0xb5,0xb3,0xb3,0xb0,0xa7,0x9c,0x9e,0xa4,0xaf, + 0xb9,0xbe,0xb2,0xa7,0x98,0x91,0x8c,0x90,0x95,0x9b,0xa2,0xa5,0x9c,0x8d,0x6f,0x52, + 0x40,0x3b,0x36,0x3b,0x40,0x3f,0x41,0x40,0x3c,0x37,0x2d,0x2a,0x29,0x28,0x2b,0x30, + 0x33,0x38,0x3c,0x3f,0x3f,0x41,0x44,0x4a,0x50,0x51,0x4c,0x49,0x4d,0x50,0x50,0x54, + 0x56,0x54,0x4e,0x4b,0x45,0x42,0x45,0x48,0x52,0x5d,0x64,0x69,0x6d,0x6e,0x6f,0x6e, + 0xeb,0xe9,0xeb,0xed,0xe6,0xea,0xe9,0xe8,0xe4,0xe2,0xe7,0xe5,0xe5,0xe1,0xe0,0xde, + 0xde,0xe3,0xdf,0xe6,0xe2,0xe6,0xe8,0xea,0xec,0xed,0xec,0xec,0xed,0xeb,0xeb,0xec, + 0xec,0xec,0xe9,0xde,0xcf,0xa3,0x64,0x6a,0x68,0x64,0x60,0x61,0x64,0x71,0x82,0x93, + 0x9e,0xa2,0xa1,0xa2,0xa0,0x9d,0x9c,0x9d,0x9a,0x97,0x92,0x8f,0x87,0x8b,0x94,0x9f, + 0xa4,0xa6,0x9f,0x9a,0x97,0x96,0x99,0x9e,0xa3,0xa6,0xa9,0xa8,0x9a,0x85,0x68,0x4a, + 0x3b,0x39,0x3a,0x3d,0x3f,0x42,0x42,0x40,0x3d,0x34,0x2e,0x2c,0x2c,0x2c,0x2f,0x34, + 0x3c,0x3f,0x45,0x49,0x49,0x4a,0x4b,0x4f,0x52,0x50,0x4d,0x4a,0x4d,0x4e,0x4e,0x52, + 0x53,0x53,0x4d,0x4a,0x42,0x3d,0x3f,0x49,0x53,0x5b,0x64,0x69,0x6d,0x6f,0x6e,0x6b, + 0xee,0xef,0xec,0xed,0xe8,0xe9,0xe9,0xe6,0xe5,0xe6,0xe3,0xe8,0xe7,0xe3,0xe1,0xe2, + 0xe0,0xe0,0xe1,0xe0,0xe7,0xe7,0xec,0xec,0xec,0xeb,0xe9,0xea,0xe7,0xe5,0xe7,0xe6, + 0xe4,0xe2,0xdb,0xd1,0xb9,0x72,0x65,0x75,0x6e,0x67,0x65,0x60,0x63,0x71,0x7f,0x8d, + 0x9c,0xa3,0xa2,0x9f,0x9c,0x9b,0x9b,0x9b,0x9c,0x9b,0x96,0x8d,0x87,0x83,0x89,0x8f, + 0x92,0x91,0x96,0x98,0x9d,0xa3,0xa6,0xa8,0xab,0xae,0xaf,0xa6,0x93,0x78,0x5f,0x44, + 0x38,0x39,0x3c,0x40,0x44,0x45,0x44,0x43,0x3c,0x36,0x31,0x31,0x30,0x30,0x33,0x38, + 0x40,0x45,0x4a,0x4b,0x4d,0x4d,0x4c,0x4f,0x4e,0x4d,0x49,0x49,0x4a,0x4b,0x4c,0x4f, + 0x51,0x51,0x4c,0x48,0x43,0x3f,0x3f,0x44,0x4e,0x55,0x5f,0x64,0x68,0x63,0x64,0x65, + 0xb2,0xd4,0xe3,0xe8,0xe5,0xe7,0xe6,0xe6,0xe7,0xe6,0xe6,0xe4,0xe5,0xe7,0xe8,0xe5, + 0xe7,0xe8,0xe7,0xe2,0xe5,0xe5,0xe1,0xdc,0xd3,0xd4,0xc9,0xbc,0xb4,0xb5,0xb1,0xae, + 0xa9,0x9a,0x8d,0x91,0x8e,0x6e,0x73,0x7b,0x73,0x71,0x6d,0x68,0x68,0x72,0x7c,0x8b, + 0x99,0xa5,0xac,0xae,0xab,0xa9,0xa9,0xab,0xaa,0xa9,0xa5,0x9f,0x99,0x93,0x92,0x93, + 0x93,0x96,0x9d,0xa4,0xac,0xaf,0xb2,0xb3,0xb4,0xb1,0xac,0xa1,0x8d,0x72,0x56,0x43, + 0x3a,0x3d,0x42,0x45,0x47,0x49,0x48,0x44,0x41,0x3d,0x3d,0x3a,0x36,0x35,0x38,0x3e, + 0x44,0x46,0x49,0x4e,0x4e,0x4e,0x4c,0x4d,0x4b,0x49,0x46,0x48,0x47,0x47,0x4b,0x50, + 0x4e,0x4d,0x4b,0x49,0x45,0x40,0x3e,0x41,0x49,0x52,0x58,0x5f,0x5e,0x59,0x5b,0x58, + 0x3d,0x48,0x63,0x7f,0x82,0x87,0x8f,0x99,0x9f,0xa3,0xa1,0x9d,0xa2,0x9a,0x96,0x9a, + 0xa2,0x9c,0x87,0x88,0x86,0x85,0x7f,0x7c,0x84,0x8c,0x96,0x99,0x95,0x92,0x94,0x92, + 0x91,0x7f,0x7c,0x80,0x7c,0x60,0x6b,0x75,0x74,0x72,0x72,0x6e,0x6a,0x6f,0x7c,0x89, + 0x9a,0xa8,0xb1,0xb6,0xba,0xb8,0xb6,0xb5,0xb6,0xb5,0xb1,0xac,0xa9,0xa2,0xa1,0xa4, + 0xa2,0xa7,0xab,0xb1,0xb7,0xba,0xbb,0xb8,0xb6,0xb2,0xaa,0x9a,0x85,0x68,0x4f,0x40, + 0x3c,0x43,0x46,0x48,0x48,0x48,0x47,0x45,0x40,0x3f,0x3f,0x3c,0x3a,0x38,0x3c,0x41, + 0x44,0x45,0x4b,0x4e,0x4e,0x4e,0x4b,0x4a,0x46,0x45,0x46,0x49,0x48,0x48,0x4c,0x50, + 0x4e,0x4c,0x4c,0x4a,0x45,0x40,0x41,0x3f,0x46,0x4c,0x4f,0x52,0x51,0x4b,0x4f,0x4b, + 0x4b,0x4b,0x5b,0x58,0x55,0x5a,0x68,0x68,0x6e,0x7a,0x68,0x5b,0x6a,0x70,0x76,0x7b, + 0x7f,0x87,0x82,0x7e,0x79,0x70,0x7a,0x8c,0x84,0x7b,0x81,0x87,0x8a,0x8c,0x94,0x88, + 0x6c,0x5f,0x69,0x6f,0x6e,0x5c,0x6d,0x74,0x73,0x74,0x74,0x74,0x70,0x71,0x76,0x81, + 0x91,0xa0,0xad,0xb3,0xbc,0xc0,0xc0,0xc0,0xc1,0xc1,0xba,0xb6,0xb2,0xb0,0xb0,0xb1, + 0xb2,0xb5,0xba,0xbd,0xbf,0xc2,0xbf,0xba,0xb5,0xae,0xa5,0x94,0x7b,0x63,0x4e,0x42, + 0x43,0x47,0x48,0x4a,0x4a,0x49,0x48,0x48,0x43,0x42,0x43,0x40,0x3c,0x3d,0x44,0x45, + 0x47,0x49,0x4e,0x50,0x4e,0x4d,0x4b,0x4a,0x47,0x44,0x48,0x4a,0x4b,0x4c,0x4f,0x4e, + 0x4f,0x50,0x4e,0x4d,0x47,0x41,0x42,0x3f,0x40,0x43,0x43,0x46,0x45,0x41,0x44,0x42, + 0x76,0x59,0x4d,0x61,0x64,0x53,0x5c,0x7b,0x84,0x7a,0x6c,0x5d,0x5b,0x55,0x5e,0x79, + 0x82,0x85,0x84,0x7f,0x79,0x41,0x63,0x8c,0x7f,0x7d,0x79,0x6c,0x76,0x80,0x6b,0x59, + 0x61,0x6c,0x6e,0x73,0x64,0x5f,0x7e,0x74,0x73,0x74,0x74,0x74,0x73,0x73,0x6f,0x72, + 0x7d,0x90,0xa2,0xab,0xb4,0xba,0xbf,0xc1,0xc3,0xc3,0xbf,0xbc,0xba,0xb9,0xba,0xba, + 0xbd,0xc0,0xc1,0xc2,0xc3,0xc3,0xbe,0xb8,0xb3,0xab,0x9f,0x8e,0x76,0x60,0x50,0x49, + 0x45,0x48,0x49,0x4a,0x4a,0x48,0x48,0x49,0x44,0x41,0x42,0x41,0x40,0x41,0x43,0x45, + 0x47,0x4a,0x50,0x50,0x4f,0x4e,0x4e,0x4b,0x46,0x43,0x4a,0x4c,0x50,0x52,0x51,0x4d, + 0x4d,0x4d,0x4c,0x4c,0x48,0x45,0x42,0x3f,0x3c,0x39,0x38,0x39,0x3b,0x39,0x3a,0x3a, + 0x86,0x7e,0x71,0x70,0x7d,0x74,0x5e,0x5f,0x71,0x83,0x8a,0x88,0x83,0x7e,0x7e,0x7f, + 0x82,0x86,0x85,0x81,0x7f,0x4e,0x2a,0x74,0x80,0x7d,0x65,0x68,0x5f,0x5a,0x54,0x55, + 0x5e,0x74,0x6d,0x7c,0x5b,0x73,0x92,0x74,0x74,0x73,0x73,0x73,0x76,0x77,0x75,0x71, + 0x71,0x78,0x89,0x98,0xa5,0xab,0xb4,0xb9,0xbd,0xbe,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf, + 0xbf,0xbf,0xc2,0xc2,0xc2,0xc0,0xbd,0xb7,0xb0,0xa7,0x9c,0x88,0x72,0x5f,0x53,0x4f, + 0x50,0x4d,0x4f,0x4e,0x4b,0x47,0x49,0x49,0x47,0x44,0x42,0x41,0x40,0x42,0x44,0x47, + 0x4b,0x4e,0x51,0x51,0x53,0x51,0x4e,0x4b,0x45,0x49,0x48,0x4c,0x51,0x54,0x52,0x4e, + 0x4e,0x4c,0x4b,0x49,0x47,0x45,0x3e,0x3b,0x39,0x36,0x38,0x39,0x37,0x37,0x36,0x32, + 0x73,0x7f,0x85,0x8b,0x84,0x88,0x7e,0x6a,0x64,0x74,0x84,0x8c,0x91,0x8f,0x8d,0x8a, + 0x89,0x86,0x85,0x82,0x7f,0x67,0x1d,0x48,0x84,0x82,0x63,0x59,0x54,0x55,0x53,0x57, + 0x55,0x66,0x70,0x82,0x59,0x7f,0x92,0x72,0x73,0x73,0x74,0x74,0x74,0x74,0x74,0x73, + 0x73,0x72,0x77,0x85,0x93,0x9f,0xa8,0xaf,0xb4,0xb9,0xbc,0xbb,0xbc,0xbc,0xbc,0xbd, + 0xbe,0xbf,0xc0,0xc0,0xc1,0xbf,0xbc,0xb4,0xac,0xa4,0x94,0x81,0x6e,0x60,0x54,0x55, + 0x54,0x54,0x52,0x51,0x4f,0x4b,0x4b,0x4c,0x4b,0x47,0x41,0x41,0x40,0x42,0x46,0x4b, + 0x4b,0x4d,0x50,0x51,0x52,0x51,0x4c,0x48,0x45,0x48,0x46,0x47,0x4c,0x51,0x50,0x4e, + 0x4f,0x4c,0x49,0x48,0x44,0x3f,0x39,0x39,0x36,0x34,0x35,0x36,0x37,0x39,0x37,0x2f, + 0x51,0x6d,0x86,0x89,0x86,0x88,0x87,0x84,0x80,0x7e,0x87,0x8c,0x8c,0x8d,0x8c,0x88, + 0x86,0x86,0x85,0x83,0x7e,0x74,0x2d,0x1e,0x6f,0x81,0x63,0x58,0x5d,0x60,0x5e,0x56, + 0x5d,0x76,0x89,0x73,0x64,0x91,0x93,0x72,0x74,0x73,0x74,0x74,0x74,0x74,0x74,0x75, + 0x75,0x76,0x74,0x77,0x7e,0x8a,0x95,0x9f,0xa7,0xaf,0xb3,0xb6,0xb9,0xb9,0xba,0xbc, + 0xbd,0xbd,0xbf,0xbf,0xbd,0xbd,0xba,0xb1,0xa7,0x9d,0x8c,0x79,0x6a,0x5d,0x56,0x58, + 0x56,0x54,0x52,0x51,0x4f,0x50,0x51,0x51,0x4d,0x4b,0x44,0x40,0x42,0x43,0x46,0x4a, + 0x4a,0x4c,0x4f,0x50,0x50,0x4e,0x4a,0x46,0x41,0x42,0x42,0x43,0x48,0x4d,0x4c,0x4e, + 0x50,0x4f,0x4c,0x4a,0x46,0x3e,0x36,0x35,0x31,0x31,0x33,0x34,0x35,0x36,0x35,0x2e, + 0x5a,0x61,0x84,0x85,0x88,0x89,0x86,0x83,0x86,0x87,0x86,0x87,0x8a,0x8a,0x86,0x85, + 0x84,0x84,0x84,0x81,0x7e,0x7c,0x47,0x16,0x3c,0x7d,0x70,0x5a,0x66,0x5f,0x71,0x58, + 0x68,0x8b,0x7c,0x6e,0x71,0xa2,0x93,0x71,0x73,0x74,0x74,0x75,0x75,0x75,0x75,0x75, + 0x75,0x75,0x76,0x75,0x79,0x7e,0x86,0x91,0x9b,0xa4,0xad,0xb3,0xb6,0xb7,0xb9,0xbb, + 0xbb,0xbc,0xbe,0xbf,0xbc,0xba,0xb7,0xad,0xa2,0x94,0x82,0x70,0x63,0x5c,0x58,0x59, + 0x56,0x54,0x52,0x51,0x53,0x57,0x57,0x54,0x53,0x4d,0x48,0x45,0x45,0x45,0x47,0x49, + 0x49,0x4c,0x4e,0x4e,0x4d,0x4b,0x48,0x43,0x3e,0x3f,0x3e,0x3d,0x43,0x48,0x4b,0x4d, + 0x4f,0x50,0x4e,0x4e,0x48,0x40,0x36,0x33,0x30,0x2d,0x2f,0x31,0x30,0x2e,0x2d,0x2a, + 0x78,0x72,0x6f,0x7a,0x81,0x85,0x85,0x81,0x84,0x84,0x85,0x84,0x83,0x83,0x82,0x83, + 0x81,0x81,0x82,0x80,0x7d,0x7e,0x67,0x1e,0x18,0x6c,0x7d,0x5c,0x5b,0x5e,0x6f,0x6a, + 0x68,0x71,0x65,0x64,0x74,0xb9,0x92,0x72,0x74,0x75,0x76,0x76,0x75,0x75,0x75,0x75, + 0x75,0x75,0x77,0x77,0x78,0x79,0x7c,0x85,0x8e,0x9b,0xa6,0xad,0xb4,0xb5,0xb6,0xb8, + 0xba,0xbc,0xbc,0xbc,0xba,0xb7,0xb2,0xa5,0x99,0x89,0x76,0x67,0x5b,0x5b,0x5a,0x59, + 0x57,0x56,0x55,0x56,0x58,0x5d,0x5b,0x5c,0x59,0x54,0x4e,0x4a,0x47,0x46,0x46,0x48, + 0x48,0x4a,0x4d,0x4c,0x4b,0x49,0x44,0x3e,0x39,0x38,0x38,0x39,0x3f,0x46,0x4b,0x50, + 0x52,0x52,0x4e,0x4f,0x49,0x42,0x38,0x31,0x2d,0x2a,0x2b,0x2e,0x2c,0x2b,0x26,0x24, + 0x7f,0x7f,0x7c,0x7d,0x83,0x85,0x83,0x82,0x83,0x83,0x85,0x83,0x82,0x81,0x7f,0x84, + 0x82,0x82,0x82,0x82,0x81,0x84,0x7c,0x23,0x14,0x39,0x85,0x72,0x53,0x54,0x60,0x63, + 0x5e,0x63,0x58,0x67,0x7e,0xc7,0x89,0x72,0x74,0x74,0x75,0x75,0x74,0x75,0x75,0x75, + 0x75,0x75,0x79,0x78,0x7a,0x7a,0x7b,0x7c,0x84,0x94,0xa0,0xab,0xb0,0xb3,0xb6,0xb8, + 0xb8,0xba,0xba,0xba,0xb9,0xb4,0xac,0x9c,0x8b,0x7a,0x67,0x5d,0x59,0x57,0x55,0x55, + 0x56,0x55,0x57,0x59,0x5c,0x61,0x61,0x5f,0x5e,0x59,0x52,0x4e,0x4a,0x47,0x48,0x49, + 0x4c,0x4d,0x4b,0x48,0x49,0x47,0x40,0x3a,0x35,0x33,0x32,0x35,0x38,0x43,0x45,0x4d, + 0x4e,0x4f,0x4e,0x4d,0x4b,0x43,0x3b,0x31,0x2d,0x29,0x29,0x2a,0x29,0x28,0x24,0x21, + 0x7d,0x7e,0x7e,0x7d,0x81,0x82,0x82,0x84,0x84,0x85,0x81,0x81,0x80,0x81,0x82,0x81, + 0x82,0x82,0x85,0x85,0x89,0x8c,0x85,0x29,0x13,0x14,0x6b,0x85,0x5b,0x55,0x5c,0x60, + 0x60,0x5f,0x59,0x73,0x84,0xd4,0x82,0x75,0x74,0x76,0x76,0x75,0x75,0x75,0x75,0x75, + 0x76,0x76,0x77,0x77,0x7a,0x79,0x7a,0x7f,0x88,0x93,0x9d,0xa3,0xaf,0xb1,0xb4,0xb5, + 0xb8,0xb9,0xb9,0xb8,0xb8,0xb1,0xa1,0x8e,0x7c,0x6b,0x5b,0x55,0x57,0x55,0x53,0x51, + 0x53,0x55,0x57,0x59,0x5f,0x63,0x63,0x62,0x61,0x5d,0x55,0x4f,0x4e,0x4d,0x4d,0x4c, + 0x4d,0x50,0x4f,0x4a,0x49,0x45,0x3c,0x38,0x35,0x31,0x2f,0x32,0x3b,0x41,0x42,0x47, + 0x4a,0x49,0x4d,0x4d,0x49,0x44,0x3d,0x36,0x2f,0x2a,0x29,0x25,0x26,0x24,0x21,0x1f, + 0x7e,0x7e,0x7e,0x7e,0x7f,0x7d,0x80,0x81,0x82,0x83,0x82,0x81,0x80,0x80,0x7f,0x80, + 0x82,0x84,0x85,0x8a,0x8f,0x93,0x89,0x24,0x12,0x0d,0x3a,0x88,0x72,0x5e,0x5c,0x5d, + 0x5e,0x5a,0x61,0x84,0x99,0xd9,0x7c,0x75,0x73,0x75,0x76,0x76,0x76,0x76,0x75,0x75, + 0x75,0x75,0x76,0x77,0x76,0x76,0x79,0x82,0x8b,0x93,0xa0,0xa6,0xaa,0xae,0xb1,0xb2, + 0xb6,0xb7,0xb8,0xb8,0xb4,0xaa,0x98,0x82,0x6d,0x5e,0x53,0x52,0x53,0x52,0x53,0x52, + 0x54,0x56,0x57,0x59,0x62,0x64,0x62,0x63,0x63,0x5f,0x57,0x54,0x55,0x54,0x54,0x52, + 0x51,0x54,0x51,0x4e,0x4a,0x44,0x3e,0x39,0x33,0x30,0x30,0x36,0x3e,0x40,0x41,0x44, + 0x44,0x46,0x48,0x49,0x49,0x46,0x40,0x3a,0x35,0x2e,0x28,0x25,0x24,0x23,0x21,0x21, + 0x7f,0x7e,0x7f,0x7e,0x7e,0x7e,0x80,0x81,0x81,0x82,0x80,0x7f,0x7f,0x80,0x81,0x81, + 0x84,0x87,0x8d,0x92,0x97,0x98,0x62,0x18,0x11,0x0c,0x1a,0x71,0x87,0x5f,0x64,0x60, + 0x5d,0x58,0x78,0x8d,0xb6,0xd8,0x78,0x77,0x77,0x77,0x77,0x76,0x76,0x76,0x75,0x75, + 0x76,0x76,0x77,0x76,0x74,0x75,0x7b,0x88,0x90,0x99,0xa3,0xa9,0xab,0xad,0xb0,0xb1, + 0xb4,0xb5,0xb6,0xb7,0xb0,0xa3,0x8c,0x75,0x60,0x56,0x55,0x56,0x57,0x55,0x54,0x55, + 0x56,0x58,0x5b,0x5e,0x63,0x67,0x67,0x65,0x65,0x62,0x5f,0x5c,0x5b,0x59,0x59,0x58, + 0x5a,0x5a,0x56,0x54,0x4e,0x46,0x3d,0x37,0x33,0x33,0x34,0x3b,0x40,0x41,0x42,0x3f, + 0x42,0x46,0x47,0x49,0x46,0x42,0x44,0x39,0x37,0x31,0x2b,0x27,0x25,0x24,0x21,0x20, + 0x7f,0x82,0x84,0x82,0x82,0x83,0x7f,0x80,0x80,0x80,0x7f,0x7e,0x7f,0x7f,0x82,0x83, + 0x87,0x8e,0x97,0x99,0x9c,0x6a,0x14,0x10,0x0d,0x0a,0x0f,0x3b,0x87,0x66,0x5f,0x5f, + 0x5e,0x5e,0x8a,0x90,0xd2,0xcd,0x77,0x77,0x77,0x77,0x76,0x77,0x77,0x76,0x75,0x75, + 0x75,0x77,0x76,0x75,0x72,0x76,0x81,0x8f,0x95,0x9f,0xa6,0xa8,0xa8,0xad,0xb0,0xb1, + 0xb2,0xb4,0xb4,0xb4,0xac,0x9b,0x81,0x6b,0x58,0x53,0x57,0x58,0x59,0x57,0x57,0x57, + 0x56,0x56,0x5b,0x60,0x63,0x65,0x67,0x6c,0x6c,0x69,0x64,0x63,0x5f,0x5d,0x5d,0x5f, + 0x61,0x62,0x5d,0x58,0x4c,0x43,0x3d,0x39,0x37,0x3b,0x3d,0x43,0x46,0x46,0x42,0x41, + 0x41,0x40,0x43,0x46,0x46,0x46,0x43,0x3c,0x38,0x33,0x2e,0x27,0x25,0x24,0x23,0x22, + 0x80,0x83,0x84,0x81,0x81,0x82,0x7f,0x81,0x7e,0x7f,0x7e,0x7e,0x7d,0x82,0x85,0x89, + 0x8e,0x97,0x9b,0x9e,0x8e,0x56,0x30,0x18,0x15,0x0b,0x0e,0x16,0x6c,0x7a,0x5a,0x5f, + 0x62,0x6c,0x8a,0x96,0xe4,0xbc,0x77,0x77,0x77,0x77,0x79,0x79,0x77,0x76,0x77,0x77, + 0x76,0x76,0x74,0x72,0x72,0x7b,0x86,0x95,0x9d,0xa3,0xa7,0xa8,0xa8,0xad,0xaf,0xb0, + 0xb1,0xb3,0xb3,0xb1,0xa8,0x97,0x7d,0x69,0x57,0x54,0x55,0x58,0x5b,0x58,0x57,0x57, + 0x58,0x57,0x5c,0x61,0x63,0x67,0x67,0x6c,0x6c,0x6b,0x68,0x65,0x61,0x61,0x62,0x63, + 0x67,0x68,0x61,0x55,0x49,0x43,0x3f,0x3a,0x3c,0x40,0x45,0x49,0x48,0x47,0x43,0x42, + 0x41,0x41,0x44,0x45,0x46,0x47,0x44,0x41,0x3c,0x37,0x34,0x2b,0x28,0x26,0x24,0x21, + 0x82,0x84,0x84,0x82,0x81,0x7f,0x7f,0x80,0x7f,0x7d,0x7d,0x7e,0x7f,0x82,0x89,0x8e, + 0x99,0x9f,0xa0,0x93,0x7b,0x73,0x62,0x27,0x24,0x14,0x0f,0x12,0x3d,0x82,0x64,0x57, + 0x5d,0x7a,0x88,0xa9,0xea,0xa6,0x77,0x76,0x78,0x78,0x78,0x77,0x77,0x77,0x75,0x75, + 0x74,0x72,0x71,0x6e,0x74,0x7f,0x8b,0x97,0xa3,0xa4,0xa5,0xa8,0xa9,0xac,0xac,0xae, + 0xb0,0xb1,0xb2,0xb0,0xa5,0x90,0x7b,0x6c,0x5b,0x59,0x58,0x5a,0x57,0x54,0x55,0x55, + 0x58,0x58,0x5d,0x60,0x64,0x69,0x68,0x6c,0x6b,0x6c,0x69,0x64,0x5f,0x61,0x62,0x65, + 0x6a,0x66,0x5f,0x55,0x4e,0x44,0x3c,0x39,0x3d,0x42,0x48,0x4a,0x49,0x47,0x43,0x44, + 0x43,0x43,0x43,0x45,0x43,0x44,0x46,0x44,0x42,0x40,0x39,0x31,0x2d,0x26,0x27,0x22, + 0x85,0x84,0x7f,0x7f,0x7f,0x81,0x7e,0x7e,0x7d,0x7d,0x7e,0x80,0x82,0x89,0x91,0x98, + 0x9e,0xa2,0x95,0x7d,0x78,0x77,0x6f,0x28,0x19,0x1a,0x18,0x0f,0x18,0x69,0x76,0x58, + 0x65,0x82,0x85,0xc7,0xe9,0x91,0x75,0x75,0x77,0x78,0x78,0x77,0x77,0x77,0x77,0x74, + 0x72,0x6e,0x6a,0x6b,0x76,0x84,0x91,0x9c,0xa4,0xa4,0xa6,0xa7,0xa8,0xa8,0xa9,0xab, + 0xac,0xad,0xaf,0xac,0xa0,0x8f,0x7e,0x6e,0x61,0x5e,0x5b,0x5c,0x58,0x56,0x56,0x58, + 0x5a,0x5a,0x5e,0x61,0x66,0x69,0x69,0x6c,0x6c,0x6a,0x66,0x60,0x5c,0x5c,0x5d,0x60, + 0x64,0x5f,0x58,0x54,0x4a,0x3f,0x39,0x36,0x38,0x40,0x48,0x4b,0x48,0x47,0x43,0x42, + 0x43,0x44,0x46,0x48,0x47,0x48,0x48,0x48,0x49,0x45,0x3d,0x38,0x32,0x2b,0x27,0x25, + 0x7b,0x7d,0x7e,0x7f,0x81,0x80,0x7e,0x7e,0x7e,0x7e,0x7f,0x82,0x89,0x93,0x9a,0x9e, + 0xa0,0x93,0x80,0x7d,0x7a,0x7a,0x71,0x2f,0x15,0x10,0x16,0x16,0x14,0x3e,0x76,0x64, + 0x73,0x89,0x97,0xe4,0xda,0x79,0x71,0x71,0x75,0x75,0x77,0x77,0x77,0x77,0x77,0x73, + 0x6f,0x68,0x67,0x6e,0x7c,0x8a,0x96,0x9f,0xa2,0xa2,0xa2,0xa2,0xa2,0xa3,0xa5,0xa7, + 0xa9,0xab,0xac,0xa7,0x9e,0x90,0x80,0x6f,0x66,0x62,0x60,0x5e,0x58,0x58,0x58,0x5a, + 0x5e,0x61,0x64,0x65,0x68,0x69,0x6b,0x6c,0x6c,0x68,0x62,0x5c,0x56,0x56,0x56,0x58, + 0x59,0x55,0x51,0x4a,0x40,0x38,0x34,0x33,0x31,0x3b,0x42,0x48,0x47,0x45,0x42,0x41, + 0x43,0x46,0x46,0x47,0x48,0x49,0x48,0x4b,0x4a,0x44,0x40,0x3c,0x38,0x2e,0x2c,0x28, + 0x7d,0x81,0x81,0x7f,0x7a,0x7d,0x7c,0x7d,0x7f,0x7e,0x83,0x89,0x93,0x9b,0x9f,0xa1, + 0x91,0x82,0x7d,0x7f,0x7e,0x7a,0x72,0x2e,0x18,0x0e,0x13,0x10,0x0f,0x1e,0x63,0x72, + 0x84,0x8a,0xb6,0xeb,0xc0,0x6e,0x70,0x70,0x72,0x73,0x74,0x76,0x75,0x76,0x73,0x6e, + 0x6a,0x65,0x64,0x6f,0x81,0x8d,0x99,0x9d,0x9d,0x9d,0x9e,0x9e,0x9e,0x9f,0xa0,0xa3, + 0xa5,0xa8,0xa8,0xa3,0x9a,0x8f,0x81,0x72,0x67,0x63,0x5e,0x59,0x55,0x53,0x56,0x5c, + 0x5e,0x61,0x64,0x69,0x6a,0x6a,0x6b,0x6d,0x6a,0x61,0x5b,0x56,0x51,0x4f,0x4f,0x4e, + 0x4f,0x49,0x45,0x3c,0x34,0x2c,0x2b,0x2d,0x2d,0x32,0x39,0x3f,0x43,0x44,0x41,0x42, + 0x45,0x47,0x48,0x4a,0x4a,0x4b,0x4a,0x49,0x47,0x43,0x42,0x3e,0x39,0x32,0x2f,0x2a, + 0x80,0x7f,0x7d,0x7b,0x7b,0x7b,0x7c,0x7f,0x81,0x83,0x8c,0x96,0x9b,0x9e,0x9e,0x91, + 0x82,0x81,0x7f,0x7e,0x7e,0x7b,0x75,0x34,0x16,0x10,0x12,0x0d,0x0d,0x10,0x49,0x6a, + 0x8a,0x92,0xd8,0xeb,0x9d,0x6b,0x6f,0x6f,0x70,0x70,0x72,0x74,0x74,0x72,0x6f,0x68, + 0x63,0x5f,0x65,0x74,0x83,0x90,0x98,0x99,0x99,0x99,0x99,0x9a,0x9b,0x9d,0x9f,0xa1, + 0xa2,0xa5,0xa5,0x9f,0x99,0x8d,0x82,0x78,0x72,0x6a,0x65,0x5b,0x58,0x56,0x57,0x58, + 0x5c,0x64,0x65,0x6b,0x6d,0x6a,0x67,0x66,0x62,0x5a,0x57,0x52,0x4c,0x47,0x45,0x42, + 0x40,0x3e,0x37,0x2f,0x2a,0x26,0x27,0x26,0x26,0x2b,0x33,0x3a,0x3f,0x43,0x43,0x46, + 0x49,0x4a,0x4d,0x4d,0x4d,0x4d,0x4a,0x47,0x46,0x43,0x42,0x3f,0x3b,0x33,0x31,0x2f, + 0x7a,0x7a,0x7a,0x7a,0x7a,0x7c,0x7e,0x80,0x83,0x8c,0x98,0x9c,0xa1,0xa0,0x92,0x85, + 0x83,0x83,0x80,0x7f,0x7f,0x7c,0x76,0x37,0x18,0x0f,0x11,0x0f,0x0f,0x0d,0x33,0x72, + 0x8c,0xa9,0xe6,0xe1,0x7e,0x6b,0x6e,0x6e,0x6e,0x70,0x71,0x73,0x71,0x70,0x69,0x62, + 0x5e,0x5d,0x68,0x74,0x83,0x8f,0x94,0x95,0x94,0x95,0x98,0x99,0x9b,0x9d,0xa0,0xa2, + 0xa6,0xa2,0xb7,0xdd,0xc7,0xcf,0xd4,0xd7,0xdb,0xd5,0xd1,0xcd,0xca,0xc6,0xc0,0xb9, + 0xae,0xa5,0x9e,0x98,0x91,0x8a,0x82,0x7a,0x75,0x6f,0x68,0x60,0x56,0x50,0x48,0x3f, + 0x2f,0x2e,0x2b,0x24,0x25,0x24,0x24,0x22,0x21,0x27,0x31,0x3a,0x41,0x44,0x46,0x49, + 0x4b,0x4d,0x51,0x54,0x52,0x4f,0x4a,0x48,0x45,0x41,0x40,0x3e,0x3b,0x35,0x34,0x31, + 0x7a,0x7a,0x7a,0x7a,0x7b,0x7d,0x7f,0x87,0x90,0x9b,0x9e,0xa1,0x9b,0x90,0x86,0x85, + 0x85,0x84,0x81,0x80,0x7e,0x7c,0x76,0x3a,0x19,0x0f,0x10,0x0f,0x0f,0x12,0x3f,0x84, + 0x91,0xd3,0xeb,0xcc,0x6e,0x6d,0x6c,0x6c,0x6d,0x6e,0x70,0x71,0x71,0x6b,0x65,0x5d, + 0x5a,0x5c,0x69,0x77,0x83,0x8c,0x91,0x92,0x94,0x95,0x99,0x9b,0x9d,0xa1,0xa3,0xa5, + 0xa3,0x9f,0x91,0x2b,0x28,0x27,0x27,0x2a,0x2e,0x2f,0x34,0x41,0x49,0x53,0x5f,0x63, + 0x69,0x77,0x8a,0x95,0x9a,0xa5,0xab,0xb1,0xb4,0xba,0xc0,0xc3,0xcb,0xd4,0xd6,0xcb, + 0xb1,0x62,0x27,0x24,0x23,0x23,0x21,0x22,0x24,0x29,0x33,0x3c,0x3f,0x41,0x45,0x48, + 0x4b,0x4d,0x53,0x58,0x56,0x52,0x4d,0x46,0x44,0x3f,0x3f,0x3e,0x3b,0x38,0x36,0x37, + 0x7a,0x7a,0x79,0x7b,0x7c,0x7e,0x85,0x93,0x9b,0x9f,0xa0,0x98,0x8c,0x86,0x85,0x85, + 0x84,0x83,0x83,0x81,0x80,0x7f,0x7a,0x3e,0x19,0x10,0x0e,0x0f,0x11,0x19,0x61,0x8a, + 0xac,0xe5,0xea,0xac,0x6d,0x6c,0x6a,0x6b,0x6b,0x6d,0x6e,0x70,0x6e,0x67,0x60,0x59, + 0x58,0x5e,0x6c,0x79,0x82,0x89,0x90,0x92,0x92,0x95,0x9a,0x9e,0xa0,0xa4,0xa5,0xa8, + 0x96,0xc5,0x3a,0x39,0x6b,0x6b,0x61,0x5a,0x52,0x47,0x43,0x3b,0x37,0x33,0x30,0x2d, + 0x2f,0x2f,0x30,0x32,0x34,0x35,0x32,0x39,0x37,0x3a,0x3c,0x3e,0x40,0x41,0x43,0x43, + 0x41,0x53,0x78,0x27,0x26,0x24,0x24,0x24,0x25,0x2b,0x31,0x3a,0x3a,0x3d,0x3f,0x44, + 0x48,0x49,0x52,0x57,0x55,0x52,0x4d,0x47,0x44,0x3f,0x40,0x40,0x3e,0x3b,0x39,0x36, + 0x79,0x7a,0x7a,0x7c,0x7c,0x85,0x95,0x9e,0x9b,0xa1,0x98,0x8b,0x87,0x86,0x87,0x85, + 0x85,0x85,0x83,0x83,0x82,0x80,0x7c,0x41,0x18,0x11,0x0d,0x0d,0x12,0x3d,0x7f,0x8d, + 0xd3,0xe8,0xe2,0x88,0x6b,0x6a,0x6a,0x6a,0x6a,0x6b,0x6e,0x6f,0x6c,0x63,0x5d,0x57, + 0x58,0x65,0x71,0x7d,0x85,0x8b,0x8e,0x92,0x95,0x97,0x9d,0xa0,0xa3,0xa6,0xa7,0xa6, + 0x8c,0xc2,0x37,0x4a,0xc9,0xd0,0xd1,0xdc,0xdd,0xdc,0xdf,0xde,0xdf,0xdd,0xd8,0xd5, + 0xce,0xc7,0xc0,0xbb,0xb1,0xaa,0xa2,0x99,0x96,0x8e,0x88,0x83,0x7c,0x78,0x72,0x69, + 0x3c,0x2a,0x8b,0x2a,0x2e,0x2b,0x29,0x28,0x29,0x2b,0x31,0x36,0x37,0x38,0x3c,0x40, + 0x41,0x48,0x4e,0x50,0x53,0x54,0x52,0x4b,0x48,0x43,0x41,0x41,0x40,0x3e,0x3e,0x39, + 0x7b,0x7c,0x7b,0x7c,0x84,0x93,0x9d,0x9f,0x9c,0x96,0x8b,0x89,0x87,0x86,0x86,0x86, + 0x87,0x88,0x84,0x84,0x81,0x81,0x7b,0x44,0x19,0x12,0x0d,0x0d,0x21,0x6b,0x82,0xad, + 0xe8,0xe7,0xcb,0x6f,0x6a,0x68,0x69,0x69,0x69,0x6b,0x6d,0x6e,0x6a,0x62,0x5b,0x58, + 0x5c,0x69,0x76,0x80,0x88,0x8e,0x91,0x94,0x96,0x98,0x9e,0xa1,0xa4,0xa4,0xa5,0xa5, + 0x7f,0xc1,0x3a,0x49,0xc1,0xca,0xcc,0xcc,0xd1,0xd4,0xd2,0xd3,0xd3,0xd4,0xd5,0xd5, + 0xd5,0xd6,0xd5,0xd6,0xd4,0xd5,0xd6,0xd6,0xd5,0xd7,0xd6,0xd7,0xd4,0xd3,0xd4,0xdc, + 0x3e,0x2f,0x8c,0x3b,0x39,0x35,0x33,0x2f,0x2f,0x2e,0x30,0x34,0x35,0x36,0x36,0x39, + 0x3c,0x42,0x46,0x49,0x51,0x54,0x51,0x4b,0x49,0x45,0x43,0x41,0x41,0x41,0x3f,0x3b, + 0x7c,0x7e,0x7f,0x86,0x98,0x9c,0x9b,0x99,0x90,0x89,0x89,0x88,0x86,0x86,0x87,0x89, + 0x88,0x88,0x84,0x85,0x82,0x7f,0x7d,0x48,0x18,0x12,0x0d,0x12,0x47,0x7f,0x92,0xd8, + 0xe8,0xe5,0x9f,0x6a,0x6b,0x6a,0x69,0x69,0x68,0x6a,0x6c,0x6c,0x67,0x60,0x5a,0x58, + 0x61,0x71,0x7e,0x87,0x8e,0x91,0x94,0x95,0x95,0x9e,0xa7,0xa8,0xa8,0xa7,0xac,0xaa, + 0x73,0x6f,0x37,0x4c,0xbf,0xcb,0xcf,0xce,0xd0,0xd2,0xd3,0xd4,0xd5,0xd5,0xd5,0xd4, + 0xd5,0xd6,0xd6,0xd5,0xd6,0xd5,0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xd7,0xd6,0xd5,0xe1, + 0x44,0x33,0x8c,0x43,0x46,0x40,0x3c,0x38,0x35,0x34,0x33,0x32,0x32,0x33,0x33,0x34, + 0x38,0x3b,0x3e,0x44,0x4a,0x4f,0x4c,0x4b,0x4d,0x4a,0x46,0x45,0x43,0x42,0x40,0x3c, + 0x7d,0x80,0x87,0x99,0x9b,0x9d,0x97,0x8e,0x89,0x89,0x87,0x87,0x88,0x88,0x88,0x86, + 0x87,0x87,0x85,0x85,0x81,0x7f,0x77,0x4b,0x19,0x11,0x0c,0x23,0x6b,0x89,0xb5,0xe6, + 0xe6,0xd8,0x7e,0x70,0x6d,0x6c,0x6a,0x68,0x67,0x68,0x69,0x69,0x65,0x5e,0x59,0x57, + 0x69,0x79,0x83,0x8c,0x92,0x94,0x93,0x93,0x9e,0xad,0xb1,0xaf,0xae,0xb1,0xb6,0xb6, + 0x55,0x1f,0x2f,0x50,0xc7,0xd1,0xce,0xd1,0xd3,0xd4,0xd3,0xd4,0xd4,0xd4,0xd4,0xd3, + 0xd4,0xd4,0xd4,0xd0,0xd5,0xd5,0xd6,0xd7,0xd7,0xd6,0xd6,0xd6,0xd7,0xd6,0xd5,0xd5, + 0x35,0x2c,0x87,0x54,0x51,0x4b,0x48,0x42,0x3c,0x38,0x36,0x35,0x33,0x34,0x36,0x36, + 0x37,0x39,0x3b,0x3e,0x42,0x48,0x47,0x48,0x4a,0x48,0x45,0x43,0x42,0x40,0x40,0x3f, + 0x86,0x94,0xa2,0xa3,0xa3,0x9d,0x93,0x90,0x91,0x90,0x90,0x92,0x91,0x90,0x8f,0x8e, + 0x8d,0x8c,0x89,0x8c,0x87,0x83,0x7d,0x53,0x14,0x0a,0x0f,0x4c,0x85,0x9e,0xe2,0xf0, + 0xf0,0xba,0x75,0x72,0x74,0x72,0x6f,0x6e,0x67,0x6a,0x6b,0x68,0x63,0x5c,0x5b,0x5f, + 0x70,0x80,0x8b,0x91,0x94,0x95,0x93,0x9d,0xb1,0xbb,0xbb,0xb9,0xb6,0xc5,0xce,0xc4, + 0x30,0x1f,0x2c,0x51,0xc9,0xcc,0xd1,0xd1,0xd2,0xd4,0xd2,0xd3,0xd4,0xd5,0xd5,0xd6, + 0xd5,0xd4,0xd5,0xd6,0xd5,0xd6,0xd5,0xd6,0xd6,0xd6,0xd6,0xd6,0xd6,0xd5,0xd6,0xcf, + 0x38,0x26,0x15,0x60,0x5c,0x56,0x52,0x4a,0x42,0x3d,0x3a,0x37,0x36,0x36,0x39,0x3a, + 0x39,0x3b,0x3c,0x3a,0x3f,0x43,0x44,0x46,0x45,0x44,0x44,0x41,0x3f,0x3b,0x3b,0x3c, + 0x36,0x38,0x33,0x3a,0x37,0x3b,0x3e,0x3e,0x3d,0x40,0x41,0x41,0x45,0x47,0x46,0x4c, + 0x4b,0x4a,0x4c,0x4b,0x4d,0x51,0x50,0x4f,0x40,0x3d,0x3d,0x51,0x5a,0x6f,0x7f,0x81, + 0x83,0x68,0x5b,0x5a,0x59,0x5d,0x65,0x62,0x64,0x5d,0x65,0x60,0x5d,0x56,0x57,0x5b, + 0x66,0x70,0x7a,0x7e,0x80,0x82,0x8b,0x99,0xa9,0xa9,0xa4,0x98,0x97,0x76,0x26,0x01, + 0x11,0x21,0x26,0x51,0xc9,0xcf,0xcc,0xcf,0xd1,0xd2,0xd3,0xd2,0xd2,0xd4,0xd4,0xd5, + 0xd5,0xd4,0xd3,0xd3,0xd3,0xd4,0xd6,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xe1, + 0x34,0x19,0x13,0x6a,0x62,0x5e,0x58,0x54,0x4c,0x41,0x3d,0x38,0x35,0x39,0x3b,0x3c, + 0x3c,0x3f,0x3e,0x3c,0x3f,0x42,0x45,0x44,0x43,0x42,0x41,0x40,0x3c,0x36,0x36,0x36, + 0x60,0x60,0x5e,0x5f,0x5f,0x61,0x6a,0x5f,0x61,0x64,0x60,0x5e,0x58,0x5a,0x5a,0x5c, + 0x5e,0x61,0x5e,0x5d,0x59,0x61,0x62,0x64,0x5e,0x5b,0x63,0x5d,0x5c,0x5b,0x56,0x51, + 0x50,0x51,0x57,0x4e,0x5d,0x6f,0xc4,0xd0,0xbe,0xce,0xc7,0x7a,0x59,0x58,0x58,0x54, + 0x50,0x52,0x4e,0x47,0x3a,0x39,0x3a,0x3c,0x3a,0x1f,0x12,0x14,0x12,0x13,0x12,0x12, + 0x0d,0x18,0x21,0x51,0xcc,0xcf,0xcd,0xce,0xd0,0xd2,0xd0,0xd1,0xd3,0xd5,0xd5,0xd5, + 0xd4,0xd4,0xd4,0xd4,0xd5,0xd4,0xd6,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd4,0xe1, + 0x29,0x15,0x14,0x6c,0x68,0x63,0x5e,0x59,0x51,0x49,0x41,0x3d,0x3a,0x3d,0x3e,0x40, + 0x44,0x47,0x44,0x42,0x43,0x43,0x43,0x43,0x46,0x44,0x3f,0x3b,0x38,0x34,0x33,0x33, + 0x9a,0x9f,0x93,0x9f,0x8d,0x9e,0x8c,0x96,0x93,0x8d,0x99,0x95,0x94,0x91,0x94,0x99, + 0x95,0x9a,0x97,0x9b,0x9c,0x9f,0xa1,0x9e,0xa3,0xa3,0xa2,0xa4,0xa1,0x9e,0x93,0x95, + 0x96,0x91,0x94,0x8f,0x99,0xa9,0xfa,0xf7,0xf6,0xf6,0xf9,0xb6,0x9b,0x9c,0x9c,0x99, + 0x97,0x78,0x71,0x52,0x4f,0x49,0x49,0x50,0x4f,0x1b,0x16,0x14,0x12,0x15,0x16,0x10, + 0x32,0x8b,0x2f,0x4f,0xc6,0xcd,0xcf,0xd0,0xd1,0xd0,0xd3,0xd3,0xd3,0xd4,0xd1,0xd1, + 0xd5,0xd4,0xd4,0xd4,0xd6,0xd6,0xd6,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xe1, + 0x31,0x15,0x12,0x6c,0x68,0x67,0x61,0x5a,0x53,0x49,0x46,0x40,0x3d,0x3f,0x43,0x46, + 0x4b,0x4d,0x4c,0x49,0x48,0x47,0x43,0x44,0x44,0x43,0x3e,0x3b,0x37,0x33,0x31,0x31, + 0x8a,0x8b,0x94,0x8f,0x92,0x9b,0x94,0x95,0x93,0x94,0x96,0x98,0x9f,0xa4,0xa1,0xa6, + 0xa8,0xa5,0xa2,0xad,0xb3,0xb2,0xab,0xad,0xae,0xb2,0xb0,0xab,0xb0,0xac,0xb2,0xb6, + 0xb2,0xb4,0xb4,0xb4,0xb7,0xd0,0xfb,0xf8,0xf9,0xfc,0xff,0xc7,0xbf,0xba,0xab,0x86, + 0x5f,0x56,0x54,0x5e,0x67,0x5d,0x52,0x56,0x5a,0x43,0x15,0x13,0x1f,0x1e,0x12,0x1d, + 0x45,0xbb,0x39,0x4e,0xca,0xcf,0xcf,0xd1,0xd2,0xd3,0xd4,0xd4,0xd4,0xd3,0xd4,0xd4, + 0xd4,0xd4,0xd4,0xd4,0xd2,0xd2,0xd6,0xd5,0xd5,0xd5,0xd5,0xd5,0xd2,0xd4,0xd5,0xe0, + 0x38,0x1c,0x3e,0x68,0x6a,0x68,0x62,0x5c,0x54,0x4e,0x48,0x44,0x41,0x43,0x47,0x4c, + 0x50,0x53,0x54,0x54,0x52,0x4d,0x4c,0x49,0x47,0x43,0x3f,0x3b,0x38,0x36,0x31,0x32, + 0x8c,0x89,0x88,0x88,0x89,0x87,0x89,0x8b,0x84,0x86,0x89,0x85,0x86,0x84,0x83,0x82, + 0x84,0x84,0x82,0x82,0x82,0x8a,0x85,0x83,0x64,0x7a,0xa8,0xc5,0xc6,0xc6,0xbe,0x90, + 0x87,0x8b,0x8a,0x89,0x91,0xa9,0xc8,0xc9,0xc9,0xc4,0xbf,0x87,0x6b,0x5e,0x5e,0x64, + 0x6b,0x70,0x7a,0x7f,0x81,0x89,0x8d,0x89,0x88,0x79,0x59,0x55,0x5c,0x54,0x4a,0x52, + 0x57,0xbf,0x38,0x50,0xcd,0xce,0xce,0xce,0xd1,0xd1,0xd3,0xd4,0xd5,0xd4,0xd4,0xd4, + 0xd4,0xd4,0xd3,0xd3,0xd3,0xd4,0xd4,0xd5,0xd5,0xd5,0xd5,0xd5,0xd6,0xd5,0xd4,0xe0, + 0x46,0x32,0x85,0x6b,0x6a,0x67,0x64,0x5e,0x55,0x50,0x49,0x48,0x48,0x49,0x4d,0x50, + 0x56,0x58,0x59,0x5b,0x58,0x55,0x51,0x4e,0x4b,0x47,0x42,0x3e,0x3b,0x38,0x36,0x33, + 0x8b,0x8a,0x8e,0x8b,0x8c,0x8d,0x8d,0x8d,0x8b,0x89,0x87,0x87,0x88,0x86,0x83,0x82, + 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x80,0x76,0x7a,0x9c,0xde,0xe4,0xe5,0xe4,0xba,0x7e, + 0x7f,0x7f,0x7f,0x7f,0x7e,0x80,0x7e,0x80,0x81,0x7e,0x7c,0x76,0x6f,0x6d,0x73,0x84, + 0x93,0xa0,0xaf,0xc0,0xcd,0xd8,0xd9,0xd6,0xd1,0xcf,0xce,0xd6,0xde,0xe7,0xed,0xe8, + 0x9f,0xbe,0x39,0x50,0xcd,0xce,0xcc,0xd3,0xd4,0xd4,0xd4,0xd5,0xd5,0xd4,0xd4,0xd4, + 0xd4,0xd4,0xd5,0xd5,0xd4,0xd4,0xd4,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd4,0xd4,0xdf, + 0x44,0x32,0x7e,0x68,0x68,0x65,0x60,0x5d,0x57,0x53,0x50,0x51,0x51,0x52,0x54,0x58, + 0x5d,0x5f,0x5e,0x5e,0x5c,0x5b,0x56,0x52,0x52,0x4d,0x4a,0x45,0x43,0x42,0x3e,0x3b, + 0x88,0x89,0x8b,0x8f,0x8d,0x8b,0x8b,0x8b,0x8c,0x88,0x87,0x87,0x87,0x84,0x82,0x81, + 0x81,0x80,0x7e,0x7e,0x7f,0x81,0x75,0x71,0x96,0xd3,0xe5,0xe5,0xe6,0xd7,0x8e,0x7e, + 0x7f,0x7e,0x7e,0x7f,0x7f,0x7e,0x7f,0x7e,0x7d,0x7b,0x79,0x72,0x6c,0x6d,0x79,0x89, + 0x9d,0xaa,0xba,0xcb,0xd6,0xdb,0xd9,0xcd,0xc0,0xb8,0xbb,0xc5,0xd7,0xe3,0xed,0xf0, + 0xa3,0xbd,0x39,0x53,0xcc,0xc7,0xcd,0xd0,0xd1,0xd4,0xd4,0xd5,0xd5,0xd3,0xd2,0xd4, + 0xd5,0xd4,0xd5,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd5,0xd3,0xd4,0xd4,0xdf, + 0x46,0x32,0x83,0x66,0x64,0x60,0x5d,0x5c,0x5a,0x58,0x56,0x57,0x58,0x5a,0x5e,0x63, + 0x67,0x67,0x65,0x64,0x62,0x5f,0x5b,0x59,0x56,0x54,0x4f,0x4d,0x4b,0x49,0x45,0x44, + 0x88,0x8a,0x8f,0x8e,0x8d,0x8b,0x8b,0x8a,0x8c,0x87,0x86,0x86,0x86,0x84,0x81,0x7f, + 0x80,0x7f,0x82,0x80,0x7b,0x79,0x69,0x90,0xd0,0xe2,0xe4,0xe3,0xe1,0xa9,0x7f,0x81, + 0x82,0x82,0x82,0x81,0x7e,0x7f,0x7d,0x7d,0x7d,0x7b,0x76,0x6f,0x6a,0x6f,0x7f,0x97, + 0xa4,0xb1,0xc3,0xd1,0xd7,0xdb,0xd5,0xc0,0xa4,0x91,0x96,0xb1,0xca,0xdd,0xea,0xef, + 0xa7,0xba,0x3c,0x4d,0xca,0xce,0xd3,0xd0,0xd2,0xd4,0xd4,0xd5,0xd5,0xd3,0xd3,0xd4, + 0xd6,0xd4,0xd5,0xd5,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd5,0xd4,0xd4,0xdf, + 0x47,0x31,0x81,0x64,0x61,0x61,0x5e,0x5e,0x5c,0x5d,0x5b,0x5d,0x5c,0x5f,0x65,0x6a, + 0x6a,0x6b,0x6b,0x6b,0x69,0x62,0x61,0x61,0x60,0x59,0x54,0x54,0x53,0x50,0x4f,0x50, + 0x8c,0x8d,0x8f,0x8e,0x8d,0x8c,0x8b,0x89,0x88,0x86,0x86,0x85,0x85,0x83,0x81,0x81, + 0x81,0x81,0x7d,0x6c,0x78,0x61,0x80,0xca,0xe5,0xe5,0xe4,0xe5,0xc2,0x83,0x80,0x81, + 0x82,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x7b,0x7a,0x79,0x74,0x6c,0x6a,0x73,0x89,0x9e, + 0xa7,0xb3,0xc4,0xce,0xd5,0xd6,0xcf,0xb7,0x96,0x74,0x72,0x95,0xb6,0xd4,0xe4,0xeb, + 0xa8,0xb9,0x3b,0x53,0xd1,0xcf,0xd0,0xd1,0xd2,0xd3,0xd3,0xd3,0xd3,0xd3,0xd3,0xd4, + 0xd4,0xd4,0xd4,0xd3,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xdf, + 0x48,0x32,0x7f,0x60,0x62,0x62,0x60,0x5f,0x5e,0x5e,0x5f,0x5f,0x61,0x65,0x68,0x6b, + 0x6b,0x6c,0x6d,0x6a,0x67,0x66,0x65,0x64,0x60,0x58,0x56,0x56,0x58,0x58,0x58,0x58, + 0x8d,0x8e,0x8f,0x8f,0x8d,0x8d,0x8a,0x88,0x87,0x85,0x85,0x84,0x83,0x82,0x81,0x81, + 0x81,0x7c,0x5c,0x60,0x5e,0x7b,0xbd,0xeb,0xea,0xec,0xe8,0xdb,0x9b,0x78,0x7b,0x7f, + 0x82,0x82,0x81,0x80,0x7e,0x7d,0x7c,0x79,0x78,0x76,0x71,0x68,0x6a,0x7c,0x92,0xa0, + 0xac,0xb8,0xc6,0xcb,0xcf,0xd0,0xc5,0xad,0x90,0x75,0x6a,0x89,0xaf,0xca,0xde,0xe6, + 0xa4,0xbc,0x39,0x56,0xd1,0xd0,0xd1,0xd2,0xd1,0xd2,0xd3,0xd3,0xd3,0xd3,0xd3,0xd3, + 0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xe0, + 0x48,0x31,0x86,0x63,0x62,0x61,0x60,0x61,0x61,0x64,0x63,0x63,0x66,0x6a,0x6e,0x6e, + 0x6f,0x6e,0x6c,0x67,0x68,0x69,0x66,0x62,0x5f,0x5b,0x5a,0x5b,0x5c,0x5e,0x5e,0x5e, + 0x8b,0x8f,0x8e,0x8d,0x8b,0x8a,0x87,0x87,0x85,0x84,0x84,0x84,0x82,0x82,0x7f,0x7f, + 0x85,0x63,0x52,0x55,0x71,0xc3,0xe5,0xee,0xef,0xf5,0xee,0xce,0xa4,0x7c,0x72,0x79, + 0x7c,0x81,0x80,0x7f,0x7d,0x7b,0x79,0x78,0x76,0x73,0x6f,0x68,0x6d,0x81,0x92,0xa6, + 0xb4,0xbb,0xc3,0xc7,0xcd,0xcc,0xbc,0xa5,0x96,0x8e,0x8e,0x9c,0xb1,0xc8,0xd8,0xe1, + 0xa0,0xbd,0x3b,0x55,0xd0,0xcd,0xd2,0xd1,0xd2,0xd2,0xd3,0xd3,0xd4,0xe1,0xdf,0xde, + 0xda,0xdc,0xd7,0xd4,0xd4,0xd6,0xd2,0xd3,0xd1,0xd4,0xd0,0xd5,0xd4,0xd4,0xd4,0xdf, + 0x48,0x31,0x7f,0x69,0x63,0x63,0x62,0x64,0x65,0x66,0x66,0x65,0x6a,0x6a,0x6b,0x6c, + 0x6e,0x6e,0x6a,0x66,0x66,0x67,0x65,0x61,0x5e,0x5c,0x5b,0x59,0x59,0x5d,0x5e,0x5c, + 0x8c,0x8e,0x8d,0x8b,0x8a,0x89,0x87,0x86,0x84,0x84,0x84,0x84,0x83,0x83,0x73,0x77, + 0x71,0x4d,0x51,0x5c,0xc1,0xe4,0xee,0xef,0xf7,0xf7,0xe3,0xc2,0xb1,0x97,0x75,0x6f, + 0x75,0x79,0x7e,0x7c,0x7b,0x78,0x76,0x75,0x73,0x70,0x6e,0x68,0x6e,0x80,0x95,0xad, + 0xbb,0xbd,0xc1,0xc5,0xcb,0xc7,0xb9,0xb1,0xac,0xa9,0xad,0xb8,0xbd,0xc3,0xcd,0xd5, + 0x9e,0xb9,0x3b,0x59,0xd0,0xd0,0xd1,0xd1,0xd2,0xd2,0xd2,0xd3,0xcf,0x3e,0x3b,0x46, + 0x4e,0x5c,0x65,0x71,0x7d,0x88,0x92,0x99,0xa1,0xa3,0xca,0xd3,0xd4,0xd3,0xd2,0xdf, + 0x48,0x33,0x81,0x6b,0x65,0x67,0x67,0x68,0x6a,0x69,0x68,0x68,0x65,0x69,0x6b,0x6a, + 0x6c,0x6d,0x69,0x64,0x64,0x64,0x64,0x5f,0x5f,0x5b,0x59,0x55,0x53,0x57,0x59,0x57, + 0x8c,0x8d,0x8c,0x8b,0x89,0x88,0x85,0x85,0x84,0x86,0x86,0x83,0x82,0x79,0x6d,0x77, + 0x51,0x4b,0x4f,0xba,0xe2,0xee,0xee,0xf6,0xfa,0xec,0xbe,0xb8,0xad,0x9d,0x87,0x6f, + 0x6b,0x70,0x76,0x79,0x77,0x74,0x74,0x72,0x71,0x6e,0x6a,0x66,0x6e,0x81,0x9c,0xb5, + 0xbe,0xbf,0xc3,0xc7,0xcb,0xc7,0xc2,0xbf,0xbd,0xbc,0xc3,0xc9,0xc8,0xc2,0xc6,0xd0, + 0x9b,0xb5,0x3a,0x53,0xcc,0xce,0xd2,0xd1,0xd1,0xcf,0xd0,0xd2,0xce,0x2c,0x13,0x10, + 0x0b,0x0b,0x0f,0x18,0x15,0x10,0x15,0x17,0x15,0x0e,0x9a,0xd6,0xd1,0xd3,0xd4,0xdf, + 0x48,0x33,0x7d,0x6a,0x66,0x65,0x68,0x69,0x6a,0x6a,0x68,0x69,0x65,0x68,0x69,0x68, + 0x69,0x6a,0x68,0x65,0x65,0x65,0x62,0x62,0x5f,0x5b,0x55,0x52,0x4d,0x50,0x52,0x52, + 0x8c,0x8d,0x8c,0x89,0x89,0x87,0x86,0x85,0x86,0x88,0x86,0x84,0x79,0x5d,0x70,0x5f, + 0x4c,0x56,0xad,0xea,0xef,0xef,0xf3,0xfc,0xf0,0x8e,0x83,0xa4,0xa7,0x95,0x83,0x70, + 0x5d,0x64,0x6a,0x72,0x72,0x74,0x72,0x71,0x70,0x6d,0x69,0x65,0x70,0x8a,0xa8,0xb9, + 0xc1,0xc6,0xca,0xce,0xcd,0xcb,0xca,0xca,0xcb,0xcb,0xcc,0xce,0xd0,0xce,0xc9,0xcd, + 0x97,0xb5,0x39,0x54,0xcc,0xd0,0xd1,0xd1,0xd2,0xd3,0xd3,0xd3,0xd3,0xdd,0xdb,0xd5, + 0xcf,0xc8,0x98,0x13,0x46,0xa8,0x99,0x8e,0x86,0x79,0xb8,0xd5,0xd4,0xd3,0xd3,0xde, + 0x47,0x31,0x7c,0x67,0x63,0x62,0x66,0x68,0x6c,0x6e,0x6d,0x6b,0x67,0x69,0x69,0x6b, + 0x6b,0x6b,0x68,0x66,0x66,0x66,0x66,0x64,0x60,0x58,0x53,0x50,0x4b,0x47,0x48,0x4d, + 0x8b,0x8c,0x8b,0x88,0x88,0x87,0x84,0x86,0x87,0x86,0x84,0x81,0x5f,0x5c,0x6a,0x4d, + 0x55,0x9b,0xe4,0xed,0xee,0xf0,0xf7,0xf9,0x9a,0x23,0x3e,0x72,0x8f,0x88,0x7a,0x61, + 0x4e,0x56,0x5d,0x67,0x70,0x72,0x71,0x6f,0x6e,0x6a,0x68,0x64,0x75,0x94,0xad,0xbf, + 0xc6,0xcc,0xd0,0xd0,0xd1,0xd2,0xd3,0xd3,0xd2,0xcc,0xce,0xd2,0xd3,0xd1,0xce,0xcc, + 0x99,0xb1,0x3d,0x58,0xce,0xcc,0xcc,0xd2,0xd2,0xd1,0xd2,0xd2,0xd2,0xd2,0xd3,0xd1, + 0xd3,0xd4,0xa7,0x0f,0x52,0xdc,0xd6,0xd4,0xd2,0xd3,0xd3,0xd4,0xd4,0xd2,0xd3,0xdd, + 0x49,0x32,0x7f,0x64,0x5d,0x5e,0x5f,0x66,0x6d,0x6d,0x6a,0x68,0x67,0x68,0x6a,0x6d, + 0x6c,0x6d,0x68,0x67,0x68,0x67,0x66,0x65,0x61,0x56,0x51,0x4d,0x48,0x43,0x48,0x4c, + 0x8d,0x8c,0x8b,0x89,0x88,0x86,0x84,0x87,0x8a,0x85,0x84,0x69,0x4e,0x69,0x52,0x57, + 0x8f,0xe5,0xed,0xed,0xf0,0xf8,0xf8,0xae,0x1b,0x20,0x2d,0x3f,0x64,0x6e,0x66,0x53, + 0x3a,0x3f,0x4c,0x5d,0x6a,0x6f,0x6f,0x6e,0x6b,0x6c,0x6a,0x68,0x82,0xa1,0xb7,0xc8, + 0xce,0xd2,0xd4,0xd4,0xd5,0xd8,0xd5,0xd1,0xcd,0xd1,0xd0,0xd3,0xd6,0xd8,0xd9,0xd5, + 0x95,0xb3,0x3c,0x5d,0xd3,0xd0,0xd1,0xd2,0xd3,0xd2,0xd1,0xd2,0xd2,0xd2,0xd1,0xd3, + 0xd2,0xd2,0xa1,0x11,0x51,0xde,0xd4,0xd4,0xd3,0xd4,0xd2,0xd4,0xd3,0xd4,0xd3,0xde, + 0x4a,0x34,0x80,0x64,0x5d,0x5d,0x5a,0x61,0x67,0x67,0x67,0x68,0x68,0x68,0x69,0x6a, + 0x6b,0x6e,0x68,0x67,0x69,0x67,0x66,0x62,0x5a,0x4f,0x4c,0x4c,0x49,0x46,0x4a,0x4f, + 0x8c,0x8a,0x89,0x89,0x86,0x83,0x82,0x8c,0x8a,0x83,0x79,0x49,0x53,0x5e,0x4b,0x75, + 0xcf,0xe9,0xf1,0xed,0xf8,0xfa,0xc9,0x2f,0x1c,0x24,0x30,0x38,0x42,0x4f,0x4f,0x40, + 0x2e,0x27,0x3a,0x51,0x62,0x6e,0x6f,0x6d,0x6b,0x6e,0x6c,0x6e,0x8a,0xa9,0xbf,0xcf, + 0xd5,0xd6,0xd6,0xd6,0xd6,0xd3,0xd0,0xce,0xce,0xd1,0xd2,0xd6,0xd9,0xdd,0xe1,0xe0, + 0x91,0xb3,0x3b,0x5e,0xd0,0xd1,0xd3,0xd1,0xd1,0xd1,0xd1,0xd2,0xd0,0xbc,0xc2,0xc8, + 0xc9,0xcb,0xa2,0x0d,0x4e,0xd6,0xcb,0xcf,0xce,0xcd,0xd5,0xd3,0xd3,0xd3,0xd3,0xde, + 0x49,0x33,0x7f,0x64,0x5d,0x5c,0x59,0x5c,0x5e,0x5e,0x61,0x64,0x66,0x68,0x64,0x63, + 0x62,0x63,0x60,0x5f,0x63,0x61,0x5f,0x5a,0x54,0x4b,0x4a,0x47,0x44,0x46,0x4c,0x4f, + 0x8b,0x87,0x87,0x87,0x85,0x83,0x84,0x8e,0x87,0x82,0x5c,0x41,0x5d,0x46,0x64,0xb4, + 0xe0,0xec,0xf2,0xf7,0xfa,0xcc,0x38,0x1d,0x1f,0x23,0x28,0x31,0x3d,0x50,0x53,0x45, + 0x33,0x1e,0x2d,0x46,0x5a,0x6a,0x6d,0x6c,0x6c,0x6e,0x6f,0x71,0x90,0xad,0xc4,0xd3, + 0xd8,0xd8,0xd5,0xd4,0xd0,0xce,0xcd,0xce,0xcf,0xd2,0xd5,0xd9,0xdd,0xe4,0xe6,0xe3, + 0x96,0xaf,0x3a,0x5d,0xd2,0xcf,0xd0,0xd2,0xd1,0xd1,0xd0,0xd1,0xcc,0x21,0x0f,0x14, + 0x18,0x1d,0x1e,0x19,0x1d,0x2e,0x33,0x36,0x3a,0x36,0xa9,0xd4,0xd4,0xd2,0xd1,0xde, + 0x49,0x34,0x7c,0x69,0x5f,0x5b,0x55,0x57,0x58,0x57,0x5b,0x60,0x62,0x60,0x5e,0x5c, + 0x59,0x55,0x50,0x52,0x58,0x56,0x53,0x51,0x4a,0x43,0x41,0x3e,0x3d,0x42,0x49,0x4a, + 0x89,0x88,0x89,0x85,0x84,0x83,0x84,0x86,0x7e,0x70,0x41,0x51,0x52,0x5a,0x93,0xd0, + 0xe1,0xee,0xf4,0xf9,0xdc,0x4c,0x24,0x23,0x22,0x23,0x23,0x2b,0x49,0x6c,0x74,0x68, + 0x4f,0x2e,0x2a,0x3f,0x53,0x64,0x6a,0x6b,0x6c,0x6f,0x72,0x73,0x91,0xae,0xc5,0xd5, + 0xd8,0xd3,0xd1,0xcd,0xcf,0xcf,0xcf,0xd0,0xd2,0xd6,0xd9,0xdd,0xe3,0xe4,0xe5,0xe2, + 0x99,0xab,0x3c,0x5f,0xd1,0xce,0xd0,0xd1,0xd1,0xd0,0xd1,0xd1,0xd0,0x7b,0x66,0x5a, + 0x4f,0x45,0x3b,0x37,0x31,0x30,0x2b,0x2a,0x26,0x1e,0x9e,0xd5,0xd3,0xd3,0xd3,0xdd, + 0x4a,0x34,0x7a,0x66,0x5d,0x57,0x50,0x52,0x52,0x52,0x5a,0x5b,0x5d,0x5b,0x59,0x52, + 0x4c,0x46,0x44,0x44,0x4a,0x49,0x48,0x46,0x40,0x38,0x38,0x35,0x35,0x3b,0x42,0x42, + 0x89,0x89,0x87,0x84,0x83,0x83,0x87,0x78,0x77,0x52,0x3d,0x5b,0x5a,0x89,0xc8,0xde, + 0xe6,0xf3,0xfb,0xe3,0x62,0x35,0x33,0x31,0x2b,0x2d,0x2b,0x35,0x55,0x80,0x99,0x97, + 0x7e,0x57,0x33,0x3b,0x4e,0x5e,0x66,0x6c,0x6d,0x71,0x75,0x76,0x8c,0xae,0xc2,0xcf, + 0xd0,0xcf,0xcd,0xcf,0xd0,0xd1,0xd2,0xd4,0xd7,0xdb,0xdf,0xe2,0xe2,0xe4,0xe6,0xe3, + 0x99,0xa8,0x3a,0x61,0xd1,0xce,0xd0,0xd0,0xd1,0xd2,0xd1,0xd1,0xd1,0xd7,0xd3,0xe5, + 0xdc,0xd5,0xd5,0xd3,0xd0,0xce,0xc9,0xca,0xc8,0xc6,0xcd,0xd2,0xd2,0xd2,0xd2,0xdd, + 0x4a,0x33,0x7e,0x62,0x58,0x50,0x4b,0x4a,0x4c,0x50,0x59,0x5f,0x5c,0x5a,0x55,0x4e, + 0x42,0x3b,0x38,0x3b,0x3c,0x3a,0x39,0x3a,0x36,0x30,0x2f,0x2d,0x2c,0x2f,0x35,0x3a, + 0x89,0x87,0x83,0x82,0x82,0x85,0x81,0x67,0x6b,0x36,0x4c,0x5f,0x78,0xc4,0xdd,0xe2, + 0xf1,0xfa,0xea,0x9b,0x6c,0x60,0x50,0x3a,0x33,0x34,0x32,0x41,0x68,0x8f,0xad,0xb5, + 0xac,0x8e,0x69,0x4f,0x52,0x59,0x64,0x6b,0x6f,0x73,0x77,0x78,0x89,0xa5,0xb8,0xc3, + 0xce,0xcf,0xcf,0xd0,0xd3,0xd5,0xd7,0xd9,0xdd,0xde,0xe0,0xe1,0xe1,0xe1,0xe3,0xe0, + 0x99,0xa9,0x3b,0x63,0xd2,0xcf,0xd0,0xd1,0xd1,0xd0,0xd1,0xd1,0xd2,0xc9,0x77,0x3d, + 0x53,0x9f,0xd2,0xc8,0xaa,0xd2,0xd3,0xd3,0xd3,0xd3,0xd4,0xd1,0xd2,0xd1,0xd2,0xde, + 0x4a,0x31,0x7e,0x57,0x4e,0x4a,0x48,0x46,0x4d,0x50,0x58,0x5e,0x5b,0x5a,0x55,0x4e, + 0x40,0x36,0x30,0x2d,0x2f,0x2e,0x31,0x33,0x2f,0x2c,0x29,0x26,0x23,0x21,0x27,0x2f, + 0x87,0x83,0x7c,0x7e,0x85,0x83,0x6d,0x63,0x50,0x33,0x60,0x68,0xb2,0xde,0xdf,0xe9, + 0xf9,0xee,0xc2,0xb8,0xb8,0xae,0x8d,0x5d,0x3c,0x3c,0x49,0x5e,0x83,0xa4,0xba,0xc3, + 0xc5,0xbc,0xa9,0x85,0x6e,0x5f,0x63,0x6c,0x71,0x76,0x7a,0x7a,0x84,0x99,0xae,0xbe, + 0xcb,0xcf,0xd1,0xd3,0xd6,0xda,0xdf,0xdf,0xde,0xdf,0xe0,0xe0,0xe0,0xe0,0xe0,0xdf, + 0x96,0xa8,0x3d,0x63,0xd4,0xd0,0xd0,0xd0,0xd0,0xcc,0xd0,0xd0,0xd1,0x40,0x17,0x0e, + 0x12,0x18,0x8b,0xc3,0x1c,0x3d,0xce,0xd2,0xd3,0xd2,0xd2,0xd1,0xd1,0xd2,0xd2,0xdd, + 0x4a,0x32,0x7e,0x4c,0x41,0x45,0x45,0x47,0x4d,0x4e,0x55,0x59,0x5c,0x5c,0x57,0x4f, + 0x3f,0x35,0x2e,0x26,0x28,0x28,0x2d,0x31,0x31,0x2b,0x26,0x20,0x1a,0x1d,0x23,0x2a, + 0x86,0x7f,0x71,0x7f,0x86,0x7f,0x5a,0x59,0x35,0x4d,0x6e,0xaa,0xd8,0xde,0xe5,0xf4, + 0xec,0xd1,0xd0,0xdb,0xdf,0xd0,0xad,0x7f,0x51,0x4e,0x69,0x8c,0xa5,0xb1,0xbd,0xc5, + 0xcd,0xd2,0xcc,0xb3,0x8f,0x70,0x68,0x6e,0x74,0x79,0x7d,0x7c,0x7f,0x8f,0xa3,0xb8, + 0xc6,0xcf,0xd6,0xd8,0xdc,0xde,0xdf,0xde,0xdd,0xdd,0xdf,0xde,0xde,0xde,0xde,0xdd, + 0x92,0xa6,0x3d,0x66,0xd5,0xd0,0xd0,0xd0,0xd0,0xd1,0xd0,0xcf,0xc3,0x23,0x29,0xcb, + 0xa3,0x16,0x46,0xd2,0x52,0x0f,0x8f,0xd5,0xd2,0xd3,0xd2,0xd2,0xd0,0xd1,0xd2,0xdd, + 0x4b,0x34,0x7e,0x41,0x38,0x3e,0x40,0x46,0x4b,0x4e,0x53,0x56,0x5b,0x58,0x54,0x4e, + 0x43,0x38,0x2f,0x26,0x23,0x28,0x2c,0x2c,0x2d,0x29,0x26,0x1f,0x19,0x20,0x28,0x31, + 0x84,0x71,0x6f,0x84,0x86,0x6d,0x50,0x41,0x3f,0x5e,0xa7,0xda,0xdc,0xde,0xee,0xe6, + 0xc7,0xd3,0xe7,0xee,0xef,0xe4,0xc3,0x93,0x5f,0x5b,0x88,0xb3,0xc9,0xc0,0xbd,0xbd, + 0xc6,0xd5,0xd5,0xc3,0xa7,0x83,0x70,0x74,0x75,0x7b,0x7e,0x7f,0x7e,0x83,0x98,0xac, + 0xc0,0xcc,0xda,0xdb,0xdc,0xde,0xdd,0xdd,0xdc,0xdc,0xdc,0xdd,0xdc,0xdd,0xde,0xdd, + 0x94,0xa2,0x3f,0x69,0xd3,0xcf,0xd0,0xd1,0xd0,0xd0,0xd0,0xd0,0xc7,0x27,0x37,0xce, + 0xc2,0x11,0x37,0xdf,0xd1,0x0b,0x38,0xe0,0xd0,0xd2,0xd2,0xd2,0xd2,0xd1,0xd2,0xdc, + 0x4c,0x34,0x7a,0x3f,0x34,0x3c,0x41,0x46,0x49,0x49,0x4b,0x51,0x55,0x54,0x53,0x4a, + 0x43,0x39,0x2b,0x22,0x24,0x26,0x29,0x29,0x2a,0x28,0x26,0x21,0x21,0x28,0x2f,0x37, + 0x82,0x65,0x79,0x87,0x7d,0x57,0x4b,0x3c,0x3d,0xa1,0xdd,0xdd,0xde,0xe3,0xd8,0xb4, + 0xbc,0xdc,0xef,0xf5,0xf4,0xe9,0xc7,0x95,0x66,0x6b,0x9b,0xc6,0xdb,0xd2,0xba,0xa9, + 0xa8,0xc3,0xc6,0xbc,0xa9,0x87,0x73,0x77,0x7b,0x80,0x82,0x80,0x82,0x80,0x8c,0xa3, + 0xb7,0xc8,0xd4,0xdb,0xdc,0xdc,0xdb,0xdc,0xda,0xda,0xdb,0xdc,0xdb,0xdc,0xdd,0xdc, + 0x91,0xa2,0x3f,0x6b,0xd1,0xcf,0xcf,0xd0,0xd0,0xd0,0xd0,0xd0,0xd1,0x75,0x13,0x7f, + 0xd0,0x29,0x22,0xd7,0xb6,0x0c,0x47,0xde,0xd2,0xd2,0xd2,0xd2,0xd1,0xd1,0xd2,0xdd, + 0x4c,0x35,0x7d,0x41,0x3a,0x40,0x45,0x49,0x48,0x49,0x48,0x4a,0x4e,0x4e,0x4c,0x48, + 0x40,0x35,0x27,0x21,0x24,0x25,0x27,0x29,0x29,0x2a,0x26,0x25,0x29,0x2f,0x34,0x39, + 0x70,0x64,0x80,0x86,0x73,0x44,0x34,0x2a,0x7a,0xd7,0xe0,0xdd,0xdc,0xce,0xb1,0xa9, + 0xc5,0xe3,0xf0,0xf6,0xf1,0xe0,0xba,0x84,0x5c,0x7b,0xaf,0xd4,0xe4,0xdd,0xbe,0x8b, + 0x6b,0x8f,0xa2,0x9f,0x92,0x7a,0x72,0x79,0x82,0x84,0x84,0x83,0x84,0x83,0x83,0x93, + 0xa8,0xbd,0xca,0xd5,0xd9,0xd9,0xda,0xda,0xda,0xda,0xda,0xda,0xda,0xda,0xdb,0xda, + 0x91,0xa0,0x3d,0x6d,0xd2,0xce,0xcf,0xd0,0xd0,0xd0,0xd0,0xcf,0xca,0x23,0x19,0x14, + 0x0b,0x16,0x17,0x0f,0x14,0x17,0xaf,0xd1,0xd2,0xd2,0xd2,0xd2,0xd0,0xd2,0xd0,0xdd, + 0x4d,0x35,0x7b,0x46,0x41,0x43,0x49,0x4e,0x4f,0x4f,0x4a,0x49,0x4b,0x49,0x48,0x44, + 0x3c,0x34,0x29,0x24,0x25,0x27,0x2a,0x2d,0x2d,0x2c,0x2c,0x2f,0x31,0x37,0x3b,0x3f, + 0x63,0x6a,0x7f,0x81,0x62,0x22,0x25,0x6e,0xd8,0xe1,0xe1,0xd9,0xc5,0xb5,0xab,0xa8, + 0xc3,0xe1,0xf0,0xf4,0xeb,0xd4,0xa7,0x6a,0x63,0x92,0xc0,0xde,0xe8,0xdc,0xbf,0x7a, + 0x30,0x3b,0x60,0x6d,0x6a,0x66,0x72,0x7b,0x81,0x86,0x86,0x85,0x85,0x84,0x84,0x86, + 0x94,0xaa,0xbc,0xc9,0xd4,0xd7,0xd8,0xd8,0xd9,0xd9,0xd9,0xd9,0xd9,0xd9,0xdb,0xd7, + 0x8c,0x9b,0x3c,0x6e,0xd2,0xce,0xcf,0xcf,0xd0,0xd0,0xd0,0xcf,0xcd,0x7b,0x61,0x55, + 0x48,0x3b,0x2f,0x26,0x46,0xa5,0xd2,0xd1,0xd1,0xd2,0xd1,0xd1,0xd1,0xd1,0xd1,0xdb, + 0x4f,0x35,0x75,0x47,0x40,0x45,0x4d,0x52,0x52,0x51,0x4b,0x4a,0x46,0x48,0x46,0x3f, + 0x38,0x33,0x2f,0x28,0x2a,0x2b,0x2c,0x31,0x30,0x32,0x32,0x38,0x3d,0x42,0x45,0x49, + 0x5e,0x75,0x7e,0x7a,0x42,0x2a,0x4c,0xd5,0xee,0xe5,0xd4,0xbf,0xb4,0xb1,0xaa,0xa7, + 0xba,0xd3,0xe1,0xe5,0xdb,0xbe,0x8d,0x57,0x79,0xa5,0xcb,0xe0,0xe6,0xdb,0xb9,0x70, + 0x23,0x1a,0x29,0x40,0x52,0x64,0x72,0x7b,0x81,0x87,0x87,0x86,0x86,0x85,0x85,0x85, + 0x87,0x9a,0xaf,0xbe,0xca,0xd3,0xd6,0xd7,0xd8,0xd8,0xd8,0xd7,0xd7,0xd6,0xd6,0xcb, + 0x88,0x99,0x3b,0x6f,0xd1,0xce,0xcf,0xcf,0xcf,0xcf,0xcf,0xd0,0xd1,0xd2,0xd6,0xdd, + 0xe0,0xe1,0xe4,0xe0,0xda,0xce,0xd2,0xd1,0xd1,0xd1,0xd1,0xd2,0xd1,0xd1,0xd1,0xda, + 0x4e,0x35,0x73,0x4c,0x43,0x4a,0x4f,0x54,0x55,0x50,0x4c,0x48,0x45,0x46,0x44,0x3f, + 0x3a,0x36,0x34,0x30,0x30,0x32,0x34,0x37,0x38,0x3c,0x3e,0x42,0x48,0x4c,0x4c,0x4a, + 0x61,0x7d,0x80,0x60,0x39,0x3f,0xca,0xed,0xec,0xd8,0xb7,0xab,0xb2,0xb2,0xaa,0xa2, + 0xac,0xbd,0xca,0xcc,0xc2,0xa2,0x6a,0x5a,0x8a,0xb2,0xd2,0xe5,0xe8,0xdd,0xb5,0x71, + 0x1e,0x1e,0x2a,0x3c,0x51,0x64,0x72,0x7c,0x82,0x86,0x87,0x87,0x86,0x85,0x86,0x86, + 0x87,0x8e,0xa0,0xb3,0xc2,0xcf,0xd5,0xd6,0xd6,0xd5,0xd7,0xd5,0xd3,0xcd,0xc5,0xb8, + 0x81,0x95,0x3a,0x71,0xd1,0xcd,0xcd,0xd0,0xd0,0xcf,0xce,0xd0,0xd1,0xcd,0xcc,0x68, + 0x4a,0x54,0x61,0x79,0xca,0xd4,0xd4,0xd2,0xd0,0xd1,0xd1,0xd1,0xd1,0xd1,0xd0,0xdc, + 0x4e,0x37,0x74,0x4f,0x45,0x49,0x4b,0x51,0x50,0x4c,0x47,0x46,0x45,0x45,0x46,0x41, + 0x3b,0x35,0x37,0x37,0x3c,0x3c,0x3f,0x40,0x43,0x46,0x4a,0x4b,0x4f,0x50,0x4d,0x4a, + 0x6d,0x80,0x5e,0x35,0x31,0xa6,0xee,0xef,0xdb,0xb8,0xaa,0xab,0xb1,0xb2,0xac,0xa3, + 0x8e,0x93,0xa5,0xa8,0x9c,0x80,0x5c,0x63,0x91,0xb9,0xd9,0xe9,0xe7,0xd9,0xb1,0x6d, + 0x22,0x24,0x33,0x44,0x56,0x67,0x75,0x7e,0x83,0x86,0x87,0x87,0x86,0x85,0x86,0x87, + 0x88,0x8a,0x92,0xa5,0xb6,0xc6,0xcf,0xd4,0xd5,0xd3,0xd2,0xce,0xc4,0xb8,0xaa,0x9a, + 0x7a,0x94,0x3b,0x73,0xd2,0xcd,0xce,0xd1,0xd0,0xcf,0xcf,0xcf,0xd1,0xa4,0x15,0x0c, + 0x16,0x12,0x0e,0x0c,0x0a,0x79,0xce,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd0,0xdc, + 0x4e,0x36,0x74,0x4b,0x3e,0x41,0x43,0x46,0x46,0x46,0x43,0x47,0x49,0x46,0x44,0x42, + 0x3d,0x38,0x39,0x3e,0x47,0x47,0x45,0x46,0x4a,0x4d,0x4f,0x52,0x55,0x54,0x50,0x4a, + 0x77,0x93,0x5f,0x1e,0x99,0xe7,0xe1,0xc6,0xb2,0xb0,0xad,0xaa,0xaf,0xb0,0xae,0xa4, + 0x89,0x7a,0x7b,0x7a,0x77,0x6a,0x5e,0x67,0x8e,0xb8,0xd8,0xe8,0xe5,0xd5,0xab,0x67, + 0x2c,0x2f,0x3c,0x4b,0x5d,0x6d,0x7a,0x80,0x83,0x85,0x86,0x86,0x86,0x85,0x86,0x86, + 0x88,0x8a,0x8c,0x99,0xac,0xbe,0xc9,0xd3,0xd3,0xd0,0xc8,0xbc,0xae,0x9b,0x86,0x6c, + 0x73,0x90,0x3b,0x73,0xd0,0xcd,0xcf,0xcf,0xcf,0xce,0xcf,0xce,0xc8,0x31,0x15,0x8f, + 0xce,0xc6,0xc5,0xaa,0x29,0x14,0xa7,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd0,0xdc, + 0x4e,0x34,0x78,0x4a,0x3b,0x3b,0x3b,0x3c,0x3c,0x3d,0x40,0x48,0x48,0x45,0x43,0x45, + 0x41,0x3d,0x3e,0x47,0x4e,0x4a,0x4d,0x4e,0x4f,0x51,0x52,0x52,0x56,0x56,0x50,0x4a, + 0x85,0xa7,0x3b,0x67,0xdc,0xcd,0xad,0xa2,0xab,0xad,0xab,0xab,0xae,0xad,0xa8,0xa1, + 0x95,0x8c,0x81,0x6c,0x6a,0x65,0x59,0x60,0x8a,0xb6,0xd6,0xe3,0xdf,0xce,0xa0,0x64, + 0x35,0x3c,0x47,0x56,0x65,0x71,0x7a,0x81,0x85,0x84,0x85,0x85,0x85,0x83,0x84,0x85, + 0x87,0x8b,0x8d,0x90,0xa0,0xb4,0xc3,0xcb,0xca,0xc1,0xb1,0xa2,0x8a,0x6f,0x53,0x36, + 0x6b,0x91,0x3e,0x74,0xd0,0xce,0xce,0xcf,0xce,0xcc,0xce,0xcd,0xbe,0x21,0x38,0xce, + 0xcf,0xd0,0xd2,0xd0,0xaf,0x07,0x4e,0xdd,0xd1,0xd2,0xd1,0xd1,0xd0,0xd0,0xd0,0xd9, + 0x4e,0x35,0x78,0x4b,0x3b,0x3a,0x37,0x39,0x3b,0x3c,0x40,0x47,0x49,0x47,0x47,0x46, + 0x45,0x42,0x46,0x4c,0x4f,0x50,0x52,0x51,0x51,0x4f,0x52,0x51,0x55,0x53,0x4e,0x4b, + 0xbe,0xc3,0x85,0xc4,0xbe,0x95,0x95,0xa1,0xaa,0xa9,0xa9,0xa9,0xac,0xac,0xa6,0xa0, + 0x93,0x7e,0x77,0x69,0x61,0x5a,0x53,0x66,0x8e,0xab,0xc6,0xd2,0xd0,0xbc,0x90,0x57, + 0x44,0x48,0x4f,0x57,0x61,0x69,0x74,0x7d,0x83,0x84,0x86,0x84,0x84,0x85,0x83,0x84, + 0x86,0x8a,0x8d,0x8e,0x98,0xaa,0xb6,0xbb,0xb5,0xab,0x97,0x81,0x62,0x40,0x26,0x18, + 0x6a,0x8d,0x3d,0x78,0xcf,0xcd,0xcf,0xd0,0xcf,0xcf,0xd0,0xd1,0xc5,0x2b,0x1f,0xc6, + 0xcf,0xd0,0xcf,0xd1,0x9f,0x0b,0x4e,0xda,0xd2,0xd1,0xd1,0xd1,0xcf,0xce,0xd0,0xda, + 0x4f,0x35,0x75,0x4d,0x3a,0x38,0x37,0x37,0x3d,0x41,0x43,0x47,0x4b,0x4d,0x4a,0x47, + 0x45,0x43,0x47,0x4d,0x51,0x54,0x53,0x50,0x4e,0x4e,0x4f,0x4e,0x4f,0x4d,0x49,0x48, + 0xcf,0xd5,0xcd,0xaa,0x8b,0x8b,0x97,0xa0,0xa7,0xaa,0xaa,0xaa,0xab,0xaa,0xa5,0x9c, + 0x90,0x74,0x5f,0x57,0x45,0x42,0x54,0x73,0x93,0xa4,0xba,0xbf,0xb1,0x98,0x72,0x48, + 0x46,0x46,0x48,0x4a,0x50,0x5b,0x6d,0x77,0x81,0x83,0x85,0x85,0x85,0x8a,0x8a,0x89, + 0x87,0x89,0x8c,0x8e,0x93,0x9f,0xa7,0xaa,0xa6,0x99,0x81,0x68,0x47,0x2e,0x1d,0x14, + 0x6a,0x8c,0x3c,0x78,0xd0,0xce,0xce,0xce,0xce,0xce,0xcf,0xcf,0xd1,0x8e,0x0b,0x12, + 0xb7,0xd0,0xd0,0xa2,0x18,0x13,0xac,0xd1,0xd1,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xda, + 0x51,0x35,0x71,0x4e,0x3a,0x3b,0x3a,0x3b,0x3f,0x41,0x43,0x45,0x4a,0x4d,0x4d,0x49, + 0x45,0x45,0x48,0x4b,0x4d,0x52,0x54,0x4f,0x4b,0x4e,0x4e,0x4d,0x4c,0x4a,0x43,0x42, + 0xc8,0xb5,0x97,0x8d,0x8c,0x8f,0x99,0xa1,0xa8,0xab,0xab,0xac,0xad,0xaa,0xa2,0x97, + 0x87,0x79,0x60,0x43,0x39,0x44,0x61,0x85,0x9e,0xb8,0xb7,0xae,0x96,0x6d,0x50,0x3f, + 0x3f,0x3d,0x3c,0x39,0x3d,0x4e,0x63,0x72,0x7c,0x82,0x85,0x84,0x89,0x93,0x93,0x93, + 0x8e,0x89,0x8c,0x8f,0x92,0x9a,0x9f,0x9e,0x9c,0x8e,0x7a,0x6a,0x55,0x3e,0x2b,0x17, + 0x6d,0x88,0x3c,0x7a,0xd0,0xce,0xce,0xce,0xce,0xce,0xcf,0xcf,0xcf,0xd4,0xa4,0x21, + 0xaf,0xcd,0xd3,0x95,0x18,0x87,0xd3,0xd2,0xd2,0xd1,0xd0,0xd0,0xd0,0xd0,0xd0,0xda, + 0x53,0x36,0x74,0x50,0x3d,0x40,0x41,0x3c,0x3c,0x3c,0x3e,0x40,0x46,0x4a,0x4a,0x48, + 0x49,0x48,0x47,0x48,0x4a,0x50,0x53,0x4e,0x4f,0x4f,0x4f,0x4d,0x4f,0x4e,0x48,0x45, + 0x94,0x7d,0x80,0x8a,0x91,0x9b,0xa1,0xa3,0xa9,0xaa,0xaa,0xac,0xac,0xaa,0xa3,0x97, + 0x83,0x6e,0x55,0x3f,0x3d,0x48,0x68,0x8c,0xb2,0xca,0xbe,0xa3,0x7d,0x4f,0x37,0x32, + 0x31,0x2e,0x2b,0x24,0x28,0x40,0x5a,0x70,0x7f,0x83,0x84,0x85,0x8c,0x96,0x97,0x99, + 0x94,0x8b,0x8d,0x91,0x93,0x98,0x98,0x99,0x97,0x91,0x8b,0x82,0x71,0x5c,0x40,0x25, + 0x72,0x85,0x3b,0x7d,0xcf,0xcc,0xce,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xd2,0xd0, + 0xcc,0xce,0xcd,0xc8,0xc3,0xd4,0xcc,0xd0,0xd1,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd9, + 0x51,0x37,0x74,0x4f,0x3e,0x40,0x3f,0x3a,0x39,0x38,0x3a,0x3c,0x41,0x46,0x4a,0x4a, + 0x47,0x46,0x44,0x45,0x4a,0x51,0x53,0x4f,0x51,0x4f,0x4f,0x4f,0x52,0x50,0x4d,0x47, + 0x6c,0x75,0x7e,0x8b,0x9d,0xab,0xaa,0xa9,0xa7,0xa6,0xa7,0xa9,0xac,0xa8,0xa4,0x9a, + 0x8a,0x74,0x5d,0x4a,0x43,0x4b,0x6c,0x9c,0xc7,0xd6,0xc9,0xa6,0x79,0x3a,0x21,0x22, + 0x1f,0x1d,0x1b,0x1a,0x22,0x40,0x5d,0x74,0x82,0x85,0x83,0x84,0x8c,0x99,0x9b,0x9b, + 0x95,0x8d,0x8d,0x8f,0x93,0x97,0x9b,0x9c,0x9f,0x9f,0xa3,0x9c,0x8b,0x74,0x57,0x35, + 0x73,0x82,0x3d,0x7b,0xcd,0xcc,0xcd,0xcf,0xce,0xce,0xcf,0xce,0xc9,0x91,0x92,0x9c, + 0xa7,0xac,0xb2,0xb1,0xbb,0xbb,0xbe,0xc1,0xc2,0xc3,0xcf,0xd1,0xd1,0xd0,0xcf,0xda, + 0x54,0x37,0x73,0x52,0x3c,0x3d,0x3b,0x38,0x37,0x35,0x35,0x36,0x3d,0x47,0x4a,0x4a, + 0x48,0x44,0x41,0x44,0x48,0x4f,0x54,0x54,0x51,0x51,0x52,0x53,0x52,0x56,0x53,0x4c, + 0x72,0x75,0x7e,0x87,0xa2,0xb4,0xb4,0xb0,0xa5,0xa2,0xa4,0xa4,0xa6,0xa5,0xa1,0x98, + 0x8c,0x7b,0x67,0x52,0x4b,0x53,0x76,0xaf,0xcd,0xd5,0xc2,0xa4,0x68,0x23,0x1a,0x1b, + 0x1a,0x1a,0x1a,0x19,0x1f,0x3d,0x5c,0x74,0x83,0x84,0x83,0x82,0x88,0x93,0x97,0x97, + 0x92,0x8d,0x8b,0x8f,0x93,0x98,0x9c,0xa0,0xa5,0xa9,0xae,0xad,0x9f,0x8b,0x6c,0x48, + 0x75,0x7f,0x3b,0x82,0xcd,0xcc,0xcc,0xcc,0xcc,0xcd,0xcd,0xcd,0xb8,0x1b,0x12,0x16, + 0x18,0x17,0x16,0x1b,0x1a,0x1e,0x20,0x21,0x21,0x1b,0x9e,0xd2,0xd0,0xcf,0xcf,0xd9, + 0x53,0x37,0x71,0x4d,0x38,0x3c,0x3a,0x36,0x34,0x32,0x32,0x35,0x3c,0x46,0x4a,0x4a, + 0x46,0x41,0x3f,0x41,0x46,0x4e,0x54,0x55,0x52,0x53,0x55,0x56,0x55,0x57,0x54,0x4e, + 0x75,0x79,0x7c,0x84,0xa0,0xb6,0xb9,0xb6,0xa6,0xa1,0xa2,0xa1,0xa4,0xa4,0x9e,0x96, + 0x83,0x6e,0x5b,0x4b,0x4b,0x53,0x76,0xa1,0xbd,0xc0,0xb1,0x8a,0x42,0x1b,0x1a,0x1b, + 0x1a,0x19,0x19,0x19,0x1d,0x38,0x58,0x72,0x82,0x86,0x85,0x83,0x85,0x89,0x8d,0x8e, + 0x8c,0x8b,0x8c,0x8e,0x92,0x97,0x9c,0xa3,0xa7,0xab,0xb0,0xb2,0xaa,0x99,0x7c,0x55, + 0x77,0x7e,0x3b,0x86,0xcf,0xcc,0xcd,0xcb,0xcc,0xcd,0xce,0xce,0xca,0x97,0x89,0x4e, + 0x12,0x16,0x2f,0x5e,0x52,0x4c,0x44,0x41,0x3b,0x2e,0xa9,0xd1,0xd0,0xcf,0xce,0xd9, + 0x53,0x37,0x6f,0x4a,0x36,0x3a,0x36,0x35,0x34,0x34,0x36,0x3b,0x41,0x46,0x49,0x49, + 0x42,0x3e,0x3f,0x41,0x46,0x4e,0x57,0x56,0x52,0x53,0x55,0x53,0x57,0x58,0x55,0x51, + 0x76,0x7a,0x7e,0x82,0x98,0xb0,0xb4,0xb5,0xa9,0xa4,0xa4,0xa3,0xa2,0xa2,0x9c,0x91, + 0x7c,0x64,0x55,0x4b,0x4b,0x4d,0x67,0x8d,0xa4,0xa9,0x94,0x5e,0x2c,0x26,0x29,0x27, + 0x25,0x22,0x20,0x1f,0x1d,0x2f,0x54,0x6f,0x80,0x86,0x87,0x86,0x86,0x86,0x86,0x87, + 0x88,0x89,0x8d,0x8d,0x91,0x96,0x9d,0xa2,0xa7,0xab,0xae,0xb2,0xae,0xa0,0x85,0x62, + 0x78,0x7a,0x3c,0x88,0xcf,0xcc,0xcd,0xcd,0xce,0xcd,0xcd,0xcd,0xce,0xd1,0xcb,0xcd, + 0x83,0x13,0x15,0x58,0xce,0xce,0xce,0xca,0xcc,0xcb,0xcc,0xd2,0xcf,0xcf,0xcf,0xd9, + 0x54,0x36,0x71,0x4b,0x35,0x36,0x36,0x38,0x38,0x3a,0x3e,0x44,0x49,0x4d,0x4b,0x47, + 0x43,0x41,0x41,0x41,0x47,0x4e,0x54,0x52,0x50,0x50,0x4e,0x4c,0x4f,0x4f,0x4e,0x4d, + 0x7b,0x7f,0x82,0x86,0x8e,0xa2,0xab,0xb1,0xaa,0xa7,0xa5,0xa3,0xa1,0xa1,0x9a,0x8e, + 0x7a,0x63,0x4f,0x4b,0x48,0x4a,0x56,0x71,0x81,0x7c,0x67,0x4e,0x49,0x54,0x61,0x5f, + 0x5d,0x59,0x51,0x47,0x36,0x3a,0x53,0x6f,0x81,0x8a,0x8b,0x89,0x88,0x85,0x85,0x86, + 0x87,0x8a,0x8d,0x8e,0x91,0x97,0x9c,0xa1,0xa6,0xaa,0xae,0xb1,0xaf,0xa6,0x90,0x74, + 0x7d,0x77,0x3a,0x89,0xcd,0xcc,0xcd,0xcd,0xcd,0xcb,0xcd,0xcd,0xcd,0xcf,0xd6,0x9a, + 0x50,0x16,0x0f,0x12,0x45,0xbe,0xcf,0xce,0xce,0xce,0xce,0xcf,0xcf,0xcf,0xcf,0xd9, + 0x54,0x34,0x73,0x49,0x2f,0x32,0x35,0x3d,0x41,0x45,0x49,0x4d,0x4c,0x50,0x4e,0x4b, + 0x48,0x46,0x46,0x44,0x49,0x50,0x53,0x52,0x4f,0x4c,0x49,0x47,0x48,0x46,0x44,0x46, + 0x7f,0x82,0x86,0x88,0x86,0x97,0xa4,0xa9,0xa9,0xa7,0xa3,0xa2,0xa2,0xa1,0x99,0x92, + 0x7c,0x61,0x46,0x3c,0x40,0x42,0x47,0x4b,0x57,0x5c,0x4e,0x50,0x5d,0x69,0x75,0x78, + 0x79,0x73,0x6d,0x62,0x56,0x55,0x61,0x75,0x82,0x8b,0x8c,0x8a,0x89,0x88,0x89,0x87, + 0x88,0x8a,0x8e,0x8f,0x92,0x97,0x9b,0xa1,0xa3,0xaa,0xb2,0xb6,0xb6,0xab,0x98,0x81, + 0x7e,0x76,0x39,0x8a,0xce,0xcc,0xcc,0xcc,0xcd,0xca,0xce,0xcd,0xcd,0x9c,0x23,0x16, + 0x17,0x19,0x81,0x28,0x18,0x1d,0xb6,0xd0,0xcf,0xce,0xce,0xcf,0xcf,0xce,0xcf,0xd7, + 0x53,0x33,0x70,0x47,0x2a,0x31,0x39,0x3e,0x45,0x49,0x4d,0x52,0x51,0x52,0x4e,0x4d, + 0x4b,0x48,0x4a,0x4a,0x4e,0x50,0x51,0x51,0x4d,0x48,0x46,0x44,0x43,0x40,0x3f,0x40, + 0x81,0x84,0x87,0x8c,0x87,0x8e,0x97,0xa0,0xa4,0xa3,0xa3,0xa1,0xa2,0xa0,0x9c,0x95, + 0x85,0x6c,0x4a,0x35,0x34,0x35,0x3e,0x46,0x51,0x54,0x4d,0x58,0x6a,0x7c,0x8a,0x8d, + 0x8e,0x8a,0x87,0x7f,0x77,0x7f,0x8a,0x92,0x93,0x91,0x8f,0x8e,0x8d,0x8c,0x8b,0x8b, + 0x8c,0x8d,0x90,0x91,0x93,0x96,0x9b,0xa1,0xa6,0xaf,0xb7,0xbd,0xbf,0xbb,0xa6,0x91, + 0x80,0x72,0x3a,0x8d,0xce,0xcc,0xcc,0xcc,0xcd,0xce,0xcd,0xcd,0xc1,0x1f,0x11,0x3f, + 0x92,0xca,0xd3,0xcb,0x62,0x07,0x7f,0xd2,0xce,0xce,0xce,0xce,0xcf,0xce,0xce,0xd8, + 0x53,0x37,0x6d,0x48,0x2f,0x37,0x3c,0x41,0x48,0x4f,0x51,0x56,0x56,0x53,0x53,0x51, + 0x4f,0x4f,0x52,0x55,0x54,0x56,0x55,0x4f,0x4d,0x48,0x46,0x41,0x3b,0x3a,0x38,0x35, + 0x82,0x83,0x88,0x8c,0x8d,0x9f,0xa7,0xa5,0xa4,0xa5,0xa1,0xa0,0xa0,0x9f,0x9d,0x98, + 0x8b,0x73,0x57,0x2a,0x18,0x1c,0x2b,0x40,0x44,0x44,0x42,0x4c,0x63,0x76,0x85,0x89, + 0x89,0x88,0x89,0x86,0x93,0x9e,0xa7,0xaf,0xaf,0xa5,0x9c,0x93,0x90,0x8d,0x8e,0x8e, + 0x8f,0x90,0x91,0x91,0x93,0x96,0x9d,0xa1,0xa6,0xb2,0xbc,0xc4,0xc7,0xc3,0xb7,0xa1, + 0x83,0x72,0x38,0x91,0xce,0xcb,0xcb,0xcc,0xcc,0xcd,0xcb,0xcc,0xbe,0x3c,0xa4,0xd5, + 0xcd,0xce,0xcc,0xce,0xd3,0x91,0x86,0xd2,0xce,0xcf,0xcf,0xcd,0xce,0xcd,0xce,0xd8, + 0x53,0x34,0x6a,0x4a,0x35,0x3c,0x3f,0x46,0x4e,0x50,0x51,0x54,0x53,0x55,0x56,0x57, + 0x56,0x58,0x58,0x57,0x56,0x59,0x56,0x51,0x4e,0x49,0x47,0x3e,0x3a,0x38,0x35,0x33, + 0x83,0x82,0x82,0x8b,0x9c,0xb7,0xb8,0xb1,0xa5,0xa3,0xa2,0xa0,0x9f,0x9f,0x9c,0x9a, + 0x91,0x7e,0x62,0x48,0x20,0x1f,0x30,0x37,0x39,0x36,0x34,0x3f,0x4e,0x65,0x76,0x7b, + 0x7a,0x7c,0x7a,0x7f,0x95,0xad,0xb9,0xbf,0xc1,0xba,0xb2,0xae,0xaa,0xa2,0x96,0x91, + 0x93,0x93,0x93,0x94,0x94,0x98,0x9d,0xa1,0xa5,0xb5,0xc3,0xcc,0xd1,0xcc,0xc4,0xb8, + 0x85,0x6e,0x39,0x93,0xcd,0xcb,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xce,0xda,0xdf,0xdc, + 0xdc,0xd6,0xd5,0xd3,0xd0,0xd1,0xc6,0xcd,0xcd,0xcd,0xcc,0xce,0xce,0xce,0xce,0xd8, + 0x53,0x36,0x69,0x4c,0x36,0x3d,0x42,0x47,0x4d,0x4d,0x4f,0x53,0x55,0x54,0x58,0x5d, + 0x5e,0x5e,0x5e,0x5d,0x5c,0x59,0x58,0x52,0x51,0x4f,0x4b,0x45,0x3d,0x39,0x35,0x32, + 0x84,0x80,0x7e,0x86,0xa5,0xc4,0xc5,0xbe,0xaa,0xa2,0xa2,0xa2,0x9f,0x9c,0x9b,0x93, + 0x91,0x83,0x6a,0x59,0x50,0x3b,0x34,0x36,0x34,0x2f,0x27,0x2d,0x40,0x5b,0x6b,0x74, + 0x76,0x76,0x79,0x88,0x9e,0xb8,0xca,0xd1,0xd4,0xd0,0xd1,0xcf,0xc8,0xb8,0xa8,0x99, + 0x92,0x95,0x95,0x95,0x94,0x95,0x9a,0x9e,0xa0,0xb4,0xc8,0xd3,0xd9,0xd5,0xcb,0xc5, + 0x84,0x6d,0x39,0x92,0xcc,0xca,0xca,0xca,0xcb,0xcb,0xcc,0xca,0xbd,0x27,0x20,0x2a, + 0x37,0x47,0x56,0x5f,0x70,0x7a,0xb1,0xd0,0xac,0xa1,0xc9,0xcc,0xcd,0xcd,0xcd,0xd8, + 0x53,0x36,0x6a,0x4f,0x37,0x3c,0x3f,0x42,0x45,0x47,0x4d,0x53,0x55,0x55,0x59,0x5e, + 0x5f,0x5f,0x60,0x61,0x5f,0x5b,0x58,0x55,0x54,0x50,0x4e,0x47,0x41,0x3a,0x36,0x33, + 0x86,0x83,0x80,0x84,0xa0,0xcc,0xcd,0xc5,0xaa,0xa1,0xa3,0xa2,0x9e,0x9b,0x98,0x90, + 0x7d,0x7b,0x70,0x67,0x66,0x5e,0x45,0x3a,0x32,0x27,0x1f,0x1f,0x31,0x4a,0x5d,0x6b, + 0x71,0x79,0x8f,0xab,0xc2,0xd1,0xdd,0xe1,0xe2,0xe1,0xe2,0xe4,0xdc,0xd2,0xbc,0xa1, + 0x8f,0x8e,0x8e,0x8b,0x87,0x8f,0x97,0x97,0x9b,0xb1,0xc5,0xd2,0xdc,0xd9,0xd1,0xd1, + 0x86,0x6a,0x38,0x91,0xcb,0xc9,0xca,0xca,0xcb,0xcb,0xcb,0xca,0xbf,0x25,0x15,0x0e, + 0x09,0x06,0x08,0x0a,0x08,0x06,0x80,0xd6,0x51,0x06,0xa9,0xcf,0xcd,0xcd,0xcd,0xd6, + 0x54,0x34,0x6b,0x52,0x37,0x3b,0x3b,0x3c,0x3f,0x41,0x48,0x4f,0x52,0x55,0x5a,0x5e, + 0x5d,0x5e,0x62,0x60,0x5f,0x5c,0x58,0x54,0x53,0x4e,0x4b,0x44,0x40,0x39,0x38,0x36, + 0x88,0x87,0x8a,0x87,0x9c,0xc5,0xc9,0xc1,0xa4,0x9e,0xa3,0xa2,0x9e,0x9c,0x96,0x8d, + 0x78,0x62,0x70,0x73,0x6f,0x72,0x69,0x48,0x2a,0x1d,0x1c,0x1c,0x20,0x38,0x49,0x5a, + 0x64,0x73,0xa2,0xc6,0xda,0xe5,0xe8,0xea,0xea,0xe9,0xe9,0xeb,0xea,0xdf,0xc7,0xa7, + 0x84,0x7f,0x82,0x82,0x82,0x89,0x8d,0x8c,0x94,0xad,0xc3,0xd2,0xda,0xdb,0xd6,0xd8, + 0x89,0x66,0x39,0x92,0xca,0xc9,0xcb,0xcc,0xcc,0xcc,0xcb,0xcb,0xcd,0xdd,0xdb,0xd6, + 0xce,0xc8,0xbb,0xae,0xa5,0x9a,0xb4,0xd0,0x95,0x69,0xbb,0xce,0xcd,0xcd,0xcd,0xd6, + 0x55,0x37,0x68,0x50,0x33,0x32,0x32,0x35,0x38,0x3f,0x43,0x44,0x4a,0x51,0x55,0x58, + 0x58,0x57,0x5b,0x5b,0x5b,0x5c,0x59,0x55,0x51,0x4c,0x47,0x42,0x40,0x39,0x38,0x39, + 0x86,0x8a,0x8e,0x8a,0x92,0xb1,0xb8,0xb6,0xa4,0x9f,0xa3,0xa2,0x9d,0x9b,0x94,0x8c, + 0x7d,0x66,0x59,0x66,0x77,0x79,0x6e,0x61,0x3f,0x21,0x1d,0x18,0x19,0x23,0x33,0x45, + 0x54,0x6d,0xae,0xd4,0xe8,0xf1,0xf0,0xef,0xee,0xec,0xec,0xee,0xec,0xe1,0xcb,0xaa, + 0x86,0x7a,0x7d,0x7f,0x82,0x83,0x82,0x82,0x90,0xa9,0xc0,0xd0,0xd7,0xda,0xd9,0xdd, + 0x8e,0x62,0x39,0x97,0xcb,0xc9,0xca,0xca,0xca,0xca,0xcb,0xcb,0xc0,0x7a,0x84,0x96, + 0xa6,0xb3,0xc0,0xc8,0xcf,0xd1,0xd0,0xcd,0xcb,0xcd,0xcb,0xcd,0xcd,0xcd,0xcd,0xd4, + 0x56,0x38,0x67,0x4f,0x2d,0x2b,0x2d,0x2a,0x2f,0x37,0x3a,0x3d,0x42,0x47,0x4c,0x4d, + 0x4e,0x4e,0x54,0x55,0x5b,0x59,0x57,0x51,0x4d,0x49,0x41,0x40,0x3e,0x38,0x39,0x3d, + 0x84,0x8a,0x8d,0x88,0x85,0x9b,0xa8,0xab,0x9f,0x9f,0xa1,0xa0,0x9c,0x98,0x92,0x89, + 0x7f,0x6d,0x5b,0x50,0x65,0x77,0x70,0x64,0x5a,0x3c,0x1b,0x18,0x1a,0x24,0x33,0x41, + 0x4a,0x63,0xb1,0xdb,0xeb,0xf3,0xf3,0xf0,0xec,0xea,0xea,0xed,0xec,0xe1,0xcd,0xac, + 0x85,0x78,0x7d,0x81,0x84,0x82,0x7d,0x7a,0x8b,0xa5,0xbc,0xcb,0xd4,0xd7,0xd7,0xe0, + 0x90,0x5c,0x38,0x98,0xc9,0xc9,0xca,0xca,0xcb,0xcc,0xca,0xc9,0xb9,0x11,0x04,0x04, + 0x04,0x06,0x07,0x0c,0x0d,0x13,0x85,0xcf,0xcc,0xcc,0xce,0xcc,0xcd,0xcc,0xcd,0xd5, + 0x55,0x37,0x64,0x4f,0x27,0x27,0x23,0x22,0x26,0x2e,0x34,0x38,0x3c,0x3e,0x40,0x40, + 0x43,0x44,0x4d,0x52,0x56,0x53,0x50,0x4b,0x48,0x41,0x3c,0x3b,0x3b,0x3a,0x39,0x3f, + 0x88,0x8c,0x8e,0x8a,0x80,0x86,0x8f,0x9c,0x9e,0xa3,0xa5,0xa4,0x9e,0x9a,0x94,0x8b, + 0x82,0x70,0x5b,0x4a,0x41,0x57,0x6f,0x6f,0x61,0x55,0x3c,0x1d,0x26,0x35,0x3c,0x41, + 0x4c,0x69,0xb1,0xdb,0xec,0xf2,0xf1,0xee,0xec,0xe9,0xea,0xed,0xec,0xe1,0xd0,0xae, + 0x8b,0x85,0x8a,0x8e,0x8f,0x88,0x7f,0x79,0x88,0xa2,0xb8,0xc7,0xd0,0xd3,0xd4,0xe0, + 0x94,0x5c,0x37,0x9b,0xca,0xc9,0xca,0xca,0xcb,0xc9,0xcb,0xc9,0xc7,0xa6,0x94,0x82, + 0x74,0x66,0x54,0x30,0x1c,0x16,0x8c,0xcc,0xcb,0xcd,0xcc,0xcc,0xcb,0xcc,0xcc,0xd5, + 0x55,0x36,0x63,0x51,0x26,0x24,0x1f,0x20,0x24,0x27,0x2d,0x33,0x38,0x36,0x35,0x35, + 0x37,0x3f,0x4a,0x4e,0x4f,0x4d,0x48,0x43,0x40,0x3e,0x39,0x39,0x3a,0x3b,0x3f,0x46, + 0x8c,0x8e,0x8e,0x8e,0x84,0x8e,0x97,0x9b,0x9e,0xa4,0xa7,0xa6,0xa0,0x99,0x94,0x8f, + 0x84,0x6d,0x52,0x3c,0x31,0x34,0x4e,0x6b,0x6c,0x60,0x51,0x45,0x3c,0x4c,0x58,0x5d, + 0x60,0x7c,0xb8,0xdc,0xee,0xf2,0xf2,0xed,0xeb,0xe9,0xea,0xee,0xec,0xe0,0xcc,0xac, + 0x90,0x8e,0x90,0x8e,0x8d,0x84,0x79,0x76,0x83,0x9c,0xb3,0xc1,0xca,0xcf,0xd3,0xe0, + 0x93,0x5c,0x38,0x9e,0xcb,0xc9,0xca,0xca,0xca,0xca,0xca,0xcb,0xcb,0xcd,0xca,0xd0, + 0xd0,0xd3,0xd4,0xd1,0x66,0x17,0xa6,0xcd,0xcb,0xcc,0xcb,0xcd,0xcb,0xcc,0xcd,0xd5, + 0x56,0x35,0x63,0x53,0x26,0x21,0x20,0x23,0x24,0x26,0x2b,0x31,0x35,0x31,0x2f,0x2e, + 0x2f,0x38,0x41,0x45,0x46,0x45,0x41,0x3b,0x38,0x38,0x34,0x34,0x34,0x39,0x3f,0x4a, + 0x92,0x91,0x93,0x8f,0x90,0xa5,0xaa,0xa6,0x9f,0xa2,0xa5,0xa8,0xa3,0x9c,0x98,0x91, + 0x83,0x66,0x47,0x2d,0x23,0x28,0x2c,0x49,0x6b,0x67,0x5b,0x5b,0x66,0x73,0x7a,0x7a, + 0x6c,0x7e,0xbc,0xde,0xec,0xef,0xed,0xec,0xeb,0xeb,0xed,0xf1,0xe9,0xde,0xc6,0xa4, + 0x8c,0x8e,0x8f,0x8c,0x87,0x7e,0x75,0x71,0x82,0x99,0xad,0xbd,0xc6,0xce,0xd3,0xdd, + 0x91,0x5a,0x38,0xa0,0xcb,0xc9,0xca,0xca,0xca,0xca,0xca,0xca,0xca,0xc9,0xcd,0xcc, + 0xcc,0xcb,0xca,0xca,0x9a,0x0b,0x5a,0xd5,0xcb,0xcc,0xcb,0xcc,0xcb,0xcc,0xce,0xd5, + 0x56,0x34,0x67,0x54,0x29,0x23,0x20,0x24,0x26,0x27,0x2c,0x31,0x33,0x31,0x2f,0x2b, + 0x2c,0x33,0x39,0x3a,0x3c,0x38,0x33,0x2e,0x2d,0x2f,0x2f,0x2d,0x2d,0x37,0x3f,0x4b, + 0x93,0x91,0x90,0x8d,0x96,0xbb,0xc0,0xb8,0xa6,0xa0,0xa7,0xa9,0xa4,0x9e,0x9a,0x92, + 0x7f,0x63,0x3d,0x20,0x17,0x1a,0x24,0x30,0x50,0x69,0x69,0x71,0x7d,0x84,0x83,0x7b, + 0x71,0x7f,0xbd,0xda,0xe4,0xe6,0xe4,0xe1,0xe3,0xe5,0xea,0xee,0xe7,0xdb,0xbf,0x9b, + 0x8b,0x8e,0x8c,0x87,0x83,0x7a,0x71,0x6e,0x81,0x9a,0xac,0xbd,0xc9,0xcf,0xd1,0xd5, + 0x8a,0x56,0x38,0xa0,0xc9,0xc8,0xc9,0xc9,0xc9,0xc9,0xc8,0xc8,0xc1,0x68,0x6b,0x77, + 0x89,0x95,0xa4,0x98,0x30,0x11,0x75,0xd1,0xcb,0xcc,0xcb,0xca,0xcb,0xcb,0xcc,0xd5, + 0x56,0x35,0x66,0x57,0x2a,0x28,0x25,0x26,0x27,0x29,0x2c,0x30,0x32,0x2f,0x2b,0x29, + 0x2a,0x2e,0x2f,0x2f,0x2f,0x29,0x24,0x23,0x26,0x29,0x2a,0x29,0x2a,0x32,0x3d,0x45, + 0x95,0x90,0x8f,0x8c,0x99,0xc6,0xce,0xc7,0xab,0x9a,0xa4,0xa5,0xa2,0x9c,0x98,0x91, + 0x7f,0x61,0x3a,0x1a,0x10,0x13,0x1c,0x2d,0x40,0x5b,0x6b,0x76,0x86,0x8f,0x8d,0x7c, + 0x6f,0x86,0xbc,0xd4,0xe1,0xdc,0xcf,0xc7,0xc7,0xd2,0xe3,0xe8,0xe6,0xd8,0xba,0x94, + 0x8a,0x88,0x89,0x83,0x7f,0x7a,0x72,0x71,0x89,0xa2,0xb4,0xc0,0xcb,0xce,0xc8,0xc8, + 0x89,0x53,0x37,0xa4,0xc8,0xc9,0xc8,0xc9,0xc9,0xc9,0xca,0xc8,0xba,0x1b,0x0f,0x0c, + 0x09,0x08,0x06,0x0d,0x0f,0x31,0xc0,0xcb,0xcc,0xcb,0xcb,0xcc,0xcb,0xcb,0xcb,0xd5, + 0x57,0x37,0x64,0x54,0x2d,0x2e,0x2b,0x29,0x2b,0x2b,0x2e,0x2f,0x2f,0x2c,0x29,0x25, + 0x23,0x26,0x29,0x28,0x23,0x1d,0x1d,0x1f,0x1f,0x21,0x22,0x23,0x28,0x30,0x3c,0x45, + 0x92,0x93,0x91,0x8e,0x97,0xc3,0xcd,0xc5,0xad,0x98,0x9d,0xa0,0x9e,0x98,0x94,0x91, + 0x80,0x60,0x3b,0x1b,0x10,0x0e,0x14,0x26,0x3d,0x4d,0x5a,0x75,0x7b,0x7e,0x83,0x6b, + 0x51,0x79,0xad,0xc0,0xc8,0xc3,0xa6,0x8f,0x96,0xb5,0xd6,0xe5,0xe4,0xd8,0xbb,0x94, + 0x87,0x86,0x82,0x82,0x7f,0x79,0x71,0x7e,0x95,0xab,0xbc,0xc7,0xca,0xc4,0xba,0xb3, + 0x88,0x4f,0x36,0xa6,0xc9,0xc8,0xc9,0xc8,0xc9,0xc9,0xc9,0xca,0xc4,0xb1,0xa8,0x9d, + 0x90,0x83,0x71,0x67,0x91,0xc5,0xca,0xca,0xca,0xca,0xcb,0xcb,0xcb,0xcb,0xcb,0xd5, + 0x56,0x38,0x61,0x56,0x2c,0x2f,0x2e,0x2d,0x2e,0x2d,0x2f,0x31,0x33,0x2e,0x28,0x23, + 0x1e,0x1b,0x1c,0x1b,0x18,0x12,0x15,0x19,0x19,0x18,0x1a,0x1d,0x21,0x2a,0x38,0x40, + 0x94,0x92,0x92,0x8e,0x91,0xb1,0xbb,0xb8,0xa4,0x93,0x96,0x99,0x9a,0x93,0x92,0x8f, + 0x7e,0x5f,0x3c,0x1e,0x12,0x11,0x16,0x27,0x39,0x45,0x47,0x5d,0x64,0x5d,0x5f,0x50, + 0x3a,0x57,0x93,0xa8,0xaa,0x9b,0x71,0x51,0x54,0x95,0xc9,0xde,0xde,0xd3,0xb8,0x9e, + 0x8d,0x88,0x84,0x83,0x81,0x7c,0x7e,0x93,0xa3,0xb3,0xc2,0xc8,0xc2,0xb8,0xa7,0x97, + 0x87,0x4e,0x33,0xa8,0xc8,0xc8,0xc9,0xc8,0xc7,0xc7,0xc8,0xd0,0xc8,0xc9,0xca,0xd0, + 0xd2,0xd1,0xd5,0xd0,0xcc,0xc9,0xcb,0xca,0xc9,0xca,0xca,0xcb,0xcb,0xcb,0xcb,0xd5, + 0x56,0x36,0x5e,0x58,0x2c,0x30,0x31,0x32,0x32,0x32,0x33,0x34,0x35,0x30,0x2a,0x24, + 0x1c,0x17,0x14,0x13,0x13,0x10,0x13,0x15,0x16,0x15,0x1a,0x20,0x20,0x2a,0x32,0x3b, + 0x96,0x91,0x90,0x8c,0x89,0x94,0xa6,0xa5,0x96,0x8d,0x90,0x95,0x98,0x94,0x91,0x8c, + 0x7b,0x60,0x40,0x24,0x15,0x18,0x1f,0x28,0x31,0x3b,0x34,0x36,0x42,0x46,0x46,0x3f, + 0x37,0x34,0x57,0x79,0x83,0x75,0x56,0x41,0x4d,0x90,0xb9,0xcf,0xd8,0xcd,0xb8,0x9f, + 0x8d,0x8a,0x87,0x86,0x85,0x87,0x94,0xa2,0xac,0xb9,0xc0,0xc0,0xb4,0xa4,0x8f,0x79, + 0x8d,0x4b,0x34,0xa9,0xc8,0xc8,0xc8,0xc7,0xc8,0xce,0xad,0x37,0xda,0xc9,0xa4,0x4e, + 0x4e,0x5e,0x6c,0x7e,0xbe,0xcc,0xca,0xc9,0xc9,0xc9,0xc9,0xca,0xca,0xca,0xca,0xd4, + 0x56,0x35,0x64,0x55,0x2b,0x2f,0x34,0x38,0x37,0x34,0x33,0x36,0x37,0x35,0x2c,0x24, + 0x1d,0x16,0x12,0x10,0x11,0x0f,0x11,0x12,0x14,0x15,0x1a,0x23,0x27,0x2c,0x31,0x37, + 0x9b,0x93,0x8d,0x8c,0x89,0x89,0x91,0x94,0x8d,0x8d,0x91,0x99,0x9d,0x9f,0x99,0x91, + 0x7b,0x5e,0x3c,0x21,0x1a,0x21,0x2c,0x34,0x34,0x30,0x27,0x1d,0x24,0x31,0x33,0x3a, + 0x39,0x35,0x2f,0x34,0x3a,0x35,0x3d,0x48,0x5e,0x8e,0xad,0xc0,0xc6,0xbc,0xa7,0x91, + 0x8b,0x8a,0x89,0x88,0x8d,0x96,0xa2,0xac,0xb4,0xb6,0xb6,0xad,0x9d,0x87,0x6e,0x57, + 0x91,0x4a,0x34,0xab,0xc7,0xc8,0xc7,0xc8,0xc7,0x7e,0x0f,0x12,0xd6,0x5d,0x0c,0x0b, + 0x0a,0x06,0x03,0x08,0x0d,0x5e,0xc7,0xc9,0xc9,0xc9,0xc9,0xca,0xca,0xca,0xca,0xd3, + 0x55,0x34,0x64,0x53,0x27,0x2e,0x32,0x37,0x37,0x36,0x37,0x38,0x36,0x32,0x2a,0x24, + 0x1c,0x14,0x11,0x12,0x10,0x11,0x10,0x10,0x11,0x14,0x1b,0x24,0x2d,0x32,0x34,0x35, + 0x9d,0x94,0x8c,0x8d,0x8d,0x97,0xa0,0x9d,0x94,0x90,0x93,0x9d,0xa4,0xa3,0x9f,0x94, + 0x7b,0x5d,0x37,0x21,0x22,0x2f,0x3d,0x41,0x3e,0x30,0x21,0x1c,0x22,0x27,0x33,0x4e, + 0x52,0x48,0x3e,0x2c,0x30,0x55,0x67,0x6c,0x6b,0x7b,0x8f,0x9b,0xa3,0x97,0x89,0x86, + 0x8a,0x8a,0x88,0x8d,0x96,0x9d,0xa6,0xaf,0xb0,0xae,0xa6,0x94,0x7c,0x66,0x4c,0x46, + 0x92,0x47,0x31,0xad,0xc8,0xc6,0xc7,0xc8,0xc9,0x29,0x15,0x98,0xaf,0x1a,0x18,0x8b, + 0xbf,0xb2,0xa4,0x81,0x1b,0x15,0x9a,0xca,0xc8,0xca,0xc9,0xc8,0xca,0xca,0xcb,0xd3, + 0x53,0x36,0x65,0x51,0x26,0x2d,0x32,0x35,0x36,0x39,0x36,0x33,0x31,0x2c,0x28,0x21, + 0x1c,0x13,0x10,0x10,0x10,0x10,0x0f,0x0f,0x12,0x13,0x1c,0x23,0x2d,0x33,0x37,0x35, + 0x98,0x97,0x8e,0x90,0x98,0xb2,0xbb,0xb2,0x9d,0x92,0x97,0xa0,0xa8,0xa9,0xa6,0x99, + 0x79,0x5a,0x36,0x23,0x28,0x39,0x46,0x47,0x42,0x32,0x21,0x1e,0x27,0x41,0x53,0x68, + 0x5b,0x56,0x59,0x43,0x52,0x5d,0x63,0x68,0x68,0x65,0x69,0x76,0x75,0x73,0x79,0x80, + 0x83,0x83,0x87,0x8c,0x96,0x9f,0xaa,0xad,0xa5,0x9c,0x8c,0x74,0x58,0x42,0x37,0x48, + 0x92,0x46,0x31,0xaf,0xc8,0xc7,0xc7,0xc8,0xc8,0x12,0x23,0xca,0xac,0x19,0x45,0xc9, + 0xc9,0xc8,0xc9,0xcf,0x90,0x0b,0x56,0xd1,0xc8,0xc8,0xc8,0xca,0xc9,0xca,0xc9,0xd3, + 0x54,0x31,0x61,0x54,0x25,0x2b,0x31,0x33,0x32,0x34,0x31,0x2e,0x2b,0x29,0x25,0x1e, + 0x1b,0x14,0x13,0x12,0x12,0x12,0x10,0x10,0x11,0x12,0x1a,0x23,0x2d,0x33,0x37,0x33, + 0x93,0x96,0x90,0x93,0xa0,0xc7,0xcf,0xc6,0xaf,0x92,0x98,0xa2,0xaa,0xaa,0xa6,0x98, + 0x79,0x56,0x33,0x20,0x2a,0x39,0x44,0x45,0x40,0x31,0x1f,0x22,0x36,0x5a,0x75,0x89, + 0x7e,0x66,0x52,0x44,0x4e,0x5a,0x60,0x65,0x65,0x56,0x55,0x56,0x5a,0x64,0x70,0x7b, + 0x7f,0x82,0x87,0x8d,0x9c,0xa8,0xa9,0xa3,0x98,0x88,0x70,0x53,0x3a,0x33,0x34,0x4e, + 0x94,0x43,0x32,0xaf,0xc5,0xc5,0xc6,0xc6,0xc6,0x32,0x17,0xa0,0xb8,0x29,0x34,0xbb, + 0xcb,0xca,0xcc,0xc7,0x8d,0x0d,0x82,0xcc,0xc7,0xc7,0xc8,0xc9,0xc9,0xc5,0xc9,0xd2, + 0x54,0x33,0x5e,0x50,0x21,0x28,0x2d,0x30,0x2e,0x2f,0x2c,0x2a,0x26,0x25,0x1f,0x19, + 0x16,0x14,0x14,0x13,0x13,0x13,0x11,0x11,0x11,0x13,0x1b,0x22,0x2a,0x30,0x32,0x2e, + 0x93,0x95,0x90,0x90,0x9f,0xcf,0xde,0xd3,0xb8,0x94,0x98,0xa3,0xa9,0xaa,0xa4,0x93, + 0x77,0x54,0x30,0x1e,0x22,0x30,0x3c,0x3d,0x34,0x2c,0x1e,0x20,0x3a,0x6a,0x7e,0x7d, + 0x7c,0x68,0x4d,0x3e,0x44,0x49,0x4a,0x4b,0x3b,0x3a,0x43,0x4e,0x5d,0x6d,0x7a,0x7f, + 0x81,0x86,0x90,0x98,0xa4,0xaa,0xa3,0x9e,0x96,0x83,0x63,0x4b,0x3b,0x37,0x36,0x53, + 0x94,0x44,0x31,0xb2,0xc5,0xc5,0xc6,0xc6,0xc6,0x8c,0x09,0x1b,0x31,0x20,0x13,0x2b, + 0x44,0x50,0x5b,0x56,0x22,0x22,0xaf,0xc9,0xc9,0xc8,0xc7,0xc7,0xc9,0xc7,0xc9,0xd3, + 0x56,0x33,0x5f,0x50,0x20,0x27,0x2e,0x30,0x2f,0x30,0x2c,0x27,0x24,0x21,0x1a,0x16, + 0x16,0x17,0x15,0x15,0x14,0x14,0x14,0x13,0x12,0x13,0x1a,0x21,0x29,0x2d,0x2e,0x2d, + 0x95,0x92,0x8e,0x8c,0x9f,0xc9,0xd6,0xd3,0xb9,0x99,0x9c,0xa4,0xa7,0xa5,0xa0,0x92, + 0x77,0x54,0x2f,0x1c,0x1d,0x24,0x2d,0x31,0x36,0x33,0x2c,0x29,0x34,0x65,0x83,0x7c, + 0x6b,0x54,0x3d,0x37,0x33,0x2c,0x22,0x1f,0x2e,0x3e,0x4d,0x5c,0x6d,0x79,0x7e,0x82, + 0x88,0x90,0x9b,0xa1,0xac,0xae,0xab,0xaa,0xa3,0x83,0x64,0x57,0x4c,0x3e,0x37,0x56, + 0x96,0x40,0x2f,0xb4,0xc5,0xc5,0xc6,0xc6,0xc6,0xcb,0x97,0x3e,0x27,0x22,0x1b,0x1b, + 0x17,0x12,0x11,0x10,0x0e,0x07,0x86,0xcc,0xc9,0xc8,0xc9,0xca,0xca,0xca,0xc9,0xd2, + 0x57,0x34,0x60,0x54,0x22,0x28,0x2f,0x32,0x32,0x31,0x2e,0x29,0x24,0x21,0x1b,0x17, + 0x16,0x15,0x16,0x14,0x15,0x15,0x14,0x14,0x13,0x15,0x1b,0x22,0x28,0x2b,0x2d,0x2c, + 0x98,0x92,0x8b,0x85,0x91,0xb4,0xc4,0xc2,0xaf,0x99,0x9f,0xa2,0xa6,0xa3,0x9e,0x93, + 0x7c,0x59,0x38,0x20,0x1b,0x1d,0x23,0x35,0x49,0x46,0x3c,0x32,0x31,0x4d,0x69,0x74, + 0x62,0x3d,0x27,0x22,0x1f,0x1f,0x21,0x2e,0x3e,0x4c,0x5d,0x6d,0x79,0x7c,0x82,0x87, + 0x91,0x9b,0xa5,0xaa,0xb3,0xb7,0xba,0xb6,0xa3,0x85,0x6d,0x65,0x59,0x44,0x40,0x5a, + 0x95,0x3f,0x2e,0xb5,0xc5,0xc5,0xc5,0xc5,0xc6,0xc6,0xc7,0xc5,0xc0,0xbe,0xbe,0xba, + 0xb9,0xb4,0xac,0xa3,0x97,0x8c,0xad,0xc9,0xc8,0xc7,0xc4,0xc9,0xca,0xca,0xca,0xd1, + 0x57,0x34,0x60,0x57,0x24,0x2a,0x2f,0x31,0x32,0x33,0x30,0x29,0x23,0x20,0x1b,0x17, + 0x14,0x13,0x13,0x14,0x15,0x15,0x15,0x14,0x15,0x17,0x1c,0x21,0x28,0x2c,0x2b,0x29, + 0x9b,0x92,0x8a,0x89,0x8c,0x9f,0xaa,0xab,0x9c,0x94,0x9e,0xa1,0xa5,0xa2,0x9d,0x92, + 0x7e,0x5f,0x3e,0x26,0x1d,0x1e,0x2c,0x40,0x4d,0x50,0x47,0x3c,0x3c,0x47,0x4e,0x4c, + 0x3f,0x27,0x1e,0x20,0x21,0x23,0x32,0x3e,0x4d,0x5d,0x6b,0x78,0x7d,0x81,0x85,0x8e, + 0x98,0xa1,0xab,0xb0,0xb8,0xbe,0xbc,0xb1,0xa3,0x8c,0x7b,0x6e,0x59,0x47,0x46,0x61, + 0x95,0x3d,0x2d,0xb6,0xc5,0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc4,0xc5,0xc5,0xc6, + 0xc5,0xc5,0xc7,0xc6,0xc8,0xc8,0xc8,0xc7,0xc7,0xc8,0xc7,0xca,0xc8,0xc9,0xc8,0xd1, + 0x57,0x35,0x5e,0x5c,0x28,0x2e,0x30,0x31,0x31,0x33,0x31,0x2a,0x24,0x20,0x1b,0x15, + 0x13,0x13,0x13,0x13,0x14,0x15,0x15,0x14,0x16,0x19,0x1c,0x1f,0x25,0x29,0x28,0x27, + 0x96,0x8c,0x87,0x88,0x8c,0x93,0x96,0x94,0x8e,0x8f,0x98,0xa1,0xa5,0xa3,0x9d,0x93, + 0x7e,0x63,0x42,0x2b,0x23,0x23,0x2f,0x3f,0x4c,0x48,0x48,0x3b,0x37,0x46,0x52,0x51, + 0x3d,0x26,0x1f,0x1f,0x21,0x32,0x3f,0x4c,0x5c,0x6d,0x77,0x7c,0x80,0x85,0x8d,0x95, + 0x9e,0xa6,0xae,0xb4,0xb8,0xba,0xb3,0xa8,0x9c,0x89,0x7a,0x5f,0x4b,0x47,0x47,0x5f, + 0x97,0x3a,0x2c,0xb6,0xc5,0xc4,0xc4,0xc4,0xc5,0xc5,0xc4,0xc5,0xc6,0xc5,0xc5,0xc5, + 0xc7,0xc6,0xc6,0xc5,0xc7,0xc8,0xc7,0xc8,0xc7,0xc8,0xc9,0xc8,0xc8,0xc8,0xc8,0xd1, + 0x56,0x34,0x5a,0x5d,0x2a,0x2f,0x30,0x31,0x30,0x2e,0x2e,0x29,0x22,0x1d,0x19,0x14, + 0x12,0x12,0x12,0x13,0x14,0x14,0x14,0x15,0x15,0x19,0x19,0x1d,0x21,0x24,0x25,0x23, + 0x94,0x8a,0x84,0x85,0x8f,0xa3,0xac,0xa3,0x92,0x8e,0x98,0xa0,0xa5,0xa5,0x9f,0x94, + 0x7e,0x61,0x45,0x30,0x27,0x27,0x2a,0x34,0x3d,0x3d,0x32,0x2b,0x37,0x4e,0x56,0x50, + 0x3c,0x2a,0x21,0x23,0x32,0x3e,0x4a,0x59,0x6b,0x76,0x78,0x7f,0x84,0x8a,0x94,0x9e, + 0xa4,0xab,0xaf,0xb3,0xb5,0xb2,0xaa,0x9f,0x8f,0x79,0x5e,0x47,0x42,0x42,0x41,0x5a, + 0x9b,0x3c,0x2e,0xb7,0xc4,0xc4,0xc5,0xc4,0xc5,0xc5,0xc5,0xc5,0xc5,0xc5,0xc5,0xc7, + 0xc7,0xc7,0xc7,0xc7,0xc7,0xc8,0xc8,0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xc8,0xd0, + 0x55,0x33,0x5b,0x60,0x2a,0x2d,0x2d,0x2d,0x2a,0x29,0x29,0x25,0x21,0x1b,0x18,0x13, + 0x11,0x11,0x12,0x13,0x14,0x14,0x14,0x15,0x16,0x17,0x1a,0x1b,0x1e,0x20,0x22,0x22, + 0x91,0x86,0x80,0x87,0x92,0xb0,0xbb,0xb3,0xa1,0x8e,0x96,0xa0,0xa7,0xa8,0x9f,0x94, + 0x7e,0x61,0x46,0x30,0x24,0x23,0x26,0x29,0x28,0x27,0x20,0x25,0x39,0x52,0x58,0x4e, + 0x3e,0x2e,0x28,0x35,0x3f,0x4a,0x59,0x67,0x74,0x7a,0x7e,0x83,0x87,0x8f,0x98,0xa2, + 0xa9,0xad,0xb0,0xb0,0xaf,0xad,0xa6,0x96,0x7c,0x60,0x4a,0x44,0x46,0x44,0x42,0x56, + 0x97,0x3c,0x2e,0xb9,0xc5,0xc4,0xc5,0xc4,0xc5,0xc5,0xc5,0xc5,0xc5,0xc5,0xc5,0xc4, + 0xc5,0xc7,0xc7,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xd0, + 0x55,0x31,0x5a,0x5e,0x2a,0x2c,0x2d,0x2d,0x2a,0x29,0x27,0x24,0x1f,0x1a,0x18,0x13, + 0x11,0x11,0x14,0x14,0x17,0x16,0x17,0x17,0x15,0x13,0x16,0x17,0x19,0x1b,0x20,0x21, + 0x8a,0x82,0x80,0x84,0x92,0xb6,0xc7,0xc2,0xb0,0x8e,0x94,0x9e,0xa8,0xa7,0x9e,0x92, + 0x7e,0x61,0x44,0x2f,0x23,0x20,0x22,0x23,0x1f,0x1b,0x1a,0x26,0x3d,0x50,0x55,0x4f, + 0x40,0x30,0x33,0x3f,0x4b,0x59,0x67,0x72,0x77,0x7d,0x82,0x84,0x84,0x8e,0x9d,0xa8, + 0xaf,0xaf,0xaf,0xaf,0xb0,0xae,0xa4,0x94,0x7a,0x57,0x4c,0x4d,0x4c,0x4b,0x4b,0x5b, + 0x97,0x3b,0x2f,0xba,0xc4,0xc4,0xc5,0xc4,0xc4,0xc5,0xc5,0xc5,0xc5,0xc6,0xc5,0xc7, + 0xc5,0xc4,0xc9,0xc7,0xc7,0xc6,0xc7,0xc6,0xc7,0xc7,0xc8,0xc7,0xc6,0xc7,0xc7,0xd0, + 0x57,0x32,0x5b,0x60,0x2a,0x2c,0x2c,0x2f,0x2a,0x28,0x27,0x23,0x1e,0x1b,0x1a,0x15, + 0x13,0x13,0x13,0x15,0x1b,0x1c,0x1c,0x1a,0x17,0x14,0x15,0x14,0x16,0x1c,0x20,0x21, + 0x81,0x82,0x83,0x8b,0x91,0xb3,0xcd,0xc5,0xb3,0x8e,0x92,0x9e,0xa4,0xa3,0x9a,0x8e, + 0x7a,0x62,0x44,0x2b,0x20,0x1a,0x1a,0x1b,0x1b,0x1a,0x1a,0x24,0x38,0x49,0x51,0x4b, + 0x3e,0x37,0x45,0x52,0x5c,0x67,0x73,0x78,0x7e,0x82,0x85,0x82,0x80,0x8b,0x9d,0xa7, + 0xaf,0xaf,0xaf,0xaf,0xb2,0xaf,0xa6,0x95,0x7f,0x6a,0x55,0x4e,0x4c,0x4b,0x49,0x59, + 0x97,0x39,0x30,0xbb,0xc4,0xc3,0xc4,0xc4,0xc4,0xc0,0xc4,0xc4,0xc4,0xc5,0xc5,0xc0, + 0xc6,0xbb,0x9b,0xc2,0xc5,0xc6,0xc6,0xc6,0xc7,0xc7,0xc8,0xc7,0xc7,0xc7,0xc7,0xcf, + 0x57,0x33,0x58,0x61,0x29,0x2c,0x2f,0x2f,0x2a,0x28,0x26,0x22,0x1e,0x1d,0x19,0x16, + 0x14,0x13,0x16,0x1d,0x23,0x24,0x24,0x1f,0x1d,0x18,0x16,0x16,0x18,0x1d,0x21,0x22, + 0x7d,0x80,0x82,0x84,0x8b,0xa8,0xbd,0xba,0xae,0x8e,0x90,0x9b,0xa3,0xa0,0x96,0x8b, + 0x7b,0x64,0x4c,0x2f,0x21,0x1a,0x18,0x18,0x19,0x18,0x18,0x1e,0x2f,0x41,0x46,0x46, + 0x43,0x44,0x50,0x5c,0x6a,0x76,0x78,0x7e,0x82,0x85,0x87,0x85,0x83,0x8d,0x9a,0xa4, + 0xad,0xaf,0xaf,0xb0,0xb3,0xb2,0xaa,0x9d,0x8c,0x79,0x6c,0x5d,0x5a,0x59,0x59,0x5b, + 0x95,0x39,0x31,0xc0,0xc2,0xc3,0xc4,0xc3,0xc5,0xc5,0xc5,0xc5,0xc5,0xc4,0xc5,0xc5, + 0x9b,0x26,0x17,0x7c,0xc8,0xc5,0xc5,0xc5,0xc6,0xc6,0xc8,0xc8,0xc4,0xc4,0xc8,0xcf, + 0x58,0x33,0x56,0x5c,0x25,0x2a,0x2c,0x2f,0x30,0x2c,0x29,0x26,0x20,0x1d,0x19,0x16, + 0x15,0x16,0x1b,0x21,0x26,0x28,0x27,0x22,0x21,0x1b,0x19,0x1a,0x1d,0x20,0x23,0x23, + 0x7c,0x7e,0x7d,0x82,0x84,0x94,0xa9,0xa7,0x9c,0x88,0x8f,0x98,0xa2,0x9f,0x97,0x8c, + 0x79,0x67,0x50,0x37,0x24,0x1a,0x18,0x18,0x18,0x17,0x16,0x18,0x26,0x35,0x3b,0x46, + 0x4b,0x50,0x5c,0x69,0x73,0x7e,0x7d,0x83,0x83,0x82,0x84,0x87,0x89,0x91,0x9e,0xa6, + 0xae,0xae,0xaf,0xae,0xaf,0xae,0xad,0xa5,0x93,0x84,0x78,0x71,0x61,0x5e,0x6b,0x66, + 0x95,0x3a,0x33,0xbf,0xc4,0xc3,0xc4,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc4,0xc5,0x8c, + 0x17,0x17,0x16,0x14,0x80,0xca,0xc6,0xc5,0xc6,0xc6,0xc7,0xc6,0xc6,0xc7,0xc7,0xcd, + 0x59,0x33,0x52,0x5c,0x24,0x29,0x2b,0x2f,0x30,0x2e,0x2b,0x28,0x25,0x20,0x1c,0x17, + 0x16,0x1a,0x1e,0x21,0x2a,0x2b,0x2b,0x28,0x25,0x22,0x1f,0x20,0x21,0x21,0x22,0x23, + 0x7f,0x7d,0x78,0x7a,0x80,0x81,0x8f,0x91,0x84,0x7d,0x88,0x97,0xa1,0x9f,0x99,0x8c, + 0x7c,0x6b,0x54,0x3b,0x29,0x1b,0x18,0x17,0x17,0x16,0x15,0x14,0x19,0x2a,0x3d,0x48, + 0x53,0x59,0x68,0x73,0x7a,0x82,0x83,0x87,0x88,0x87,0x87,0x87,0x8c,0x98,0xa7,0xaf, + 0xaf,0xb1,0xaf,0xad,0xac,0xa7,0xa4,0xa6,0x9d,0x8c,0x88,0x81,0x7b,0x81,0x8c,0x78, + 0x98,0x38,0x31,0xc0,0xc3,0xc3,0xc4,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc9,0x73,0x1c, + 0x16,0x16,0x16,0x16,0x15,0x5f,0xc6,0xc5,0xc6,0xc6,0xc7,0xc6,0xc6,0xc6,0xc7,0xce, + 0x59,0x35,0x57,0x5c,0x21,0x28,0x2c,0x2e,0x30,0x2d,0x2b,0x28,0x27,0x23,0x20,0x1d, + 0x1a,0x1c,0x1f,0x26,0x2d,0x2f,0x2e,0x2d,0x29,0x26,0x25,0x24,0x23,0x21,0x21,0x22, + 0x80,0x7d,0x77,0x7b,0x7f,0x86,0x89,0x8a,0x7f,0x7d,0x88,0x95,0x9e,0x9d,0x96,0x8e, + 0x7d,0x6f,0x58,0x41,0x2d,0x1c,0x16,0x13,0x14,0x15,0x15,0x16,0x15,0x1e,0x36,0x48, + 0x59,0x62,0x69,0x72,0x78,0x7e,0x85,0x8a,0x8a,0x87,0x8d,0x92,0x91,0x9a,0xaa,0xb3, + 0xb1,0xb2,0xaf,0xab,0xa8,0xa2,0x9b,0x9b,0x9f,0x96,0x96,0x9c,0xa2,0xa5,0xa0,0x7b, + 0x99,0x35,0x31,0xbf,0xc2,0xc3,0xc4,0xc3,0xc2,0xc3,0xc3,0xc2,0xc3,0x5d,0x0d,0x15, + 0x17,0x14,0x15,0x17,0x15,0x18,0x66,0xc8,0xc5,0xc5,0xc7,0xc7,0xc6,0xc6,0xc5,0xce, + 0x59,0x36,0x5a,0x5e,0x21,0x27,0x2c,0x2f,0x2e,0x2c,0x2c,0x2b,0x2b,0x2a,0x2a,0x25, + 0x22,0x1f,0x22,0x27,0x2d,0x2f,0x2e,0x2d,0x2b,0x2a,0x2a,0x2a,0x27,0x23,0x22,0x22, + 0x80,0x7f,0x7c,0x7f,0x85,0x90,0xa0,0x9e,0x92,0x88,0x8c,0x97,0x9c,0x9c,0x96,0x8f, + 0x7e,0x70,0x5b,0x41,0x2e,0x1c,0x12,0x0e,0x11,0x15,0x16,0x16,0x18,0x18,0x2d,0x4b, + 0x63,0x71,0x74,0x78,0x7b,0x7b,0x7d,0x88,0x90,0x8e,0x8b,0x8d,0x9a,0xa3,0xaa,0xb4, + 0xb4,0xb3,0xaf,0xa9,0xa3,0x9f,0x97,0x94,0x99,0xa1,0xa9,0xb2,0xbb,0xc3,0xbb,0x85, + 0x99,0x33,0x2f,0xc1,0xc2,0xc2,0xc2,0xc2,0xc3,0xc1,0xc1,0xc4,0x31,0x16,0x17,0x16, + 0x16,0x15,0x15,0x17,0x1a,0x16,0x14,0x4c,0xc1,0xc6,0xc5,0xc6,0xc6,0xc5,0xc5,0xcc, + 0x5a,0x34,0x59,0x62,0x22,0x29,0x2d,0x2e,0x2e,0x2e,0x2e,0x31,0x33,0x32,0x2f,0x2b, + 0x29,0x25,0x27,0x29,0x2c,0x2d,0x2d,0x2d,0x2b,0x2e,0x2e,0x2e,0x2b,0x28,0x25,0x25, + 0x81,0x84,0x81,0x81,0x85,0x94,0xae,0xaf,0xa5,0x93,0x92,0x9a,0x9b,0x9c,0x98,0x8d, + 0x81,0x71,0x5f,0x45,0x2d,0x1c,0x12,0x0f,0x12,0x16,0x19,0x19,0x18,0x18,0x2b,0x4b, + 0x64,0x74,0x79,0x7a,0x75,0x76,0x74,0x79,0x87,0x94,0x94,0x8e,0x91,0xa3,0xaf,0xb2, + 0xb5,0xb5,0xad,0xa6,0xa1,0x9c,0x96,0x93,0x96,0xa3,0xb3,0xbd,0xc9,0xcd,0xc7,0x84, + 0x9a,0x33,0x31,0xc2,0xc2,0xc1,0xc1,0xc1,0xc0,0xc7,0xa6,0x29,0x14,0x16,0x18,0x16, + 0x16,0x15,0x16,0x17,0x16,0x15,0x15,0x12,0x49,0xc2,0xc5,0xc5,0xc5,0xc5,0xc6,0xcb, + 0x59,0x33,0x58,0x65,0x24,0x2a,0x2d,0x2e,0x2e,0x2f,0x2f,0x35,0x39,0x37,0x35,0x30, + 0x2c,0x26,0x27,0x28,0x29,0x2b,0x2c,0x2c,0x2d,0x2e,0x2e,0x2d,0x2a,0x27,0x26,0x24, + 0x81,0x84,0x85,0x84,0x84,0x95,0xbb,0xbc,0xb4,0x9c,0x94,0x9a,0x9e,0x9e,0x9a,0x91, + 0x84,0x71,0x5e,0x46,0x30,0x20,0x16,0x13,0x19,0x22,0x26,0x24,0x1e,0x16,0x28,0x49, + 0x62,0x74,0x79,0x77,0x79,0x78,0x73,0x71,0x6e,0x78,0x87,0x95,0x96,0xa3,0xb0,0xb6, + 0xb5,0xb5,0xad,0xa6,0x9f,0x98,0x94,0x95,0x96,0xa1,0xb4,0xc6,0xd1,0xd1,0xcb,0x81, + 0x98,0x35,0x31,0xc2,0xc0,0xc0,0xc0,0xc3,0xc2,0x7d,0x10,0x17,0x17,0x16,0x15,0x17, + 0x15,0x15,0x16,0x16,0x16,0x16,0x14,0x15,0x15,0x3f,0xc6,0xc4,0xc3,0xc6,0xc5,0xca, + 0x59,0x32,0x54,0x65,0x26,0x29,0x2c,0x2e,0x2e,0x2d,0x2f,0x37,0x3d,0x3a,0x38,0x33, + 0x2d,0x25,0x27,0x29,0x28,0x27,0x29,0x27,0x2a,0x2f,0x2d,0x2d,0x2a,0x25,0x23,0x20, + 0x80,0x83,0x87,0x84,0x84,0x8e,0xb7,0xbb,0xb9,0xa2,0x92,0x98,0x9f,0xa0,0x9d,0x94, + 0x85,0x71,0x59,0x43,0x30,0x1f,0x17,0x19,0x27,0x36,0x3d,0x38,0x2c,0x1d,0x22,0x43, + 0x5c,0x6b,0x73,0x73,0x78,0x7e,0x7e,0x79,0x70,0x67,0x64,0x7c,0x94,0xa2,0xb2,0xbc, + 0xb9,0xb5,0xad,0xa3,0x9b,0x96,0x93,0x94,0x95,0xa0,0xb6,0xc9,0xd3,0xd2,0xc4,0x73, + 0x99,0x31,0x32,0xc4,0xbf,0xbf,0xc0,0xc0,0x4c,0x17,0x16,0x15,0x19,0x17,0x16,0x15, + 0x15,0x15,0x15,0x15,0x15,0x15,0x16,0x14,0x16,0x11,0x2e,0xb8,0xc4,0xc3,0xc4,0xc9, + 0x58,0x32,0x50,0x63,0x23,0x29,0x2a,0x2d,0x2e,0x2d,0x33,0x39,0x38,0x37,0x32,0x2d, + 0x27,0x25,0x25,0x27,0x26,0x26,0x27,0x26,0x2c,0x2d,0x2e,0x29,0x27,0x22,0x1f,0x1d, + 0x7e,0x7e,0x82,0x84,0x88,0x8a,0xa6,0xb2,0xb0,0xa1,0x93,0x95,0x9b,0x9e,0x9e,0x99, + 0x86,0x73,0x5d,0x47,0x33,0x20,0x1a,0x2a,0x3b,0x4f,0x5b,0x55,0x47,0x33,0x2b,0x3c, + 0x52,0x5e,0x68,0x6b,0x70,0x7f,0x87,0x87,0x7e,0x6f,0x62,0x64,0x82,0x9c,0xb2,0xbf, + 0xbd,0xb6,0xad,0xa3,0x9a,0x96,0x97,0x96,0x97,0xa8,0xbb,0xcb,0xd4,0xcf,0xbc,0x63, + 0x9a,0x31,0x32,0xc5,0xbf,0xbf,0xba,0x41,0x13,0x13,0x16,0x15,0x15,0x16,0x15,0x16, + 0x15,0x14,0x15,0x15,0x14,0x15,0x16,0x17,0x18,0x14,0x16,0x2d,0xbd,0xc5,0xc4,0xc9, + 0x58,0x34,0x52,0x64,0x21,0x28,0x2a,0x2d,0x2f,0x30,0x35,0x38,0x35,0x32,0x2b,0x24, + 0x1c,0x1d,0x21,0x24,0x25,0x26,0x28,0x2a,0x2f,0x2d,0x2b,0x25,0x22,0x1e,0x1a,0x1c, + 0x7c,0x7a,0x7e,0x81,0x88,0x85,0x91,0x9d,0x9f,0x98,0x94,0x93,0x96,0x9a,0x9a,0x94, + 0x86,0x75,0x5e,0x47,0x35,0x24,0x28,0x39,0x4e,0x61,0x70,0x6e,0x5e,0x44,0x39,0x3b, + 0x4b,0x59,0x5a,0x57,0x68,0x7c,0x87,0x87,0x84,0x7f,0x6e,0x5e,0x75,0x93,0xad,0xbb, + 0xbf,0xb6,0xad,0xa2,0x9a,0x97,0x96,0x95,0x9d,0xb1,0xc3,0xcf,0xd5,0xcc,0xb6,0x5a, + 0x99,0x32,0x32,0xc8,0xbd,0xaf,0x2a,0x14,0x16,0x15,0x16,0x15,0x14,0x14,0x15,0x15, + 0x15,0x15,0x15,0x14,0x14,0x14,0x16,0x14,0x14,0x14,0x14,0x12,0x29,0xb4,0xc1,0xcb, + 0x59,0x34,0x52,0x65,0x21,0x28,0x2a,0x2d,0x30,0x30,0x37,0x37,0x34,0x2d,0x28,0x20, + 0x1a,0x1a,0x1e,0x24,0x26,0x29,0x2b,0x2e,0x30,0x2c,0x28,0x21,0x1e,0x1d,0x1e,0x20, + 0x7d,0x7a,0x7d,0x81,0x87,0x87,0x88,0x8d,0x91,0x90,0x90,0x91,0x90,0x95,0x92,0x90, + 0x83,0x72,0x5e,0x49,0x39,0x31,0x3a,0x4e,0x61,0x70,0x78,0x77,0x6a,0x51,0x44,0x43, + 0x48,0x51,0x55,0x52,0x5b,0x6a,0x7f,0x8a,0x87,0x7d,0x74,0x6f,0x72,0x8e,0xab,0xbd, + 0xc1,0xba,0xae,0xa2,0x99,0x96,0x98,0x9d,0xac,0xbd,0xcb,0xd6,0xd7,0xca,0xaf,0x55, + 0x9a,0x32,0x34,0xc2,0x93,0x19,0x12,0x16,0x14,0x15,0x15,0x15,0x16,0x1c,0x17,0x17, + 0x17,0x17,0x18,0x17,0x16,0x15,0x17,0x13,0x13,0x13,0x14,0x16,0x14,0x1d,0xad,0xca, + 0x5b,0x36,0x51,0x69,0x23,0x29,0x2b,0x2f,0x32,0x33,0x36,0x33,0x2f,0x27,0x1f,0x1b, + 0x18,0x1c,0x20,0x26,0x2a,0x2f,0x30,0x32,0x33,0x2d,0x27,0x21,0x1e,0x20,0x25,0x25, + 0x7e,0x7e,0x7f,0x7e,0x86,0x85,0x87,0x8e,0x8c,0x8d,0x8d,0x8f,0x8b,0x8c,0x8b,0x8a, + 0x82,0x75,0x69,0x58,0x4a,0x47,0x4f,0x5f,0x6d,0x75,0x74,0x70,0x67,0x57,0x4a,0x45, + 0x46,0x4c,0x4e,0x4e,0x56,0x5e,0x66,0x77,0x7f,0x7a,0x71,0x70,0x78,0x93,0xae,0xbf, + 0xc0,0xbe,0xaf,0xa1,0x97,0x97,0x9a,0xa5,0xb4,0xc5,0xcf,0xd9,0xda,0xc7,0xa7,0x4d, + 0x97,0x32,0x35,0xc3,0x51,0x12,0x15,0x14,0x19,0x13,0x14,0x16,0x1b,0x15,0x15,0x15, + 0x14,0x14,0x14,0x15,0x15,0x16,0x16,0x13,0x14,0x14,0x13,0x15,0x15,0x13,0x1a,0xb2, + 0x5b,0x33,0x51,0x6c,0x25,0x2b,0x2c,0x2e,0x34,0x34,0x35,0x31,0x2b,0x24,0x1e,0x1c, + 0x1d,0x20,0x24,0x29,0x2d,0x32,0x32,0x32,0x2f,0x29,0x24,0x1f,0x20,0x21,0x28,0x27, + 0x80,0x85,0x84,0x82,0x85,0x86,0x8e,0x94,0x92,0x8f,0x8b,0x8b,0x89,0x87,0x8a,0x89, + 0x85,0x7f,0x75,0x67,0x5c,0x57,0x61,0x6a,0x70,0x6f,0x69,0x60,0x57,0x51,0x47,0x45, + 0x40,0x42,0x43,0x42,0x49,0x56,0x60,0x5f,0x61,0x65,0x62,0x68,0x75,0x93,0xb2,0xc4, + 0xc5,0xc0,0xb3,0xa2,0x9a,0x9c,0x9f,0xa8,0xb9,0xca,0xd7,0xdd,0xda,0xc3,0x9f,0x46, + 0x96,0x31,0x35,0xc7,0xc6,0x57,0x10,0x17,0x13,0x10,0x30,0x35,0x12,0x14,0x14,0x14, + 0x13,0x13,0x13,0x13,0x13,0x14,0x14,0x14,0x15,0x13,0x13,0x15,0x14,0x14,0x16,0x18, + 0x43,0x34,0x4f,0x71,0x2b,0x30,0x30,0x31,0x34,0x32,0x32,0x2f,0x2a,0x23,0x1d,0x1f, + 0x20,0x24,0x28,0x2d,0x30,0x33,0x32,0x31,0x2d,0x24,0x1f,0x1d,0x20,0x25,0x2b,0x2c, + 0x7e,0x86,0x85,0x81,0x7f,0x83,0x8f,0x97,0x95,0x91,0x89,0x8b,0x8e,0x8c,0x8a,0x8a, + 0x86,0x84,0x7b,0x70,0x67,0x61,0x60,0x62,0x63,0x5c,0x52,0x4a,0x45,0x40,0x3b,0x3a, + 0x39,0x35,0x35,0x34,0x35,0x3d,0x4c,0x51,0x4a,0x40,0x45,0x4f,0x5d,0x77,0x9e,0xb5, + 0xb8,0xb6,0xad,0x9d,0x94,0x92,0x98,0xa9,0xba,0xc8,0xd4,0xd9,0xd0,0xb5,0x8f,0x3c, + 0x96,0x2f,0x36,0xc7,0xbe,0xc0,0x61,0x11,0x17,0x40,0xc3,0x49,0x14,0x14,0x14,0x15, + 0x14,0x14,0x10,0x13,0x13,0x15,0x16,0x14,0x13,0x17,0x14,0x18,0x13,0x14,0x13,0x12, + 0x11,0x2a,0x50,0x72,0x31,0x35,0x34,0x32,0x32,0x31,0x2e,0x2c,0x27,0x22,0x20,0x20, + 0x22,0x28,0x2b,0x30,0x31,0x32,0x31,0x2d,0x27,0x22,0x1c,0x1e,0x21,0x26,0x29,0x29, + 0x80,0x8b,0x85,0x7e,0x7a,0x7f,0x88,0x91,0x93,0x92,0x8d,0x8f,0x91,0x93,0x8f,0x8b, + 0x87,0x7d,0x77,0x6d,0x64,0x5b,0x52,0x50,0x4c,0x44,0x3d,0x36,0x35,0x34,0x34,0x30, + 0x31,0x2b,0x29,0x26,0x29,0x2a,0x2f,0x33,0x31,0x2c,0x2a,0x2d,0x40,0x58,0x79,0x91, + 0x97,0x96,0x8d,0x85,0x7c,0x7c,0x83,0x96,0xa3,0xaf,0xb9,0xc0,0xb7,0xa1,0x87,0x44, + 0x95,0x2f,0x36,0xc6,0xbd,0xbc,0xc6,0x7a,0x64,0xbe,0xbd,0x45,0x13,0x14,0x15,0x15, + 0x14,0x12,0x16,0x13,0x14,0x15,0x14,0x15,0x14,0x38,0x99,0x17,0x17,0x16,0x15,0x12, + 0x17,0x30,0x51,0x76,0x37,0x39,0x36,0x35,0x35,0x35,0x30,0x2b,0x25,0x21,0x1f,0x20, + 0x24,0x28,0x2c,0x2f,0x32,0x33,0x2e,0x28,0x25,0x1e,0x1b,0x1c,0x21,0x26,0x28,0x24, + 0x83,0x87,0x82,0x7c,0x7b,0x81,0x82,0x8a,0x90,0x8e,0x8e,0x90,0x93,0x95,0x90,0x8d, + 0x84,0x77,0x6e,0x63,0x59,0x4f,0x45,0x3c,0x36,0x31,0x2d,0x2f,0x2e,0x2c,0x2d,0x2b, + 0x29,0x25,0x21,0x1e,0x1f,0x21,0x25,0x25,0x23,0x21,0x20,0x26,0x2d,0x3e,0x4a,0x5b, + 0x64,0x63,0x65,0x62,0x65,0x69,0x64,0x6c,0x77,0x83,0x8f,0x98,0x9a,0x99,0x94,0x59, + 0x92,0x2f,0x35,0xc6,0xbc,0xbd,0xbc,0xbf,0xbf,0xbe,0xb9,0x3e,0x14,0x14,0x14,0x14, + 0x13,0x13,0x14,0x13,0x14,0x14,0x15,0x14,0x14,0x40,0xc4,0x9a,0x16,0x12,0x16,0x36, + 0x5a,0x31,0x4a,0x7a,0x3b,0x3c,0x38,0x36,0x36,0x37,0x31,0x2b,0x24,0x20,0x1d,0x1d, + 0x21,0x27,0x2a,0x2f,0x32,0x32,0x2c,0x28,0x23,0x1d,0x1a,0x1a,0x1d,0x21,0x20,0x1f, + 0x7f,0x85,0x7f,0x7c,0x7f,0x83,0x84,0x85,0x87,0x89,0x8c,0x8d,0x90,0x91,0x8f,0x8a, + 0x81,0x75,0x68,0x5e,0x59,0x52,0x44,0x37,0x2d,0x2d,0x2f,0x2f,0x30,0x31,0x31,0x2f, + 0x2c,0x27,0x1f,0x1a,0x1b,0x1b,0x1d,0x21,0x1f,0x21,0x27,0x33,0x38,0x40,0x3d,0x36, + 0x3d,0x47,0x51,0x63,0x67,0x62,0x58,0x4b,0x44,0x47,0x51,0x69,0x8a,0x9e,0xac,0x60, + 0x90,0x2f,0x36,0xc6,0xbc,0xbc,0xbd,0xbc,0xbd,0xbd,0xb6,0x1e,0x15,0x14,0x13,0x13, + 0x13,0x13,0x13,0x13,0x13,0x17,0x13,0x14,0x14,0x4f,0xbf,0xc3,0xa7,0x1a,0x44,0xc6, + 0x58,0x31,0x48,0x79,0x3a,0x3c,0x37,0x39,0x37,0x37,0x31,0x2c,0x27,0x24,0x24,0x2a, + 0x31,0x3b,0x43,0x47,0x46,0x3f,0x30,0x26,0x20,0x1a,0x18,0x19,0x1a,0x1c,0x1b,0x1a, + 0x77,0x7f,0x79,0x7a,0x7d,0x85,0x83,0x82,0x81,0x88,0x8a,0x87,0x8d,0x8c,0x8b,0x89, + 0x7f,0x74,0x6a,0x61,0x60,0x5a,0x4a,0x3b,0x33,0x32,0x34,0x34,0x35,0x36,0x38,0x39, + 0x31,0x29,0x21,0x1b,0x1b,0x19,0x1a,0x1c,0x20,0x23,0x31,0x3f,0x44,0x49,0x48,0x3c, + 0x42,0x56,0x70,0x85,0x8a,0x81,0x6f,0x59,0x44,0x33,0x35,0x61,0x8d,0xa8,0xba,0x63, + 0x8e,0x30,0x36,0xc5,0xbd,0xbc,0xbc,0xbc,0xbc,0xbc,0xb6,0x26,0x13,0x14,0x13,0x13, + 0x13,0x13,0x13,0x13,0x13,0x16,0x14,0x14,0x13,0x57,0xbf,0xc3,0xc1,0xaa,0xc6,0xc1, + 0x59,0x31,0x49,0x77,0x37,0x3a,0x3a,0x39,0x39,0x37,0x36,0x36,0x39,0x41,0x4e,0x62, + 0x6e,0x76,0x7d,0x7d,0x72,0x5e,0x40,0x27,0x1e,0x19,0x19,0x18,0x19,0x19,0x18,0x17, + 0x77,0x7c,0x79,0x76,0x7c,0x7f,0x80,0x7d,0x7d,0x82,0x87,0x89,0x8a,0x8a,0x8b,0x8a, + 0x81,0x78,0x6f,0x69,0x6a,0x65,0x53,0x41,0x37,0x39,0x3c,0x3e,0x3c,0x39,0x39,0x37, + 0x35,0x31,0x28,0x22,0x21,0x22,0x25,0x2a,0x2e,0x32,0x3e,0x4b,0x51,0x52,0x51,0x43, + 0x4b,0x64,0x86,0x9f,0xa6,0xa0,0x92,0x7b,0x64,0x48,0x3e,0x5f,0x91,0xae,0xc1,0x60, + 0x92,0x2f,0x38,0xc4,0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xb7,0x3a,0x13,0x15,0x15,0x13, + 0x13,0x13,0x13,0x13,0x13,0x14,0x15,0x14,0x15,0x62,0xc0,0xc3,0xc2,0xc4,0xc0,0xc4, + 0x58,0x2f,0x48,0x7f,0x48,0x4a,0x4a,0x4b,0x4f,0x53,0x55,0x52,0x5f,0x6f,0x7a,0x8a, + 0x94,0x96,0x96,0x8c,0x7c,0x63,0x48,0x2c,0x20,0x19,0x18,0x18,0x1a,0x19,0x17,0x17, + 0x76,0x7a,0x79,0x76,0x78,0x7c,0x7c,0x7f,0x7f,0x82,0x83,0x88,0x8a,0x8a,0x8b,0x8b, + 0x84,0x7f,0x73,0x6c,0x6a,0x60,0x4d,0x3f,0x37,0x36,0x3d,0x41,0x3c,0x38,0x38,0x37, + 0x39,0x37,0x36,0x32,0x30,0x37,0x3e,0x44,0x48,0x4a,0x4f,0x5a,0x5d,0x5f,0x59,0x50, + 0x58,0x72,0x96,0xa9,0xb4,0xb7,0xad,0x9b,0x87,0x68,0x57,0x66,0x91,0xaf,0xc5,0x60, + 0x72,0x31,0x39,0xc5,0xba,0xbc,0xbc,0xbc,0xbd,0xb8,0xb9,0x37,0x15,0x14,0x13,0x13, + 0x13,0x13,0x14,0x12,0x13,0x14,0x13,0x15,0x15,0x6a,0xc0,0xc1,0xc0,0xc1,0xc2,0xc6, + 0x5b,0x2f,0x35,0x6d,0x51,0x53,0x54,0x55,0x58,0x5d,0x5c,0x56,0x65,0x77,0x81,0x8f, + 0x94,0x95,0x91,0x84,0x75,0x58,0x39,0x26,0x1f,0x19,0x18,0x17,0x18,0x18,0x18,0x17, + 0x76,0x7c,0x7b,0x75,0x75,0x79,0x7c,0x83,0x80,0x81,0x82,0x86,0x8a,0x8c,0x8c,0x8b, + 0x86,0x89,0x8a,0x88,0x7f,0x6a,0x53,0x42,0x3a,0x3e,0x49,0x52,0x50,0x52,0x52,0x49, + 0x48,0x44,0x42,0x43,0x44,0x4c,0x59,0x5e,0x66,0x68,0x6c,0x70,0x6e,0x70,0x6f,0x66, + 0x66,0x80,0xa0,0xb7,0xc4,0xc7,0xc0,0xb6,0xa6,0x8e,0x76,0x7d,0x9a,0xb4,0xcf,0x5f, + 0x11,0x31,0x39,0xc5,0xba,0xbc,0xbc,0xbc,0xbc,0xbd,0xb9,0x34,0x14,0x13,0x13,0x12, + 0x13,0x12,0x10,0x17,0x14,0x14,0x13,0x16,0x14,0x75,0xbf,0xbf,0xbe,0xbe,0xc3,0xc6, + 0x5a,0x2f,0x14,0x35,0x53,0x53,0x53,0x53,0x55,0x59,0x58,0x53,0x57,0x60,0x6a,0x6f, + 0x6c,0x68,0x64,0x5c,0x4b,0x37,0x27,0x20,0x1d,0x18,0x17,0x17,0x16,0x16,0x15,0x14, + 0x78,0x7b,0x7b,0x7a,0x76,0x78,0x80,0x87,0x86,0x86,0x85,0x86,0x8a,0x8e,0x8d,0x89, + 0x86,0x94,0x9c,0x9d,0x9c,0x8b,0x73,0x63,0x5a,0x60,0x6a,0x73,0x7c,0x86,0x80,0x6f, + 0x67,0x5c,0x52,0x52,0x56,0x5d,0x6b,0x71,0x7b,0x7a,0x7e,0x80,0x83,0x7c,0x7f,0x75, + 0x70,0x8d,0xac,0xc3,0xd1,0xd3,0xce,0xc4,0xbd,0xae,0x9b,0x97,0xa4,0xbf,0xd5,0x40, + 0x13,0x34,0x38,0xc5,0xbb,0xbb,0xbb,0xbb,0xbc,0xbd,0xb8,0x32,0x13,0x13,0x13,0x10, + 0x1d,0x29,0x2a,0x16,0x12,0x13,0x12,0x13,0x15,0x80,0xc0,0xbf,0xbf,0xc0,0xc1,0xc5, + 0x5a,0x2e,0x15,0x2d,0x40,0x3e,0x40,0x3f,0x3d,0x3b,0x39,0x32,0x28,0x25,0x29,0x2a, + 0x25,0x21,0x1f,0x1f,0x1f,0x20,0x1d,0x1a,0x18,0x15,0x16,0x15,0x14,0x14,0x12,0x12, + 0x7b,0x7b,0x7d,0x79,0x77,0x77,0x7e,0x83,0x87,0x87,0x85,0x84,0x87,0x89,0x8c,0x88, + 0x84,0x8f,0x9d,0xa2,0xa4,0x99,0x85,0x78,0x72,0x7a,0x81,0x8d,0x97,0x9e,0x9c,0x8d, + 0x7d,0x70,0x61,0x62,0x65,0x68,0x6e,0x74,0x7a,0x7e,0x7f,0x86,0x87,0x82,0x83,0x7f, + 0x7c,0x93,0xb0,0xc5,0xd1,0xd6,0xd2,0xcc,0xcb,0xc4,0xbc,0xb3,0xb4,0xc3,0xcf,0x19, + 0x14,0x34,0x38,0xc3,0xba,0xba,0xbb,0xbb,0xbb,0xbc,0xb8,0x32,0x13,0x13,0x12,0x37, + 0x1a,0x13,0x15,0x11,0x14,0x13,0x13,0x14,0x14,0x8d,0xbf,0xbf,0xbf,0xc0,0xc1,0xc4, + 0x57,0x2c,0x15,0x24,0x27,0x28,0x28,0x28,0x25,0x22,0x1d,0x1b,0x16,0x15,0x13,0x12, + 0x13,0x16,0x17,0x18,0x19,0x18,0x16,0x16,0x14,0x14,0x13,0x12,0x13,0x13,0x12,0x11, + 0x59,0x5b,0x5d,0x59,0x59,0x5a,0x5e,0x62,0x67,0x68,0x67,0x68,0x6c,0x6e,0x71,0x70, + 0x6d,0x6b,0x73,0x81,0x89,0x81,0x75,0x72,0x73,0x7c,0x88,0x92,0x99,0x9c,0x9b,0x94, + 0x87,0x7a,0x72,0x73,0x75,0x7a,0x80,0x7c,0x79,0x7a,0x7c,0x81,0x83,0x83,0x86,0x89, + 0x82,0x8c,0xa7,0xb4,0xc0,0xc7,0xc6,0xc5,0xc8,0xcc,0xcd,0xcc,0xc8,0xcd,0xb0,0x0d, + 0x17,0x33,0x36,0xc5,0xba,0xba,0xbb,0xbb,0xbb,0xbc,0xb8,0x2f,0x11,0x13,0x13,0x11, + 0x13,0x12,0x13,0x12,0x13,0x13,0x13,0x14,0x11,0x97,0xbd,0xbf,0xbf,0xc0,0xc0,0xc4, + 0x58,0x2a,0x14,0x20,0x26,0x26,0x24,0x26,0x26,0x24,0x1d,0x1a,0x18,0x18,0x17,0x15, + 0x14,0x14,0x14,0x16,0x17,0x15,0x13,0x12,0x12,0x11,0x10,0x11,0x11,0x11,0x11,0x10, + 0x3b,0x3c,0x3c,0x3a,0x38,0x3c,0x38,0x3b,0x3a,0x37,0x35,0x34,0x33,0x33,0x32,0x35, + 0x30,0x35,0x34,0x30,0x2e,0x2f,0x32,0x2f,0x2e,0x2e,0x2d,0x2e,0x2d,0x2d,0x2c,0x31, + 0x32,0x32,0x39,0x38,0x33,0x35,0x39,0x3c,0x3d,0x3b,0x3e,0x40,0x3e,0x45,0x44,0x44, + 0x43,0x41,0x44,0x42,0x45,0x46,0x49,0x51,0x54,0x47,0x24,0x33,0x40,0x2e,0x18,0x10, + 0x15,0x33,0x36,0xc5,0xba,0xba,0xbb,0xbb,0xbb,0xbb,0xb7,0x2e,0x14,0x13,0x13,0x13, + 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x14,0x0f,0xa0,0xbd,0xbf,0xbe,0xbf,0xc0,0xc4, + 0x58,0x26,0x12,0x1f,0x2b,0x28,0x28,0x26,0x26,0x24,0x20,0x1c,0x1b,0x1b,0x1a,0x19, + 0x17,0x17,0x17,0x17,0x17,0x16,0x15,0x13,0x11,0x12,0x13,0x13,0x13,0x13,0x11,0x11, + 0x69,0x65,0x62,0x68,0x6b,0x66,0x6e,0x69,0x64,0x65,0x66,0x6d,0x6a,0x6e,0x6d,0x69, + 0x67,0x68,0x68,0x68,0x63,0x67,0x6c,0x62,0x60,0x68,0x69,0x63,0x65,0x66,0x67,0x67, + 0x67,0x61,0x62,0x5d,0x5a,0x5d,0x5f,0x5c,0x5d,0x5b,0x5c,0x5a,0x57,0x5b,0x59,0x56, + 0x54,0x55,0x52,0x4e,0x4b,0x4c,0x3d,0x3d,0x39,0x3c,0x10,0x0f,0x0e,0x0f,0x0f,0x10, + 0x13,0x31,0x36,0xc4,0xb9,0xb9,0xba,0xbb,0xbb,0xbb,0xb8,0x2b,0x13,0x13,0x13,0x13, + 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0b,0xad,0xbe,0xbe,0xbd,0xbe,0xbf,0xc3, + 0x57,0x26,0x11,0x23,0x28,0x28,0x29,0x26,0x23,0x22,0x22,0x1e,0x1e,0x1e,0x1c,0x1c, + 0x1c,0x1c,0x1c,0x1c,0x1b,0x1a,0x18,0x16,0x16,0x15,0x15,0x16,0x16,0x15,0x13,0x11, + 0x7e,0x77,0x7d,0x76,0x7f,0x8c,0x7b,0x86,0x84,0x87,0x85,0x8d,0x88,0x8c,0x86,0x8e, + 0x8e,0x8d,0x8d,0x8e,0x90,0x90,0x90,0x93,0x8f,0x91,0x8c,0x8f,0x92,0x93,0x93,0x93, + 0x90,0x8e,0x8a,0x87,0x8d,0x8b,0x8e,0x8e,0x8a,0x86,0x87,0x85,0x84,0x82,0x85,0x86, + 0x83,0x83,0x87,0x88,0x7f,0x81,0x7b,0x4c,0x43,0x41,0x39,0x17,0x0e,0x0c,0x0c,0x10, + 0x14,0x31,0x39,0xc3,0xb9,0xb9,0xba,0xba,0xba,0xba,0xb8,0x26,0x13,0x13,0x13,0x13, + 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0b,0xbb,0xbf,0xbd,0xbd,0xbc,0xbc,0xc3, + 0x56,0x2a,0x10,0x23,0x29,0x28,0x27,0x25,0x21,0x20,0x22,0x1e,0x1e,0x1e,0x1f,0x1f, + 0x20,0x21,0x21,0x22,0x20,0x1c,0x1c,0x1b,0x19,0x18,0x18,0x19,0x19,0x18,0x15,0x14, + 0x85,0x88,0x87,0x86,0x8a,0x87,0x8a,0x92,0x8c,0x8e,0x89,0x88,0x8b,0x80,0x7f,0x7c, + 0x81,0x81,0x84,0x8a,0x8d,0x89,0x8f,0x93,0x94,0x98,0x99,0x91,0x8e,0x8d,0x88,0x94, + 0x8f,0x8a,0x8a,0x85,0x89,0x8d,0x90,0x94,0x92,0x94,0x93,0x93,0x97,0x9a,0x94,0x9a, + 0x94,0x91,0x92,0x93,0x94,0x93,0x95,0x7b,0x4f,0x53,0x4f,0x53,0x23,0x0d,0x0d,0x0e, + 0x13,0x34,0x3b,0xc3,0xb9,0xb9,0xb9,0xb9,0xba,0xb9,0xb3,0x24,0x13,0x12,0x15,0x13, + 0x16,0x14,0x15,0x12,0x12,0x12,0x13,0x14,0x12,0xbe,0xbe,0xbc,0xbd,0xbd,0xbd,0xc2, + 0x55,0x29,0x12,0x1f,0x28,0x27,0x25,0x24,0x20,0x1f,0x20,0x1d,0x1e,0x1f,0x1f,0x20, + 0x22,0x24,0x25,0x27,0x25,0x22,0x1f,0x20,0x1e,0x1d,0x1e,0x1e,0x1e,0x1c,0x19,0x17, + 0x73,0x6f,0x74,0x76,0x75,0x78,0x7d,0x8a,0x91,0x90,0x88,0x82,0x7f,0x82,0x84,0x7f, + 0x79,0x72,0x6a,0x6b,0x6e,0x64,0x52,0x3e,0x38,0x47,0x56,0x61,0x67,0x6a,0x6d,0x76, + 0x79,0x7b,0x7b,0x7e,0x81,0x83,0x83,0x84,0x84,0x86,0x8b,0x8a,0x87,0x84,0x82,0x81, + 0x79,0x75,0x77,0x72,0x75,0x73,0x78,0x79,0x5e,0x5f,0x59,0x44,0x3f,0x32,0x2e,0x10, + 0x14,0x28,0x3d,0xc3,0xb8,0xb9,0xb9,0xb9,0xb8,0xb8,0xb8,0x6d,0x5b,0x54,0x48,0x1a, + 0x13,0x0f,0x0c,0x11,0x12,0x12,0x11,0x0d,0x14,0xc5,0xbe,0xbc,0xbd,0xbd,0xbd,0xc1, + 0x54,0x29,0x10,0x1f,0x25,0x26,0x24,0x22,0x1e,0x1c,0x1b,0x1b,0x1b,0x1c,0x1e,0x1f, + 0x22,0x24,0x26,0x29,0x2a,0x27,0x25,0x24,0x21,0x22,0x22,0x20,0x1e,0x1d,0x1c,0x19, + 0x72,0x72,0x73,0x72,0x70,0x70,0x77,0x85,0x8f,0x8b,0x88,0x81,0x7d,0x80,0x86,0x84, + 0x7b,0x6f,0x6a,0x6d,0x74,0x6b,0x56,0x42,0x3c,0x45,0x54,0x5e,0x63,0x69,0x6f,0x75, + 0x7b,0x7e,0x81,0x83,0x85,0x86,0x88,0x8a,0x8d,0x8f,0x91,0x92,0x94,0x8f,0x87,0x7b, + 0x70,0x63,0x59,0x59,0x66,0x73,0x80,0x8c,0x94,0x96,0x9a,0x9e,0x99,0x8f,0x7d,0x14, + 0x13,0x17,0x42,0xbf,0xb7,0xb6,0xb6,0xb8,0xb7,0xb5,0xb8,0xba,0xb7,0xba,0xb8,0xb1, + 0xaf,0x9d,0x79,0x81,0xae,0xac,0xaa,0x9f,0x9c,0xc0,0xbd,0xbc,0xbc,0xbe,0xbc,0xc1, + 0x55,0x29,0x15,0x1e,0x27,0x27,0x25,0x23,0x1e,0x1c,0x18,0x19,0x1a,0x1b,0x1b,0x1d, + 0x21,0x27,0x2a,0x30,0x2f,0x31,0x2b,0x28,0x26,0x24,0x23,0x22,0x21,0x1e,0x1b,0x1a, + 0x71,0x6f,0x6e,0x67,0x69,0x6a,0x70,0x7a,0x82,0x84,0x82,0x7f,0x81,0x84,0x86,0x85, + 0x7b,0x6f,0x69,0x6c,0x72,0x6c,0x59,0x43,0x3d,0x44,0x51,0x5d,0x63,0x68,0x6d,0x72, + 0x77,0x7d,0x80,0x83,0x86,0x89,0x8b,0x8d,0x8f,0x91,0x93,0x94,0x95,0x91,0x8a,0x81, + 0x7b,0x75,0x6c,0x69,0x73,0x80,0x8a,0x98,0xa2,0xa6,0xab,0xae,0xa9,0x9e,0x90,0x19, + 0x2b,0x20,0x3a,0xc4,0xbc,0xbe,0xbf,0xc0,0xbd,0xbc,0xbc,0xb9,0xba,0xba,0xb9,0xb8, + 0xb7,0xba,0xbc,0xbe,0xba,0xbb,0xbe,0xbc,0xbd,0xbb,0xbd,0xbc,0xbb,0xbc,0xbb,0xc2, + 0x56,0x29,0x14,0x1e,0x29,0x2b,0x27,0x23,0x1f,0x1d,0x1a,0x16,0x17,0x18,0x1a,0x1d, + 0x24,0x2d,0x30,0x33,0x37,0x36,0x32,0x2f,0x2a,0x25,0x24,0x20,0x20,0x1e,0x1a,0x1a, + 0x73,0x6e,0x6c,0x66,0x66,0x68,0x6e,0x71,0x75,0x75,0x79,0x7a,0x80,0x85,0x86,0x80, + 0x77,0x6b,0x69,0x6f,0x72,0x6a,0x53,0x41,0x3a,0x43,0x4f,0x5c,0x63,0x68,0x6b,0x70, + 0x74,0x79,0x81,0x86,0x88,0x8c,0x8f,0x92,0x91,0x93,0x94,0x95,0x94,0x90,0x8a,0x84, + 0x7f,0x80,0x7b,0x78,0x7c,0x89,0x95,0x9d,0xa3,0xab,0xb1,0xb4,0xb2,0xaf,0xa2,0x1e, + 0x85,0x2a,0x2c,0x31,0x3a,0x41,0x4c,0x54,0x61,0x75,0x7f,0x8e,0x9d,0xa5,0xaf,0xb6, + 0xba,0xbe,0xb9,0xba,0xb9,0xbb,0xba,0xba,0xba,0xb9,0xbc,0xbb,0xbc,0xbb,0xbc,0xc1, + 0x55,0x28,0x14,0x21,0x2a,0x2e,0x28,0x22,0x1e,0x1b,0x16,0x14,0x16,0x17,0x1a,0x1e, + 0x27,0x2f,0x36,0x3e,0x3e,0x3c,0x39,0x34,0x30,0x2a,0x27,0x24,0x20,0x1d,0x1a,0x19, + 0x70,0x6d,0x69,0x62,0x66,0x6b,0x71,0x71,0x72,0x71,0x71,0x73,0x7b,0x83,0x86,0x7f, + 0x76,0x6d,0x6b,0x70,0x70,0x64,0x4d,0x3b,0x38,0x43,0x50,0x5b,0x64,0x69,0x6c,0x6e, + 0x72,0x77,0x7e,0x84,0x88,0x8c,0x8e,0x91,0x92,0x94,0x95,0x94,0x91,0x8d,0x87,0x81, + 0x7e,0x7e,0x80,0x80,0x81,0x84,0x90,0x9a,0x9e,0xa6,0xb0,0xb0,0xb2,0xb7,0xb9,0x44, + 0x63,0x70,0x26,0x1e,0x20,0x20,0x21,0x22,0x25,0x22,0x22,0x24,0x25,0x26,0x26,0x2b, + 0x2f,0x32,0x3a,0x3e,0x46,0x4c,0x53,0x58,0x5f,0x65,0x6c,0x74,0x7c,0x86,0x8f,0x9b, + 0x43,0x24,0x17,0x29,0x2e,0x2b,0x29,0x21,0x1d,0x19,0x14,0x16,0x16,0x18,0x1a,0x21, + 0x28,0x31,0x39,0x3f,0x3f,0x3f,0x3b,0x39,0x36,0x32,0x2a,0x29,0x25,0x1e,0x1c,0x1b, + 0x6d,0x6d,0x65,0x62,0x68,0x69,0x70,0x71,0x71,0x6e,0x71,0x73,0x78,0x7c,0x80,0x7a, + 0x73,0x6d,0x6a,0x6f,0x71,0x63,0x4d,0x3c,0x3c,0x44,0x50,0x5b,0x64,0x69,0x6c,0x6e, + 0x70,0x76,0x7c,0x81,0x85,0x89,0x8d,0x90,0x91,0x91,0x91,0x8e,0x8a,0x88,0x81,0x7c, + 0x7a,0x7a,0x7c,0x80,0x84,0x84,0x83,0x8b,0x92,0x9a,0xa5,0xa8,0xa9,0xb5,0xba,0x9f, + 0x46,0x8b,0xa0,0x8d,0x81,0x77,0x6d,0x5a,0x4a,0x47,0x3f,0x36,0x30,0x2c,0x25,0x25, + 0x26,0x29,0x27,0x29,0x29,0x29,0x28,0x29,0x2b,0x2e,0x2a,0x2c,0x2d,0x28,0x28,0x29, + 0x26,0x1d,0x70,0x5e,0x2d,0x2c,0x27,0x21,0x1f,0x1a,0x15,0x16,0x17,0x18,0x1c,0x24, + 0x2b,0x33,0x3b,0x3d,0x3d,0x3e,0x3d,0x3c,0x38,0x33,0x2e,0x29,0x24,0x20,0x1d,0x1c, + 0x6f,0x6c,0x67,0x65,0x62,0x66,0x6d,0x6f,0x72,0x74,0x74,0x74,0x74,0x72,0x77,0x77, + 0x71,0x6e,0x6a,0x6e,0x6e,0x61,0x4c,0x3b,0x3d,0x44,0x52,0x5f,0x64,0x69,0x6c,0x6e, + 0x71,0x74,0x79,0x7d,0x81,0x85,0x86,0x89,0x88,0x88,0x85,0x84,0x81,0x7e,0x7a,0x78, + 0x77,0x77,0x7b,0x7d,0x7e,0x80,0x82,0x84,0x87,0x88,0x8e,0x97,0x9c,0xa7,0xb3,0xb1, + 0x8d,0x8e,0xb8,0xb8,0xd5,0xd5,0xdb,0xc8,0xc5,0xc6,0xc2,0xbb,0xb6,0xb6,0xb5,0xb1, + 0xae,0xae,0xa5,0x9c,0x91,0x88,0x7d,0x75,0x6c,0x62,0x55,0x48,0x3d,0x37,0x33,0x26, + 0x26,0x6e,0xcb,0x2b,0x2f,0x2b,0x25,0x22,0x22,0x1e,0x19,0x17,0x17,0x19,0x1e,0x23, + 0x29,0x2e,0x33,0x36,0x37,0x39,0x3a,0x3a,0x37,0x35,0x34,0x2d,0x2a,0x26,0x22,0x20, + 0x6f,0x6d,0x6b,0x6b,0x65,0x64,0x69,0x6c,0x76,0x79,0x77,0x75,0x73,0x6d,0x6f,0x74, + 0x72,0x6f,0x6c,0x6a,0x6a,0x5d,0x49,0x3c,0x3e,0x45,0x54,0x5e,0x64,0x69,0x6c,0x6e, + 0x70,0x73,0x77,0x79,0x7d,0x7e,0x7d,0x7c,0x7b,0x7c,0x7a,0x7a,0x79,0x77,0x73,0x73, + 0x73,0x78,0x7d,0x81,0x82,0x7f,0x7e,0x7e,0x7d,0x80,0x81,0x80,0x80,0x8c,0x9d,0xaa, + 0xa8,0xa7,0xa9,0xb7,0xc0,0xc6,0xcd,0xd4,0xd9,0xd8,0xdb,0xda,0xd3,0xd6,0xd5,0xd1, + 0xd4,0xd4,0xcf,0xd3,0xd6,0xd3,0xcf,0xce,0xcb,0xd1,0xd4,0xd2,0xcd,0xcc,0xd2,0xca, + 0xdd,0xf5,0xa1,0x2d,0x30,0x30,0x2d,0x2a,0x28,0x23,0x1c,0x18,0x17,0x18,0x1d,0x22, + 0x24,0x25,0x27,0x2b,0x2d,0x2f,0x33,0x33,0x35,0x36,0x38,0x35,0x31,0x2e,0x29,0x23, + 0x6d,0x71,0x73,0x71,0x6c,0x69,0x6a,0x6e,0x73,0x76,0x76,0x77,0x77,0x71,0x72,0x74, + 0x70,0x70,0x6b,0x6b,0x6a,0x5a,0x46,0x3b,0x3a,0x47,0x56,0x5e,0x66,0x6c,0x6d,0x6e, + 0x70,0x70,0x73,0x74,0x75,0x75,0x72,0x6f,0x6d,0x6f,0x70,0x71,0x71,0x71,0x71,0x6f, + 0x71,0x74,0x7e,0x84,0x87,0x86,0x82,0x7f,0x7a,0x78,0x79,0x7a,0x77,0x76,0x88,0x9b, + 0xa3,0xa3,0xa4,0xa5,0xab,0xba,0xcc,0xd8,0xdd,0xdd,0xdf,0xde,0xd4,0xc7,0xc4,0xce, + 0xca,0xb8,0xa5,0x80,0x58,0x64,0x76,0x88,0xa2,0xb5,0xc7,0xd7,0xdd,0xde,0xe4,0xea, + 0xf3,0xa4,0x38,0x33,0x34,0x37,0x34,0x30,0x2b,0x26,0x1e,0x18,0x17,0x16,0x1a,0x1e, + 0x21,0x21,0x21,0x22,0x25,0x25,0x2b,0x2e,0x30,0x34,0x36,0x35,0x32,0x2f,0x29,0x25, + 0x6b,0x70,0x72,0x72,0x6e,0x6c,0x6b,0x6f,0x73,0x73,0x73,0x76,0x76,0x77,0x78,0x74, + 0x72,0x70,0x6b,0x68,0x66,0x56,0x40,0x39,0x36,0x47,0x59,0x62,0x67,0x6c,0x6c,0x6e, + 0x6f,0x6e,0x6f,0x70,0x6f,0x6d,0x69,0x64,0x62,0x63,0x68,0x6f,0x6e,0x6f,0x6c,0x6c, + 0x6f,0x75,0x7d,0x83,0x87,0x88,0x86,0x82,0x80,0x7d,0x7a,0x78,0x77,0x77,0x7c,0x8c, + 0x98,0xa2,0xa7,0xb6,0xc6,0xd6,0xdd,0xde,0xdc,0xd8,0xd4,0xca,0xbd,0xbc,0xbd,0xc9, + 0xbe,0xaf,0x90,0x55,0x1e,0x21,0x20,0x1f,0x1a,0x19,0x19,0x1e,0x27,0x34,0x44,0x4a, + 0x38,0x32,0x3a,0x37,0x38,0x3b,0x38,0x33,0x2f,0x27,0x1d,0x18,0x15,0x15,0x1a,0x1a, + 0x1b,0x1b,0x1c,0x1b,0x1d,0x23,0x25,0x2b,0x2c,0x32,0x35,0x35,0x33,0x2f,0x2c,0x27, + 0x65,0x6b,0x6f,0x70,0x6c,0x6c,0x6a,0x6d,0x6c,0x6b,0x69,0x6c,0x76,0x7a,0x7d,0x76, + 0x73,0x6c,0x65,0x66,0x62,0x50,0x3d,0x35,0x37,0x4a,0x5b,0x64,0x6b,0x6c,0x6b,0x6c, + 0x6d,0x6b,0x6b,0x6b,0x69,0x66,0x62,0x5b,0x59,0x5c,0x63,0x6b,0x68,0x68,0x6a,0x6a, + 0x6f,0x76,0x7c,0x82,0x86,0x87,0x86,0x82,0x80,0x80,0x7f,0x7d,0x78,0x77,0x82,0x8b, + 0x90,0x9f,0xb4,0xc4,0xd1,0xda,0xd8,0xd3,0xcc,0xbe,0xae,0xa4,0xaa,0xb1,0xb3,0xc0, + 0xb7,0xa3,0x80,0x38,0x1f,0x22,0x24,0x25,0x29,0x2e,0x2e,0x31,0x34,0x35,0x39,0x3a, + 0x3a,0x3a,0x38,0x39,0x39,0x3a,0x39,0x36,0x32,0x29,0x20,0x1c,0x16,0x13,0x16,0x17, + 0x17,0x18,0x18,0x18,0x1a,0x1d,0x1f,0x23,0x26,0x2f,0x30,0x31,0x31,0x2d,0x28,0x28, + 0x64,0x69,0x6d,0x6c,0x68,0x64,0x67,0x69,0x68,0x67,0x66,0x68,0x72,0x79,0x7e,0x7b, + 0x71,0x6c,0x65,0x64,0x61,0x4d,0x38,0x31,0x39,0x4d,0x5e,0x67,0x6c,0x6c,0x6c,0x6b, + 0x6a,0x69,0x68,0x68,0x67,0x62,0x5c,0x56,0x52,0x53,0x5c,0x60,0x66,0x6e,0x6c,0x6e, + 0x73,0x7a,0x7e,0x82,0x86,0x87,0x86,0x82,0x82,0x82,0x82,0x82,0x82,0x80,0x86,0x93, + 0x95,0xa3,0xb5,0xc1,0xc6,0xcc,0xc6,0xb7,0x9a,0x7c,0x7a,0x8c,0x9a,0xa1,0xb4,0xb9, + 0xb1,0x9a,0x6e,0x2c,0x20,0x23,0x24,0x29,0x2a,0x2e,0x33,0x36,0x38,0x38,0x39,0x3c, + 0x38,0x36,0x39,0x3b,0x3b,0x3b,0x3a,0x3b,0x36,0x2d,0x27,0x24,0x1d,0x18,0x15,0x14, + 0x15,0x16,0x17,0x18,0x19,0x18,0x18,0x1b,0x20,0x28,0x28,0x28,0x2a,0x29,0x29,0x2c, + 0x67,0x69,0x69,0x69,0x69,0x63,0x63,0x63,0x62,0x60,0x5f,0x63,0x6c,0x76,0x79,0x7a, + 0x70,0x6a,0x68,0x68,0x60,0x4e,0x38,0x32,0x3b,0x50,0x60,0x68,0x6b,0x6a,0x69,0x68, + 0x67,0x67,0x67,0x66,0x64,0x60,0x5b,0x56,0x49,0x44,0x4e,0x58,0x65,0x71,0x70,0x74, + 0x78,0x7c,0x7f,0x83,0x86,0x86,0x85,0x83,0x82,0x81,0x80,0x82,0x84,0x86,0x89,0x94, + 0x99,0x9b,0xa6,0xac,0xaf,0xa6,0x92,0x72,0x50,0x48,0x5a,0x6e,0x7f,0x8f,0xaf,0xb7, + 0xab,0x93,0x61,0x24,0x23,0x24,0x25,0x2a,0x2c,0x2d,0x2d,0x30,0x32,0x35,0x38,0x3a, + 0x37,0x37,0x37,0x38,0x3a,0x3c,0x3c,0x3a,0x37,0x32,0x2e,0x29,0x26,0x1c,0x16,0x15, + 0x13,0x13,0x13,0x15,0x15,0x15,0x17,0x18,0x1b,0x24,0x27,0x26,0x28,0x2b,0x2c,0x32, + 0x6f,0x69,0x65,0x67,0x68,0x62,0x5e,0x5e,0x5f,0x60,0x60,0x61,0x6a,0x74,0x77,0x79, + 0x72,0x6e,0x6a,0x69,0x64,0x50,0x39,0x35,0x3f,0x54,0x63,0x69,0x6a,0x67,0x66,0x65, + 0x64,0x66,0x66,0x66,0x65,0x61,0x59,0x53,0x48,0x41,0x4b,0x58,0x66,0x73,0x78,0x7a, + 0x7c,0x7c,0x7e,0x81,0x84,0x85,0x85,0x83,0x81,0x81,0x80,0x82,0x84,0x87,0x8d,0x94, + 0x99,0x9c,0x95,0x8e,0x81,0x68,0x51,0x42,0x44,0x44,0x47,0x54,0x66,0x80,0xa3,0xa7, + 0x9c,0x86,0x53,0x20,0x23,0x23,0x24,0x25,0x27,0x28,0x28,0x2a,0x2d,0x2e,0x30,0x32, + 0x33,0x34,0x34,0x38,0x3c,0x3c,0x3b,0x3b,0x3e,0x3a,0x38,0x33,0x2c,0x20,0x18,0x15, + 0x12,0x11,0x11,0x12,0x12,0x12,0x15,0x18,0x1e,0x24,0x27,0x2b,0x2e,0x30,0x34,0x3a, + 0x70,0x6a,0x64,0x66,0x65,0x62,0x5f,0x58,0x5e,0x64,0x64,0x67,0x6b,0x72,0x75,0x78, + 0x73,0x70,0x6a,0x69,0x61,0x4d,0x3c,0x37,0x43,0x56,0x66,0x67,0x64,0x64,0x65,0x65, + 0x65,0x66,0x64,0x65,0x63,0x5e,0x57,0x54,0x52,0x4c,0x55,0x60,0x6c,0x74,0x7c,0x7b, + 0x7a,0x7b,0x7c,0x7f,0x84,0x84,0x86,0x81,0x80,0x7e,0x7d,0x82,0x82,0x84,0x8c,0x93, + 0x96,0x9b,0x92,0x81,0x6c,0x53,0x45,0x43,0x42,0x42,0x42,0x45,0x4f,0x67,0x8b,0x90, + 0x83,0x6b,0x3c,0x22,0x1f,0x21,0x21,0x20,0x1f,0x21,0x23,0x25,0x25,0x28,0x2a,0x31, + 0x32,0x33,0x36,0x39,0x3b,0x3a,0x3a,0x39,0x3b,0x3a,0x39,0x36,0x2d,0x22,0x19,0x13, + 0x11,0x0e,0x0f,0x10,0x10,0x11,0x15,0x1b,0x26,0x27,0x2a,0x2e,0x34,0x35,0x38,0x3d, + 0x6e,0x68,0x67,0x6a,0x68,0x66,0x60,0x5b,0x5f,0x6c,0x6d,0x72,0x71,0x74,0x76,0x79, + 0x74,0x70,0x68,0x65,0x5f,0x4b,0x3e,0x3d,0x47,0x59,0x64,0x61,0x61,0x63,0x63,0x64, + 0x65,0x65,0x64,0x65,0x61,0x59,0x59,0x56,0x57,0x5b,0x61,0x6c,0x75,0x7a,0x7e,0x7d, + 0x7c,0x7a,0x7b,0x7e,0x82,0x84,0x84,0x81,0x7f,0x7e,0x7d,0x7f,0x82,0x84,0x8a,0x92, + 0x96,0x99,0x92,0x7f,0x69,0x53,0x46,0x41,0x40,0x3f,0x40,0x41,0x48,0x55,0x6d,0x70, + 0x66,0x4b,0x27,0x1e,0x1f,0x1e,0x1f,0x1e,0x1d,0x1f,0x20,0x1f,0x21,0x23,0x2a,0x2b, + 0x2f,0x35,0x37,0x3a,0x3a,0x39,0x36,0x36,0x35,0x3a,0x37,0x33,0x2a,0x22,0x17,0x10, + 0x0d,0x0d,0x0e,0x0f,0x11,0x12,0x17,0x1e,0x26,0x28,0x2b,0x31,0x36,0x39,0x3c,0x3d, +}; diff --git a/partitions_example.csv b/partitions_example.csv new file mode 100644 index 0000000..c02ec11 --- /dev/null +++ b/partitions_example.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, spiffs, 0x180000, 1M, diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 0000000..3a6598f --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1,10 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" +CONFIG_APP_OFFSET=0x10000 +CONFIG_ESP32_PANIC_PRINT_HALT=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_MAIN_TASK_STACK_SIZE=8192 +CONFIG_TASK_WDT_TIMEOUT_S=20