Repository: loboris/ESP32_ePaper_example Branch: master Commit: 413b5759898e Files: 106 Total size: 1.3 MB Directory structure: gitextract_tqp4d6q6/ ├── .gitignore ├── Makefile ├── README.md ├── components/ │ ├── epaper/ │ │ ├── DefaultFont.c │ │ ├── DejaVuSans18.c │ │ ├── DejaVuSans24.c │ │ ├── EPD.c │ │ ├── EPD.h │ │ ├── EPDspi.c │ │ ├── EPDspi.h │ │ ├── SmallFont.c │ │ ├── Ubuntu16.c │ │ ├── comic24.c │ │ ├── component.mk │ │ ├── dejavuX.c │ │ ├── minya24.c │ │ └── tooney32.c │ ├── mkspiffs/ │ │ ├── .travis.yml │ │ ├── Makefile.projbuild │ │ ├── component.mk │ │ └── src/ │ │ ├── Makefile │ │ ├── Makefile.original │ │ ├── README.md │ │ ├── appveyor.yml │ │ ├── main.cpp │ │ ├── mkspiffs │ │ ├── spiffs/ │ │ │ ├── esp_spiffs.c │ │ │ ├── esp_spiffs.h │ │ │ ├── spiffs.h │ │ │ ├── spiffs_cache.c │ │ │ ├── spiffs_check.c │ │ │ ├── spiffs_config.h │ │ │ ├── spiffs_gc.c │ │ │ ├── spiffs_hydrogen.c │ │ │ ├── spiffs_nucleus.c │ │ │ └── spiffs_nucleus.h │ │ └── tclap/ │ │ ├── Arg.h │ │ ├── ArgException.h │ │ ├── ArgTraits.h │ │ ├── COPYING │ │ ├── CmdLine.h │ │ ├── CmdLineInterface.h │ │ ├── CmdLineOutput.h │ │ ├── Constraint.h │ │ ├── DocBookOutput.h │ │ ├── HelpVisitor.h │ │ ├── IgnoreRestVisitor.h │ │ ├── MultiArg.h │ │ ├── MultiSwitchArg.h │ │ ├── OptionalUnlabeledTracker.h │ │ ├── StandardTraits.h │ │ ├── StdOutput.h │ │ ├── SwitchArg.h │ │ ├── UnlabeledMultiArg.h │ │ ├── UnlabeledValueArg.h │ │ ├── ValueArg.h │ │ ├── ValuesConstraint.h │ │ ├── VersionVisitor.h │ │ ├── Visitor.h │ │ ├── XorHandler.h │ │ └── ZshCompletionOutput.h │ ├── spidriver/ │ │ ├── component.mk │ │ ├── spi_master_lobo.c │ │ └── spi_master_lobo.h │ ├── spiffs/ │ │ ├── component.mk │ │ ├── esp_spiffs.c │ │ ├── esp_spiffs.h │ │ ├── list.c │ │ ├── list.h │ │ ├── mutex.c │ │ ├── mutex.h │ │ ├── spiffs.h │ │ ├── spiffs_cache.c │ │ ├── spiffs_check.c │ │ ├── spiffs_config.h │ │ ├── spiffs_gc.c │ │ ├── spiffs_hydrogen.c │ │ ├── spiffs_nucleus.c │ │ ├── spiffs_nucleus.h │ │ ├── spiffs_vfs.c │ │ └── spiffs_vfs.h │ └── spiffs_image/ │ ├── Makefile.projbuild │ ├── component.mk │ ├── image/ │ │ ├── fonts/ │ │ │ ├── BigFont.fon │ │ │ ├── DejaVuSans12.fon │ │ │ ├── DejaVuSans18.fon │ │ │ ├── DejaVuSans24.fon │ │ │ ├── DotMatrix_M.fon │ │ │ ├── Grotesk24x48.fon │ │ │ ├── SmallFont.fon │ │ │ ├── Ubuntu.fon │ │ │ ├── arial_bold.fon │ │ │ ├── ocrfont.c │ │ │ └── swiss721_outline.fon │ │ └── spiffs.info │ └── spiffs_image.img ├── main/ │ ├── Kconfig.projbuild │ ├── component.mk │ ├── ePaper.c │ ├── ePaper.h │ ├── img1.h │ ├── img2.h │ ├── img3.h │ └── img_hacking.c ├── partitions_example.csv └── sdkconfig.defaults ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .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 ================================================ FILE: Makefile ================================================ # # This is a project Makefile. It is assumed the directory this Makefile resides in is a # project subdirectory. # PROJECT_NAME := ePaper #EXTRA_CFLAGS += --save-temps include $(IDF_PATH)/make/project.mk ================================================ FILE: README.md ================================================ ### 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) ================================================ FILE: components/epaper/DefaultFont.c ================================================ // 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 }; ================================================ FILE: components/epaper/DejaVuSans18.c ================================================ // ============================================================================ // 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 }; ================================================ FILE: components/epaper/DejaVuSans24.c ================================================ // ======================================================================== // 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 }; ================================================ FILE: components/epaper/EPD.c ================================================ /* 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; } ================================================ FILE: components/epaper/EPD.h ================================================ /* * 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 ================================================ FILE: components/epaper/EPDspi.c ================================================ /* * 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 26 #define BUSY_Pin 32 #define RST_Pin 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 ================================================ FILE: components/epaper/SmallFont.c ================================================ // 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, // ~ }; ================================================ FILE: components/epaper/Ubuntu16.c ================================================ // 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 }; ================================================ FILE: components/epaper/comic24.c ================================================ // 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 }; ================================================ FILE: components/epaper/component.mk ================================================ # # 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 := . ================================================ FILE: components/epaper/dejavuX.c ================================================ // 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 }; ================================================ FILE: components/epaper/minya24.c ================================================ // 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 }; ================================================ FILE: components/epaper/tooney32.c ================================================ // 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 }; ================================================ FILE: components/mkspiffs/.travis.yml ================================================ 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 ================================================ FILE: components/mkspiffs/Makefile.projbuild ================================================ 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 ================================================ FILE: components/mkspiffs/component.mk ================================================ # # Component Makefile # COMPONENT_SRCDIRS := COMPONENT_ADD_INCLUDEDIRS := ================================================ FILE: components/mkspiffs/src/Makefile ================================================ 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 CC=gcc.exe CXX=g++.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 CC=gcc CXX=g++ TARGET_CFLAGS = -std=gnu99 -Os -Wall -Itclap -Ispiffs -I. -D$(TARGET_OS) -DVERSION=\"$(VERSION)\" -D__NO_INLINE__ TARGET_CXXFLAGS = -std=gnu++11 -Os -Wall -Itclap -Ispiffs -I. -D$(TARGET_OS) -DVERSION=\"$(VERSION)\" -D__NO_INLINE__ endif ifeq ($(UNAME_S),Darwin) TARGET_OS := OSX DIST_SUFFIX := osx CC=clang CXX=clang++ TARGET_CFLAGS = -std=gnu99 -Os -Wall -Itclap -Ispiffs -I. -D$(TARGET_OS) -DVERSION=\"$(VERSION)\" -D__NO_INLINE__ -mmacosx-version-min=10.7 -arch x86_64 TARGET_CXXFLAGS = -std=gnu++11 -Os -Wall -Itclap -Ispiffs -I. -D$(TARGET_OS) -DVERSION=\"$(VERSION)\" -D__NO_INLINE__ -mmacosx-version-min=10.7 -arch x86_64 -stdlib=libc++ TARGET_LDFLAGS = -arch x86_64 -stdlib=libc++ endif ARCHIVE_CMD := tar czf ARCHIVE_EXTENSION := tar.gz TARGET := mkspiffs endif OBJ := main.o \ spiffs/spiffs_cache.o \ spiffs/spiffs_check.o \ spiffs/spiffs_gc.o \ spiffs/spiffs_hydrogen.o \ spiffs/spiffs_nucleus.o \ VERSION ?= $(shell git describe --always) .PHONY: all clean all: $(TARGET) $(TARGET): @echo "Building mkspiffs ..." $(CC) $(TARGET_CFLAGS) -c spiffs/spiffs_cache.c -o spiffs/spiffs_cache.o $(CC) $(TARGET_CFLAGS) -c spiffs/spiffs_check.c -o spiffs/spiffs_check.o $(CC) $(TARGET_CFLAGS) -c spiffs/spiffs_gc.c -o spiffs/spiffs_gc.o $(CC) $(TARGET_CFLAGS) -c spiffs/spiffs_hydrogen.c -o spiffs/spiffs_hydrogen.o $(CC) $(TARGET_CFLAGS) -c spiffs/spiffs_nucleus.c -o spiffs/spiffs_nucleus.o $(CXX) $(TARGET_CXXFLAGS) -c main.cpp -o main.o $(CXX) $(TARGET_CFLAGS) -o $(TARGET) $(OBJ) $(TARGET_LDFLAGS) clean: @rm -f *.o @rm -f spiffs/*.o @rm -f $(TARGET) ================================================ FILE: components/mkspiffs/src/Makefile.original ================================================ 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 ================================================ FILE: components/mkspiffs/src/README.md ================================================ # 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 ================================================ FILE: components/mkspiffs/src/appveyor.yml ================================================ 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 ================================================ FILE: components/mkspiffs/src/main.cpp ================================================ // // 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; } ================================================ FILE: components/mkspiffs/src/spiffs/esp_spiffs.c ================================================ /* * 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; } ================================================ FILE: components/mkspiffs/src/spiffs/esp_spiffs.h ================================================ /* * 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__ ================================================ FILE: components/mkspiffs/src/spiffs/spiffs.h ================================================ /* * 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_ */ ================================================ FILE: components/mkspiffs/src/spiffs/spiffs_cache.c ================================================ /* * 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 ================================================ FILE: components/mkspiffs/src/spiffs/spiffs_check.c ================================================ /* * 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 ================================================ FILE: components/mkspiffs/src/spiffs/spiffs_config.h ================================================ /* * 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_ */ ================================================ FILE: components/mkspiffs/src/spiffs/spiffs_gc.c ================================================ #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 ================================================ FILE: components/mkspiffs/src/spiffs/spiffs_hydrogen.c ================================================ /* * 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 ================================================ FILE: components/mkspiffs/src/spiffs/spiffs_nucleus.c ================================================ #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 ================================================ FILE: components/mkspiffs/src/spiffs/spiffs_nucleus.h ================================================ /* * 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_ */ ================================================ FILE: components/mkspiffs/src/tclap/Arg.h ================================================ // -*- 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 ================================================ FILE: components/mkspiffs/src/tclap/ArgException.h ================================================ // -*- 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 ================================================ FILE: components/mkspiffs/src/tclap/ArgTraits.h ================================================ // -*- 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 ================================================ FILE: components/mkspiffs/src/tclap/COPYING ================================================ 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. ================================================ FILE: components/mkspiffs/src/tclap/CmdLine.h ================================================ // -*- 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 ================================================ FILE: components/mkspiffs/src/tclap/CmdLineInterface.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/CmdLineOutput.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/Constraint.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/DocBookOutput.h ================================================ // -*- 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 ================================================ FILE: components/mkspiffs/src/tclap/HelpVisitor.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/IgnoreRestVisitor.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/MultiArg.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/MultiSwitchArg.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/OptionalUnlabeledTracker.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/StandardTraits.h ================================================ // -*- 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 ================================================ FILE: components/mkspiffs/src/tclap/StdOutput.h ================================================ // -*- 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 ================================================ FILE: components/mkspiffs/src/tclap/SwitchArg.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/UnlabeledMultiArg.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/UnlabeledValueArg.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/ValueArg.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/ValuesConstraint.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/VersionVisitor.h ================================================ // -*- 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 ================================================ FILE: components/mkspiffs/src/tclap/Visitor.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/XorHandler.h ================================================ /****************************************************************************** * * 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 ================================================ FILE: components/mkspiffs/src/tclap/ZshCompletionOutput.h ================================================ // -*- 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 ================================================ FILE: components/spidriver/component.mk ================================================ # # 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 := . ================================================ FILE: components/spidriver/spi_master_lobo.c ================================================ // 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; } ================================================ FILE: components/spidriver/spi_master_lobo.h ================================================ // 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 ================================================ FILE: components/spiffs/component.mk ================================================ # # Component Makefile # COMPONENT_SRCDIRS := . COMPONENT_ADD_INCLUDEDIRS := . COMPONENT_PRIV_INCLUDEDIRS := ================================================ FILE: components/spiffs/esp_spiffs.c ================================================ /* * 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; } ================================================ FILE: components/spiffs/esp_spiffs.h ================================================ /* * 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__ ================================================ FILE: components/spiffs/list.c ================================================ /* * 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); } ================================================ FILE: components/spiffs/list.h ================================================ /* * 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 */ ================================================ FILE: components/spiffs/mutex.c ================================================ /* * 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; } ================================================ FILE: components/spiffs/mutex.h ================================================ /* * 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 */ ================================================ FILE: components/spiffs/spiffs.h ================================================ /* * 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_ */ ================================================ FILE: components/spiffs/spiffs_cache.c ================================================ /* * 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 ================================================ FILE: components/spiffs/spiffs_check.c ================================================ /* * 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 ================================================ FILE: components/spiffs/spiffs_config.h ================================================ /* * 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_ */ ================================================ FILE: components/spiffs/spiffs_gc.c ================================================ #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 ================================================ FILE: components/spiffs/spiffs_hydrogen.c ================================================ /* * 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 ================================================ FILE: components/spiffs/spiffs_nucleus.c ================================================ #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 ================================================ FILE: components/spiffs/spiffs_nucleus.h ================================================ /* * 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_ */ ================================================ FILE: components/spiffs/spiffs_vfs.c ================================================ /* * 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; } ================================================ FILE: components/spiffs/spiffs_vfs.h ================================================ /* * 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); ================================================ FILE: components/spiffs_image/Makefile.projbuild ================================================ 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 ================================================ FILE: components/spiffs_image/component.mk ================================================ # # Component Makefile # COMPONENT_SRCDIRS := COMPONENT_ADD_INCLUDEDIRS := ================================================ FILE: components/spiffs_image/image/fonts/ocrfont.c ================================================ // 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, // ~ }; ================================================ FILE: components/spiffs_image/image/spiffs.info ================================================ 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 ================================================ FILE: main/Kconfig.projbuild ================================================ 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 ================================================ FILE: main/component.mk ================================================ # # 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 := . ================================================ FILE: main/ePaper.c ================================================ /* 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(1000); } 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); } } ================================================ FILE: main/ePaper.h ================================================ /** ****************************************************************************** * 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 26 #define BUSY_Pin 32 #define RST_Pin 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****/ ================================================ FILE: main/img1.h ================================================ 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, }; ================================================ FILE: main/img2.h ================================================ 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, }; ================================================ FILE: main/img3.h ================================================ 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, }; ================================================ FILE: main/img_hacking.c ================================================ #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, }; ================================================ FILE: partitions_example.csv ================================================ # 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, ================================================ FILE: sdkconfig.defaults ================================================ 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