@@ -9,3 +9,36 @@ install_manifest.txt | |||
compile_commands.json | |||
CTestTestfile.cmake | |||
_deps | |||
.config | |||
*.o | |||
*.pyc | |||
*.a | |||
*.d | |||
# gtags | |||
GTAGS | |||
GRTAGS | |||
GPATH | |||
# emacs | |||
.dir-locals.el | |||
# emacs temp file suffixes | |||
*~ | |||
.#* | |||
\#*# | |||
sdkconfig | |||
sdkconfig.old | |||
sdkconfig.lobo | |||
.cproject | |||
.project | |||
.settings | |||
BUILD | |||
build/ | |||
temp/ | |||
local/ | |||
*.dis | |||
*.elf | |||
*.map | |||
**/.DS_Store |
@@ -1,2 +1,177 @@ | |||
# lilybook | |||
Little ePaper reader for ESP32 | |||
### ePaper library for ESP32 | |||
--- | |||
#### Features | |||
* Support for **GDEH029A1** / **SSD1608** based ePaper modules in 4-wire SPI mode. Support for other controllers will be added later | |||
* **emulated** 4-bit gray scale mode | |||
* **SPI displays oriented SPI driver library** based on *spi-master* driver | |||
* Combined **DMA SPI** transfer mode and **direct SPI** for maximal speed | |||
* **4-bit Grayscale mode** or **1-bit b/w mode** can be selected during runtime | |||
* SPI speeds up to **20 MHz** are tested and works without problems | |||
* **Demo application** included which demonstrates most of the library features | |||
* **Graphics drawing functions**: | |||
* **EPD_drawPixel** Draw pixel at given x,y coordinates | |||
* **EPD_drawLine** Draw line between two points | |||
* **EPD_drawFastVLine**, **EPD_drawFastHLine** Draw vertical or horizontal line of given lenght | |||
* **EPD_drawLineByAngle** Draw line on screen from (x,y) point at given angle | |||
* **EPD_drawRect**, **EPD_fillRect** Draw rectangle on screen or fill given rectangular screen region with color | |||
* **EPD_drawRoundRect**, **EPD_fillRoundRect** Draw rectangle on screen or fill given rectangular screen region with color with rounded corners | |||
* **EPD_drawCircle**, **EPD_fillCircle** Draw or fill circle on screen | |||
* **EPD_drawEllipse**, **EPD_fillEllipse** Draw or fill ellipse on screen | |||
* **EPD_drawTriangel**, **EPD_fillTriangle** Draw or fill triangle on screen | |||
* **EPD_drawArc** Draw circle arc on screen, from ~ to given angles, with given thickness. Can be outlined with different color | |||
* **EPD_drawPolygon** Draw poligon on screen with given number of sides (3~60). Can be outlined with different color and rotated by given angle. | |||
* **Fonts**: | |||
* **fixed** width and proportional fonts are supported; 8 fonts embeded | |||
* unlimited number of **fonts from file** | |||
* **7-segment vector font** with variable width/height is included (only numbers and few characters) | |||
* Proportional fonts can be used in fixed width mode. | |||
* Related functions: | |||
* **EPD_setFont** Set current font from one of embeded fonts or font file | |||
* **EPD_getfontsize** Returns current font height & width in pixels. | |||
* **EPD_getfontheight** Returns current font height in pixels. | |||
* **set_7seg_font_atrib** Set atributes for 7 segment vector font | |||
* **getFontCharacters** Get all font's characters to buffer | |||
* **String write function**: | |||
* **EPD_print** Write text to display. | |||
* Strings can be printed at **any angle**. Rotation of the displayed text depends on *font_ratate* variable (0~360) | |||
* if *font_transparent* variable is set to 1, no background pixels will be printed | |||
* If the text does not fit the screen/window width it will be clipped ( if *text_wrap=0* ), or continued on next line ( if *text_wrap=1* ) | |||
* Two special characters are allowed in strings: *\r* CR (0x0D), clears the display to EOL, *\n* LF (ox0A), continues to the new line, x=0 | |||
* Special values can be entered for X position: | |||
* *CENTER* centers the text | |||
* *RIGHT* right justifies the text horizontaly | |||
* *LASTX* continues from last X position; offset can be used: *LASTX+n* | |||
* Special values can be entered for Y: | |||
* *CENTER* centers the text verticaly | |||
* *BOTTOM* bottom justifies the text | |||
* *LASTY* continues from last Y position; offset can be used: *LASTY+n* | |||
* **EPD_getStringWidth** Returns the string width in pixels based on current font characteristics. Useful for positioning strings on the screen. | |||
* **EPD_clearStringRect** Fills the rectangle occupied by string with current background color | |||
* **Images**: | |||
* **EPD_jpg_image** Decodes and displays JPG images | |||
* Limits: | |||
* Baseline only. Progressive and Lossless JPEG format are not supported. | |||
* Image size: Up to 65520 x 65520 pixels | |||
* Color space: YCbCr three components only. Gray scale image is not supported. | |||
* Sampling factor: 4:4:4, 4:2:2 or 4:2:0. | |||
* Can display the image **from file** or **memory buffer** | |||
* Image can be **scaled** by factor 0 ~ 3 (1/1, 1/2, 1/4 or 1/8) | |||
* Image is displayed from X,Y position on screen/window: | |||
* X: image left position; constants CENTER & RIGHT can be used; *negative* value is accepted | |||
* Y: image top position; constants CENTER & BOTTOM can be used; *negative* value is accepted | |||
* Image is converted to **4-bit Gray Scale mode** | |||
* **Other display functions**: | |||
* **EPD_fillScreen** Fill the whole screen with black, white or gray scale | |||
* **compile_font_file** Function which compiles font c source file to font file which can be used in *EPD_setFont()* function to select external font. Created file have the same name as source file and extension *.fnt* | |||
* **Global wariables** | |||
* **orientation** current screen orientation | |||
* **font_ratate** current font rotate angle (0~395) | |||
* **font_transparent** if not 0 draw fonts transparent | |||
* **font_forceFixed** if not zero force drawing proportional fonts with fixed width | |||
* **text_wrap** if not 0 wrap long text to the new line, else clip | |||
* **_fg** current foreground color for fonts | |||
* **_bg** current background for non transparent fonts | |||
* **_angleOffset** angle offset for arc, polygon and line by angle functions | |||
* **image_debug** print debug messages during image decode if set to 1 | |||
* **cfont** Currently used font structure | |||
* **EPD_X** X position of the next character after EPD_print() function | |||
* **EPD_Y** Y position of the next character after EPD_print() function | |||
* **_gs** use 4-bit Gray scale if set to 1 | |||
* **_width** screen width (larger dimension) in pixels | |||
* **_height** screen height (smaller dimension) in pixels | |||
--- | |||
Full functions **syntax and descriptions** can be found in *EPD.h* and *EPDspi.h* files. | |||
Full **demo application**, well documented, is included, please **analyze it** to learn how to use the library functions. | |||
--- | |||
#### Connecting the display | |||
To run the demo, attach display module to ESP32. Default pins used are: | |||
* mosi: 23 | |||
* sck: 18 | |||
* CS: 5 (display CS) | |||
* DC: 26 (display DC) | |||
* RST: 27 (display RESET) | |||
* BUSY: 32 (display BUSY output) | |||
The display can be powered from 3.3V or from **GPIO pin**. See *EPDspi.h* for configuration options. | |||
**If you want to use different pins, change them in** *EPDspi.h* | |||
Using *make menuconfig* **select tick rate 1000** ( → Component config → FreeRTOS → Tick rate (Hz) ) to get more accurate timings | |||
--- | |||
#### How to build | |||
Configure your esp32 build environment as for **esp-idf examples** | |||
Clone the repository | |||
`git clone https://github.com/loboris/ESP32_ePaper_example.git` | |||
Execute menuconfig and configure your Serial flash config and other settings. Included *sdkconfig.defaults* sets some defaults to be used. | |||
Navigate to **ePaper Display DEMO Configuration** and set **SPIFFS** options. | |||
Select if you want to use **wifi** (recommended) to get the time from **NTP** server and set your WiFi SSID and password. | |||
`make menuconfig` | |||
Make and flash the example. | |||
`make all && make flash` | |||
--- | |||
#### Prepare **SPIFFS** image | |||
*The demo uses some image and font files and it is necessary to flash the spiffs image* | |||
**To flash already prepared image** *components/spiffs_image/spiffs_image.img* execute: | |||
`make copyfs` | |||
--- | |||
You can also prepare different SFPIFFS **image** and flash it to ESP32. | |||
Files to be included on spiffs are already in **components/spiffs_image/image/** directory. You can add or remove the files you want to include. | |||
Then execute: | |||
`make makefs` | |||
to create **spiffs image** in *build* directory **without flashing** to ESP32 | |||
Or execute: | |||
`make flashfs` | |||
to create **spiffs image** in *build* directory and **flash** it to ESP32 | |||
--- | |||
Tested on Waveshare 2.9" ePaper module connected to SparkFun ESP32 Thing board. | |||
![Tested on](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/2.9inch-e-paper-module-4.jpg) | |||
--- | |||
![Fonts](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD-fonts.jpg) | |||
![Rotated](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD-Rotated.jpg) | |||
![7-segFont](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD-7sef_font.jpg) | |||
![Grayscale](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD_Grayscale.jpg) | |||
![No power](https://raw.githubusercontent.com/loboris/ESP32_ePaper_example/master/Documents/EPD-No_power.jpg) |
@@ -0,0 +1,333 @@ | |||
// Default font | |||
// ======================================================================== | |||
// This comes with no warranty, implied or otherwise | |||
// This data structure was designed to support Proportional fonts | |||
// fonts. Individual characters do not have to be multiples of 8 bits wide. | |||
// Any width is fine and does not need to be fixed. | |||
// The data bits are packed to minimize data requirements, but the tradeoff | |||
// is that a header is required per character. | |||
// Header Format: | |||
// ------------------------------------------------ | |||
// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) | |||
// Character Height | |||
// First Character (Reserved. 0x00) | |||
// Number Of Characters (Reserved. 0x00) | |||
// Individual Character Format: | |||
// ---------------------------- | |||
// Character Code | |||
// Adjusted Y Offset (start Y of visible pixels) | |||
// Width (width of the visible pixels) | |||
// Height (height of the visible pixels) | |||
// xOffset (start X of visible pixels) | |||
// xDelta (the distance to move the cursor. Effective width of the character.) | |||
// Data[n] | |||
// NOTE: You can remove any of these characters if they are not needed in | |||
// your application. The first character number in each Glyph indicates | |||
// the ASCII character code. Therefore, these do not have to be sequential. | |||
// Just remove all the content for a particular character to save space. | |||
// ======================================================================== | |||
// dejavu | |||
// Point Size : 12 | |||
// Memory usage : 1158 bytes | |||
// # characters : 95 | |||
const unsigned char tft_DefaultFont[] = | |||
{ | |||
0x00, 0x0B, 0x86, 0x04, | |||
// ' ' | |||
0x20,0x0A,0x00,0x00,0x00,0x04, | |||
// '!' | |||
0x21,0x01,0x01,0x09,0x02,0x05, | |||
0xFD,0x80, | |||
// '"' | |||
0x22,0x01,0x03,0x03,0x01,0x05, | |||
0xB6,0x80, | |||
// '#' | |||
0x23,0x02,0x08,0x08,0x01,0x0A, | |||
0x12,0x14,0x7F,0x24,0x24,0xFE,0x28,0x48, | |||
// '$' | |||
0x24,0x01,0x06,0x0B,0x02,0x08, | |||
0x21,0xCA,0xA8,0xE0,0xE2,0xAA,0x70,0x82,0x00, | |||
// '%' | |||
0x25,0x01,0x0A,0x09,0x00,0x0B, | |||
0x61,0x24,0x89,0x22,0x50,0x6D,0x82,0x91,0x24,0x49,0x21,0x80, | |||
// '&' | |||
0x26,0x01,0x09,0x09,0x01,0x0A, | |||
0x30,0x24,0x10,0x0C,0x05,0x14,0x4A,0x19,0x8C,0x7B,0x00, | |||
// ''' | |||
0x27,0x01,0x01,0x03,0x01,0x03, | |||
0xE0, | |||
// '(' | |||
0x28,0x00,0x03,0x0B,0x01,0x05, | |||
0x69,0x49,0x24,0x48,0x80, | |||
// ')' | |||
0x29,0x00,0x03,0x0B,0x01,0x05, | |||
0x89,0x12,0x49,0x4A,0x00, | |||
// '*' | |||
0x2A,0x01,0x05,0x06,0x01,0x06, | |||
0x25,0x5C,0xEA,0x90, | |||
// '+' | |||
0x2B,0x03,0x07,0x07,0x01,0x0A, | |||
0x10,0x20,0x47,0xF1,0x02,0x04,0x00, | |||
// ',' | |||
0x2C,0x08,0x01,0x03,0x01,0x04, | |||
0xE0, | |||
// '-' | |||
0x2D,0x06,0x03,0x01,0x01,0x04, | |||
0xE0, | |||
// '.' | |||
0x2E,0x08,0x01,0x02,0x01,0x04, | |||
0xC0, | |||
// '/' | |||
0x2F,0x01,0x04,0x0A,0x00,0x04, | |||
0x11,0x22,0x24,0x44,0x88, | |||
// '0' | |||
0x30,0x01,0x06,0x09,0x01,0x08, | |||
0x79,0x28,0x61,0x86,0x18,0x52,0x78, | |||
// '1' | |||
0x31,0x01,0x05,0x09,0x01,0x08, | |||
0xE1,0x08,0x42,0x10,0x84,0xF8, | |||
// '2' | |||
0x32,0x01,0x07,0x09,0x01,0x08, | |||
0x79,0x18,0x10,0x20,0x82,0x08,0x20,0xFC, | |||
// '3' | |||
0x33,0x01,0x06,0x09,0x01,0x08, | |||
0x7A,0x10,0x41,0x38,0x30,0x63,0x78, | |||
// '4' | |||
0x34,0x01,0x06,0x09,0x01,0x08, | |||
0x18,0x62,0x92,0x4A,0x2F,0xC2,0x08, | |||
// '5' | |||
0x35,0x01,0x06,0x09,0x01,0x08, | |||
0xFA,0x08,0x3C,0x0C,0x10,0x63,0x78, | |||
// '6' | |||
0x36,0x01,0x06,0x09,0x01,0x08, | |||
0x39,0x18,0x3E,0xCE,0x18,0x53,0x78, | |||
// '7' | |||
0x37,0x01,0x06,0x09,0x01,0x08, | |||
0xFC,0x10,0x82,0x10,0x42,0x08,0x40, | |||
// '8' | |||
0x38,0x01,0x06,0x09,0x01,0x08, | |||
0x7B,0x38,0x73,0x7B,0x38,0x73,0x78, | |||
// '9' | |||
0x39,0x01,0x06,0x09,0x01,0x08, | |||
0x7B,0x28,0x61,0xCD,0xD0,0x62,0x70, | |||
// ':' | |||
0x3A,0x04,0x01,0x06,0x01,0x04, | |||
0xCC, | |||
// ';' | |||
0x3B,0x04,0x01,0x07,0x01,0x04, | |||
0xCE, | |||
// '<' | |||
0x3C,0x03,0x08,0x06,0x01,0x0A, | |||
0x03,0x1E,0xE0,0xE0,0x1E,0x03, | |||
// '=' | |||
0x3D,0x05,0x08,0x03,0x01,0x0A, | |||
0xFF,0x00,0xFF, | |||
// '>' | |||
0x3E,0x03,0x08,0x06,0x01,0x0A, | |||
0xC0,0x78,0x07,0x07,0x78,0xC0, | |||
// '?' | |||
0x3F,0x01,0x05,0x09,0x00,0x06, | |||
0x74,0x42,0x22,0x10,0x04,0x20, | |||
// '@' | |||
0x40,0x01,0x0B,0x0B,0x01,0x0D, | |||
0x1F,0x06,0x19,0x01,0x46,0x99,0x13,0x22,0x64,0x54,0x6C,0x40,0x04,0x10,0x7C,0x00, | |||
// 'A' | |||
0x41,0x01,0x08,0x09,0x00,0x08, | |||
0x18,0x18,0x24,0x24,0x24,0x42,0x7E,0x42,0x81, | |||
// 'B' | |||
0x42,0x01,0x06,0x09,0x01,0x08, | |||
0xFA,0x18,0x61,0xFA,0x18,0x61,0xF8, | |||
// 'C' | |||
0x43,0x01,0x06,0x09,0x01,0x08, | |||
0x39,0x18,0x20,0x82,0x08,0x11,0x38, | |||
// 'D' | |||
0x44,0x01,0x07,0x09,0x01,0x09, | |||
0xF9,0x0A,0x0C,0x18,0x30,0x60,0xC2,0xF8, | |||
// 'E' | |||
0x45,0x01,0x06,0x09,0x01,0x08, | |||
0xFE,0x08,0x20,0xFE,0x08,0x20,0xFC, | |||
// 'F' | |||
0x46,0x01,0x05,0x09,0x01,0x07, | |||
0xFC,0x21,0x0F,0xC2,0x10,0x80, | |||
// 'G' | |||
0x47,0x01,0x07,0x09,0x01,0x09, | |||
0x3C,0x86,0x04,0x08,0xF0,0x60,0xA1,0x3C, | |||
// 'H' | |||
0x48,0x01,0x07,0x09,0x01,0x09, | |||
0x83,0x06,0x0C,0x1F,0xF0,0x60,0xC1,0x82, | |||
// 'I' | |||
0x49,0x01,0x01,0x09,0x01,0x03, | |||
0xFF,0x80, | |||
// 'J' | |||
0x4A,0x01,0x03,0x0B,0xFF,0x03, | |||
0x24,0x92,0x49,0x27,0x00, | |||
// 'K' | |||
0x4B,0x01,0x07,0x09,0x01,0x07, | |||
0x85,0x12,0x45,0x0C,0x14,0x24,0x44,0x84, | |||
// 'L' | |||
0x4C,0x01,0x05,0x09,0x01,0x06, | |||
0x84,0x21,0x08,0x42,0x10,0xF8, | |||
// 'M' | |||
0x4D,0x01,0x08,0x09,0x01,0x0A, | |||
0x81,0xC3,0xC3,0xA5,0xA5,0x99,0x99,0x81,0x81, | |||
// 'N' | |||
0x4E,0x01,0x07,0x09,0x01,0x09, | |||
0xC3,0x86,0x8D,0x19,0x31,0x62,0xC3,0x86, | |||
// 'O' | |||
0x4F,0x01,0x07,0x09,0x01,0x09, | |||
0x38,0x8A,0x0C,0x18,0x30,0x60,0xA2,0x38, | |||
// 'P' | |||
0x50,0x01,0x06,0x09,0x01,0x08, | |||
0xFA,0x38,0x63,0xFA,0x08,0x20,0x80, | |||
// 'Q' | |||
0x51,0x01,0x07,0x0B,0x01,0x09, | |||
0x38,0x8A,0x0C,0x18,0x30,0x60,0xA2,0x38,0x10,0x10, | |||
// 'R' | |||
0x52,0x01,0x07,0x09,0x01,0x08, | |||
0xF9,0x1A,0x14,0x6F,0x91,0x21,0x42,0x82, | |||
// 'S' | |||
0x53,0x01,0x06,0x09,0x01,0x08, | |||
0x7B,0x18,0x30,0x78,0x30,0x63,0x78, | |||
// 'T' | |||
0x54,0x01,0x07,0x09,0x00,0x07, | |||
0xFE,0x20,0x40,0x81,0x02,0x04,0x08,0x10, | |||
// 'U' | |||
0x55,0x01,0x07,0x09,0x01,0x09, | |||
0x83,0x06,0x0C,0x18,0x30,0x60,0xA2,0x38, | |||
// 'V' | |||
0x56,0x01,0x0A,0x09,0xFF,0x08, | |||
0x40,0x90,0x22,0x10,0x84,0x21,0x04,0x81,0x20,0x30,0x0C,0x00, | |||
// 'W' | |||
0x57,0x01,0x0B,0x09,0x00,0x0B, | |||
0x84,0x28,0x89,0x11,0x27,0x22,0xA8,0x55,0x0E,0xE0,0x88,0x11,0x00, | |||
// 'X' | |||
0x58,0x01,0x07,0x09,0x00,0x07, | |||
0xC6,0x88,0xA1,0xC1,0x07,0x0A,0x22,0x82, | |||
// 'Y' | |||
0x59,0x01,0x07,0x09,0x00,0x07, | |||
0x82,0x89,0x11,0x43,0x82,0x04,0x08,0x10, | |||
// 'Z' | |||
0x5A,0x01,0x07,0x09,0x01,0x09, | |||
0xFE,0x04,0x10,0x41,0x04,0x10,0x40,0xFE, | |||
// '[' | |||
0x5B,0x01,0x02,0x0B,0x02,0x05, | |||
0xEA,0xAA,0xAC, | |||
// '\' | |||
0x5C,0x01,0x04,0x0A,0x00,0x04, | |||
0x88,0x44,0x42,0x22,0x11, | |||
// ']' | |||
0x5D,0x01,0x02,0x0B,0x01,0x05, | |||
0xD5,0x55,0x5C, | |||
// '^' | |||
0x5E,0x01,0x08,0x03,0x01,0x0A, | |||
0x18,0x24,0x42, | |||
// '_' | |||
0x5F,0x0C,0x06,0x01,0x00,0x06, | |||
0xFC, | |||
// '`' | |||
0x60,0x00,0x03,0x02,0x01,0x06, | |||
0x44, | |||
// 'a' | |||
0x61,0x03,0x06,0x07,0x01,0x08, | |||
0x7A,0x30,0x5F,0x86,0x37,0x40, | |||
// 'b' | |||
0x62,0x00,0x06,0x0A,0x01,0x08, | |||
0x82,0x08,0x2E,0xCA,0x18,0x61,0xCE,0xE0, | |||
// 'c' | |||
0x63,0x03,0x05,0x07,0x01,0x07, | |||
0x72,0x61,0x08,0x25,0xC0, | |||
// 'd' | |||
0x64,0x00,0x06,0x0A,0x01,0x08, | |||
0x04,0x10,0x5D,0xCE,0x18,0x61,0xCD,0xD0, | |||
// 'e' | |||
0x65,0x03,0x06,0x07,0x01,0x08, | |||
0x39,0x38,0x7F,0x81,0x13,0x80, | |||
// 'f' | |||
0x66,0x00,0x04,0x0A,0x00,0x04, | |||
0x34,0x4F,0x44,0x44,0x44, | |||
// 'g' | |||
0x67,0x03,0x06,0x0A,0x01,0x08, | |||
0x77,0x38,0x61,0x87,0x37,0x41,0x4C,0xE0, | |||
// 'h' | |||
0x68,0x00,0x06,0x0A,0x01,0x08, | |||
0x82,0x08,0x2E,0xC6,0x18,0x61,0x86,0x10, | |||
// 'i' | |||
0x69,0x01,0x01,0x09,0x01,0x03, | |||
0xBF,0x80, | |||
// 'j' | |||
0x6A,0x01,0x02,0x0C,0x00,0x03, | |||
0x45,0x55,0x56, | |||
// 'k' | |||
0x6B,0x00,0x06,0x0A,0x01,0x07, | |||
0x82,0x08,0x22,0x92,0x8E,0x28,0x92,0x20, | |||
// 'l' | |||
0x6C,0x00,0x01,0x0A,0x01,0x03, | |||
0xFF,0xC0, | |||
// 'm' | |||
0x6D,0x03,0x09,0x07,0x01,0x0B, | |||
0xB3,0x66,0x62,0x31,0x18,0x8C,0x46,0x22, | |||
// 'n' | |||
0x6E,0x03,0x06,0x07,0x01,0x08, | |||
0xBB,0x18,0x61,0x86,0x18,0x40, | |||
// 'o' | |||
0x6F,0x03,0x06,0x07,0x01,0x08, | |||
0x7B,0x38,0x61,0x87,0x37,0x80, | |||
// 'p' | |||
0x70,0x03,0x06,0x0A,0x01,0x08, | |||
0xBB,0x28,0x61,0x87,0x3B,0xA0,0x82,0x00, | |||
// 'q' | |||
0x71,0x03,0x06,0x0A,0x01,0x08, | |||
0x77,0x38,0x61,0x87,0x37,0x41,0x04,0x10, | |||
// 'r' | |||
0x72,0x03,0x04,0x07,0x01,0x05, | |||
0xBC,0x88,0x88,0x80, | |||
// 's' | |||
0x73,0x03,0x06,0x07,0x01,0x07, | |||
0x72,0x28,0x1C,0x0A,0x27,0x00, | |||
// 't' | |||
0x74,0x01,0x04,0x09,0x00,0x05, | |||
0x44,0xF4,0x44,0x44,0x30, | |||
// 'u' | |||
0x75,0x03,0x06,0x07,0x01,0x08, | |||
0x86,0x18,0x61,0x86,0x37,0x40, | |||
// 'v' | |||
0x76,0x03,0x08,0x07,0xFF,0x06, | |||
0x42,0x42,0x24,0x24,0x24,0x18,0x18, | |||
// 'w' | |||
0x77,0x03,0x09,0x07,0x00,0x09, | |||
0x88,0xC4,0x57,0x4A,0xA5,0x51,0x10,0x88, | |||
// 'x' | |||
0x78,0x03,0x06,0x07,0x00,0x06, | |||
0x85,0x24,0x8C,0x49,0x28,0x40, | |||
// 'y' | |||
0x79,0x03,0x08,0x0A,0xFF,0x06, | |||
0x42,0x42,0x24,0x24,0x14,0x18,0x08,0x08,0x10,0x60, | |||
// 'z' | |||
0x7A,0x03,0x05,0x07,0x00,0x05, | |||
0xF8,0x44,0x44,0x43,0xE0, | |||
// '{' | |||
0x7B,0x01,0x05,0x0B,0x02,0x08, | |||
0x19,0x08,0x42,0x60,0x84,0x21,0x06, | |||
// '|' | |||
0x7C,0x01,0x01,0x0C,0x02,0x04, | |||
0xFF,0xF0, | |||
// '}' | |||
0x7D,0x01,0x05,0x0B,0x01,0x08, | |||
0xC1,0x08,0x42,0x0C,0x84,0x21,0x30, | |||
// '~' | |||
0x7E,0x04,0x08,0x03,0x01,0x0A, | |||
0x00,0x71,0x8E, | |||
// Terminator | |||
0xFF | |||
}; |
@@ -0,0 +1,322 @@ | |||
// ============================================================================ | |||
// Proportional font Header Format: | |||
// ------------------------------------------------ | |||
// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) | |||
// Character Height | |||
// First Character (Reserved. 0x00) | |||
// Number Of Characters (Reserved. 0x00) | |||
// Individual Character Format: | |||
// ---------------------------- | |||
// Character Code | |||
// Adjusted Y Offset | |||
// Width | |||
// Height | |||
// xOffset | |||
// xDelta (the distance to move the cursor. Effective width of the character.) | |||
// Data[n] | |||
// NOTE: You can remove any of these characters if they are not needed in | |||
// your application. The first character number in each Glyph indicates | |||
// the ASCII character code. Therefore, these do not have to be sequential. | |||
// Just remove all the content for a particular character to save space. | |||
// ============================================================================ | |||
// DejaVuSans | |||
// Point Size : 18 | |||
// Memory usage : 1828 bytes | |||
// # characters : 95 | |||
const unsigned char tft_Dejavu18x[] = | |||
{ | |||
0x00, 0x12, 0x00, 0x00, | |||
// ' ' | |||
0x20,0x0E,0x00,0x00,0x00,0x06, | |||
// '!' | |||
0x21,0x01,0x02,0x0D,0x03,0x07, | |||
0xFF,0xFF,0xC3,0xC0, | |||
// '"' | |||
0x22,0x01,0x06,0x05,0x01,0x08, | |||
0xCF,0x3C,0xF3,0xCC, | |||
// '#' | |||
0x23,0x00,0x0C,0x0E,0x01,0x0F, | |||
0x04,0x40,0x44,0x0C,0xC0,0xC8,0x7F,0xF7,0xFF,0x09,0x81,0x90,0xFF,0xEF,0xFE,0x13,0x03,0x30,0x32,0x02,0x20, | |||
// '$' | |||
0x24,0x00,0x0A,0x11,0x01,0x0B, | |||
0x08,0x02,0x03,0xE1,0xFC,0xE9,0x32,0x0F,0x81,0xF8,0x0F,0x02,0x60,0x9A,0x2E,0xFF,0x1F,0x80,0x80,0x20,0x08,0x00, | |||
// '%' | |||
0x25,0x01,0x0F,0x0D,0x01,0x11, | |||
0x78,0x10,0x90,0x43,0x31,0x86,0x62,0x0C,0xC8,0x19,0x10,0x1E,0x4F,0x01,0x12,0x02,0x66,0x08,0xCC,0x31,0x98,0x41,0x21,0x03,0xC0, | |||
// '&' | |||
0x26,0x01,0x0C,0x0D,0x01,0x0D, | |||
0x0F,0x01,0xF8,0x30,0x83,0x00,0x38,0x03,0xC0,0x7E,0x6C,0x76,0xC3,0xCC,0x18,0xE1,0xC7,0xFE,0x3E,0x70, | |||
// ''' | |||
0x27,0x01,0x02,0x05,0x01,0x04, | |||
0xFF,0xC0, | |||
// '(' | |||
0x28,0x00,0x04,0x10,0x02,0x07, | |||
0x32,0x66,0x4C,0xCC,0xCC,0xC4,0x66,0x23, | |||
// ')' | |||
0x29,0x00,0x04,0x10,0x01,0x07, | |||
0xC4,0x66,0x23,0x33,0x33,0x32,0x66,0x4C, | |||
// '*' | |||
0x2A,0x01,0x07,0x08,0x01,0x09, | |||
0x11,0x25,0x51,0xC3,0x8A,0xA4,0x88, | |||
// '+' | |||
0x2B,0x02,0x0C,0x0C,0x02,0x0F, | |||
0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x0F,0xFF,0xFF,0xF0,0x60,0x06,0x00,0x60,0x06,0x00,0x60, | |||
// ',' | |||
0x2C,0x0C,0x03,0x04,0x01,0x06, | |||
0x6D,0x40, | |||
// '-' | |||
0x2D,0x08,0x05,0x02,0x01,0x07, | |||
0xFF,0xC0, | |||
// '.' | |||
0x2E,0x0C,0x02,0x02,0x02,0x06, | |||
0xF0, | |||
// '/' | |||
0x2F,0x01,0x06,0x0F,0x00,0x06, | |||
0x0C,0x31,0x86,0x18,0xE3,0x0C,0x31,0xC6,0x18,0x63,0x0C,0x00, | |||
// '0' | |||
0x30,0x01,0x09,0x0D,0x01,0x0B, | |||
0x3E,0x3F,0x98,0xD8,0x3C,0x1E,0x0F,0x07,0x83,0xC1,0xE0,0xD8,0xCF,0xE3,0xE0, | |||
// '1' | |||
0x31,0x01,0x08,0x0D,0x02,0x0B, | |||
0x38,0xF8,0xD8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xFF, | |||
// '2' | |||
0x32,0x01,0x09,0x0D,0x01,0x0B, | |||
0x7C,0x7F,0x21,0xC0,0x60,0x30,0x30,0x18,0x18,0x18,0x18,0x18,0x1F,0xEF,0xF0, | |||
// '3' | |||
0x33,0x01,0x09,0x0D,0x01,0x0B, | |||
0x7E,0x7F,0xA0,0xE0,0x30,0x39,0xF0,0xFC,0x07,0x01,0x80,0xE0,0xFF,0xE7,0xE0, | |||
// '4' | |||
0x34,0x01,0x0A,0x0D,0x01,0x0B, | |||
0x07,0x01,0xC0,0xB0,0x6C,0x13,0x08,0xC6,0x31,0x0C,0xFF,0xFF,0xF0,0x30,0x0C,0x03,0x00, | |||
// '5' | |||
0x35,0x01,0x08,0x0D,0x01,0x0B, | |||
0x7E,0x7E,0x60,0x60,0x7C,0x7E,0x47,0x03,0x03,0x03,0x87,0xFE,0x7C, | |||
// '6' | |||
0x36,0x01,0x09,0x0D,0x01,0x0B, | |||
0x1E,0x1F,0x9C,0x5C,0x0C,0x06,0xF3,0xFD,0xC7,0xC1,0xE0,0xD8,0xEF,0xE1,0xE0, | |||
// '7' | |||
0x37,0x01,0x08,0x0D,0x01,0x0B, | |||
0xFF,0xFF,0x06,0x06,0x06,0x0E,0x0C,0x0C,0x1C,0x18,0x18,0x38,0x30, | |||
// '8' | |||
0x38,0x01,0x09,0x0D,0x01,0x0B, | |||
0x3E,0x3F,0xB8,0xF8,0x3E,0x39,0xF1,0xFD,0xC7,0xC1,0xE0,0xF8,0xEF,0xE3,0xE0, | |||
// '9' | |||
0x39,0x01,0x09,0x0D,0x01,0x0B, | |||
0x3C,0x3F,0xB8,0xD8,0x3C,0x1F,0x1D,0xFE,0x7B,0x01,0x81,0xD1,0xCF,0xC3,0xC0, | |||
// ':' | |||
0x3A,0x05,0x02,0x09,0x02,0x06, | |||
0xF0,0x03,0xC0, | |||
// ';' | |||
0x3B,0x05,0x03,0x0B,0x01,0x06, | |||
0x6C,0x00,0x03,0x6A,0x00, | |||
// '<' | |||
0x3C,0x04,0x0B,0x0A,0x02,0x0F, | |||
0x00,0x20,0x3C,0x1F,0x1F,0x0F,0x81,0xF0,0x0F,0x80,0x3E,0x01,0xE0,0x04, | |||
// '=' | |||
0x3D,0x05,0x0B,0x06,0x02,0x0F, | |||
0xFF,0xFF,0xFC,0x00,0x00,0x0F,0xFF,0xFF,0xC0, | |||
// '>' | |||
0x3E,0x04,0x0B,0x0A,0x02,0x0F, | |||
0x80,0x1E,0x01,0xF0,0x07,0xC0,0x3E,0x07,0xC3,0xE3,0xE0,0xF0,0x10,0x00, | |||
// '?' | |||
0x3F,0x01,0x07,0x0D,0x01,0x0A, | |||
0x79,0xFA,0x38,0x30,0x61,0x86,0x18,0x30,0x60,0x01,0x83,0x00, | |||
// '@' | |||
0x40,0x01,0x10,0x10,0x01,0x12, | |||
0x07,0xE0,0x1F,0xF8,0x3C,0x1C,0x70,0x06,0x60,0x07,0xE3,0x63,0xC7,0xE3,0xC6,0x63,0xC6,0x66,0xC7,0xFC,0xE3,0x70,0x60,0x00,0x70,0x00,0x3C,0x30,0x1F,0xF0,0x07,0xC0, | |||
// 'A' | |||
0x41,0x01,0x0C,0x0D,0x00,0x0C, | |||
0x06,0x00,0x60,0x0F,0x00,0xF0,0x19,0x81,0x98,0x19,0x83,0x0C,0x3F,0xC7,0xFE,0x60,0x66,0x06,0xC0,0x30, | |||
// 'B' | |||
0x42,0x01,0x09,0x0D,0x02,0x0C, | |||
0xFC,0x7F,0xB0,0xD8,0x6C,0x37,0xF3,0xF9,0x86,0xC1,0xE0,0xF0,0xFF,0xEF,0xE0, | |||
// 'C' | |||
0x43,0x01,0x0B,0x0D,0x01,0x0D, | |||
0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x0C,0x01,0xC0,0x9F,0xF0,0xFC, | |||
// 'D' | |||
0x44,0x01,0x0B,0x0D,0x02,0x0E, | |||
0xFE,0x1F,0xF3,0x07,0x60,0x6C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x1B,0x07,0x7F,0xCF,0xE0, | |||
// 'E' | |||
0x45,0x01,0x08,0x0D,0x02,0x0B, | |||
0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, | |||
// 'F' | |||
0x46,0x01,0x08,0x0D,0x02,0x0A, | |||
0xFF,0xFF,0xC0,0xC0,0xC0,0xFE,0xFE,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, | |||
// 'G' | |||
0x47,0x01,0x0B,0x0D,0x01,0x0E, | |||
0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x87,0xF0,0xFE,0x03,0xC0,0x6C,0x0D,0xC1,0x9F,0xE0,0xF8, | |||
// 'H' | |||
0x48,0x01,0x0A,0x0D,0x02,0x0E, | |||
0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xFF,0xFF,0xFF,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xC0, | |||
// 'I' | |||
0x49,0x01,0x02,0x0D,0x02,0x06, | |||
0xFF,0xFF,0xFF,0xC0, | |||
// 'J' | |||
0x4A,0x01,0x05,0x11,0xFF,0x06, | |||
0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0x8C,0xFE,0xE0, | |||
// 'K' | |||
0x4B,0x01,0x0B,0x0D,0x02,0x0C, | |||
0xC1,0x98,0x63,0x18,0x66,0x0D,0x81,0xE0,0x3C,0x06,0xC0,0xCC,0x18,0xC3,0x0C,0x60,0xCC,0x0C, | |||
// 'L' | |||
0x4C,0x01,0x08,0x0D,0x02,0x0A, | |||
0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, | |||
// 'M' | |||
0x4D,0x01,0x0C,0x0D,0x02,0x10, | |||
0xE0,0x7F,0x0F,0xF0,0xFD,0x8B,0xD9,0xBD,0x9B,0xCF,0x3C,0xF3,0xC6,0x3C,0x63,0xC0,0x3C,0x03,0xC0,0x30, | |||
// 'N' | |||
0x4E,0x01,0x0A,0x0D,0x02,0x0E, | |||
0xE0,0xF8,0x3F,0x0F,0xC3,0xD8,0xF6,0x3C,0xCF,0x1B,0xC6,0xF0,0xFC,0x3F,0x07,0xC1,0xC0, | |||
// 'O' | |||
0x4F,0x01,0x0C,0x0D,0x01,0x0E, | |||
0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80, | |||
// 'P' | |||
0x50,0x01,0x08,0x0D,0x02,0x0B, | |||
0xFC,0xFE,0xC7,0xC3,0xC3,0xC7,0xFE,0xFC,0xC0,0xC0,0xC0,0xC0,0xC0, | |||
// 'Q' | |||
0x51,0x01,0x0C,0x0F,0x01,0x0E, | |||
0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80,0x18,0x00,0xC0, | |||
// 'R' | |||
0x52,0x01,0x0A,0x0D,0x02,0x0D, | |||
0xFC,0x3F,0x8C,0x73,0x0C,0xC3,0x31,0xCF,0xE3,0xF0,0xC6,0x30,0xCC,0x33,0x06,0xC1,0xC0, | |||
// 'S' | |||
0x53,0x01,0x0A,0x0D,0x01,0x0B, | |||
0x3E,0x1F,0xCE,0x13,0x00,0xC0,0x1F,0x03,0xF0,0x0E,0x01,0x80,0x68,0x3B,0xFC,0x7E,0x00, | |||
// 'T' | |||
0x54,0x01,0x0C,0x0D,0x00,0x0C, | |||
0xFF,0xFF,0xFF,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00, | |||
// 'U' | |||
0x55,0x01,0x0A,0x0D,0x02,0x0E, | |||
0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x36,0x19,0xFE,0x1E,0x00, | |||
// 'V' | |||
0x56,0x01,0x0C,0x0D,0x00,0x0C, | |||
0xC0,0x36,0x06,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60,0x06,0x00, | |||
// 'W' | |||
0x57,0x01,0x11,0x0D,0x01,0x13, | |||
0xC1,0xC1,0xE0,0xE0,0xD8,0xF8,0xCC,0x6C,0x66,0x36,0x33,0x1B,0x18,0xD8,0xD8,0x6C,0x6C,0x36,0x36,0x1F,0x1F,0x07,0x07,0x03,0x83,0x81,0xC1,0xC0, | |||
// 'X' | |||
0x58,0x01,0x0B,0x0D,0x01,0x0D, | |||
0x70,0xE6,0x18,0xE6,0x0D,0xC0,0xF0,0x1C,0x03,0x80,0x78,0x1B,0x07,0x30,0xC7,0x30,0x6E,0x0E, | |||
// 'Y' | |||
0x59,0x01,0x0C,0x0D,0x00,0x0C, | |||
0xE0,0x76,0x06,0x30,0xC1,0x98,0x19,0x80,0xF0,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00, | |||
// 'Z' | |||
0x5A,0x01,0x0B,0x0D,0x01,0x0D, | |||
0xFF,0xFF,0xFC,0x07,0x01,0xC0,0x30,0x0E,0x03,0x80,0xE0,0x18,0x06,0x01,0xC0,0x7F,0xFF,0xFE, | |||
// '[' | |||
0x5B,0x00,0x04,0x10,0x01,0x07, | |||
0xFF,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xFF, | |||
// '\' | |||
0x5C,0x01,0x06,0x0F,0x00,0x06, | |||
0xC3,0x06,0x18,0x61,0xC3,0x0C,0x30,0xE1,0x86,0x18,0x30,0xC0, | |||
// ']' | |||
0x5D,0x00,0x04,0x10,0x02,0x07, | |||
0xFF,0x33,0x33,0x33,0x33,0x33,0x33,0xFF, | |||
// '^' | |||
0x5E,0x01,0x0B,0x05,0x02,0x0F, | |||
0x0E,0x03,0xE0,0xC6,0x30,0x6C,0x06, | |||
// '_' | |||
0x5F,0x10,0x09,0x02,0x00,0x09, | |||
0xFF,0xFF,0xC0, | |||
// '`' | |||
0x60,0x00,0x04,0x03,0x02,0x09, | |||
0xC6,0x30, | |||
// 'a' | |||
0x61,0x04,0x08,0x0A,0x01,0x0A, | |||
0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, | |||
// 'b' | |||
0x62,0x00,0x09,0x0E,0x02,0x0B, | |||
0xC0,0x60,0x30,0x18,0x0D,0xE7,0xFB,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x7F,0xF6,0xF0, | |||
// 'c' | |||
0x63,0x04,0x08,0x0A,0x01,0x09, | |||
0x1E,0x7F,0x61,0xC0,0xC0,0xC0,0xC0,0x61,0x7F,0x1E, | |||
// 'd' | |||
0x64,0x00,0x09,0x0E,0x01,0x0B, | |||
0x01,0x80,0xC0,0x60,0x33,0xDB,0xFF,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x77,0xF9,0xEC, | |||
// 'e' | |||
0x65,0x04,0x0A,0x0A,0x01,0x0B, | |||
0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, | |||
// 'f' | |||
0x66,0x00,0x07,0x0E,0x00,0x06, | |||
0x1E,0x7C,0xC1,0x8F,0xFF,0xCC,0x18,0x30,0x60,0xC1,0x83,0x06,0x00, | |||
// 'g' | |||
0x67,0x04,0x09,0x0E,0x01,0x0B, | |||
0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x68,0x67,0xF1,0xF0, | |||
// 'h' | |||
0x68,0x00,0x08,0x0E,0x02,0x0B, | |||
0xC0,0xC0,0xC0,0xC0,0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, | |||
// 'i' | |||
0x69,0x00,0x02,0x0E,0x02,0x05, | |||
0xF0,0xFF,0xFF,0xF0, | |||
// 'j' | |||
0x6A,0x00,0x04,0x12,0x00,0x05, | |||
0x33,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0xEC, | |||
// 'k' | |||
0x6B,0x00,0x09,0x0E,0x02,0x0A, | |||
0xC0,0x60,0x30,0x18,0x0C,0x36,0x33,0x31,0xB0,0xF0,0x78,0x36,0x19,0x8C,0x66,0x18, | |||
// 'l' | |||
0x6C,0x00,0x02,0x0E,0x02,0x05, | |||
0xFF,0xFF,0xFF,0xF0, | |||
// 'm' | |||
0x6D,0x04,0x0E,0x0A,0x02,0x11, | |||
0xDC,0x7B,0xFB,0xEE,0x79,0xF0,0xC3,0xC3,0x0F,0x0C,0x3C,0x30,0xF0,0xC3,0xC3,0x0F,0x0C,0x30, | |||
// 'n' | |||
0x6E,0x04,0x08,0x0A,0x02,0x0B, | |||
0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, | |||
// 'o' | |||
0x6F,0x04,0x0A,0x0A,0x01,0x0B, | |||
0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, | |||
// 'p' | |||
0x70,0x04,0x09,0x0E,0x02,0x0B, | |||
0xDE,0x7F,0xB8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0xFF,0x6F,0x30,0x18,0x0C,0x06,0x00, | |||
// 'q' | |||
0x71,0x04,0x09,0x0E,0x01,0x0B, | |||
0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x60,0x30,0x18,0x0C, | |||
// 'r' | |||
0x72,0x04,0x06,0x0A,0x02,0x08, | |||
0xDF,0xFE,0x30,0xC3,0x0C,0x30,0xC3,0x00, | |||
// 's' | |||
0x73,0x04,0x08,0x0A,0x01,0x08, | |||
0x7C,0xFE,0xC2,0xE0,0x7C,0x1E,0x06,0x86,0xFE,0x78, | |||
// 't' | |||
0x74,0x01,0x06,0x0D,0x01,0x07, | |||
0x61,0x86,0x3F,0xFD,0x86,0x18,0x61,0x86,0x1F,0x3C, | |||
// 'u' | |||
0x75,0x04,0x08,0x0A,0x02,0x0B, | |||
0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, | |||
// 'v' | |||
0x76,0x04,0x0C,0x0A,0x00,0x0B, | |||
0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60, | |||
// 'w' | |||
0x77,0x04,0x0F,0x0A,0x01,0x10, | |||
0x63,0x8C,0xC7,0x19,0x8E,0x31,0xB6,0xC3,0x6D,0x86,0xDB,0x0F,0x1E,0x0E,0x38,0x1C,0x70,0x38,0xE0, | |||
// 'x' | |||
0x78,0x04,0x0A,0x0A,0x01,0x0B, | |||
0xE1,0xD8,0x63,0x30,0xCC,0x1E,0x07,0x83,0x30,0xCC,0x61,0xB8,0x70, | |||
// 'y' | |||
0x79,0x04,0x0C,0x0E,0x00,0x0B, | |||
0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80, | |||
// 'z' | |||
0x7A,0x04,0x08,0x0A,0x01,0x09, | |||
0xFF,0xFF,0x06,0x0C,0x1C,0x38,0x30,0x70,0xFF,0xFF, | |||
// '{' | |||
0x7B,0x00,0x08,0x11,0x02,0x0B, | |||
0x0F,0x1F,0x18,0x18,0x18,0x18,0x38,0xF0,0xF0,0x38,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F, | |||
// '|' | |||
0x7C,0x00,0x02,0x12,0x02,0x06, | |||
0xFF,0xFF,0xFF,0xFF,0xF0, | |||
// '}' | |||
0x7D,0x00,0x08,0x11,0x02,0x0B, | |||
0xF0,0xF8,0x18,0x18,0x18,0x18,0x1C,0x0F,0x0F,0x1C,0x18,0x18,0x18,0x18,0x18,0xF8,0xF0, | |||
// '~' | |||
0x7E,0x05,0x0B,0x05,0x02,0x0F, | |||
0x00,0x0F,0x87,0xFF,0xC3,0xE0,0x00, | |||
// Terminator | |||
0xFF | |||
}; |
@@ -0,0 +1,331 @@ | |||
// ======================================================================== | |||
// This comes with no warranty, implied or otherwise | |||
// This data structure was designed to support Proportional fonts | |||
// fonts. Individual characters do not have to be multiples of 8 bits wide. | |||
// Any width is fine and does not need to be fixed. | |||
// The data bits are packed to minimize data requirements, but the tradeoff | |||
// is that a header is required per character. | |||
// Header Format: | |||
// ------------------------------------------------ | |||
// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) | |||
// Character Height | |||
// First Character (Reserved. 0x00) | |||
// Number Of Characters (Reserved. 0x00) | |||
// Individual Character Format: | |||
// ---------------------------- | |||
// Character Code | |||
// Adjusted Y Offset | |||
// Width | |||
// Height | |||
// xOffset | |||
// xDelta (the distance to move the cursor. Effective width of the character.) | |||
// Data[n] | |||
// NOTE: You can remove any of these characters if they are not needed in | |||
// your application. The first character number in each Glyph indicates | |||
// the ASCII character code. Therefore, these do not have to be sequential. | |||
// Just remove all the content for a particular character to save space. | |||
// ======================================================================== | |||
// dejavu | |||
// Point Size : 24 | |||
// Memory usage : 2724 bytes | |||
// # characters : 95 | |||
const unsigned char tft_Dejavu24[] = | |||
{ | |||
0x00, 0x17, 0x00, 0x00, | |||
// ' ' | |||
0x20,0x13,0x00,0x00,0x00,0x08, | |||
// '!' | |||
0x21,0x01,0x02,0x12,0x04,0x0A, | |||
0xFF,0xFF,0xFF,0x03,0xF0, | |||
// '"' | |||
0x22,0x01,0x06,0x07,0x02,0x0B, | |||
0xCF,0x3C,0xF3,0xCF,0x3C,0xC0, | |||
// '#' | |||
0x23,0x01,0x10,0x12,0x02,0x14, | |||
0x03,0x08,0x03,0x18,0x03,0x18,0x03,0x18,0x02,0x18,0x7F,0xFF,0x7F,0xFF,0x06,0x30,0x04,0x30,0x0C,0x20,0x0C,0x60,0xFF,0xFE,0xFF,0xFE,0x18,0x40,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x10,0xC0, | |||
// '$' | |||
0x24,0x01,0x0B,0x16,0x02,0x0F, | |||
0x04,0x00,0x80,0x10,0x0F,0xC7,0xFD,0xC8,0xB1,0x06,0x20,0xE4,0x0F,0x80,0xFE,0x03,0xE0,0x4E,0x08,0xC1,0x1E,0x27,0xFF,0xC7,0xE0,0x10,0x02,0x00,0x40,0x08,0x00, | |||
// '%' | |||
0x25,0x01,0x14,0x12,0x01,0x17, | |||
0x3C,0x03,0x06,0x60,0x60,0xC3,0x06,0x0C,0x30,0xC0,0xC3,0x1C,0x0C,0x31,0x80,0xC3,0x38,0x0C,0x33,0x00,0x66,0x63,0xC3,0xC6,0x66,0x00,0xCC,0x30,0x1C,0xC3,0x01,0x8C,0x30,0x38,0xC3,0x03,0x0C,0x30,0x60,0xC3,0x06,0x06,0x60,0xC0,0x3C, | |||
// '&' | |||
0x26,0x01,0x10,0x12,0x01,0x13, | |||
0x07,0xC0,0x1F,0xE0,0x38,0x20,0x30,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x1C,0x00,0x3E,0x00,0x77,0x06,0xE3,0x86,0xC1,0xCC,0xC0,0xFC,0xC0,0x78,0xE0,0x78,0x70,0xFC,0x3F,0xCE,0x0F,0x87, | |||
// ''' | |||
0x27,0x01,0x02,0x07,0x02,0x07, | |||
0xFF,0xFC, | |||
// '(' | |||
0x28,0x01,0x05,0x15,0x02,0x09, | |||
0x19,0x8C,0xC6,0x31,0x18,0xC6,0x31,0x8C,0x61,0x0C,0x63,0x0C,0x61,0x80, | |||
// ')' | |||
0x29,0x01,0x05,0x15,0x02,0x09, | |||
0xC3,0x18,0x63,0x18,0x43,0x18,0xC6,0x31,0x8C,0x46,0x31,0x98,0xCC,0x00, | |||
// '*' | |||
0x2A,0x01,0x0B,0x0A,0x00,0x0C, | |||
0x04,0x00,0x83,0x11,0xBA,0xE1,0xF0,0x3E,0x1D,0x76,0x23,0x04,0x00,0x80, | |||
// '+' | |||
0x2B,0x03,0x10,0x10,0x03,0x14, | |||
0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0xFF,0xFF,0xFF,0xFF,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80, | |||
// ',' | |||
0x2C,0x10,0x03,0x06,0x02,0x08, | |||
0x6D,0xBD,0x80, | |||
// '-' | |||
0x2D,0x0B,0x06,0x02,0x01,0x09, | |||
0xFF,0xF0, | |||
// '.' | |||
0x2E,0x10,0x02,0x03,0x03,0x08, | |||
0xFC, | |||
// '/' | |||
0x2F,0x01,0x08,0x14,0x00,0x08, | |||
0x03,0x07,0x06,0x06,0x06,0x0C,0x0C,0x0C,0x18,0x18,0x18,0x18,0x30,0x30,0x30,0x60,0x60,0x60,0xE0,0xC0, | |||
// '0' | |||
0x30,0x01,0x0C,0x12,0x02,0x0F, | |||
0x0F,0x03,0xFC,0x70,0xE6,0x06,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x60,0x67,0x0E,0x3F,0xC0,0xF0, | |||
// '1' | |||
0x31,0x01,0x0A,0x12,0x03,0x0F, | |||
0x3C,0x3F,0x0C,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0, | |||
// '2' | |||
0x32,0x01,0x0C,0x12,0x02,0x0F, | |||
0x3F,0x0F,0xF8,0xC1,0xC0,0x0E,0x00,0x60,0x06,0x00,0x60,0x0C,0x01,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xEF,0xFE, | |||
// '3' | |||
0x33,0x01,0x0C,0x12,0x02,0x0F, | |||
0x3F,0x07,0xFC,0x41,0xC0,0x06,0x00,0x60,0x06,0x00,0x60,0x0C,0x1F,0x81,0xFC,0x00,0xE0,0x07,0x00,0x30,0x03,0x00,0x78,0x0E,0xFF,0xC3,0xF0, | |||
// '4' | |||
0x34,0x01,0x0D,0x12,0x01,0x0F, | |||
0x01,0xC0,0x1E,0x00,0xB0,0x0D,0x80,0xCC,0x06,0x60,0x63,0x03,0x18,0x30,0xC3,0x06,0x18,0x31,0x81,0x8F,0xFF,0xFF,0xFC,0x03,0x00,0x18,0x00,0xC0,0x06,0x00, | |||
// '5' | |||
0x35,0x01,0x0B,0x12,0x02,0x0F, | |||
0x7F,0xCF,0xF9,0x80,0x30,0x06,0x00,0xC0,0x1F,0xC3,0xFC,0x41,0xC0,0x1C,0x01,0x80,0x30,0x06,0x00,0xC0,0x3C,0x0E,0xFF,0x8F,0xC0, | |||
// '6' | |||
0x36,0x01,0x0C,0x12,0x02,0x0F, | |||
0x07,0xC1,0xFE,0x38,0x27,0x00,0x60,0x0C,0x00,0xCF,0x8D,0xFC,0xF8,0xEF,0x07,0xE0,0x3E,0x03,0xE0,0x36,0x03,0x70,0x77,0x8E,0x3F,0xC0,0xF8, | |||
// '7' | |||
0x37,0x01,0x0B,0x12,0x02,0x0F, | |||
0xFF,0xFF,0xFC,0x03,0x00,0x60,0x1C,0x03,0x00,0x60,0x18,0x03,0x00,0xE0,0x18,0x03,0x00,0xC0,0x18,0x07,0x00,0xC0,0x18,0x06,0x00, | |||
// '8' | |||
0x38,0x01,0x0C,0x12,0x02,0x0F, | |||
0x1F,0x87,0xFE,0x70,0xEC,0x03,0xC0,0x3C,0x03,0xC0,0x37,0x0E,0x3F,0xC3,0xFC,0x70,0xEC,0x03,0xC0,0x3C,0x03,0xC0,0x37,0x0E,0x7F,0xE1,0xF8, | |||
// '9' | |||
0x39,0x01,0x0C,0x12,0x02,0x0F, | |||
0x1F,0x03,0xFC,0x71,0xCE,0x0E,0xC0,0x6C,0x07,0xC0,0x7C,0x07,0xE0,0xF7,0x1F,0x3F,0xB1,0xF3,0x00,0x30,0x06,0x00,0xE4,0x1C,0x7F,0x83,0xE0, | |||
// ':' | |||
0x3A,0x07,0x02,0x0C,0x03,0x08, | |||
0xFC,0x00,0x3F, | |||
// ';' | |||
0x3B,0x07,0x03,0x0F,0x02,0x08, | |||
0x6D,0x80,0x00,0x0D,0xB7,0xB0, | |||
// '<' | |||
0x3C,0x05,0x0F,0x0D,0x03,0x14, | |||
0x00,0x02,0x00,0x3C,0x03,0xF0,0x3F,0x01,0xF8,0x1F,0x80,0x3C,0x00,0x7E,0x00,0x1F,0x80,0x0F,0xC0,0x03,0xF0,0x00,0xF0,0x00,0x20, | |||
// '=' | |||
0x3D,0x08,0x0F,0x07,0x03,0x14, | |||
0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0x80, | |||
// '>' | |||
0x3E,0x05,0x0F,0x0D,0x03,0x14, | |||
0x80,0x01,0xE0,0x01,0xF8,0x00,0x7E,0x00,0x3F,0x00,0x0F,0xC0,0x07,0x80,0x3F,0x03,0xF0,0x1F,0x81,0xF8,0x07,0x80,0x08,0x00,0x00, | |||
// '?' | |||
0x3F,0x01,0x09,0x12,0x02,0x0D, | |||
0x3E,0x3F,0xB0,0xF0,0x30,0x18,0x0C,0x0C,0x0E,0x0E,0x0E,0x06,0x03,0x01,0x80,0x00,0x00,0x30,0x18,0x0C,0x00, | |||
// '@' | |||
0x40,0x02,0x15,0x15,0x02,0x18, | |||
0x00,0xFC,0x00,0x3F,0xF8,0x03,0xC0,0xF0,0x38,0x01,0xC3,0x80,0x07,0x38,0x79,0x99,0x8F,0xEC,0xFC,0x71,0xE3,0xC7,0x07,0x1E,0x30,0x18,0xF1,0x80,0xC7,0x8C,0x06,0x3C,0x70,0x73,0x71,0xC7,0xB9,0x8F,0xEF,0x8E,0x1E,0x70,0x38,0x00,0x00,0xE0,0x04,0x03,0xC0,0xE0,0x0F,0xFE,0x00,0x0F,0x80,0x00, | |||
// 'A' | |||
0x41,0x01,0x10,0x12,0x00,0x10, | |||
0x03,0xC0,0x03,0xC0,0x03,0xC0,0x07,0xE0,0x06,0x60,0x06,0x60,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x18,0x18,0x18,0x18,0x38,0x1C,0x3F,0xFC,0x3F,0xFC,0x60,0x06,0x60,0x06,0x60,0x06,0xC0,0x03, | |||
// 'B' | |||
0x42,0x01,0x0C,0x12,0x02,0x10, | |||
0xFF,0x0F,0xFC,0xC0,0xEC,0x06,0xC0,0x6C,0x06,0xC0,0x6C,0x0C,0xFF,0x8F,0xFC,0xC0,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x06,0xFF,0xEF,0xF8, | |||
// 'C' | |||
0x43,0x01,0x0E,0x12,0x01,0x11, | |||
0x07,0xE0,0x7F,0xE3,0xC1,0xDC,0x01,0x60,0x01,0x80,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0x60,0x01,0x80,0x07,0x00,0x4F,0x07,0x1F,0xF8,0x1F,0x80, | |||
// 'D' | |||
0x44,0x01,0x0F,0x12,0x02,0x12, | |||
0xFF,0x81,0xFF,0xE3,0x01,0xE6,0x00,0xEC,0x00,0xD8,0x01,0xF0,0x01,0xE0,0x03,0xC0,0x07,0x80,0x0F,0x00,0x1E,0x00,0x3C,0x00,0xF8,0x01,0xB0,0x07,0x60,0x3C,0xFF,0xF1,0xFF,0x00, | |||
// 'E' | |||
0x45,0x01,0x0B,0x12,0x02,0x0F, | |||
0xFF,0xFF,0xFF,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xDF,0xFB,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xFF,0xFC, | |||
// 'F' | |||
0x46,0x01,0x0A,0x12,0x02,0x0E, | |||
0xFF,0xFF,0xFC,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xFF,0xBF,0xEC,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x00, | |||
// 'G' | |||
0x47,0x01,0x0F,0x12,0x01,0x13, | |||
0x07,0xE0,0x3F,0xF0,0xE0,0x73,0x80,0x26,0x00,0x1C,0x00,0x30,0x00,0x60,0x00,0xC0,0x7F,0x80,0xFF,0x00,0x1E,0x00,0x36,0x00,0x6C,0x00,0xDC,0x01,0x9E,0x07,0x1F,0xFC,0x0F,0xE0, | |||
// 'H' | |||
0x48,0x01,0x0D,0x12,0x02,0x12, | |||
0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xFF,0xFF,0xFF,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xC0, | |||
// 'I' | |||
0x49,0x01,0x02,0x12,0x02,0x07, | |||
0xFF,0xFF,0xFF,0xFF,0xF0, | |||
// 'J' | |||
0x4A,0x01,0x06,0x17,0xFE,0x07, | |||
0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x1B,0xEF,0x00, | |||
// 'K' | |||
0x4B,0x01,0x0F,0x12,0x02,0x10, | |||
0xC0,0x71,0x81,0xC3,0x07,0x06,0x1C,0x0C,0x70,0x19,0xC0,0x37,0x00,0x7C,0x00,0xF8,0x01,0xB0,0x03,0x38,0x06,0x38,0x0C,0x38,0x18,0x38,0x30,0x38,0x60,0x38,0xC0,0x39,0x80,0x38, | |||
// 'L' | |||
0x4C,0x01,0x0B,0x12,0x02,0x0D, | |||
0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xFF,0xFC, | |||
// 'M' | |||
0x4D,0x01,0x10,0x12,0x02,0x15, | |||
0xE0,0x07,0xF0,0x0F,0xF0,0x0F,0xF8,0x1F,0xD8,0x1B,0xD8,0x1B,0xCC,0x33,0xCC,0x33,0xCC,0x33,0xC6,0x63,0xC6,0x63,0xC7,0xE3,0xC3,0xC3,0xC3,0xC3,0xC1,0x83,0xC0,0x03,0xC0,0x03,0xC0,0x03, | |||
// 'N' | |||
0x4E,0x01,0x0D,0x12,0x02,0x12, | |||
0xE0,0x1F,0x80,0xFC,0x07,0xF0,0x3D,0x81,0xE6,0x0F,0x30,0x78,0xC3,0xC6,0x1E,0x18,0xF0,0xC7,0x83,0x3C,0x19,0xE0,0x6F,0x03,0x78,0x0F,0xC0,0x7E,0x01,0xC0, | |||
// 'O' | |||
0x4F,0x01,0x10,0x12,0x01,0x13, | |||
0x07,0xE0,0x1F,0xF8,0x3C,0x3C,0x70,0x0E,0x60,0x06,0x60,0x06,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0x60,0x06,0x60,0x06,0x70,0x0E,0x3C,0x3C,0x1F,0xF8,0x07,0xE0, | |||
// 'P' | |||
0x50,0x01,0x0B,0x12,0x02,0x0E, | |||
0xFF,0x1F,0xFB,0x07,0x60,0x3C,0x07,0x80,0xF0,0x1E,0x0E,0xFF,0xDF,0xE3,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x00, | |||
// 'Q' | |||
0x51,0x01,0x10,0x15,0x01,0x13, | |||
0x07,0xE0,0x1F,0xF8,0x3C,0x3C,0x70,0x0E,0x60,0x06,0x60,0x06,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0x60,0x07,0x60,0x06,0x70,0x0E,0x3C,0x3C,0x1F,0xF8,0x07,0xF0,0x00,0x38,0x00,0x18,0x00,0x0C, | |||
// 'R' | |||
0x52,0x01,0x0D,0x12,0x02,0x11, | |||
0xFF,0x07,0xFE,0x30,0x31,0x80,0xCC,0x06,0x60,0x33,0x01,0x98,0x18,0xFF,0xC7,0xFC,0x30,0x71,0x81,0x8C,0x06,0x60,0x33,0x01,0xD8,0x06,0xC0,0x36,0x00,0xC0, | |||
// 'S' | |||
0x53,0x01,0x0C,0x12,0x02,0x0F, | |||
0x1F,0x87,0xFE,0x70,0x6C,0x00,0xC0,0x0C,0x00,0xC0,0x07,0x00,0x7F,0x01,0xFC,0x00,0xE0,0x07,0x00,0x30,0x03,0x00,0x3C,0x0E,0xFF,0xE3,0xF8, | |||
// 'T' | |||
0x54,0x01,0x0E,0x12,0x00,0x0F, | |||
0xFF,0xFF,0xFF,0xF0,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00, | |||
// 'U' | |||
0x55,0x01,0x0D,0x12,0x02,0x12, | |||
0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0D,0x80,0xCE,0x0E,0x3F,0xE0,0x7C,0x00, | |||
// 'V' | |||
0x56,0x01,0x10,0x12,0x00,0x10, | |||
0xC0,0x03,0x60,0x06,0x60,0x06,0x60,0x06,0x30,0x0C,0x30,0x0C,0x38,0x1C,0x18,0x18,0x18,0x18,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x06,0x60,0x06,0x60,0x07,0x60,0x03,0xC0,0x03,0xC0,0x03,0xC0, | |||
// 'W' | |||
0x57,0x01,0x16,0x12,0x01,0x18, | |||
0xC0,0x78,0x0F,0x01,0xE0,0x36,0x07,0x81,0x98,0x1E,0x06,0x60,0xEC,0x19,0x83,0x30,0x63,0x0C,0xC3,0x0C,0x33,0x0C,0x30,0xCE,0x30,0xC6,0x18,0xC1,0x98,0x66,0x06,0x61,0x98,0x19,0x86,0x60,0x6C,0x0D,0x80,0xF0,0x3C,0x03,0xC0,0xF0,0x0F,0x03,0xC0,0x38,0x07,0x00, | |||
// 'X' | |||
0x58,0x01,0x0F,0x12,0x01,0x11, | |||
0x70,0x0E,0x60,0x18,0x60,0x60,0xE1,0xC0,0xC7,0x00,0xCC,0x01,0xF0,0x01,0xE0,0x03,0x80,0x07,0x80,0x1F,0x00,0x37,0x00,0xC6,0x03,0x86,0x0E,0x0E,0x18,0x0C,0x60,0x0D,0xC0,0x1C, | |||
// 'Y' | |||
0x59,0x01,0x0E,0x12,0x00,0x0F, | |||
0xE0,0x1D,0x80,0x63,0x03,0x0E,0x1C,0x18,0x60,0x33,0x00,0xFC,0x01,0xE0,0x07,0x80,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00, | |||
// 'Z' | |||
0x5A,0x01,0x0E,0x12,0x01,0x10, | |||
0xFF,0xFF,0xFF,0xF0,0x01,0x80,0x0E,0x00,0x70,0x01,0x80,0x0C,0x00,0x60,0x03,0x80,0x1C,0x00,0x60,0x03,0x00,0x18,0x00,0xE0,0x07,0x00,0x18,0x00,0xFF,0xFF,0xFF,0xF0, | |||
// '[' | |||
0x5B,0x01,0x05,0x15,0x02,0x09, | |||
0xFF,0xF1,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0xFF,0x80, | |||
// '\' | |||
0x5C,0x01,0x08,0x14,0x00,0x08, | |||
0xC0,0xE0,0x60,0x60,0x60,0x30,0x30,0x30,0x18,0x18,0x18,0x18,0x0C,0x0C,0x0C,0x06,0x06,0x06,0x07,0x03, | |||
// ']' | |||
0x5D,0x01,0x05,0x15,0x02,0x09, | |||
0xFF,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC7,0xFF,0x80, | |||
// '^' | |||
0x5E,0x01,0x0F,0x07,0x03,0x14, | |||
0x03,0x80,0x0F,0x80,0x3B,0x80,0xE3,0x83,0x83,0x8E,0x03,0xB8,0x03,0x80, | |||
// '_' | |||
0x5F,0x17,0x0C,0x02,0x00,0x0C, | |||
0xFF,0xFF,0xFF, | |||
// '`' | |||
0x60,0x00,0x06,0x04,0x02,0x0C, | |||
0x60,0xC1,0x83, | |||
// 'a' | |||
0x61,0x06,0x0B,0x0D,0x01,0x0E, | |||
0x3F,0x0F,0xF9,0x03,0x00,0x30,0x06,0x3F,0xDF,0xFF,0x03,0xC0,0x78,0x1F,0x87,0xBF,0xF3,0xE6, | |||
// 'b' | |||
0x62,0x01,0x0C,0x12,0x02,0x0F, | |||
0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0xF8,0xFF,0xCF,0x0E,0xE0,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xE0,0x6F,0x0E,0xFF,0xCC,0xF8, | |||
// 'c' | |||
0x63,0x06,0x0A,0x0D,0x01,0x0D, | |||
0x0F,0x8F,0xF7,0x05,0x80,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x18,0x07,0x04,0xFF,0x0F,0x80, | |||
// 'd' | |||
0x64,0x01,0x0C,0x12,0x01,0x0F, | |||
0x00,0x30,0x03,0x00,0x30,0x03,0x00,0x31,0xF3,0x3F,0xF7,0x0F,0x60,0x7C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x77,0x0F,0x3F,0xF1,0xF3, | |||
// 'e' | |||
0x65,0x06,0x0C,0x0D,0x01,0x0E, | |||
0x0F,0x83,0xFC,0x70,0xE6,0x07,0xC0,0x3F,0xFF,0xFF,0xFC,0x00,0xC0,0x06,0x00,0x70,0x23,0xFE,0x0F,0xC0, | |||
// 'f' | |||
0x66,0x01,0x08,0x12,0x01,0x08, | |||
0x0F,0x1F,0x38,0x30,0x30,0xFF,0xFF,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, | |||
// 'g' | |||
0x67,0x06,0x0C,0x12,0x01,0x0F, | |||
0x1F,0x33,0xFF,0x70,0xF6,0x07,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x07,0x70,0xF3,0xFF,0x1F,0x30,0x03,0x00,0x72,0x0E,0x3F,0xC1,0xF8, | |||
// 'h' | |||
0x68,0x01,0x0B,0x12,0x02,0x0F, | |||
0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x9F,0x3F,0xF7,0x87,0xE0,0x78,0x0F,0x01,0xE0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x0C, | |||
// 'i' | |||
0x69,0x01,0x02,0x12,0x02,0x07, | |||
0xFC,0x3F,0xFF,0xFF,0xF0, | |||
// 'j' | |||
0x6A,0x01,0x05,0x17,0xFF,0x07, | |||
0x18,0xC6,0x00,0x0C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x33,0xFB,0x80, | |||
// 'k' | |||
0x6B,0x01,0x0C,0x12,0x02,0x0E, | |||
0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x1C,0xC3,0x8C,0x70,0xCE,0x0D,0xC0,0xF8,0x0F,0x80,0xDC,0x0C,0xE0,0xC7,0x0C,0x38,0xC1,0xCC,0x0E, | |||
// 'l' | |||
0x6C,0x01,0x02,0x12,0x02,0x06, | |||
0xFF,0xFF,0xFF,0xFF,0xF0, | |||
// 'm' | |||
0x6D,0x06,0x14,0x0D,0x02,0x18, | |||
0xCF,0x87,0xCF,0xFC,0xFE,0xF0,0xF8,0x7E,0x07,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x30, | |||
// 'n' | |||
0x6E,0x06,0x0B,0x0D,0x02,0x0F, | |||
0xCF,0x9F,0xFB,0xC3,0xF0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x0F,0x01,0xE0,0x3C,0x06, | |||
// 'o' | |||
0x6F,0x06,0x0C,0x0D,0x01,0x0E, | |||
0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80, | |||
// 'p' | |||
0x70,0x06,0x0C,0x12,0x02,0x0F, | |||
0xCF,0x8F,0xFC,0xF0,0xEE,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x3E,0x06,0xF0,0xEF,0xFC,0xCF,0x8C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00, | |||
// 'q' | |||
0x71,0x06,0x0C,0x12,0x01,0x0F, | |||
0x1F,0x33,0xFF,0x70,0xF6,0x07,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x07,0x70,0xF3,0xFF,0x1F,0x30,0x03,0x00,0x30,0x03,0x00,0x30,0x03, | |||
// 'r' | |||
0x72,0x06,0x08,0x0D,0x02,0x0A, | |||
0xCF,0xFF,0xF0,0xE0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, | |||
// 's' | |||
0x73,0x06,0x0B,0x0D,0x01,0x0C, | |||
0x3F,0x0F,0xF3,0x82,0x60,0x0C,0x00,0xF0,0x0F,0xC0,0x3C,0x00,0xC0,0x1A,0x07,0x7F,0xC7,0xF0, | |||
// 't' | |||
0x74,0x02,0x08,0x11,0x00,0x09, | |||
0x30,0x30,0x30,0x30,0xFF,0xFF,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x1F,0x0F, | |||
// 'u' | |||
0x75,0x06,0x0B,0x0D,0x02,0x0F, | |||
0xC0,0x78,0x0F,0x01,0xE0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x1F,0x87,0xBF,0xF3,0xE6, | |||
// 'v' | |||
0x76,0x06,0x0D,0x0D,0x01,0x0F, | |||
0xC0,0x1B,0x01,0x98,0x0C,0xC0,0x63,0x06,0x18,0x30,0x63,0x03,0x18,0x18,0xC0,0x6C,0x03,0x60,0x1F,0x00,0x70,0x00, | |||
// 'w' | |||
0x77,0x06,0x12,0x0D,0x01,0x14, | |||
0xC1,0xE0,0xF0,0x78,0x36,0x1E,0x19,0x87,0x86,0x63,0x31,0x9C,0xCC,0xE3,0x33,0x30,0xCC,0xCC,0x36,0x1B,0x07,0x87,0x81,0xE1,0xE0,0x78,0x78,0x1C,0x0E,0x00, | |||
// 'x' | |||
0x78,0x06,0x0D,0x0D,0x01,0x0F, | |||
0xE0,0x3B,0x83,0x8E,0x38,0x31,0x80,0xD8,0x07,0xC0,0x1C,0x01,0xF0,0x1D,0xC0,0xC6,0x0C,0x18,0xE0,0xEE,0x03,0x80, | |||
// 'y' | |||
0x79,0x06,0x0D,0x12,0x01,0x0F, | |||
0xC0,0x1B,0x01,0x98,0x0C,0xE0,0xE3,0x06,0x18,0x70,0x63,0x03,0x18,0x0D,0x80,0x6C,0x03,0xE0,0x0E,0x00,0x70,0x03,0x00,0x18,0x01,0x80,0x7C,0x03,0xC0,0x00, | |||
// 'z' | |||
0x7A,0x06,0x0B,0x0D,0x01,0x0D, | |||
0xFF,0xFF,0xFC,0x03,0x00,0xE0,0x38,0x0E,0x03,0x80,0xE0,0x38,0x0E,0x01,0x80,0x7F,0xFF,0xFE, | |||
// '{' | |||
0x7B,0x01,0x09,0x16,0x03,0x0F, | |||
0x03,0x83,0xC3,0x81,0x80,0xC0,0x60,0x30,0x18,0x0C,0x0E,0x3E,0x1F,0x01,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0xC0,0x78,0x1C, | |||
// '|' | |||
0x7C,0x01,0x02,0x18,0x03,0x08, | |||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |||
// '}' | |||
0x7D,0x01,0x09,0x16,0x03,0x0F, | |||
0xE0,0x78,0x0E,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0E,0x03,0xE1,0xF1,0xC0,0xC0,0x60,0x30,0x18,0x0C,0x06,0x07,0x0F,0x07,0x00, | |||
// '~' | |||
0x7E,0x09,0x0F,0x05,0x03,0x14, | |||
0x00,0x00,0x7C,0x05,0xFE,0x1E,0x1F,0xE0,0x0F,0x80, | |||
// Terminator | |||
0xFF | |||
}; |
@@ -0,0 +1,571 @@ | |||
/* | |||
* High level EPD functions | |||
* Author: LoBo 06/2017, https://github/loboris | |||
* | |||
*/ | |||
#ifndef _EPD_H_ | |||
#define _EPD_H_ | |||
#include <stdlib.h> | |||
#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 |
@@ -0,0 +1,759 @@ | |||
/* | |||
* Author: LoBo (loboris@gmail.com, loboris.github) | |||
* | |||
* Module supporting SPI ePaper displays | |||
* | |||
* HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS | |||
* USING DIRECT or DMA SPI TRANSFER MODEs | |||
* | |||
*/ | |||
#include "spi_master_lobo.h" | |||
#include <errno.h> | |||
#include <stdio.h> | |||
#include <time.h> | |||
#include <string.h> | |||
#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<datalen-1;i++){ // sub the data | |||
SPI_Write(*ptemp); | |||
ptemp++; | |||
} | |||
spi_lobo_device_deselect(disp_spi); | |||
} | |||
// Send data buffer to display | |||
//---------------------------------------------------------------------------- | |||
static void EPD_WriteDispRam(uint8_t XSize, uint16_t YSize, uint8_t *Dispbuff) | |||
{ | |||
if (XSize%8 != 0) XSize = XSize+(8-XSize%8); | |||
XSize = XSize/8; | |||
spi_lobo_device_select(disp_spi, 0); | |||
//ReadBusy(); | |||
EPD_DC_0; //command write | |||
SPI_Write(0x24); | |||
EPD_DC_1; //data write | |||
SPI_send_data(Dispbuff, XSize*YSize, 0); | |||
spi_lobo_device_deselect(disp_spi); | |||
} | |||
// Fill the display with value | |||
//------------------------------------------------------------------------------- | |||
static void EPD_WriteDispRamMono(uint8_t XSize, uint16_t YSize, uint8_t dispdata) | |||
{ | |||
if (XSize%8 != 0) XSize = XSize+(8-XSize%8); | |||
XSize = XSize/8; | |||
spi_lobo_device_select(disp_spi, 0); | |||
//ReadBusy(); | |||
EPD_DC_0; // command write | |||
SPI_Write(0x24); | |||
EPD_DC_1; // data write | |||
SPI_send_data(&dispdata, XSize*YSize, 1); | |||
spi_lobo_device_deselect(disp_spi); | |||
} | |||
/* | |||
=== Set RAM X - Address Start / End Position (44h) === | |||
Specify the start/end positions of the window address in the X direction by 8 times address unit. | |||
Data is written to the RAM within the area determined by the addresses specified by XSA [4:0] and XEA [4:0]. | |||
These addresses must be set before the RAM write. It allows on XEA [4:0] ≤ XSA [4:0]. | |||
The settings follow the condition on 00h ≤ XSA [4:0], XEA [4:0] ≤ 1Dh. | |||
The windows is followed by the control setting of Data Entry Setting (R11h) | |||
=== Set RAM Y - Address Start / End Position (45h) === | |||
Specify the start/end positions of the window address in the Y direction by an address unit. | |||
Data is written to the RAM within the area determined by the addresses specified by YSA [8:0] and YEA [8:0]. | |||
These addresses must be set before the RAM write. | |||
It allows YEA [8:0] ≤ YSA [8:0]. | |||
The settings follow the condition on 00h ≤ YSA [8:0], YEA [8:0] ≤ 13Fh. | |||
The windows is followed by the control setting of Data Entry Setting (R11h) | |||
*/ | |||
//-------------------------------------------------------------------------------------- | |||
static void EPD_SetRamArea(uint8_t Xstart, uint8_t Xend, uint16_t Ystart, uint16_t Yend) | |||
{ | |||
uint8_t RamAreaX[3]; // X start and end | |||
uint8_t RamAreaY[5]; // Y start and end | |||
RamAreaX[0] = 0x44; // command | |||
RamAreaX[1] = Xstart; | |||
RamAreaX[2] = Xend; | |||
RamAreaY[0] = 0x45; // command | |||
RamAreaY[1] = Ystart & 0xFF; | |||
RamAreaY[2] = Ystart >> 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<<n)) EPD_gsUpdate(xStart, xEnd, yStart, yEnd, n); | |||
} | |||
} | |||
} | |||
//------------------- | |||
void EPD_UpdateScreen() | |||
{ | |||
EPD_Update(0, EPD_DISPLAY_WIDTH-1, 0, EPD_DISPLAY_HEIGHT-1); | |||
} | |||
//------------------------ | |||
void EPD_wait(uint32_t ms) | |||
{ | |||
if (ms < 100) ms = 100; | |||
uint32_t n = 0; | |||
while (n < ms) { | |||
vTaskDelay(100 / portTICK_RATE_MS); | |||
n += 100; | |||
} | |||
} | |||
@@ -0,0 +1,86 @@ | |||
/* | |||
* 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 | |||
* | |||
*/ | |||
#ifndef _EPDSPI_H_ | |||
#define _EPDSPI_H_ | |||
#include <stdint.h> | |||
#include "spi_master_lobo.h" | |||
#define EPD_DISPLAY_WIDTH 296 | |||
#define EPD_DISPLAY_HEIGHT 128 | |||
#define SCK_Pin 18 | |||
#define MOSI_Pin 23 | |||
//#define MISO_Pin 19 | |||
#define DC_Pin 17//26 | |||
#define BUSY_Pin 4//32 | |||
#define RST_Pin 16//27 | |||
#define CS_Pin 5 | |||
// ePaper display can be powered from GPIO | |||
// if powered directly from Vcc, set this to 0 | |||
#define POWER_Pin 22 | |||
#define DC_VAL (1 << DC_Pin) | |||
#define EPD_CS_0 gpio_set_level(CS_Pin, 0) | |||
#define EPD_CS_1 gpio_set_level(CS_Pin, 1) | |||
#define isEPD_CS gpio_get_level(CS_Pin) | |||
#define EPD_RST_0 gpio_set_level(RST_Pin, 0) | |||
#define EPD_RST_1 gpio_set_level(RST_Pin, 1) | |||
#define isEPD_RST gpio_get_level(RST_Pin) | |||
#define EPD_DC_0 gpio_set_level(DC_Pin, 0) | |||
#define EPD_DC_1 gpio_set_level(DC_Pin, 1) | |||
#define isEPD_BUSY gpio_get_level(BUSY_Pin) | |||
#define EPD_BUSY_LEVEL 0 | |||
// ================================================== | |||
// Define which spi bus to use VSPI_HOST or HSPI_HOST | |||
#define SPI_BUS VSPI_HOST | |||
// ================================================== | |||
spi_lobo_device_handle_t disp_spi; | |||
uint8_t *gs_disp_buffer; | |||
uint8_t *disp_buffer; | |||
uint8_t *gs_drawBuff; | |||
uint8_t *drawBuff; | |||
int _width; | |||
int _height; | |||
uint16_t gs_used_shades; | |||
uint8_t _gs; | |||
uint8_t *LUT_part; | |||
uint8_t LUTDefault_fastest[31]; | |||
uint8_t LUTDefault_part[31]; | |||
uint8_t LUT_gs[31]; | |||
uint8_t LUTDefault_full[31]; | |||
uint8_t lvl_buf[16]; | |||
uint8_t lvl_buf_jpg[16]; | |||
void EPD_wait(uint32_t ms); | |||
void EPD_DisplaySetFull(uint8_t val); | |||
void EPD_DisplaySetPart(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t val); | |||
void EPD_DisplayClearFull(); | |||
void EPD_DisplayClearPart(); | |||
void EPD_DisplayFull(uint8_t *DisBuffer); | |||
void EPD_DisplayPart(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t *DisBuffer); | |||
void EPD_gsUpdate(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd, uint8_t gs); | |||
void EPD_Update(int xStart, int xEnd, uint8_t yStart, uint8_t yEnd); | |||
void EPD_UpdateScreen(); | |||
void EPD_Cls(); | |||
void EPD_PowerOn(); | |||
void EPD_PowerOff(); | |||
#endif |
@@ -0,0 +1,120 @@ | |||
// SmallFont.c | |||
// Font type : Full (95 characters) | |||
// Font size : 8x12 pixels | |||
// Memory usage : 1144 bytes | |||
#if defined(__AVR__) | |||
#include <avr/pgmspace.h> | |||
#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, // <space> | |||
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, // <backslash> | |||
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, // ~ | |||
}; |
@@ -0,0 +1,331 @@ | |||
// This comes with no warranty, implied or otherwise | |||
// This data structure was designed to support Proportional fonts | |||
// on Arduinos. It can however handle any ttf font that has been converted | |||
// using the conversion program. These could be fixed width or proportional | |||
// fonts. Individual characters do not have to be multiples of 8 bits wide. | |||
// Any width is fine and does not need to be fixed. | |||
// The data bits are packed to minimize data requirements, but the tradeoff | |||
// is that a header is required per character. | |||
// Ubuntu16.c | |||
// Point Size : 16 | |||
// Memory usage : 1433 bytes | |||
// # characters : 95 | |||
// Header Format (to make Arduino UTFT Compatible): | |||
// ------------------------------------------------ | |||
// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) | |||
// Character Height | |||
// First Character (Reserved. 0x00) | |||
// Number Of Characters (Reserved. 0x00) | |||
const unsigned char tft_Ubuntu16[] = | |||
{ | |||
0x00, 0x10, 0x00, 0x00, | |||
// Individual Character Format: | |||
// ---------------------------- | |||
// Character Code | |||
// Adjusted Y Offset | |||
// Width | |||
// Height | |||
// xOffset | |||
// xDelta (the distance to move the cursor. Effective width of the character.) | |||
// Data[n] | |||
// NOTE: You can remove any of these characters if they are not needed in | |||
// your application. The first character number in each Glyph indicates | |||
// the ASCII character code. Therefore, these do not have to be sequential. | |||
// Just remove all the content for a particular character to save space. | |||
// ' ' | |||
0x20,0x0D,0x00,0x00,0x00,0x04, | |||
// '!' | |||
0x21,0x02,0x01,0x0B,0x01,0x04, | |||
0xFC,0x60, | |||
// '"' | |||
0x22,0x00,0x04,0x04,0x01,0x07, | |||
0x99,0x99, | |||
// '#' | |||
0x23,0x02,0x09,0x0B,0x01,0x0B, | |||
0x11,0x08,0x84,0x5F,0xF2,0x21,0x10,0x89,0xFF,0x44,0x22,0x11,0x00, | |||
// '$' | |||
0x24,0x00,0x07,0x10,0x01,0x09, | |||
0x10,0x20,0xF6,0x08,0x10,0x18,0x08,0x0C,0x0C,0x08,0x3F,0xC2,0x04,0x00, | |||
// '%' | |||
0x25,0x02,0x0C,0x0B,0x01,0x0E, | |||
0x70,0x4D,0x88,0x89,0x08,0x90,0xDA,0x07,0x4E,0x05,0xB0,0x91,0x09,0x11,0x1B,0x20,0xE0, | |||
// '&' | |||
0x26,0x02,0x0A,0x0B,0x01,0x0B, | |||
0x3C,0x18,0x84,0x21,0x08,0x2C,0x0C,0x04,0x8A,0x10,0x83,0x30,0xC7,0xC8, | |||
// ''' | |||
0x27,0x00,0x01,0x04,0x01,0x04, | |||
0xF0, | |||
// '(' | |||
0x28,0x00,0x04,0x10,0x01,0x05, | |||
0x02,0x44,0x48,0x88,0x88,0x84,0x44,0x20, | |||
// ')' | |||
0x29,0x00,0x04,0x10,0x00,0x05, | |||
0x04,0x22,0x21,0x11,0x11,0x12,0x22,0x40, | |||
// '*' | |||
0x2A,0x02,0x09,0x06,0x00,0x08, | |||
0x08,0x24,0x8F,0x83,0x81,0x41,0x10, | |||
// '+' | |||
0x2B,0x05,0x07,0x07,0x01,0x09, | |||
0x10,0x20,0x47,0xF1,0x02,0x04,0x00, | |||
// ',' | |||
0x2C,0x0B,0x02,0x05,0x00,0x04, | |||
0x54,0x80, | |||
// '-' | |||
0x2D,0x08,0x04,0x01,0x01,0x06, | |||
0xF0, | |||
// '.' | |||
0x2E,0x0B,0x01,0x02,0x01,0x04, | |||
0xC0, | |||
// '/' | |||
0x2F,0x00,0x07,0x10,0x00,0x06, | |||
0x02,0x08,0x10,0x20,0x81,0x02,0x08,0x10,0x40,0x81,0x04,0x08,0x10,0x40, | |||
// '0' | |||
0x30,0x02,0x07,0x0B,0x01,0x09, | |||
0x38,0x8B,0x1C,0x18,0x30,0x60,0xC1,0x86,0x88,0xE0, | |||
// '1' | |||
0x31,0x02,0x04,0x0B,0x01,0x09, | |||
0x13,0x59,0x11,0x11,0x11,0x10, | |||
// '2' | |||
0x32,0x02,0x06,0x0B,0x01,0x09, | |||
0x7A,0x30,0x41,0x08,0x21,0x08,0x42,0x0F,0xC0, | |||
// '3' | |||
0x33,0x02,0x07,0x0B,0x01,0x09, | |||
0x78,0x08,0x08,0x10,0x47,0x01,0x01,0x02,0x0B,0xE0, | |||
// '4' | |||
0x34,0x02,0x07,0x0B,0x01,0x09, | |||
0x04,0x18,0x51,0x22,0x48,0xA1,0x7F,0x04,0x08,0x10, | |||
// '5' | |||
0x35,0x02,0x07,0x0B,0x01,0x09, | |||
0x7E,0x81,0x02,0x07,0x81,0x80,0x81,0x02,0x0B,0xE0, | |||
// '6' | |||
0x36,0x02,0x07,0x0B,0x01,0x09, | |||
0x1C,0x61,0x00,0x0F,0x90,0xA0,0xC1,0x82,0x88,0xE0, | |||
// '7' | |||
0x37,0x02,0x07,0x0B,0x01,0x09, | |||
0xFE,0x04,0x10,0x40,0x82,0x04,0x08,0x20,0x40,0x80, | |||
// '8' | |||
0x38,0x02,0x07,0x0B,0x01,0x09, | |||
0x39,0x8A,0x0C,0x14,0x47,0x11,0x41,0x83,0x89,0xE0, | |||
// '9' | |||
0x39,0x02,0x07,0x0B,0x01,0x09, | |||
0x38,0x8A,0x0C,0x18,0x28,0x4F,0x81,0x04,0x11,0xC0, | |||
// ':' | |||
0x3A,0x05,0x01,0x08,0x01,0x04, | |||
0xC3, | |||
// ';' | |||
0x3B,0x05,0x02,0x0B,0x00,0x04, | |||
0x50,0x05,0x48, | |||
// '<' | |||
0x3C,0x05,0x08,0x07,0x01,0x09, | |||
0x02,0x0C,0x30,0x60,0x30,0x0C,0x02, | |||
// '=' | |||
0x3D,0x06,0x07,0x04,0x01,0x09, | |||
0xFE,0x00,0x07,0xF0, | |||
// '>' | |||
0x3E,0x05,0x09,0x07,0x00,0x09, | |||
0x40,0x1C,0x01,0x80,0x70,0x61,0xC1,0x00, | |||
// '?' | |||
0x3F,0x02,0x06,0x0B,0x01,0x07, | |||
0x78,0x30,0x41,0x18,0xC2,0x00,0x00,0x82,0x00, | |||
// '@' | |||
0x40,0x02,0x0D,0x0D,0x01,0x0F, | |||
0x0F,0x81,0x83,0x10,0x0C,0x8F,0xA8,0x84,0xC8,0x26,0x41,0x32,0x09,0x88,0x5A,0x3F,0x90,0x00,0x60,0x00,0xFC,0x00, | |||
// 'A' | |||
0x41,0x02,0x0B,0x0B,0x00,0x0B, | |||
0x04,0x01,0xC0,0x28,0x08,0x81,0x10,0x61,0x08,0x21,0xFC,0x60,0x48,0x0B,0x00,0x80, | |||
// 'B' | |||
0x42,0x02,0x08,0x0B,0x01,0x0A, | |||
0xF8,0x86,0x82,0x82,0x86,0xFC,0x82,0x81,0x81,0x82,0xFC, | |||
// 'C' | |||
0x43,0x02,0x09,0x0B,0x01,0x0B, | |||
0x1F,0x10,0x10,0x10,0x08,0x04,0x02,0x01,0x00,0x40,0x30,0x07,0xC0, | |||
// 'D' | |||
0x44,0x02,0x09,0x0B,0x01,0x0B, | |||
0xFC,0x41,0x20,0x50,0x18,0x0C,0x06,0x03,0x01,0x81,0x41,0x3F,0x00, | |||
// 'E' | |||
0x45,0x02,0x07,0x0B,0x01,0x09, | |||
0xFF,0x02,0x04,0x08,0x1F,0xA0,0x40,0x81,0x03,0xF8, | |||
// 'F' | |||
0x46,0x02,0x07,0x0B,0x01,0x09, | |||
0xFF,0x02,0x04,0x08,0x1F,0xA0,0x40,0x81,0x02,0x00, | |||
// 'G' | |||
0x47,0x02,0x09,0x0B,0x01,0x0B, | |||
0x1F,0x10,0x10,0x10,0x08,0x04,0x02,0x03,0x01,0x40,0xB0,0x47,0xE0, | |||
// 'H' | |||
0x48,0x02,0x09,0x0B,0x01,0x0B, | |||
0x80,0xC0,0x60,0x30,0x18,0x0F,0xFE,0x03,0x01,0x80,0xC0,0x60,0x20, | |||
// 'I' | |||
0x49,0x02,0x01,0x0B,0x01,0x03, | |||
0xFF,0xE0, | |||
// 'J' | |||
0x4A,0x02,0x07,0x0B,0x00,0x08, | |||
0x02,0x04,0x08,0x10,0x20,0x40,0x81,0x02,0x09,0xE0, | |||
// 'K' | |||
0x4B,0x02,0x09,0x0B,0x01,0x0A, | |||
0x81,0x41,0x23,0x12,0x0A,0x06,0x02,0xC1,0x10,0x86,0x40,0xA0,0x20, | |||
// 'L' | |||
0x4C,0x02,0x07,0x0B,0x01,0x08, | |||
0x81,0x02,0x04,0x08,0x10,0x20,0x40,0x81,0x03,0xF8, | |||
// 'M' | |||
0x4D,0x02,0x0B,0x0B,0x01,0x0D, | |||
0x40,0x4C,0x19,0x01,0x28,0xA5,0x14,0x94,0xB2,0x9C,0x33,0x84,0x30,0x06,0x00,0x80, | |||
// 'N' | |||
0x4E,0x02,0x09,0x0B,0x01,0x0B, | |||
0x80,0xE0,0x68,0x32,0x19,0x0C,0x46,0x13,0x05,0x82,0xC0,0xE0,0x20, | |||
// 'O' | |||
0x4F,0x02,0x0B,0x0B,0x01,0x0D, | |||
0x1F,0x04,0x11,0x01,0x40,0x18,0x03,0x00,0x60,0x0C,0x01,0x40,0x44,0x10,0x7C,0x00, | |||
// 'P' | |||
0x50,0x02,0x08,0x0B,0x01,0x0A, | |||
0xFC,0x82,0x81,0x81,0x81,0x82,0xFC,0x80,0x80,0x80,0x80, | |||
// 'Q' | |||
0x51,0x02,0x0B,0x0E,0x01,0x0D, | |||
0x1F,0x04,0x11,0x01,0x40,0x18,0x03,0x00,0x60,0x0C,0x01,0x40,0x44,0x10,0x78,0x02,0x00,0x30,0x01,0x80, | |||
// 'R' | |||
0x52,0x02,0x09,0x0B,0x01,0x0A, | |||
0xFC,0x41,0x20,0x50,0x28,0x14,0x13,0xF1,0x08,0x82,0x40,0xA0,0x20, | |||
// 'S' | |||
0x53,0x02,0x08,0x0B,0x01,0x09, | |||
0x3C,0xC2,0x80,0x80,0x40,0x1C,0x06,0x02,0x02,0x06,0x78, | |||
// 'T' | |||
0x54,0x02,0x09,0x0B,0x00,0x09, | |||
0xFF,0x84,0x02,0x01,0x00,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x00, | |||
// 'U' | |||
0x55,0x02,0x09,0x0B,0x01,0x0B, | |||
0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xA0,0x8F,0x80, | |||
// 'V' | |||
0x56,0x02,0x09,0x0B,0x00,0x09, | |||
0x80,0xE0,0xD0,0x48,0x26,0x21,0x10,0x88,0x28,0x14,0x0E,0x02,0x00, | |||
// 'W' | |||
0x57,0x02,0x0D,0x0B,0x00,0x0D, | |||
0x80,0x0E,0x10,0xD0,0x84,0x8E,0x24,0x51,0x22,0x88,0xA2,0x85,0x14,0x38,0xE0,0xC2,0x04,0x10, | |||
// 'X' | |||
0x58,0x02,0x09,0x0B,0x00,0x09, | |||
0xC1,0xA0,0x88,0x86,0xC1,0x40,0x60,0x70,0x6C,0x22,0x20,0xB0,0x60, | |||
// 'Y' | |||
0x59,0x02,0x09,0x0B,0x00,0x09, | |||
0x80,0xA0,0x90,0x44,0x41,0x40,0xA0,0x20,0x10,0x08,0x04,0x02,0x00, | |||
// 'Z' | |||
0x5A,0x02,0x07,0x0B,0x01,0x09, | |||
0xFE,0x04,0x10,0x41,0x02,0x08,0x00,0x41,0x03,0xF8, | |||
// '[' | |||
0x5B,0x00,0x03,0x10,0x02,0x05, | |||
0xF2,0x49,0x24,0x92,0x49,0x27, | |||
// '\' | |||
0x5C,0x00,0x07,0x10,0x00,0x06, | |||
0x80,0x81,0x02,0x02,0x04,0x08,0x08,0x10,0x10,0x20,0x40,0x40,0x81,0x01, | |||
// ']' | |||
0x5D,0x00,0x03,0x10,0x00,0x05, | |||
0xE4,0x92,0x49,0x24,0x92,0x4F, | |||
// '^' | |||
0x5E,0x02,0x07,0x06,0x01,0x09, | |||
0x10,0x70,0xA2,0x24,0x50,0x40, | |||
// '_' | |||
0x5F,0x0F,0x08,0x01,0x00,0x08, | |||
0xFF, | |||
// '`' | |||
0x60,0x01,0x04,0x03,0x01,0x06, | |||
0x86,0x10, | |||
// 'a' | |||
0x61,0x05,0x06,0x08,0x01,0x08, | |||
0x78,0x30,0x5F,0xC6,0x18,0x5F, | |||
// 'b' | |||
0x62,0x01,0x07,0x0C,0x01,0x09, | |||
0x81,0x02,0x04,0x0F,0x90,0xA0,0xC1,0x83,0x06,0x17,0xC0, | |||
// 'c' | |||
0x63,0x05,0x06,0x08,0x01,0x08, | |||
0x3D,0x08,0x20,0x82,0x04,0x0F, | |||
// 'd' | |||
0x64,0x01,0x07,0x0C,0x01,0x09, | |||
0x02,0x04,0x08,0x13,0xE8,0x60,0xC1,0x83,0x05,0x09,0xF0, | |||
// 'e' | |||
0x65,0x05,0x07,0x08,0x01,0x09, | |||
0x3C,0x8A,0x0F,0xF8,0x10,0x10,0x1E, | |||
// 'f' | |||
0x66,0x01,0x05,0x0C,0x01,0x06, | |||
0x7E,0x21,0x0F,0xC2,0x10,0x84,0x21,0x00, | |||
// 'g' | |||
0x67,0x05,0x07,0x0B,0x01,0x09, | |||
0x3E,0x86,0x0C,0x18,0x30,0x50,0x9F,0x02,0x0B,0xE0, | |||
// 'h' | |||
0x68,0x01,0x07,0x0C,0x01,0x09, | |||
0x81,0x02,0x04,0x0F,0x90,0xE0,0xC1,0x83,0x06,0x0C,0x10, | |||
// 'i' | |||
0x69,0x01,0x03,0x0C,0x00,0x03, | |||
0x48,0x04,0x92,0x49,0x20, | |||
// 'j' | |||
0x6A,0x01,0x04,0x0F,0xFF,0x03, | |||
0x22,0x00,0x22,0x22,0x22,0x22,0x22,0xC0, | |||
// 'k' | |||
0x6B,0x01,0x06,0x0C,0x01,0x08, | |||
0x82,0x08,0x20,0x8A,0x4A,0x30,0xA2,0x48,0xA1, | |||
// 'l' | |||
0x6C,0x01,0x04,0x0C,0x01,0x04, | |||
0x88,0x88,0x88,0x88,0x88,0x86, | |||
// 'm' | |||
0x6D,0x05,0x0B,0x08,0x01,0x0D, | |||
0xFB,0xD1,0x8E,0x10,0xC2,0x18,0x43,0x08,0x61,0x0C,0x21, | |||
// 'n' | |||
0x6E,0x05,0x07,0x08,0x01,0x09, | |||
0xFD,0x0E,0x0C,0x18,0x30,0x60,0xC1, | |||
// 'o' | |||
0x6F,0x05,0x08,0x08,0x01,0x0A, | |||
0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C, | |||
// 'p' | |||
0x70,0x05,0x07,0x0B,0x01,0x09, | |||
0xF9,0x0A,0x0C,0x18,0x30,0x61,0x7C,0x81,0x02,0x00, | |||
// 'q' | |||
0x71,0x05,0x07,0x0B,0x01,0x09, | |||
0x3E,0x86,0x0C,0x18,0x30,0x50,0x9F,0x02,0x04,0x08, | |||
// 'r' | |||
0x72,0x05,0x05,0x08,0x01,0x06, | |||
0xFC,0x21,0x08,0x42,0x10, | |||
// 's' | |||
0x73,0x05,0x05,0x08,0x01,0x07, | |||
0x7C,0x20,0xC3,0x04,0x3E, | |||
// 't' | |||
0x74,0x02,0x05,0x0B,0x01,0x07, | |||
0x84,0x21,0xF8,0x42,0x10,0x84,0x1E, | |||
// 'u' | |||
0x75,0x05,0x07,0x08,0x01,0x09, | |||
0x83,0x06,0x0C,0x18,0x30,0x50,0xBF, | |||
// 'v' | |||
0x76,0x05,0x07,0x08,0x00,0x07, | |||
0x83,0x05,0x12,0x22,0x85,0x0E,0x08, | |||
// 'w' | |||
0x77,0x05,0x0D,0x08,0x00,0x0D, | |||
0x82,0x0C,0x10,0x51,0xC4,0x8A,0x26,0x5B,0x14,0x50,0xE3,0x82,0x08, | |||
// 'x' | |||
0x78,0x05,0x08,0x08,0x00,0x08, | |||
0xC3,0x66,0x24,0x18,0x18,0x24,0x42,0xC3, | |||
// 'y' | |||
0x79,0x05,0x07,0x0B,0x00,0x07, | |||
0x82,0x89,0x12,0x22,0x85,0x04,0x08,0x10,0x43,0x00, | |||
// 'z' | |||
0x7A,0x05,0x06,0x08,0x01,0x08, | |||
0xFC,0x10,0x84,0x21,0x08,0x3F, | |||
// '{' | |||
0x7B,0x00,0x05,0x10,0x00,0x05, | |||
0x19,0x08,0x42,0x10,0x98,0x61,0x08,0x42,0x10,0x83, | |||
// '|' | |||
0x7C,0x00,0x01,0x10,0x02,0x05, | |||
0xFF,0xFF, | |||
// '}' | |||
0x7D,0x00,0x05,0x10,0x00,0x05, | |||
0xC1,0x08,0x42,0x10,0x83,0x31,0x08,0x42,0x10,0x98, | |||
// '~' | |||
0x7E,0x07,0x07,0x02,0x01,0x09, | |||
0x73,0x18, | |||
// Terminator | |||
0xFF | |||
}; |
@@ -0,0 +1,331 @@ | |||
// This comes with no warranty, implied or otherwise | |||
// This data structure was designed to support Proportional fonts | |||
// on Arduinos. It can however handle any ttf font that has been converted | |||
// using the conversion program. These could be fixed width or proportional | |||
// fonts. Individual characters do not have to be multiples of 8 bits wide. | |||
// Any width is fine and does not need to be fixed. | |||
// The data bits are packed to minimize data requirements, but the tradeoff | |||
// is that a header is required per character. | |||
// comic.c | |||
// Point Size : 24 | |||
// Memory usage : 2814 bytes | |||
// # characters : 95 | |||
// Header Format (to make Arduino UTFT Compatible): | |||
// ------------------------------------------------ | |||
// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) | |||
// Character Height | |||
// First Character (Reserved. 0x00) | |||
// Number Of Characters (Reserved. 0x00) | |||
unsigned char tft_Comic24[] = | |||
{ | |||
0x00, 0x19, 0x00, 0x00, | |||
// Individual Character Format: | |||
// ---------------------------- | |||
// Character Code | |||
// Adjusted Y Offset | |||
// Width | |||
// Height | |||
// xOffset | |||
// xDelta (the distance to move the cursor. Effective width of the character.) | |||
// Data[n] | |||
// NOTE: You can remove any of these characters if they are not needed in | |||
// your application. The first character number in each Glyph indicates | |||
// the ASCII character code. Therefore, these do not have to be sequential. | |||
// Just remove all the content for a particular character to save space. | |||
// ' ' | |||
0x20,0x15,0x00,0x00,0x00,0x07, | |||
// '!' | |||
0x21,0x02,0x02,0x14,0x01,0x06, | |||
0xFF,0xFF,0xFF,0xFC,0x2D, | |||
// '"' | |||
0x22,0x03,0x06,0x08,0x02,0x0A, | |||
0xCF,0x3C,0xF3,0xCF,0x3C,0xF3, | |||
// '#' | |||
0x23,0x03,0x14,0x12,0x01,0x14, | |||
0x01,0x81,0x80,0x18,0x18,0x01,0x81,0x80,0x30,0x30,0x03,0x03,0x07,0xFF,0xFF,0x7F,0xFF,0xF0,0x60,0x60,0x06,0x06,0x00,0xC0,0xC0,0x0C,0x0C,0x0F,0xFF,0xFE,0xFF,0xFF,0xE1,0x81,0x80,0x18,0x18,0x03,0x83,0x00,0x30,0x30,0x03,0x03,0x00, | |||
// '$' | |||
0x24,0x00,0x0B,0x19,0x02,0x11, | |||
0x0C,0x01,0x80,0x30,0x0F,0x83,0xFC,0xD9,0xBB,0x06,0x60,0xCC,0x19,0x83,0xB0,0x3F,0x83,0xFC,0x1B,0x83,0x18,0x63,0x0C,0x71,0x9F,0x37,0x7F,0xC3,0xF0,0x18,0x03,0x00,0x60,0x0C,0x00, | |||
// '%' | |||
0x25,0x01,0x11,0x14,0x02,0x14, | |||
0x00,0x00,0x00,0x0C,0x0E,0x0E,0x0F,0x86,0x0C,0x67,0x06,0x33,0x03,0x19,0x80,0xF9,0x80,0x38,0xC0,0x00,0xE0,0x00,0x60,0x00,0x70,0x00,0x31,0xE0,0x39,0xF8,0x19,0xCE,0x1C,0xC3,0x0C,0x61,0x86,0x39,0xC6,0x0F,0xC3,0x03,0xC0, | |||
// '&' | |||
0x26,0x03,0x0F,0x13,0x01,0x10, | |||
0x01,0xC0,0x07,0xC0,0x19,0x80,0x33,0x00,0x6E,0x00,0xF8,0x01,0xE0,0x07,0x80,0x1F,0x8C,0x73,0x19,0xC3,0x37,0x07,0xEC,0x07,0xD8,0x07,0x30,0x0E,0x38,0x7E,0x3F,0xEC,0x3F,0x0C,0x00,0x18, | |||
// ''' | |||
0x27,0x03,0x02,0x06,0x03,0x09, | |||
0xFF,0xF0, | |||
// '(' | |||
0x28,0x02,0x07,0x18,0x01,0x09, | |||
0x06,0x1C,0x71,0xC3,0x0E,0x18,0x30,0xE1,0x83,0x06,0x0C,0x18,0x30,0x60,0xE0,0xC1,0x83,0x83,0x83,0x87,0x83, | |||
// ')' | |||
0x29,0x02,0x06,0x18,0x02,0x09, | |||
0xC3,0x86,0x0C,0x30,0x61,0x86,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x61,0x86,0x31,0xCE,0x30, | |||
// '*' | |||
0x2A,0x03,0x0B,0x09,0x01,0x0D, | |||
0x0C,0x01,0x83,0xBF,0xFF,0xF3,0xFC,0x3C,0x0F,0xC3,0x9C,0x61,0x80, | |||
// '+' | |||
0x2B,0x09,0x0A,0x0A,0x00,0x0C, | |||
0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0,0xC0,0x30,0x0C,0x03,0x00, | |||
// ',' | |||
0x2C,0x13,0x04,0x06,0x02,0x07, | |||
0x37,0x66,0xEC, | |||
// '-' | |||
0x2D,0x0E,0x08,0x02,0x01,0x0A, | |||
0xFF,0xFF, | |||
// '.' | |||
0x2E,0x12,0x03,0x03,0x02,0x06, | |||
0xFF,0x80, | |||
// '/' | |||
0x2F,0x01,0x0A,0x15,0x01,0x0C, | |||
0x00,0x00,0x30,0x0C,0x06,0x01,0x80,0x60,0x30,0x0C,0x06,0x01,0x80,0xC0,0x30,0x18,0x06,0x03,0x00,0xC0,0x60,0x18,0x0E,0x03,0x00,0xC0,0x00, | |||
// '0' | |||
0x30,0x03,0x0D,0x12,0x01,0x0F, | |||
0x0F,0x80,0xFF,0x0E,0x18,0xE0,0x66,0x03,0x70,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x03,0xB0,0x19,0x81,0xC7,0x1C,0x3F,0xC0,0x7C,0x00, | |||
// '1' | |||
0x31,0x03,0x06,0x12,0x03,0x0B, | |||
0x10,0xC7,0x3C,0xB0,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0xFF,0xF0, | |||
// '2' | |||
0x32,0x03,0x0B,0x12,0x02,0x0F, | |||
0x1F,0x07,0xFB,0xC3,0xE0,0x30,0x06,0x00,0xC0,0x38,0x0E,0x07,0x81,0xE0,0xF8,0x3C,0x07,0x01,0xC0,0x30,0x06,0x00,0xFF,0xDF,0xFC, | |||
// '3' | |||
0x33,0x03,0x0B,0x12,0x02,0x0F, | |||
0x1F,0x0F,0xF9,0xC3,0x80,0x30,0x06,0x00,0xC0,0x78,0x7E,0x0F,0x80,0x78,0x03,0x80,0x30,0x06,0x00,0xF0,0x1F,0x0E,0x7F,0x83,0xE0, | |||
// '4' | |||
0x34,0x03,0x0D,0x12,0x02,0x0F, | |||
0x01,0xC0,0x0E,0x00,0xF0,0x0F,0x80,0x6C,0x07,0x60,0x33,0x03,0x98,0x38,0xC1,0x86,0x1C,0x31,0xFF,0xFF,0xFF,0x80,0x60,0x03,0x00,0x18,0x00,0xC0,0x06,0x00, | |||
// '5' | |||
0x35,0x02,0x0C,0x13,0x02,0x0F, | |||
0x00,0x0F,0xFE,0xFF,0xE6,0x00,0x60,0x0E,0x00,0xEF,0x8F,0xFC,0xF8,0x6E,0x07,0xC0,0x30,0x03,0x00,0x30,0x03,0x00,0x7C,0x06,0xE1,0xE7,0xFC,0x3F,0x00, | |||
// '6' | |||
0x36,0x03,0x0C,0x12,0x01,0x0F, | |||
0x03,0x00,0x70,0x0E,0x01,0xC0,0x38,0x03,0x00,0x60,0x06,0xF8,0xFF,0xEE,0x0E,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x77,0x0E,0x3F,0xC1,0xF8, | |||
// '7' | |||
0x37,0x02,0x0D,0x13,0x01,0x0F, | |||
0x00,0x07,0xFF,0xFF,0xFE,0x00,0xE0,0x0E,0x00,0x60,0x06,0x00,0x30,0x03,0x80,0x18,0x01,0xC0,0x0C,0x00,0x60,0x07,0x00,0x30,0x03,0x80,0x18,0x00,0xC0,0x04,0x00, | |||
// '8' | |||
0x38,0x02,0x0C,0x13,0x01,0x0F, | |||
0x00,0x00,0xFC,0x3F,0xE3,0x07,0x60,0x36,0x03,0x60,0x37,0x8F,0x3F,0xE1,0xFE,0x38,0xE7,0x07,0x60,0x36,0x03,0x60,0x36,0x03,0x30,0x63,0xFE,0x0F,0x80, | |||
// '9' | |||
0x39,0x03,0x0D,0x13,0x01,0x0F, | |||
0x0F,0x01,0xFE,0x1C,0x38,0xC0,0xCC,0x07,0x60,0x1B,0x00,0xD8,0x06,0xE0,0x73,0x87,0x8F,0xF8,0x3E,0xC0,0x0E,0x00,0x60,0x07,0x00,0xF0,0x1F,0x03,0xE0,0x1C,0x00, | |||
// ':' | |||
0x3A,0x09,0x03,0x0B,0x02,0x07, | |||
0xFF,0x80,0x00,0xFF,0x80, | |||
// ';' | |||
0x3B,0x09,0x04,0x0E,0x02,0x07, | |||
0xEE,0xE0,0x00,0x00,0x03,0x7E,0xCC, | |||
// '<' | |||
0x3C,0x09,0x07,0x0A,0x01,0x09, | |||
0x06,0x1C,0x71,0xC7,0x1E,0x1E,0x0E,0x0E,0x0C, | |||
// '=' | |||
0x3D,0x0A,0x09,0x09,0x01,0x0C, | |||
0xFF,0xFF,0xC0,0x00,0x00,0x00,0x03,0xFF,0xFF,0x00,0x00, | |||
// '>' | |||
0x3E,0x08,0x08,0x0B,0x01,0x0A, | |||
0x60,0x70,0x38,0x3C,0x1E,0x0F,0x06,0x0C,0x38,0x70,0xC0, | |||
// '?' | |||
0x3F,0x04,0x0B,0x12,0x01,0x0D, | |||
0x1E,0x0F,0xE3,0xC6,0x60,0x60,0x06,0x00,0xC0,0x18,0x07,0x01,0xE0,0xF8,0x3E,0x0F,0x01,0x80,0x00,0x00,0x01,0x80,0x30,0x06,0x00, | |||
// '@' | |||
0x40,0x02,0x13,0x14,0x01,0x16, | |||
0x03,0xF8,0x01,0xFF,0xC0,0x78,0x3C,0x1C,0x01,0xC3,0x00,0x1C,0xC1,0xC1,0x98,0xF8,0x1E,0x3C,0x03,0xC6,0x30,0x79,0x8E,0x0F,0x31,0xC1,0xE6,0x78,0x6C,0x7F,0xFC,0xC7,0x3E,0x18,0x00,0x01,0x80,0x00,0x38,0x00,0x03,0xC0,0xE0,0x1F,0xFC,0x00,0xFE,0x00, | |||
// 'A' | |||
0x41,0x03,0x0E,0x12,0x01,0x11, | |||
0x00,0x80,0x07,0x00,0x1C,0x00,0xF0,0x03,0xC0,0x1D,0x80,0x76,0x03,0x98,0x0E,0x20,0x70,0xC1,0xFF,0x0F,0xFC,0x7C,0x19,0xC0,0x67,0x01,0xB8,0x07,0xE0,0x0F,0x00,0x30, | |||
// 'B' | |||
0x42,0x03,0x0B,0x13,0x03,0x0F, | |||
0x7C,0x1F,0xE3,0x0E,0x60,0xEC,0x0D,0x81,0xB0,0x36,0x0E,0xC3,0x9F,0xE3,0xFC,0x61,0xEC,0x0F,0x80,0xF0,0x1E,0x0E,0xC7,0xDF,0xE3,0xF0,0x00, | |||
// 'C' | |||
0x43,0x03,0x0D,0x12,0x01,0x0E, | |||
0x01,0xF8,0x3F,0xC3,0xC6,0x38,0x31,0x80,0x1C,0x01,0xC0,0x0C,0x00,0x60,0x06,0x00,0x30,0x01,0x80,0x0C,0x00,0x60,0x19,0x81,0xCE,0x3C,0x3F,0xC0,0xF8,0x00, | |||
// 'D' | |||
0x44,0x03,0x0D,0x12,0x02,0x11, | |||
0x60,0x07,0xC0,0x37,0x81,0x8F,0x0C,0x1C,0x60,0x73,0x01,0xD8,0x06,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x1B,0x01,0xDC,0x1C,0xFF,0xC1,0xF8,0x00, | |||
// 'E' | |||
0x45,0x03,0x0D,0x12,0x02,0x0F, | |||
0xFF,0xF7,0xFF,0xF0,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x7E,0xFF,0xF7,0xE0,0x30,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x00,0x7F,0xF1,0xFF,0x80, | |||
// 'F' | |||
0x46,0x03,0x0C,0x12,0x02,0x0F, | |||
0xFF,0xCF,0xFF,0xC0,0x7C,0x00,0xC0,0x0C,0x00,0xC0,0x0D,0xFE,0xFF,0xEF,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00, | |||
// 'G' | |||
0x47,0x03,0x0F,0x12,0x01,0x10, | |||
0x03,0xE0,0x0F,0xF0,0x38,0xE0,0xE0,0x03,0x80,0x06,0x00,0x18,0x00,0x30,0x00,0x61,0xFF,0x9F,0xFF,0x3C,0x36,0x00,0x6C,0x01,0x98,0x07,0x30,0x0C,0x30,0x70,0x7F,0xC0,0x3E,0x00, | |||
// 'H' | |||
0x48,0x03,0x0F,0x12,0x02,0x12, | |||
0xC0,0x03,0x80,0x0F,0x00,0x1E,0x00,0x3C,0x00,0x78,0x00,0xF0,0x01,0xE0,0x03,0xC0,0xFF,0xFF,0xFF,0xFC,0x1E,0x00,0x3C,0x00,0x78,0x00,0xF0,0x01,0xE0,0x03,0xC0,0x07,0x80,0x0C, | |||
// 'I' | |||
0x49,0x03,0x0C,0x12,0x00,0x0D, | |||
0xFF,0xEF,0xFF,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0xFF,0xFF,0xFF, | |||
// 'J' | |||
0x4A,0x03,0x0E,0x12,0x01,0x10, | |||
0x1F,0xFC,0x7F,0xF0,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0xC0,0xC3,0x06,0x0E,0x18,0x1C,0x60,0x3F,0x80,0x3C,0x00, | |||
// 'K' | |||
0x4B,0x03,0x0C,0x12,0x03,0x0F, | |||
0xC0,0x6C,0x0E,0xC1,0xCC,0x38,0xC7,0x0C,0xE0,0xDC,0x0F,0x80,0xF0,0x0F,0x00,0xF8,0x0F,0xC0,0xDE,0x0C,0xF0,0xC7,0x8C,0x1E,0xC0,0xFC,0x07, | |||
// 'L' | |||
0x4C,0x03,0x0B,0x12,0x01,0x0D, | |||
0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xEF,0xFC, | |||
// 'M' | |||
0x4D,0x03,0x13,0x13,0x01,0x15, | |||
0x0C,0x06,0x01,0x80,0xC0,0x78,0x3C,0x0F,0x07,0x81,0xE0,0xF0,0x3C,0x1E,0x07,0x83,0xC1,0xD8,0xEC,0x3B,0x1D,0x87,0x63,0xB0,0xCC,0xE6,0x38,0xDC,0x47,0x1B,0x8C,0xE3,0xF1,0xB8,0x3C,0x37,0x07,0x86,0xE0,0xF0,0x7C,0x1E,0x0F,0x01,0x81,0x80, | |||
// 'N' | |||
0x4E,0x03,0x11,0x12,0x01,0x13, | |||
0x60,0x01,0x38,0x00,0xDE,0x00,0x6F,0x00,0x37,0xC0,0x1B,0x70,0x0D,0x9C,0x06,0xCF,0x03,0x63,0x81,0xB0,0xE0,0xD8,0x38,0x6C,0x0E,0x36,0x03,0x9B,0x00,0xED,0x80,0x3E,0xC0,0x0F,0x60,0x03,0xB0,0x00,0xC0, | |||
// 'O' | |||
0x4F,0x03,0x11,0x12,0x01,0x13, | |||
0x01,0xF8,0x03,0xFF,0x07,0x81,0xC3,0x00,0x63,0x00,0x1B,0x80,0x0D,0x80,0x07,0xC0,0x03,0xC0,0x01,0xE0,0x00,0xF0,0x00,0xF8,0x00,0x6C,0x00,0x33,0x00,0x31,0xC0,0x38,0x70,0x78,0x1F,0xF8,0x03,0xF0,0x00, | |||
// 'P' | |||
0x50,0x03,0x0B,0x12,0x01,0x0D, | |||
0xFE,0x1F,0xF3,0x0F,0x60,0x7C,0x07,0x80,0xF0,0x1E,0x06,0xC3,0xDF,0xF3,0xF8,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x00, | |||
// 'Q' | |||
0x51,0x03,0x14,0x17,0x01,0x15, | |||
0x01,0xF8,0x00,0x7F,0xE0,0x1E,0x07,0x03,0x80,0x18,0x30,0x01,0xC6,0x00,0x0C,0x60,0x00,0xEC,0x00,0x06,0xC0,0x00,0x6C,0x00,0x06,0xC0,0x00,0x6C,0x00,0x06,0x60,0xE0,0xE7,0x0F,0x0C,0x38,0x79,0xC1,0xC3,0xF8,0x0F,0xFF,0x00,0x3F,0x78,0x00,0x03,0xC0,0x00,0x1E,0x00,0x00,0xF0,0x00,0x07,0x00,0x00,0x20, | |||
// 'R' | |||
0x52,0x02,0x0D,0x13,0x01,0x0F, | |||
0x00,0x03,0xE0,0x3F,0xC1,0x8F,0x0C,0x0E,0x60,0x33,0x00,0xD8,0x06,0xC0,0x36,0x03,0xB0,0x79,0xFF,0x8F,0xF0,0x7F,0x83,0x1F,0x18,0x3C,0xC0,0xF6,0x01,0xF0,0x06, | |||
// 'S' | |||
0x53,0x03,0x0F,0x13,0x01,0x11, | |||
0x01,0xF0,0x07,0xF8,0x18,0x70,0x60,0x01,0x80,0x03,0x00,0x06,0x00,0x0E,0x00,0x0F,0xF0,0x07,0xF0,0x00,0xF0,0x00,0x70,0x00,0x60,0x00,0xD8,0x01,0xB8,0x06,0x78,0x3C,0x7F,0xE0,0x3F,0x00, | |||
// 'T' | |||
0x54,0x02,0x0F,0x13,0x01,0x10, | |||
0x00,0x01,0xFF,0xFD,0xFF,0xF8,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00, | |||
// 'U' | |||
0x55,0x03,0x11,0x12,0x01,0x12, | |||
0x60,0x03,0x30,0x01,0x98,0x00,0xCC,0x00,0x66,0x00,0x33,0x00,0x19,0x80,0x0C,0xC0,0x06,0x60,0x03,0x30,0x01,0x98,0x01,0xCC,0x00,0xC7,0x00,0x61,0x80,0x70,0xE0,0x30,0x38,0x38,0x0F,0xF8,0x01,0xF0,0x00, | |||
// 'V' | |||
0x56,0x03,0x0E,0x13,0x02,0x10, | |||
0x80,0x0F,0x00,0x3C,0x01,0xB0,0x06,0x60,0x31,0x80,0xC6,0x03,0x0C,0x18,0x30,0x60,0xC1,0x81,0x8C,0x06,0x30,0x0D,0x80,0x36,0x00,0xF8,0x01,0xC0,0x07,0x00,0x08,0x00,0x00,0x00, | |||
// 'W' | |||
0x57,0x03,0x17,0x12,0x01,0x19, | |||
0xC0,0x20,0x0F,0xC0,0x60,0x19,0x81,0xC0,0x23,0x03,0x80,0xC6,0x07,0x01,0x86,0x1E,0x03,0x0C,0x36,0x0C,0x18,0x6C,0x18,0x11,0x98,0x60,0x33,0x30,0xC0,0x66,0x61,0x80,0xD8,0x66,0x01,0xB0,0xCC,0x01,0xC1,0xB0,0x03,0x83,0x60,0x07,0x07,0x80,0x0C,0x07,0x00,0x08,0x0E,0x00, | |||
// 'X' | |||
0x58,0x03,0x10,0x12,0x01,0x11, | |||
0x60,0x03,0x70,0x07,0x38,0x0E,0x1C,0x1C,0x0C,0x1C,0x0E,0x38,0x07,0x70,0x03,0xE0,0x01,0xC0,0x03,0xC0,0x07,0xE0,0x07,0x70,0x0E,0x38,0x1C,0x18,0x38,0x1C,0x70,0x0E,0xE0,0x07,0xC0,0x03, | |||
// 'Y' | |||
0x59,0x03,0x0F,0x13,0x00,0x10, | |||
0x60,0x06,0xE0,0x1D,0xC0,0x31,0xC0,0xE1,0xC1,0x83,0x83,0x03,0x8C,0x07,0x18,0x07,0x70,0x0F,0xC0,0x0F,0x80,0x0F,0x00,0x1C,0x00,0x38,0x00,0x60,0x01,0xC0,0x03,0x00,0x06,0x00,0x08,0x00, | |||
// 'Z' | |||
0x5A,0x03,0x0F,0x12,0x01,0x11, | |||
0xFF,0xFF,0xFF,0xFC,0x00,0xF0,0x03,0x80,0x0E,0x00,0x3C,0x00,0xF0,0x03,0xC0,0x07,0x00,0x1E,0x00,0x38,0x00,0xE0,0x03,0xC0,0x07,0x00,0x1C,0x00,0x70,0x00,0xFF,0xFF,0xFF,0xFC, | |||
// '[' | |||
0x5B,0x01,0x07,0x1A,0x01,0x09, | |||
0x00,0xFD,0xFB,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x3F,0x7E,0x00, | |||
// '\' | |||
0x5C,0x03,0x0B,0x14,0x02,0x0D, | |||
0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x60,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x20,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x60, | |||
// ']' | |||
0x5D,0x01,0x07,0x1A,0x02,0x09, | |||
0x01,0xFB,0xF0,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x7E,0xFC,0x00, | |||
// '^' | |||
0x5E,0x02,0x0A,0x06,0x02,0x0E, | |||
0x0C,0x07,0x83,0xF1,0xCE,0xE1,0xF0,0x30, | |||
// '_' | |||
0x5F,0x16,0x0F,0x04,0x00,0x0F, | |||
0x00,0x01,0xFF,0xFF,0xFF,0xF8,0x00,0x00, | |||
// '`' | |||
0x60,0x02,0x05,0x06,0x02,0x0D, | |||
0xC7,0x1C,0x63,0x8C, | |||
// 'a' | |||
0x61,0x09,0x0B,0x0C,0x01,0x0C, | |||
0x0F,0x87,0xF9,0xE3,0x30,0x6E,0x0D,0x81,0xB0,0x36,0x06,0xC0,0xCC,0x39,0xFF,0x9F,0x30, | |||
// 'b' | |||
0x62,0x02,0x0C,0x13,0x01,0x0E, | |||
0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x78,0x7F,0xC7,0x8E,0x60,0x76,0x03,0x60,0x36,0x03,0x60,0x36,0x06,0x70,0xE7,0xFC,0x7F,0x00, | |||
// 'c' | |||
0x63,0x09,0x0A,0x0C,0x01,0x0C, | |||
0x0F,0x07,0xF3,0x0D,0x80,0x60,0x30,0x0C,0x03,0x00,0xC0,0x1C,0x33,0xFC,0x7C, | |||
// 'd' | |||
0x64,0x02,0x0C,0x13,0x01,0x0E, | |||
0x00,0x20,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x61,0xF6,0x3F,0xE7,0x0E,0x60,0x6C,0x06,0xC0,0x6C,0x06,0xC0,0x6E,0x06,0x70,0xE3,0xFE,0x1F,0x60, | |||
// 'e' | |||
0x65,0x09,0x0B,0x0C,0x01,0x0D, | |||
0x1F,0x07,0xF9,0xC7,0x30,0xEC,0x79,0xBE,0x3E,0x07,0x00,0xC0,0x6E,0x1D,0xFF,0x0F,0x80, | |||
// 'f' | |||
0x66,0x02,0x0A,0x14,0x01,0x0C, | |||
0x03,0x83,0xE0,0xE0,0x70,0x18,0x06,0x01,0x83,0xFF,0xFF,0xC6,0x01,0x80,0x60,0x18,0x06,0x01,0x80,0x60,0x18,0x06,0x01,0x80,0x60, | |||
// 'g' | |||
0x67,0x09,0x0A,0x13,0x02,0x0D, | |||
0x0F,0x0F,0xF7,0x0D,0x83,0xC0,0xF0,0x3C,0x1F,0x07,0xC1,0xD8,0xF7,0xEC,0xF3,0x00,0xC0,0x30,0x18,0x06,0x03,0xBF,0xC7,0xE0, | |||
// 'h' | |||
0x68,0x02,0x0B,0x13,0x01,0x0E, | |||
0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x1E,0x6F,0xEF,0x8D,0xE1,0xB8,0x36,0x06,0xC0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0x80, | |||
// 'i' | |||
0x69,0x04,0x02,0x11,0x03,0x07, | |||
0xF0,0x3F,0xFF,0xFF,0xC0, | |||
// 'j' | |||
0x6A,0x04,0x08,0x18,0x00,0x0A, | |||
0x03,0x03,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xC3,0xE3,0x77,0x7E,0x1C, | |||
// 'k' | |||
0x6B,0x03,0x0B,0x13,0x02,0x0E, | |||
0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x36,0x0E,0xC7,0x99,0xE3,0x70,0x7E,0x0F,0xE1,0xCE,0x30,0xE6,0x0E,0xC0,0xF8,0x08,0x00,0x00, | |||
// 'l' | |||
0x6C,0x02,0x02,0x13,0x03,0x07, | |||
0xFF,0xFF,0xFF,0xFF,0xFC, | |||
// 'm' | |||
0x6D,0x09,0x10,0x0C,0x01,0x12, | |||
0x67,0x3C,0x6F,0xFE,0x7D,0xEE,0x79,0x86,0x71,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86, | |||
// 'n' | |||
0x6E,0x09,0x0B,0x0C,0x01,0x0D, | |||
0x63,0x8D,0xF9,0xF1,0xBC,0x37,0x06,0xE0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0xB0,0x30, | |||
// 'o' | |||
0x6F,0x09,0x0C,0x0C,0x01,0x0D, | |||
0x0F,0x81,0xFC,0x38,0xC3,0x06,0x60,0x66,0x06,0x60,0x66,0x06,0x60,0xE3,0x1C,0x1F,0x80,0xF0, | |||
// 'p' | |||
0x70,0x08,0x0A,0x14,0x02,0x0D, | |||
0xC0,0x33,0xCF,0xFB,0xC6,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x7C,0x1B,0xFC,0xFE,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00, | |||
// 'q' | |||
0x71,0x08,0x0A,0x14,0x01,0x0C, | |||
0x00,0x03,0xF3,0xFD,0xE3,0x60,0xF8,0x3C,0x0F,0x03,0xC0,0xF0,0x76,0x1D,0xFF,0x1F,0x80,0x60,0x18,0x06,0x01,0x80,0x60,0x18,0x06, | |||
// 'r' | |||
0x72,0x09,0x09,0x0C,0x01,0x0B, | |||
0xCF,0x6F,0xFE,0x7C,0x3C,0x1E,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x00, | |||
// 's' | |||
0x73,0x09,0x09,0x0C,0x02,0x0C, | |||
0x03,0x9F,0xDE,0x7C,0x3E,0x07,0xF0,0xFC,0x07,0x01,0xE0,0xFF,0xC7,0xC0, | |||
// 't' | |||
0x74,0x05,0x0A,0x10,0x00,0x0A, | |||
0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30, | |||
// 'u' | |||
0x75,0x09,0x0B,0x0C,0x01,0x0C, | |||
0xC0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0xB0,0x36,0x06,0xC0,0xD8,0x19,0xFF,0x1F,0x60, | |||
// 'v' | |||
0x76,0x09,0x0B,0x0D,0x01,0x0C, | |||
0xC0,0x78,0x1F,0x83,0x30,0x67,0x1C,0x63,0x0C,0xE0,0xD8,0x1E,0x03,0xC0,0x30,0x06,0x00,0x00, | |||
// 'w' | |||
0x77,0x09,0x0F,0x0D,0x01,0x11, | |||
0xC1,0x87,0x83,0x0F,0x0E,0x1E,0x1C,0x66,0x7C,0xCC,0xD9,0x99,0x36,0x36,0x6C,0x7C,0xD8,0x70,0xE0,0xE1,0xC0,0x83,0x80,0x00,0x00, | |||
// 'x' | |||
0x78,0x09,0x0D,0x0D,0x01,0x0E, | |||
0x60,0x1B,0x81,0xCE,0x1C,0x39,0xC0,0xFC,0x03,0xC0,0x3C,0x03,0xF0,0x39,0xC3,0x87,0x38,0x1D,0x80,0x70,0x01,0x80, | |||
// 'y' | |||
0x79,0x09,0x0C,0x13,0x00,0x0D, | |||
0xC0,0x3E,0x07,0x60,0x67,0x0C,0x30,0xC3,0x98,0x19,0x81,0xD8,0x0F,0x00,0xF0,0x06,0x00,0x60,0x0C,0x00,0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x30,0x00, | |||
// 'z' | |||
0x7A,0x09,0x0B,0x0C,0x01,0x0D, | |||
0xFF,0xFF,0xFC,0x07,0x00,0xC0,0x30,0x0C,0x03,0x80,0xE0,0x38,0x0E,0x03,0xFF,0xFF,0xF0, | |||
// '{' | |||
0x7B,0x02,0x08,0x18,0x01,0x09, | |||
0x0F,0x1F,0x38,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x60,0xE0,0xE0,0x70,0x30,0x30,0x30,0x30,0x30,0x38,0x18,0x1F,0x07, | |||
// '|' | |||
0x7C,0x01,0x02,0x18,0x04,0x0A, | |||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |||
// '}' | |||
0x7D,0x02,0x08,0x18,0x01,0x09, | |||
0x70,0xF8,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x06,0x07,0x07,0x0E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1C,0x18,0xF8,0xE0, | |||
// '~' | |||
0x7E,0x0B,0x0C,0x05,0x01,0x0E, | |||
0x38,0x37,0xE3,0xE7,0x7C,0x3E,0x01,0xC0, | |||
// Terminator | |||
0xFF | |||
}; |
@@ -0,0 +1,7 @@ | |||
# | |||
# Main Makefile. This is basically the same as a component makefile. | |||
# | |||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) | |||
COMPONENT_SRCDIRS := . | |||
COMPONENT_ADD_INCLUDEDIRS := . |
@@ -0,0 +1,718 @@ | |||
// This comes with no warranty, implied or otherwise | |||
// This data structure was designed to support Proportional fonts | |||
// on Arduinos. It can however handle any ttf font that has been converted | |||
// using the conversion program. These could be fixed width or proportional | |||
// fonts. Individual characters do not have to be multiples of 8 bits wide. | |||
// Any width is fine and does not need to be fixed. | |||
// The data bits are packed to minimize data requirements, but the tradeoff | |||
// is that a header is required per character. | |||
// dejavuX.c | |||
// Point Size : 18 | |||
// Memory usage : 4676 bytes | |||
// # characters : 224 | |||
// Header Format (to make Arduino UTFT Compatible): | |||
// ------------------------------------------------ | |||
// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) | |||
// Character Height | |||
// First Character (Reserved. 0x00) | |||
// Number Of Characters (Reserved. 0x00) | |||
const unsigned char tft_Dejavu18[] = | |||
{ | |||
0x00, 0x12, 0x00, 0x00, | |||
// Individual Character Format: | |||
// ---------------------------- | |||
// Character Code | |||
// Adjusted Y Offset | |||
// Width | |||
// Height | |||
// xOffset | |||
// xDelta (the distance to move the cursor. Effective width of the character.) | |||
// Data[n] | |||
// NOTE: You can remove any of these characters if they are not needed in | |||
// your application. The first character number in each Glyph indicates | |||
// the ASCII character code. Therefore, these do not have to be sequential. | |||
// Just remove all the content for a particular character to save space. | |||
// ' ' | |||
0x20,0x11,0x00,0x00,0x00,0x06, | |||
// '!' | |||
0x21,0x04,0x02,0x0D,0x03,0x07, | |||
0xFF,0xFF,0xC3,0xC0, | |||
// '"' | |||
0x22,0x04,0x06,0x05,0x01,0x08, | |||
0xCF,0x3C,0xF3,0xCC, | |||
// '#' | |||
0x23,0x03,0x0C,0x0E,0x01,0x0F, | |||
0x04,0x40,0x44,0x0C,0xC0,0xC8,0x7F,0xF7,0xFF,0x09,0x81,0x90,0xFF,0xEF,0xFE,0x13,0x03,0x30,0x32,0x02,0x20, | |||
// '$' | |||
0x24,0x03,0x0A,0x11,0x01,0x0B, | |||
0x08,0x02,0x03,0xE1,0xFC,0xE9,0x32,0x0F,0x81,0xF8,0x0F,0x02,0x60,0x9A,0x2E,0xFF,0x1F,0x80,0x80,0x20,0x08,0x00, | |||
// '%' | |||
0x25,0x04,0x0F,0x0D,0x01,0x11, | |||
0x78,0x10,0x90,0x43,0x31,0x86,0x62,0x0C,0xC8,0x19,0x10,0x1E,0x4F,0x01,0x12,0x02,0x66,0x08,0xCC,0x31,0x98,0x41,0x21,0x03,0xC0, | |||
// '&' | |||
0x26,0x04,0x0C,0x0D,0x01,0x0D, | |||
0x0F,0x01,0xF8,0x30,0x83,0x00,0x38,0x03,0xC0,0x7E,0x6C,0x76,0xC3,0xCC,0x18,0xE1,0xC7,0xFE,0x3E,0x70, | |||
// ''' | |||
0x27,0x04,0x02,0x05,0x01,0x04, | |||
0xFF,0xC0, | |||
// '(' | |||
0x28,0x03,0x04,0x10,0x02,0x07, | |||
0x32,0x66,0x4C,0xCC,0xCC,0xC4,0x66,0x23, | |||
// ')' | |||
0x29,0x03,0x04,0x10,0x01,0x07, | |||
0xC4,0x66,0x23,0x33,0x33,0x32,0x66,0x4C, | |||
// '*' | |||
0x2A,0x04,0x07,0x08,0x01,0x09, | |||
0x11,0x25,0x51,0xC3,0x8A,0xA4,0x88, | |||
// '+' | |||
0x2B,0x05,0x0C,0x0C,0x02,0x0F, | |||
0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x0F,0xFF,0xFF,0xF0,0x60,0x06,0x00,0x60,0x06,0x00,0x60, | |||
// ',' | |||
0x2C,0x0F,0x03,0x04,0x01,0x06, | |||
0x6D,0x40, | |||
// '-' | |||
0x2D,0x0B,0x05,0x02,0x01,0x07, | |||
0xFF,0xC0, | |||
// '.' | |||
0x2E,0x0F,0x02,0x02,0x02,0x06, | |||
0xF0, | |||
// '/' | |||
0x2F,0x04,0x06,0x0F,0x00,0x06, | |||
0x0C,0x31,0x86,0x18,0xE3,0x0C,0x31,0xC6,0x18,0x63,0x0C,0x00, | |||
// '0' | |||
0x30,0x04,0x09,0x0D,0x01,0x0B, | |||
0x3E,0x3F,0x98,0xD8,0x3C,0x1E,0x0F,0x07,0x83,0xC1,0xE0,0xD8,0xCF,0xE3,0xE0, | |||
// '1' | |||
0x31,0x04,0x08,0x0D,0x02,0x0B, | |||
0x38,0xF8,0xD8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xFF, | |||
// '2' | |||
0x32,0x04,0x09,0x0D,0x01,0x0B, | |||
0x7C,0x7F,0x21,0xC0,0x60,0x30,0x30,0x18,0x18,0x18,0x18,0x18,0x1F,0xEF,0xF0, | |||
// '3' | |||
0x33,0x04,0x09,0x0D,0x01,0x0B, | |||
0x7E,0x7F,0xA0,0xE0,0x30,0x39,0xF0,0xFC,0x07,0x01,0x80,0xE0,0xFF,0xE7,0xE0, | |||
// '4' | |||
0x34,0x04,0x0A,0x0D,0x01,0x0B, | |||
0x07,0x01,0xC0,0xB0,0x6C,0x13,0x08,0xC6,0x31,0x0C,0xFF,0xFF,0xF0,0x30,0x0C,0x03,0x00, | |||
// '5' | |||
0x35,0x04,0x08,0x0D,0x01,0x0B, | |||
0x7E,0x7E,0x60,0x60,0x7C,0x7E,0x47,0x03,0x03,0x03,0x87,0xFE,0x7C, | |||
// '6' | |||
0x36,0x04,0x09,0x0D,0x01,0x0B, | |||
0x1E,0x1F,0x9C,0x5C,0x0C,0x06,0xF3,0xFD,0xC7,0xC1,0xE0,0xD8,0xEF,0xE1,0xE0, | |||
// '7' | |||
0x37,0x04,0x08,0x0D,0x01,0x0B, | |||
0xFF,0xFF,0x06,0x06,0x06,0x0E,0x0C,0x0C,0x1C,0x18,0x18,0x38,0x30, | |||
// '8' | |||
0x38,0x04,0x09,0x0D,0x01,0x0B, | |||
0x3E,0x3F,0xB8,0xF8,0x3E,0x39,0xF1,0xFD,0xC7,0xC1,0xE0,0xF8,0xEF,0xE3,0xE0, | |||
// '9' | |||
0x39,0x04,0x09,0x0D,0x01,0x0B, | |||
0x3C,0x3F,0xB8,0xD8,0x3C,0x1F,0x1D,0xFE,0x7B,0x01,0x81,0xD1,0xCF,0xC3,0xC0, | |||
// ':' | |||
0x3A,0x08,0x02,0x09,0x02,0x06, | |||
0xF0,0x03,0xC0, | |||
// ';' | |||
0x3B,0x08,0x03,0x0B,0x01,0x06, | |||
0x6C,0x00,0x03,0x6A,0x00, | |||
// '<' | |||
0x3C,0x07,0x0B,0x0A,0x02,0x0F, | |||
0x00,0x20,0x3C,0x1F,0x1F,0x0F,0x81,0xF0,0x0F,0x80,0x3E,0x01,0xE0,0x04, | |||
// '=' | |||
0x3D,0x08,0x0B,0x06,0x02,0x0F, | |||
0xFF,0xFF,0xFC,0x00,0x00,0x0F,0xFF,0xFF,0xC0, | |||
// '>' | |||
0x3E,0x07,0x0B,0x0A,0x02,0x0F, | |||
0x80,0x1E,0x01,0xF0,0x07,0xC0,0x3E,0x07,0xC3,0xE3,0xE0,0xF0,0x10,0x00, | |||
// '?' | |||
0x3F,0x04,0x07,0x0D,0x01,0x0A, | |||
0x79,0xFA,0x38,0x30,0x61,0x86,0x18,0x30,0x60,0x01,0x83,0x00, | |||
// '@' | |||
0x40,0x04,0x10,0x10,0x01,0x12, | |||
0x07,0xE0,0x1F,0xF8,0x3C,0x1C,0x70,0x06,0x60,0x07,0xE3,0x63,0xC7,0xE3,0xC6,0x63,0xC6,0x66,0xC7,0xFC,0xE3,0x70,0x60,0x00,0x70,0x00,0x3C,0x30,0x1F,0xF0,0x07,0xC0, | |||
// 'A' | |||
0x41,0x04,0x0C,0x0D,0x00,0x0C, | |||
0x06,0x00,0x60,0x0F,0x00,0xF0,0x19,0x81,0x98,0x19,0x83,0x0C,0x3F,0xC7,0xFE,0x60,0x66,0x06,0xC0,0x30, | |||
// 'B' | |||
0x42,0x04,0x09,0x0D,0x02,0x0C, | |||
0xFC,0x7F,0xB0,0xD8,0x6C,0x37,0xF3,0xF9,0x86,0xC1,0xE0,0xF0,0xFF,0xEF,0xE0, | |||
// 'C' | |||
0x43,0x04,0x0B,0x0D,0x01,0x0D, | |||
0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x0C,0x01,0xC0,0x9F,0xF0,0xFC, | |||
// 'D' | |||
0x44,0x04,0x0B,0x0D,0x02,0x0E, | |||
0xFE,0x1F,0xF3,0x07,0x60,0x6C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x1B,0x07,0x7F,0xCF,0xE0, | |||
// 'E' | |||
0x45,0x04,0x08,0x0D,0x02,0x0B, | |||
0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, | |||
// 'F' | |||
0x46,0x04,0x08,0x0D,0x02,0x0A, | |||
0xFF,0xFF,0xC0,0xC0,0xC0,0xFE,0xFE,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, | |||
// 'G' | |||
0x47,0x04,0x0B,0x0D,0x01,0x0E, | |||
0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x87,0xF0,0xFE,0x03,0xC0,0x6C,0x0D,0xC1,0x9F,0xE0,0xF8, | |||
// 'H' | |||
0x48,0x04,0x0A,0x0D,0x02,0x0E, | |||
0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xFF,0xFF,0xFF,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xC0, | |||
// 'I' | |||
0x49,0x04,0x02,0x0D,0x02,0x06, | |||
0xFF,0xFF,0xFF,0xC0, | |||
// 'J' | |||
0x4A,0x04,0x05,0x11,0xFF,0x06, | |||
0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0x8C,0xFE,0xE0, | |||
// 'K' | |||
0x4B,0x04,0x0B,0x0D,0x02,0x0C, | |||
0xC1,0x98,0x63,0x18,0x66,0x0D,0x81,0xE0,0x3C,0x06,0xC0,0xCC,0x18,0xC3,0x0C,0x60,0xCC,0x0C, | |||
// 'L' | |||
0x4C,0x04,0x08,0x0D,0x02,0x0A, | |||
0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, | |||
// 'M' | |||
0x4D,0x04,0x0C,0x0D,0x02,0x10, | |||
0xE0,0x7F,0x0F,0xF0,0xFD,0x8B,0xD9,0xBD,0x9B,0xCF,0x3C,0xF3,0xC6,0x3C,0x63,0xC0,0x3C,0x03,0xC0,0x30, | |||
// 'N' | |||
0x4E,0x04,0x0A,0x0D,0x02,0x0E, | |||
0xE0,0xF8,0x3F,0x0F,0xC3,0xD8,0xF6,0x3C,0xCF,0x1B,0xC6,0xF0,0xFC,0x3F,0x07,0xC1,0xC0, | |||
// 'O' | |||
0x4F,0x04,0x0C,0x0D,0x01,0x0E, | |||
0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80, | |||
// 'P' | |||
0x50,0x04,0x08,0x0D,0x02,0x0B, | |||
0xFC,0xFE,0xC7,0xC3,0xC3,0xC7,0xFE,0xFC,0xC0,0xC0,0xC0,0xC0,0xC0, | |||
// 'Q' | |||
0x51,0x04,0x0C,0x0F,0x01,0x0E, | |||
0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80,0x18,0x00,0xC0, | |||
// 'R' | |||
0x52,0x04,0x0A,0x0D,0x02,0x0D, | |||
0xFC,0x3F,0x8C,0x73,0x0C,0xC3,0x31,0xCF,0xE3,0xF0,0xC6,0x30,0xCC,0x33,0x06,0xC1,0xC0, | |||
// 'S' | |||
0x53,0x04,0x0A,0x0D,0x01,0x0B, | |||
0x3E,0x1F,0xCE,0x13,0x00,0xC0,0x1F,0x03,0xF0,0x0E,0x01,0x80,0x68,0x3B,0xFC,0x7E,0x00, | |||
// 'T' | |||
0x54,0x04,0x0C,0x0D,0x00,0x0C, | |||
0xFF,0xFF,0xFF,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00, | |||
// 'U' | |||
0x55,0x04,0x0A,0x0D,0x02,0x0E, | |||
0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x36,0x19,0xFE,0x1E,0x00, | |||
// 'V' | |||
0x56,0x04,0x0C,0x0D,0x00,0x0C, | |||
0xC0,0x36,0x06,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60,0x06,0x00, | |||
// 'W' | |||
0x57,0x04,0x11,0x0D,0x01,0x13, | |||
0xC1,0xC1,0xE0,0xE0,0xD8,0xF8,0xCC,0x6C,0x66,0x36,0x33,0x1B,0x18,0xD8,0xD8,0x6C,0x6C,0x36,0x36,0x1F,0x1F,0x07,0x07,0x03,0x83,0x81,0xC1,0xC0, | |||
// 'X' | |||
0x58,0x04,0x0B,0x0D,0x01,0x0D, | |||
0x70,0xE6,0x18,0xE6,0x0D,0xC0,0xF0,0x1C,0x03,0x80,0x78,0x1B,0x07,0x30,0xC7,0x30,0x6E,0x0E, | |||
// 'Y' | |||
0x59,0x04,0x0C,0x0D,0x00,0x0C, | |||
0xE0,0x76,0x06,0x30,0xC1,0x98,0x19,0x80,0xF0,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00, | |||
// 'Z' | |||
0x5A,0x04,0x0B,0x0D,0x01,0x0D, | |||
0xFF,0xFF,0xFC,0x07,0x01,0xC0,0x30,0x0E,0x03,0x80,0xE0,0x18,0x06,0x01,0xC0,0x7F,0xFF,0xFE, | |||
// '[' | |||
0x5B,0x03,0x04,0x10,0x01,0x07, | |||
0xFF,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xFF, | |||
// '\' | |||
0x5C,0x04,0x06,0x0F,0x00,0x06, | |||
0xC3,0x06,0x18,0x61,0xC3,0x0C,0x30,0xE1,0x86,0x18,0x30,0xC0, | |||
// ']' | |||
0x5D,0x03,0x04,0x10,0x02,0x07, | |||
0xFF,0x33,0x33,0x33,0x33,0x33,0x33,0xFF, | |||
// '^' | |||
0x5E,0x04,0x0B,0x05,0x02,0x0F, | |||
0x0E,0x03,0xE0,0xC6,0x30,0x6C,0x06, | |||
// '_' | |||
0x5F,0x13,0x09,0x02,0x00,0x09, | |||
0xFF,0xFF,0xC0, | |||
// '`' | |||
0x60,0x03,0x04,0x03,0x02,0x09, | |||
0xC6,0x30, | |||
// 'a' | |||
0x61,0x07,0x08,0x0A,0x01,0x0A, | |||
0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, | |||
// 'b' | |||
0x62,0x03,0x09,0x0E,0x02,0x0B, | |||
0xC0,0x60,0x30,0x18,0x0D,0xE7,0xFB,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x7F,0xF6,0xF0, | |||
// 'c' | |||
0x63,0x07,0x08,0x0A,0x01,0x09, | |||
0x1E,0x7F,0x61,0xC0,0xC0,0xC0,0xC0,0x61,0x7F,0x1E, | |||
// 'd' | |||
0x64,0x03,0x09,0x0E,0x01,0x0B, | |||
0x01,0x80,0xC0,0x60,0x33,0xDB,0xFF,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x77,0xF9,0xEC, | |||
// 'e' | |||
0x65,0x07,0x0A,0x0A,0x01,0x0B, | |||
0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, | |||
// 'f' | |||
0x66,0x03,0x07,0x0E,0x00,0x06, | |||
0x1E,0x7C,0xC1,0x8F,0xFF,0xCC,0x18,0x30,0x60,0xC1,0x83,0x06,0x00, | |||
// 'g' | |||
0x67,0x07,0x09,0x0E,0x01,0x0B, | |||
0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x68,0x67,0xF1,0xF0, | |||
// 'h' | |||
0x68,0x03,0x08,0x0E,0x02,0x0B, | |||
0xC0,0xC0,0xC0,0xC0,0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, | |||
// 'i' | |||
0x69,0x03,0x02,0x0E,0x02,0x05, | |||
0xF0,0xFF,0xFF,0xF0, | |||
// 'j' | |||
0x6A,0x03,0x04,0x12,0x00,0x05, | |||
0x33,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0xEC, | |||
// 'k' | |||
0x6B,0x03,0x09,0x0E,0x02,0x0A, | |||
0xC0,0x60,0x30,0x18,0x0C,0x36,0x33,0x31,0xB0,0xF0,0x78,0x36,0x19,0x8C,0x66,0x18, | |||
// 'l' | |||
0x6C,0x03,0x02,0x0E,0x02,0x05, | |||
0xFF,0xFF,0xFF,0xF0, | |||
// 'm' | |||
0x6D,0x07,0x0E,0x0A,0x02,0x11, | |||
0xDC,0x7B,0xFB,0xEE,0x79,0xF0,0xC3,0xC3,0x0F,0x0C,0x3C,0x30,0xF0,0xC3,0xC3,0x0F,0x0C,0x30, | |||
// 'n' | |||
0x6E,0x07,0x08,0x0A,0x02,0x0B, | |||
0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, | |||
// 'o' | |||
0x6F,0x07,0x0A,0x0A,0x01,0x0B, | |||
0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, | |||
// 'p' | |||
0x70,0x07,0x09,0x0E,0x02,0x0B, | |||
0xDE,0x7F,0xB8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0xFF,0x6F,0x30,0x18,0x0C,0x06,0x00, | |||
// 'q' | |||
0x71,0x07,0x09,0x0E,0x01,0x0B, | |||
0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x60,0x30,0x18,0x0C, | |||
// 'r' | |||
0x72,0x07,0x06,0x0A,0x02,0x08, | |||
0xDF,0xFE,0x30,0xC3,0x0C,0x30,0xC3,0x00, | |||
// 's' | |||
0x73,0x07,0x08,0x0A,0x01,0x08, | |||
0x7C,0xFE,0xC2,0xE0,0x7C,0x1E,0x06,0x86,0xFE,0x78, | |||
// 't' | |||
0x74,0x04,0x06,0x0D,0x01,0x07, | |||
0x61,0x86,0x3F,0xFD,0x86,0x18,0x61,0x86,0x1F,0x3C, | |||
// 'u' | |||
0x75,0x07,0x08,0x0A,0x02,0x0B, | |||
0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, | |||
// 'v' | |||
0x76,0x07,0x0C,0x0A,0x00,0x0B, | |||
0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60, | |||
// 'w' | |||
0x77,0x07,0x0F,0x0A,0x01,0x10, | |||
0x63,0x8C,0xC7,0x19,0x8E,0x31,0xB6,0xC3,0x6D,0x86,0xDB,0x0F,0x1E,0x0E,0x38,0x1C,0x70,0x38,0xE0, | |||
// 'x' | |||
0x78,0x07,0x0A,0x0A,0x01,0x0B, | |||
0xE1,0xD8,0x63,0x30,0xCC,0x1E,0x07,0x83,0x30,0xCC,0x61,0xB8,0x70, | |||
// 'y' | |||
0x79,0x07,0x0C,0x0E,0x00,0x0B, | |||
0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80, | |||
// 'z' | |||
0x7A,0x07,0x08,0x0A,0x01,0x09, | |||
0xFF,0xFF,0x06,0x0C,0x1C,0x38,0x30,0x70,0xFF,0xFF, | |||
// '{' | |||
0x7B,0x03,0x08,0x11,0x02,0x0B, | |||
0x0F,0x1F,0x18,0x18,0x18,0x18,0x38,0xF0,0xF0,0x38,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F, | |||
// '|' | |||
0x7C,0x03,0x02,0x12,0x02,0x06, | |||
0xFF,0xFF,0xFF,0xFF,0xF0, | |||
// '}' | |||
0x7D,0x03,0x08,0x11,0x02,0x0B, | |||
0xF0,0xF8,0x18,0x18,0x18,0x18,0x1C,0x0F,0x0F,0x1C,0x18,0x18,0x18,0x18,0x18,0xF8,0xF0, | |||
// '~' | |||
0x7E,0x08,0x0B,0x05,0x02,0x0F, | |||
0x00,0x0F,0x87,0xFF,0xC3,0xE0,0x00, | |||
// '' | |||
0x7F,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '€' | |||
0x80,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '�' | |||
0x81,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '‚' | |||
0x82,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'ƒ' | |||
0x83,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '„' | |||
0x84,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '…' | |||
0x85,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '†' | |||
0x86,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '‡' | |||
0x87,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'ˆ' | |||
0x88,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '‰' | |||
0x89,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'Š' | |||
0x8A,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '‹' | |||
0x8B,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'Œ' | |||
0x8C,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '�' | |||
0x8D,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'Ž' | |||
0x8E,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '�' | |||
0x8F,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '�' | |||
0x90,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '‘' | |||
0x91,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '’' | |||
0x92,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '“' | |||
0x93,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '”' | |||
0x94,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '•' | |||
0x95,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '–' | |||
0x96,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '—' | |||
0x97,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '˜' | |||
0x98,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '™' | |||
0x99,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'š' | |||
0x9A,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '›' | |||
0x9B,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'œ' | |||
0x9C,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// '�' | |||
0x9D,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'ž' | |||
0x9E,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// 'Ÿ' | |||
0x9F,0x04,0x09,0x10,0x01,0x0B, | |||
0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0xFF, | |||
// ' ' | |||
0xA0,0x11,0x00,0x00,0x00,0x06, | |||
// '¡' | |||
0xA1,0x07,0x02,0x0D,0x03,0x07, | |||
0xF0,0xFF,0xFF,0xC0, | |||
// '¢' | |||
0xA2,0x04,0x08,0x10,0x02,0x0B, | |||
0x04,0x04,0x04,0x1E,0x7F,0x75,0xC4,0xC4,0xC4,0xC4,0x65,0x7F,0x1E,0x04,0x04,0x04, | |||
// '£' | |||
0xA3,0x04,0x09,0x0D,0x01,0x0B, | |||
0x0F,0x0F,0xCE,0x26,0x03,0x01,0x83,0xF9,0xFC,0x30,0x18,0x0C,0x1F,0xFF,0xF8, | |||
// '¤' | |||
0xA4,0x06,0x0A,0x0A,0x00,0x0B, | |||
0x00,0x10,0x13,0x58,0xFE,0x11,0x0C,0x61,0x10,0xFE,0x35,0x90,0x10, | |||
// '¥' | |||
0xA5,0x04,0x0A,0x0D,0x01,0x0B, | |||
0xC0,0xD8,0x66,0x18,0xCC,0x33,0x3F,0xF1,0xE0,0x30,0xFF,0xC3,0x00,0xC0,0x30,0x0C,0x00, | |||
// '¦' | |||
0xA6,0x04,0x02,0x10,0x02,0x06, | |||
0xFF,0xFC,0x3F,0xFF, | |||
// '§' | |||
0xA7,0x04,0x07,0x0F,0x01,0x09, | |||
0x3C,0xF9,0x83,0x03,0x0F,0x33,0x63,0x66,0x78,0x60,0x60,0xCF,0x9E,0x00, | |||
// '¨' | |||
0xA8,0x03,0x06,0x02,0x02,0x09, | |||
0xCF,0x30, | |||
// '©' | |||
0xA9,0x04,0x0D,0x0D,0x02,0x12, | |||
0x0F,0x81,0x83,0x18,0x0C,0x8F,0x28,0x80,0xC8,0x06,0x40,0x32,0x01,0x88,0x8A,0x38,0x98,0x0C,0x60,0xC0,0xF8,0x00, | |||
// 'ª' | |||
0xAA,0x04,0x08,0x09,0x01,0x08, | |||
0x7C,0x02,0x7E,0xC2,0x82,0xC6,0x7A,0x00,0xFE, | |||
// '«' | |||
0xAB,0x08,0x08,0x08,0x01,0x0B, | |||
0x11,0x33,0x66,0xCC,0xCC,0x66,0x33,0x11, | |||
// '¬' | |||
0xAC,0x09,0x0B,0x05,0x02,0x0F, | |||
0xFF,0xFF,0xFC,0x01,0x80,0x30,0x06, | |||
// '' | |||
0xAD,0x0B,0x05,0x02,0x01,0x07, | |||
0xFF,0xC0, | |||
// '®' | |||
0xAE,0x04,0x0D,0x0D,0x02,0x12, | |||
0x0F,0x81,0x83,0x18,0x0C,0x9E,0x28,0x88,0xC4,0x46,0x3C,0x31,0x21,0x88,0x8A,0x46,0x98,0x0C,0x60,0xC0,0xF8,0x00, | |||
// '¯' | |||
0xAF,0x04,0x05,0x02,0x02,0x09, | |||
0xFF,0xC0, | |||
// '°' | |||
0xB0,0x04,0x06,0x06,0x02,0x09, | |||
0x7B,0x38,0x61,0xCD,0xE0, | |||
// '±' | |||
0xB1,0x06,0x0C,0x0B,0x02,0x0F, | |||
0x06,0x00,0x60,0x06,0x0F,0xFF,0xFF,0xF0,0x60,0x06,0x00,0x60,0x00,0x0F,0xFF,0xFF,0xF0, | |||
// '²' | |||
0xB2,0x04,0x05,0x07,0x01,0x07, | |||
0x74,0x42,0x22,0x23,0xE0, | |||
// '³' | |||
0xB3,0x04,0x05,0x07,0x01,0x07, | |||
0xF0,0x5C,0x30,0x8F,0xC0, | |||
// '´' | |||
0xB4,0x03,0x04,0x03,0x03,0x09, | |||
0x36,0xC0, | |||
// 'µ' | |||
0xB5,0x07,0x09,0x0E,0x02,0x0B, | |||
0xC3,0x61,0xB0,0xD8,0x6C,0x36,0x1B,0x0D,0xCE,0xFF,0xEC,0xF0,0x18,0x0C,0x06,0x00, | |||
// '¶' | |||
0xB6,0x04,0x08,0x0F,0x01,0x0B, | |||
0x3F,0x79,0xF9,0xF9,0xF9,0xF9,0x79,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, | |||
// '·' | |||
0xB7,0x0A,0x02,0x02,0x02,0x06, | |||
0xF0, | |||
// '¸' | |||
0xB8,0x11,0x04,0x03,0x03,0x09, | |||
0x23,0xF0, | |||
// '¹' | |||
0xB9,0x04,0x05,0x07,0x01,0x07, | |||
0xE1,0x08,0x42,0x13,0xE0, | |||
// 'º' | |||
0xBA,0x04,0x07,0x09,0x01,0x08, | |||
0x38,0x8A,0x0C,0x18,0x28,0x8E,0x00,0xFE, | |||
// '»' | |||
0xBB,0x08,0x08,0x08,0x02,0x0B, | |||
0x88,0xCC,0x66,0x33,0x33,0x66,0xCC,0x88, | |||
// '¼' | |||
0xBC,0x04,0x10,0x0D,0x01,0x11, | |||
0xE0,0x18,0x20,0x10,0x20,0x20,0x20,0x60,0x20,0x40,0x20,0x80,0xF9,0x86,0x01,0x0A,0x02,0x12,0x06,0x22,0x04,0x3F,0x08,0x02,0x18,0x02, | |||
// '½' | |||
0xBD,0x04,0x0F,0x0D,0x01,0x11, | |||
0xE0,0x18,0x40,0x20,0x80,0x81,0x03,0x02,0x04,0x04,0x10,0x3E,0x67,0x00,0x91,0x02,0x02,0x0C,0x08,0x10,0x20,0x40,0x81,0x83,0xE0, | |||
// '¾' | |||
0xBE,0x04,0x10,0x0D,0x01,0x11, | |||
0xF0,0x18,0x08,0x10,0x70,0x20,0x18,0x60,0x08,0x40,0x18,0x80,0xF1,0x86,0x01,0x0A,0x02,0x12,0x06,0x22,0x04,0x3F,0x08,0x02,0x18,0x02, | |||
// '¿' | |||
0xBF,0x07,0x07,0x0E,0x02,0x0A, | |||
0x18,0x30,0x00,0xC1,0x83,0x0C,0x18,0x61,0x83,0x07,0x17,0xE7,0x80, | |||
// 'À' | |||
0xC0,0x01,0x0C,0x10,0x00,0x0C, | |||
0x06,0x00,0x30,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, | |||
// 'Á' | |||
0xC1,0x01,0x0C,0x10,0x00,0x0C, | |||
0x03,0x00,0x60,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, | |||
// 'Â' | |||
0xC2,0x01,0x0C,0x10,0x00,0x0C, | |||
0x06,0x00,0x90,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, | |||
// 'Ã' | |||
0xC3,0x01,0x0C,0x10,0x00,0x0C, | |||
0x0C,0x81,0x30,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, | |||
// 'Ä' | |||
0xC4,0x01,0x0C,0x10,0x00,0x0C, | |||
0x19,0x81,0x98,0x00,0x00,0x60,0x06,0x00,0xF0,0x0F,0x01,0x98,0x19,0x81,0x98,0x30,0xC3,0xFC,0x7F,0xE6,0x06,0x60,0x6C,0x03, | |||
// 'Å' | |||
0xC5,0x00,0x0C,0x11,0x00,0x0C, | |||
0x06,0x00,0x90,0x09,0x00,0x90,0x06,0x00,0x60,0x0F,0x00,0xF0,0x19,0x81,0x98,0x19,0x83,0x0C,0x3F,0xC7,0xFE,0x60,0x66,0x06,0xC0,0x30, | |||
// 'Æ' | |||
0xC6,0x04,0x10,0x0D,0x00,0x11, | |||
0x07,0xFF,0x07,0xFF,0x06,0xC0,0x0C,0xC0,0x0C,0xC0,0x18,0xFF,0x18,0xFF,0x30,0xC0,0x3F,0xC0,0x3F,0xC0,0x60,0xC0,0x60,0xFF,0xC0,0xFF, | |||
// 'Ç' | |||
0xC7,0x04,0x0B,0x10,0x01,0x0D, | |||
0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x0C,0x01,0xC0,0x9F,0xF0,0xFC,0x02,0x00,0x60,0x3C, | |||
// 'È' | |||
0xC8,0x01,0x08,0x10,0x02,0x0B, | |||
0x30,0x18,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, | |||
// 'É' | |||
0xC9,0x01,0x08,0x10,0x02,0x0B, | |||
0x18,0x30,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, | |||
// 'Ê' | |||
0xCA,0x01,0x08,0x10,0x02,0x0B, | |||
0x38,0x6C,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, | |||
// 'Ë' | |||
0xCB,0x01,0x08,0x10,0x02,0x0B, | |||
0x66,0x66,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF, | |||
// 'Ì' | |||
0xCC,0x01,0x05,0x10,0x00,0x06, | |||
0x61,0x80,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6, | |||
// 'Í' | |||
0xCD,0x01,0x04,0x10,0x01,0x06, | |||
0x6C,0x06,0x66,0x66,0x66,0x66,0x66,0x66, | |||
// 'Î' | |||
0xCE,0x01,0x06,0x10,0x00,0x06, | |||
0x31,0x20,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C, | |||
// 'Ï' | |||
0xCF,0x01,0x06,0x10,0x00,0x06, | |||
0xCF,0x30,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C, | |||
// 'Ð' | |||
0xD0,0x04,0x0D,0x0D,0x00,0x0E, | |||
0x3F,0x81,0xFF,0x0C,0x1C,0x60,0x73,0x01,0xFF,0x0F,0xF8,0x66,0x03,0x30,0x19,0x81,0xCC,0x1C,0x7F,0xC3,0xF8,0x00, | |||
// 'Ñ' | |||
0xD1,0x01,0x0A,0x10,0x02,0x0E, | |||
0x19,0x09,0x80,0x03,0x83,0xE0,0xFC,0x3F,0x0F,0x63,0xD8,0xF3,0x3C,0x6F,0x1B,0xC3,0xF0,0xFC,0x1F,0x07, | |||
// 'Ò' | |||
0xD2,0x01,0x0C,0x10,0x01,0x0E, | |||
0x06,0x00,0x30,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, | |||
// 'Ó' | |||
0xD3,0x01,0x0C,0x10,0x01,0x0E, | |||
0x03,0x00,0x60,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, | |||
// 'Ô' | |||
0xD4,0x01,0x0C,0x10,0x01,0x0E, | |||
0x06,0x00,0x90,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, | |||
// 'Õ' | |||
0xD5,0x01,0x0C,0x10,0x01,0x0E, | |||
0x0C,0x81,0x30,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, | |||
// 'Ö' | |||
0xD6,0x01,0x0C,0x10,0x01,0x0E, | |||
0x19,0x81,0x98,0x00,0x01,0xF8,0x3F,0xC7,0x0E,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x67,0x0E,0x3F,0xC1,0xF8, | |||
// '×' | |||
0xD7,0x06,0x0A,0x0A,0x02,0x0F, | |||
0x40,0xB8,0x77,0x38,0xFC,0x1E,0x07,0x83,0xF1,0xCE,0xE1,0xD0,0x20, | |||
// 'Ø' | |||
0xD8,0x03,0x0E,0x0F,0x00,0x0E, | |||
0x00,0x00,0x3E,0x21,0xFD,0x0E,0x1C,0x70,0x71,0x83,0x66,0x19,0x98,0xC6,0x66,0x19,0xB0,0x63,0x83,0x8E,0x1C,0x2F,0xE1,0x1F,0x00,0x00,0x00, | |||
// 'Ù' | |||
0xD9,0x01,0x0A,0x10,0x02,0x0E, | |||
0x18,0x03,0x00,0x03,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xD8,0x67,0xF8,0x78, | |||
// 'Ú' | |||
0xDA,0x01,0x0A,0x10,0x02,0x0E, | |||
0x0C,0x06,0x00,0x03,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xD8,0x67,0xF8,0x78, | |||
// 'Û' | |||
0xDB,0x01,0x0A,0x10,0x02,0x0E, | |||
0x0C,0x04,0x80,0x03,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xD8,0x67,0xF8,0x78, | |||
// 'Ü' | |||
0xDC,0x01,0x0A,0x10,0x02,0x0E, | |||
0x33,0x0C,0xC0,0x03,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xD8,0x67,0xF8,0x78, | |||
// 'Ý' | |||
0xDD,0x01,0x0C,0x10,0x00,0x0C, | |||
0x06,0x00,0xC0,0x00,0x0E,0x07,0x60,0x63,0x0C,0x19,0x81,0x98,0x0F,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60, | |||
// 'Þ' | |||
0xDE,0x04,0x08,0x0D,0x02,0x0B, | |||
0xC0,0xC0,0xFC,0xFE,0xC7,0xC3,0xC3,0xC7,0xFE,0xFC,0xC0,0xC0,0xC0, | |||
// 'ß' | |||
0xDF,0x03,0x09,0x0E,0x02,0x0B, | |||
0x3C,0x3F,0x39,0xD8,0x6C,0x66,0x63,0x31,0x8C,0xC3,0x60,0xF0,0x7A,0x7D,0xF6,0x70, | |||
// 'à' | |||
0xE0,0x03,0x08,0x0E,0x01,0x0A, | |||
0x30,0x18,0x0C,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, | |||
// 'á' | |||
0xE1,0x03,0x08,0x0E,0x01,0x0A, | |||
0x06,0x0C,0x18,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, | |||
// 'â' | |||
0xE2,0x03,0x08,0x0E,0x01,0x0A, | |||
0x18,0x3C,0x66,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, | |||
// 'ã' | |||
0xE3,0x03,0x08,0x0E,0x01,0x0A, | |||
0x32,0x4C,0x00,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, | |||
// 'ä' | |||
0xE4,0x03,0x08,0x0E,0x01,0x0A, | |||
0x66,0x66,0x00,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, | |||
// 'å' | |||
0xE5,0x01,0x08,0x10,0x01,0x0A, | |||
0x1C,0x22,0x22,0x22,0x1C,0x00,0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B, | |||
// 'æ' | |||
0xE6,0x07,0x0F,0x0A,0x01,0x11, | |||
0x3C,0x78,0xFD,0xF9,0x1E,0x38,0x18,0x33,0xFF,0xFF,0xFF,0xF0,0xC0,0x63,0xC1,0xFD,0xFE,0xF0,0xF8, | |||
// 'ç' | |||
0xE7,0x07,0x08,0x0D,0x01,0x09, | |||
0x1E,0x7F,0x61,0xC0,0xC0,0xC0,0xC0,0x61,0x7F,0x1E,0x04,0x06,0x1E, | |||
// 'è' | |||
0xE8,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x30,0x06,0x00,0xC0,0x00,0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, | |||
// 'é' | |||
0xE9,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x06,0x03,0x01,0x80,0x00,0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, | |||
// 'ê' | |||
0xEA,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x0C,0x07,0x83,0x30,0x00,0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, | |||
// 'ë' | |||
0xEB,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x33,0x0C,0xC0,0x00,0x00,0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0, | |||
// 'ì' | |||
0xEC,0x03,0x04,0x0E,0x00,0x05, | |||
0xC6,0x30,0x33,0x33,0x33,0x33,0x33, | |||
// 'í' | |||
0xED,0x03,0x04,0x0E,0x01,0x05, | |||
0x36,0xC0,0x66,0x66,0x66,0x66,0x66, | |||
// 'î' | |||
0xEE,0x03,0x06,0x0E,0x00,0x05, | |||
0x31,0xEC,0xC0,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC0, | |||
// 'ï' | |||
0xEF,0x03,0x06,0x0E,0x00,0x05, | |||
0xCF,0x30,0x00,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC0, | |||
// 'ð' | |||
0xF0,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x10,0x07,0xC7,0xC0,0x18,0x1F,0x1F,0xE6,0x1F,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, | |||
// 'ñ' | |||
0xF1,0x03,0x08,0x0E,0x02,0x0B, | |||
0x32,0x4C,0x00,0x00,0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, | |||
// 'ò' | |||
0xF2,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x30,0x06,0x00,0xC0,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, | |||
// 'ó' | |||
0xF3,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x06,0x03,0x01,0x80,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, | |||
// 'ô' | |||
0xF4,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x0C,0x07,0x83,0x30,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, | |||
// 'õ' | |||
0xF5,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x19,0x09,0x80,0x00,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, | |||
// 'ö' | |||
0xF6,0x03,0x0A,0x0E,0x01,0x0B, | |||
0x33,0x0C,0xC0,0x00,0x00,0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80, | |||
// '÷' | |||
0xF7,0x07,0x0C,0x08,0x02,0x0F, | |||
0x06,0x00,0x60,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x06,0x00,0x60, | |||
// 'ø' | |||
0xF8,0x06,0x0C,0x0C,0x00,0x0B, | |||
0x00,0x00,0xF2,0x1F,0xC3,0x0C,0x61,0xE6,0x26,0x64,0x67,0x86,0x30,0xC3,0xFC,0x4F,0x00,0x00, | |||
// 'ù' | |||
0xF9,0x03,0x08,0x0E,0x02,0x0B, | |||
0x60,0x30,0x18,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, | |||
// 'ú' | |||
0xFA,0x03,0x08,0x0E,0x02,0x0B, | |||
0x0C,0x18,0x30,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, | |||
// 'û' | |||
0xFB,0x03,0x08,0x0E,0x02,0x0B, | |||
0x18,0x3C,0x66,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, | |||
// 'ü' | |||
0xFC,0x03,0x08,0x0E,0x02,0x0B, | |||
0x66,0x66,0x00,0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B, | |||
// 'ý' | |||
0xFD,0x03,0x0C,0x12,0x00,0x0B, | |||
0x03,0x00,0x60,0x0C,0x00,0x00,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80, | |||
// 'þ' | |||
0xFE,0x03,0x09,0x12,0x02,0x0B, | |||
0xC0,0x60,0x30,0x18,0x0D,0xE7,0xFB,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x7F,0xF6,0xF3,0x01,0x80,0xC0,0x60,0x00, | |||
// 'ÿ' | |||
0xFF,0x03,0x0C,0x12,0x00,0x0B, | |||
0x1B,0x01,0xB0,0x00,0x00,0x00,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80, | |||
// Terminator | |||
0xFF | |||
}; |
@@ -0,0 +1,331 @@ | |||
// This comes with no warranty, implied or otherwise | |||
// This data structure was designed to support Proportional fonts | |||
// on Arduinos. It can however handle any ttf font that has been converted | |||
// using the conversion program. These could be fixed width or proportional | |||
// fonts. Individual characters do not have to be multiples of 8 bits wide. | |||
// Any width is fine and does not need to be fixed. | |||
// The data bits are packed to minimize data requirements, but the tradeoff | |||
// is that a header is required per character. | |||
// minya24.c | |||
// Point Size : 24 | |||
// Memory usage : 2807 bytes | |||
// # characters : 95 | |||
// Header Format (to make Arduino UTFT Compatible): | |||
// ------------------------------------------------ | |||
// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) | |||
// Character Height | |||
// First Character (Reserved. 0x00) | |||
// Number Of Characters (Reserved. 0x00) | |||
unsigned char tft_minya24[] = | |||
{ | |||
0x00, 0x15, 0x00, 0x00, | |||
// Individual Character Format: | |||
// ---------------------------- | |||
// Character Code | |||
// Adjusted Y Offset | |||
// Width | |||
// Height | |||
// xOffset | |||
// xDelta (the distance to move the cursor. Effective width of the character.) | |||
// Data[n] | |||
// NOTE: You can remove any of these characters if they are not needed in | |||
// your application. The first character number in each Glyph indicates | |||
// the ASCII character code. Therefore, these do not have to be sequential. | |||
// Just remove all the content for a particular character to save space. | |||
// ' ' | |||
0x20,0x13,0x00,0x00,0x00,0x07, | |||
// '!' | |||
0x21,0x02,0x05,0x12,0x00,0x05, | |||
0x11,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x42,0x01,0xCE,0x73,0x80, | |||
// '"' | |||
0x22,0x01,0x06,0x08,0x00,0x06, | |||
0x01,0xA6,0xDB,0x6D,0xB6,0xC0, | |||
// '#' | |||
0x23,0x04,0x0C,0x0E,0x00,0x0B, | |||
0x04,0x80,0x6C,0x0C,0x80,0xD8,0x7F,0xE7,0xFE,0x1B,0x01,0xB0,0x7F,0xE7,0xFC,0x12,0x03,0x20,0x32,0x00,0x00, | |||
// '$' | |||
0x24,0x02,0x0A,0x11,0x00,0x0B, | |||
0x04,0x01,0x61,0xF8,0xFE,0x65,0x19,0x06,0x40,0xF0,0x1F,0x01,0xE0,0x4C,0x93,0x7C,0xCF,0xE0,0x60,0x10,0x04,0x00, | |||
// '%' | |||
0x25,0x01,0x0D,0x14,0x01,0x0F, | |||
0x00,0x01,0xC1,0x1F,0x19,0x8C,0xCC,0x6C,0x63,0x61,0x36,0x0F,0xB0,0x1B,0x00,0x18,0x01,0x80,0x0C,0x00,0xCE,0x06,0xF8,0x6C,0x66,0x63,0x33,0x13,0x0F,0x98,0x38,0x00,0x00, | |||
// '&' | |||
0x26,0x02,0x0E,0x11,0x00,0x0D, | |||
0x0E,0x00,0x7C,0x01,0xB0,0x06,0xC0,0x1E,0x00,0x38,0x00,0xC3,0x07,0x8C,0x37,0x60,0xCD,0x86,0x1E,0x18,0x70,0x41,0xE1,0x07,0xC6,0x33,0x9F,0x86,0x38,0x00, | |||
// ''' | |||
0x27,0x02,0x03,0x08,0x00,0x03, | |||
0x6D,0xB6,0xD8, | |||
// '(' | |||
0x28,0x01,0x05,0x14,0x02,0x07, | |||
0x00,0x8E,0x66,0x33,0x18,0xC6,0x31,0x8C,0x61,0x0C,0x63,0x8C,0x00, | |||
// ')' | |||
0x29,0x01,0x06,0x15,0x00,0x07, | |||
0x01,0x86,0x0C,0x18,0x61,0x82,0x08,0x30,0xC2,0x08,0x61,0x86,0x30,0xC6,0x10,0x00, | |||
// '*' | |||
0x2A,0x04,0x0A,0x0D,0x01,0x0B, | |||
0x08,0x03,0x04,0xC1,0xF6,0x7F,0x07,0x81,0xC0,0xF8,0x3F,0x1B,0xCC,0xD8,0x30,0x0C,0x00, | |||
// '+' | |||
0x2B,0x06,0x0A,0x0A,0x01,0x0B, | |||
0x00,0x03,0x00,0xC0,0x30,0x7F,0xBF,0xE0,0xC0,0x30,0x0C,0x00,0x00, | |||
// ',' | |||
0x2C,0x10,0x05,0x07,0x00,0x05, | |||
0x33,0x9C,0x63,0x20,0x00, | |||
// '-' | |||
0x2D,0x09,0x07,0x02,0x00,0x07, | |||
0x7D,0xF8, | |||
// '.' | |||
0x2E,0x10,0x04,0x04,0x01,0x05, | |||
0x6E,0xE6, | |||
// '/' | |||
0x2F,0x01,0x0C,0x13,0x00,0x0B, | |||
0x00,0x00,0x06,0x00,0x60,0x0C,0x00,0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x60,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x30,0x06,0x00,0x40,0x00, | |||
// '0' | |||
0x30,0x08,0x0B,0x0B,0x00,0x0B, | |||
0x0E,0x03,0xE0,0xC6,0x30,0x66,0x0C,0xC0,0x98,0x33,0x06,0x61,0xC7,0xF0,0x7C,0x00, | |||
// '1' | |||
0x31,0x08,0x0A,0x0D,0x00,0x09, | |||
0x04,0x1F,0x03,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x07,0xF9,0xFE,0x00,0x00, | |||
// '2' | |||
0x32,0x06,0x0A,0x0E,0x00,0x0A, | |||
0x0E,0x07,0xC3,0x30,0xCC,0x03,0x01,0x80,0x60,0x30,0x18,0x0C,0x03,0x01,0xF8,0x7F,0xC0,0x20, | |||
// '3' | |||
0x33,0x08,0x09,0x10,0x00,0x08, | |||
0x78,0x3F,0x81,0x81,0x81,0xC0,0xC0,0xE0,0x78,0x06,0x01,0x01,0x80,0xC0,0xE1,0xE0,0xC0,0x00, | |||
// '4' | |||
0x34,0x03,0x0B,0x13,0x00,0x0A, | |||
0x00,0x00,0xC0,0x18,0x03,0x00,0x60,0x08,0x03,0x00,0x60,0x0D,0x83,0x20,0x64,0x18,0x83,0x10,0xC6,0x1F,0xF3,0xFE,0x03,0x00,0x60,0x0C,0x00, | |||
// '5' | |||
0x35,0x09,0x0A,0x0E,0x00,0x0A, | |||
0x3F,0x8F,0xE3,0x00,0x80,0x60,0x1F,0x87,0xF0,0x06,0x01,0x80,0x60,0x19,0x86,0x7F,0x07,0x80, | |||
// '6' | |||
0x36,0x03,0x0A,0x12,0x00,0x0A, | |||
0x00,0x01,0x80,0xE0,0x30,0x18,0x06,0x03,0x00,0xC0,0x60,0x1B,0x87,0xF1,0x86,0x61,0x90,0x66,0x19,0x86,0x3F,0x07,0x80, | |||
// '7' | |||
0x37,0x09,0x0A,0x0E,0x00,0x0A, | |||
0x7F,0x9F,0xF0,0x18,0x0C,0x06,0x01,0x80,0xC0,0x30,0x18,0x06,0x01,0x80,0x40,0x10,0x0C,0x00, | |||
// '8' | |||
0x38,0x03,0x0B,0x11,0x00,0x0B, | |||
0x0F,0x07,0xF0,0xC3,0x30,0x66,0x0C,0x63,0x87,0xE0,0xF8,0x39,0xCE,0x19,0x81,0x30,0x34,0x06,0xC1,0x98,0x71,0xFC,0x1F,0x00, | |||
// '9' | |||
0x39,0x07,0x0A,0x11,0x00,0x0A, | |||
0x1E,0x0F,0xC6,0x19,0x86,0x41,0x98,0x66,0x18,0xFE,0x1D,0x80,0x40,0x30,0x0C,0x06,0x03,0x01,0xC0,0x20,0x00,0x00, | |||
// ':' | |||
0x3A,0x0A,0x05,0x0A,0x00,0x05, | |||
0x33,0x9C,0x60,0x00,0xCE,0x71,0x80, | |||
// ';' | |||
0x3B,0x09,0x05,0x0D,0x00,0x06, | |||
0x33,0xDE,0x60,0x00,0x0E,0x73,0x8C,0xC0,0x00, | |||
// '<' | |||
0x3C,0x06,0x0A,0x0C,0x00,0x09, | |||
0x00,0x00,0xE0,0x70,0x38,0x1C,0x1C,0x03,0x00,0x70,0x0E,0x01,0xC0,0x10,0x00, | |||
// '=' | |||
0x3D,0x09,0x08,0x06,0x01,0x0A, | |||
0xFF,0xFE,0x00,0x00,0x7F,0xFF, | |||
// '>' | |||
0x3E,0x06,0x0A,0x0C,0x00,0x09, | |||
0x00,0x18,0x03,0x80,0x70,0x0E,0x00,0xE0,0x30,0x38,0x1C,0x0E,0x02,0x00,0x00, | |||
// '?' | |||
0x3F,0x02,0x09,0x12,0x00,0x09, | |||
0x1E,0x1F,0x98,0x6C,0x30,0x18,0x18,0x38,0x30,0x30,0x18,0x0C,0x02,0x01,0x00,0x00,0x60,0x78,0x3C,0x0C,0x00, | |||
// '@' | |||
0x40,0x02,0x11,0x11,0x00,0x11, | |||
0x01,0xF0,0x03,0xFE,0x03,0x03,0x83,0x00,0xC3,0x04,0x31,0x1F,0x19,0x9F,0x0C,0xCC,0x86,0x4C,0x43,0x24,0x21,0x12,0x39,0x89,0xF7,0x84,0x71,0x83,0x00,0x00,0xC1,0x80,0x3F,0xC0,0x0F,0x80,0x00, | |||
// 'A' | |||
0x41,0x02,0x12,0x13,0x00,0x10, | |||
0x00,0x00,0x01,0xF0,0x00,0x7E,0x00,0x03,0x80,0x01,0xA0,0x00,0x6C,0x00,0x1B,0x00,0x0C,0x40,0x03,0x18,0x01,0x86,0x00,0x61,0x80,0x1F,0xE0,0x0F,0xFC,0x03,0x03,0x01,0x80,0xC0,0x60,0x10,0x18,0x1F,0x9F,0xC7,0xEF,0xF8,0x00, | |||
// 'B' | |||
0x42,0x02,0x0E,0x13,0x00,0x0E, | |||
0x7F,0x81,0xFF,0x81,0x86,0x06,0x18,0x18,0xC0,0x66,0x01,0xFC,0x07,0xFC,0x1C,0x30,0x60,0x61,0x81,0x86,0x06,0x18,0x18,0x60,0x61,0x81,0x06,0x0C,0x18,0xE0,0xFE,0x03,0xE0,0x00, | |||
// 'C' | |||
0x43,0x02,0x0F,0x12,0x00,0x0F, | |||
0x03,0xF0,0x1F,0xE0,0x70,0xC1,0x80,0x83,0x00,0x04,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x06,0x00,0x0E,0x01,0x0E,0x0E,0x0F,0xF8,0x07,0xE0, | |||
// 'D' | |||
0x44,0x02,0x0F,0x13,0x00,0x0F, | |||
0x3F,0xC0,0x7F,0xE0,0x60,0xE0,0xC0,0xC1,0x80,0xC3,0x01,0x86,0x03,0x0C,0x02,0x18,0x04,0x30,0x08,0x60,0x30,0xC0,0x61,0x80,0x83,0x03,0x06,0x0C,0x0C,0x38,0x18,0xE0,0xFF,0x81,0xF8,0x00, | |||
// 'E' | |||
0x45,0x02,0x0D,0x13,0x00,0x0D, | |||
0x3F,0xF1,0xFF,0x86,0x0C,0x30,0x61,0x80,0x0C,0x40,0x62,0x03,0xF0,0x1F,0x80,0xC4,0x06,0x20,0x30,0x01,0x80,0x0C,0x00,0x60,0xC3,0x06,0x7F,0xF3,0xFF,0x80,0x00, | |||
// 'F' | |||
0x46,0x01,0x0E,0x13,0x00,0x0E, | |||
0x00,0x01,0xFF,0xE7,0xFF,0x86,0x06,0x18,0x18,0x63,0x01,0x8C,0x03,0xF0,0x0F,0xC0,0x63,0x01,0x8C,0x06,0x30,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x3F,0x81,0xFE,0x00,0x00,0x00, | |||
// 'G' | |||
0x47,0x01,0x11,0x14,0x00,0x11, | |||
0x00,0x00,0x03,0xC8,0x03,0xFC,0x03,0x0E,0x03,0x03,0x01,0x00,0x01,0x80,0x00,0xC0,0x00,0x60,0x03,0x30,0x3F,0xD8,0x1F,0x0C,0x01,0x86,0x00,0xC3,0x80,0x60,0xC0,0x30,0x70,0x38,0x1C,0x3C,0x07,0xF6,0x01,0xF3,0x80,0x01,0xE0, | |||
// 'H' | |||
0x48,0x01,0x10,0x13,0x00,0x10, | |||
0x00,0x00,0x7C,0x0F,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x08,0x0C,0x08,0x0F,0xF8,0x0F,0xF8,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x1A,0x7C,0x1F,0x7C,0x10, | |||
// 'I' | |||
0x49,0x02,0x08,0x13,0x00,0x08, | |||
0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E, | |||
// 'J' | |||
0x4A,0x01,0x0C,0x14,0x00,0x0B, | |||
0x00,0x00,0x7E,0x07,0xE0,0x18,0x01,0x80,0x18,0x01,0x80,0x18,0x00,0x80,0x08,0x00,0x80,0x18,0x61,0x84,0x18,0x41,0x8C,0x18,0xC3,0x06,0x70,0x7E,0x01,0x80, | |||
// 'K' | |||
0x4B,0x02,0x11,0x13,0x00,0x10, | |||
0x7E,0x7E,0x3F,0x3E,0x06,0x06,0x03,0x06,0x01,0x86,0x00,0xC6,0x00,0x66,0x00,0x37,0x00,0x1F,0x80,0x0E,0x60,0x06,0x10,0x03,0x0C,0x01,0x06,0x00,0x81,0x00,0x60,0x80,0x30,0x66,0x7E,0x1B,0x3F,0x0F,0x80,0x01,0x80, | |||
// 'L' | |||
0x4C,0x02,0x0D,0x11,0x00,0x0D, | |||
0x7E,0x03,0xF0,0x06,0x00,0x30,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x00,0xC0,0x06,0x00,0x30,0x01,0x81,0x0C,0x08,0x60,0x4F,0xFF,0x7F,0xF8, | |||
// 'M' | |||
0x4D,0x02,0x14,0x12,0x00,0x13, | |||
0x7C,0x07,0x87,0xC0,0x7C,0x0C,0x06,0x00,0xE0,0xE0,0x0E,0x0E,0x00,0xE0,0xE0,0x1B,0x1E,0x01,0xB1,0x60,0x0B,0x16,0x00,0x9B,0x60,0x09,0xA6,0x00,0x9E,0x60,0x08,0xE6,0x00,0x8C,0x60,0x08,0xC6,0x03,0x8C,0x7E,0x78,0x47,0xE0,0x00,0x00, | |||
// 'N' | |||
0x4E,0x01,0x12,0x13,0x00,0x12, | |||
0x00,0x00,0x1F,0x03,0xF3,0xC0,0x7C,0x38,0x10,0x0E,0x04,0x03,0xC1,0x00,0xB0,0x40,0x26,0x10,0x09,0x84,0x02,0x31,0x00,0x8C,0x40,0x21,0x90,0x08,0x74,0x06,0x0F,0x01,0x81,0xC0,0x60,0x70,0x78,0x0C,0x3E,0x03,0x00,0x00,0x40, | |||
// 'O' | |||
0x4F,0x02,0x11,0x12,0x00,0x11, | |||
0x03,0xE0,0x07,0xFC,0x07,0x07,0x06,0x01,0x82,0x00,0x63,0x00,0x11,0x80,0x0C,0x80,0x06,0x40,0x03,0x20,0x01,0x98,0x00,0xCC,0x00,0x66,0x00,0x31,0x80,0x30,0xE0,0x18,0x38,0x18,0x0F,0xF8,0x01,0xF0,0x00, | |||
// 'P' | |||
0x50,0x02,0x0C,0x12,0x00,0x0C, | |||
0x7F,0x07,0xFC,0x10,0x61,0x03,0x10,0x31,0x03,0x10,0x31,0x06,0x10,0xE1,0xFC,0x1E,0x01,0x00,0x18,0x01,0x80,0x18,0x01,0x80,0x7E,0x07,0xF0, | |||
// 'Q' | |||
0x51,0x02,0x13,0x15,0x00,0x11, | |||
0x07,0xE0,0x01,0xFE,0x00,0x60,0x70,0x18,0x06,0x06,0x00,0x60,0xC0,0x0C,0x18,0x00,0xC2,0x00,0x18,0x40,0x03,0x08,0x0C,0x61,0x83,0xCC,0x30,0xCD,0x83,0x09,0xE0,0x60,0x3C,0x06,0x07,0x00,0x7F,0xE3,0x07,0xEC,0x40,0x01,0x98,0x00,0x33,0x00,0x03,0xC0,0x00,0x30, | |||
// 'R' | |||
0x52,0x02,0x0F,0x13,0x00,0x0F, | |||
0x7F,0x80,0xFF,0xE0,0x60,0xE0,0xC0,0x61,0x80,0xC3,0x01,0x86,0x07,0x0C,0x1C,0x1F,0xF0,0x3F,0x80,0x66,0x00,0xC4,0x01,0x8C,0x63,0x18,0xC6,0x11,0x8C,0x33,0x7E,0x7C,0xFC,0x70,0x00,0x00, | |||
// 'S' | |||
0x53,0x01,0x0D,0x13,0x00,0x0D, | |||
0x00,0x60,0x7B,0x07,0xF8,0x71,0xC3,0x06,0x18,0x00,0xC0,0x03,0x80,0x0E,0x00,0x1C,0x00,0x78,0x00,0xC0,0x03,0x30,0x19,0x80,0xCC,0x06,0x30,0x70,0xFF,0x03,0xE0, | |||
// 'T' | |||
0x54,0x01,0x0F,0x12,0x00,0x0F, | |||
0x00,0x00,0xFF,0xF9,0xFF,0xF3,0x18,0x66,0x30,0xCC,0x61,0x90,0xC3,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x0F,0xE0,0x1F,0xC0, | |||
// 'U' | |||
0x55,0x02,0x10,0x11,0x00,0x0F, | |||
0x7C,0x7E,0x7C,0x7E,0x30,0x08,0x30,0x08,0x30,0x08,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x10,0x08,0x18,0x18,0x18,0x18,0x0C,0x30,0x0F,0xF0,0x03,0xC0, | |||
// 'V' | |||
0x56,0x01,0x10,0x12,0x00,0x10, | |||
0x00,0x7E,0xFE,0x7E,0x7E,0x18,0x18,0x30,0x18,0x30,0x18,0x20,0x08,0x60,0x0C,0x60,0x0C,0x60,0x0C,0x40,0x0C,0xC0,0x04,0xC0,0x04,0xC0,0x06,0x80,0x07,0x80,0x07,0x80,0x03,0x00,0x03,0x00, | |||
// 'W' | |||
0x57,0x01,0x14,0x13,0x00,0x14, | |||
0x00,0x00,0x0F,0xC4,0xFF,0xF0,0x4F,0xE3,0x06,0x10,0x30,0xE3,0x01,0x8E,0x30,0x18,0xE3,0x01,0x8E,0x30,0x19,0xA3,0x01,0x9A,0x20,0x09,0xA6,0x00,0xD2,0x60,0x0D,0x36,0x00,0xF3,0x40,0x0F,0x34,0x00,0x63,0xC0,0x06,0x1C,0x00,0x61,0x80,0x00,0x18,0x00, | |||
// 'X' | |||
0x58,0x01,0x10,0x13,0x00,0x10, | |||
0x00,0x00,0x7F,0x7F,0x7E,0x7E,0x0C,0x30,0x06,0x20,0x06,0x60,0x03,0xC0,0x01,0x80,0x01,0x80,0x03,0x80,0x02,0xC0,0x06,0x40,0x04,0x60,0x0C,0x60,0x18,0x30,0x18,0x30,0x7C,0x30,0xFC,0xFE,0x00,0xFF, | |||
// 'Y' | |||
0x59,0x01,0x11,0x13,0x00,0x10, | |||
0x00,0x00,0x7F,0x3F,0xBF,0x07,0x03,0x03,0x00,0xC1,0x00,0x31,0x80,0x18,0x80,0x06,0xC0,0x01,0xC0,0x00,0xE0,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x07,0x80,0x03,0xF8,0x00,0x7C,0x00, | |||
// 'Z' | |||
0x5A,0x02,0x0E,0x12,0x00,0x0D, | |||
0x7F,0xF9,0xFF,0xE6,0x03,0x18,0x18,0x60,0xC1,0x07,0x00,0x18,0x00,0xC0,0x06,0x00,0x18,0x00,0xC0,0x06,0x00,0x18,0x30,0xC0,0xC3,0x03,0x1F,0xFE,0x7F,0xF8,0x00,0x60, | |||
// '[' | |||
0x5B,0x01,0x08,0x14,0x01,0x08, | |||
0x00,0x7C,0x7C,0x40,0x40,0x40,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x7E,0x00, | |||
// '\' | |||
0x5C,0x01,0x0A,0x13,0x00,0x0A, | |||
0x00,0x10,0x06,0x01,0x80,0x30,0x0C,0x01,0x80,0x60,0x0C,0x03,0x00,0x60,0x18,0x06,0x00,0xC0,0x30,0x06,0x01,0x80,0x60,0x08, | |||
// ']' | |||
0x5D,0x01,0x08,0x14,0x00,0x08, | |||
0x00,0x7C,0x7E,0x04,0x04,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x7C,0xFC, | |||
// '^' | |||
0x5E,0x03,0x0B,0x0B,0x00,0x0B, | |||
0x00,0x00,0x40,0x18,0x03,0x00,0xF0,0x32,0x0C,0x63,0x86,0x60,0xC0,0x0C,0x00,0x00, | |||
// '_' | |||
0x5F,0x15,0x11,0x02,0xFF,0x0F, | |||
0x7F,0xFF,0x3F,0xFF,0x80, | |||
// '`' | |||
0x60,0x00,0x06,0x07,0x00,0x06, | |||
0x01,0x87,0x0C,0x18,0x20,0x00, | |||
// 'a' | |||
0x61,0x06,0x0C,0x0D,0x00,0x0B, | |||
0x0E,0x03,0xF0,0x31,0x06,0x10,0x23,0x80,0xF8,0x3D,0x87,0x10,0x61,0x0C,0x18,0x63,0xA7,0xFE,0x1C,0xC0, | |||
// 'b' | |||
0x62,0x01,0x0C,0x14,0x00,0x0C, | |||
0x00,0x07,0xC0,0x3C,0x01,0x80,0x18,0x01,0x80,0x18,0x01,0x98,0x1F,0xC1,0xC6,0x18,0x21,0x83,0x10,0x31,0x03,0x18,0x31,0x83,0x18,0x6F,0xC6,0xF7,0xC0,0x30, | |||
// 'c' | |||
0x63,0x06,0x0B,0x0D,0x00,0x0B, | |||
0x06,0x03,0xF0,0xC7,0x31,0xE4,0x1C,0x80,0x30,0x06,0x00,0x60,0x0C,0x01,0xC3,0x1F,0xC0,0xF0, | |||
// 'd' | |||
0x64,0x01,0x0D,0x13,0x00,0x0D, | |||
0x00,0x00,0x1E,0x00,0xF0,0x01,0x80,0x0C,0x00,0x60,0x03,0x01,0x98,0x3E,0x81,0x9C,0x18,0x60,0xC3,0x04,0x08,0x60,0x41,0x02,0x0C,0x30,0x71,0x91,0xFF,0x87,0x1C, | |||
// 'e' | |||
0x65,0x07,0x0B,0x0C,0x00,0x0B, | |||
0x0F,0x03,0xF0,0xC3,0x30,0x66,0x1C,0xDE,0x1E,0x03,0x00,0x60,0x06,0x18,0x7E,0x07,0x80, | |||
// 'f' | |||
0x66,0x02,0x0A,0x12,0x00,0x08, | |||
0x07,0x07,0xE1,0xB8,0x4E,0x10,0x04,0x07,0xE1,0xF8,0x18,0x06,0x01,0x80,0x60,0x18,0x06,0x01,0x81,0xF8,0x7E,0x00,0x00, | |||
// 'g' | |||
0x67,0x05,0x0A,0x12,0x00,0x0A, | |||
0x00,0x00,0x20,0x18,0x7E,0x3F,0x18,0x64,0x09,0x02,0x61,0x9F,0xC1,0xE0,0x0C,0x01,0x84,0x67,0x19,0x8C,0x7E,0x0F,0x00, | |||
// 'h' | |||
0x68,0x02,0x0E,0x12,0x00,0x0D, | |||
0x78,0x01,0xE0,0x01,0x80,0x06,0x00,0x19,0xC0,0x2F,0x80,0xE6,0x07,0x08,0x18,0x20,0x60,0x81,0x82,0x06,0x08,0x18,0x20,0x60,0x81,0x82,0x1F,0x9E,0x7E,0x78,0x00,0x00, | |||
// 'i' | |||
0x69,0x02,0x08,0x11,0x00,0x08, | |||
0x30,0x78,0x78,0x30,0x00,0x78,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7F, | |||
// 'j' | |||
0x6A,0x02,0x09,0x15,0xFE,0x07, | |||
0x06,0x07,0x03,0x81,0xC0,0x00,0xF8,0x7C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x99,0x8F,0xC3,0xC0, | |||
// 'k' | |||
0x6B,0x02,0x0D,0x12,0x00,0x0D, | |||
0x78,0x03,0xC0,0x06,0x00,0x37,0xC1,0xBC,0x0C,0xC0,0x6C,0x03,0xC0,0x1C,0x00,0xC0,0x07,0x00,0x3E,0x01,0xB8,0x08,0x61,0xC1,0xCF,0x07,0x00,0x10,0x00,0x00, | |||
// 'l' | |||
0x6C,0x02,0x08,0x12,0x00,0x08, | |||
0x78,0x78,0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00, | |||
// 'm' | |||
0x6D,0x08,0x14,0x0D,0x00,0x14, | |||
0x40,0xC3,0x0F,0xBE,0xF8,0x1E,0x78,0xC1,0xC7,0x0C,0x18,0x20,0xC1,0x82,0x0C,0x10,0x20,0xC1,0x82,0x08,0x18,0x60,0x81,0x84,0x18,0x7E,0x71,0xE7,0xE7,0x9E,0x00,0x00,0x00, | |||
// 'n' | |||
0x6E,0x06,0x0E,0x0E,0x00,0x0D, | |||
0x79,0xC1,0xEF,0x81,0xE6,0x07,0x08,0x18,0x30,0x60,0xC1,0x82,0x06,0x08,0x18,0x20,0x60,0x81,0x82,0x1F,0x1E,0x7E,0x78,0x00,0x00, | |||
// 'o' | |||
0x6F,0x08,0x0B,0x0C,0x00,0x0C, | |||
0x0F,0x03,0xF8,0xC3,0x30,0x36,0x06,0x80,0xD0,0x1B,0x03,0x60,0x66,0x18,0xFE,0x07,0x80, | |||
// 'p' | |||
0x70,0x07,0x0B,0x10,0x00,0x0B, | |||
0x07,0x1F,0xF1,0xE3,0x18,0x23,0x06,0x60,0xCC,0x19,0x82,0x30,0xC6,0x18,0xFE,0x1B,0x83,0x00,0x60,0x1C,0x07,0x80, | |||
// 'q' | |||
0x71,0x06,0x0D,0x11,0x00,0x0B, | |||
0x0C,0x01,0xF7,0x0C,0xF8,0xC3,0x04,0x18,0x20,0x41,0x02,0x08,0x10,0x60,0x83,0x04,0x0C,0x60,0x7F,0x01,0xE8,0x00,0xC0,0x06,0xC0,0x1E,0x00,0xE0, | |||
// 'r' | |||
0x72,0x08,0x0A,0x0D,0x00,0x09, | |||
0x77,0x3F,0xE3,0xB8,0xCE,0x33,0x0C,0x03,0x00,0xC0,0x30,0x04,0x07,0xC1,0xF0,0x00,0x00, | |||
// 's' | |||
0x73,0x05,0x0A,0x0E,0x00,0x0A, | |||
0x03,0x00,0xC1,0xF0,0xFC,0x63,0x18,0x47,0x00,0xF8,0x07,0x80,0x64,0x19,0x86,0x7F,0x07,0x80, | |||
// 't' | |||
0x74,0x02,0x09,0x13,0x00,0x09, | |||
0x30,0x18,0x0C,0x06,0x02,0x07,0xE3,0xF8,0x40,0x20,0x10,0x08,0x04,0x02,0x11,0x18,0x84,0x62,0x33,0x0F,0x83,0x00, | |||
// 'u' | |||
0x75,0x06,0x0E,0x0E,0x00,0x0D, | |||
0x00,0x03,0xE7,0xC7,0x9F,0x0C,0x30,0x30,0xC0,0xC3,0x03,0x0C,0x0C,0x30,0x30,0xC0,0xC3,0x03,0x0C,0x0E,0x7C,0x1F,0xF8,0x3C,0x00, | |||
// 'v' | |||
0x76,0x07,0x0E,0x0D,0x00,0x0D, | |||
0x01,0xFB,0xF3,0xEF,0xC6,0x06,0x18,0x18,0x60,0x31,0x00,0xCC,0x01,0x30,0x06,0x80,0x0A,0x00,0x38,0x00,0xE0,0x01,0x00, | |||
// 'w' | |||
0x77,0x08,0x11,0x0D,0x00,0x11, | |||
0x04,0x1F,0xFF,0x6F,0xBE,0x31,0x83,0x1C,0xC1,0x9E,0x40,0x4D,0x20,0x34,0xB0,0x1A,0x78,0x05,0x3C,0x03,0x8E,0x01,0x86,0x00,0x43,0x00,0x00,0x00, | |||
// 'x' | |||
0x78,0x08,0x0D,0x0D,0x00,0x0D, | |||
0x0C,0xFB,0xE7,0xDE,0x18,0x31,0x80,0xD8,0x03,0x80,0x18,0x01,0xE0,0x19,0x81,0x8C,0x0E,0x7D,0xF3,0xE4,0x00,0x00, | |||
// 'y' | |||
0x79,0x06,0x0F,0x11,0x00,0x0D, | |||
0x7C,0x01,0xF8,0x00,0xC3,0xF0,0xC7,0xE1,0x82,0x01,0x0C,0x03,0x18,0x06,0x20,0x06,0xC0,0x0D,0x00,0x0E,0x00,0x18,0x00,0x30,0x06,0xC0,0x1F,0x00,0x3E,0x00,0x30,0x00, | |||
// 'z' | |||
0x7A,0x08,0x0B,0x0B,0x00,0x0B, | |||
0x7F,0xCF,0xF9,0x86,0x31,0x86,0x60,0x1C,0x07,0x00,0xC6,0x30,0xCF,0xF9,0xFF,0x00, | |||
// '{' | |||
0x7B,0x02,0x09,0x12,0x00,0x08, | |||
0x07,0x07,0x83,0x01,0x01,0x80,0xC0,0x60,0xE0,0x70,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x1F,0x03,0x80, | |||
// '|' | |||
0x7C,0x02,0x03,0x12,0x01,0x05, | |||
0x49,0x24,0x92,0x6D,0xB6,0xDB,0x6C, | |||
// '}' | |||
0x7D,0x02,0x07,0x11,0x00,0x07, | |||
0x30,0xF0,0x20,0x41,0x83,0x02,0x06,0x0E,0x18,0x60,0xC0,0x81,0x83,0x3C,0x78, | |||
// '~' | |||
0x7E,0x09,0x0D,0x04,0x00,0x0D, | |||
0x18,0x03,0xF1,0x98,0xFC,0x83,0xC0, | |||
// Terminator | |||
0xFF | |||
}; |
@@ -0,0 +1,331 @@ | |||
// This comes with no warranty, implied or otherwise | |||
// This data structure was designed to support Proportional fonts | |||
// on Arduinos. It can however handle any ttf font that has been converted | |||
// using the conversion program. These could be fixed width or proportional | |||
// fonts. Individual characters do not have to be multiples of 8 bits wide. | |||
// Any width is fine and does not need to be fixed. | |||
// The data bits are packed to minimize data requirements, but the tradeoff | |||
// is that a header is required per character. | |||
// tooney32.c | |||
// Point Size : 32 | |||
// Memory usage : 5470 bytes | |||
// # characters : 95 | |||
// Header Format (to make Arduino UTFT Compatible): | |||
// ------------------------------------------------ | |||
// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00) | |||
// Character Height | |||
// First Character (Reserved. 0x00) | |||
// Number Of Characters (Reserved. 0x00) | |||
unsigned char tft_tooney32[] = | |||
{ | |||
0x00, 0x20, 0x00, 0x00, | |||
// Individual Character Format: | |||
// ---------------------------- | |||
// Character Code | |||
// Adjusted Y Offset | |||
// Width | |||
// Height | |||
// xOffset | |||
// xDelta (the distance to move the cursor. Effective width of the character.) | |||
// Data[n] | |||
// NOTE: You can remove any of these characters if they are not needed in | |||
// your application. The first character number in each Glyph indicates | |||
// the ASCII character code. Therefore, these do not have to be sequential. | |||
// Just remove all the content for a particular character to save space. | |||
// ' ' | |||
0x20,0x1E,0x00,0x00,0x00,0x09, | |||
// '!' | |||
0x21,0x09,0x0B,0x16,0x00,0x0B, | |||
0x3F,0xC8,0x07,0x81,0x70,0x27,0x00,0xE0,0x1C,0x21,0x84,0x30,0x86,0x10,0xC2,0x18,0x43,0xF0,0x61,0x0C,0x13,0x02,0x60,0x4E,0x09,0xE2,0x1F,0x81,0xE0,0x00,0x00, | |||
// '"' | |||
0x22,0x05,0x0E,0x0A,0xFF,0x0D, | |||
0x04,0x30,0x2D,0x61,0x8C,0x44,0x71,0x31,0x88,0xCE,0x42,0x72,0x18,0xC8,0x7B,0xC0,0xC6,0x00, | |||
// '#' | |||
0x23,0x07,0x18,0x16,0x00,0x18, | |||
0x00,0xFF,0xF8,0x01,0x83,0x08,0x01,0x82,0x08,0x01,0x82,0x08,0x0F,0x06,0x0F,0x10,0x00,0x01,0x30,0x00,0x01,0x30,0x00,0x00,0x20,0x00,0x02,0x7E,0x0C,0x1E,0x7E,0x0C,0x1E,0x60,0x00,0x02,0x60,0x00,0x00,0x40,0x00,0x04,0x40,0x00,0x04,0xC0,0x00,0x04,0xFC,0x10,0x78,0xFC,0x30,0x78,0x08,0x30,0x40,0x18,0x30,0x40,0x1F,0xFF,0x80,0x1F,0xFF,0x80, | |||
// '$' | |||
0x24,0x09,0x0F,0x14,0x00,0x0F, | |||
0x01,0x80,0x04,0xF8,0x18,0x08,0x20,0x10,0xC0,0x41,0x00,0x86,0x01,0x0C,0x12,0x18,0x38,0x30,0x60,0xA0,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x10,0x30,0x20,0x6E,0x40,0xFF,0x81,0x9E,0x00,0x18,0x00, | |||
// '%' | |||
0x25,0x07,0x17,0x16,0x00,0x17, | |||
0x0F,0x81,0xF8,0x20,0x84,0x10,0x80,0x98,0x42,0x00,0x21,0x0C,0x01,0x82,0x18,0x43,0x08,0x30,0x04,0x10,0x70,0x18,0x40,0xE0,0x21,0x00,0xF1,0x82,0x00,0xFF,0x0F,0xC0,0xFC,0x10,0x40,0x18,0x40,0x40,0x21,0x80,0x40,0x82,0x00,0x83,0x0C,0x21,0x04,0x18,0x02,0x18,0x78,0x04,0x21,0x30,0x10,0xC2,0x78,0xC3,0xF8,0x7F,0x07,0xE0,0x7C,0x00, | |||
// '&' | |||
0x26,0x08,0x17,0x17,0x00,0x17, | |||
0x01,0xF6,0x00,0x04,0x1A,0x00,0x10,0x04,0x00,0x40,0x10,0x01,0x00,0x40,0x06,0x00,0x80,0x0C,0x1A,0x00,0x18,0x1F,0xC0,0x30,0x18,0xF8,0x60,0x08,0x09,0x80,0x00,0x33,0x00,0x00,0xCC,0x0C,0x01,0x18,0x3C,0x04,0x30,0x30,0x07,0x60,0x00,0x01,0xE0,0x00,0x07,0xC0,0x00,0x09,0xC0,0x0C,0x23,0xC0,0x38,0x03,0xE1,0xF8,0x03,0xFF,0x70,0x01,0xF8,0x60,0x00, | |||
// ''' | |||
0x27,0x05,0x09,0x0A,0xFF,0x08, | |||
0x06,0x05,0x86,0x23,0x13,0x11,0x90,0x90,0xC8,0x78,0x18,0x00, | |||
// '(' | |||
0x28,0x05,0x0D,0x1D,0x00,0x0D, | |||
0x03,0x00,0x34,0x01,0x90,0x08,0x40,0x81,0x88,0x1C,0xC1,0xC4,0x08,0x60,0x83,0x04,0x10,0x41,0x82,0x0C,0x10,0x60,0x83,0x04,0x18,0x20,0xC0,0x06,0x04,0x38,0x20,0xC0,0x87,0x06,0x38,0x1C,0xE0,0x67,0x86,0x1C,0x60,0x76,0x01,0xE0,0x0E,0x00,0x60,0x00, | |||
// ')' | |||
0x29,0x05,0x0D,0x1D,0x00,0x0D, | |||
0x01,0x00,0x10,0x01,0x20,0x11,0x03,0x04,0x30,0x11,0xE0,0x8F,0x82,0x1C,0x10,0x70,0x81,0x82,0x0E,0x10,0x30,0x81,0x84,0x0C,0x20,0x61,0x02,0x08,0x10,0x01,0x84,0x08,0x20,0x81,0x18,0x11,0x80,0x8E,0x08,0x78,0x80,0xE4,0x03,0xE0,0x0E,0x00,0x30,0x00, | |||
// '*' | |||
0x2A,0x09,0x0C,0x0D,0x01,0x0D, | |||
0x07,0x00,0x88,0x18,0xE4,0x11,0xC0,0x1F,0x8E,0xC0,0x1C,0x11,0xC8,0xBF,0x8E,0x7D,0xC1,0xE0,0x0C,0x00, | |||
// '+' | |||
0x2B,0x09,0x15,0x14,0x00,0x15, | |||
0x00,0x7C,0x00,0x04,0x10,0x00,0x60,0x80,0x07,0x04,0x00,0x38,0x20,0x01,0xC1,0x00,0xFE,0x0F,0xCC,0x00,0x01,0xE0,0x00,0x0F,0x00,0x00,0x78,0x00,0x03,0xFF,0x07,0xFF,0xF8,0x3F,0x7F,0xC1,0xF0,0x0E,0x08,0x00,0x70,0x40,0x03,0x82,0x00,0x1F,0xF0,0x00,0xFE,0x00,0x03,0xE0,0x00, | |||
// ',' | |||
0x2C,0x17,0x09,0x0B,0x00,0x09, | |||
0x1E,0x10,0x98,0x38,0x1C,0x0F,0x0B,0xC4,0xE4,0x32,0x1E,0x0E,0x00, | |||
// '-' | |||
0x2D,0x11,0x09,0x06,0x00,0x09, | |||
0x1B,0x90,0x50,0x39,0x2F,0xE7,0xF0, | |||
// '.' | |||
0x2E,0x16,0x09,0x09,0x00,0x09, | |||
0x1E,0x10,0x90,0x38,0x1C,0x0F,0x07,0xCC,0xFC,0x3C,0x00, | |||
// '/' | |||
0x2F,0x09,0x11,0x19,0x00,0x11, | |||
0x00,0x3F,0x80,0x30,0x40,0x30,0x20,0x18,0x20,0x18,0x10,0x0C,0x08,0x06,0x08,0x06,0x04,0x03,0x04,0x03,0x02,0x01,0x81,0x00,0xC1,0x00,0xC0,0x80,0x60,0x00,0x60,0x40,0x30,0x20,0x18,0x20,0x18,0x10,0x0C,0x08,0x06,0x08,0x06,0x04,0x03,0x04,0x03,0xFE,0x01,0xFE,0x00,0xFE,0x00,0x00, | |||
// '0' | |||
0x30,0x08,0x17,0x17,0x00,0x17, | |||
0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00, | |||
// '1' | |||
0x31,0x09,0x0D,0x16,0x00,0x0D, | |||
0x00,0x30,0x06,0x40,0xC4,0x18,0x43,0x02,0x20,0x13,0xC0,0x9E,0x04,0x30,0x21,0x81,0x0C,0x08,0x60,0x43,0x02,0x18,0x10,0xC0,0x86,0x04,0x30,0x21,0x81,0x98,0x03,0xFF,0xE7,0xFE,0x00,0x00, | |||
// '2' | |||
0x32,0x08,0x12,0x17,0x00,0x12, | |||
0x00,0xF0,0x00,0x81,0x00,0xC0,0x20,0xE0,0x04,0x40,0x01,0x38,0x00,0x2F,0x84,0x08,0xF9,0x82,0x1F,0xE0,0x81,0xF0,0x00,0x1C,0x10,0x06,0x04,0x01,0x03,0x80,0xC0,0xD0,0x20,0x04,0x18,0x01,0x0C,0x00,0x43,0x00,0x11,0x80,0x04,0x60,0x01,0x3F,0xFF,0x4F,0xFF,0xE0,0x00,0x30, | |||
// '3' | |||
0x33,0x08,0x12,0x17,0x00,0x12, | |||
0x0C,0x00,0x05,0xFF,0xE3,0x00,0x08,0xC0,0x02,0x30,0x01,0x0C,0x00,0x43,0x00,0x10,0xC0,0x0C,0x37,0x01,0x0F,0x80,0x23,0xE0,0x08,0x10,0x01,0x0F,0xE0,0x43,0xF8,0x10,0x4E,0x04,0x23,0x01,0x10,0x00,0x08,0x00,0x24,0x00,0x13,0x00,0x08,0xFC,0x0C,0x1F,0xFE,0x00,0xFE,0x00, | |||
// '4' | |||
0x34,0x09,0x12,0x16,0x00,0x12, | |||
0x00,0x0E,0x00,0x04,0x80,0x06,0x20,0x03,0x08,0x01,0x82,0x00,0xC0,0x80,0x40,0x20,0x20,0x08,0x10,0x03,0x88,0x20,0x94,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0xFE,0x09,0xFF,0x83,0x80,0x60,0xC0,0x30,0x10,0x0F,0xF8,0x03,0xFC,0x00,0x00,0x00, | |||
// '5' | |||
0x35,0x08,0x11,0x17,0x00,0x11, | |||
0x00,0x03,0x00,0xFF,0x40,0x80,0x20,0xC0,0x10,0x60,0x08,0x30,0x04,0x30,0x02,0x18,0x1D,0x0C,0x07,0x06,0x01,0x82,0x00,0x43,0x00,0x11,0xFC,0x08,0xFF,0x04,0x17,0x82,0x18,0x01,0x18,0x00,0x08,0x00,0x8C,0x00,0x8C,0x00,0xC7,0xC1,0xC3,0xFF,0x80,0xBF,0x00, | |||
// '6' | |||
0x36,0x08,0x13,0x17,0x00,0x13, | |||
0x00,0x7F,0xC0,0x30,0x08,0x18,0x01,0x06,0x00,0x41,0x80,0x10,0x60,0x02,0x08,0x0C,0x83,0x00,0x30,0x60,0x01,0x18,0x00,0x13,0x00,0x01,0x60,0x00,0x0C,0x00,0x03,0x80,0xC0,0x78,0x3C,0x0F,0x03,0x01,0x60,0x00,0x0E,0x00,0x08,0xE0,0x02,0x1E,0x00,0x81,0xF0,0x60,0x1F,0xF8,0x00,0xFC,0x00, | |||
// '7' | |||
0x37,0x08,0x12,0x17,0x00,0x12, | |||
0x0C,0x00,0x0D,0xFF,0xF3,0x00,0x04,0xC0,0x02,0x30,0x00,0x8C,0x00,0x43,0x00,0x10,0xDC,0x08,0x3F,0x02,0x0E,0x81,0x00,0x60,0x40,0x10,0x20,0x0C,0x08,0x02,0x04,0x01,0x81,0x00,0x40,0x80,0x30,0x20,0x10,0x10,0x0F,0x04,0x03,0xF1,0x00,0x3F,0x40,0x03,0xE0,0x00,0x30,0x00, | |||
// '8' | |||
0x38,0x08,0x12,0x17,0x00,0x12, | |||
0x01,0xF0,0x00,0x83,0x00,0xC0,0x20,0x20,0x08,0x10,0x01,0x0C,0x18,0x43,0x06,0x10,0xC0,0x04,0x38,0x01,0x0C,0x00,0x42,0x00,0x09,0x80,0x01,0xC0,0x00,0x70,0x3C,0x1C,0x0F,0x07,0x00,0x01,0xE0,0x00,0x9C,0x00,0x27,0x80,0x30,0xF8,0x38,0x1F,0xFC,0x01,0xFC,0x00,0x00,0x00, | |||
// '9' | |||
0x39,0x08,0x13,0x17,0x00,0x13, | |||
0x01,0xF8,0x00,0xC0,0xC0,0x20,0x04,0x08,0x00,0x42,0x00,0x08,0xC0,0x00,0x90,0x18,0x16,0x07,0x81,0xC0,0x60,0x38,0x00,0x07,0x80,0x00,0xF0,0x00,0x17,0x00,0x02,0xF0,0x00,0x0F,0x80,0x10,0xE6,0x02,0x08,0x00,0x83,0x00,0x20,0x40,0x0C,0x10,0x03,0x06,0xC1,0xC0,0xFF,0xE0,0x1F,0xF0,0x00, | |||
// ':' | |||
0x3A,0x0E,0x09,0x11,0x00,0x09, | |||
0x0E,0x10,0x90,0x38,0x1C,0x0F,0x07,0xC4,0xFC,0x3C,0x19,0x98,0x38,0x1C,0x0F,0x07,0xCC,0xFC,0x3C,0x00, | |||
// ';' | |||
0x3B,0x0F,0x09,0x13,0x00,0x09, | |||
0x0E,0x10,0x98,0x38,0x1C,0x0F,0x07,0xC4,0xFC,0x3E,0x19,0x98,0x38,0x1C,0x0F,0x03,0xC4,0xE4,0x32,0x1E,0x0E,0x00, | |||
// '<' | |||
0x3C,0x0A,0x13,0x13,0x00,0x13, | |||
0x00,0x00,0xC0,0x00,0x64,0x00,0x60,0x80,0x30,0x10,0x38,0x02,0x18,0x03,0x8C,0x01,0xE3,0x01,0xF0,0xE0,0xF8,0x1C,0x06,0x03,0x80,0x18,0x7E,0x00,0xCF,0xF0,0x06,0x7F,0xC0,0x43,0xFE,0x08,0x0F,0xF9,0x00,0x7F,0xE0,0x01,0xF8,0x00,0x0C,0x00, | |||
// '=' | |||
0x3D,0x0D,0x14,0x0E,0x00,0x14, | |||
0x3F,0xFF,0xE6,0x00,0x01,0xE0,0x00,0x1E,0x00,0x01,0xE0,0x00,0x1F,0xFF,0xFE,0xFF,0xFF,0xE6,0x00,0x01,0x60,0x00,0x1E,0x00,0x01,0xE0,0x00,0x1F,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF8, | |||
// '>' | |||
0x3E,0x0A,0x13,0x13,0x00,0x13, | |||
0x38,0x00,0x0C,0xC0,0x03,0x87,0x00,0x70,0x18,0x0E,0x00,0xE1,0xF8,0x03,0x3F,0xC0,0x19,0xFF,0x01,0x0F,0xF8,0x20,0x3C,0x04,0x0C,0x00,0x86,0x00,0xE3,0x00,0x78,0xC0,0x7C,0x38,0x3E,0x07,0x3E,0x00,0xFF,0x00,0x1F,0x00,0x03,0x80,0x00,0x00, | |||
// '?' | |||
0x3F,0x08,0x11,0x16,0x00,0x11, | |||
0x00,0xF0,0x01,0x82,0x01,0x00,0x81,0x00,0x23,0x00,0x0B,0x00,0x05,0xC0,0x02,0xF8,0xC1,0x3F,0x40,0x87,0xE0,0x80,0xE0,0x40,0x30,0x40,0x18,0x20,0x0F,0xE0,0x07,0x30,0x03,0x04,0x03,0x02,0x01,0x81,0x00,0xE0,0x80,0x78,0x80,0x1F,0x80,0x07,0x80, | |||
// '@' | |||
0x40,0x09,0x16,0x16,0x00,0x16, | |||
0x00,0x3F,0x00,0x06,0x03,0x00,0x23,0xFB,0x01,0x3F,0xFC,0x09,0x81,0xF8,0x48,0x7F,0xE2,0x44,0x13,0xD9,0x23,0x8F,0x41,0x1E,0x1F,0x2C,0x51,0x7C,0xE0,0xC5,0xF3,0x8B,0x06,0xCE,0x28,0x3B,0x38,0xE2,0xEE,0x71,0x4D,0x19,0xE3,0x88,0x73,0xFF,0xD1,0xE7,0x9F,0xA3,0xC7,0xF1,0x07,0xE0,0x38,0x07,0xFF,0x80,0x07,0xF8,0x00, | |||
// 'A' | |||
0x41,0x08,0x19,0x17,0x00,0x19, | |||
0x00,0x0C,0x00,0x00,0x09,0x00,0x00,0x08,0x80,0x00,0x0C,0x20,0x00,0x04,0x08,0x00,0x06,0x04,0x00,0x02,0x01,0x00,0x03,0x00,0xC0,0x03,0x00,0x20,0x01,0x00,0x08,0x01,0x80,0x04,0x00,0x80,0x01,0x00,0xC0,0xC0,0x40,0xC0,0x60,0x20,0x40,0x00,0x08,0x60,0x00,0x06,0x40,0x00,0x00,0xF0,0x00,0x00,0xFE,0x1F,0xE1,0xCF,0xCF,0xF1,0x81,0xF4,0x1B,0x80,0x3E,0x0F,0x00,0x0E,0x06,0x00, | |||
// 'B' | |||
0x42,0x09,0x13,0x15,0x00,0x13, | |||
0x3F,0xFC,0x0C,0x00,0x61,0xC0,0x06,0x38,0x00,0x43,0x00,0x04,0x60,0x60,0x8C,0x0C,0x11,0x81,0x02,0x30,0x00,0x46,0x00,0x08,0xC0,0x00,0x98,0x18,0x13,0x03,0x02,0x60,0x60,0x4C,0x00,0x09,0x80,0x01,0x30,0x00,0x44,0x00,0x11,0x80,0x0E,0x3F,0xFF,0x07,0xFF,0x80, | |||
// 'C' | |||
0x43,0x08,0x16,0x17,0x00,0x16, | |||
0x00,0x7E,0x00,0x06,0x02,0x00,0x60,0x06,0x02,0x00,0x06,0x10,0x00,0x00,0xC0,0x00,0x22,0x00,0x01,0x18,0x0F,0x18,0x40,0x7E,0xC3,0x02,0x3E,0x0C,0x08,0xF0,0x30,0x21,0x80,0xC0,0x83,0x03,0x81,0x1A,0x0E,0x03,0xC4,0x18,0x00,0x08,0x70,0x00,0x19,0xE0,0x00,0x23,0xC0,0x01,0x87,0x80,0x1E,0x0F,0xC1,0xE0,0x0F,0xFE,0x00,0x0F,0xC0,0x00, | |||
// 'D' | |||
0x44,0x09,0x16,0x15,0xFF,0x15, | |||
0x1F,0xFE,0x00,0x80,0x06,0x07,0x00,0x06,0x1C,0x00,0x0C,0x30,0x00,0x10,0xC0,0x00,0x23,0x00,0x00,0x8C,0x0F,0x01,0x30,0x3E,0x04,0xC0,0xF8,0x13,0x03,0xE0,0x4C,0x0F,0x01,0x30,0x00,0x04,0xC0,0x00,0x23,0x00,0x00,0x8C,0x00,0x04,0x30,0x00,0x20,0x80,0x01,0x06,0x00,0x18,0x1F,0xFF,0x80,0x3F,0xF0,0x00, | |||
// 'E' | |||
0x45,0x08,0x11,0x17,0x00,0x11, | |||
0x00,0x01,0x0F,0xFF,0x48,0x00,0x2C,0x00,0x17,0x00,0x0B,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x1A,0x98,0x01,0x8C,0x00,0x86,0x00,0x43,0x00,0x31,0x80,0xD4,0xC0,0x02,0x60,0x01,0x30,0x00,0x98,0x00,0x4C,0x00,0x24,0x00,0x17,0xFF,0xEB,0xFF,0xF8,0x00,0x08, | |||
// 'F' | |||
0x46,0x08,0x11,0x16,0xFF,0x10, | |||
0x00,0x01,0x0F,0xFF,0x48,0x00,0x2E,0x00,0x17,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x3A,0x98,0x01,0x8C,0x00,0xC6,0x00,0x43,0x00,0x21,0x81,0xD0,0xC0,0xF0,0x60,0x70,0x30,0x20,0x18,0x10,0x08,0x02,0x0C,0x01,0x07,0xFF,0x03,0xFF,0x00, | |||
// 'G' | |||
0x47,0x08,0x16,0x17,0x00,0x16, | |||
0x00,0x7F,0x00,0x06,0x03,0x00,0x60,0x03,0x82,0x00,0x02,0x10,0x00,0x08,0xC0,0x00,0xC2,0x00,0x06,0x18,0x07,0x30,0x40,0x3C,0x83,0x01,0x3C,0x0C,0x04,0xFF,0xB0,0x14,0x02,0xC0,0x78,0x1B,0x80,0xE0,0x4E,0x01,0x81,0x18,0x00,0x04,0x70,0x00,0x11,0xE0,0x00,0x43,0xC0,0x01,0x07,0x80,0x18,0x0F,0x81,0xC0,0x1F,0xFC,0x00,0x0F,0xC0,0x00, | |||
// 'H' | |||
0x48,0x09,0x16,0x15,0x00,0x16, | |||
0x3F,0xC7,0xF9,0x00,0xE0,0x1E,0x03,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x80,0xF0,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x00,0x09,0x80,0x00,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x3C,0x09,0x80,0xF0,0x26,0x02,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x00,0x20,0x1F,0xFF,0xFF,0xFF,0xF7,0xFC,0x00,0x00,0x00, | |||
// 'I' | |||
0x49,0x09,0x0B,0x15,0x00,0x0B, | |||
0x3F,0xC8,0x07,0x81,0x70,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x4C,0x09,0x81,0x30,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x48,0x07,0x00,0xFF,0xEF,0xF8, | |||
// 'J' | |||
0x4A,0x09,0x0F,0x16,0x00,0x0F, | |||
0x03,0xFC,0x08,0x04,0x38,0x08,0x70,0x10,0x60,0x20,0xC0,0x41,0x80,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x08,0x30,0x10,0xA0,0x23,0x00,0x44,0x00,0x98,0x01,0x20,0x00,0xC0,0x09,0x00,0x27,0xC1,0x8F,0xFE,0x0F,0xF0,0x00, | |||
// 'K' | |||
0x4B,0x08,0x17,0x18,0x00,0x17, | |||
0x00,0x01,0x80,0x7F,0xEE,0x81,0x80,0x38,0x87,0x81,0xE0,0x8F,0x03,0x80,0x86,0x06,0x00,0x8C,0x08,0x07,0x18,0x00,0x1C,0x30,0x00,0x70,0x60,0x01,0x00,0xC0,0x02,0x01,0x80,0x02,0x03,0x00,0x04,0x06,0x04,0x04,0x0C,0x0C,0x04,0x18,0x18,0x06,0x30,0x38,0x00,0x60,0x78,0x09,0x00,0x30,0x26,0x00,0x71,0x8F,0xFF,0xE4,0x0F,0xFC,0xD0,0x00,0x01,0xC0,0x00,0x00,0x00, | |||
// 'L' | |||
0x4C,0x09,0x11,0x16,0x00,0x11, | |||
0x3F,0xF0,0x30,0x04,0x1C,0x06,0x0E,0x02,0x03,0x01,0x01,0x80,0x80,0xC0,0x40,0x60,0x20,0x30,0x10,0x18,0x08,0x0C,0x04,0xC6,0x03,0xD3,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x00,0xB0,0x00,0x58,0x00,0x2F,0xFF,0xD7,0xFF,0xF0,0x00,0x30, | |||
// 'M' | |||
0x4D,0x09,0x20,0x16,0x00,0x1F, | |||
0x00,0xF8,0x1F,0x00,0x01,0x84,0x30,0x80,0x01,0x82,0x61,0x00,0x01,0x82,0x40,0x00,0x01,0x81,0xC0,0x80,0x01,0x01,0x80,0x80,0x03,0x00,0x80,0x40,0x02,0x00,0x00,0x40,0x06,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x04,0x00,0x00,0x10,0x0C,0x00,0x00,0x10,0x08,0x00,0x00,0x10,0x18,0x08,0x08,0x08,0x10,0x18,0x18,0x08,0x30,0x1C,0x1C,0x04,0x60,0x3C,0x3C,0x02,0x70,0x2E,0x2E,0x06,0x7C,0x27,0x4E,0x3C,0x3F,0x27,0xC6,0xF0,0x07,0xC3,0x8F,0x80,0x01,0x83,0x06,0x00, | |||
// 'N' | |||
0x4E,0x09,0x17,0x15,0x00,0x17, | |||
0x3F,0x07,0xFC,0xC1,0x10,0x07,0xC1,0x78,0x1F,0x81,0xF0,0x23,0x01,0xE0,0x46,0x01,0xC0,0x8C,0x01,0x81,0x18,0x01,0x02,0x30,0x00,0x04,0x60,0x00,0x08,0xC0,0x00,0x11,0x80,0x00,0x23,0x02,0x00,0x46,0x06,0x00,0x8C,0x0E,0x01,0x18,0x1E,0x02,0x30,0x3F,0x04,0x60,0x3F,0x0B,0x00,0x2F,0x17,0xFF,0x8F,0xEF,0xFE,0x0F,0x80, | |||
// 'O' | |||
0x4F,0x08,0x17,0x17,0x00,0x17, | |||
0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00, | |||
// 'P' | |||
0x50,0x09,0x13,0x15,0x00,0x13, | |||
0x3F,0xFC,0x08,0x00,0x43,0x80,0x06,0x70,0x00,0x46,0x00,0x04,0xC0,0x00,0x18,0x00,0x0B,0x01,0x81,0x60,0x30,0x2C,0x04,0x05,0x80,0x00,0x30,0x00,0x26,0x00,0x08,0xC0,0x02,0x18,0x01,0x83,0x01,0xE0,0x60,0x30,0x08,0x04,0x03,0x00,0x80,0x7F,0xE0,0x07,0xF8,0x00, | |||
// 'Q' | |||
0x51,0x09,0x17,0x1C,0x00,0x17, | |||
0x00,0x7E,0x00,0x03,0x01,0x00,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x01,0x08,0x00,0x01,0x30,0x1E,0x02,0x40,0x7E,0x03,0x81,0x1E,0x07,0x02,0x1C,0x0E,0x04,0x18,0x1C,0x04,0x20,0x3C,0x07,0x80,0x78,0x00,0x01,0x30,0x00,0x02,0x70,0x00,0x04,0xF0,0x00,0x10,0xF0,0x00,0x70,0xF0,0x00,0x00,0xFC,0x00,0x40,0xFF,0x81,0x00,0x3F,0x84,0x00,0x0F,0x10,0x00,0x06,0x40,0x00,0x0F,0x00,0x00,0x1C,0x00,0x00,0x00,0x00, | |||
// 'R' | |||
0x52,0x09,0x18,0x17,0x00,0x18, | |||
0x3F,0xFE,0x00,0x60,0x01,0x80,0x70,0x00,0x40,0x70,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x10,0x10,0x30,0x18,0x10,0x30,0x10,0x10,0x30,0x00,0x10,0x30,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x1E,0x30,0x00,0x0A,0x30,0x18,0x06,0x30,0x1C,0x0C,0x20,0x0E,0x18,0x7F,0xFF,0x30,0x7F,0xE7,0x20,0x00,0x03,0xC0,0x00,0x03,0x80,0x00,0x03,0x00, | |||
// 'S' | |||
0x53,0x07,0x11,0x19,0x00,0x11, | |||
0x00,0x06,0x00,0x02,0x80,0x1E,0x40,0x10,0x20,0x30,0x10,0x30,0x08,0x10,0x04,0x18,0x01,0x08,0x00,0x8C,0x0F,0x46,0x07,0xA3,0x01,0xE1,0xC0,0xE1,0xE0,0x41,0x70,0x21,0x80,0x10,0xC0,0x08,0x70,0x08,0x38,0x04,0x0C,0x04,0x06,0x04,0x03,0x3C,0x01,0xFC,0x00,0xF0,0x00,0x30,0x00,0x00, | |||
// 'T' | |||
0x54,0x08,0x12,0x16,0x01,0x13, | |||
0x30,0x01,0x93,0xFF,0xDC,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0x00,0x01,0xCC,0x07,0x7F,0x01,0xFF,0xC0,0x79,0xB0,0x1C,0x0C,0x04,0x03,0x01,0x00,0xC0,0x40,0x30,0x10,0x0C,0x04,0x03,0x01,0x00,0xC0,0x60,0x60,0x08,0x1F,0xFC,0x07,0xFE,0x00, | |||
// 'U' | |||
0x55,0x09,0x19,0x16,0xFF,0x18, | |||
0x1F,0xF1,0xFF,0x10,0x05,0x00,0x9C,0x07,0xC0,0xCE,0x02,0xE0,0x43,0x01,0x30,0x21,0x80,0x98,0x10,0xC0,0x4C,0x08,0x60,0x26,0x04,0x30,0x13,0x02,0x18,0x09,0x81,0x0C,0x04,0xC0,0x86,0x02,0x60,0x43,0x01,0x30,0x21,0xC0,0x70,0x10,0xE0,0x00,0x10,0x30,0x00,0x08,0x1C,0x00,0x08,0x0F,0x00,0x08,0x03,0xC0,0x08,0x00,0xFC,0x18,0x00,0x1F,0xF8,0x00,0x03,0xF0,0x00, | |||
// 'V' | |||
0x56,0x07,0x19,0x18,0x00,0x19, | |||
0x00,0xC1,0xC0,0x01,0x91,0x98,0x03,0x18,0xC3,0x06,0x04,0x60,0x64,0x02,0x20,0x1F,0x00,0xB0,0x1B,0xC0,0x70,0x0C,0xF0,0x10,0x08,0x38,0x00,0x08,0x0E,0x00,0x04,0x07,0x00,0x04,0x01,0xC0,0x06,0x00,0xE0,0x02,0x00,0x38,0x02,0x00,0x1C,0x01,0x00,0x07,0x01,0x00,0x03,0x81,0x80,0x00,0xE0,0x80,0x00,0x30,0x80,0x00,0x1C,0x40,0x00,0x06,0x40,0x00,0x03,0xE0,0x00,0x00,0xE0,0x00,0x00,0x60,0x00, | |||
// 'W' | |||
0x57,0x07,0x20,0x18,0x00,0x20, | |||
0x00,0x60,0x03,0x80,0x01,0x90,0x06,0x60,0x0E,0x10,0xC6,0x1C,0x38,0x11,0xA6,0x07,0x60,0x11,0x26,0x01,0x78,0x12,0x1C,0x06,0x78,0x0E,0x1C,0x04,0x3C,0x0C,0x08,0x08,0x1C,0x00,0x08,0x10,0x0C,0x00,0x00,0x10,0x0E,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x07,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x80,0x00,0x80,0x03,0x80,0x00,0x80,0x01,0xC0,0x81,0x00,0x01,0xC1,0xC0,0x00,0x00,0xE1,0xC2,0x00,0x00,0xE2,0xE4,0x00,0x00,0x74,0xE4,0x00,0x00,0x7C,0x78,0x00,0x00,0x38,0x78,0x00,0x00,0x30,0x30,0x00, | |||
// 'X' | |||
0x58,0x05,0x19,0x1D,0x00,0x18, | |||
0x00,0x01,0x80,0x00,0x01,0x20,0x00,0x61,0x8C,0x00,0x48,0xC3,0x00,0x44,0x40,0x60,0x43,0x60,0x10,0xC0,0xE0,0x18,0xC0,0x30,0x1C,0xC0,0x00,0x18,0x7C,0x00,0x08,0x1F,0x00,0x08,0x03,0x80,0x08,0x00,0xE0,0x04,0x00,0x38,0x02,0x00,0x0C,0x01,0x80,0x04,0x00,0x60,0x06,0x00,0x10,0x02,0x00,0x07,0x03,0x00,0x00,0x41,0x01,0x00,0x63,0x00,0xC0,0x62,0x00,0xF0,0x63,0x80,0x7C,0x61,0xF0,0x4E,0x60,0x7C,0x63,0xE0,0x0F,0x91,0xE0,0x01,0xF0,0x40,0x00,0x70,0x00,0x00,0x00,0x00,0x00, | |||
// 'Y' | |||
0x59,0x06,0x19,0x1B,0x00,0x19, | |||
0x00,0x00,0x60,0x00,0xC0,0x48,0x00,0x80,0x62,0x00,0x88,0x20,0xC0,0x82,0x30,0x11,0x80,0xB0,0x01,0x80,0x30,0x0D,0x80,0x00,0x0C,0xF8,0x00,0x08,0x3E,0x00,0x04,0x07,0x80,0x04,0x01,0xE0,0x04,0x00,0x78,0x04,0x00,0x1C,0x02,0x00,0x0C,0x02,0x00,0x04,0x02,0x00,0x04,0x02,0x00,0x06,0x01,0x00,0x0E,0x01,0x00,0x0C,0x01,0x00,0x07,0x01,0x00,0x03,0xC0,0x80,0x01,0xF0,0x80,0x00,0x3E,0x00,0x00,0x0F,0xC0,0x00,0x03,0xE0,0x00,0x00,0x40,0x00,0x00, | |||
// 'Z' | |||
0x5A,0x08,0x13,0x17,0xFF,0x12, | |||
0x0C,0x00,0x02,0xFF,0xFC,0xC0,0x01,0x98,0x00,0x23,0x00,0x04,0x60,0x01,0x0C,0x00,0x21,0x80,0x08,0x37,0x01,0x07,0xE0,0x60,0xF8,0x08,0x03,0x01,0x00,0x40,0x70,0x18,0x0D,0x02,0x00,0x20,0xC0,0x04,0x18,0x00,0x86,0x00,0x10,0xC0,0x02,0x10,0x00,0x47,0xFF,0xE8,0xFF,0xFE,0x00,0x01,0x80, | |||
// '[' | |||
0x5B,0x04,0x0B,0x20,0x00,0x0B, | |||
0x00,0x40,0x19,0xFD,0x60,0x2C,0x05,0x80,0xB0,0x16,0x0E,0xC1,0xD8,0x33,0x04,0x60,0x8C,0x11,0x82,0x30,0x46,0x08,0xC1,0x18,0x23,0x04,0x60,0x8C,0x11,0x82,0xB0,0x76,0x02,0xC0,0x58,0x0B,0x01,0x60,0x2F,0xFD,0xFF,0x80,0x60,0x00, | |||
// '\' | |||
0x5C,0x09,0x11,0x18,0x00,0x11, | |||
0x3F,0x00,0x30,0x40,0x38,0x10,0x1E,0x08,0x0F,0x04,0x03,0x81,0x01,0xE0,0x80,0x70,0x20,0x3C,0x10,0x1E,0x08,0x07,0x02,0x03,0xC1,0x00,0xE0,0x40,0x78,0x20,0x3C,0x08,0x0F,0x04,0x07,0x82,0x01,0xC0,0x80,0xF0,0x40,0x38,0x10,0x1E,0x08,0x0F,0xFC,0x03,0xFC,0x01,0xFC, | |||
// ']' | |||
0x5D,0x04,0x0C,0x1F,0xFF,0x0B, | |||
0x30,0x02,0xFF,0x60,0x16,0x01,0x60,0x16,0x01,0x60,0x16,0xC1,0x7C,0x16,0xC1,0x6C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x13,0xC1,0x6C,0x16,0x01,0x60,0x16,0x01,0x60,0x16,0x01,0x7F,0xE7,0xFE,0x60,0x00, | |||
// '^' | |||
0x5E,0x1E,0x00,0x00,0x00,0x09, | |||
// '_' | |||
0x5F,0x20,0x10,0x04,0x00,0x10, | |||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |||
// '`' | |||
0x60,0x00,0x0A,0x09,0x00,0x0B, | |||
0x00,0x06,0x02,0x61,0x8C,0x60,0xBF,0x17,0xFC,0x3E,0x03,0x00, | |||
// 'a' | |||
0x61,0x08,0x19,0x17,0x00,0x19, | |||
0x00,0x0C,0x00,0x00,0x09,0x00,0x00,0x08,0x80,0x00,0x0C,0x20,0x00,0x04,0x08,0x00,0x06,0x04,0x00,0x02,0x01,0x00,0x03,0x00,0xC0,0x03,0x00,0x20,0x01,0x00,0x08,0x01,0x80,0x04,0x00,0x80,0x01,0x00,0xC0,0xC0,0x40,0xC0,0x60,0x20,0x40,0x00,0x08,0x60,0x00,0x06,0x40,0x00,0x00,0xF0,0x00,0x00,0xFE,0x1F,0xE1,0xCF,0xCF,0xF1,0x81,0xF4,0x1B,0x80,0x3E,0x0F,0x00,0x0E,0x06,0x00, | |||
// 'b' | |||
0x62,0x09,0x13,0x15,0x00,0x13, | |||
0x3F,0xFC,0x0C,0x00,0x61,0xC0,0x06,0x38,0x00,0x43,0x00,0x04,0x60,0x60,0x8C,0x0C,0x11,0x81,0x02,0x30,0x00,0x46,0x00,0x08,0xC0,0x00,0x98,0x18,0x13,0x03,0x02,0x60,0x60,0x4C,0x00,0x09,0x80,0x01,0x30,0x00,0x44,0x00,0x11,0x80,0x0E,0x3F,0xFF,0x07,0xFF,0x80, | |||
// 'c' | |||
0x63,0x08,0x16,0x17,0x00,0x16, | |||
0x00,0x7E,0x00,0x06,0x02,0x00,0x60,0x06,0x02,0x00,0x06,0x10,0x00,0x00,0xC0,0x00,0x22,0x00,0x01,0x18,0x0F,0x18,0x40,0x7E,0xC3,0x02,0x3E,0x0C,0x08,0xF0,0x30,0x21,0x80,0xC0,0x83,0x03,0x81,0x1A,0x0E,0x03,0xC4,0x18,0x00,0x08,0x70,0x00,0x19,0xE0,0x00,0x23,0xC0,0x01,0x87,0x80,0x1E,0x0F,0xC1,0xE0,0x0F,0xFE,0x00,0x0F,0xC0,0x00, | |||
// 'd' | |||
0x64,0x09,0x16,0x15,0xFF,0x15, | |||
0x1F,0xFE,0x00,0x80,0x06,0x07,0x00,0x06,0x1C,0x00,0x0C,0x30,0x00,0x10,0xC0,0x00,0x23,0x00,0x00,0x8C,0x0F,0x01,0x30,0x3E,0x04,0xC0,0xF8,0x13,0x03,0xE0,0x4C,0x0F,0x01,0x30,0x00,0x04,0xC0,0x00,0x23,0x00,0x00,0x8C,0x00,0x04,0x30,0x00,0x20,0x80,0x01,0x06,0x00,0x18,0x1F,0xFF,0x80,0x3F,0xF0,0x00, | |||
// 'e' | |||
0x65,0x08,0x11,0x17,0x00,0x11, | |||
0x00,0x01,0x0F,0xFF,0x48,0x00,0x2C,0x00,0x17,0x00,0x0B,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x1A,0x98,0x01,0x8C,0x00,0x86,0x00,0x43,0x00,0x31,0x80,0xD4,0xC0,0x02,0x60,0x01,0x30,0x00,0x98,0x00,0x4C,0x00,0x24,0x00,0x17,0xFF,0xEB,0xFF,0xF8,0x00,0x08, | |||
// 'f' | |||
0x66,0x08,0x11,0x16,0xFF,0x10, | |||
0x00,0x01,0x0F,0xFF,0x48,0x00,0x2E,0x00,0x17,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x3A,0x98,0x01,0x8C,0x00,0xC6,0x00,0x43,0x00,0x21,0x81,0xD0,0xC0,0xF0,0x60,0x70,0x30,0x20,0x18,0x10,0x08,0x02,0x0C,0x01,0x07,0xFF,0x03,0xFF,0x00, | |||
// 'g' | |||
0x67,0x08,0x16,0x17,0x00,0x16, | |||
0x00,0x7F,0x00,0x06,0x03,0x00,0x60,0x03,0x82,0x00,0x02,0x10,0x00,0x08,0xC0,0x00,0xC2,0x00,0x06,0x18,0x07,0x30,0x40,0x3C,0x83,0x01,0x3C,0x0C,0x04,0xFF,0xB0,0x14,0x02,0xC0,0x78,0x1B,0x80,0xE0,0x4E,0x01,0x81,0x18,0x00,0x04,0x70,0x00,0x11,0xE0,0x00,0x43,0xC0,0x01,0x07,0x80,0x18,0x0F,0x81,0xC0,0x1F,0xFC,0x00,0x0F,0xC0,0x00, | |||
// 'h' | |||
0x68,0x09,0x16,0x15,0x00,0x16, | |||
0x3F,0xC7,0xF9,0x00,0xE0,0x1E,0x03,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x80,0xF0,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x00,0x09,0x80,0x00,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x3C,0x09,0x80,0xF0,0x26,0x02,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x00,0x20,0x1F,0xFF,0xFF,0xFF,0xF7,0xFC,0x00,0x00,0x00, | |||
// 'i' | |||
0x69,0x09,0x0B,0x15,0x00,0x0B, | |||
0x3F,0xC8,0x07,0x81,0x70,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x4C,0x09,0x81,0x30,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x48,0x07,0x00,0xFF,0xEF,0xF8, | |||
// 'j' | |||
0x6A,0x09,0x0F,0x16,0x00,0x0F, | |||
0x03,0xFC,0x08,0x04,0x38,0x08,0x70,0x10,0x60,0x20,0xC0,0x41,0x80,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x08,0x30,0x10,0xA0,0x23,0x00,0x44,0x00,0x98,0x01,0x20,0x00,0xC0,0x09,0x00,0x27,0xC1,0x8F,0xFE,0x0F,0xF0,0x00, | |||
// 'k' | |||
0x6B,0x08,0x17,0x18,0x00,0x17, | |||
0x00,0x01,0x80,0x7F,0xEE,0x81,0x80,0x38,0x87,0x81,0xE0,0x8F,0x03,0x80,0x86,0x06,0x00,0x8C,0x08,0x07,0x18,0x00,0x1C,0x30,0x00,0x70,0x60,0x01,0x00,0xC0,0x02,0x01,0x80,0x02,0x03,0x00,0x04,0x06,0x04,0x04,0x0C,0x0C,0x04,0x18,0x18,0x06,0x30,0x38,0x00,0x60,0x78,0x09,0x00,0x30,0x26,0x00,0x71,0x8F,0xFF,0xE4,0x0F,0xFC,0xD0,0x00,0x01,0xC0,0x00,0x00,0x00, | |||
// 'l' | |||
0x6C,0x09,0x11,0x16,0x00,0x11, | |||
0x3F,0xF0,0x30,0x04,0x1C,0x06,0x0E,0x02,0x03,0x01,0x01,0x80,0x80,0xC0,0x40,0x60,0x20,0x30,0x10,0x18,0x08,0x0C,0x04,0xC6,0x03,0xD3,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x00,0xB0,0x00,0x58,0x00,0x2F,0xFF,0xD7,0xFF,0xF0,0x00,0x30, | |||
// 'm' | |||
0x6D,0x09,0x20,0x16,0x00,0x1F, | |||
0x00,0xF8,0x1F,0x00,0x01,0x84,0x30,0x80,0x01,0x82,0x61,0x00,0x01,0x82,0x40,0x00,0x01,0x81,0xC0,0x80,0x01,0x01,0x80,0x80,0x03,0x00,0x80,0x40,0x02,0x00,0x00,0x40,0x06,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x04,0x00,0x00,0x10,0x0C,0x00,0x00,0x10,0x08,0x00,0x00,0x10,0x18,0x08,0x08,0x08,0x10,0x18,0x18,0x08,0x30,0x1C,0x1C,0x04,0x60,0x3C,0x3C,0x02,0x70,0x2E,0x2E,0x06,0x7C,0x27,0x4E,0x3C,0x3F,0x27,0xC6,0xF0,0x07,0xC3,0x8F,0x80,0x01,0x83,0x06,0x00, | |||
// 'n' | |||
0x6E,0x09,0x17,0x15,0x00,0x17, | |||
0x3F,0x07,0xFC,0xC1,0x10,0x07,0xC1,0x78,0x1F,0x81,0xF0,0x23,0x01,0xE0,0x46,0x01,0xC0,0x8C,0x01,0x81,0x18,0x01,0x02,0x30,0x00,0x04,0x60,0x00,0x08,0xC0,0x00,0x11,0x80,0x00,0x23,0x02,0x00,0x46,0x06,0x00,0x8C,0x0E,0x01,0x18,0x1E,0x02,0x30,0x3F,0x04,0x60,0x3F,0x0B,0x00,0x2F,0x17,0xFF,0x8F,0xEF,0xFE,0x0F,0x80, | |||
// 'o' | |||
0x6F,0x08,0x17,0x17,0x00,0x17, | |||
0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00, | |||
// 'p' | |||
0x70,0x09,0x13,0x15,0x00,0x13, | |||
0x3F,0xFC,0x08,0x00,0x43,0x80,0x06,0x70,0x00,0x46,0x00,0x04,0xC0,0x00,0x18,0x00,0x0B,0x01,0x81,0x60,0x30,0x2C,0x04,0x05,0x80,0x00,0x30,0x00,0x26,0x00,0x08,0xC0,0x02,0x18,0x01,0x83,0x01,0xE0,0x60,0x30,0x08,0x04,0x03,0x00,0x80,0x7F,0xE0,0x07,0xF8,0x00, | |||
// 'q' | |||
0x71,0x09,0x17,0x1C,0x00,0x17, | |||
0x00,0x7E,0x00,0x03,0x01,0x00,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x01,0x08,0x00,0x01,0x30,0x1E,0x02,0x40,0x7E,0x03,0x81,0x1E,0x07,0x02,0x1C,0x0E,0x04,0x18,0x1C,0x04,0x20,0x3C,0x07,0x80,0x78,0x00,0x01,0x30,0x00,0x02,0x70,0x00,0x04,0xF0,0x00,0x10,0xF0,0x00,0x70,0xF0,0x00,0x00,0xFC,0x00,0x40,0xFF,0x81,0x00,0x3F,0x84,0x00,0x0F,0x10,0x00,0x06,0x40,0x00,0x0F,0x00,0x00,0x1C,0x00,0x00,0x00,0x00, | |||
// 'r' | |||
0x72,0x09,0x18,0x17,0x00,0x18, | |||
0x3F,0xFE,0x00,0x60,0x01,0x80,0x70,0x00,0x40,0x70,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x10,0x10,0x30,0x18,0x10,0x30,0x10,0x10,0x30,0x00,0x10,0x30,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x1E,0x30,0x00,0x0A,0x30,0x18,0x06,0x30,0x1C,0x0C,0x20,0x0E,0x18,0x7F,0xFF,0x30,0x7F,0xE7,0x20,0x00,0x03,0xC0,0x00,0x03,0x80,0x00,0x03,0x00, | |||
// 's' | |||
0x73,0x07,0x11,0x19,0x00,0x11, | |||
0x00,0x06,0x00,0x02,0x80,0x1E,0x40,0x10,0x20,0x30,0x10,0x30,0x08,0x10,0x04,0x18,0x01,0x08,0x00,0x8C,0x0F,0x46,0x07,0xA3,0x01,0xE1,0xC0,0xE1,0xE0,0x41,0x70,0x21,0x80,0x10,0xC0,0x08,0x70,0x08,0x38,0x04,0x0C,0x04,0x06,0x04,0x03,0x3C,0x01,0xFC,0x00,0xF0,0x00,0x30,0x00,0x00, | |||
// 't' | |||
0x74,0x08,0x12,0x16,0x01,0x13, | |||
0x30,0x01,0x93,0xFF,0xDC,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0x00,0x01,0xCC,0x07,0x7F,0x01,0xFF,0xC0,0x79,0xB0,0x1C,0x0C,0x04,0x03,0x01,0x00,0xC0,0x40,0x30,0x10,0x0C,0x04,0x03,0x01,0x00,0xC0,0x60,0x60,0x08,0x1F,0xFC,0x07,0xFE,0x00, | |||
// 'u' | |||
0x75,0x09,0x19,0x16,0xFF,0x18, | |||
0x1F,0xF1,0xFF,0x10,0x05,0x00,0x9C,0x07,0xC0,0xCE,0x02,0xE0,0x43,0x01,0x30,0x21,0x80,0x98,0x10,0xC0,0x4C,0x08,0x60,0x26,0x04,0x30,0x13,0x02,0x18,0x09,0x81,0x0C,0x04,0xC0,0x86,0x02,0x60,0x43,0x01,0x30,0x21,0xC0,0x70,0x10,0xE0,0x00,0x10,0x30,0x00,0x08,0x1C,0x00,0x08,0x0F,0x00,0x08,0x03,0xC0,0x08,0x00,0xFC,0x18,0x00,0x1F,0xF8,0x00,0x03,0xF0,0x00, | |||
// 'v' | |||
0x76,0x07,0x19,0x18,0x00,0x19, | |||
0x00,0xC1,0xC0,0x01,0x91,0x98,0x03,0x18,0xC3,0x06,0x04,0x60,0x64,0x02,0x20,0x1F,0x00,0xB0,0x1B,0xC0,0x70,0x0C,0xF0,0x10,0x08,0x38,0x00,0x08,0x0E,0x00,0x04,0x07,0x00,0x04,0x01,0xC0,0x06,0x00,0xE0,0x02,0x00,0x38,0x02,0x00,0x1C,0x01,0x00,0x07,0x01,0x00,0x03,0x81,0x80,0x00,0xE0,0x80,0x00,0x30,0x80,0x00,0x1C,0x40,0x00,0x06,0x40,0x00,0x03,0xE0,0x00,0x00,0xE0,0x00,0x00,0x60,0x00, | |||
// 'w' | |||
0x77,0x07,0x20,0x18,0x00,0x20, | |||
0x00,0x60,0x03,0x80,0x01,0x90,0x06,0x60,0x0E,0x10,0xC6,0x1C,0x38,0x11,0xA6,0x07,0x60,0x11,0x26,0x01,0x78,0x12,0x1C,0x06,0x78,0x0E,0x1C,0x04,0x3C,0x0C,0x08,0x08,0x1C,0x00,0x08,0x10,0x0C,0x00,0x00,0x10,0x0E,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x07,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x80,0x00,0x80,0x03,0x80,0x00,0x80,0x01,0xC0,0x81,0x00,0x01,0xC1,0xC0,0x00,0x00,0xE1,0xC2,0x00,0x00,0xE2,0xE4,0x00,0x00,0x74,0xE4,0x00,0x00,0x7C,0x78,0x00,0x00,0x38,0x78,0x00,0x00,0x30,0x30,0x00, | |||
// 'x' | |||
0x78,0x05,0x19,0x1D,0x00,0x18, | |||
0x00,0x01,0x80,0x00,0x01,0x20,0x00,0x61,0x8C,0x00,0x48,0xC3,0x00,0x44,0x40,0x60,0x43,0x60,0x10,0xC0,0xE0,0x18,0xC0,0x30,0x1C,0xC0,0x00,0x18,0x7C,0x00,0x08,0x1F,0x00,0x08,0x03,0x80,0x08,0x00,0xE0,0x04,0x00,0x38,0x02,0x00,0x0C,0x01,0x80,0x04,0x00,0x60,0x06,0x00,0x10,0x02,0x00,0x07,0x03,0x00,0x00,0x41,0x01,0x00,0x63,0x00,0xC0,0x62,0x00,0xF0,0x63,0x80,0x7C,0x61,0xF0,0x4E,0x60,0x7C,0x63,0xE0,0x0F,0x91,0xE0,0x01,0xF0,0x40,0x00,0x70,0x00,0x00,0x00,0x00,0x00, | |||
// 'y' | |||
0x79,0x06,0x19,0x1B,0x00,0x19, | |||
0x00,0x00,0x60,0x00,0xC0,0x48,0x00,0x80,0x62,0x00,0x88,0x20,0xC0,0x82,0x30,0x11,0x80,0xB0,0x01,0x80,0x30,0x0D,0x80,0x00,0x0C,0xF8,0x00,0x08,0x3E,0x00,0x04,0x07,0x80,0x04,0x01,0xE0,0x04,0x00,0x78,0x04,0x00,0x1C,0x02,0x00,0x0C,0x02,0x00,0x04,0x02,0x00,0x04,0x02,0x00,0x06,0x01,0x00,0x0E,0x01,0x00,0x0C,0x01,0x00,0x07,0x01,0x00,0x03,0xC0,0x80,0x01,0xF0,0x80,0x00,0x3E,0x00,0x00,0x0F,0xC0,0x00,0x03,0xE0,0x00,0x00,0x40,0x00,0x00, | |||
// 'z' | |||
0x7A,0x08,0x13,0x17,0xFF,0x12, | |||
0x0C,0x00,0x02,0xFF,0xFC,0xC0,0x01,0x98,0x00,0x23,0x00,0x04,0x60,0x01,0x0C,0x00,0x21,0x80,0x08,0x37,0x01,0x07,0xE0,0x60,0xF8,0x08,0x03,0x01,0x00,0x40,0x70,0x18,0x0D,0x02,0x00,0x20,0xC0,0x04,0x18,0x00,0x86,0x00,0x10,0xC0,0x02,0x10,0x00,0x47,0xFF,0xE8,0xFF,0xFE,0x00,0x01,0x80, | |||
// '{' | |||
0x7B,0x05,0x0D,0x1F,0x01,0x0E, | |||
0x00,0x10,0x0E,0x81,0x04,0x10,0x21,0x81,0x08,0x08,0xC0,0x46,0x0E,0x30,0x71,0x83,0x0C,0x10,0x60,0x84,0x04,0x60,0x23,0x02,0x18,0x08,0xC0,0x47,0x82,0x3C,0x10,0x60,0x83,0x05,0x18,0x38,0xC0,0x46,0x02,0x38,0x11,0xC0,0x87,0x84,0x1F,0xE0,0x7F,0x00,0x30,0x00,0x00, | |||
// '|' | |||
0x7C,0x1E,0x00,0x00,0x00,0x09, | |||
// '}' | |||
0x7D,0x04,0x0E,0x1F,0x00,0x0F, | |||
0x30,0x00,0xBC,0x06,0x0C,0x18,0x08,0x60,0x21,0x80,0x46,0x01,0x1F,0x04,0x7C,0x11,0xB0,0x44,0xC1,0x03,0x04,0x0C,0x0C,0x30,0x10,0xE0,0x43,0x81,0x04,0x04,0x30,0x70,0xC1,0x03,0x04,0x0C,0x10,0xF0,0x46,0xC1,0x18,0x04,0x60,0x11,0x80,0x86,0x04,0x18,0x60,0x7F,0x81,0xF8,0x06,0x00,0x00, | |||
// '~' | |||
0x7E,0x1E,0x00,0x00,0x00,0x09, | |||
// Terminator | |||
0xFF | |||
}; |
@@ -0,0 +1,24 @@ | |||
language: cpp | |||
addons: | |||
apt: | |||
sources: | |||
- ubuntu-toolchain-r-test | |||
packages: | |||
- g++-4.8 | |||
script: | |||
- export CXX="g++-4.8" CC="gcc-4.8" | |||
- make dist | |||
notifications: | |||
email: | |||
recipients: | |||
- ivan@esp8266.com | |||
on_success: change | |||
on_failure: always | |||
deploy: | |||
provider: releases | |||
api_key: | |||
secure: k/DDoCfLXIct8TcGjekKm5CAoTL6Wy6LXhI74Ssgc8VSbjJQ1crO2J4V5HnQw7QplgAXwqjkoAUkzEJiz34wqBaAv0w9o8+5jPwCP6rMQ/VEvbn1MPI52KIFbYqKxmXe5J24B00BttbGc773ldXPnvmv+qPWWcXDTpcosni2laBt3z8bxCGVwkQ7nuQUaAelzs21wQuhjmKQ6F1dgNN5XhdJ5qgFYYM8cwiigvcqIaCwLMcrOs7gj22TS242pzp38etWhfxUbqUKejWgOH4qYeU3Tf5gsu4WV0otbqXYMvV18gVSoAiMnodDfYJUNHlfelCAEqebvECrIvvE8D0syuVYz6fh2/BrxZ6HeiYj1CXILghOjPUZZZ7/chKglWnA1vL+6Uxn5LtyTJ5gbgMYXvKQUXrRVZ3Zd9xrmv/YaMnGq+6BDBkS30aXpSK2X0HvW9/6JyQ9L1sjnKdOzDmagvikHm2rrPXBRMMfYTt4nucgUBWqJqyFe0uGva/n8TG5RzOzdOgRxFx/lF8XudtR4Z4gUBUFpve/meVHVVK82+cxzfN97aFdxvBzyGq18EDjOEpDi7k7mZjXUycvD8UZ7o12sxJ0Zr6/9esiFUsaqyE+2Vi6bMbgEHGx7hfWbdfREnpOtjFLH+u5mAPIqOh89a/UJ6SbYm0cON+ewTMUkJ8= | |||
file: mkspiffs-$TRAVIS_TAG-linux64.tar.gz | |||
on: | |||
repo: igrr/mkspiffs | |||
tags: true |
@@ -0,0 +1,14 @@ | |||
MKSPIFFS_COMPONENT_PATH := $(COMPONENT_PATH) | |||
MKSPIFFS_BUILD_DIR=$(abspath $(MKSPIFFS_COMPONENT_PATH)/mkspiffs) | |||
# Custom recursive make for mkspiffs sub-project | |||
MKSPIFFS_MAKE=+$(MAKE) -C $(MKSPIFFS_COMPONENT_PATH)/src | |||
.PHONY: mkspiffs clean | |||
mkspiffs: $(SDKCONFIG_MAKEFILE) | |||
$(MKSPIFFS_MAKE) all | |||
clean: $(SDKCONFIG_MAKEFILE) | |||
$(MKSPIFFS_MAKE) clean | |||
@@ -0,0 +1,6 @@ | |||
# | |||
# Component Makefile | |||
# | |||
COMPONENT_SRCDIRS := | |||
COMPONENT_ADD_INCLUDEDIRS := |
@@ -0,0 +1,93 @@ | |||
CFLAGS ?= -std=gnu99 -Os -Wall | |||
CXXFLAGS ?= -std=gnu++11 -Os -Wall | |||
ifeq ($(OS),Windows_NT) | |||
TARGET_OS := WINDOWS | |||
DIST_SUFFIX := windows | |||
ARCHIVE_CMD := 7z a | |||
ARCHIVE_EXTENSION := zip | |||
TARGET := mkspiffs.exe | |||
TARGET_CFLAGS := -mno-ms-bitfields | |||
TARGET_LDFLAGS := -Wl,-static -static-libgcc | |||
else | |||
UNAME_S := $(shell uname -s) | |||
ifeq ($(UNAME_S),Linux) | |||
TARGET_OS := LINUX | |||
UNAME_P := $(shell uname -p) | |||
ifeq ($(UNAME_P),x86_64) | |||
DIST_SUFFIX := linux64 | |||
endif | |||
ifneq ($(filter %86,$(UNAME_P)),) | |||
DIST_SUFFIX := linux32 | |||
endif | |||
endif | |||
ifeq ($(UNAME_S),Darwin) | |||
TARGET_OS := OSX | |||
DIST_SUFFIX := osx | |||
CC=clang | |||
CXX=clang++ | |||
TARGET_CFLAGS = -mmacosx-version-min=10.7 -arch i386 -arch x86_64 | |||
TARGET_CXXFLAGS = -mmacosx-version-min=10.7 -arch i386 -arch x86_64 -stdlib=libc++ | |||
TARGET_LDFLAGS = -arch i386 -arch x86_64 -stdlib=libc++ | |||
endif | |||
ARCHIVE_CMD := tar czf | |||
ARCHIVE_EXTENSION := tar.gz | |||
TARGET := mkspiffs | |||
endif | |||
VERSION ?= $(shell git describe --always) | |||
OBJ := main.o \ | |||
spiffs/spiffs_cache.o \ | |||
spiffs/spiffs_check.o \ | |||
spiffs/spiffs_gc.o \ | |||
spiffs/spiffs_hydrogen.o \ | |||
spiffs/spiffs_nucleus.o \ | |||
INCLUDES := -Itclap -Ispiffs -I. | |||
CFLAGS += $(TARGET_CFLAGS) | |||
CXXFLAGS += $(TARGET_CXXFLAGS) | |||
LDFLAGS += $(TARGET_LDFLAGS) | |||
CPPFLAGS += $(INCLUDES) -D$(TARGET_OS) -DVERSION=\"$(VERSION)\" -D__NO_INLINE__ | |||
DIST_NAME := mkspiffs-$(VERSION)-$(DIST_SUFFIX) | |||
DIST_DIR := $(DIST_NAME) | |||
DIST_ARCHIVE := $(DIST_NAME).$(ARCHIVE_EXTENSION) | |||
.PHONY: all clean dist | |||
all: $(TARGET) | |||
dist: test $(DIST_ARCHIVE) | |||
$(DIST_ARCHIVE): $(TARGET) $(DIST_DIR) | |||
cp $(TARGET) $(DIST_DIR)/ | |||
$(ARCHIVE_CMD) $(DIST_ARCHIVE) $(DIST_DIR) | |||
$(TARGET): $(OBJ) | |||
$(CXX) $^ -o $@ $(LDFLAGS) | |||
strip $(TARGET) | |||
$(DIST_DIR): | |||
@mkdir -p $@ | |||
clean: | |||
@rm -f *.o | |||
@rm -f spiffs/*.o | |||
@rm -f $(TARGET) | |||
SPIFFS_TEST_FS_CONFIG := -s 0x100000 -p 512 -b 0x2000 | |||
test: $(TARGET) | |||
ls -1 spiffs > out.list0 | |||
./mkspiffs -c spiffs $(SPIFFS_TEST_FS_CONFIG) out.spiffs | sort | sed s/^\\/// > out.list1 | |||
./mkspiffs -u spiffs_u $(SPIFFS_TEST_FS_CONFIG) out.spiffs | sort | sed s/^\\/// > out.list_u | |||
./mkspiffs -l $(SPIFFS_TEST_FS_CONFIG) out.spiffs | cut -f 2 | sort | sed s/^\\/// > out.list2 | |||
diff --strip-trailing-cr out.list0 out.list1 | |||
diff --strip-trailing-cr out.list0 out.list2 | |||
diff spiffs spiffs_u | |||
rm -f out.{list0,list1,list2,list_u,spiffs} | |||
rm -R spiffs_u |
@@ -0,0 +1,80 @@ | |||
# mkspiffs | |||
Tool to build and unpack [SPIFFS](https://github.com/pellepl/spiffs) images. | |||
## Usage | |||
``` | |||
mkspiffs {-c <pack_dir>|-u <dest_dir>|-l|-i} [-d <0-5>] [-b <number>] | |||
[-p <number>] [-s <number>] [--] [--version] [-h] | |||
<image_file> | |||
Where: | |||
-c <pack_dir>, --create <pack_dir> | |||
(OR required) create spiffs image from a directory | |||
-- OR -- | |||
-u <dest_dir>, --unpack <dest_dir> | |||
(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 <number>, --block <number> | |||
fs block size, in bytes | |||
-p <number>, --page <number> | |||
fs page size, in bytes | |||
-s <number>, --size <number> | |||
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. | |||
<image_file> | |||
(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 |
@@ -0,0 +1,28 @@ | |||
version: 0.0.{build} | |||
platform: | |||
- x86 | |||
skip_commits: | |||
message: /\[ci skip\]/ | |||
matrix: | |||
fast_finish: true | |||
build_script: | |||
- SET PATH=C:\MinGW\bin;C:\MinGW\msys\1.0\bin;%PATH% | |||
- make dist | |||
artifacts: | |||
- path: '*.zip' | |||
deploy: | |||
release: $(PRODUCT_VERSION) | |||
provider: GitHub | |||
auth_token: | |||
secure: 'PGg5fnoBpP1Omzr6f3KIYDiD8J30rretQjSl/MITRpzvSCmN88kM6VDMz1TBGZTA' | |||
artifact: /.*\.zip/ | |||
draft: true | |||
prerelease: false | |||
on: | |||
appveyor_repo_tag: true |
@@ -0,0 +1,646 @@ | |||
// | |||
// main.cpp | |||
// make_spiffs | |||
// | |||
// Created by Ivan Grokhotkov on 13/05/15. | |||
// Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. | |||
// | |||
#define TCLAP_SETBASE_ZERO 1 | |||
#define VERSION "0.3.6" | |||
#include <iostream> | |||
#include "spiffs/spiffs.h" | |||
#include <vector> | |||
#include <dirent.h> | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <unistd.h> | |||
#include <cstring> | |||
#include <string> | |||
#include <time.h> | |||
#include <memory> | |||
#include <cstdlib> | |||
#include "tclap/CmdLine.h" | |||
#include "tclap/UnlabeledValueArg.h" | |||
static std::vector<uint8_t> 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<uint8_t> s_spiffsWorkBuf; | |||
static std::vector<uint8_t> s_spiffsFds; | |||
static std::vector<uint8_t> 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<std::string> packArg( "c", "create", "create spiffs image from a directory", true, "", "pack_dir"); | |||
TCLAP::ValueArg<std::string> 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<std::string> outNameArg( "image_file", "spiffs image file", true, "", "image_file" ); | |||
TCLAP::ValueArg<int> imageSizeArg( "s", "size", "fs image size, in bytes", false, 0x10000, "number" ); | |||
TCLAP::ValueArg<int> pageSizeArg( "p", "page", "fs page size, in bytes", false, 256, "number" ); | |||
TCLAP::ValueArg<int> blockSizeArg( "b", "block", "fs block size, in bytes", false, 4096, "number" ); | |||
TCLAP::ValueArg<int> 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<TCLAP::Arg*> 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; | |||
} |
@@ -0,0 +1,138 @@ | |||
/* | |||
* Lua RTOS, SPIFFS low access | |||
* | |||
* Copyright (C) 2015 - 2017 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* | |||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission to use, copy, modify, and distribute this software | |||
* and its documentation for any purpose and without fee is hereby | |||
* granted, provided that the above copyright notice appear in all | |||
* copies and that both that the copyright notice and this | |||
* permission notice and warranty disclaimer appear in supporting | |||
* documentation, and that the name of the author not be used in | |||
* advertising or publicity pertaining to distribution of the | |||
* software without specific, written prior permission. | |||
* | |||
* The author disclaim all warranties with regard to this | |||
* software, including all implied warranties of merchantability | |||
* and fitness. In no event shall the author be liable for any | |||
* special, indirect or consequential damages or any damages | |||
* whatsoever resulting from loss of use, data or profits, whether | |||
* in an action of contract, negligence or other tortious action, | |||
* arising out of or in connection with the use or performance of | |||
* this software. | |||
*/ | |||
#include <stdlib.h> | |||
#include "esp_spiffs.h" | |||
#include "esp_attr.h" | |||
#include "spiffs.h" | |||
#include <esp_spi_flash.h> | |||
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; | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* Lua RTOS, write syscall implementation | |||
* | |||
* Copyright (C) 2015 - 2017 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* | |||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission to use, copy, modify, and distribute this software | |||
* and its documentation for any purpose and without fee is hereby | |||
* granted, provided that the above copyright notice appear in all | |||
* copies and that both that the copyright notice and this | |||
* permission notice and warranty disclaimer appear in supporting | |||
* documentation, and that the name of the author not be used in | |||
* advertising or publicity pertaining to distribution of the | |||
* software without specific, written prior permission. | |||
* | |||
* The author disclaim all warranties with regard to this | |||
* software, including all implied warranties of merchantability | |||
* and fitness. In no event shall the author be liable for any | |||
* special, indirect or consequential damages or any damages | |||
* whatsoever resulting from loss of use, data or profits, whether | |||
* in an action of contract, negligence or other tortious action, | |||
* arising out of or in connection with the use or performance of | |||
* this software. | |||
*/ | |||
#ifndef __ESP_SPIFFS_H__ | |||
#define __ESP_SPIFFS_H__ | |||
#include "spiffs.h" | |||
s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst); | |||
s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src); | |||
s32_t esp32_spi_flash_erase(u32_t addr, u32_t size); | |||
#define low_spiffs_read (spiffs_read *)esp32_spi_flash_read | |||
#define low_spiffs_write (spiffs_write *)esp32_spi_flash_write | |||
#define low_spiffs_erase (spiffs_erase *)esp32_spi_flash_erase | |||
#endif // __ESP_SPIFFS_H__ |
@@ -0,0 +1,813 @@ | |||
/* | |||
* spiffs.h | |||
* | |||
* Created on: May 26, 2013 | |||
* Author: petera | |||
*/ | |||
#ifndef SPIFFS_H_ | |||
#define SPIFFS_H_ | |||
#if defined(__cplusplus) | |||
extern "C" { | |||
#endif | |||
#include "spiffs_config.h" | |||
#define SPIFFS_OK 0 | |||
#define SPIFFS_ERR_NOT_MOUNTED -10000 | |||
#define SPIFFS_ERR_FULL -10001 | |||
#define SPIFFS_ERR_NOT_FOUND -10002 | |||
#define SPIFFS_ERR_END_OF_OBJECT -10003 | |||
#define SPIFFS_ERR_DELETED -10004 | |||
#define SPIFFS_ERR_NOT_FINALIZED -10005 | |||
#define SPIFFS_ERR_NOT_INDEX -10006 | |||
#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007 | |||
#define SPIFFS_ERR_FILE_CLOSED -10008 | |||
#define SPIFFS_ERR_FILE_DELETED -10009 | |||
#define SPIFFS_ERR_BAD_DESCRIPTOR -10010 | |||
#define SPIFFS_ERR_IS_INDEX -10011 | |||
#define SPIFFS_ERR_IS_FREE -10012 | |||
#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013 | |||
#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014 | |||
#define SPIFFS_ERR_INDEX_REF_FREE -10015 | |||
#define SPIFFS_ERR_INDEX_REF_LU -10016 | |||
#define SPIFFS_ERR_INDEX_REF_INVALID -10017 | |||
#define SPIFFS_ERR_INDEX_FREE -10018 | |||
#define SPIFFS_ERR_INDEX_LU -10019 | |||
#define SPIFFS_ERR_INDEX_INVALID -10020 | |||
#define SPIFFS_ERR_NOT_WRITABLE -10021 | |||
#define SPIFFS_ERR_NOT_READABLE -10022 | |||
#define SPIFFS_ERR_CONFLICTING_NAME -10023 | |||
#define SPIFFS_ERR_NOT_CONFIGURED -10024 | |||
#define SPIFFS_ERR_NOT_A_FS -10025 | |||
#define SPIFFS_ERR_MOUNTED -10026 | |||
#define SPIFFS_ERR_ERASE_FAIL -10027 | |||
#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028 | |||
#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 | |||
#define SPIFFS_ERR_FILE_EXISTS -10030 | |||
#define SPIFFS_ERR_NOT_A_FILE -10031 | |||
#define SPIFFS_ERR_RO_NOT_IMPL -10032 | |||
#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033 | |||
#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034 | |||
#define SPIFFS_ERR_PROBE_NOT_A_FS -10035 | |||
#define SPIFFS_ERR_NAME_TOO_LONG -10036 | |||
#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037 | |||
#define SPIFFS_ERR_IX_MAP_MAPPED -10038 | |||
#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039 | |||
#define SPIFFS_ERR_INTERNAL -10050 | |||
#define SPIFFS_ERR_TEST -10100 | |||
// spiffs file descriptor index type. must be signed | |||
typedef s16_t spiffs_file; | |||
// spiffs file descriptor flags | |||
typedef u16_t spiffs_flags; | |||
// spiffs file mode | |||
typedef u16_t spiffs_mode; | |||
// object type | |||
typedef u8_t spiffs_obj_type; | |||
struct spiffs_t; | |||
#if SPIFFS_HAL_CALLBACK_EXTRA | |||
/* spi read call function type */ | |||
typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); | |||
/* spi write call function type */ | |||
typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); | |||
/* spi erase call function type */ | |||
typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); | |||
#else // SPIFFS_HAL_CALLBACK_EXTRA | |||
/* spi read call function type */ | |||
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); | |||
/* spi write call function type */ | |||
typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); | |||
/* spi erase call function type */ | |||
typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); | |||
#endif // SPIFFS_HAL_CALLBACK_EXTRA | |||
/* file system check callback report operation */ | |||
typedef enum { | |||
SPIFFS_CHECK_LOOKUP = 0, | |||
SPIFFS_CHECK_INDEX, | |||
SPIFFS_CHECK_PAGE | |||
} spiffs_check_type; | |||
/* file system check callback report type */ | |||
typedef enum { | |||
SPIFFS_CHECK_PROGRESS = 0, | |||
SPIFFS_CHECK_ERROR, | |||
SPIFFS_CHECK_FIX_INDEX, | |||
SPIFFS_CHECK_FIX_LOOKUP, | |||
SPIFFS_CHECK_DELETE_ORPHANED_INDEX, | |||
SPIFFS_CHECK_DELETE_PAGE, | |||
SPIFFS_CHECK_DELETE_BAD_FILE | |||
} spiffs_check_report; | |||
/* file system check callback function */ | |||
#if SPIFFS_HAL_CALLBACK_EXTRA | |||
typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, | |||
u32_t arg1, u32_t arg2); | |||
#else // SPIFFS_HAL_CALLBACK_EXTRA | |||
typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, | |||
u32_t arg1, u32_t arg2); | |||
#endif // SPIFFS_HAL_CALLBACK_EXTRA | |||
/* file system listener callback operation */ | |||
typedef enum { | |||
/* the file has been created */ | |||
SPIFFS_CB_CREATED = 0, | |||
/* the file has been updated or moved to another page */ | |||
SPIFFS_CB_UPDATED, | |||
/* the file has been deleted */ | |||
SPIFFS_CB_DELETED | |||
} spiffs_fileop_type; | |||
/* file system listener callback function */ | |||
typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix); | |||
#ifndef SPIFFS_DBG | |||
#define SPIFFS_DBG(...) \ | |||
printf(__VA_ARGS__) | |||
#endif | |||
#ifndef SPIFFS_GC_DBG | |||
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) | |||
#endif | |||
#ifndef SPIFFS_CACHE_DBG | |||
#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) | |||
#endif | |||
#ifndef SPIFFS_CHECK_DBG | |||
#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) | |||
#endif | |||
/* Any write to the filehandle is appended to end of the file */ | |||
#define SPIFFS_APPEND (1<<0) | |||
#define SPIFFS_O_APPEND SPIFFS_APPEND | |||
/* If the opened file exists, it will be truncated to zero length before opened */ | |||
#define SPIFFS_TRUNC (1<<1) | |||
#define SPIFFS_O_TRUNC SPIFFS_TRUNC | |||
/* If the opened file does not exist, it will be created before opened */ | |||
#define SPIFFS_CREAT (1<<2) | |||
#define SPIFFS_O_CREAT SPIFFS_CREAT | |||
/* The opened file may only be read */ | |||
#define SPIFFS_RDONLY (1<<3) | |||
#define SPIFFS_O_RDONLY SPIFFS_RDONLY | |||
/* The opened file may only be written */ | |||
#define SPIFFS_WRONLY (1<<4) | |||
#define SPIFFS_O_WRONLY SPIFFS_WRONLY | |||
/* The opened file may be both read and written */ | |||
#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) | |||
#define SPIFFS_O_RDWR SPIFFS_RDWR | |||
/* Any writes to the filehandle will never be cached but flushed directly */ | |||
#define SPIFFS_DIRECT (1<<5) | |||
#define SPIFFS_O_DIRECT SPIFFS_DIRECT | |||
/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */ | |||
#define SPIFFS_EXCL (1<<6) | |||
#define SPIFFS_O_EXCL SPIFFS_EXCL | |||
#define SPIFFS_SEEK_SET (0) | |||
#define SPIFFS_SEEK_CUR (1) | |||
#define SPIFFS_SEEK_END (2) | |||
#define SPIFFS_TYPE_FILE (1) | |||
#define SPIFFS_TYPE_DIR (2) | |||
#define SPIFFS_TYPE_HARD_LINK (3) | |||
#define SPIFFS_TYPE_SOFT_LINK (4) | |||
#ifndef SPIFFS_LOCK | |||
#define SPIFFS_LOCK(fs) | |||
#endif | |||
#ifndef SPIFFS_UNLOCK | |||
#define SPIFFS_UNLOCK(fs) | |||
#endif | |||
// phys structs | |||
// spiffs spi configuration struct | |||
typedef struct { | |||
// physical read function | |||
spiffs_read hal_read_f; | |||
// physical write function | |||
spiffs_write hal_write_f; | |||
// physical erase function | |||
spiffs_erase hal_erase_f; | |||
#if SPIFFS_SINGLETON == 0 | |||
// physical size of the spi flash | |||
u32_t phys_size; | |||
// physical offset in spi flash used for spiffs, | |||
// must be on block boundary | |||
u32_t phys_addr; | |||
// physical size when erasing a block | |||
u32_t phys_erase_block; | |||
// logical size of a block, must be on physical | |||
// block size boundary and must never be less than | |||
// a physical block | |||
u32_t log_block_size; | |||
// logical size of a page, must be at least | |||
// log_block_size / 8 | |||
u32_t log_page_size; | |||
#endif | |||
#if SPIFFS_FILEHDL_OFFSET | |||
// an integer offset added to each file handle | |||
u16_t fh_ix_offset; | |||
#endif | |||
} spiffs_config; | |||
typedef struct spiffs_t { | |||
// file system configuration | |||
spiffs_config cfg; | |||
// number of logical blocks | |||
u32_t block_count; | |||
// cursor for free blocks, block index | |||
spiffs_block_ix free_cursor_block_ix; | |||
// cursor for free blocks, entry index | |||
int free_cursor_obj_lu_entry; | |||
// cursor when searching, block index | |||
spiffs_block_ix cursor_block_ix; | |||
// cursor when searching, entry index | |||
int cursor_obj_lu_entry; | |||
// primary work buffer, size of a logical page | |||
u8_t *lu_work; | |||
// secondary work buffer, size of a logical page | |||
u8_t *work; | |||
// file descriptor memory area | |||
u8_t *fd_space; | |||
// available file descriptors | |||
u32_t fd_count; | |||
// last error | |||
s32_t err_code; | |||
// current number of free blocks | |||
u32_t free_blocks; | |||
// current number of busy pages | |||
u32_t stats_p_allocated; | |||
// current number of deleted pages | |||
u32_t stats_p_deleted; | |||
// flag indicating that garbage collector is cleaning | |||
u8_t cleaning; | |||
// max erase count amongst all blocks | |||
spiffs_obj_id max_erase_count; | |||
#if SPIFFS_GC_STATS | |||
u32_t stats_gc_runs; | |||
#endif | |||
#if SPIFFS_CACHE | |||
// cache memory | |||
void *cache; | |||
// cache size | |||
u32_t cache_size; | |||
#if SPIFFS_CACHE_STATS | |||
u32_t cache_hits; | |||
u32_t cache_misses; | |||
#endif | |||
#endif | |||
// check callback function | |||
spiffs_check_callback check_cb_f; | |||
// file callback function | |||
spiffs_file_callback file_cb_f; | |||
// mounted flag | |||
u8_t mounted; | |||
// user data | |||
void *user_data; | |||
// config magic | |||
u32_t config_magic; | |||
} spiffs; | |||
/* spiffs file status struct */ | |||
typedef struct { | |||
spiffs_obj_id obj_id; | |||
u32_t size; | |||
spiffs_obj_type type; | |||
spiffs_page_ix pix; | |||
u8_t name[SPIFFS_OBJ_NAME_LEN]; | |||
#if SPIFFS_OBJ_META_LEN | |||
u8_t meta[SPIFFS_OBJ_META_LEN]; | |||
#endif | |||
} spiffs_stat; | |||
struct spiffs_dirent { | |||
spiffs_obj_id obj_id; | |||
u8_t name[SPIFFS_OBJ_NAME_LEN]; | |||
spiffs_obj_type type; | |||
u32_t size; | |||
spiffs_page_ix pix; | |||
#if SPIFFS_OBJ_META_LEN | |||
u8_t meta[SPIFFS_OBJ_META_LEN]; | |||
#endif | |||
}; | |||
typedef struct { | |||
spiffs *fs; | |||
spiffs_block_ix block; | |||
int entry; | |||
} spiffs_DIR; | |||
#if SPIFFS_IX_MAP | |||
typedef struct { | |||
// buffer with looked up data pixes | |||
spiffs_page_ix *map_buf; | |||
// precise file byte offset | |||
u32_t offset; | |||
// start data span index of lookup buffer | |||
spiffs_span_ix start_spix; | |||
// end data span index of lookup buffer | |||
spiffs_span_ix end_spix; | |||
} spiffs_ix_map; | |||
#endif | |||
// functions | |||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 | |||
/** | |||
* Special function. This takes a spiffs config struct and returns the number | |||
* of blocks this file system was formatted with. This function relies on | |||
* that following info is set correctly in given config struct: | |||
* | |||
* phys_addr, log_page_size, and log_block_size. | |||
* | |||
* Also, hal_read_f must be set in the config struct. | |||
* | |||
* One must be sure of the correct page size and that the physical address is | |||
* correct in the probed file system when calling this function. It is not | |||
* checked if the phys_addr actually points to the start of the file system, | |||
* so one might get a false positive if entering a phys_addr somewhere in the | |||
* middle of the file system at block boundary. In addition, it is not checked | |||
* if the page size is actually correct. If it is not, weird file system sizes | |||
* will be returned. | |||
* | |||
* If this function detects a file system it returns the assumed file system | |||
* size, which can be used to set the phys_size. | |||
* | |||
* Otherwise, it returns an error indicating why it is not regarded as a file | |||
* system. | |||
* | |||
* Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK | |||
* macros. It returns the error code directly, instead of as read by | |||
* SPIFFS_errno. | |||
* | |||
* @param config essential parts of the physical and logical | |||
* configuration of the file system. | |||
*/ | |||
s32_t SPIFFS_probe_fs(spiffs_config *config); | |||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 | |||
/** | |||
* Initializes the file system dynamic parameters and mounts the filesystem. | |||
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS | |||
* if the flash does not contain a recognizable file system. | |||
* In this case, SPIFFS_format must be called prior to remounting. | |||
* @param fs the file system struct | |||
* @param config the physical and logical configuration of the file system | |||
* @param work a memory work buffer comprising 2*config->log_page_size | |||
* bytes used throughout all file system operations | |||
* @param fd_space memory for file descriptors | |||
* @param fd_space_size memory size of file descriptors | |||
* @param cache memory for cache, may be null | |||
* @param cache_size memory size of cache | |||
* @param check_cb_f callback function for reporting during consistency checks | |||
*/ | |||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, | |||
u8_t *fd_space, u32_t fd_space_size, | |||
void *cache, u32_t cache_size, | |||
spiffs_check_callback check_cb_f); | |||
/** | |||
* Unmounts the file system. All file handles will be flushed of any | |||
* cached writes and closed. | |||
* @param fs the file system struct | |||
*/ | |||
void SPIFFS_unmount(spiffs *fs); | |||
/** | |||
* Creates a new file. | |||
* @param fs the file system struct | |||
* @param path the path of the new file | |||
* @param mode ignored, for posix compliance | |||
*/ | |||
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); | |||
/** | |||
* Opens/creates a file. | |||
* @param fs the file system struct | |||
* @param path the path of the new file | |||
* @param flags the flags for the open command, can be combinations of | |||
* SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, | |||
* SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL | |||
* @param mode ignored, for posix compliance | |||
*/ | |||
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); | |||
/** | |||
* Opens a file by given dir entry. | |||
* Optimization purposes, when traversing a file system with SPIFFS_readdir | |||
* a normal SPIFFS_open would need to traverse the filesystem again to find | |||
* the file, whilst SPIFFS_open_by_dirent already knows where the file resides. | |||
* @param fs the file system struct | |||
* @param e the dir entry to the file | |||
* @param flags the flags for the open command, can be combinations of | |||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, | |||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. | |||
* SPIFFS_CREAT will have no effect in this case. | |||
* @param mode ignored, for posix compliance | |||
*/ | |||
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode); | |||
/** | |||
* Opens a file by given page index. | |||
* Optimization purposes, opens a file by directly pointing to the page | |||
* index in the spi flash. | |||
* If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE | |||
* is returned. | |||
* @param fs the file system struct | |||
* @param page_ix the page index | |||
* @param flags the flags for the open command, can be combinations of | |||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, | |||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. | |||
* SPIFFS_CREAT will have no effect in this case. | |||
* @param mode ignored, for posix compliance | |||
*/ | |||
spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode); | |||
/** | |||
* Reads from given filehandle. | |||
* @param fs the file system struct | |||
* @param fh the filehandle | |||
* @param buf where to put read data | |||
* @param len how much to read | |||
* @returns number of bytes read, or -1 if error | |||
*/ | |||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); | |||
/** | |||
* Writes to given filehandle. | |||
* @param fs the file system struct | |||
* @param fh the filehandle | |||
* @param buf the data to write | |||
* @param len how much to write | |||
* @returns number of bytes written, or -1 if error | |||
*/ | |||
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); | |||
/** | |||
* Moves the read/write file offset. Resulting offset is returned or negative if error. | |||
* lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. | |||
* @param fs the file system struct | |||
* @param fh the filehandle | |||
* @param offs how much/where to move the offset | |||
* @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes | |||
* if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset | |||
* if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative | |||
*/ | |||
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); | |||
/** | |||
* Removes a file by path | |||
* @param fs the file system struct | |||
* @param path the path of the file to remove | |||
*/ | |||
s32_t SPIFFS_remove(spiffs *fs, const char *path); | |||
/** | |||
* Removes a file by filehandle | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to remove | |||
*/ | |||
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Gets file status by path | |||
* @param fs the file system struct | |||
* @param path the path of the file to stat | |||
* @param s the stat struct to populate | |||
*/ | |||
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); | |||
/** | |||
* Gets file status by filehandle | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to stat | |||
* @param s the stat struct to populate | |||
*/ | |||
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); | |||
/** | |||
* Flushes all pending write operations from cache for given file | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to flush | |||
*/ | |||
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Closes a filehandle. If there are pending write operations, these are finalized before closing. | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to close | |||
*/ | |||
s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Renames a file | |||
* @param fs the file system struct | |||
* @param old path of file to rename | |||
* @param newPath new path of file | |||
*/ | |||
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath); | |||
#if SPIFFS_OBJ_META_LEN | |||
/** | |||
* Updates file's metadata | |||
* @param fs the file system struct | |||
* @param path path to the file | |||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. | |||
*/ | |||
s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); | |||
/** | |||
* Updates file's metadata | |||
* @param fs the file system struct | |||
* @param fh file handle of the file | |||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. | |||
*/ | |||
s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); | |||
#endif | |||
/** | |||
* Returns last error of last file operation. | |||
* @param fs the file system struct | |||
*/ | |||
s32_t SPIFFS_errno(spiffs *fs); | |||
/** | |||
* Clears last error. | |||
* @param fs the file system struct | |||
*/ | |||
void SPIFFS_clearerr(spiffs *fs); | |||
/** | |||
* Opens a directory stream corresponding to the given name. | |||
* The stream is positioned at the first entry in the directory. | |||
* On hydrogen builds the name argument is ignored as hydrogen builds always correspond | |||
* to a flat file structure - no directories. | |||
* @param fs the file system struct | |||
* @param name the name of the directory | |||
* @param d pointer the directory stream to be populated | |||
*/ | |||
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); | |||
/** | |||
* Closes a directory stream | |||
* @param d the directory stream to close | |||
*/ | |||
s32_t SPIFFS_closedir(spiffs_DIR *d); | |||
/** | |||
* Reads a directory into given spifs_dirent struct. | |||
* @param d pointer to the directory stream | |||
* @param e the dirent struct to be populated | |||
* @returns null if error or end of stream, else given dirent is returned | |||
*/ | |||
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); | |||
/** | |||
* Runs a consistency check on given filesystem. | |||
* @param fs the file system struct | |||
*/ | |||
s32_t SPIFFS_check(spiffs *fs); | |||
/** | |||
* Returns number of total bytes available and number of used bytes. | |||
* This is an estimation, and depends on if there a many files with little | |||
* data or few files with much data. | |||
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should | |||
* run. This indicates a power loss in midst of things. In worst case | |||
* (repeated powerlosses in mending or gc) you might have to delete some files. | |||
* | |||
* @param fs the file system struct | |||
* @param total total number of bytes in filesystem | |||
* @param used used number of bytes in filesystem | |||
*/ | |||
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); | |||
/** | |||
* Formats the entire file system. All data will be lost. | |||
* The filesystem must not be mounted when calling this. | |||
* | |||
* NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount | |||
* MUST be called prior to formatting in order to configure the filesystem. | |||
* If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling | |||
* SPIFFS_format. | |||
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling | |||
* SPIFFS_unmount first. | |||
* | |||
* @param fs the file system struct | |||
*/ | |||
s32_t SPIFFS_format(spiffs *fs); | |||
/** | |||
* Returns nonzero if spiffs is mounted, or zero if unmounted. | |||
* @param fs the file system struct | |||
*/ | |||
u8_t SPIFFS_mounted(spiffs *fs); | |||
/** | |||
* Tries to find a block where most or all pages are deleted, and erase that | |||
* block if found. Does not care for wear levelling. Will not move pages | |||
* around. | |||
* If parameter max_free_pages are set to 0, only blocks with only deleted | |||
* pages will be selected. | |||
* | |||
* NB: the garbage collector is automatically called when spiffs needs free | |||
* pages. The reason for this function is to give possibility to do background | |||
* tidying when user knows the system is idle. | |||
* | |||
* Use with care. | |||
* | |||
* Setting max_free_pages to anything larger than zero will eventually wear | |||
* flash more as a block containing free pages can be erased. | |||
* | |||
* Will set err_no to SPIFFS_OK if a block was found and erased, | |||
* SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, | |||
* or other error. | |||
* | |||
* @param fs the file system struct | |||
* @param max_free_pages maximum number allowed free pages in block | |||
*/ | |||
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); | |||
/** | |||
* Will try to make room for given amount of bytes in the filesystem by moving | |||
* pages and erasing blocks. | |||
* If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If | |||
* there already is this amount (or more) of free space, SPIFFS_gc will | |||
* silently return. It is recommended to call SPIFFS_info before invoking | |||
* this method in order to determine what amount of bytes to give. | |||
* | |||
* NB: the garbage collector is automatically called when spiffs needs free | |||
* pages. The reason for this function is to give possibility to do background | |||
* tidying when user knows the system is idle. | |||
* | |||
* Use with care. | |||
* | |||
* @param fs the file system struct | |||
* @param size amount of bytes that should be freed | |||
*/ | |||
s32_t SPIFFS_gc(spiffs *fs, u32_t size); | |||
/** | |||
* Check if EOF reached. | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to check | |||
*/ | |||
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Get position in file. | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to check | |||
*/ | |||
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Registers a callback function that keeps track on operations on file | |||
* headers. Do note, that this callback is called from within internal spiffs | |||
* mechanisms. Any operations on the actual file system being callbacked from | |||
* in this callback will mess things up for sure - do not do this. | |||
* This can be used to track where files are and move around during garbage | |||
* collection, which in turn can be used to build location tables in ram. | |||
* Used in conjuction with SPIFFS_open_by_page this may improve performance | |||
* when opening a lot of files. | |||
* Must be invoked after mount. | |||
* | |||
* @param fs the file system struct | |||
* @param cb_func the callback on file operations | |||
*/ | |||
s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); | |||
#if SPIFFS_IX_MAP | |||
/** | |||
* Maps the first level index lookup to a given memory map. | |||
* This will make reading big files faster, as the memory map will be used for | |||
* looking up data pages instead of searching for the indices on the physical | |||
* medium. When mapping, all affected indicies are found and the information is | |||
* copied to the array. | |||
* Whole file or only parts of it may be mapped. The index map will cover file | |||
* contents from argument offset until and including arguments (offset+len). | |||
* It is valid to map a longer range than the current file size. The map will | |||
* then be populated when the file grows. | |||
* On garbage collections and file data page movements, the map array will be | |||
* automatically updated. Do not tamper with the map array, as this contains | |||
* the references to the data pages. Modifying it from outside will corrupt any | |||
* future readings using this file descriptor. | |||
* The map will no longer be used when the file descriptor closed or the file | |||
* is unmapped. | |||
* This can be useful to get faster and more deterministic timing when reading | |||
* large files, or when seeking and reading a lot within a file. | |||
* @param fs the file system struct | |||
* @param fh the file handle of the file to map | |||
* @param map a spiffs_ix_map struct, describing the index map | |||
* @param offset absolute file offset where to start the index map | |||
* @param len length of the mapping in actual file bytes | |||
* @param map_buf the array buffer for the look up data - number of required | |||
* elements in the array can be derived from function | |||
* SPIFFS_bytes_to_ix_map_entries given the length | |||
*/ | |||
s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, | |||
u32_t offset, u32_t len, spiffs_page_ix *map_buf); | |||
/** | |||
* Unmaps the index lookup from this filehandle. All future readings will | |||
* proceed as normal, requiring reading of the first level indices from | |||
* physical media. | |||
* The map and map buffer given in function SPIFFS_ix_map will no longer be | |||
* referenced by spiffs. | |||
* It is not strictly necessary to unmap a file before closing it, as closing | |||
* a file will automatically unmap it. | |||
* @param fs the file system struct | |||
* @param fh the file handle of the file to unmap | |||
*/ | |||
s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Moves the offset for the index map given in function SPIFFS_ix_map. Parts or | |||
* all of the map buffer will repopulated. | |||
* @param fs the file system struct | |||
* @param fh the mapped file handle of the file to remap | |||
* @param offset new absolute file offset where to start the index map | |||
*/ | |||
s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs); | |||
/** | |||
* Utility function to get number of spiffs_page_ix entries a map buffer must | |||
* contain on order to map given amount of file data in bytes. | |||
* See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. | |||
* @param fs the file system struct | |||
* @param bytes number of file data bytes to map | |||
* @return needed number of elements in a spiffs_page_ix array needed to | |||
* map given amount of bytes in a file | |||
*/ | |||
s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes); | |||
/** | |||
* Utility function to amount of file data bytes that can be mapped when | |||
* mapping a file with buffer having given number of spiffs_page_ix entries. | |||
* See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. | |||
* @param fs the file system struct | |||
* @param map_page_ix_entries number of entries in a spiffs_page_ix array | |||
* @return amount of file data in bytes that can be mapped given a map | |||
* buffer having given amount of spiffs_page_ix entries | |||
*/ | |||
s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); | |||
#endif // SPIFFS_IX_MAP | |||
#if SPIFFS_TEST_VISUALISATION | |||
/** | |||
* Prints out a visualization of the filesystem. | |||
* @param fs the file system struct | |||
*/ | |||
s32_t SPIFFS_vis(spiffs *fs); | |||
#endif | |||
#if SPIFFS_BUFFER_HELP | |||
/** | |||
* Returns number of bytes needed for the filedescriptor buffer given | |||
* amount of file descriptors. | |||
*/ | |||
u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs); | |||
#if SPIFFS_CACHE | |||
/** | |||
* Returns number of bytes needed for the cache buffer given | |||
* amount of cache pages. | |||
*/ | |||
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); | |||
#endif | |||
#endif | |||
#if SPIFFS_CACHE | |||
#endif | |||
#if defined(__cplusplus) | |||
} | |||
#endif | |||
#endif /* SPIFFS_H_ */ |
@@ -0,0 +1,314 @@ | |||
/* | |||
* spiffs_cache.c | |||
* | |||
* Created on: Jun 23, 2013 | |||
* Author: petera | |||
*/ | |||
#include "spiffs.h" | |||
#include "spiffs_nucleus.h" | |||
#if SPIFFS_CACHE | |||
// returns cached page for give page index, or null if no such cached page | |||
static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { | |||
spiffs_cache *cache = spiffs_get_cache(fs); | |||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; | |||
int i; | |||
for (i = 0; i < cache->cpage_count; i++) { | |||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); | |||
if ((cache->cpage_use_map & (1<<i)) && | |||
(cp->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<<ix)) { | |||
if (write_back && | |||
(cp->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<<i)) == 0) { | |||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); | |||
cache->cpage_use_map |= (1<<i); | |||
cp->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<<i)) && | |||
(cp->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 |
@@ -0,0 +1,995 @@ | |||
/* | |||
* spiffs_check.c | |||
* | |||
* Contains functionality for checking file system consistency | |||
* and mending problems. | |||
* Three levels of consistency checks are implemented: | |||
* | |||
* Look up consistency | |||
* Checks if indices in lookup pages are coherent with page headers | |||
* Object index consistency | |||
* Checks if there are any orphaned object indices (missing object index headers). | |||
* If an object index is found but not its header, the object index is deleted. | |||
* This is critical for the following page consistency check. | |||
* Page consistency | |||
* Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed | |||
* | |||
* | |||
* Created on: Jul 7, 2013 | |||
* Author: petera | |||
*/ | |||
#include "spiffs.h" | |||
#include "spiffs_nucleus.h" | |||
#if !SPIFFS_READ_ONLY | |||
#if SPIFFS_HAL_CALLBACK_EXTRA | |||
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ | |||
do { \ | |||
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ | |||
} while (0) | |||
#else | |||
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ | |||
do { \ | |||
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ | |||
} while (0) | |||
#endif | |||
//--------------------------------------- | |||
// Look up consistency | |||
// searches in the object indices and returns the referenced page index given | |||
// the object id and the data span index | |||
// destroys fs->lu_work | |||
static s32_t spiffs_object_get_data_page_index_reference( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_span_ix data_spix, | |||
spiffs_page_ix *pix, | |||
spiffs_page_ix *objix_pix) { | |||
s32_t res; | |||
// calculate object index span index for given data page span index | |||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); | |||
// find obj index for obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix); | |||
SPIFFS_CHECK_RES(res); | |||
// load obj index entry | |||
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); | |||
if (objix_spix == 0) { | |||
// get referenced page from object index header | |||
addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); | |||
} else { | |||
// get referenced page from object index | |||
addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); | |||
} | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); | |||
return res; | |||
} | |||
// copies page contents to a new page | |||
static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) { | |||
s32_t res; | |||
res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_phys_cpy(fs, 0, | |||
SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_DATA_PAGE_SIZE(fs)); | |||
SPIFFS_CHECK_RES(res); | |||
return res; | |||
} | |||
// rewrites the object index for given object id and replaces the | |||
// data page index to a new page index | |||
static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) { | |||
s32_t res; | |||
spiffs_block_ix bix; | |||
int entry; | |||
spiffs_page_ix free_pix; | |||
obj_id |= SPIFFS_OBJ_ID_IX_FLAG; | |||
// find free entry | |||
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); | |||
SPIFFS_CHECK_RES(res); | |||
free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); | |||
// calculate object index span index for given data page span index | |||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); | |||
if (objix_spix == 0) { | |||
// calc index in index header | |||
entry = data_spix; | |||
} else { | |||
// calc entry in index | |||
entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); | |||
} | |||
// load index | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; | |||
// be ultra safe, double check header against provided data | |||
if (objix_p_hdr->obj_id != obj_id) { | |||
spiffs_page_delete(fs, free_pix); | |||
return SPIFFS_ERR_CHECK_OBJ_ID_MISM; | |||
} | |||
if (objix_p_hdr->span_ix != objix_spix) { | |||
spiffs_page_delete(fs, free_pix); | |||
return SPIFFS_ERR_CHECK_SPIX_MISM; | |||
} | |||
if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX | | |||
SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) != | |||
(SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) { | |||
spiffs_page_delete(fs, free_pix); | |||
return SPIFFS_ERR_CHECK_FLAGS_BAD; | |||
} | |||
// rewrite in mem | |||
if (objix_spix == 0) { | |||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; | |||
} else { | |||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; | |||
} | |||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, | |||
0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), | |||
sizeof(spiffs_obj_id), | |||
(u8_t *)&obj_id); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_page_delete(fs, objix_pix); | |||
return res; | |||
} | |||
// deletes an object just by marking object index header as deleted | |||
static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) { | |||
spiffs_page_ix objix_hdr_pix; | |||
s32_t res; | |||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
return SPIFFS_OK; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE; | |||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), | |||
sizeof(u8_t), | |||
(u8_t *)&flags); | |||
return res; | |||
} | |||
// validates the given look up entry | |||
static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr, | |||
spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) { | |||
(void)cur_block; | |||
(void)cur_entry; | |||
u8_t delete_page = 0; | |||
s32_t res = SPIFFS_OK; | |||
spiffs_page_ix objix_pix; | |||
spiffs_page_ix ref_pix; | |||
// check validity, take actions | |||
if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || | |||
((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) { | |||
// look up entry deleted / free but used in page header | |||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix); | |||
*reload_lu = 1; | |||
delete_page = 1; | |||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { | |||
// header says data page | |||
// data page can be removed if not referenced by some object index | |||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// no object with this id, so remove page safely | |||
res = SPIFFS_OK; | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
if (ref_pix == cur_pix) { | |||
// data page referenced by object index but deleted in lu | |||
// copy page to new place and re-write the object index to new place | |||
spiffs_page_ix new_pix; | |||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); | |||
SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
*reload_lu = 1; | |||
SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix); | |||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); | |||
res = spiffs_page_delete(fs, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); | |||
} else { | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
} | |||
} else { | |||
// header says index page | |||
// index page can be removed if other index with same obj_id and spanix is found | |||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// no such index page found, check for a data page amongst page headers | |||
// lu cannot be trusted | |||
res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0); | |||
if (res == SPIFFS_OK) { // ignore other errors | |||
// got a data page also, assume lu corruption only, rewrite to new page | |||
spiffs_page_ix new_pix; | |||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); | |||
SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
*reload_lu = 1; | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); | |||
} | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
} | |||
} | |||
if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) { | |||
// look up entry used | |||
if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) { | |||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id); | |||
delete_page = 1; | |||
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || | |||
(p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || | |||
(p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) { | |||
// page deleted or not finalized, just remove it | |||
} else { | |||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { | |||
// if data page, check for reference to this page | |||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// no object with this id, so remove page safely | |||
res = SPIFFS_OK; | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
// if found, rewrite page with object id, update index, and delete current | |||
if (ref_pix == cur_pix) { | |||
spiffs_page_ix new_pix; | |||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); | |||
res = spiffs_page_delete(fs, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); | |||
*reload_lu = 1; | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
} | |||
} else { | |||
// else if index, check for other pages with both obj_id's and spanix | |||
spiffs_page_ix objix_pix_lu, objix_pix_ph; | |||
// see if other object index page exists for lookup obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_lu = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
// see if other object index exists for page header obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_ph = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
// if both obj_id's found, just delete current | |||
if (objix_pix_ph == 0 || objix_pix_lu == 0) { | |||
// otherwise try finding first corresponding data pages | |||
spiffs_page_ix data_pix_lu, data_pix_ph; | |||
// see if other data page exists for look up obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_lu = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
// see if other data page exists for page header obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_ph = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_page_header new_ph; | |||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL); | |||
new_ph.span_ix = p_hdr->span_ix; | |||
spiffs_page_ix new_pix; | |||
if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) || | |||
(objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) { | |||
// got a data page for page header obj id | |||
// rewrite as obj_id_ph | |||
new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; | |||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); | |||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); | |||
SPIFFS_CHECK_RES(res); | |||
*reload_lu = 1; | |||
} else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || | |||
(objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) { | |||
// got a data page for look up obj id | |||
// rewrite as obj_id_lu | |||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; | |||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); | |||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
*reload_lu = 1; | |||
} else { | |||
// cannot safely do anything | |||
SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n"); | |||
} | |||
} | |||
} | |||
} | |||
} else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || | |||
((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) { | |||
SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix); | |||
spiffs_page_ix data_pix, objix_pix_d; | |||
// see if other data page exists for given obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
data_pix = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
// see if other object index exists for given obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_d = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
delete_page = 1; | |||
// if other data page exists and object index exists, just delete page | |||
if (data_pix && objix_pix_d) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n"); | |||
} else | |||
// if only data page exists, make this page index | |||
if (data_pix && objix_pix_d == 0) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); | |||
spiffs_page_header new_ph; | |||
spiffs_page_ix new_pix; | |||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); | |||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; | |||
new_ph.span_ix = p_hdr->span_ix; | |||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); | |||
SPIFFS_CHECK_RES(res); | |||
} else | |||
// if only index exists, make data page | |||
if (data_pix == 0 && objix_pix_d) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); | |||
spiffs_page_header new_ph; | |||
spiffs_page_ix new_pix; | |||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); | |||
new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; | |||
new_ph.span_ix = p_hdr->span_ix; | |||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); | |||
SPIFFS_CHECK_RES(res); | |||
} else { | |||
// if nothing exists, we cannot safely make a decision - delete | |||
} | |||
} | |||
else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) { | |||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix); | |||
delete_page = 1; | |||
} else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) { | |||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix); | |||
// page can be removed if not referenced by object index | |||
*reload_lu = 1; | |||
res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// no object with this id, so remove page safely | |||
res = SPIFFS_OK; | |||
delete_page = 1; | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
if (ref_pix != cur_pix) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n"); | |||
delete_page = 1; | |||
} else { | |||
// page referenced by object index but not final | |||
// just finalize | |||
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); | |||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; | |||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), | |||
sizeof(u8_t), (u8_t*)&flags); | |||
} | |||
} | |||
} | |||
} | |||
if (delete_page) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
return res; | |||
} | |||
static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, | |||
const void *user_const_p, void *user_var_p) { | |||
(void)user_const_p; | |||
(void)user_var_p; | |||
s32_t res = SPIFFS_OK; | |||
spiffs_page_header p_hdr; | |||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, | |||
(cur_block * 256)/fs->block_count, 0); | |||
// load header | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
int reload_lu = 0; | |||
res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu); | |||
SPIFFS_CHECK_RES(res); | |||
if (res == SPIFFS_OK) { | |||
return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; | |||
} | |||
return res; | |||
} | |||
// Scans all object look up. For each entry, corresponding page header is checked for validity. | |||
// If an object index header page is found, this is also checked | |||
s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { | |||
(void)check_all_objects; | |||
s32_t res = SPIFFS_OK; | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); | |||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); | |||
if (res == SPIFFS_VIS_END) { | |||
res = SPIFFS_OK; | |||
} | |||
if (res != SPIFFS_OK) { | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); | |||
} | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); | |||
return res; | |||
} | |||
//--------------------------------------- | |||
// Page consistency | |||
// Scans all pages (except lu pages), reserves 4 bits in working memory for each page | |||
// bit 0: 0 == FREE|DELETED, 1 == USED | |||
// bit 1: 0 == UNREFERENCED, 1 == REFERENCED | |||
// bit 2: 0 == NOT_INDEX, 1 == INDEX | |||
// bit 3: unused | |||
// A consistent file system will have only pages being | |||
// * x000 free, unreferenced, not index | |||
// * x011 used, referenced only once, not index | |||
// * x101 used, unreferenced, index | |||
// The working memory might not fit all pages so several scans might be needed | |||
static s32_t spiffs_page_consistency_check_i(spiffs *fs) { | |||
const u32_t bits = 4; | |||
const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits; | |||
s32_t res = SPIFFS_OK; | |||
spiffs_page_ix pix_offset = 0; | |||
// for each range of pages fitting into work memory | |||
while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { | |||
// set this flag to abort all checks and rescan the page range | |||
u8_t restart = 0; | |||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); | |||
spiffs_block_ix cur_block = 0; | |||
// build consistency bitmap for id range traversing all blocks | |||
while (!restart && cur_block < fs->block_count) { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, | |||
(pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + | |||
((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), | |||
0); | |||
// traverse each page except for lookup pages | |||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; | |||
while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { | |||
//if ((cur_pix & 0xff) == 0) | |||
// SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n", | |||
// cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); | |||
// read header | |||
spiffs_page_header p_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan); | |||
const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits); | |||
const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits; | |||
if (within_range && | |||
(p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) { | |||
// used | |||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0)); | |||
} | |||
if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) && | |||
(p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) && | |||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) { | |||
// found non-deleted index | |||
if (within_range) { | |||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2)); | |||
} | |||
// load non-deleted index | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
// traverse index for referenced pages | |||
spiffs_page_ix *object_page_index; | |||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; | |||
int entries; | |||
int i; | |||
spiffs_span_ix data_spix_offset; | |||
if (p_hdr.span_ix == 0) { | |||
// object header page index | |||
entries = SPIFFS_OBJ_HDR_IX_LEN(fs); | |||
data_spix_offset = 0; | |||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); | |||
} else { | |||
// object page index | |||
entries = SPIFFS_OBJ_IX_LEN(fs); | |||
data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1); | |||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)); | |||
} | |||
// for all entries in index | |||
for (i = 0; !restart && i < entries; i++) { | |||
spiffs_page_ix rpix = object_page_index[i]; | |||
u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan; | |||
if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs)) | |||
|| (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) { | |||
// bad reference | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n", | |||
rpix, cur_pix); | |||
// check for data page elsewhere | |||
spiffs_page_ix data_pix; | |||
res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, | |||
data_spix_offset + i, 0, &data_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
data_pix = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
if (data_pix == 0) { | |||
// if not, allocate free page | |||
spiffs_page_header new_ph; | |||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); | |||
new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; | |||
new_ph.span_ix = data_spix_offset + i; | |||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); | |||
SPIFFS_CHECK_RES(res); | |||
SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix); | |||
} | |||
// remap index | |||
SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix); | |||
res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, | |||
data_spix_offset + i, data_pix, cur_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); | |||
// delete file | |||
res = spiffs_page_delete(fs, cur_pix); | |||
} else { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
restart = 1; | |||
} else if (rpix_within_range) { | |||
// valid reference | |||
// read referenced page header | |||
spiffs_page_header rp_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
// cross reference page header check | |||
if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) || | |||
rp_hdr.span_ix != data_spix_offset + i || | |||
(rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != | |||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) { | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n", | |||
rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, | |||
rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); | |||
// try finding correct page | |||
spiffs_page_ix data_pix; | |||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, | |||
data_spix_offset + i, rpix, &data_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
data_pix = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
if (data_pix == 0) { | |||
// not found, this index is badly borked | |||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); | |||
SPIFFS_CHECK_RES(res); | |||
break; | |||
} else { | |||
// found it, so rewrite index | |||
SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n", | |||
data_pix, cur_pix, p_hdr.obj_id); | |||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); | |||
} else { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
restart = 1; | |||
} | |||
} | |||
else { | |||
// mark rpix as referenced | |||
const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits); | |||
const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits; | |||
if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) { | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n", | |||
rpix, cur_pix); | |||
// Here, we should have fixed all broken references - getting this means there | |||
// must be multiple files with same object id. Only solution is to delete | |||
// the object which is referring to this page | |||
SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\n", | |||
p_hdr.obj_id, cur_pix); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); | |||
SPIFFS_CHECK_RES(res); | |||
// extra precaution, delete this page also | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
restart = 1; | |||
} | |||
fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1)); | |||
} | |||
} | |||
} // for all index entries | |||
} // found index | |||
// next page | |||
cur_pix++; | |||
} | |||
// next block | |||
cur_block++; | |||
} | |||
// check consistency bitmap | |||
if (!restart) { | |||
spiffs_page_ix objix_pix; | |||
spiffs_page_ix rpix; | |||
u32_t byte_ix; | |||
u8_t bit_ix; | |||
for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) { | |||
for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) { | |||
u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7; | |||
spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix; | |||
// 000 ok - free, unreferenced, not index | |||
if (bitmask == 0x1) { | |||
// 001 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix); | |||
u8_t rewrite_ix_to_this = 0; | |||
u8_t delete_page = 0; | |||
// check corresponding object index entry | |||
spiffs_page_header p_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix, | |||
&rpix, &objix_pix); | |||
if (res == SPIFFS_OK) { | |||
if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) { | |||
// pointing to a bad page altogether, rewrite index to this | |||
rewrite_ix_to_this = 1; | |||
SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix); | |||
} else { | |||
// pointing to something else, check what | |||
spiffs_page_header rp_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) && | |||
((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == | |||
(SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) { | |||
// pointing to something else valid, just delete this page then | |||
SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix); | |||
delete_page = 1; | |||
} else { | |||
// pointing to something weird, update index to point to this page instead | |||
if (rpix != cur_pix) { | |||
SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix, | |||
(rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", | |||
(rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", | |||
(rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", | |||
(rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "", | |||
cur_pix); | |||
rewrite_ix_to_this = 1; | |||
} else { | |||
// should not happen, destined for fubar | |||
} | |||
} | |||
} | |||
} else if (res == SPIFFS_ERR_NOT_FOUND) { | |||
SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix); | |||
delete_page = 1; | |||
res = SPIFFS_OK; | |||
} | |||
if (rewrite_ix_to_this) { | |||
// if pointing to invalid page, redirect index to this page | |||
SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n", | |||
p_hdr.obj_id, p_hdr.span_ix, cur_pix); | |||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); | |||
} else { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
restart = 1; | |||
continue; | |||
} else if (delete_page) { | |||
SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
if (bitmask == 0x2) { | |||
// 010 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix); | |||
// no op, this should be taken care of when checking valid references | |||
} | |||
// 011 ok - busy, referenced, not index | |||
if (bitmask == 0x4) { | |||
// 100 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix); | |||
// this should never happen, major fubar | |||
} | |||
// 101 ok - busy, unreferenced, index | |||
if (bitmask == 0x6) { | |||
// 110 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix); | |||
// no op, this should be taken care of when checking valid references | |||
} | |||
if (bitmask == 0x7) { | |||
// 111 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix); | |||
// no op, this should be taken care of when checking valid references | |||
} | |||
} | |||
} | |||
} | |||
SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\n", pix_offset, restart); | |||
// next page range | |||
if (!restart) { | |||
pix_offset += pages_per_scan; | |||
} | |||
} // while page range not reached end | |||
return res; | |||
} | |||
// Checks consistency amongst all pages and fixes irregularities | |||
s32_t spiffs_page_consistency_check(spiffs *fs) { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); | |||
s32_t res = spiffs_page_consistency_check_i(fs); | |||
if (res != SPIFFS_OK) { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); | |||
} | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); | |||
return res; | |||
} | |||
//--------------------------------------- | |||
// Object index consistency | |||
// searches for given object id in temporary object id index, | |||
// returns the index or -1 | |||
static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) { | |||
u32_t i; | |||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; | |||
obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; | |||
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) { | |||
if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, | |||
int cur_entry, const void *user_const_p, void *user_var_p) { | |||
(void)user_const_p; | |||
s32_t res_c = SPIFFS_VIS_COUNTINUE; | |||
s32_t res = SPIFFS_OK; | |||
u32_t *log_ix = (u32_t*)user_var_p; | |||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, | |||
(cur_block * 256)/fs->block_count, 0); | |||
if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { | |||
spiffs_page_header p_hdr; | |||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); | |||
// load header | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
if (p_hdr.span_ix == 0 && | |||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == | |||
(SPIFFS_PH_FLAG_DELET)) { | |||
SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n", | |||
cur_pix, obj_id, p_hdr.span_ix); | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
return res_c; | |||
} | |||
if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == | |||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { | |||
return res_c; | |||
} | |||
if (p_hdr.span_ix == 0) { | |||
// objix header page, register objid as reachable | |||
int r = spiffs_object_index_search(fs, obj_id); | |||
if (r == -1) { | |||
// not registered, do it | |||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; | |||
(*log_ix)++; | |||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { | |||
*log_ix = 0; | |||
} | |||
} | |||
} else { // span index | |||
// objix page, see if header can be found | |||
int r = spiffs_object_index_search(fs, obj_id); | |||
u8_t delete = 0; | |||
if (r == -1) { | |||
// not in temporary index, try finding it | |||
spiffs_page_ix objix_hdr_pix; | |||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix); | |||
res_c = SPIFFS_VIS_COUNTINUE_RELOAD; | |||
if (res == SPIFFS_OK) { | |||
// found, register as reachable | |||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; | |||
} else if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// not found, register as unreachable | |||
delete = 1; | |||
obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG; | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
(*log_ix)++; | |||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { | |||
*log_ix = 0; | |||
} | |||
} else { | |||
// in temporary index, check reachable flag | |||
if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) { | |||
// registered as unreachable | |||
delete = 1; | |||
} | |||
} | |||
if (delete) { | |||
SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" is orphan index - deleting\n", | |||
cur_pix, obj_id, p_hdr.span_ix); | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
} // span index | |||
} // valid object index id | |||
return res_c; | |||
} | |||
// Removes orphaned and partially deleted index pages. | |||
// Scans for index pages. When an index page is found, corresponding index header is searched for. | |||
// If no such page exists, the index page cannot be reached as no index header exists and must be | |||
// deleted. | |||
s32_t spiffs_object_index_consistency_check(spiffs *fs) { | |||
s32_t res = SPIFFS_OK; | |||
// impl note: | |||
// fs->work is used for a temporary object index memory, listing found object ids and | |||
// indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. | |||
// In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate | |||
// a reachable/unreachable object id. | |||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); | |||
u32_t obj_id_log_ix = 0; | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); | |||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, | |||
0, 0); | |||
if (res == SPIFFS_VIS_END) { | |||
res = SPIFFS_OK; | |||
} | |||
if (res != SPIFFS_OK) { | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); | |||
} | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); | |||
return res; | |||
} | |||
#endif // !SPIFFS_READ_ONLY |
@@ -0,0 +1,361 @@ | |||
/* | |||
* spiffs_config.h | |||
* | |||
* Created on: Jul 3, 2013 | |||
* Author: petera | |||
*/ | |||
#ifndef SPIFFS_CONFIG_H_ | |||
#define SPIFFS_CONFIG_H_ | |||
// ----------- 8< ------------ | |||
// Following includes are for the linux test build of spiffs | |||
// These may/should/must be removed/altered/replaced in your target | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <stddef.h> | |||
#include <unistd.h> | |||
#include <stdint.h> | |||
#include <ctype.h> | |||
// ----------- >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_ */ |
@@ -0,0 +1,606 @@ | |||
#include "spiffs.h" | |||
#include "spiffs_nucleus.h" | |||
#if !SPIFFS_READ_ONLY | |||
// Erases a logical block and updates the erase counter. | |||
// If cache is enabled, all pages that might be cached in this block | |||
// is dropped. | |||
static s32_t spiffs_gc_erase_block( | |||
spiffs *fs, | |||
spiffs_block_ix bix) { | |||
s32_t res; | |||
SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix); | |||
res = spiffs_erase_block(fs, bix); | |||
SPIFFS_CHECK_RES(res); | |||
#if SPIFFS_CACHE | |||
{ | |||
u32_t i; | |||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { | |||
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); | |||
} | |||
} | |||
#endif | |||
return res; | |||
} | |||
// Searches for blocks where all entries are deleted - if one is found, | |||
// the block is erased. Compared to the non-quick gc, the quick one ensures | |||
// that no updates are needed on existing objects on pages that are erased. | |||
s32_t spiffs_gc_quick( | |||
spiffs *fs, u16_t max_free_pages) { | |||
s32_t res = SPIFFS_OK; | |||
u32_t blocks = fs->block_count; | |||
spiffs_block_ix cur_block = 0; | |||
u32_t cur_block_addr = 0; | |||
int cur_entry = 0; | |||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; | |||
SPIFFS_GC_DBG("gc_quick: running\n"); | |||
#if SPIFFS_GC_STATS | |||
fs->stats_gc_runs++; | |||
#endif | |||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); | |||
// find fully deleted blocks | |||
// check each block | |||
while (res == SPIFFS_OK && blocks--) { | |||
u16_t deleted_pages_in_block = 0; | |||
u16_t free_pages_in_block = 0; | |||
int obj_lookup_page = 0; | |||
// check each object lookup page | |||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { | |||
int entry_offset = obj_lookup_page * entries_per_page; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
// check each entry | |||
while (res == SPIFFS_OK && | |||
cur_entry - entry_offset < entries_per_page && | |||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { | |||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; | |||
if (obj_id == SPIFFS_OBJ_ID_DELETED) { | |||
deleted_pages_in_block++; | |||
} else if (obj_id == SPIFFS_OBJ_ID_FREE) { | |||
// kill scan, go for next block | |||
free_pages_in_block++; | |||
if (free_pages_in_block > max_free_pages) { | |||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); | |||
res = 1; // kill object lu loop | |||
break; | |||
} | |||
} else { | |||
// kill scan, go for next block | |||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); | |||
res = 1; // kill object lu loop | |||
break; | |||
} | |||
cur_entry++; | |||
} // per entry | |||
obj_lookup_page++; | |||
} // per object lookup page | |||
if (res == 1) res = SPIFFS_OK; | |||
if (res == SPIFFS_OK && | |||
deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) && | |||
free_pages_in_block <= max_free_pages) { | |||
// found a fully deleted block | |||
fs->stats_p_deleted -= deleted_pages_in_block; | |||
res = spiffs_gc_erase_block(fs, cur_block); | |||
return res; | |||
} | |||
cur_entry = 0; | |||
cur_block++; | |||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); | |||
} // per block | |||
if (res == SPIFFS_OK) { | |||
res = SPIFFS_ERR_NO_DELETED_BLOCKS; | |||
} | |||
return res; | |||
} | |||
// Checks if garbage collecting is necessary. If so a candidate block is found, | |||
// cleansed and erased | |||
s32_t spiffs_gc_check( | |||
spiffs *fs, | |||
u32_t len) { | |||
s32_t res; | |||
s32_t free_pages = | |||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2) | |||
- fs->stats_p_allocated - fs->stats_p_deleted; | |||
int tries = 0; | |||
if (fs->free_blocks > 3 && | |||
(s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { | |||
return SPIFFS_OK; | |||
} | |||
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); | |||
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { | |||
// SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); | |||
// return SPIFFS_ERR_FULL; | |||
// } | |||
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { | |||
SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); | |||
return SPIFFS_ERR_FULL; | |||
} | |||
do { | |||
SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n", | |||
tries, | |||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), | |||
len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); | |||
spiffs_block_ix *cands; | |||
int count; | |||
spiffs_block_ix cand; | |||
s32_t prev_free_pages = free_pages; | |||
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state | |||
res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); | |||
SPIFFS_CHECK_RES(res); | |||
if (count == 0) { | |||
SPIFFS_GC_DBG("gc_check: no candidates, return\n"); | |||
return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; | |||
} | |||
#if SPIFFS_GC_STATS | |||
fs->stats_gc_runs++; | |||
#endif | |||
cand = cands[0]; | |||
fs->cleaning = 1; | |||
//SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand); | |||
res = spiffs_gc_clean(fs, cand); | |||
fs->cleaning = 0; | |||
if (res < 0) { | |||
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); | |||
} else { | |||
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_gc_erase_page_stats(fs, cand); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_gc_erase_block(fs, cand); | |||
SPIFFS_CHECK_RES(res); | |||
free_pages = | |||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) | |||
- fs->stats_p_allocated - fs->stats_p_deleted; | |||
if (prev_free_pages <= 0 && prev_free_pages == free_pages) { | |||
// abort early to reduce wear, at least tried once | |||
SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n"); | |||
break; | |||
} | |||
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || | |||
(s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); | |||
free_pages = | |||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) | |||
- fs->stats_p_allocated - fs->stats_p_deleted; | |||
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { | |||
res = SPIFFS_ERR_FULL; | |||
} | |||
SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n", | |||
fs->stats_p_allocated + fs->stats_p_deleted, | |||
fs->free_blocks, free_pages, tries, res); | |||
return res; | |||
} | |||
// Updates page statistics for a block that is about to be erased | |||
s32_t spiffs_gc_erase_page_stats( | |||
spiffs *fs, | |||
spiffs_block_ix bix) { | |||
s32_t res = SPIFFS_OK; | |||
int obj_lookup_page = 0; | |||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); | |||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; | |||
int cur_entry = 0; | |||
u32_t dele = 0; | |||
u32_t allo = 0; | |||
// check each object lookup page | |||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { | |||
int entry_offset = obj_lookup_page * entries_per_page; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
// check each entry | |||
while (res == SPIFFS_OK && | |||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { | |||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; | |||
if (obj_id == SPIFFS_OBJ_ID_FREE) { | |||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) { | |||
dele++; | |||
} else { | |||
allo++; | |||
} | |||
cur_entry++; | |||
} // per entry | |||
obj_lookup_page++; | |||
} // per object lookup page | |||
SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele); | |||
fs->stats_p_allocated -= allo; | |||
fs->stats_p_deleted -= dele; | |||
return res; | |||
} | |||
// Finds block candidates to erase | |||
s32_t spiffs_gc_find_candidate( | |||
spiffs *fs, | |||
spiffs_block_ix **block_candidates, | |||
int *candidate_count, | |||
char fs_crammed) { | |||
s32_t res = SPIFFS_OK; | |||
u32_t blocks = fs->block_count; | |||
spiffs_block_ix cur_block = 0; | |||
u32_t cur_block_addr = 0; | |||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; | |||
int cur_entry = 0; | |||
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score | |||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t))); | |||
*candidate_count = 0; | |||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); | |||
// divide up work area into block indices and scores | |||
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; | |||
s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); | |||
// align cand_scores on s32_t boundary | |||
cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); | |||
*block_candidates = cand_blocks; | |||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); | |||
// check each block | |||
while (res == SPIFFS_OK && blocks--) { | |||
u16_t deleted_pages_in_block = 0; | |||
u16_t used_pages_in_block = 0; | |||
int obj_lookup_page = 0; | |||
// check each object lookup page | |||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { | |||
int entry_offset = obj_lookup_page * entries_per_page; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
// check each entry | |||
while (res == SPIFFS_OK && | |||
cur_entry - entry_offset < entries_per_page && | |||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { | |||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; | |||
if (obj_id == SPIFFS_OBJ_ID_FREE) { | |||
// when a free entry is encountered, scan logic ensures that all following entries are free also | |||
res = 1; // kill object lu loop | |||
break; | |||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) { | |||
deleted_pages_in_block++; | |||
} else { | |||
used_pages_in_block++; | |||
} | |||
cur_entry++; | |||
} // per entry | |||
obj_lookup_page++; | |||
} // per object lookup page | |||
if (res == 1) res = SPIFFS_OK; | |||
// calculate score and insert into candidate table | |||
// stoneage sort, but probably not so many blocks | |||
if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) { | |||
// read erase count | |||
spiffs_obj_id erase_count; | |||
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, | |||
SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), | |||
sizeof(spiffs_obj_id), (u8_t *)&erase_count); | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_obj_id erase_age; | |||
if (fs->max_erase_count > erase_count) { | |||
erase_age = fs->max_erase_count - erase_count; | |||
} else { | |||
erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); | |||
} | |||
s32_t score = | |||
deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + | |||
used_pages_in_block * SPIFFS_GC_HEUR_W_USED + | |||
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); | |||
int cand_ix = 0; | |||
SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); | |||
while (cand_ix < max_candidates) { | |||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { | |||
cand_blocks[cand_ix] = cur_block; | |||
cand_scores[cand_ix] = score; | |||
break; | |||
} else if (cand_scores[cand_ix] < score) { | |||
int reorder_cand_ix = max_candidates - 2; | |||
while (reorder_cand_ix >= cand_ix) { | |||
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; | |||
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; | |||
reorder_cand_ix--; | |||
} | |||
cand_blocks[cand_ix] = cur_block; | |||
cand_scores[cand_ix] = score; | |||
break; | |||
} | |||
cand_ix++; | |||
} | |||
(*candidate_count)++; | |||
} | |||
cur_entry = 0; | |||
cur_block++; | |||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); | |||
} // per block | |||
return res; | |||
} | |||
typedef enum { | |||
FIND_OBJ_DATA, | |||
MOVE_OBJ_DATA, | |||
MOVE_OBJ_IX, | |||
FINISHED | |||
} spiffs_gc_clean_state; | |||
typedef struct { | |||
spiffs_gc_clean_state state; | |||
spiffs_obj_id cur_obj_id; | |||
spiffs_span_ix cur_objix_spix; | |||
spiffs_page_ix cur_objix_pix; | |||
spiffs_page_ix cur_data_pix; | |||
int stored_scan_entry_index; | |||
u8_t obj_id_found; | |||
} spiffs_gc; | |||
// Empties given block by moving all data into free pages of another block | |||
// Strategy: | |||
// loop: | |||
// scan object lookup for object data pages | |||
// for first found id, check spix and load corresponding object index page to memory | |||
// push object scan lookup entry index | |||
// rescan object lookup, find data pages with same id and referenced by same object index | |||
// move data page, update object index in memory | |||
// when reached end of lookup, store updated object index | |||
// pop object scan lookup entry index | |||
// repeat loop until end of object lookup | |||
// scan object lookup again for remaining object index pages, move to new page in other block | |||
// | |||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { | |||
s32_t res = SPIFFS_OK; | |||
const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); | |||
// this is the global localizer being pushed and popped | |||
int cur_entry = 0; | |||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; | |||
spiffs_gc gc; // our stack frame/state | |||
spiffs_page_ix cur_pix = 0; | |||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; | |||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; | |||
SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix); | |||
memset(&gc, 0, sizeof(spiffs_gc)); | |||
gc.state = FIND_OBJ_DATA; | |||
if (fs->free_cursor_block_ix == bix) { | |||
// move free cursor to next block, cannot use free pages from the block we want to clean | |||
fs->free_cursor_block_ix = (bix+1)%fs->block_count; | |||
fs->free_cursor_obj_lu_entry = 0; | |||
SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix); | |||
} | |||
while (res == SPIFFS_OK && gc.state != FINISHED) { | |||
SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry); | |||
gc.obj_id_found = 0; // reset (to no found data page) | |||
// scan through lookup pages | |||
int obj_lookup_page = cur_entry / entries_per_page; | |||
u8_t scan = 1; | |||
// check each object lookup page | |||
while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { | |||
int entry_offset = obj_lookup_page * entries_per_page; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
// check each object lookup entry | |||
while (scan && res == SPIFFS_OK && | |||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { | |||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; | |||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); | |||
// act upon object id depending on gc state | |||
switch (gc.state) { | |||
case FIND_OBJ_DATA: | |||
// find a data page | |||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && | |||
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { | |||
// found a data page, stop scanning and handle in switch case below | |||
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id); | |||
gc.obj_id_found = 1; | |||
gc.cur_obj_id = obj_id; | |||
gc.cur_data_pix = cur_pix; | |||
scan = 0; | |||
} | |||
break; | |||
case MOVE_OBJ_DATA: | |||
// evacuate found data pages for corresponding object index we have in memory, | |||
// update memory representation | |||
if (obj_id == gc.cur_obj_id) { | |||
spiffs_page_header p_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); | |||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); | |||
} else { | |||
spiffs_page_ix new_data_pix; | |||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { | |||
// move page | |||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); | |||
SPIFFS_CHECK_RES(res); | |||
// move wipes obj_lu, reload it | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
} else { | |||
// page is deleted but not deleted in lookup, scrap it - | |||
// might seem unnecessary as we will erase this block, but | |||
// we might get aborted | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
new_data_pix = SPIFFS_OBJ_ID_FREE; | |||
} | |||
// update memory representation of object index page with new data page | |||
if (gc.cur_objix_spix == 0) { | |||
// update object index header page | |||
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); | |||
} else { | |||
// update object index page | |||
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); | |||
} | |||
} | |||
} | |||
break; | |||
case MOVE_OBJ_IX: | |||
// find and evacuate object index pages | |||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && | |||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { | |||
// found an index object id | |||
spiffs_page_header p_hdr; | |||
spiffs_page_ix new_pix; | |||
// load header | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { | |||
// move page | |||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, | |||
SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); | |||
// move wipes obj_lu, reload it | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
} else { | |||
// page is deleted but not deleted in lookup, scrap it - | |||
// might seem unnecessary as we will erase this block, but | |||
// we might get aborted | |||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
if (res == SPIFFS_OK) { | |||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, | |||
SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); | |||
} | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
break; | |||
default: | |||
scan = 0; | |||
break; | |||
} // switch gc state | |||
cur_entry++; | |||
} // per entry | |||
obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop | |||
} // per object lookup page | |||
if (res != SPIFFS_OK) break; | |||
// state finalization and switch | |||
switch (gc.state) { | |||
case FIND_OBJ_DATA: | |||
if (gc.obj_id_found) { | |||
// handle found data page - | |||
// find out corresponding obj ix page and load it to memory | |||
spiffs_page_header p_hdr; | |||
spiffs_page_ix objix_pix; | |||
gc.stored_scan_entry_index = cur_entry; // push cursor | |||
cur_entry = 0; // restart scan from start | |||
gc.state = MOVE_OBJ_DATA; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); | |||
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix); | |||
res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// on borked systems we might get an ERR_NOT_FOUND here - | |||
// this is handled by simply deleting the page as it is not referenced | |||
// from anywhere | |||
SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix); | |||
res = spiffs_page_delete(fs, gc.cur_data_pix); | |||
SPIFFS_CHECK_RES(res); | |||
// then we restore states and continue scanning for data pages | |||
cur_entry = gc.stored_scan_entry_index; // pop cursor | |||
gc.state = FIND_OBJ_DATA; | |||
break; // done | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix); | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); | |||
SPIFFS_CHECK_RES(res); | |||
// cannot allow a gc if the presumed index in fact is no index, a | |||
// check must run or lot of data may be lost | |||
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); | |||
gc.cur_objix_pix = objix_pix; | |||
} else { | |||
// no more data pages found, passed thru all block, start evacuating object indices | |||
gc.state = MOVE_OBJ_IX; | |||
cur_entry = 0; // restart entry scan index | |||
} | |||
break; | |||
case MOVE_OBJ_DATA: { | |||
// store modified objix (hdr) page residing in memory now that all | |||
// data pages belonging to this object index and residing in the block | |||
// we want to evacuate | |||
spiffs_page_ix new_objix_pix; | |||
gc.state = FIND_OBJ_DATA; | |||
cur_entry = gc.stored_scan_entry_index; // pop cursor | |||
if (gc.cur_objix_spix == 0) { | |||
// store object index header page | |||
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0); | |||
SPIFFS_CHECK_RES(res); | |||
} else { | |||
// store object index page | |||
res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, | |||
SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); | |||
} | |||
} | |||
break; | |||
case MOVE_OBJ_IX: | |||
// scanned thru all block, no more object indices found - our work here is done | |||
gc.state = FINISHED; | |||
break; | |||
default: | |||
cur_entry = 0; | |||
break; | |||
} // switch gc.state | |||
SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state); | |||
} // while state != FINISHED | |||
return res; | |||
} | |||
#endif // !SPIFFS_READ_ONLY |
@@ -0,0 +1,797 @@ | |||
/* | |||
* spiffs_nucleus.h | |||
* | |||
* Created on: Jun 15, 2013 | |||
* Author: petera | |||
*/ | |||
/* SPIFFS layout | |||
* | |||
* spiffs is designed for following spi flash characteristics: | |||
* - only big areas of data (blocks) can be erased | |||
* - erasing resets all bits in a block to ones | |||
* - writing pulls ones to zeroes | |||
* - zeroes cannot be pulled to ones, without erase | |||
* - wear leveling | |||
* | |||
* spiffs is also meant to be run on embedded, memory constraint devices. | |||
* | |||
* Entire area is divided in blocks. Entire area is also divided in pages. | |||
* Each block contains same number of pages. A page cannot be erased, but a | |||
* block can be erased. | |||
* | |||
* Entire area must be block_size * x | |||
* page_size must be block_size / (2^y) where y > 2 | |||
* | |||
* ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes | |||
* | |||
* BLOCK 0 PAGE 0 object lookup 1 | |||
* PAGE 1 object lookup 2 | |||
* ... | |||
* PAGE n-1 object lookup n | |||
* PAGE n object data 1 | |||
* PAGE n+1 object data 2 | |||
* ... | |||
* PAGE n+m-1 object data m | |||
* | |||
* BLOCK 1 PAGE n+m object lookup 1 | |||
* PAGE n+m+1 object lookup 2 | |||
* ... | |||
* PAGE 2n+m-1 object lookup n | |||
* PAGE 2n+m object data 1 | |||
* PAGE 2n+m object data 2 | |||
* ... | |||
* PAGE 2n+2m-1 object data m | |||
* ... | |||
* | |||
* n is number of object lookup pages, which is number of pages needed to index all pages | |||
* in a block by object id | |||
* : block_size / page_size * sizeof(obj_id) / page_size | |||
* m is number data pages, which is number of pages in block minus number of lookup pages | |||
* : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size | |||
* thus, n+m is total number of pages in a block | |||
* : block_size / page_size | |||
* | |||
* ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 | |||
* | |||
* Object lookup pages contain object id entries. Each entry represent the corresponding | |||
* data page. | |||
* Assuming a 16 bit object id, an object id being 0xffff represents a free page. | |||
* An object id being 0x0000 represents a deleted page. | |||
* | |||
* ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. | |||
* page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. | |||
* page 2 : data : data for object id 0008 | |||
* page 3 : data : data for object id 0001 | |||
* page 4 : data : data for object id 0aaa | |||
* ... | |||
* | |||
* | |||
* Object data pages can be either object index pages or object content. | |||
* All object data pages contains a data page header, containing object id and span index. | |||
* The span index denotes the object page ordering amongst data pages with same object id. | |||
* This applies to both object index pages (when index spans more than one page of entries), | |||
* and object data pages. | |||
* An object index page contains page entries pointing to object content page. The entry index | |||
* in a object index page correlates to the span index in the actual object data page. | |||
* The first object index page (span index 0) is called object index header page, and also | |||
* contains object flags (directory/file), size, object name etc. | |||
* | |||
* ex: | |||
* BLOCK 1 | |||
* PAGE 256: objectl lookup page 1 | |||
* [*123] [ 123] [ 123] [ 123] | |||
* [ 123] [*123] [ 123] [ 123] | |||
* [free] [free] [free] [free] ... | |||
* PAGE 257: objectl lookup page 2 | |||
* [free] [free] [free] [free] ... | |||
* PAGE 258: object index page (header) | |||
* obj.id:0123 span.ix:0000 flags:INDEX | |||
* size:1600 name:ex.txt type:file | |||
* [259] [260] [261] [262] | |||
* PAGE 259: object data page | |||
* obj.id:0123 span.ix:0000 flags:DATA | |||
* PAGE 260: object data page | |||
* obj.id:0123 span.ix:0001 flags:DATA | |||
* PAGE 261: object data page | |||
* obj.id:0123 span.ix:0002 flags:DATA | |||
* PAGE 262: object data page | |||
* obj.id:0123 span.ix:0003 flags:DATA | |||
* PAGE 263: object index page | |||
* obj.id:0123 span.ix:0001 flags:INDEX | |||
* [264] [265] [fre] [fre] | |||
* [fre] [fre] [fre] [fre] | |||
* PAGE 264: object data page | |||
* obj.id:0123 span.ix:0004 flags:DATA | |||
* PAGE 265: object data page | |||
* obj.id:0123 span.ix:0005 flags:DATA | |||
* | |||
*/ | |||
#ifndef SPIFFS_NUCLEUS_H_ | |||
#define SPIFFS_NUCLEUS_H_ | |||
#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1) | |||
#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1) | |||
#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2) | |||
#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3) | |||
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4) | |||
// visitor result, continue searching | |||
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20) | |||
// visitor result, continue searching after reloading lu buffer | |||
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21) | |||
// visitor result, stop searching | |||
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22) | |||
// updating an object index contents | |||
#define SPIFFS_EV_IX_UPD (0) | |||
// creating a new object index | |||
#define SPIFFS_EV_IX_NEW (1) | |||
// deleting an object index | |||
#define SPIFFS_EV_IX_DEL (2) | |||
// moving an object index without updating contents | |||
#define SPIFFS_EV_IX_MOV (3) | |||
// updating an object index header data only, not the table itself | |||
#define SPIFFS_EV_IX_UPD_HDR (4) | |||
#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) | |||
#define SPIFFS_UNDEFINED_LEN (u32_t)(-1) | |||
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) | |||
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) | |||
#if SPIFFS_USE_MAGIC | |||
#if !SPIFFS_USE_MAGIC_LENGTH | |||
#define SPIFFS_MAGIC(fs, bix) \ | |||
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) | |||
#else // SPIFFS_USE_MAGIC_LENGTH | |||
#define SPIFFS_MAGIC(fs, bix) \ | |||
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) | |||
#endif // SPIFFS_USE_MAGIC_LENGTH | |||
#endif // SPIFFS_USE_MAGIC | |||
#define SPIFFS_CONFIG_MAGIC (0x20090315) | |||
#if SPIFFS_SINGLETON == 0 | |||
#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ | |||
((fs)->cfg.log_page_size) | |||
#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ | |||
((fs)->cfg.log_block_size) | |||
#define SPIFFS_CFG_PHYS_SZ(fs) \ | |||
((fs)->cfg.phys_size) | |||
#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ | |||
((fs)->cfg.phys_erase_block) | |||
#define SPIFFS_CFG_PHYS_ADDR(fs) \ | |||
((fs)->cfg.phys_addr) | |||
#endif | |||
// total number of pages | |||
#define SPIFFS_MAX_PAGES(fs) \ | |||
( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// total number of pages per block, including object lookup pages | |||
#define SPIFFS_PAGES_PER_BLOCK(fs) \ | |||
( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// number of object lookup pages per block | |||
#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ | |||
(MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) | |||
// checks if page index belongs to object lookup | |||
#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ | |||
(((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) | |||
// number of object lookup entries in all object lookup pages | |||
#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ | |||
(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) | |||
// converts a block to physical address | |||
#define SPIFFS_BLOCK_TO_PADDR(fs, block) \ | |||
( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) | |||
// converts a object lookup entry to page index | |||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ | |||
((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) | |||
// converts a object lookup entry to physical address of corresponding page | |||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ | |||
(SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// converts a page to physical address | |||
#define SPIFFS_PAGE_TO_PADDR(fs, page) \ | |||
( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// converts a physical address to page | |||
#define SPIFFS_PADDR_TO_PAGE(fs, addr) \ | |||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// gives index in page for a physical address | |||
#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ | |||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// returns containing block for given page | |||
#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ | |||
( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) | |||
// returns starting page for block | |||
#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ | |||
( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) | |||
// converts page to entry in object lookup page | |||
#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ | |||
( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) | |||
// returns data size in a data page | |||
#define SPIFFS_DATA_PAGE_SIZE(fs) \ | |||
( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) | |||
// returns physical address for block's erase count, | |||
// always in the physical last entry of the last object lookup page | |||
#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ | |||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) | |||
// returns physical address for block's magic, | |||
// always in the physical second last entry of the last object lookup page | |||
#define SPIFFS_MAGIC_PADDR(fs, bix) \ | |||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) | |||
// checks if there is any room for magic in the object luts | |||
#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \ | |||
( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ | |||
<= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) | |||
// define helpers object | |||
// entries in an object header page index | |||
#define SPIFFS_OBJ_HDR_IX_LEN(fs) \ | |||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) | |||
// entries in an object page index | |||
#define SPIFFS_OBJ_IX_LEN(fs) \ | |||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) | |||
// object index entry for given data span index | |||
#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ | |||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) | |||
// object index span index number for given data span index or entry | |||
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ | |||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) | |||
// get data span index for object index span index | |||
#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ | |||
( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) | |||
#define SPIFFS_OP_T_OBJ_LU (0<<0) | |||
#define SPIFFS_OP_T_OBJ_LU2 (1<<0) | |||
#define SPIFFS_OP_T_OBJ_IX (2<<0) | |||
#define SPIFFS_OP_T_OBJ_DA (3<<0) | |||
#define SPIFFS_OP_C_DELE (0<<2) | |||
#define SPIFFS_OP_C_UPDT (1<<2) | |||
#define SPIFFS_OP_C_MOVS (2<<2) | |||
#define SPIFFS_OP_C_MOVD (3<<2) | |||
#define SPIFFS_OP_C_FLSH (4<<2) | |||
#define SPIFFS_OP_C_READ (5<<2) | |||
#define SPIFFS_OP_C_WRTHRU (6<<2) | |||
#define SPIFFS_OP_TYPE_MASK (3<<0) | |||
#define SPIFFS_OP_COM_MASK (7<<2) | |||
// if 0, this page is written to, else clean | |||
#define SPIFFS_PH_FLAG_USED (1<<0) | |||
// if 0, writing is finalized, else under modification | |||
#define SPIFFS_PH_FLAG_FINAL (1<<1) | |||
// if 0, this is an index page, else a data page | |||
#define SPIFFS_PH_FLAG_INDEX (1<<2) | |||
// if 0, page is deleted, else valid | |||
#define SPIFFS_PH_FLAG_DELET (1<<7) | |||
// if 0, this index header is being deleted | |||
#define SPIFFS_PH_FLAG_IXDELE (1<<6) | |||
#define SPIFFS_CHECK_MOUNT(fs) \ | |||
((fs)->mounted != 0) | |||
#define SPIFFS_CHECK_CFG(fs) \ | |||
((fs)->config_magic == SPIFFS_CONFIG_MAGIC) | |||
#define SPIFFS_CHECK_RES(res) \ | |||
do { \ | |||
if ((res) < SPIFFS_OK) return (res); \ | |||
} while (0); | |||
#define SPIFFS_API_CHECK_MOUNT(fs) \ | |||
if (!SPIFFS_CHECK_MOUNT((fs))) { \ | |||
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ | |||
return SPIFFS_ERR_NOT_MOUNTED; \ | |||
} | |||
#define SPIFFS_API_CHECK_CFG(fs) \ | |||
if (!SPIFFS_CHECK_CFG((fs))) { \ | |||
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ | |||
return SPIFFS_ERR_NOT_CONFIGURED; \ | |||
} | |||
#define SPIFFS_API_CHECK_RES(fs, res) \ | |||
if ((res) < SPIFFS_OK) { \ | |||
(fs)->err_code = (res); \ | |||
return (res); \ | |||
} | |||
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ | |||
if ((res) < SPIFFS_OK) { \ | |||
(fs)->err_code = (res); \ | |||
SPIFFS_UNLOCK(fs); \ | |||
return (res); \ | |||
} | |||
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ | |||
if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \ | |||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH; | |||
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; | |||
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \ | |||
if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \ | |||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; | |||
// check id, only visit matching objec ids | |||
#define SPIFFS_VIS_CHECK_ID (1<<0) | |||
// report argument object id to visitor - else object lookup id is reported | |||
#define SPIFFS_VIS_CHECK_PH (1<<1) | |||
// stop searching at end of all look up pages | |||
#define SPIFFS_VIS_NO_WRAP (1<<2) | |||
#if SPIFFS_HAL_CALLBACK_EXTRA | |||
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ | |||
(_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) | |||
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ | |||
(_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) | |||
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ | |||
(_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) | |||
#else // SPIFFS_HAL_CALLBACK_EXTRA | |||
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ | |||
(_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) | |||
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ | |||
(_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) | |||
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ | |||
(_fs)->cfg.hal_erase_f((_paddr), (_len)) | |||
#endif // SPIFFS_HAL_CALLBACK_EXTRA | |||
#if SPIFFS_CACHE | |||
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0) | |||
#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1) | |||
#define SPIFFS_CACHE_FLAG_OBJLU (1<<2) | |||
#define SPIFFS_CACHE_FLAG_OBJIX (1<<3) | |||
#define SPIFFS_CACHE_FLAG_DATA (1<<4) | |||
#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) | |||
#define SPIFFS_CACHE_PAGE_SIZE(fs) \ | |||
(sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) | |||
#define spiffs_get_cache(fs) \ | |||
((spiffs_cache *)((fs)->cache)) | |||
#define spiffs_get_cache_page_hdr(fs, c, ix) \ | |||
((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) | |||
#define spiffs_get_cache_page(fs, c, ix) \ | |||
((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) | |||
// cache page struct | |||
typedef struct { | |||
// cache flags | |||
u8_t flags; | |||
// cache page index | |||
u8_t ix; | |||
// last access of this cache page | |||
u32_t last_access; | |||
union { | |||
// type read cache | |||
struct { | |||
// read cache page index | |||
spiffs_page_ix pix; | |||
}; | |||
#if SPIFFS_CACHE_WR | |||
// type write cache | |||
struct { | |||
// write cache | |||
spiffs_obj_id obj_id; | |||
// offset in cache page | |||
u32_t offset; | |||
// size of cache page | |||
u16_t size; | |||
}; | |||
#endif | |||
}; | |||
} spiffs_cache_page; | |||
// cache struct | |||
typedef struct { | |||
u8_t cpage_count; | |||
u32_t last_access; | |||
u32_t cpage_use_map; | |||
u32_t cpage_use_mask; | |||
u8_t *cpages; | |||
} spiffs_cache; | |||
#endif | |||
// spiffs nucleus file descriptor | |||
typedef struct { | |||
// the filesystem of this descriptor | |||
spiffs *fs; | |||
// number of file descriptor - if 0, the file descriptor is closed | |||
spiffs_file file_nbr; | |||
// object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted | |||
spiffs_obj_id obj_id; | |||
// size of the file | |||
u32_t size; | |||
// cached object index header page index | |||
spiffs_page_ix objix_hdr_pix; | |||
// cached offset object index page index | |||
spiffs_page_ix cursor_objix_pix; | |||
// cached offset object index span index | |||
spiffs_span_ix cursor_objix_spix; | |||
// current absolute offset | |||
u32_t offset; | |||
// current file descriptor offset | |||
u32_t fdoffset; | |||
// fd flags | |||
spiffs_flags flags; | |||
#if SPIFFS_CACHE_WR | |||
spiffs_cache_page *cache_page; | |||
#endif | |||
#if SPIFFS_TEMPORAL_FD_CACHE | |||
// djb2 hash of filename | |||
u32_t name_hash; | |||
// hit score (score == 0 indicates never used fd) | |||
u16_t score; | |||
#endif | |||
#if SPIFFS_IX_MAP | |||
// spiffs index map, if 0 it means unmapped | |||
spiffs_ix_map *ix_map; | |||
#endif | |||
} spiffs_fd; | |||
// object structs | |||
// page header, part of each page except object lookup pages | |||
// NB: this is always aligned when the data page is an object index, | |||
// as in this case struct spiffs_page_object_ix is used | |||
typedef struct __attribute(( packed )) { | |||
// object id | |||
spiffs_obj_id obj_id; | |||
// object span index | |||
spiffs_span_ix span_ix; | |||
// flags | |||
u8_t flags; | |||
} spiffs_page_header; | |||
// object index header page header | |||
typedef struct __attribute(( packed )) | |||
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES | |||
__attribute(( aligned(sizeof(spiffs_page_ix)) )) | |||
#endif | |||
{ | |||
// common page header | |||
spiffs_page_header p_hdr; | |||
// alignment | |||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; | |||
// size of object | |||
u32_t size; | |||
// type of object | |||
spiffs_obj_type type; | |||
// name of object | |||
u8_t name[SPIFFS_OBJ_NAME_LEN]; | |||
#if SPIFFS_OBJ_META_LEN | |||
// metadata. not interpreted by SPIFFS in any way. | |||
u8_t meta[SPIFFS_OBJ_META_LEN]; | |||
#endif | |||
} spiffs_page_object_ix_header; | |||
// object index page header | |||
typedef struct __attribute(( packed )) { | |||
spiffs_page_header p_hdr; | |||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; | |||
} spiffs_page_object_ix; | |||
// callback func for object lookup visitor | |||
typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, | |||
const void *user_const_p, void *user_var_p); | |||
#if SPIFFS_CACHE | |||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \ | |||
spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst)) | |||
#define _spiffs_wr(fs, op, fh, addr, len, src) \ | |||
spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src)) | |||
#else | |||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \ | |||
spiffs_phys_rd((fs), (addr), (len), (dst)) | |||
#define _spiffs_wr(fs, op, fh, addr, len, src) \ | |||
spiffs_phys_wr((fs), (addr), (len), (src)) | |||
#endif | |||
#ifndef MIN | |||
#define MIN(a,b) ((a) < (b) ? (a) : (b)) | |||
#endif | |||
#ifndef MAX | |||
#define MAX(a,b) ((a) > (b) ? (a) : (b)) | |||
#endif | |||
// --------------- | |||
s32_t spiffs_phys_rd( | |||
spiffs *fs, | |||
#if SPIFFS_CACHE | |||
u8_t op, | |||
spiffs_file fh, | |||
#endif | |||
u32_t addr, | |||
u32_t len, | |||
u8_t *dst); | |||
s32_t spiffs_phys_wr( | |||
spiffs *fs, | |||
#if SPIFFS_CACHE | |||
u8_t op, | |||
spiffs_file fh, | |||
#endif | |||
u32_t addr, | |||
u32_t len, | |||
u8_t *src); | |||
s32_t spiffs_phys_cpy( | |||
spiffs *fs, | |||
spiffs_file fh, | |||
u32_t dst, | |||
u32_t src, | |||
u32_t len); | |||
s32_t spiffs_phys_count_free_blocks( | |||
spiffs *fs); | |||
s32_t spiffs_obj_lu_find_entry_visitor( | |||
spiffs *fs, | |||
spiffs_block_ix starting_block, | |||
int starting_lu_entry, | |||
u8_t flags, | |||
spiffs_obj_id obj_id, | |||
spiffs_visitor_f v, | |||
const void *user_const_p, | |||
void *user_var_p, | |||
spiffs_block_ix *block_ix, | |||
int *lu_entry); | |||
s32_t spiffs_erase_block( | |||
spiffs *fs, | |||
spiffs_block_ix bix); | |||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH | |||
s32_t spiffs_probe( | |||
spiffs_config *cfg); | |||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH | |||
// --------------- | |||
s32_t spiffs_obj_lu_scan( | |||
spiffs *fs); | |||
s32_t spiffs_obj_lu_find_free_obj_id( | |||
spiffs *fs, | |||
spiffs_obj_id *obj_id, | |||
const u8_t *conflicting_name); | |||
s32_t spiffs_obj_lu_find_free( | |||
spiffs *fs, | |||
spiffs_block_ix starting_block, | |||
int starting_lu_entry, | |||
spiffs_block_ix *block_ix, | |||
int *lu_entry); | |||
s32_t spiffs_obj_lu_find_id( | |||
spiffs *fs, | |||
spiffs_block_ix starting_block, | |||
int starting_lu_entry, | |||
spiffs_obj_id obj_id, | |||
spiffs_block_ix *block_ix, | |||
int *lu_entry); | |||
s32_t spiffs_obj_lu_find_id_and_span( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_span_ix spix, | |||
spiffs_page_ix exclusion_pix, | |||
spiffs_page_ix *pix); | |||
s32_t spiffs_obj_lu_find_id_and_span_by_phdr( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_span_ix spix, | |||
spiffs_page_ix exclusion_pix, | |||
spiffs_page_ix *pix); | |||
// --------------- | |||
s32_t spiffs_page_allocate_data( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_page_header *ph, | |||
u8_t *data, | |||
u32_t len, | |||
u32_t page_offs, | |||
u8_t finalize, | |||
spiffs_page_ix *pix); | |||
s32_t spiffs_page_move( | |||
spiffs *fs, | |||
spiffs_file fh, | |||
u8_t *page_data, | |||
spiffs_obj_id obj_id, | |||
spiffs_page_header *page_hdr, | |||
spiffs_page_ix src_pix, | |||
spiffs_page_ix *dst_pix); | |||
s32_t spiffs_page_delete( | |||
spiffs *fs, | |||
spiffs_page_ix pix); | |||
// --------------- | |||
s32_t spiffs_object_create( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
const u8_t name[], | |||
const u8_t meta[], | |||
spiffs_obj_type type, | |||
spiffs_page_ix *objix_hdr_pix); | |||
s32_t spiffs_object_update_index_hdr( | |||
spiffs *fs, | |||
spiffs_fd *fd, | |||
spiffs_obj_id obj_id, | |||
spiffs_page_ix objix_hdr_pix, | |||
u8_t *new_objix_hdr_data, | |||
const u8_t name[], | |||
const u8_t meta[], | |||
u32_t size, | |||
spiffs_page_ix *new_pix); | |||
#if SPIFFS_IX_MAP | |||
s32_t spiffs_populate_ix_map( | |||
spiffs *fs, | |||
spiffs_fd *fd, | |||
u32_t vec_entry_start, | |||
u32_t vec_entry_end); | |||
#endif | |||
void spiffs_cb_object_event( | |||
spiffs *fs, | |||
spiffs_page_object_ix *objix, | |||
int ev, | |||
spiffs_obj_id obj_id, | |||
spiffs_span_ix spix, | |||
spiffs_page_ix new_pix, | |||
u32_t new_size); | |||
s32_t spiffs_object_open_by_id( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_fd *f, | |||
spiffs_flags flags, | |||
spiffs_mode mode); | |||
s32_t spiffs_object_open_by_page( | |||
spiffs *fs, | |||
spiffs_page_ix pix, | |||
spiffs_fd *f, | |||
spiffs_flags flags, | |||
spiffs_mode mode); | |||
s32_t spiffs_object_append( | |||
spiffs_fd *fd, | |||
u32_t offset, | |||
u8_t *data, | |||
u32_t len); | |||
s32_t spiffs_object_modify( | |||
spiffs_fd *fd, | |||
u32_t offset, | |||
u8_t *data, | |||
u32_t len); | |||
s32_t spiffs_object_read( | |||
spiffs_fd *fd, | |||
u32_t offset, | |||
u32_t len, | |||
u8_t *dst); | |||
s32_t spiffs_object_truncate( | |||
spiffs_fd *fd, | |||
u32_t new_len, | |||
u8_t remove_object); | |||
s32_t spiffs_object_find_object_index_header_by_name( | |||
spiffs *fs, | |||
const u8_t name[SPIFFS_OBJ_NAME_LEN], | |||
spiffs_page_ix *pix); | |||
// --------------- | |||
s32_t spiffs_gc_check( | |||
spiffs *fs, | |||
u32_t len); | |||
s32_t spiffs_gc_erase_page_stats( | |||
spiffs *fs, | |||
spiffs_block_ix bix); | |||
s32_t spiffs_gc_find_candidate( | |||
spiffs *fs, | |||
spiffs_block_ix **block_candidate, | |||
int *candidate_count, | |||
char fs_crammed); | |||
s32_t spiffs_gc_clean( | |||
spiffs *fs, | |||
spiffs_block_ix bix); | |||
s32_t spiffs_gc_quick( | |||
spiffs *fs, u16_t max_free_pages); | |||
// --------------- | |||
s32_t spiffs_fd_find_new( | |||
spiffs *fs, | |||
spiffs_fd **fd, | |||
const char *name); | |||
s32_t spiffs_fd_return( | |||
spiffs *fs, | |||
spiffs_file f); | |||
s32_t spiffs_fd_get( | |||
spiffs *fs, | |||
spiffs_file f, | |||
spiffs_fd **fd); | |||
#if SPIFFS_TEMPORAL_FD_CACHE | |||
void spiffs_fd_temporal_cache_rehash( | |||
spiffs *fs, | |||
const char *old_path, | |||
const char *new_path); | |||
#endif | |||
#if SPIFFS_CACHE | |||
void spiffs_cache_init( | |||
spiffs *fs); | |||
void spiffs_cache_drop_page( | |||
spiffs *fs, | |||
spiffs_page_ix pix); | |||
#if SPIFFS_CACHE_WR | |||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd( | |||
spiffs *fs, | |||
spiffs_fd *fd); | |||
void spiffs_cache_fd_release( | |||
spiffs *fs, | |||
spiffs_cache_page *cp); | |||
spiffs_cache_page *spiffs_cache_page_get_by_fd( | |||
spiffs *fs, | |||
spiffs_fd *fd); | |||
#endif | |||
#endif | |||
s32_t spiffs_lookup_consistency_check( | |||
spiffs *fs, | |||
u8_t check_all_objects); | |||
s32_t spiffs_page_consistency_check( | |||
spiffs *fs); | |||
s32_t spiffs_object_index_consistency_check( | |||
spiffs *fs); | |||
#endif /* SPIFFS_NUCLEUS_H_ */ |
@@ -0,0 +1,692 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: Arg.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_ARGUMENT_H | |||
#define TCLAP_ARGUMENT_H | |||
#ifdef HAVE_CONFIG_H | |||
#include <config.h> | |||
#else | |||
#define HAVE_SSTREAM | |||
#endif | |||
#include <string> | |||
#include <vector> | |||
#include <list> | |||
#include <iostream> | |||
#include <iomanip> | |||
#include <cstdio> | |||
#if defined(HAVE_SSTREAM) | |||
#include <sstream> | |||
typedef std::istringstream istringstream; | |||
#elif defined(HAVE_STRSTREAM) | |||
#include <strstream> | |||
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<Arg*>& 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<std::string>& 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<Arg*>::iterator ArgListIterator; | |||
/** | |||
* Typedef of an Arg vector iterator. | |||
*/ | |||
typedef std::vector<Arg*>::iterator ArgVectorIterator; | |||
/** | |||
* Typedef of a Visitor list iterator. | |||
*/ | |||
typedef std::list<Visitor*>::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<typename T> void | |||
ExtractValue(T &destVal, const std::string& strVal, ValueLike vl) | |||
{ | |||
static_cast<void>(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<typename T> void | |||
ExtractValue(T &destVal, const std::string& strVal, StringLike sl) | |||
{ | |||
static_cast<void>(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<unsigned int>(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<unsigned int>(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<Arg*>& argList ) const | |||
{ | |||
argList.push_front( const_cast<Arg*>(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 | |||
@@ -0,0 +1,200 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: ArgException.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_ARG_EXCEPTION_H | |||
#define TCLAP_ARG_EXCEPTION_H | |||
#include <string> | |||
#include <exception> | |||
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 | |||
@@ -0,0 +1,87 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: ArgTraits.h | |||
* | |||
* Copyright (c) 2007, Daniel Aarno, Michael E. Smoot . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
// This is an internal tclap file, you should probably not have to | |||
// include this directly | |||
#ifndef TCLAP_ARGTRAITS_H | |||
#define TCLAP_ARGTRAITS_H | |||
namespace TCLAP { | |||
// We use two empty structs to get compile type specialization | |||
// function to work | |||
/** | |||
* A value like argument value type is a value that can be set using | |||
* operator>>. This is the default value type. | |||
*/ | |||
struct ValueLike { | |||
typedef ValueLike ValueCategory; | |||
virtual ~ValueLike() {} | |||
}; | |||
/** | |||
* A string like argument value type is a value that can be set using | |||
* operator=(string). Usefull if the value type contains spaces which | |||
* will be broken up into individual tokens by operator>>. | |||
*/ | |||
struct StringLike { | |||
virtual ~StringLike() {} | |||
}; | |||
/** | |||
* A class can inherit from this object to make it have string like | |||
* traits. This is a compile time thing and does not add any overhead | |||
* to the inherenting class. | |||
*/ | |||
struct StringLikeTrait { | |||
typedef StringLike ValueCategory; | |||
virtual ~StringLikeTrait() {} | |||
}; | |||
/** | |||
* A class can inherit from this object to make it have value like | |||
* traits. This is a compile time thing and does not add any overhead | |||
* to the inherenting class. | |||
*/ | |||
struct ValueLikeTrait { | |||
typedef ValueLike ValueCategory; | |||
virtual ~ValueLikeTrait() {} | |||
}; | |||
/** | |||
* Arg traits are used to get compile type specialization when parsing | |||
* argument values. Using an ArgTraits you can specify the way that | |||
* values gets assigned to any particular type during parsing. The two | |||
* supported types are StringLike and ValueLike. | |||
*/ | |||
template<typename T> | |||
struct ArgTraits { | |||
typedef typename T::ValueCategory ValueCategory; | |||
virtual ~ArgTraits() {} | |||
//typedef ValueLike ValueCategory; | |||
}; | |||
#endif | |||
} // namespace |
@@ -0,0 +1,25 @@ | |||
Copyright (c) 2003 Michael E. Smoot | |||
Permission is hereby granted, free of charge, to any person | |||
obtaining a copy of this software and associated documentation | |||
files (the "Software"), to deal in the Software without restriction, | |||
including without limitation the rights to use, copy, modify, merge, | |||
publish, distribute, sublicense, and/or sell copies of the Software, | |||
and to permit persons to whom the Software is furnished to do so, | |||
subject to the following conditions: | |||
The above copyright notice and this permission notice shall be | |||
included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | |||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR | |||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. | |||
@@ -0,0 +1,633 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: CmdLine.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_CMDLINE_H | |||
#define TCLAP_CMDLINE_H | |||
#include "SwitchArg.h" | |||
#include "MultiSwitchArg.h" | |||
#include "UnlabeledValueArg.h" | |||
#include "UnlabeledMultiArg.h" | |||
#include "XorHandler.h" | |||
#include "HelpVisitor.h" | |||
#include "VersionVisitor.h" | |||
#include "IgnoreRestVisitor.h" | |||
#include "CmdLineOutput.h" | |||
#include "StdOutput.h" | |||
#include "Constraint.h" | |||
#include "ValuesConstraint.h" | |||
#include <string> | |||
#include <vector> | |||
#include <list> | |||
#include <iostream> | |||
#include <iomanip> | |||
#include <algorithm> | |||
#include <stdlib.h> // Needed for exit(), which isn't defined in some envs. | |||
namespace TCLAP { | |||
template<typename T> void DelPtr(T ptr) | |||
{ | |||
delete ptr; | |||
} | |||
template<typename C> void ClearContainer(C &c) | |||
{ | |||
typedef typename C::value_type value_type; | |||
std::for_each(c.begin(), c.end(), DelPtr<value_type>); | |||
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<Arg*> _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<Arg*> _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<Visitor*> _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<Arg*>& 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<std::string>& args); | |||
/** | |||
* | |||
*/ | |||
CmdLineOutput* getOutput(); | |||
/** | |||
* | |||
*/ | |||
void setOutput(CmdLineOutput* co); | |||
/** | |||
* | |||
*/ | |||
std::string& getVersion(); | |||
/** | |||
* | |||
*/ | |||
std::string& getProgramName(); | |||
/** | |||
* | |||
*/ | |||
std::list<Arg*>& 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<Arg*>()), | |||
_progName("not_set_yet"), | |||
_message(m), | |||
_version(v), | |||
_numRequired(0), | |||
_delimiter(delim), | |||
_xorHandler(XorHandler()), | |||
_argDeleteOnExitList(std::list<Arg*>()), | |||
_visitorDeleteOnExitList(std::list<Visitor*>()), | |||
_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<Arg*>& 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<Arg*> 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<std::string> args; | |||
for (int i = 0; i < argc; i++) | |||
args.push_back(argv[i]); | |||
parse(args); | |||
} | |||
inline void CmdLine::parse(std::vector<std::string>& args) | |||
{ | |||
bool shouldExit = false; | |||
int estat = 0; | |||
try { | |||
_progName = args.front(); | |||
args.erase(args.begin()); | |||
int requiredCount = 0; | |||
for (int i = 0; static_cast<unsigned int>(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<unsigned int>(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<Arg*>& 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 |
@@ -0,0 +1,150 @@ | |||
/****************************************************************************** | |||
* | |||
* file: CmdLineInterface.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_COMMANDLINE_INTERFACE_H | |||
#define TCLAP_COMMANDLINE_INTERFACE_H | |||
#include <string> | |||
#include <vector> | |||
#include <list> | |||
#include <iostream> | |||
#include <algorithm> | |||
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<Arg*>& 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<std::string>& 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<Arg*>& 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 |
@@ -0,0 +1,74 @@ | |||
/****************************************************************************** | |||
* | |||
* file: CmdLineOutput.h | |||
* | |||
* Copyright (c) 2004, Michael E. Smoot | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_CMDLINEOUTPUT_H | |||
#define TCLAP_CMDLINEOUTPUT_H | |||
#include <string> | |||
#include <vector> | |||
#include <list> | |||
#include <iostream> | |||
#include <iomanip> | |||
#include <algorithm> | |||
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 |
@@ -0,0 +1,68 @@ | |||
/****************************************************************************** | |||
* | |||
* file: Constraint.h | |||
* | |||
* Copyright (c) 2005, Michael E. Smoot | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_CONSTRAINT_H | |||
#define TCLAP_CONSTRAINT_H | |||
#include <string> | |||
#include <vector> | |||
#include <list> | |||
#include <iostream> | |||
#include <iomanip> | |||
#include <algorithm> | |||
namespace TCLAP { | |||
/** | |||
* The interface that defines the interaction between the Arg and Constraint. | |||
*/ | |||
template<class T> | |||
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 |
@@ -0,0 +1,299 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: DocBookOutput.h | |||
* | |||
* Copyright (c) 2004, Michael E. Smoot | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_DOCBOOKOUTPUT_H | |||
#define TCLAP_DOCBOOKOUTPUT_H | |||
#include <string> | |||
#include <vector> | |||
#include <list> | |||
#include <iostream> | |||
#include <algorithm> | |||
#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<Arg*> argList = _cmd.getArgList(); | |||
std::string progName = _cmd.getProgramName(); | |||
std::string xversion = _cmd.getVersion(); | |||
theDelimiter = _cmd.getDelimiter(); | |||
XorHandler xorHandler = _cmd.getXorHandler(); | |||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList(); | |||
basename(progName); | |||
std::cout << "<?xml version='1.0'?>" << std::endl; | |||
std::cout << "<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\"" << std::endl; | |||
std::cout << "\t\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\">" << std::endl << std::endl; | |||
std::cout << "<refentry>" << std::endl; | |||
std::cout << "<refmeta>" << std::endl; | |||
std::cout << "<refentrytitle>" << progName << "</refentrytitle>" << std::endl; | |||
std::cout << "<manvolnum>1</manvolnum>" << std::endl; | |||
std::cout << "</refmeta>" << std::endl; | |||
std::cout << "<refnamediv>" << std::endl; | |||
std::cout << "<refname>" << progName << "</refname>" << std::endl; | |||
std::cout << "<refpurpose>" << _cmd.getMessage() << "</refpurpose>" << std::endl; | |||
std::cout << "</refnamediv>" << std::endl; | |||
std::cout << "<refsynopsisdiv>" << std::endl; | |||
std::cout << "<cmdsynopsis>" << std::endl; | |||
std::cout << "<command>" << progName << "</command>" << std::endl; | |||
// xor | |||
for ( int i = 0; (unsigned int)i < xorList.size(); i++ ) | |||
{ | |||
std::cout << "<group choice='req'>" << std::endl; | |||
for ( ArgVectorIterator it = xorList[i].begin(); | |||
it != xorList[i].end(); it++ ) | |||
printShortArg((*it)); | |||
std::cout << "</group>" << std::endl; | |||
} | |||
// rest of args | |||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++) | |||
if ( !xorHandler.contains( (*it) ) ) | |||
printShortArg((*it)); | |||
std::cout << "</cmdsynopsis>" << std::endl; | |||
std::cout << "</refsynopsisdiv>" << std::endl; | |||
std::cout << "<refsect1>" << std::endl; | |||
std::cout << "<title>Description</title>" << std::endl; | |||
std::cout << "<para>" << std::endl; | |||
std::cout << _cmd.getMessage() << std::endl; | |||
std::cout << "</para>" << std::endl; | |||
std::cout << "</refsect1>" << std::endl; | |||
std::cout << "<refsect1>" << std::endl; | |||
std::cout << "<title>Options</title>" << std::endl; | |||
std::cout << "<variablelist>" << std::endl; | |||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++) | |||
printLongArg((*it)); | |||
std::cout << "</variablelist>" << std::endl; | |||
std::cout << "</refsect1>" << std::endl; | |||
std::cout << "<refsect1>" << std::endl; | |||
std::cout << "<title>Version</title>" << std::endl; | |||
std::cout << "<para>" << std::endl; | |||
std::cout << xversion << std::endl; | |||
std::cout << "</para>" << std::endl; | |||
std::cout << "</refsect1>" << std::endl; | |||
std::cout << "</refentry>" << std::endl; | |||
} | |||
inline void DocBookOutput::failure( CmdLineInterface& _cmd, | |||
ArgException& e ) | |||
{ | |||
static_cast<void>(_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 << "<arg choice='" << choice << '\''; | |||
if ( a->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 << "<replaceable>" << arg << "</replaceable>"; | |||
} | |||
std::cout << "</arg>" << 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 << "<varlistentry>" << std::endl; | |||
if ( !a->getFlag().empty() ) | |||
{ | |||
std::cout << "<term>" << std::endl; | |||
std::cout << "<option>"; | |||
std::cout << a->flagStartChar() << a->getFlag(); | |||
std::cout << "</option>" << std::endl; | |||
std::cout << "</term>" << std::endl; | |||
} | |||
std::cout << "<term>" << std::endl; | |||
std::cout << "<option>"; | |||
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 << "<replaceable>" << arg << "</replaceable>"; | |||
} | |||
std::cout << "</option>" << std::endl; | |||
std::cout << "</term>" << std::endl; | |||
std::cout << "<listitem>" << std::endl; | |||
std::cout << "<para>" << std::endl; | |||
std::cout << desc << std::endl; | |||
std::cout << "</para>" << std::endl; | |||
std::cout << "</listitem>" << std::endl; | |||
std::cout << "</varlistentry>" << std::endl; | |||
} | |||
} //namespace TCLAP | |||
#endif |
@@ -0,0 +1,76 @@ | |||
/****************************************************************************** | |||
* | |||
* file: HelpVisitor.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_HELP_VISITOR_H | |||
#define TCLAP_HELP_VISITOR_H | |||
#include "CmdLineInterface.h" | |||
#include "CmdLineOutput.h" | |||
#include "Visitor.h" | |||
namespace TCLAP { | |||
/** | |||
* A Visitor object that calls the usage method of the given CmdLineOutput | |||
* object for the specified CmdLine object. | |||
*/ | |||
class HelpVisitor: public Visitor | |||
{ | |||
private: | |||
/** | |||
* Prevent accidental copying. | |||
*/ | |||
HelpVisitor(const HelpVisitor& rhs); | |||
HelpVisitor& operator=(const HelpVisitor& rhs); | |||
protected: | |||
/** | |||
* The CmdLine the output will be generated for. | |||
*/ | |||
CmdLineInterface* _cmd; | |||
/** | |||
* The output object. | |||
*/ | |||
CmdLineOutput** _out; | |||
public: | |||
/** | |||
* Constructor. | |||
* \param cmd - The CmdLine the output will be generated for. | |||
* \param out - The type of output. | |||
*/ | |||
HelpVisitor(CmdLineInterface* cmd, CmdLineOutput** out) | |||
: Visitor(), _cmd( cmd ), _out( out ) { } | |||
/** | |||
* Calls the usage method of the CmdLineOutput for the | |||
* specified CmdLine. | |||
*/ | |||
void visit() { (*_out)->usage(*_cmd); throw ExitException(0); } | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,52 @@ | |||
/****************************************************************************** | |||
* | |||
* file: IgnoreRestVisitor.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_IGNORE_REST_VISITOR_H | |||
#define TCLAP_IGNORE_REST_VISITOR_H | |||
#include "Visitor.h" | |||
#include "Arg.h" | |||
namespace TCLAP { | |||
/** | |||
* A Vistor that tells the CmdLine to begin ignoring arguments after | |||
* this one is parsed. | |||
*/ | |||
class IgnoreRestVisitor: public Visitor | |||
{ | |||
public: | |||
/** | |||
* Constructor. | |||
*/ | |||
IgnoreRestVisitor() : Visitor() {} | |||
/** | |||
* Sets Arg::_ignoreRest. | |||
*/ | |||
void visit() { Arg::beginIgnoring(); } | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,433 @@ | |||
/****************************************************************************** | |||
* | |||
* file: MultiArg.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_MULTIPLE_ARGUMENT_H | |||
#define TCLAP_MULTIPLE_ARGUMENT_H | |||
#include <string> | |||
#include <vector> | |||
#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 T> | |||
class MultiArg : public Arg | |||
{ | |||
public: | |||
typedef std::vector<T> 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<T> _values; | |||
/** | |||
* The description of type T to be used in the usage. | |||
*/ | |||
std::string _typeDesc; | |||
/** | |||
* A list of constraint on this Arg. | |||
*/ | |||
Constraint<T>* _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<T>* 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<T>* 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<std::string>& args); | |||
/** | |||
* Returns a vector of type T containing the values parsed from | |||
* the command line. | |||
*/ | |||
const std::vector<T>& 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<T>(const MultiArg<T>& rhs); | |||
MultiArg<T>& operator=(const MultiArg<T>& rhs); | |||
}; | |||
template<class T> | |||
MultiArg<T>::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<T>()), | |||
_typeDesc( typeDesc ), | |||
_constraint( NULL ), | |||
_allowMore(false) | |||
{ | |||
_acceptsMultipleValues = true; | |||
} | |||
template<class T> | |||
MultiArg<T>::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<T>()), | |||
_typeDesc( typeDesc ), | |||
_constraint( NULL ), | |||
_allowMore(false) | |||
{ | |||
parser.add( this ); | |||
_acceptsMultipleValues = true; | |||
} | |||
/** | |||
* | |||
*/ | |||
template<class T> | |||
MultiArg<T>::MultiArg(const std::string& flag, | |||
const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
Constraint<T>* constraint, | |||
Visitor* v) | |||
: Arg( flag, name, desc, req, true, v ), | |||
_values(std::vector<T>()), | |||
_typeDesc( constraint->shortID() ), | |||
_constraint( constraint ), | |||
_allowMore(false) | |||
{ | |||
_acceptsMultipleValues = true; | |||
} | |||
template<class T> | |||
MultiArg<T>::MultiArg(const std::string& flag, | |||
const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
Constraint<T>* constraint, | |||
CmdLineInterface& parser, | |||
Visitor* v) | |||
: Arg( flag, name, desc, req, true, v ), | |||
_values(std::vector<T>()), | |||
_typeDesc( constraint->shortID() ), | |||
_constraint( constraint ), | |||
_allowMore(false) | |||
{ | |||
parser.add( this ); | |||
_acceptsMultipleValues = true; | |||
} | |||
template<class T> | |||
const std::vector<T>& MultiArg<T>::getValue() { return _values; } | |||
template<class T> | |||
bool MultiArg<T>::processArg(int *i, std::vector<std::string>& 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<unsigned int>(*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<class T> | |||
std::string MultiArg<T>::shortID(const std::string& val) const | |||
{ | |||
static_cast<void>(val); // Ignore input, don't warn | |||
return Arg::shortID(_typeDesc) + " ... "; | |||
} | |||
/** | |||
* | |||
*/ | |||
template<class T> | |||
std::string MultiArg<T>::longID(const std::string& val) const | |||
{ | |||
static_cast<void>(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<class T> | |||
bool MultiArg<T>::isRequired() const | |||
{ | |||
if ( _required ) | |||
{ | |||
if ( _values.size() > 1 ) | |||
return false; | |||
else | |||
return true; | |||
} | |||
else | |||
return false; | |||
} | |||
template<class T> | |||
void MultiArg<T>::_extractValue( const std::string& val ) | |||
{ | |||
try { | |||
T tmp; | |||
ExtractValue(tmp, val, typename ArgTraits<T>::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<class T> | |||
bool MultiArg<T>::allowMore() | |||
{ | |||
bool am = _allowMore; | |||
_allowMore = true; | |||
return am; | |||
} | |||
template<class T> | |||
void MultiArg<T>::reset() | |||
{ | |||
Arg::reset(); | |||
_values.clear(); | |||
} | |||
} // namespace TCLAP | |||
#endif |
@@ -0,0 +1,216 @@ | |||
/****************************************************************************** | |||
* | |||
* file: MultiSwitchArg.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. | |||
* Copyright (c) 2005, Michael E. Smoot, Daniel Aarno, Erik Zeek. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_MULTI_SWITCH_ARG_H | |||
#define TCLAP_MULTI_SWITCH_ARG_H | |||
#include <string> | |||
#include <vector> | |||
#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<std::string>& 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<std::string>& 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 |
@@ -0,0 +1,62 @@ | |||
/****************************************************************************** | |||
* | |||
* file: OptionalUnlabeledTracker.h | |||
* | |||
* Copyright (c) 2005, Michael E. Smoot . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_OPTIONAL_UNLABELED_TRACKER_H | |||
#define TCLAP_OPTIONAL_UNLABELED_TRACKER_H | |||
#include <string> | |||
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 |
@@ -0,0 +1,208 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: StandardTraits.h | |||
* | |||
* Copyright (c) 2007, Daniel Aarno, Michael E. Smoot . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
// This is an internal tclap file, you should probably not have to | |||
// include this directly | |||
#ifndef TCLAP_STANDARD_TRAITS_H | |||
#define TCLAP_STANDARD_TRAITS_H | |||
#ifdef HAVE_CONFIG_H | |||
#include <config.h> // 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<long> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
/** | |||
* ints have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<int> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
/** | |||
* shorts have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<short> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
/** | |||
* chars have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<char> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
#ifdef HAVE_LONG_LONG | |||
/** | |||
* long longs have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<long long> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
#endif | |||
// ====================================================================== | |||
// Unsigned integer types | |||
// ====================================================================== | |||
/** | |||
* unsigned longs have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<unsigned long> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
/** | |||
* unsigned ints have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<unsigned int> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
/** | |||
* unsigned shorts have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<unsigned short> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
/** | |||
* unsigned chars have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<unsigned char> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
// Microsoft implements size_t awkwardly. | |||
#if defined(_MSC_VER) && defined(_M_X64) | |||
/** | |||
* size_ts have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<size_t> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
#endif | |||
#ifdef HAVE_LONG_LONG | |||
/** | |||
* unsigned long longs have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<unsigned long long> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
#endif | |||
// ====================================================================== | |||
// Float types | |||
// ====================================================================== | |||
/** | |||
* floats have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<float> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
/** | |||
* doubles have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<double> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
// ====================================================================== | |||
// Other types | |||
// ====================================================================== | |||
/** | |||
* bools have value-like semantics. | |||
*/ | |||
template<> | |||
struct ArgTraits<bool> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
/** | |||
* wchar_ts have value-like semantics. | |||
*/ | |||
#ifndef TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS | |||
template<> | |||
struct ArgTraits<wchar_t> { | |||
typedef ValueLike ValueCategory; | |||
}; | |||
#endif | |||
/** | |||
* Strings have string like argument traits. | |||
*/ | |||
template<> | |||
struct ArgTraits<std::string> { | |||
typedef StringLike ValueCategory; | |||
}; | |||
template<typename T> | |||
void SetString(T &dst, const std::string &src) | |||
{ | |||
dst = src; | |||
} | |||
} // namespace | |||
#endif | |||
@@ -0,0 +1,298 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: StdOutput.h | |||
* | |||
* Copyright (c) 2004, Michael E. Smoot | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_STDCMDLINEOUTPUT_H | |||
#define TCLAP_STDCMDLINEOUTPUT_H | |||
#include <string> | |||
#include <vector> | |||
#include <list> | |||
#include <iostream> | |||
#include <algorithm> | |||
#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<Arg*> argList = _cmd.getArgList(); | |||
std::string progName = _cmd.getProgramName(); | |||
XorHandler xorHandler = _cmd.getXorHandler(); | |||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList(); | |||
std::string s = progName + " "; | |||
// first the xor | |||
for ( int i = 0; static_cast<unsigned int>(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<int>(progName.length()) + 2; | |||
if ( secondLineOffset > 75/2 ) | |||
secondLineOffset = static_cast<int>(75/2); | |||
spacePrint( os, s, 75, 3, secondLineOffset ); | |||
} | |||
inline void | |||
StdOutput::_longUsage( CmdLineInterface& _cmd, | |||
std::ostream& os ) const | |||
{ | |||
std::list<Arg*> argList = _cmd.getArgList(); | |||
std::string message = _cmd.getMessage(); | |||
XorHandler xorHandler = _cmd.getXorHandler(); | |||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList(); | |||
// first the xor | |||
for ( int i = 0; static_cast<unsigned int>(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<int>(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<int>( len - start, allowedLen ); | |||
// doing it this way to support a VisualC++ 2005 bug | |||
using namespace std; | |||
int stringLen = min<int>( 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 |
@@ -0,0 +1,266 @@ | |||
/****************************************************************************** | |||
* | |||
* file: SwitchArg.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_SWITCH_ARG_H | |||
#define TCLAP_SWITCH_ARG_H | |||
#include <string> | |||
#include <vector> | |||
#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<std::string>& 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<std::string>& 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 |
@@ -0,0 +1,301 @@ | |||
/****************************************************************************** | |||
* | |||
* file: UnlabeledMultiArg.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_MULTIPLE_UNLABELED_ARGUMENT_H | |||
#define TCLAP_MULTIPLE_UNLABELED_ARGUMENT_H | |||
#include <string> | |||
#include <vector> | |||
#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 T> | |||
class UnlabeledMultiArg : public MultiArg<T> | |||
{ | |||
// If compiler has two stage name lookup (as gcc >= 3.4 does) | |||
// this is requried to prevent undef. symbols | |||
using MultiArg<T>::_ignoreable; | |||
using MultiArg<T>::_hasBlanks; | |||
using MultiArg<T>::_extractValue; | |||
using MultiArg<T>::_typeDesc; | |||
using MultiArg<T>::_name; | |||
using MultiArg<T>::_description; | |||
using MultiArg<T>::_alreadySet; | |||
using MultiArg<T>::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<T>* 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<T>* 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<std::string>& 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<Arg*>& argList ) const; | |||
}; | |||
template<class T> | |||
UnlabeledMultiArg<T>::UnlabeledMultiArg(const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
const std::string& typeDesc, | |||
bool ignoreable, | |||
Visitor* v) | |||
: MultiArg<T>("", name, desc, req, typeDesc, v) | |||
{ | |||
_ignoreable = ignoreable; | |||
OptionalUnlabeledTracker::check(true, toString()); | |||
} | |||
template<class T> | |||
UnlabeledMultiArg<T>::UnlabeledMultiArg(const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
const std::string& typeDesc, | |||
CmdLineInterface& parser, | |||
bool ignoreable, | |||
Visitor* v) | |||
: MultiArg<T>("", name, desc, req, typeDesc, v) | |||
{ | |||
_ignoreable = ignoreable; | |||
OptionalUnlabeledTracker::check(true, toString()); | |||
parser.add( this ); | |||
} | |||
template<class T> | |||
UnlabeledMultiArg<T>::UnlabeledMultiArg(const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
Constraint<T>* constraint, | |||
bool ignoreable, | |||
Visitor* v) | |||
: MultiArg<T>("", name, desc, req, constraint, v) | |||
{ | |||
_ignoreable = ignoreable; | |||
OptionalUnlabeledTracker::check(true, toString()); | |||
} | |||
template<class T> | |||
UnlabeledMultiArg<T>::UnlabeledMultiArg(const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
Constraint<T>* constraint, | |||
CmdLineInterface& parser, | |||
bool ignoreable, | |||
Visitor* v) | |||
: MultiArg<T>("", name, desc, req, constraint, v) | |||
{ | |||
_ignoreable = ignoreable; | |||
OptionalUnlabeledTracker::check(true, toString()); | |||
parser.add( this ); | |||
} | |||
template<class T> | |||
bool UnlabeledMultiArg<T>::processArg(int *i, std::vector<std::string>& 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<class T> | |||
std::string UnlabeledMultiArg<T>::shortID(const std::string& val) const | |||
{ | |||
static_cast<void>(val); // Ignore input, don't warn | |||
return std::string("<") + _typeDesc + "> ..."; | |||
} | |||
template<class T> | |||
std::string UnlabeledMultiArg<T>::longID(const std::string& val) const | |||
{ | |||
static_cast<void>(val); // Ignore input, don't warn | |||
return std::string("<") + _typeDesc + "> (accepted multiple times)"; | |||
} | |||
template<class T> | |||
bool UnlabeledMultiArg<T>::operator==(const Arg& a) const | |||
{ | |||
if ( _name == a.getName() || _description == a.getDescription() ) | |||
return true; | |||
else | |||
return false; | |||
} | |||
template<class T> | |||
void UnlabeledMultiArg<T>::addToList( std::list<Arg*>& argList ) const | |||
{ | |||
argList.push_back( const_cast<Arg*>(static_cast<const Arg* const>(this)) ); | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,340 @@ | |||
/****************************************************************************** | |||
* | |||
* file: UnlabeledValueArg.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_UNLABELED_VALUE_ARGUMENT_H | |||
#define TCLAP_UNLABELED_VALUE_ARGUMENT_H | |||
#include <string> | |||
#include <vector> | |||
#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 T> | |||
class UnlabeledValueArg : public ValueArg<T> | |||
{ | |||
// If compiler has two stage name lookup (as gcc >= 3.4 does) | |||
// this is requried to prevent undef. symbols | |||
using ValueArg<T>::_ignoreable; | |||
using ValueArg<T>::_hasBlanks; | |||
using ValueArg<T>::_extractValue; | |||
using ValueArg<T>::_typeDesc; | |||
using ValueArg<T>::_name; | |||
using ValueArg<T>::_description; | |||
using ValueArg<T>::_alreadySet; | |||
using ValueArg<T>::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<T>* 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<T>* 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<std::string>& 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<Arg*>& argList ) const; | |||
}; | |||
/** | |||
* Constructor implemenation. | |||
*/ | |||
template<class T> | |||
UnlabeledValueArg<T>::UnlabeledValueArg(const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
T val, | |||
const std::string& typeDesc, | |||
bool ignoreable, | |||
Visitor* v) | |||
: ValueArg<T>("", name, desc, req, val, typeDesc, v) | |||
{ | |||
_ignoreable = ignoreable; | |||
OptionalUnlabeledTracker::check(req, toString()); | |||
} | |||
template<class T> | |||
UnlabeledValueArg<T>::UnlabeledValueArg(const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
T val, | |||
const std::string& typeDesc, | |||
CmdLineInterface& parser, | |||
bool ignoreable, | |||
Visitor* v) | |||
: ValueArg<T>("", name, desc, req, val, typeDesc, v) | |||
{ | |||
_ignoreable = ignoreable; | |||
OptionalUnlabeledTracker::check(req, toString()); | |||
parser.add( this ); | |||
} | |||
/** | |||
* Constructor implemenation. | |||
*/ | |||
template<class T> | |||
UnlabeledValueArg<T>::UnlabeledValueArg(const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
T val, | |||
Constraint<T>* constraint, | |||
bool ignoreable, | |||
Visitor* v) | |||
: ValueArg<T>("", name, desc, req, val, constraint, v) | |||
{ | |||
_ignoreable = ignoreable; | |||
OptionalUnlabeledTracker::check(req, toString()); | |||
} | |||
template<class T> | |||
UnlabeledValueArg<T>::UnlabeledValueArg(const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
T val, | |||
Constraint<T>* constraint, | |||
CmdLineInterface& parser, | |||
bool ignoreable, | |||
Visitor* v) | |||
: ValueArg<T>("", name, desc, req, val, constraint, v) | |||
{ | |||
_ignoreable = ignoreable; | |||
OptionalUnlabeledTracker::check(req, toString()); | |||
parser.add( this ); | |||
} | |||
/** | |||
* Implementation of processArg(). | |||
*/ | |||
template<class T> | |||
bool UnlabeledValueArg<T>::processArg(int *i, std::vector<std::string>& 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<class T> | |||
std::string UnlabeledValueArg<T>::shortID(const std::string& val) const | |||
{ | |||
static_cast<void>(val); // Ignore input, don't warn | |||
return std::string("<") + _typeDesc + ">"; | |||
} | |||
/** | |||
* Overriding longID for specific output. | |||
*/ | |||
template<class T> | |||
std::string UnlabeledValueArg<T>::longID(const std::string& val) const | |||
{ | |||
static_cast<void>(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<class T> | |||
bool UnlabeledValueArg<T>::operator==(const Arg& a ) const | |||
{ | |||
if ( _name == a.getName() || _description == a.getDescription() ) | |||
return true; | |||
else | |||
return false; | |||
} | |||
template<class T> | |||
void UnlabeledValueArg<T>::addToList( std::list<Arg*>& argList ) const | |||
{ | |||
argList.push_back( const_cast<Arg*>(static_cast<const Arg* const>(this)) ); | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,425 @@ | |||
/****************************************************************************** | |||
* | |||
* file: ValueArg.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_VALUE_ARGUMENT_H | |||
#define TCLAP_VALUE_ARGUMENT_H | |||
#include <string> | |||
#include <vector> | |||
#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 T> | |||
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<T>* _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<T>* 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<T>* 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<std::string>& 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<T>(const ValueArg<T>& rhs); | |||
ValueArg<T>& operator=(const ValueArg<T>& rhs); | |||
}; | |||
/** | |||
* Constructor implementation. | |||
*/ | |||
template<class T> | |||
ValueArg<T>::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<class T> | |||
ValueArg<T>::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<class T> | |||
ValueArg<T>::ValueArg(const std::string& flag, | |||
const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
T val, | |||
Constraint<T>* constraint, | |||
Visitor* v) | |||
: Arg(flag, name, desc, req, true, v), | |||
_value( val ), | |||
_default( val ), | |||
_typeDesc( constraint->shortID() ), | |||
_constraint( constraint ) | |||
{ } | |||
template<class T> | |||
ValueArg<T>::ValueArg(const std::string& flag, | |||
const std::string& name, | |||
const std::string& desc, | |||
bool req, | |||
T val, | |||
Constraint<T>* 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<class T> | |||
T& ValueArg<T>::getValue() { return _value; } | |||
/** | |||
* Implementation of processArg(). | |||
*/ | |||
template<class T> | |||
bool ValueArg<T>::processArg(int *i, std::vector<std::string>& 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<unsigned int>(*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<class T> | |||
std::string ValueArg<T>::shortID(const std::string& val) const | |||
{ | |||
static_cast<void>(val); // Ignore input, don't warn | |||
return Arg::shortID( _typeDesc ); | |||
} | |||
/** | |||
* Implementation of longID. | |||
*/ | |||
template<class T> | |||
std::string ValueArg<T>::longID(const std::string& val) const | |||
{ | |||
static_cast<void>(val); // Ignore input, don't warn | |||
return Arg::longID( _typeDesc ); | |||
} | |||
template<class T> | |||
void ValueArg<T>::_extractValue( const std::string& val ) | |||
{ | |||
try { | |||
ExtractValue(_value, val, typename ArgTraits<T>::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<class T> | |||
void ValueArg<T>::reset() | |||
{ | |||
Arg::reset(); | |||
_value = _default; | |||
} | |||
} // namespace TCLAP | |||
#endif |
@@ -0,0 +1,148 @@ | |||
/****************************************************************************** | |||
* | |||
* file: ValuesConstraint.h | |||
* | |||
* Copyright (c) 2005, Michael E. Smoot | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_VALUESCONSTRAINT_H | |||
#define TCLAP_VALUESCONSTRAINT_H | |||
#include <string> | |||
#include <vector> | |||
#include "Constraint.h" | |||
#ifdef HAVE_CONFIG_H | |||
#include <config.h> | |||
#else | |||
#define HAVE_SSTREAM | |||
#endif | |||
#if defined(HAVE_SSTREAM) | |||
#include <sstream> | |||
#elif defined(HAVE_STRSTREAM) | |||
#include <strstream> | |||
#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 T> | |||
class ValuesConstraint : public Constraint<T> | |||
{ | |||
public: | |||
/** | |||
* Constructor. | |||
* \param allowed - vector of allowed values. | |||
*/ | |||
ValuesConstraint(std::vector<T>& 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<T> _allowed; | |||
/** | |||
* The string used to describe the allowed values of this constraint. | |||
*/ | |||
std::string _typeDesc; | |||
}; | |||
template<class T> | |||
ValuesConstraint<T>::ValuesConstraint(std::vector<T>& 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<class T> | |||
bool ValuesConstraint<T>::check( const T& val ) const | |||
{ | |||
if ( std::find(_allowed.begin(),_allowed.end(),val) == _allowed.end() ) | |||
return false; | |||
else | |||
return true; | |||
} | |||
template<class T> | |||
std::string ValuesConstraint<T>::shortID() const | |||
{ | |||
return _typeDesc; | |||
} | |||
template<class T> | |||
std::string ValuesConstraint<T>::description() const | |||
{ | |||
return _typeDesc; | |||
} | |||
} //namespace TCLAP | |||
#endif | |||
@@ -0,0 +1,81 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: VersionVisitor.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_VERSION_VISITOR_H | |||
#define TCLAP_VERSION_VISITOR_H | |||
#include "CmdLineInterface.h" | |||
#include "CmdLineOutput.h" | |||
#include "Visitor.h" | |||
namespace TCLAP { | |||
/** | |||
* A Vistor that will call the version method of the given CmdLineOutput | |||
* for the specified CmdLine object and then exit. | |||
*/ | |||
class VersionVisitor: public Visitor | |||
{ | |||
private: | |||
/** | |||
* Prevent accidental copying | |||
*/ | |||
VersionVisitor(const VersionVisitor& rhs); | |||
VersionVisitor& operator=(const VersionVisitor& rhs); | |||
protected: | |||
/** | |||
* The CmdLine of interest. | |||
*/ | |||
CmdLineInterface* _cmd; | |||
/** | |||
* The output object. | |||
*/ | |||
CmdLineOutput** _out; | |||
public: | |||
/** | |||
* Constructor. | |||
* \param cmd - The CmdLine the output is generated for. | |||
* \param out - The type of output. | |||
*/ | |||
VersionVisitor( CmdLineInterface* cmd, CmdLineOutput** out ) | |||
: Visitor(), _cmd( cmd ), _out( out ) { } | |||
/** | |||
* Calls the version method of the output object using the | |||
* specified CmdLine. | |||
*/ | |||
void visit() { | |||
(*_out)->version(*_cmd); | |||
throw ExitException(0); | |||
} | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,53 @@ | |||
/****************************************************************************** | |||
* | |||
* file: Visitor.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_VISITOR_H | |||
#define TCLAP_VISITOR_H | |||
namespace TCLAP { | |||
/** | |||
* A base class that defines the interface for visitors. | |||
*/ | |||
class Visitor | |||
{ | |||
public: | |||
/** | |||
* Constructor. Does nothing. | |||
*/ | |||
Visitor() { } | |||
/** | |||
* Destructor. Does nothing. | |||
*/ | |||
virtual ~Visitor() { } | |||
/** | |||
* Does nothing. Should be overridden by child. | |||
*/ | |||
virtual void visit() { } | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,166 @@ | |||
/****************************************************************************** | |||
* | |||
* file: XorHandler.h | |||
* | |||
* Copyright (c) 2003, Michael E. Smoot . | |||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno. | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_XORHANDLER_H | |||
#define TCLAP_XORHANDLER_H | |||
#include "Arg.h" | |||
#include <string> | |||
#include <vector> | |||
#include <algorithm> | |||
#include <iostream> | |||
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<Arg*> > _orList; | |||
public: | |||
/** | |||
* Constructor. Does nothing. | |||
*/ | |||
XorHandler( ) : _orList(std::vector< std::vector<Arg*> >()) {} | |||
/** | |||
* 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<Arg*>& 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<Arg*> >& getXorList(); | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
//BEGIN XOR.cpp | |||
////////////////////////////////////////////////////////////////////// | |||
inline void XorHandler::add( std::vector<Arg*>& ors ) | |||
{ | |||
_orList.push_back( ors ); | |||
} | |||
inline int XorHandler::check( const Arg* a ) | |||
{ | |||
// iterate over each XOR list | |||
for ( int i = 0; static_cast<unsigned int>(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<int>(_orList[i].size()); | |||
} | |||
} | |||
if ( a->isRequired() ) | |||
return 1; | |||
else | |||
return 0; | |||
} | |||
inline bool XorHandler::contains( const Arg* a ) | |||
{ | |||
for ( int i = 0; static_cast<unsigned int>(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<Arg*> >& XorHandler::getXorList() | |||
{ | |||
return _orList; | |||
} | |||
////////////////////////////////////////////////////////////////////// | |||
//END XOR.cpp | |||
////////////////////////////////////////////////////////////////////// | |||
} //namespace TCLAP | |||
#endif |
@@ -0,0 +1,323 @@ | |||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*- | |||
/****************************************************************************** | |||
* | |||
* file: ZshCompletionOutput.h | |||
* | |||
* Copyright (c) 2006, Oliver Kiddle | |||
* All rights reverved. | |||
* | |||
* See the file COPYING in the top directory of this distribution for | |||
* more information. | |||
* | |||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
* DEALINGS IN THE SOFTWARE. | |||
* | |||
*****************************************************************************/ | |||
#ifndef TCLAP_ZSHCOMPLETIONOUTPUT_H | |||
#define TCLAP_ZSHCOMPLETIONOUTPUT_H | |||
#include <string> | |||
#include <vector> | |||
#include <list> | |||
#include <iostream> | |||
#include <map> | |||
#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<std::string, std::string> common; | |||
char theDelimiter; | |||
}; | |||
ZshCompletionOutput::ZshCompletionOutput() | |||
: common(std::map<std::string, std::string>()), | |||
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<Arg*> 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<void>(_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<std::string, std::string>::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<std::string, std::string>::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<Arg*> > xorList = xorHandler.getXorList(); | |||
if (a->getName() == "help" || a->getName() == "version") | |||
{ | |||
return "(-)"; | |||
} | |||
std::ostringstream list; | |||
if ( a->acceptsMultipleValues() ) | |||
{ | |||
list << '*'; | |||
} | |||
for ( int i = 0; static_cast<unsigned int>(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 |
@@ -0,0 +1,7 @@ | |||
# | |||
# Main Makefile. This is basically the same as a component makefile. | |||
# | |||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) | |||
COMPONENT_SRCDIRS := . | |||
COMPONENT_ADD_INCLUDEDIRS := . |
@@ -0,0 +1,355 @@ | |||
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
#ifndef _DRIVER_SPI_MASTER_LOBO_H_ | |||
#define _DRIVER_SPI_MASTER_LOBO_H_ | |||
#include "esp_err.h" | |||
#include "freertos/FreeRTOS.h" | |||
#include "freertos/semphr.h" | |||
#include "soc/spi_struct.h" | |||
#include "esp_intr.h" | |||
#include "esp_intr_alloc.h" | |||
#include "rom/lldesc.h" | |||
#ifdef __cplusplus | |||
extern "C" | |||
{ | |||
#endif | |||
//Maximum amount of bytes that can be put in one DMA descriptor | |||
#define SPI_MAX_DMA_LEN (4096-4) | |||
/** | |||
* @brief Enum with the three SPI peripherals that are software-accessible in it | |||
*/ | |||
typedef enum { | |||
SPI_HOST=0, ///< SPI1, SPI; Cannot be used in this driver! | |||
HSPI_HOST=1, ///< SPI2, HSPI | |||
VSPI_HOST=2 ///< SPI3, VSPI | |||
} spi_lobo_host_device_t; | |||
/** | |||
* @brief This is a configuration structure for a SPI bus. | |||
* | |||
* You can use this structure to specify the GPIO pins of the bus. Normally, the driver will use the | |||
* GPIO matrix to route the signals. An exception is made when all signals either can be routed through | |||
* the IO_MUX or are -1. In that case, the IO_MUX is used, allowing for >40MHz speeds. | |||
*/ | |||
typedef struct { | |||
int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used. | |||
int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used. | |||
int sclk_io_num; ///< GPIO pin for Spi CLocK signal, or -1 if not used. | |||
int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used. | |||
int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used. | |||
int max_transfer_sz; ///< Maximum transfer size, in bytes. Defaults to 4094 if 0. | |||
} spi_lobo_bus_config_t; | |||
#define SPI_DEVICE_TXBIT_LSBFIRST (1<<0) ///< Transmit command/address/data LSB first instead of the default MSB first | |||
#define SPI_DEVICE_RXBIT_LSBFIRST (1<<1) ///< Receive data LSB first instead of the default MSB first | |||
#define SPI_DEVICE_BIT_LSBFIRST (SPI_TXBIT_LSBFIRST|SPI_RXBIT_LSBFIRST); ///< Transmit and receive LSB first | |||
#define SPI_DEVICE_3WIRE (1<<2) ///< Use spiq for both sending and receiving data | |||
#define SPI_DEVICE_POSITIVE_CS (1<<3) ///< Make CS positive during a transaction instead of negative | |||
#define SPI_DEVICE_HALFDUPLEX (1<<4) ///< Transmit data before receiving it, instead of simultaneously | |||
#define SPI_DEVICE_CLK_AS_CS (1<<5) ///< Output clock on CS line if CS is active | |||
#define SPI_ERR_OTHER_CONFIG 7001 | |||
typedef struct spi_lobo_transaction_t spi_lobo_transaction_t; | |||
typedef void(*transaction_cb_t)(spi_lobo_transaction_t *trans); | |||
/** | |||
* @brief This is a configuration for a SPI slave device that is connected to one of the SPI buses. | |||
*/ | |||
typedef struct { | |||
uint8_t command_bits; ///< Amount of bits in command phase (0-16) | |||
uint8_t address_bits; ///< Amount of bits in address phase (0-64) | |||
uint8_t dummy_bits; ///< Amount of dummy bits to insert between address and data phase | |||
uint8_t mode; ///< SPI mode (0-3) | |||
uint8_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128. | |||
uint8_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions. | |||
uint8_t cs_ena_posttrans; ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16) | |||
int clock_speed_hz; ///< Clock speed, in Hz | |||
int spics_io_num; ///< CS GPIO pin for this device, handled by hardware; set to -1 if not used | |||
int spics_ext_io_num; ///< CS GPIO pin for this device, handled by software (spi_lobo_device_select/spi_lobo_device_deselect); only used if spics_io_num=-1 | |||
uint32_t flags; ///< Bitwise OR of SPI_DEVICE_* flags | |||
transaction_cb_t pre_cb; ///< Callback to be called before a transmission is started. This callback from 'spi_lobo_transfer_data' function. | |||
transaction_cb_t post_cb; ///< Callback to be called after a transmission has completed. This callback from 'spi_lobo_transfer_data' function. | |||
uint8_t selected; ///< **INTERNAL** 1 if the device's CS pin is active | |||
} spi_lobo_device_interface_config_t; | |||
#define SPI_TRANS_MODE_DIO (1<<0) ///< Transmit/receive data in 2-bit mode | |||
#define SPI_TRANS_MODE_QIO (1<<1) ///< Transmit/receive data in 4-bit mode | |||
#define SPI_TRANS_MODE_DIOQIO_ADDR (1<<2) ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO | |||
#define SPI_TRANS_USE_RXDATA (1<<3) ///< Receive into rx_data member of spi_lobo_transaction_t instead into memory at rx_buffer. | |||
#define SPI_TRANS_USE_TXDATA (1<<4) ///< Transmit tx_data member of spi_lobo_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this. | |||
/** | |||
* This structure describes one SPI transmission | |||
*/ | |||
struct spi_lobo_transaction_t { | |||
uint32_t flags; ///< Bitwise OR of SPI_TRANS_* flags | |||
uint16_t command; ///< Command data. Specific length was given when device was added to the bus. | |||
uint64_t address; ///< Address. Specific length was given when device was added to the bus. | |||
size_t length; ///< Total data length to be transmitted to the device, in bits; if 0, no data is transmitted | |||
size_t rxlength; ///< Total data length to be received from the device, in bits; if 0, no data is received | |||
void *user; ///< User-defined variable. Can be used to store eg transaction ID or data to be used by pre_cb and/or post_cb callbacks. | |||
union { | |||
const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase | |||
uint8_t tx_data[4]; ///< If SPI_USE_TXDATA is set, data set here is sent directly from this variable. | |||
}; | |||
union { | |||
void *rx_buffer; ///< Pointer to receive buffer, or NULL for no MISO phase | |||
uint8_t rx_data[4]; ///< If SPI_USE_RXDATA is set, data is received directly to this variable | |||
}; | |||
}; | |||
#define NO_CS 3 // Number of CS pins per SPI host | |||
#define NO_DEV 6 // Number of spi devices per SPI host; more than 3 devices can be attached to the same bus if using software CS's | |||
#define SPI_SEMAPHORE_WAIT 2000 // Time in ms to wait for SPI mutex | |||
typedef struct spi_lobo_device_t spi_lobo_device_t; | |||
typedef struct { | |||
spi_lobo_device_t *device[NO_DEV]; | |||
intr_handle_t intr; | |||
spi_dev_t *hw; | |||
//spi_lobo_transaction_t *cur_trans; | |||
int cur_device; | |||
lldesc_t *dmadesc_tx; | |||
lldesc_t *dmadesc_rx; | |||
bool no_gpio_matrix; | |||
int dma_chan; | |||
int max_transfer_sz; | |||
QueueHandle_t spi_lobo_bus_mutex; | |||
spi_lobo_bus_config_t cur_bus_config; | |||
} spi_lobo_host_t; | |||
struct spi_lobo_device_t { | |||
spi_lobo_device_interface_config_t cfg; | |||
spi_lobo_host_t *host; | |||
spi_lobo_bus_config_t bus_config; | |||
spi_lobo_host_device_t host_dev; | |||
}; | |||
typedef spi_lobo_device_t* spi_lobo_device_handle_t; ///< Handle for a device on a SPI bus | |||
typedef spi_lobo_host_t* spi_lobo_host_handle_t; | |||
typedef spi_lobo_device_interface_config_t* spi_lobo_device_interface_config_handle_t; | |||
/** | |||
* @brief Add a device. This allocates a CS line for the device, allocates memory for the device structure and hooks | |||
* up the CS pin to whatever is specified. | |||
* | |||
* This initializes the internal structures for a device, plus allocates a CS pin on the indicated SPI master | |||
* peripheral and routes it to the indicated GPIO. All SPI master devices have three hw CS pins and can thus control | |||
* up to three devices. Software handled CS pin can also be used for additional devices on the same SPI bus. | |||
* | |||
* ### If selected SPI host device bus is not yet initialized, it is initialized first with 'bus_config' function ### | |||
* | |||
* @note While in general, speeds up to 80MHz on the dedicated SPI pins and 40MHz on GPIO-matrix-routed pins are | |||
* supported, full-duplex transfers routed over the GPIO matrix only support speeds up to 26MHz. | |||
* | |||
* @param host SPI peripheral to allocate device on (HSPI or VSPI) | |||
* @param dev_config SPI interface protocol config for the device | |||
* @param bus_config Pointer to a spi_lobo_bus_config_t struct specifying how the host device bus should be initialized | |||
* @param handle Pointer to variable to hold the device handle | |||
* @return | |||
* - ESP_ERR_INVALID_ARG if parameter is invalid | |||
* - ESP_ERR_NOT_FOUND if host doesn't have any free CS slots | |||
* - ESP_ERR_NO_MEM if out of memory | |||
* - ESP_OK on success | |||
*/ | |||
esp_err_t spi_lobo_bus_add_device(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, spi_lobo_device_interface_config_t *dev_config, spi_lobo_device_handle_t *handle); | |||
/** | |||
* @brief Remove a device from the SPI bus. If after removal no other device is attached to the spi bus device, it is freed. | |||
* | |||
* @param handle Device handle to free | |||
* @return | |||
* - ESP_ERR_INVALID_ARG if parameter is invalid | |||
* - ESP_ERR_INVALID_STATE if device already is freed | |||
* - ESP_OK on success | |||
*/ | |||
esp_err_t spi_lobo_bus_remove_device(spi_lobo_device_handle_t handle); | |||
/** | |||
* @brief Return the actuall SPI bus speed for the spi device in Hz | |||
* | |||
* Some frequencies cannot be set, for example 30000000 will actually set SPI clock to 26666666 Hz | |||
* | |||
* @param handle Device handle obtained using spi_lobo_bus_add_device | |||
* | |||
* @return | |||
* - actuall SPI clock | |||
*/ | |||
uint32_t spi_lobo_get_speed(spi_lobo_device_handle_t handle); | |||
/** | |||
* @brief Set the new clock speed for the device, return the actuall SPI bus speed set, in Hz | |||
* This function can be used after the device is initialized | |||
* | |||
* Some frequencies cannot be set, for example 30000000 will actually set SPI clock to 26666666 Hz | |||
* | |||
* @param handle Device handle obtained using spi_lobo_bus_add_device | |||
* @param speed New device spi clock to be set in Hz | |||
* | |||
* @return | |||
* - actuall SPI clock | |||
* - 0 if speed cannot be set | |||
*/ | |||
uint32_t spi_lobo_set_speed(spi_lobo_device_handle_t handle, uint32_t speed); | |||
/** | |||
* @brief Select spi device for transmission | |||
* | |||
* It configures spi bus with selected spi device parameters if previously selected device was different than the current | |||
* If device's spics_io_num=-1 and spics_ext_io_num > 0 'spics_ext_io_num' pin is set to active state (low) | |||
* | |||
* spi bus device's semaphore is taken before selecting the device | |||
* | |||
* @param handle Device handle obtained using spi_lobo_bus_add_device | |||
* @param force configure spi bus even if the previous device was the same | |||
* | |||
* @return | |||
* - ESP_ERR_INVALID_ARG if parameter is invalid | |||
* - ESP_OK on success | |||
*/ | |||
esp_err_t spi_lobo_device_select(spi_lobo_device_handle_t handle, int force); | |||
/** | |||
* @brief De-select spi device | |||
* | |||
* If device's spics_io_num=-1 and spics_ext_io_num > 0 'spics_ext_io_num' pin is set to inactive state (high) | |||
* | |||
* spi bus device's semaphore is given after selecting the device | |||
* | |||
* @param handle Device handle obtained using spi_lobo_bus_add_device | |||
* | |||
* @return | |||
* - ESP_ERR_INVALID_ARG if parameter is invalid | |||
* - ESP_OK on success | |||
*/ | |||
esp_err_t spi_lobo_device_deselect(spi_lobo_device_handle_t handle); | |||
/** | |||
* @brief Check if spi bus uses native spi pins | |||
* | |||
* @param handle Device handle obtained using spi_lobo_bus_add_device | |||
* | |||
* @return | |||
* - true if native spi pins are used | |||
* - false if spi pins are routed through gpio matrix | |||
*/ | |||
bool spi_lobo_uses_native_pins(spi_lobo_device_handle_t handle); | |||
/** | |||
* @brief Get spi bus native spi pins | |||
* | |||
* @param handle Device handle obtained using spi_lobo_bus_add_device | |||
* | |||
* @return | |||
* places spi bus native pins in provided pointers | |||
*/ | |||
void spi_lobo_get_native_pins(int host, int *sdi, int *sdo, int *sck); | |||
/** | |||
* @brief Transimit and receive data to/from spi device based on transaction data | |||
* | |||
* TRANSMIT 8-bit data to spi device from 'trans->tx_buffer' or 'trans->tx_data' (trans->lenght/8 bytes) | |||
* and RECEIVE data to 'trans->rx_buffer' or 'trans->rx_data' (trans->rx_length/8 bytes) | |||
* Lengths must be 8-bit multiples! | |||
* If trans->rx_buffer is NULL or trans->rx_length is 0, only transmits data | |||
* If trans->tx_buffer is NULL or trans->length is 0, only receives data | |||
* If the device is in duplex mode (SPI_DEVICE_HALFDUPLEX flag NOT set), data are transmitted and received simultaneously. | |||
* If the device is in half duplex mode (SPI_DEVICE_HALFDUPLEX flag IS set), data are received after transmission | |||
* 'address', 'command' and 'dummy bits' are transmitted before data phase IF set in device's configuration | |||
* and IF 'trans->length' and 'trans->rx_length' are NOT both 0 | |||
* If device was not previously selected, it will be selected before transmission and deselected after transmission. | |||
* | |||
* @param handle Device handle obtained using spi_lobo_bus_add_device | |||
* | |||
* @param trans Pointer to variable containing the description of the transaction that is executed | |||
* | |||
* @return | |||
* - ESP_ERR_INVALID_ARG if parameter is invalid | |||
* - ESP error code if device cannot be selected | |||
* - ESP_OK on success | |||
* | |||
*/ | |||
esp_err_t spi_lobo_transfer_data(spi_lobo_device_handle_t handle, spi_lobo_transaction_t *trans); | |||
/* | |||
* SPI transactions uses the semaphore (taken in select function) to protect the transfer | |||
*/ | |||
esp_err_t spi_lobo_device_TakeSemaphore(spi_lobo_device_handle_t handle); | |||
void spi_lobo_device_GiveSemaphore(spi_lobo_device_handle_t handle); | |||
/** | |||
* @brief Setup a DMA link chain | |||
* | |||
* This routine will set up a chain of linked DMA descriptors in the array pointed to by | |||
* ``dmadesc``. Enough DMA descriptors will be used to fit the buffer of ``len`` bytes in, and the | |||
* descriptors will point to the corresponding positions in ``buffer`` and linked together. The | |||
* end result is that feeding ``dmadesc[0]`` into DMA hardware results in the entirety ``len`` bytes | |||
* of ``data`` being read or written. | |||
* | |||
* @param dmadesc Pointer to array of DMA descriptors big enough to be able to convey ``len`` bytes | |||
* @param len Length of buffer | |||
* @param data Data buffer to use for DMA transfer | |||
* @param isrx True if data is to be written into ``data``, false if it's to be read from ``data``. | |||
*/ | |||
void spi_lobo_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx); | |||
/** | |||
* @brief Check if a DMA reset is requested but has not completed yet | |||
* | |||
* @return True when a DMA reset is requested but hasn't completed yet. False otherwise. | |||
*/ | |||
bool spi_lobo_dmaworkaround_reset_in_progress(); | |||
/** | |||
* @brief Mark a DMA channel as idle. | |||
* | |||
* A call to this function tells the workaround logic that this channel will | |||
* not be affected by a global SPI DMA reset. | |||
*/ | |||
void spi_lobo_dmaworkaround_idle(int dmachan); | |||
/** | |||
* @brief Mark a DMA channel as active. | |||
* | |||
* A call to this function tells the workaround logic that this channel will | |||
* be affected by a global SPI DMA reset, and a reset like that should not be attempted. | |||
*/ | |||
void spi_lobo_dmaworkaround_transfer_active(int dmachan); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -0,0 +1,7 @@ | |||
# | |||
# Component Makefile | |||
# | |||
COMPONENT_SRCDIRS := . | |||
COMPONENT_ADD_INCLUDEDIRS := . | |||
COMPONENT_PRIV_INCLUDEDIRS := |
@@ -0,0 +1,138 @@ | |||
/* | |||
* Lua RTOS, SPIFFS low access | |||
* | |||
* Copyright (C) 2015 - 2017 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* | |||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission to use, copy, modify, and distribute this software | |||
* and its documentation for any purpose and without fee is hereby | |||
* granted, provided that the above copyright notice appear in all | |||
* copies and that both that the copyright notice and this | |||
* permission notice and warranty disclaimer appear in supporting | |||
* documentation, and that the name of the author not be used in | |||
* advertising or publicity pertaining to distribution of the | |||
* software without specific, written prior permission. | |||
* | |||
* The author disclaim all warranties with regard to this | |||
* software, including all implied warranties of merchantability | |||
* and fitness. In no event shall the author be liable for any | |||
* special, indirect or consequential damages or any damages | |||
* whatsoever resulting from loss of use, data or profits, whether | |||
* in an action of contract, negligence or other tortious action, | |||
* arising out of or in connection with the use or performance of | |||
* this software. | |||
*/ | |||
#include <stdlib.h> | |||
#include "esp_spiffs.h" | |||
#include "esp_attr.h" | |||
#include "spiffs.h" | |||
#include <esp_spi_flash.h> | |||
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; | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* Lua RTOS, write syscall implementation | |||
* | |||
* Copyright (C) 2015 - 2017 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* | |||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission to use, copy, modify, and distribute this software | |||
* and its documentation for any purpose and without fee is hereby | |||
* granted, provided that the above copyright notice appear in all | |||
* copies and that both that the copyright notice and this | |||
* permission notice and warranty disclaimer appear in supporting | |||
* documentation, and that the name of the author not be used in | |||
* advertising or publicity pertaining to distribution of the | |||
* software without specific, written prior permission. | |||
* | |||
* The author disclaim all warranties with regard to this | |||
* software, including all implied warranties of merchantability | |||
* and fitness. In no event shall the author be liable for any | |||
* special, indirect or consequential damages or any damages | |||
* whatsoever resulting from loss of use, data or profits, whether | |||
* in an action of contract, negligence or other tortious action, | |||
* arising out of or in connection with the use or performance of | |||
* this software. | |||
*/ | |||
#ifndef __ESP_SPIFFS_H__ | |||
#define __ESP_SPIFFS_H__ | |||
#include "spiffs.h" | |||
s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst); | |||
s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src); | |||
s32_t esp32_spi_flash_erase(u32_t addr, u32_t size); | |||
#define low_spiffs_read (spiffs_read *)esp32_spi_flash_read | |||
#define low_spiffs_write (spiffs_write *)esp32_spi_flash_write | |||
#define low_spiffs_erase (spiffs_erase *)esp32_spi_flash_erase | |||
#endif // __ESP_SPIFFS_H__ |
@@ -0,0 +1,251 @@ | |||
/* | |||
* Lua RTOS, list data structure | |||
* | |||
* Copyright (C) 2015 - 2017 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* | |||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission to use, copy, modify, and distribute this software | |||
* and its documentation for any purpose and without fee is hereby | |||
* granted, provided that the above copyright notice appear in all | |||
* copies and that both that the copyright notice and this | |||
* permission notice and warranty disclaimer appear in supporting | |||
* documentation, and that the name of the author not be used in | |||
* advertising or publicity pertaining to distribution of the | |||
* software without specific, written prior permission. | |||
* | |||
* The author disclaim all warranties with regard to this | |||
* software, including all implied warranties of merchantability | |||
* and fitness. In no event shall the author be liable for any | |||
* special, indirect or consequential damages or any damages | |||
* whatsoever resulting from loss of use, data or profits, whether | |||
* in an action of contract, negligence or other tortious action, | |||
* arising out of or in connection with the use or performance of | |||
* this software. | |||
*/ | |||
#include "esp_attr.h" | |||
#include <errno.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#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); | |||
} |
@@ -0,0 +1,60 @@ | |||
/* | |||
* Lua RTOS, list data structure | |||
* | |||
* Copyright (C) 2015 - 2017 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* | |||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission to use, copy, modify, and distribute this software | |||
* and its documentation for any purpose and without fee is hereby | |||
* granted, provided that the above copyright notice appear in all | |||
* copies and that both that the copyright notice and this | |||
* permission notice and warranty disclaimer appear in supporting | |||
* documentation, and that the name of the author not be used in | |||
* advertising or publicity pertaining to distribution of the | |||
* software without specific, written prior permission. | |||
* | |||
* The author disclaim all warranties with regard to this | |||
* software, including all implied warranties of merchantability | |||
* and fitness. In no event shall the author be liable for any | |||
* special, indirect or consequential damages or any damages | |||
* whatsoever resulting from loss of use, data or profits, whether | |||
* in an action of contract, negligence or other tortious action, | |||
* arising out of or in connection with the use or performance of | |||
* this software. | |||
*/ | |||
#ifndef _LIST_H | |||
#define _LIST_H | |||
#include <stdint.h> | |||
#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 */ | |||
@@ -0,0 +1,102 @@ | |||
/* | |||
* Lua RTOS, mutex api implementation over FreeRTOS | |||
* | |||
* Copyright (C) 2015 - 2017 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* | |||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission to use, copy, modify, and distribute this software | |||
* and its documentation for any purpose and without fee is hereby | |||
* granted, provided that the above copyright notice appear in all | |||
* copies and that both that the copyright notice and this | |||
* permission notice and warranty disclaimer appear in supporting | |||
* documentation, and that the name of the author not be used in | |||
* advertising or publicity pertaining to distribution of the | |||
* software without specific, written prior permission. | |||
* | |||
* The author disclaim all warranties with regard to this | |||
* software, including all implied warranties of merchantability | |||
* and fitness. In no event shall the author be liable for any | |||
* special, indirect or consequential damages or any damages | |||
* whatsoever resulting from loss of use, data or profits, whether | |||
* in an action of contract, negligence or other tortious action, | |||
* arising out of or in connection with the use or performance of | |||
* this software. | |||
* | |||
* Modified by: LoBo (loboris@gmail.com / https://github.com/loboris) | |||
* | |||
*/ | |||
#include "freertos/FreeRTOS.h" | |||
#include "esp_attr.h" | |||
#include "mutex.h" | |||
#define portEND_SWITCHING_ISR(xSwitchRequired) \ | |||
if (xSwitchRequired) { \ | |||
_frxt_setup_switch(); \ | |||
} | |||
extern unsigned port_interruptNesting[portNUM_PROCESSORS]; | |||
void _mtx_init() { | |||
} | |||
void mtx_init(struct mtx *mutex, const char *name, const char *type, int opts) { | |||
mutex->sem = xSemaphoreCreateBinary(); | |||
if (mutex->sem) { | |||
if (port_interruptNesting[xPortGetCoreID()] != 0) { | |||
BaseType_t xHigherPriorityTaskWoken = pdFALSE; | |||
xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken); | |||
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); | |||
} else { | |||
xSemaphoreGive( mutex->sem ); | |||
} | |||
} | |||
} | |||
void IRAM_ATTR mtx_lock(struct mtx *mutex) { | |||
if (port_interruptNesting[xPortGetCoreID()] != 0) { | |||
BaseType_t xHigherPriorityTaskWoken = pdFALSE; | |||
xSemaphoreTakeFromISR( mutex->sem, &xHigherPriorityTaskWoken ); | |||
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); | |||
} else { | |||
xSemaphoreTake( mutex->sem, portMAX_DELAY ); | |||
} | |||
} | |||
int mtx_trylock(struct mtx *mutex) { | |||
if (xSemaphoreTake( mutex->sem, 0 ) == pdTRUE) { | |||
return 1; | |||
} else { | |||
return 0; | |||
} | |||
} | |||
void IRAM_ATTR mtx_unlock(struct mtx *mutex) { | |||
if (port_interruptNesting[xPortGetCoreID()] != 0) { | |||
BaseType_t xHigherPriorityTaskWoken = pdFALSE; | |||
xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken ); | |||
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); | |||
} else { | |||
xSemaphoreGive( mutex->sem ); | |||
} | |||
} | |||
void mtx_destroy(struct mtx *mutex) { | |||
if (port_interruptNesting[xPortGetCoreID()] != 0) { | |||
BaseType_t xHigherPriorityTaskWoken = pdFALSE; | |||
xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken ); | |||
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); | |||
} else { | |||
xSemaphoreGive( mutex->sem ); | |||
} | |||
vSemaphoreDelete( mutex->sem ); | |||
mutex->sem = 0; | |||
} |
@@ -0,0 +1,53 @@ | |||
/* | |||
* Lua RTOS, mutex api implementation over FreeRTOS | |||
* | |||
* Copyright (C) 2015 - 2017 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* | |||
* Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
* All rights reserved. | |||
* | |||
* Permission to use, copy, modify, and distribute this software | |||
* and its documentation for any purpose and without fee is hereby | |||
* granted, provided that the above copyright notice appear in all | |||
* copies and that both that the copyright notice and this | |||
* permission notice and warranty disclaimer appear in supporting | |||
* documentation, and that the name of the author not be used in | |||
* advertising or publicity pertaining to distribution of the | |||
* software without specific, written prior permission. | |||
* | |||
* The author disclaim all warranties with regard to this | |||
* software, including all implied warranties of merchantability | |||
* and fitness. In no event shall the author be liable for any | |||
* special, indirect or consequential damages or any damages | |||
* whatsoever resulting from loss of use, data or profits, whether | |||
* in an action of contract, negligence or other tortious action, | |||
* arising out of or in connection with the use or performance of | |||
* this software. | |||
* | |||
* Modified by: LoBo (loboris@gmail.com / https://github.com/loboris) | |||
* | |||
*/ | |||
#ifndef MUTEX_H_H | |||
#define MUTEX_H_H | |||
#include "freertos/FreeRTOS.h" | |||
#include "freertos/semphr.h" | |||
#define MUTEX_INITIALIZER {.sem = 0} | |||
struct mtx { | |||
SemaphoreHandle_t sem; | |||
}; | |||
void mtx_init(struct mtx *mutex, const char *name, const char *type, int opts); | |||
void mtx_lock(struct mtx *mutex); | |||
int mtx_trylock(struct mtx *mutex); | |||
void mtx_unlock(struct mtx *mutex); | |||
void mtx_destroy(struct mtx *mutex); | |||
#endif /* MUTEX_H_H */ | |||
@@ -0,0 +1,813 @@ | |||
/* | |||
* spiffs.h | |||
* | |||
* Created on: May 26, 2013 | |||
* Author: petera | |||
*/ | |||
#ifndef SPIFFS_H_ | |||
#define SPIFFS_H_ | |||
#if defined(__cplusplus) | |||
extern "C" { | |||
#endif | |||
#include "spiffs_config.h" | |||
#define SPIFFS_OK 0 | |||
#define SPIFFS_ERR_NOT_MOUNTED -10000 | |||
#define SPIFFS_ERR_FULL -10001 | |||
#define SPIFFS_ERR_NOT_FOUND -10002 | |||
#define SPIFFS_ERR_END_OF_OBJECT -10003 | |||
#define SPIFFS_ERR_DELETED -10004 | |||
#define SPIFFS_ERR_NOT_FINALIZED -10005 | |||
#define SPIFFS_ERR_NOT_INDEX -10006 | |||
#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007 | |||
#define SPIFFS_ERR_FILE_CLOSED -10008 | |||
#define SPIFFS_ERR_FILE_DELETED -10009 | |||
#define SPIFFS_ERR_BAD_DESCRIPTOR -10010 | |||
#define SPIFFS_ERR_IS_INDEX -10011 | |||
#define SPIFFS_ERR_IS_FREE -10012 | |||
#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013 | |||
#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014 | |||
#define SPIFFS_ERR_INDEX_REF_FREE -10015 | |||
#define SPIFFS_ERR_INDEX_REF_LU -10016 | |||
#define SPIFFS_ERR_INDEX_REF_INVALID -10017 | |||
#define SPIFFS_ERR_INDEX_FREE -10018 | |||
#define SPIFFS_ERR_INDEX_LU -10019 | |||
#define SPIFFS_ERR_INDEX_INVALID -10020 | |||
#define SPIFFS_ERR_NOT_WRITABLE -10021 | |||
#define SPIFFS_ERR_NOT_READABLE -10022 | |||
#define SPIFFS_ERR_CONFLICTING_NAME -10023 | |||
#define SPIFFS_ERR_NOT_CONFIGURED -10024 | |||
#define SPIFFS_ERR_NOT_A_FS -10025 | |||
#define SPIFFS_ERR_MOUNTED -10026 | |||
#define SPIFFS_ERR_ERASE_FAIL -10027 | |||
#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028 | |||
#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 | |||
#define SPIFFS_ERR_FILE_EXISTS -10030 | |||
#define SPIFFS_ERR_NOT_A_FILE -10031 | |||
#define SPIFFS_ERR_RO_NOT_IMPL -10032 | |||
#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033 | |||
#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034 | |||
#define SPIFFS_ERR_PROBE_NOT_A_FS -10035 | |||
#define SPIFFS_ERR_NAME_TOO_LONG -10036 | |||
#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037 | |||
#define SPIFFS_ERR_IX_MAP_MAPPED -10038 | |||
#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039 | |||
#define SPIFFS_ERR_INTERNAL -10050 | |||
#define SPIFFS_ERR_TEST -10100 | |||
// spiffs file descriptor index type. must be signed | |||
typedef s16_t spiffs_file; | |||
// spiffs file descriptor flags | |||
typedef u16_t spiffs_flags; | |||
// spiffs file mode | |||
typedef u16_t spiffs_mode; | |||
// object type | |||
typedef u8_t spiffs_obj_type; | |||
struct spiffs_t; | |||
#if SPIFFS_HAL_CALLBACK_EXTRA | |||
/* spi read call function type */ | |||
typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); | |||
/* spi write call function type */ | |||
typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); | |||
/* spi erase call function type */ | |||
typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); | |||
#else // SPIFFS_HAL_CALLBACK_EXTRA | |||
/* spi read call function type */ | |||
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); | |||
/* spi write call function type */ | |||
typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); | |||
/* spi erase call function type */ | |||
typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); | |||
#endif // SPIFFS_HAL_CALLBACK_EXTRA | |||
/* file system check callback report operation */ | |||
typedef enum { | |||
SPIFFS_CHECK_LOOKUP = 0, | |||
SPIFFS_CHECK_INDEX, | |||
SPIFFS_CHECK_PAGE | |||
} spiffs_check_type; | |||
/* file system check callback report type */ | |||
typedef enum { | |||
SPIFFS_CHECK_PROGRESS = 0, | |||
SPIFFS_CHECK_ERROR, | |||
SPIFFS_CHECK_FIX_INDEX, | |||
SPIFFS_CHECK_FIX_LOOKUP, | |||
SPIFFS_CHECK_DELETE_ORPHANED_INDEX, | |||
SPIFFS_CHECK_DELETE_PAGE, | |||
SPIFFS_CHECK_DELETE_BAD_FILE | |||
} spiffs_check_report; | |||
/* file system check callback function */ | |||
#if SPIFFS_HAL_CALLBACK_EXTRA | |||
typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, | |||
u32_t arg1, u32_t arg2); | |||
#else // SPIFFS_HAL_CALLBACK_EXTRA | |||
typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, | |||
u32_t arg1, u32_t arg2); | |||
#endif // SPIFFS_HAL_CALLBACK_EXTRA | |||
/* file system listener callback operation */ | |||
typedef enum { | |||
/* the file has been created */ | |||
SPIFFS_CB_CREATED = 0, | |||
/* the file has been updated or moved to another page */ | |||
SPIFFS_CB_UPDATED, | |||
/* the file has been deleted */ | |||
SPIFFS_CB_DELETED | |||
} spiffs_fileop_type; | |||
/* file system listener callback function */ | |||
typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix); | |||
#ifndef SPIFFS_DBG | |||
#define SPIFFS_DBG(...) \ | |||
printf(__VA_ARGS__) | |||
#endif | |||
#ifndef SPIFFS_GC_DBG | |||
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) | |||
#endif | |||
#ifndef SPIFFS_CACHE_DBG | |||
#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) | |||
#endif | |||
#ifndef SPIFFS_CHECK_DBG | |||
#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) | |||
#endif | |||
/* Any write to the filehandle is appended to end of the file */ | |||
#define SPIFFS_APPEND (1<<0) | |||
#define SPIFFS_O_APPEND SPIFFS_APPEND | |||
/* If the opened file exists, it will be truncated to zero length before opened */ | |||
#define SPIFFS_TRUNC (1<<1) | |||
#define SPIFFS_O_TRUNC SPIFFS_TRUNC | |||
/* If the opened file does not exist, it will be created before opened */ | |||
#define SPIFFS_CREAT (1<<2) | |||
#define SPIFFS_O_CREAT SPIFFS_CREAT | |||
/* The opened file may only be read */ | |||
#define SPIFFS_RDONLY (1<<3) | |||
#define SPIFFS_O_RDONLY SPIFFS_RDONLY | |||
/* The opened file may only be written */ | |||
#define SPIFFS_WRONLY (1<<4) | |||
#define SPIFFS_O_WRONLY SPIFFS_WRONLY | |||
/* The opened file may be both read and written */ | |||
#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) | |||
#define SPIFFS_O_RDWR SPIFFS_RDWR | |||
/* Any writes to the filehandle will never be cached but flushed directly */ | |||
#define SPIFFS_DIRECT (1<<5) | |||
#define SPIFFS_O_DIRECT SPIFFS_DIRECT | |||
/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */ | |||
#define SPIFFS_EXCL (1<<6) | |||
#define SPIFFS_O_EXCL SPIFFS_EXCL | |||
#define SPIFFS_SEEK_SET (0) | |||
#define SPIFFS_SEEK_CUR (1) | |||
#define SPIFFS_SEEK_END (2) | |||
#define SPIFFS_TYPE_FILE (1) | |||
#define SPIFFS_TYPE_DIR (2) | |||
#define SPIFFS_TYPE_HARD_LINK (3) | |||
#define SPIFFS_TYPE_SOFT_LINK (4) | |||
#ifndef SPIFFS_LOCK | |||
#define SPIFFS_LOCK(fs) | |||
#endif | |||
#ifndef SPIFFS_UNLOCK | |||
#define SPIFFS_UNLOCK(fs) | |||
#endif | |||
// phys structs | |||
// spiffs spi configuration struct | |||
typedef struct { | |||
// physical read function | |||
spiffs_read hal_read_f; | |||
// physical write function | |||
spiffs_write hal_write_f; | |||
// physical erase function | |||
spiffs_erase hal_erase_f; | |||
#if SPIFFS_SINGLETON == 0 | |||
// physical size of the spi flash | |||
u32_t phys_size; | |||
// physical offset in spi flash used for spiffs, | |||
// must be on block boundary | |||
u32_t phys_addr; | |||
// physical size when erasing a block | |||
u32_t phys_erase_block; | |||
// logical size of a block, must be on physical | |||
// block size boundary and must never be less than | |||
// a physical block | |||
u32_t log_block_size; | |||
// logical size of a page, must be at least | |||
// log_block_size / 8 | |||
u32_t log_page_size; | |||
#endif | |||
#if SPIFFS_FILEHDL_OFFSET | |||
// an integer offset added to each file handle | |||
u16_t fh_ix_offset; | |||
#endif | |||
} spiffs_config; | |||
typedef struct spiffs_t { | |||
// file system configuration | |||
spiffs_config cfg; | |||
// number of logical blocks | |||
u32_t block_count; | |||
// cursor for free blocks, block index | |||
spiffs_block_ix free_cursor_block_ix; | |||
// cursor for free blocks, entry index | |||
int free_cursor_obj_lu_entry; | |||
// cursor when searching, block index | |||
spiffs_block_ix cursor_block_ix; | |||
// cursor when searching, entry index | |||
int cursor_obj_lu_entry; | |||
// primary work buffer, size of a logical page | |||
u8_t *lu_work; | |||
// secondary work buffer, size of a logical page | |||
u8_t *work; | |||
// file descriptor memory area | |||
u8_t *fd_space; | |||
// available file descriptors | |||
u32_t fd_count; | |||
// last error | |||
s32_t err_code; | |||
// current number of free blocks | |||
u32_t free_blocks; | |||
// current number of busy pages | |||
u32_t stats_p_allocated; | |||
// current number of deleted pages | |||
u32_t stats_p_deleted; | |||
// flag indicating that garbage collector is cleaning | |||
u8_t cleaning; | |||
// max erase count amongst all blocks | |||
spiffs_obj_id max_erase_count; | |||
#if SPIFFS_GC_STATS | |||
u32_t stats_gc_runs; | |||
#endif | |||
#if SPIFFS_CACHE | |||
// cache memory | |||
void *cache; | |||
// cache size | |||
u32_t cache_size; | |||
#if SPIFFS_CACHE_STATS | |||
u32_t cache_hits; | |||
u32_t cache_misses; | |||
#endif | |||
#endif | |||
// check callback function | |||
spiffs_check_callback check_cb_f; | |||
// file callback function | |||
spiffs_file_callback file_cb_f; | |||
// mounted flag | |||
u8_t mounted; | |||
// user data | |||
void *user_data; | |||
// config magic | |||
u32_t config_magic; | |||
} spiffs; | |||
/* spiffs file status struct */ | |||
typedef struct { | |||
spiffs_obj_id obj_id; | |||
u32_t size; | |||
spiffs_obj_type type; | |||
spiffs_page_ix pix; | |||
u8_t name[SPIFFS_OBJ_NAME_LEN]; | |||
#if SPIFFS_OBJ_META_LEN | |||
u8_t meta[SPIFFS_OBJ_META_LEN]; | |||
#endif | |||
} spiffs_stat; | |||
struct spiffs_dirent { | |||
spiffs_obj_id obj_id; | |||
u8_t name[SPIFFS_OBJ_NAME_LEN]; | |||
spiffs_obj_type type; | |||
u32_t size; | |||
spiffs_page_ix pix; | |||
#if SPIFFS_OBJ_META_LEN | |||
u8_t meta[SPIFFS_OBJ_META_LEN]; | |||
#endif | |||
}; | |||
typedef struct { | |||
spiffs *fs; | |||
spiffs_block_ix block; | |||
int entry; | |||
} spiffs_DIR; | |||
#if SPIFFS_IX_MAP | |||
typedef struct { | |||
// buffer with looked up data pixes | |||
spiffs_page_ix *map_buf; | |||
// precise file byte offset | |||
u32_t offset; | |||
// start data span index of lookup buffer | |||
spiffs_span_ix start_spix; | |||
// end data span index of lookup buffer | |||
spiffs_span_ix end_spix; | |||
} spiffs_ix_map; | |||
#endif | |||
// functions | |||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 | |||
/** | |||
* Special function. This takes a spiffs config struct and returns the number | |||
* of blocks this file system was formatted with. This function relies on | |||
* that following info is set correctly in given config struct: | |||
* | |||
* phys_addr, log_page_size, and log_block_size. | |||
* | |||
* Also, hal_read_f must be set in the config struct. | |||
* | |||
* One must be sure of the correct page size and that the physical address is | |||
* correct in the probed file system when calling this function. It is not | |||
* checked if the phys_addr actually points to the start of the file system, | |||
* so one might get a false positive if entering a phys_addr somewhere in the | |||
* middle of the file system at block boundary. In addition, it is not checked | |||
* if the page size is actually correct. If it is not, weird file system sizes | |||
* will be returned. | |||
* | |||
* If this function detects a file system it returns the assumed file system | |||
* size, which can be used to set the phys_size. | |||
* | |||
* Otherwise, it returns an error indicating why it is not regarded as a file | |||
* system. | |||
* | |||
* Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK | |||
* macros. It returns the error code directly, instead of as read by | |||
* SPIFFS_errno. | |||
* | |||
* @param config essential parts of the physical and logical | |||
* configuration of the file system. | |||
*/ | |||
s32_t SPIFFS_probe_fs(spiffs_config *config); | |||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 | |||
/** | |||
* Initializes the file system dynamic parameters and mounts the filesystem. | |||
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS | |||
* if the flash does not contain a recognizable file system. | |||
* In this case, SPIFFS_format must be called prior to remounting. | |||
* @param fs the file system struct | |||
* @param config the physical and logical configuration of the file system | |||
* @param work a memory work buffer comprising 2*config->log_page_size | |||
* bytes used throughout all file system operations | |||
* @param fd_space memory for file descriptors | |||
* @param fd_space_size memory size of file descriptors | |||
* @param cache memory for cache, may be null | |||
* @param cache_size memory size of cache | |||
* @param check_cb_f callback function for reporting during consistency checks | |||
*/ | |||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, | |||
u8_t *fd_space, u32_t fd_space_size, | |||
void *cache, u32_t cache_size, | |||
spiffs_check_callback check_cb_f); | |||
/** | |||
* Unmounts the file system. All file handles will be flushed of any | |||
* cached writes and closed. | |||
* @param fs the file system struct | |||
*/ | |||
void SPIFFS_unmount(spiffs *fs); | |||
/** | |||
* Creates a new file. | |||
* @param fs the file system struct | |||
* @param path the path of the new file | |||
* @param mode ignored, for posix compliance | |||
*/ | |||
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); | |||
/** | |||
* Opens/creates a file. | |||
* @param fs the file system struct | |||
* @param path the path of the new file | |||
* @param flags the flags for the open command, can be combinations of | |||
* SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, | |||
* SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL | |||
* @param mode ignored, for posix compliance | |||
*/ | |||
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); | |||
/** | |||
* Opens a file by given dir entry. | |||
* Optimization purposes, when traversing a file system with SPIFFS_readdir | |||
* a normal SPIFFS_open would need to traverse the filesystem again to find | |||
* the file, whilst SPIFFS_open_by_dirent already knows where the file resides. | |||
* @param fs the file system struct | |||
* @param e the dir entry to the file | |||
* @param flags the flags for the open command, can be combinations of | |||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, | |||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. | |||
* SPIFFS_CREAT will have no effect in this case. | |||
* @param mode ignored, for posix compliance | |||
*/ | |||
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode); | |||
/** | |||
* Opens a file by given page index. | |||
* Optimization purposes, opens a file by directly pointing to the page | |||
* index in the spi flash. | |||
* If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE | |||
* is returned. | |||
* @param fs the file system struct | |||
* @param page_ix the page index | |||
* @param flags the flags for the open command, can be combinations of | |||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, | |||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. | |||
* SPIFFS_CREAT will have no effect in this case. | |||
* @param mode ignored, for posix compliance | |||
*/ | |||
spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode); | |||
/** | |||
* Reads from given filehandle. | |||
* @param fs the file system struct | |||
* @param fh the filehandle | |||
* @param buf where to put read data | |||
* @param len how much to read | |||
* @returns number of bytes read, or -1 if error | |||
*/ | |||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); | |||
/** | |||
* Writes to given filehandle. | |||
* @param fs the file system struct | |||
* @param fh the filehandle | |||
* @param buf the data to write | |||
* @param len how much to write | |||
* @returns number of bytes written, or -1 if error | |||
*/ | |||
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); | |||
/** | |||
* Moves the read/write file offset. Resulting offset is returned or negative if error. | |||
* lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. | |||
* @param fs the file system struct | |||
* @param fh the filehandle | |||
* @param offs how much/where to move the offset | |||
* @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes | |||
* if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset | |||
* if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative | |||
*/ | |||
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); | |||
/** | |||
* Removes a file by path | |||
* @param fs the file system struct | |||
* @param path the path of the file to remove | |||
*/ | |||
s32_t SPIFFS_remove(spiffs *fs, const char *path); | |||
/** | |||
* Removes a file by filehandle | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to remove | |||
*/ | |||
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Gets file status by path | |||
* @param fs the file system struct | |||
* @param path the path of the file to stat | |||
* @param s the stat struct to populate | |||
*/ | |||
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); | |||
/** | |||
* Gets file status by filehandle | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to stat | |||
* @param s the stat struct to populate | |||
*/ | |||
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); | |||
/** | |||
* Flushes all pending write operations from cache for given file | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to flush | |||
*/ | |||
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Closes a filehandle. If there are pending write operations, these are finalized before closing. | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to close | |||
*/ | |||
s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Renames a file | |||
* @param fs the file system struct | |||
* @param old path of file to rename | |||
* @param newPath new path of file | |||
*/ | |||
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath); | |||
#if SPIFFS_OBJ_META_LEN | |||
/** | |||
* Updates file's metadata | |||
* @param fs the file system struct | |||
* @param path path to the file | |||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. | |||
*/ | |||
s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); | |||
/** | |||
* Updates file's metadata | |||
* @param fs the file system struct | |||
* @param fh file handle of the file | |||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. | |||
*/ | |||
s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); | |||
#endif | |||
/** | |||
* Returns last error of last file operation. | |||
* @param fs the file system struct | |||
*/ | |||
s32_t SPIFFS_errno(spiffs *fs); | |||
/** | |||
* Clears last error. | |||
* @param fs the file system struct | |||
*/ | |||
void SPIFFS_clearerr(spiffs *fs); | |||
/** | |||
* Opens a directory stream corresponding to the given name. | |||
* The stream is positioned at the first entry in the directory. | |||
* On hydrogen builds the name argument is ignored as hydrogen builds always correspond | |||
* to a flat file structure - no directories. | |||
* @param fs the file system struct | |||
* @param name the name of the directory | |||
* @param d pointer the directory stream to be populated | |||
*/ | |||
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); | |||
/** | |||
* Closes a directory stream | |||
* @param d the directory stream to close | |||
*/ | |||
s32_t SPIFFS_closedir(spiffs_DIR *d); | |||
/** | |||
* Reads a directory into given spifs_dirent struct. | |||
* @param d pointer to the directory stream | |||
* @param e the dirent struct to be populated | |||
* @returns null if error or end of stream, else given dirent is returned | |||
*/ | |||
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); | |||
/** | |||
* Runs a consistency check on given filesystem. | |||
* @param fs the file system struct | |||
*/ | |||
s32_t SPIFFS_check(spiffs *fs); | |||
/** | |||
* Returns number of total bytes available and number of used bytes. | |||
* This is an estimation, and depends on if there a many files with little | |||
* data or few files with much data. | |||
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should | |||
* run. This indicates a power loss in midst of things. In worst case | |||
* (repeated powerlosses in mending or gc) you might have to delete some files. | |||
* | |||
* @param fs the file system struct | |||
* @param total total number of bytes in filesystem | |||
* @param used used number of bytes in filesystem | |||
*/ | |||
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); | |||
/** | |||
* Formats the entire file system. All data will be lost. | |||
* The filesystem must not be mounted when calling this. | |||
* | |||
* NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount | |||
* MUST be called prior to formatting in order to configure the filesystem. | |||
* If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling | |||
* SPIFFS_format. | |||
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling | |||
* SPIFFS_unmount first. | |||
* | |||
* @param fs the file system struct | |||
*/ | |||
s32_t SPIFFS_format(spiffs *fs); | |||
/** | |||
* Returns nonzero if spiffs is mounted, or zero if unmounted. | |||
* @param fs the file system struct | |||
*/ | |||
u8_t SPIFFS_mounted(spiffs *fs); | |||
/** | |||
* Tries to find a block where most or all pages are deleted, and erase that | |||
* block if found. Does not care for wear levelling. Will not move pages | |||
* around. | |||
* If parameter max_free_pages are set to 0, only blocks with only deleted | |||
* pages will be selected. | |||
* | |||
* NB: the garbage collector is automatically called when spiffs needs free | |||
* pages. The reason for this function is to give possibility to do background | |||
* tidying when user knows the system is idle. | |||
* | |||
* Use with care. | |||
* | |||
* Setting max_free_pages to anything larger than zero will eventually wear | |||
* flash more as a block containing free pages can be erased. | |||
* | |||
* Will set err_no to SPIFFS_OK if a block was found and erased, | |||
* SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, | |||
* or other error. | |||
* | |||
* @param fs the file system struct | |||
* @param max_free_pages maximum number allowed free pages in block | |||
*/ | |||
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); | |||
/** | |||
* Will try to make room for given amount of bytes in the filesystem by moving | |||
* pages and erasing blocks. | |||
* If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If | |||
* there already is this amount (or more) of free space, SPIFFS_gc will | |||
* silently return. It is recommended to call SPIFFS_info before invoking | |||
* this method in order to determine what amount of bytes to give. | |||
* | |||
* NB: the garbage collector is automatically called when spiffs needs free | |||
* pages. The reason for this function is to give possibility to do background | |||
* tidying when user knows the system is idle. | |||
* | |||
* Use with care. | |||
* | |||
* @param fs the file system struct | |||
* @param size amount of bytes that should be freed | |||
*/ | |||
s32_t SPIFFS_gc(spiffs *fs, u32_t size); | |||
/** | |||
* Check if EOF reached. | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to check | |||
*/ | |||
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Get position in file. | |||
* @param fs the file system struct | |||
* @param fh the filehandle of the file to check | |||
*/ | |||
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Registers a callback function that keeps track on operations on file | |||
* headers. Do note, that this callback is called from within internal spiffs | |||
* mechanisms. Any operations on the actual file system being callbacked from | |||
* in this callback will mess things up for sure - do not do this. | |||
* This can be used to track where files are and move around during garbage | |||
* collection, which in turn can be used to build location tables in ram. | |||
* Used in conjuction with SPIFFS_open_by_page this may improve performance | |||
* when opening a lot of files. | |||
* Must be invoked after mount. | |||
* | |||
* @param fs the file system struct | |||
* @param cb_func the callback on file operations | |||
*/ | |||
s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); | |||
#if SPIFFS_IX_MAP | |||
/** | |||
* Maps the first level index lookup to a given memory map. | |||
* This will make reading big files faster, as the memory map will be used for | |||
* looking up data pages instead of searching for the indices on the physical | |||
* medium. When mapping, all affected indicies are found and the information is | |||
* copied to the array. | |||
* Whole file or only parts of it may be mapped. The index map will cover file | |||
* contents from argument offset until and including arguments (offset+len). | |||
* It is valid to map a longer range than the current file size. The map will | |||
* then be populated when the file grows. | |||
* On garbage collections and file data page movements, the map array will be | |||
* automatically updated. Do not tamper with the map array, as this contains | |||
* the references to the data pages. Modifying it from outside will corrupt any | |||
* future readings using this file descriptor. | |||
* The map will no longer be used when the file descriptor closed or the file | |||
* is unmapped. | |||
* This can be useful to get faster and more deterministic timing when reading | |||
* large files, or when seeking and reading a lot within a file. | |||
* @param fs the file system struct | |||
* @param fh the file handle of the file to map | |||
* @param map a spiffs_ix_map struct, describing the index map | |||
* @param offset absolute file offset where to start the index map | |||
* @param len length of the mapping in actual file bytes | |||
* @param map_buf the array buffer for the look up data - number of required | |||
* elements in the array can be derived from function | |||
* SPIFFS_bytes_to_ix_map_entries given the length | |||
*/ | |||
s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, | |||
u32_t offset, u32_t len, spiffs_page_ix *map_buf); | |||
/** | |||
* Unmaps the index lookup from this filehandle. All future readings will | |||
* proceed as normal, requiring reading of the first level indices from | |||
* physical media. | |||
* The map and map buffer given in function SPIFFS_ix_map will no longer be | |||
* referenced by spiffs. | |||
* It is not strictly necessary to unmap a file before closing it, as closing | |||
* a file will automatically unmap it. | |||
* @param fs the file system struct | |||
* @param fh the file handle of the file to unmap | |||
*/ | |||
s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh); | |||
/** | |||
* Moves the offset for the index map given in function SPIFFS_ix_map. Parts or | |||
* all of the map buffer will repopulated. | |||
* @param fs the file system struct | |||
* @param fh the mapped file handle of the file to remap | |||
* @param offset new absolute file offset where to start the index map | |||
*/ | |||
s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs); | |||
/** | |||
* Utility function to get number of spiffs_page_ix entries a map buffer must | |||
* contain on order to map given amount of file data in bytes. | |||
* See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. | |||
* @param fs the file system struct | |||
* @param bytes number of file data bytes to map | |||
* @return needed number of elements in a spiffs_page_ix array needed to | |||
* map given amount of bytes in a file | |||
*/ | |||
s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes); | |||
/** | |||
* Utility function to amount of file data bytes that can be mapped when | |||
* mapping a file with buffer having given number of spiffs_page_ix entries. | |||
* See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. | |||
* @param fs the file system struct | |||
* @param map_page_ix_entries number of entries in a spiffs_page_ix array | |||
* @return amount of file data in bytes that can be mapped given a map | |||
* buffer having given amount of spiffs_page_ix entries | |||
*/ | |||
s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); | |||
#endif // SPIFFS_IX_MAP | |||
#if SPIFFS_TEST_VISUALISATION | |||
/** | |||
* Prints out a visualization of the filesystem. | |||
* @param fs the file system struct | |||
*/ | |||
s32_t SPIFFS_vis(spiffs *fs); | |||
#endif | |||
#if SPIFFS_BUFFER_HELP | |||
/** | |||
* Returns number of bytes needed for the filedescriptor buffer given | |||
* amount of file descriptors. | |||
*/ | |||
u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs); | |||
#if SPIFFS_CACHE | |||
/** | |||
* Returns number of bytes needed for the cache buffer given | |||
* amount of cache pages. | |||
*/ | |||
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); | |||
#endif | |||
#endif | |||
#if SPIFFS_CACHE | |||
#endif | |||
#if defined(__cplusplus) | |||
} | |||
#endif | |||
#endif /* SPIFFS_H_ */ |
@@ -0,0 +1,314 @@ | |||
/* | |||
* spiffs_cache.c | |||
* | |||
* Created on: Jun 23, 2013 | |||
* Author: petera | |||
*/ | |||
#include "spiffs.h" | |||
#include "spiffs_nucleus.h" | |||
#if SPIFFS_CACHE | |||
// returns cached page for give page index, or null if no such cached page | |||
static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { | |||
spiffs_cache *cache = spiffs_get_cache(fs); | |||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; | |||
int i; | |||
for (i = 0; i < cache->cpage_count; i++) { | |||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); | |||
if ((cache->cpage_use_map & (1<<i)) && | |||
(cp->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<<ix)) { | |||
if (write_back && | |||
(cp->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<<i)) == 0) { | |||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); | |||
cache->cpage_use_map |= (1<<i); | |||
cp->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<<i)) && | |||
(cp->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 |
@@ -0,0 +1,995 @@ | |||
/* | |||
* spiffs_check.c | |||
* | |||
* Contains functionality for checking file system consistency | |||
* and mending problems. | |||
* Three levels of consistency checks are implemented: | |||
* | |||
* Look up consistency | |||
* Checks if indices in lookup pages are coherent with page headers | |||
* Object index consistency | |||
* Checks if there are any orphaned object indices (missing object index headers). | |||
* If an object index is found but not its header, the object index is deleted. | |||
* This is critical for the following page consistency check. | |||
* Page consistency | |||
* Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed | |||
* | |||
* | |||
* Created on: Jul 7, 2013 | |||
* Author: petera | |||
*/ | |||
#include "spiffs.h" | |||
#include "spiffs_nucleus.h" | |||
#if !SPIFFS_READ_ONLY | |||
#if SPIFFS_HAL_CALLBACK_EXTRA | |||
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ | |||
do { \ | |||
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ | |||
} while (0) | |||
#else | |||
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ | |||
do { \ | |||
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ | |||
} while (0) | |||
#endif | |||
//--------------------------------------- | |||
// Look up consistency | |||
// searches in the object indices and returns the referenced page index given | |||
// the object id and the data span index | |||
// destroys fs->lu_work | |||
static s32_t spiffs_object_get_data_page_index_reference( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_span_ix data_spix, | |||
spiffs_page_ix *pix, | |||
spiffs_page_ix *objix_pix) { | |||
s32_t res; | |||
// calculate object index span index for given data page span index | |||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); | |||
// find obj index for obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix); | |||
SPIFFS_CHECK_RES(res); | |||
// load obj index entry | |||
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); | |||
if (objix_spix == 0) { | |||
// get referenced page from object index header | |||
addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); | |||
} else { | |||
// get referenced page from object index | |||
addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); | |||
} | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); | |||
return res; | |||
} | |||
// copies page contents to a new page | |||
static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) { | |||
s32_t res; | |||
res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_phys_cpy(fs, 0, | |||
SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_DATA_PAGE_SIZE(fs)); | |||
SPIFFS_CHECK_RES(res); | |||
return res; | |||
} | |||
// rewrites the object index for given object id and replaces the | |||
// data page index to a new page index | |||
static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) { | |||
s32_t res; | |||
spiffs_block_ix bix; | |||
int entry; | |||
spiffs_page_ix free_pix; | |||
obj_id |= SPIFFS_OBJ_ID_IX_FLAG; | |||
// find free entry | |||
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); | |||
SPIFFS_CHECK_RES(res); | |||
free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); | |||
// calculate object index span index for given data page span index | |||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); | |||
if (objix_spix == 0) { | |||
// calc index in index header | |||
entry = data_spix; | |||
} else { | |||
// calc entry in index | |||
entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); | |||
} | |||
// load index | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; | |||
// be ultra safe, double check header against provided data | |||
if (objix_p_hdr->obj_id != obj_id) { | |||
spiffs_page_delete(fs, free_pix); | |||
return SPIFFS_ERR_CHECK_OBJ_ID_MISM; | |||
} | |||
if (objix_p_hdr->span_ix != objix_spix) { | |||
spiffs_page_delete(fs, free_pix); | |||
return SPIFFS_ERR_CHECK_SPIX_MISM; | |||
} | |||
if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX | | |||
SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) != | |||
(SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) { | |||
spiffs_page_delete(fs, free_pix); | |||
return SPIFFS_ERR_CHECK_FLAGS_BAD; | |||
} | |||
// rewrite in mem | |||
if (objix_spix == 0) { | |||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; | |||
} else { | |||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; | |||
} | |||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, | |||
0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), | |||
sizeof(spiffs_obj_id), | |||
(u8_t *)&obj_id); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_page_delete(fs, objix_pix); | |||
return res; | |||
} | |||
// deletes an object just by marking object index header as deleted | |||
static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) { | |||
spiffs_page_ix objix_hdr_pix; | |||
s32_t res; | |||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
return SPIFFS_OK; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE; | |||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), | |||
sizeof(u8_t), | |||
(u8_t *)&flags); | |||
return res; | |||
} | |||
// validates the given look up entry | |||
static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr, | |||
spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) { | |||
(void)cur_block; | |||
(void)cur_entry; | |||
u8_t delete_page = 0; | |||
s32_t res = SPIFFS_OK; | |||
spiffs_page_ix objix_pix; | |||
spiffs_page_ix ref_pix; | |||
// check validity, take actions | |||
if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || | |||
((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) { | |||
// look up entry deleted / free but used in page header | |||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix); | |||
*reload_lu = 1; | |||
delete_page = 1; | |||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { | |||
// header says data page | |||
// data page can be removed if not referenced by some object index | |||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// no object with this id, so remove page safely | |||
res = SPIFFS_OK; | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
if (ref_pix == cur_pix) { | |||
// data page referenced by object index but deleted in lu | |||
// copy page to new place and re-write the object index to new place | |||
spiffs_page_ix new_pix; | |||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); | |||
SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
*reload_lu = 1; | |||
SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix); | |||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); | |||
res = spiffs_page_delete(fs, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); | |||
} else { | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
} | |||
} else { | |||
// header says index page | |||
// index page can be removed if other index with same obj_id and spanix is found | |||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// no such index page found, check for a data page amongst page headers | |||
// lu cannot be trusted | |||
res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0); | |||
if (res == SPIFFS_OK) { // ignore other errors | |||
// got a data page also, assume lu corruption only, rewrite to new page | |||
spiffs_page_ix new_pix; | |||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); | |||
SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
*reload_lu = 1; | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); | |||
} | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
} | |||
} | |||
if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) { | |||
// look up entry used | |||
if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) { | |||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id); | |||
delete_page = 1; | |||
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || | |||
(p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || | |||
(p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) { | |||
// page deleted or not finalized, just remove it | |||
} else { | |||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { | |||
// if data page, check for reference to this page | |||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// no object with this id, so remove page safely | |||
res = SPIFFS_OK; | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
// if found, rewrite page with object id, update index, and delete current | |||
if (ref_pix == cur_pix) { | |||
spiffs_page_ix new_pix; | |||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); | |||
res = spiffs_page_delete(fs, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); | |||
*reload_lu = 1; | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
} | |||
} else { | |||
// else if index, check for other pages with both obj_id's and spanix | |||
spiffs_page_ix objix_pix_lu, objix_pix_ph; | |||
// see if other object index page exists for lookup obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_lu = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
// see if other object index exists for page header obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_ph = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
// if both obj_id's found, just delete current | |||
if (objix_pix_ph == 0 || objix_pix_lu == 0) { | |||
// otherwise try finding first corresponding data pages | |||
spiffs_page_ix data_pix_lu, data_pix_ph; | |||
// see if other data page exists for look up obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_lu = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
// see if other data page exists for page header obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_ph = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_page_header new_ph; | |||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL); | |||
new_ph.span_ix = p_hdr->span_ix; | |||
spiffs_page_ix new_pix; | |||
if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) || | |||
(objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) { | |||
// got a data page for page header obj id | |||
// rewrite as obj_id_ph | |||
new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; | |||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); | |||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); | |||
SPIFFS_CHECK_RES(res); | |||
*reload_lu = 1; | |||
} else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || | |||
(objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) { | |||
// got a data page for look up obj id | |||
// rewrite as obj_id_lu | |||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; | |||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); | |||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
*reload_lu = 1; | |||
} else { | |||
// cannot safely do anything | |||
SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n"); | |||
} | |||
} | |||
} | |||
} | |||
} else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || | |||
((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) { | |||
SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix); | |||
spiffs_page_ix data_pix, objix_pix_d; | |||
// see if other data page exists for given obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
data_pix = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
// see if other object index exists for given obj id and span index | |||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
objix_pix_d = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
delete_page = 1; | |||
// if other data page exists and object index exists, just delete page | |||
if (data_pix && objix_pix_d) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n"); | |||
} else | |||
// if only data page exists, make this page index | |||
if (data_pix && objix_pix_d == 0) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); | |||
spiffs_page_header new_ph; | |||
spiffs_page_ix new_pix; | |||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); | |||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; | |||
new_ph.span_ix = p_hdr->span_ix; | |||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); | |||
SPIFFS_CHECK_RES(res); | |||
} else | |||
// if only index exists, make data page | |||
if (data_pix == 0 && objix_pix_d) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); | |||
spiffs_page_header new_ph; | |||
spiffs_page_ix new_pix; | |||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); | |||
new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; | |||
new_ph.span_ix = p_hdr->span_ix; | |||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); | |||
SPIFFS_CHECK_RES(res); | |||
} else { | |||
// if nothing exists, we cannot safely make a decision - delete | |||
} | |||
} | |||
else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) { | |||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix); | |||
delete_page = 1; | |||
} else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) { | |||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix); | |||
// page can be removed if not referenced by object index | |||
*reload_lu = 1; | |||
res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// no object with this id, so remove page safely | |||
res = SPIFFS_OK; | |||
delete_page = 1; | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
if (ref_pix != cur_pix) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n"); | |||
delete_page = 1; | |||
} else { | |||
// page referenced by object index but not final | |||
// just finalize | |||
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); | |||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; | |||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), | |||
sizeof(u8_t), (u8_t*)&flags); | |||
} | |||
} | |||
} | |||
} | |||
if (delete_page) { | |||
SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
return res; | |||
} | |||
static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, | |||
const void *user_const_p, void *user_var_p) { | |||
(void)user_const_p; | |||
(void)user_var_p; | |||
s32_t res = SPIFFS_OK; | |||
spiffs_page_header p_hdr; | |||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, | |||
(cur_block * 256)/fs->block_count, 0); | |||
// load header | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
int reload_lu = 0; | |||
res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu); | |||
SPIFFS_CHECK_RES(res); | |||
if (res == SPIFFS_OK) { | |||
return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; | |||
} | |||
return res; | |||
} | |||
// Scans all object look up. For each entry, corresponding page header is checked for validity. | |||
// If an object index header page is found, this is also checked | |||
s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { | |||
(void)check_all_objects; | |||
s32_t res = SPIFFS_OK; | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); | |||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); | |||
if (res == SPIFFS_VIS_END) { | |||
res = SPIFFS_OK; | |||
} | |||
if (res != SPIFFS_OK) { | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); | |||
} | |||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); | |||
return res; | |||
} | |||
//--------------------------------------- | |||
// Page consistency | |||
// Scans all pages (except lu pages), reserves 4 bits in working memory for each page | |||
// bit 0: 0 == FREE|DELETED, 1 == USED | |||
// bit 1: 0 == UNREFERENCED, 1 == REFERENCED | |||
// bit 2: 0 == NOT_INDEX, 1 == INDEX | |||
// bit 3: unused | |||
// A consistent file system will have only pages being | |||
// * x000 free, unreferenced, not index | |||
// * x011 used, referenced only once, not index | |||
// * x101 used, unreferenced, index | |||
// The working memory might not fit all pages so several scans might be needed | |||
static s32_t spiffs_page_consistency_check_i(spiffs *fs) { | |||
const u32_t bits = 4; | |||
const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits; | |||
s32_t res = SPIFFS_OK; | |||
spiffs_page_ix pix_offset = 0; | |||
// for each range of pages fitting into work memory | |||
while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { | |||
// set this flag to abort all checks and rescan the page range | |||
u8_t restart = 0; | |||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); | |||
spiffs_block_ix cur_block = 0; | |||
// build consistency bitmap for id range traversing all blocks | |||
while (!restart && cur_block < fs->block_count) { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, | |||
(pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + | |||
((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), | |||
0); | |||
// traverse each page except for lookup pages | |||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; | |||
while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { | |||
//if ((cur_pix & 0xff) == 0) | |||
// SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n", | |||
// cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); | |||
// read header | |||
spiffs_page_header p_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan); | |||
const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits); | |||
const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits; | |||
if (within_range && | |||
(p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) { | |||
// used | |||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0)); | |||
} | |||
if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) && | |||
(p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) && | |||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) { | |||
// found non-deleted index | |||
if (within_range) { | |||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2)); | |||
} | |||
// load non-deleted index | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
// traverse index for referenced pages | |||
spiffs_page_ix *object_page_index; | |||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; | |||
int entries; | |||
int i; | |||
spiffs_span_ix data_spix_offset; | |||
if (p_hdr.span_ix == 0) { | |||
// object header page index | |||
entries = SPIFFS_OBJ_HDR_IX_LEN(fs); | |||
data_spix_offset = 0; | |||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); | |||
} else { | |||
// object page index | |||
entries = SPIFFS_OBJ_IX_LEN(fs); | |||
data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1); | |||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)); | |||
} | |||
// for all entries in index | |||
for (i = 0; !restart && i < entries; i++) { | |||
spiffs_page_ix rpix = object_page_index[i]; | |||
u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan; | |||
if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs)) | |||
|| (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) { | |||
// bad reference | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n", | |||
rpix, cur_pix); | |||
// check for data page elsewhere | |||
spiffs_page_ix data_pix; | |||
res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, | |||
data_spix_offset + i, 0, &data_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
data_pix = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
if (data_pix == 0) { | |||
// if not, allocate free page | |||
spiffs_page_header new_ph; | |||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); | |||
new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; | |||
new_ph.span_ix = data_spix_offset + i; | |||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); | |||
SPIFFS_CHECK_RES(res); | |||
SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix); | |||
} | |||
// remap index | |||
SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix); | |||
res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, | |||
data_spix_offset + i, data_pix, cur_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); | |||
// delete file | |||
res = spiffs_page_delete(fs, cur_pix); | |||
} else { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
restart = 1; | |||
} else if (rpix_within_range) { | |||
// valid reference | |||
// read referenced page header | |||
spiffs_page_header rp_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
// cross reference page header check | |||
if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) || | |||
rp_hdr.span_ix != data_spix_offset + i || | |||
(rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != | |||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) { | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n", | |||
rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, | |||
rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); | |||
// try finding correct page | |||
spiffs_page_ix data_pix; | |||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, | |||
data_spix_offset + i, rpix, &data_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
res = SPIFFS_OK; | |||
data_pix = 0; | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
if (data_pix == 0) { | |||
// not found, this index is badly borked | |||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); | |||
SPIFFS_CHECK_RES(res); | |||
break; | |||
} else { | |||
// found it, so rewrite index | |||
SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n", | |||
data_pix, cur_pix, p_hdr.obj_id); | |||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); | |||
} else { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
restart = 1; | |||
} | |||
} | |||
else { | |||
// mark rpix as referenced | |||
const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits); | |||
const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits; | |||
if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) { | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n", | |||
rpix, cur_pix); | |||
// Here, we should have fixed all broken references - getting this means there | |||
// must be multiple files with same object id. Only solution is to delete | |||
// the object which is referring to this page | |||
SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\n", | |||
p_hdr.obj_id, cur_pix); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); | |||
SPIFFS_CHECK_RES(res); | |||
// extra precaution, delete this page also | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
restart = 1; | |||
} | |||
fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1)); | |||
} | |||
} | |||
} // for all index entries | |||
} // found index | |||
// next page | |||
cur_pix++; | |||
} | |||
// next block | |||
cur_block++; | |||
} | |||
// check consistency bitmap | |||
if (!restart) { | |||
spiffs_page_ix objix_pix; | |||
spiffs_page_ix rpix; | |||
u32_t byte_ix; | |||
u8_t bit_ix; | |||
for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) { | |||
for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) { | |||
u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7; | |||
spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix; | |||
// 000 ok - free, unreferenced, not index | |||
if (bitmask == 0x1) { | |||
// 001 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix); | |||
u8_t rewrite_ix_to_this = 0; | |||
u8_t delete_page = 0; | |||
// check corresponding object index entry | |||
spiffs_page_header p_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix, | |||
&rpix, &objix_pix); | |||
if (res == SPIFFS_OK) { | |||
if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) { | |||
// pointing to a bad page altogether, rewrite index to this | |||
rewrite_ix_to_this = 1; | |||
SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix); | |||
} else { | |||
// pointing to something else, check what | |||
spiffs_page_header rp_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) && | |||
((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == | |||
(SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) { | |||
// pointing to something else valid, just delete this page then | |||
SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix); | |||
delete_page = 1; | |||
} else { | |||
// pointing to something weird, update index to point to this page instead | |||
if (rpix != cur_pix) { | |||
SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix, | |||
(rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", | |||
(rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", | |||
(rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", | |||
(rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "", | |||
cur_pix); | |||
rewrite_ix_to_this = 1; | |||
} else { | |||
// should not happen, destined for fubar | |||
} | |||
} | |||
} | |||
} else if (res == SPIFFS_ERR_NOT_FOUND) { | |||
SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix); | |||
delete_page = 1; | |||
res = SPIFFS_OK; | |||
} | |||
if (rewrite_ix_to_this) { | |||
// if pointing to invalid page, redirect index to this page | |||
SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n", | |||
p_hdr.obj_id, p_hdr.span_ix, cur_pix); | |||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); | |||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { | |||
// index bad also, cannot mend this file | |||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); | |||
} else { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
restart = 1; | |||
continue; | |||
} else if (delete_page) { | |||
SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
if (bitmask == 0x2) { | |||
// 010 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix); | |||
// no op, this should be taken care of when checking valid references | |||
} | |||
// 011 ok - busy, referenced, not index | |||
if (bitmask == 0x4) { | |||
// 100 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix); | |||
// this should never happen, major fubar | |||
} | |||
// 101 ok - busy, unreferenced, index | |||
if (bitmask == 0x6) { | |||
// 110 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix); | |||
// no op, this should be taken care of when checking valid references | |||
} | |||
if (bitmask == 0x7) { | |||
// 111 | |||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix); | |||
// no op, this should be taken care of when checking valid references | |||
} | |||
} | |||
} | |||
} | |||
SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\n", pix_offset, restart); | |||
// next page range | |||
if (!restart) { | |||
pix_offset += pages_per_scan; | |||
} | |||
} // while page range not reached end | |||
return res; | |||
} | |||
// Checks consistency amongst all pages and fixes irregularities | |||
s32_t spiffs_page_consistency_check(spiffs *fs) { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); | |||
s32_t res = spiffs_page_consistency_check_i(fs); | |||
if (res != SPIFFS_OK) { | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); | |||
} | |||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); | |||
return res; | |||
} | |||
//--------------------------------------- | |||
// Object index consistency | |||
// searches for given object id in temporary object id index, | |||
// returns the index or -1 | |||
static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) { | |||
u32_t i; | |||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; | |||
obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; | |||
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) { | |||
if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, | |||
int cur_entry, const void *user_const_p, void *user_var_p) { | |||
(void)user_const_p; | |||
s32_t res_c = SPIFFS_VIS_COUNTINUE; | |||
s32_t res = SPIFFS_OK; | |||
u32_t *log_ix = (u32_t*)user_var_p; | |||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, | |||
(cur_block * 256)/fs->block_count, 0); | |||
if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { | |||
spiffs_page_header p_hdr; | |||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); | |||
// load header | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
if (p_hdr.span_ix == 0 && | |||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == | |||
(SPIFFS_PH_FLAG_DELET)) { | |||
SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n", | |||
cur_pix, obj_id, p_hdr.span_ix); | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
return res_c; | |||
} | |||
if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == | |||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { | |||
return res_c; | |||
} | |||
if (p_hdr.span_ix == 0) { | |||
// objix header page, register objid as reachable | |||
int r = spiffs_object_index_search(fs, obj_id); | |||
if (r == -1) { | |||
// not registered, do it | |||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; | |||
(*log_ix)++; | |||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { | |||
*log_ix = 0; | |||
} | |||
} | |||
} else { // span index | |||
// objix page, see if header can be found | |||
int r = spiffs_object_index_search(fs, obj_id); | |||
u8_t delete = 0; | |||
if (r == -1) { | |||
// not in temporary index, try finding it | |||
spiffs_page_ix objix_hdr_pix; | |||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix); | |||
res_c = SPIFFS_VIS_COUNTINUE_RELOAD; | |||
if (res == SPIFFS_OK) { | |||
// found, register as reachable | |||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; | |||
} else if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// not found, register as unreachable | |||
delete = 1; | |||
obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG; | |||
} else { | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
(*log_ix)++; | |||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { | |||
*log_ix = 0; | |||
} | |||
} else { | |||
// in temporary index, check reachable flag | |||
if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) { | |||
// registered as unreachable | |||
delete = 1; | |||
} | |||
} | |||
if (delete) { | |||
SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" is orphan index - deleting\n", | |||
cur_pix, obj_id, p_hdr.span_ix); | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
} // span index | |||
} // valid object index id | |||
return res_c; | |||
} | |||
// Removes orphaned and partially deleted index pages. | |||
// Scans for index pages. When an index page is found, corresponding index header is searched for. | |||
// If no such page exists, the index page cannot be reached as no index header exists and must be | |||
// deleted. | |||
s32_t spiffs_object_index_consistency_check(spiffs *fs) { | |||
s32_t res = SPIFFS_OK; | |||
// impl note: | |||
// fs->work is used for a temporary object index memory, listing found object ids and | |||
// indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. | |||
// In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate | |||
// a reachable/unreachable object id. | |||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); | |||
u32_t obj_id_log_ix = 0; | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); | |||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, | |||
0, 0); | |||
if (res == SPIFFS_VIS_END) { | |||
res = SPIFFS_OK; | |||
} | |||
if (res != SPIFFS_OK) { | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); | |||
} | |||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); | |||
return res; | |||
} | |||
#endif // !SPIFFS_READ_ONLY |
@@ -0,0 +1,367 @@ | |||
/* | |||
* spiffs_config.h | |||
* | |||
* Created on: Jul 3, 2013 | |||
* Author: petera | |||
*/ | |||
#ifndef SPIFFS_CONFIG_H_ | |||
#define SPIFFS_CONFIG_H_ | |||
// ----------- 8< ------------ | |||
// Following includes are for the linux test build of spiffs | |||
// These may/should/must be removed/altered/replaced in your target | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <stddef.h> | |||
#include <unistd.h> | |||
#include <stdint.h> | |||
#include <ctype.h> | |||
// ----------- >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_ */ |
@@ -0,0 +1,606 @@ | |||
#include "spiffs.h" | |||
#include "spiffs_nucleus.h" | |||
#if !SPIFFS_READ_ONLY | |||
// Erases a logical block and updates the erase counter. | |||
// If cache is enabled, all pages that might be cached in this block | |||
// is dropped. | |||
static s32_t spiffs_gc_erase_block( | |||
spiffs *fs, | |||
spiffs_block_ix bix) { | |||
s32_t res; | |||
SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix); | |||
res = spiffs_erase_block(fs, bix); | |||
SPIFFS_CHECK_RES(res); | |||
#if SPIFFS_CACHE | |||
{ | |||
u32_t i; | |||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { | |||
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); | |||
} | |||
} | |||
#endif | |||
return res; | |||
} | |||
// Searches for blocks where all entries are deleted - if one is found, | |||
// the block is erased. Compared to the non-quick gc, the quick one ensures | |||
// that no updates are needed on existing objects on pages that are erased. | |||
s32_t spiffs_gc_quick( | |||
spiffs *fs, u16_t max_free_pages) { | |||
s32_t res = SPIFFS_OK; | |||
u32_t blocks = fs->block_count; | |||
spiffs_block_ix cur_block = 0; | |||
u32_t cur_block_addr = 0; | |||
int cur_entry = 0; | |||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; | |||
SPIFFS_GC_DBG("gc_quick: running\n"); | |||
#if SPIFFS_GC_STATS | |||
fs->stats_gc_runs++; | |||
#endif | |||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); | |||
// find fully deleted blocks | |||
// check each block | |||
while (res == SPIFFS_OK && blocks--) { | |||
u16_t deleted_pages_in_block = 0; | |||
u16_t free_pages_in_block = 0; | |||
int obj_lookup_page = 0; | |||
// check each object lookup page | |||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { | |||
int entry_offset = obj_lookup_page * entries_per_page; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
// check each entry | |||
while (res == SPIFFS_OK && | |||
cur_entry - entry_offset < entries_per_page && | |||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { | |||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; | |||
if (obj_id == SPIFFS_OBJ_ID_DELETED) { | |||
deleted_pages_in_block++; | |||
} else if (obj_id == SPIFFS_OBJ_ID_FREE) { | |||
// kill scan, go for next block | |||
free_pages_in_block++; | |||
if (free_pages_in_block > max_free_pages) { | |||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); | |||
res = 1; // kill object lu loop | |||
break; | |||
} | |||
} else { | |||
// kill scan, go for next block | |||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); | |||
res = 1; // kill object lu loop | |||
break; | |||
} | |||
cur_entry++; | |||
} // per entry | |||
obj_lookup_page++; | |||
} // per object lookup page | |||
if (res == 1) res = SPIFFS_OK; | |||
if (res == SPIFFS_OK && | |||
deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) && | |||
free_pages_in_block <= max_free_pages) { | |||
// found a fully deleted block | |||
fs->stats_p_deleted -= deleted_pages_in_block; | |||
res = spiffs_gc_erase_block(fs, cur_block); | |||
return res; | |||
} | |||
cur_entry = 0; | |||
cur_block++; | |||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); | |||
} // per block | |||
if (res == SPIFFS_OK) { | |||
res = SPIFFS_ERR_NO_DELETED_BLOCKS; | |||
} | |||
return res; | |||
} | |||
// Checks if garbage collecting is necessary. If so a candidate block is found, | |||
// cleansed and erased | |||
s32_t spiffs_gc_check( | |||
spiffs *fs, | |||
u32_t len) { | |||
s32_t res; | |||
s32_t free_pages = | |||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2) | |||
- fs->stats_p_allocated - fs->stats_p_deleted; | |||
int tries = 0; | |||
if (fs->free_blocks > 3 && | |||
(s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { | |||
return SPIFFS_OK; | |||
} | |||
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); | |||
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { | |||
// SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); | |||
// return SPIFFS_ERR_FULL; | |||
// } | |||
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { | |||
SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); | |||
return SPIFFS_ERR_FULL; | |||
} | |||
do { | |||
SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n", | |||
tries, | |||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), | |||
len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); | |||
spiffs_block_ix *cands; | |||
int count; | |||
spiffs_block_ix cand; | |||
s32_t prev_free_pages = free_pages; | |||
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state | |||
res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); | |||
SPIFFS_CHECK_RES(res); | |||
if (count == 0) { | |||
SPIFFS_GC_DBG("gc_check: no candidates, return\n"); | |||
return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; | |||
} | |||
#if SPIFFS_GC_STATS | |||
fs->stats_gc_runs++; | |||
#endif | |||
cand = cands[0]; | |||
fs->cleaning = 1; | |||
//SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand); | |||
res = spiffs_gc_clean(fs, cand); | |||
fs->cleaning = 0; | |||
if (res < 0) { | |||
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); | |||
} else { | |||
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_gc_erase_page_stats(fs, cand); | |||
SPIFFS_CHECK_RES(res); | |||
res = spiffs_gc_erase_block(fs, cand); | |||
SPIFFS_CHECK_RES(res); | |||
free_pages = | |||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) | |||
- fs->stats_p_allocated - fs->stats_p_deleted; | |||
if (prev_free_pages <= 0 && prev_free_pages == free_pages) { | |||
// abort early to reduce wear, at least tried once | |||
SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n"); | |||
break; | |||
} | |||
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || | |||
(s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); | |||
free_pages = | |||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) | |||
- fs->stats_p_allocated - fs->stats_p_deleted; | |||
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { | |||
res = SPIFFS_ERR_FULL; | |||
} | |||
SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n", | |||
fs->stats_p_allocated + fs->stats_p_deleted, | |||
fs->free_blocks, free_pages, tries, res); | |||
return res; | |||
} | |||
// Updates page statistics for a block that is about to be erased | |||
s32_t spiffs_gc_erase_page_stats( | |||
spiffs *fs, | |||
spiffs_block_ix bix) { | |||
s32_t res = SPIFFS_OK; | |||
int obj_lookup_page = 0; | |||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); | |||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; | |||
int cur_entry = 0; | |||
u32_t dele = 0; | |||
u32_t allo = 0; | |||
// check each object lookup page | |||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { | |||
int entry_offset = obj_lookup_page * entries_per_page; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
// check each entry | |||
while (res == SPIFFS_OK && | |||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { | |||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; | |||
if (obj_id == SPIFFS_OBJ_ID_FREE) { | |||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) { | |||
dele++; | |||
} else { | |||
allo++; | |||
} | |||
cur_entry++; | |||
} // per entry | |||
obj_lookup_page++; | |||
} // per object lookup page | |||
SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele); | |||
fs->stats_p_allocated -= allo; | |||
fs->stats_p_deleted -= dele; | |||
return res; | |||
} | |||
// Finds block candidates to erase | |||
s32_t spiffs_gc_find_candidate( | |||
spiffs *fs, | |||
spiffs_block_ix **block_candidates, | |||
int *candidate_count, | |||
char fs_crammed) { | |||
s32_t res = SPIFFS_OK; | |||
u32_t blocks = fs->block_count; | |||
spiffs_block_ix cur_block = 0; | |||
u32_t cur_block_addr = 0; | |||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; | |||
int cur_entry = 0; | |||
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score | |||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t))); | |||
*candidate_count = 0; | |||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); | |||
// divide up work area into block indices and scores | |||
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; | |||
s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); | |||
// align cand_scores on s32_t boundary | |||
cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); | |||
*block_candidates = cand_blocks; | |||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); | |||
// check each block | |||
while (res == SPIFFS_OK && blocks--) { | |||
u16_t deleted_pages_in_block = 0; | |||
u16_t used_pages_in_block = 0; | |||
int obj_lookup_page = 0; | |||
// check each object lookup page | |||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { | |||
int entry_offset = obj_lookup_page * entries_per_page; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
// check each entry | |||
while (res == SPIFFS_OK && | |||
cur_entry - entry_offset < entries_per_page && | |||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { | |||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; | |||
if (obj_id == SPIFFS_OBJ_ID_FREE) { | |||
// when a free entry is encountered, scan logic ensures that all following entries are free also | |||
res = 1; // kill object lu loop | |||
break; | |||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) { | |||
deleted_pages_in_block++; | |||
} else { | |||
used_pages_in_block++; | |||
} | |||
cur_entry++; | |||
} // per entry | |||
obj_lookup_page++; | |||
} // per object lookup page | |||
if (res == 1) res = SPIFFS_OK; | |||
// calculate score and insert into candidate table | |||
// stoneage sort, but probably not so many blocks | |||
if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) { | |||
// read erase count | |||
spiffs_obj_id erase_count; | |||
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, | |||
SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), | |||
sizeof(spiffs_obj_id), (u8_t *)&erase_count); | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_obj_id erase_age; | |||
if (fs->max_erase_count > erase_count) { | |||
erase_age = fs->max_erase_count - erase_count; | |||
} else { | |||
erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); | |||
} | |||
s32_t score = | |||
deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + | |||
used_pages_in_block * SPIFFS_GC_HEUR_W_USED + | |||
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); | |||
int cand_ix = 0; | |||
SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); | |||
while (cand_ix < max_candidates) { | |||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { | |||
cand_blocks[cand_ix] = cur_block; | |||
cand_scores[cand_ix] = score; | |||
break; | |||
} else if (cand_scores[cand_ix] < score) { | |||
int reorder_cand_ix = max_candidates - 2; | |||
while (reorder_cand_ix >= cand_ix) { | |||
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; | |||
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; | |||
reorder_cand_ix--; | |||
} | |||
cand_blocks[cand_ix] = cur_block; | |||
cand_scores[cand_ix] = score; | |||
break; | |||
} | |||
cand_ix++; | |||
} | |||
(*candidate_count)++; | |||
} | |||
cur_entry = 0; | |||
cur_block++; | |||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); | |||
} // per block | |||
return res; | |||
} | |||
typedef enum { | |||
FIND_OBJ_DATA, | |||
MOVE_OBJ_DATA, | |||
MOVE_OBJ_IX, | |||
FINISHED | |||
} spiffs_gc_clean_state; | |||
typedef struct { | |||
spiffs_gc_clean_state state; | |||
spiffs_obj_id cur_obj_id; | |||
spiffs_span_ix cur_objix_spix; | |||
spiffs_page_ix cur_objix_pix; | |||
spiffs_page_ix cur_data_pix; | |||
int stored_scan_entry_index; | |||
u8_t obj_id_found; | |||
} spiffs_gc; | |||
// Empties given block by moving all data into free pages of another block | |||
// Strategy: | |||
// loop: | |||
// scan object lookup for object data pages | |||
// for first found id, check spix and load corresponding object index page to memory | |||
// push object scan lookup entry index | |||
// rescan object lookup, find data pages with same id and referenced by same object index | |||
// move data page, update object index in memory | |||
// when reached end of lookup, store updated object index | |||
// pop object scan lookup entry index | |||
// repeat loop until end of object lookup | |||
// scan object lookup again for remaining object index pages, move to new page in other block | |||
// | |||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { | |||
s32_t res = SPIFFS_OK; | |||
const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); | |||
// this is the global localizer being pushed and popped | |||
int cur_entry = 0; | |||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; | |||
spiffs_gc gc; // our stack frame/state | |||
spiffs_page_ix cur_pix = 0; | |||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; | |||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; | |||
SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix); | |||
memset(&gc, 0, sizeof(spiffs_gc)); | |||
gc.state = FIND_OBJ_DATA; | |||
if (fs->free_cursor_block_ix == bix) { | |||
// move free cursor to next block, cannot use free pages from the block we want to clean | |||
fs->free_cursor_block_ix = (bix+1)%fs->block_count; | |||
fs->free_cursor_obj_lu_entry = 0; | |||
SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix); | |||
} | |||
while (res == SPIFFS_OK && gc.state != FINISHED) { | |||
SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry); | |||
gc.obj_id_found = 0; // reset (to no found data page) | |||
// scan through lookup pages | |||
int obj_lookup_page = cur_entry / entries_per_page; | |||
u8_t scan = 1; | |||
// check each object lookup page | |||
while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { | |||
int entry_offset = obj_lookup_page * entries_per_page; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
// check each object lookup entry | |||
while (scan && res == SPIFFS_OK && | |||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { | |||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; | |||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); | |||
// act upon object id depending on gc state | |||
switch (gc.state) { | |||
case FIND_OBJ_DATA: | |||
// find a data page | |||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && | |||
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { | |||
// found a data page, stop scanning and handle in switch case below | |||
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id); | |||
gc.obj_id_found = 1; | |||
gc.cur_obj_id = obj_id; | |||
gc.cur_data_pix = cur_pix; | |||
scan = 0; | |||
} | |||
break; | |||
case MOVE_OBJ_DATA: | |||
// evacuate found data pages for corresponding object index we have in memory, | |||
// update memory representation | |||
if (obj_id == gc.cur_obj_id) { | |||
spiffs_page_header p_hdr; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); | |||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); | |||
} else { | |||
spiffs_page_ix new_data_pix; | |||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { | |||
// move page | |||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); | |||
SPIFFS_CHECK_RES(res); | |||
// move wipes obj_lu, reload it | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
} else { | |||
// page is deleted but not deleted in lookup, scrap it - | |||
// might seem unnecessary as we will erase this block, but | |||
// we might get aborted | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
SPIFFS_CHECK_RES(res); | |||
new_data_pix = SPIFFS_OBJ_ID_FREE; | |||
} | |||
// update memory representation of object index page with new data page | |||
if (gc.cur_objix_spix == 0) { | |||
// update object index header page | |||
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); | |||
} else { | |||
// update object index page | |||
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); | |||
} | |||
} | |||
} | |||
break; | |||
case MOVE_OBJ_IX: | |||
// find and evacuate object index pages | |||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && | |||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { | |||
// found an index object id | |||
spiffs_page_header p_hdr; | |||
spiffs_page_ix new_pix; | |||
// load header | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { | |||
// move page | |||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, | |||
SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); | |||
// move wipes obj_lu, reload it | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, | |||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), | |||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); | |||
SPIFFS_CHECK_RES(res); | |||
} else { | |||
// page is deleted but not deleted in lookup, scrap it - | |||
// might seem unnecessary as we will erase this block, but | |||
// we might get aborted | |||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); | |||
res = spiffs_page_delete(fs, cur_pix); | |||
if (res == SPIFFS_OK) { | |||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, | |||
SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); | |||
} | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
} | |||
break; | |||
default: | |||
scan = 0; | |||
break; | |||
} // switch gc state | |||
cur_entry++; | |||
} // per entry | |||
obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop | |||
} // per object lookup page | |||
if (res != SPIFFS_OK) break; | |||
// state finalization and switch | |||
switch (gc.state) { | |||
case FIND_OBJ_DATA: | |||
if (gc.obj_id_found) { | |||
// handle found data page - | |||
// find out corresponding obj ix page and load it to memory | |||
spiffs_page_header p_hdr; | |||
spiffs_page_ix objix_pix; | |||
gc.stored_scan_entry_index = cur_entry; // push cursor | |||
cur_entry = 0; // restart scan from start | |||
gc.state = MOVE_OBJ_DATA; | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); | |||
SPIFFS_CHECK_RES(res); | |||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); | |||
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix); | |||
res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); | |||
if (res == SPIFFS_ERR_NOT_FOUND) { | |||
// on borked systems we might get an ERR_NOT_FOUND here - | |||
// this is handled by simply deleting the page as it is not referenced | |||
// from anywhere | |||
SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix); | |||
res = spiffs_page_delete(fs, gc.cur_data_pix); | |||
SPIFFS_CHECK_RES(res); | |||
// then we restore states and continue scanning for data pages | |||
cur_entry = gc.stored_scan_entry_index; // pop cursor | |||
gc.state = FIND_OBJ_DATA; | |||
break; // done | |||
} | |||
SPIFFS_CHECK_RES(res); | |||
SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix); | |||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, | |||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); | |||
SPIFFS_CHECK_RES(res); | |||
// cannot allow a gc if the presumed index in fact is no index, a | |||
// check must run or lot of data may be lost | |||
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); | |||
gc.cur_objix_pix = objix_pix; | |||
} else { | |||
// no more data pages found, passed thru all block, start evacuating object indices | |||
gc.state = MOVE_OBJ_IX; | |||
cur_entry = 0; // restart entry scan index | |||
} | |||
break; | |||
case MOVE_OBJ_DATA: { | |||
// store modified objix (hdr) page residing in memory now that all | |||
// data pages belonging to this object index and residing in the block | |||
// we want to evacuate | |||
spiffs_page_ix new_objix_pix; | |||
gc.state = FIND_OBJ_DATA; | |||
cur_entry = gc.stored_scan_entry_index; // pop cursor | |||
if (gc.cur_objix_spix == 0) { | |||
// store object index header page | |||
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0); | |||
SPIFFS_CHECK_RES(res); | |||
} else { | |||
// store object index page | |||
res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); | |||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); | |||
SPIFFS_CHECK_RES(res); | |||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, | |||
SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); | |||
} | |||
} | |||
break; | |||
case MOVE_OBJ_IX: | |||
// scanned thru all block, no more object indices found - our work here is done | |||
gc.state = FINISHED; | |||
break; | |||
default: | |||
cur_entry = 0; | |||
break; | |||
} // switch gc.state | |||
SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state); | |||
} // while state != FINISHED | |||
return res; | |||
} | |||
#endif // !SPIFFS_READ_ONLY |
@@ -0,0 +1,797 @@ | |||
/* | |||
* spiffs_nucleus.h | |||
* | |||
* Created on: Jun 15, 2013 | |||
* Author: petera | |||
*/ | |||
/* SPIFFS layout | |||
* | |||
* spiffs is designed for following spi flash characteristics: | |||
* - only big areas of data (blocks) can be erased | |||
* - erasing resets all bits in a block to ones | |||
* - writing pulls ones to zeroes | |||
* - zeroes cannot be pulled to ones, without erase | |||
* - wear leveling | |||
* | |||
* spiffs is also meant to be run on embedded, memory constraint devices. | |||
* | |||
* Entire area is divided in blocks. Entire area is also divided in pages. | |||
* Each block contains same number of pages. A page cannot be erased, but a | |||
* block can be erased. | |||
* | |||
* Entire area must be block_size * x | |||
* page_size must be block_size / (2^y) where y > 2 | |||
* | |||
* ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes | |||
* | |||
* BLOCK 0 PAGE 0 object lookup 1 | |||
* PAGE 1 object lookup 2 | |||
* ... | |||
* PAGE n-1 object lookup n | |||
* PAGE n object data 1 | |||
* PAGE n+1 object data 2 | |||
* ... | |||
* PAGE n+m-1 object data m | |||
* | |||
* BLOCK 1 PAGE n+m object lookup 1 | |||
* PAGE n+m+1 object lookup 2 | |||
* ... | |||
* PAGE 2n+m-1 object lookup n | |||
* PAGE 2n+m object data 1 | |||
* PAGE 2n+m object data 2 | |||
* ... | |||
* PAGE 2n+2m-1 object data m | |||
* ... | |||
* | |||
* n is number of object lookup pages, which is number of pages needed to index all pages | |||
* in a block by object id | |||
* : block_size / page_size * sizeof(obj_id) / page_size | |||
* m is number data pages, which is number of pages in block minus number of lookup pages | |||
* : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size | |||
* thus, n+m is total number of pages in a block | |||
* : block_size / page_size | |||
* | |||
* ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 | |||
* | |||
* Object lookup pages contain object id entries. Each entry represent the corresponding | |||
* data page. | |||
* Assuming a 16 bit object id, an object id being 0xffff represents a free page. | |||
* An object id being 0x0000 represents a deleted page. | |||
* | |||
* ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. | |||
* page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. | |||
* page 2 : data : data for object id 0008 | |||
* page 3 : data : data for object id 0001 | |||
* page 4 : data : data for object id 0aaa | |||
* ... | |||
* | |||
* | |||
* Object data pages can be either object index pages or object content. | |||
* All object data pages contains a data page header, containing object id and span index. | |||
* The span index denotes the object page ordering amongst data pages with same object id. | |||
* This applies to both object index pages (when index spans more than one page of entries), | |||
* and object data pages. | |||
* An object index page contains page entries pointing to object content page. The entry index | |||
* in a object index page correlates to the span index in the actual object data page. | |||
* The first object index page (span index 0) is called object index header page, and also | |||
* contains object flags (directory/file), size, object name etc. | |||
* | |||
* ex: | |||
* BLOCK 1 | |||
* PAGE 256: objectl lookup page 1 | |||
* [*123] [ 123] [ 123] [ 123] | |||
* [ 123] [*123] [ 123] [ 123] | |||
* [free] [free] [free] [free] ... | |||
* PAGE 257: objectl lookup page 2 | |||
* [free] [free] [free] [free] ... | |||
* PAGE 258: object index page (header) | |||
* obj.id:0123 span.ix:0000 flags:INDEX | |||
* size:1600 name:ex.txt type:file | |||
* [259] [260] [261] [262] | |||
* PAGE 259: object data page | |||
* obj.id:0123 span.ix:0000 flags:DATA | |||
* PAGE 260: object data page | |||
* obj.id:0123 span.ix:0001 flags:DATA | |||
* PAGE 261: object data page | |||
* obj.id:0123 span.ix:0002 flags:DATA | |||
* PAGE 262: object data page | |||
* obj.id:0123 span.ix:0003 flags:DATA | |||
* PAGE 263: object index page | |||
* obj.id:0123 span.ix:0001 flags:INDEX | |||
* [264] [265] [fre] [fre] | |||
* [fre] [fre] [fre] [fre] | |||
* PAGE 264: object data page | |||
* obj.id:0123 span.ix:0004 flags:DATA | |||
* PAGE 265: object data page | |||
* obj.id:0123 span.ix:0005 flags:DATA | |||
* | |||
*/ | |||
#ifndef SPIFFS_NUCLEUS_H_ | |||
#define SPIFFS_NUCLEUS_H_ | |||
#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1) | |||
#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1) | |||
#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2) | |||
#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3) | |||
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4) | |||
// visitor result, continue searching | |||
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20) | |||
// visitor result, continue searching after reloading lu buffer | |||
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21) | |||
// visitor result, stop searching | |||
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22) | |||
// updating an object index contents | |||
#define SPIFFS_EV_IX_UPD (0) | |||
// creating a new object index | |||
#define SPIFFS_EV_IX_NEW (1) | |||
// deleting an object index | |||
#define SPIFFS_EV_IX_DEL (2) | |||
// moving an object index without updating contents | |||
#define SPIFFS_EV_IX_MOV (3) | |||
// updating an object index header data only, not the table itself | |||
#define SPIFFS_EV_IX_UPD_HDR (4) | |||
#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) | |||
#define SPIFFS_UNDEFINED_LEN (u32_t)(-1) | |||
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) | |||
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) | |||
#if SPIFFS_USE_MAGIC | |||
#if !SPIFFS_USE_MAGIC_LENGTH | |||
#define SPIFFS_MAGIC(fs, bix) \ | |||
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) | |||
#else // SPIFFS_USE_MAGIC_LENGTH | |||
#define SPIFFS_MAGIC(fs, bix) \ | |||
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) | |||
#endif // SPIFFS_USE_MAGIC_LENGTH | |||
#endif // SPIFFS_USE_MAGIC | |||
#define SPIFFS_CONFIG_MAGIC (0x20090315) | |||
#if SPIFFS_SINGLETON == 0 | |||
#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ | |||
((fs)->cfg.log_page_size) | |||
#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ | |||
((fs)->cfg.log_block_size) | |||
#define SPIFFS_CFG_PHYS_SZ(fs) \ | |||
((fs)->cfg.phys_size) | |||
#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ | |||
((fs)->cfg.phys_erase_block) | |||
#define SPIFFS_CFG_PHYS_ADDR(fs) \ | |||
((fs)->cfg.phys_addr) | |||
#endif | |||
// total number of pages | |||
#define SPIFFS_MAX_PAGES(fs) \ | |||
( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// total number of pages per block, including object lookup pages | |||
#define SPIFFS_PAGES_PER_BLOCK(fs) \ | |||
( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// number of object lookup pages per block | |||
#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ | |||
(MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) | |||
// checks if page index belongs to object lookup | |||
#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ | |||
(((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) | |||
// number of object lookup entries in all object lookup pages | |||
#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ | |||
(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) | |||
// converts a block to physical address | |||
#define SPIFFS_BLOCK_TO_PADDR(fs, block) \ | |||
( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) | |||
// converts a object lookup entry to page index | |||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ | |||
((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) | |||
// converts a object lookup entry to physical address of corresponding page | |||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ | |||
(SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// converts a page to physical address | |||
#define SPIFFS_PAGE_TO_PADDR(fs, page) \ | |||
( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// converts a physical address to page | |||
#define SPIFFS_PADDR_TO_PAGE(fs, addr) \ | |||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// gives index in page for a physical address | |||
#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ | |||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) | |||
// returns containing block for given page | |||
#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ | |||
( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) | |||
// returns starting page for block | |||
#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ | |||
( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) | |||
// converts page to entry in object lookup page | |||
#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ | |||
( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) | |||
// returns data size in a data page | |||
#define SPIFFS_DATA_PAGE_SIZE(fs) \ | |||
( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) | |||
// returns physical address for block's erase count, | |||
// always in the physical last entry of the last object lookup page | |||
#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ | |||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) | |||
// returns physical address for block's magic, | |||
// always in the physical second last entry of the last object lookup page | |||
#define SPIFFS_MAGIC_PADDR(fs, bix) \ | |||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) | |||
// checks if there is any room for magic in the object luts | |||
#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \ | |||
( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ | |||
<= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) | |||
// define helpers object | |||
// entries in an object header page index | |||
#define SPIFFS_OBJ_HDR_IX_LEN(fs) \ | |||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) | |||
// entries in an object page index | |||
#define SPIFFS_OBJ_IX_LEN(fs) \ | |||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) | |||
// object index entry for given data span index | |||
#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ | |||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) | |||
// object index span index number for given data span index or entry | |||
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ | |||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) | |||
// get data span index for object index span index | |||
#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ | |||
( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) | |||
#define SPIFFS_OP_T_OBJ_LU (0<<0) | |||
#define SPIFFS_OP_T_OBJ_LU2 (1<<0) | |||
#define SPIFFS_OP_T_OBJ_IX (2<<0) | |||
#define SPIFFS_OP_T_OBJ_DA (3<<0) | |||
#define SPIFFS_OP_C_DELE (0<<2) | |||
#define SPIFFS_OP_C_UPDT (1<<2) | |||
#define SPIFFS_OP_C_MOVS (2<<2) | |||
#define SPIFFS_OP_C_MOVD (3<<2) | |||
#define SPIFFS_OP_C_FLSH (4<<2) | |||
#define SPIFFS_OP_C_READ (5<<2) | |||
#define SPIFFS_OP_C_WRTHRU (6<<2) | |||
#define SPIFFS_OP_TYPE_MASK (3<<0) | |||
#define SPIFFS_OP_COM_MASK (7<<2) | |||
// if 0, this page is written to, else clean | |||
#define SPIFFS_PH_FLAG_USED (1<<0) | |||
// if 0, writing is finalized, else under modification | |||
#define SPIFFS_PH_FLAG_FINAL (1<<1) | |||
// if 0, this is an index page, else a data page | |||
#define SPIFFS_PH_FLAG_INDEX (1<<2) | |||
// if 0, page is deleted, else valid | |||
#define SPIFFS_PH_FLAG_DELET (1<<7) | |||
// if 0, this index header is being deleted | |||
#define SPIFFS_PH_FLAG_IXDELE (1<<6) | |||
#define SPIFFS_CHECK_MOUNT(fs) \ | |||
((fs)->mounted != 0) | |||
#define SPIFFS_CHECK_CFG(fs) \ | |||
((fs)->config_magic == SPIFFS_CONFIG_MAGIC) | |||
#define SPIFFS_CHECK_RES(res) \ | |||
do { \ | |||
if ((res) < SPIFFS_OK) return (res); \ | |||
} while (0); | |||
#define SPIFFS_API_CHECK_MOUNT(fs) \ | |||
if (!SPIFFS_CHECK_MOUNT((fs))) { \ | |||
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ | |||
return SPIFFS_ERR_NOT_MOUNTED; \ | |||
} | |||
#define SPIFFS_API_CHECK_CFG(fs) \ | |||
if (!SPIFFS_CHECK_CFG((fs))) { \ | |||
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ | |||
return SPIFFS_ERR_NOT_CONFIGURED; \ | |||
} | |||
#define SPIFFS_API_CHECK_RES(fs, res) \ | |||
if ((res) < SPIFFS_OK) { \ | |||
(fs)->err_code = (res); \ | |||
return (res); \ | |||
} | |||
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ | |||
if ((res) < SPIFFS_OK) { \ | |||
(fs)->err_code = (res); \ | |||
SPIFFS_UNLOCK(fs); \ | |||
return (res); \ | |||
} | |||
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ | |||
if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \ | |||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH; | |||
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; | |||
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ | |||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \ | |||
if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \ | |||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; | |||
// check id, only visit matching objec ids | |||
#define SPIFFS_VIS_CHECK_ID (1<<0) | |||
// report argument object id to visitor - else object lookup id is reported | |||
#define SPIFFS_VIS_CHECK_PH (1<<1) | |||
// stop searching at end of all look up pages | |||
#define SPIFFS_VIS_NO_WRAP (1<<2) | |||
#if SPIFFS_HAL_CALLBACK_EXTRA | |||
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ | |||
(_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) | |||
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ | |||
(_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) | |||
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ | |||
(_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) | |||
#else // SPIFFS_HAL_CALLBACK_EXTRA | |||
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ | |||
(_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) | |||
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ | |||
(_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) | |||
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ | |||
(_fs)->cfg.hal_erase_f((_paddr), (_len)) | |||
#endif // SPIFFS_HAL_CALLBACK_EXTRA | |||
#if SPIFFS_CACHE | |||
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0) | |||
#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1) | |||
#define SPIFFS_CACHE_FLAG_OBJLU (1<<2) | |||
#define SPIFFS_CACHE_FLAG_OBJIX (1<<3) | |||
#define SPIFFS_CACHE_FLAG_DATA (1<<4) | |||
#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) | |||
#define SPIFFS_CACHE_PAGE_SIZE(fs) \ | |||
(sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) | |||
#define spiffs_get_cache(fs) \ | |||
((spiffs_cache *)((fs)->cache)) | |||
#define spiffs_get_cache_page_hdr(fs, c, ix) \ | |||
((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) | |||
#define spiffs_get_cache_page(fs, c, ix) \ | |||
((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) | |||
// cache page struct | |||
typedef struct { | |||
// cache flags | |||
u8_t flags; | |||
// cache page index | |||
u8_t ix; | |||
// last access of this cache page | |||
u32_t last_access; | |||
union { | |||
// type read cache | |||
struct { | |||
// read cache page index | |||
spiffs_page_ix pix; | |||
}; | |||
#if SPIFFS_CACHE_WR | |||
// type write cache | |||
struct { | |||
// write cache | |||
spiffs_obj_id obj_id; | |||
// offset in cache page | |||
u32_t offset; | |||
// size of cache page | |||
u16_t size; | |||
}; | |||
#endif | |||
}; | |||
} spiffs_cache_page; | |||
// cache struct | |||
typedef struct { | |||
u8_t cpage_count; | |||
u32_t last_access; | |||
u32_t cpage_use_map; | |||
u32_t cpage_use_mask; | |||
u8_t *cpages; | |||
} spiffs_cache; | |||
#endif | |||
// spiffs nucleus file descriptor | |||
typedef struct { | |||
// the filesystem of this descriptor | |||
spiffs *fs; | |||
// number of file descriptor - if 0, the file descriptor is closed | |||
spiffs_file file_nbr; | |||
// object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted | |||
spiffs_obj_id obj_id; | |||
// size of the file | |||
u32_t size; | |||
// cached object index header page index | |||
spiffs_page_ix objix_hdr_pix; | |||
// cached offset object index page index | |||
spiffs_page_ix cursor_objix_pix; | |||
// cached offset object index span index | |||
spiffs_span_ix cursor_objix_spix; | |||
// current absolute offset | |||
u32_t offset; | |||
// current file descriptor offset | |||
u32_t fdoffset; | |||
// fd flags | |||
spiffs_flags flags; | |||
#if SPIFFS_CACHE_WR | |||
spiffs_cache_page *cache_page; | |||
#endif | |||
#if SPIFFS_TEMPORAL_FD_CACHE | |||
// djb2 hash of filename | |||
u32_t name_hash; | |||
// hit score (score == 0 indicates never used fd) | |||
u16_t score; | |||
#endif | |||
#if SPIFFS_IX_MAP | |||
// spiffs index map, if 0 it means unmapped | |||
spiffs_ix_map *ix_map; | |||
#endif | |||
} spiffs_fd; | |||
// object structs | |||
// page header, part of each page except object lookup pages | |||
// NB: this is always aligned when the data page is an object index, | |||
// as in this case struct spiffs_page_object_ix is used | |||
typedef struct __attribute(( packed )) { | |||
// object id | |||
spiffs_obj_id obj_id; | |||
// object span index | |||
spiffs_span_ix span_ix; | |||
// flags | |||
u8_t flags; | |||
} spiffs_page_header; | |||
// object index header page header | |||
typedef struct __attribute(( packed )) | |||
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES | |||
__attribute(( aligned(sizeof(spiffs_page_ix)) )) | |||
#endif | |||
{ | |||
// common page header | |||
spiffs_page_header p_hdr; | |||
// alignment | |||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; | |||
// size of object | |||
u32_t size; | |||
// type of object | |||
spiffs_obj_type type; | |||
// name of object | |||
u8_t name[SPIFFS_OBJ_NAME_LEN]; | |||
#if SPIFFS_OBJ_META_LEN | |||
// metadata. not interpreted by SPIFFS in any way. | |||
u8_t meta[SPIFFS_OBJ_META_LEN]; | |||
#endif | |||
} spiffs_page_object_ix_header; | |||
// object index page header | |||
typedef struct __attribute(( packed )) { | |||
spiffs_page_header p_hdr; | |||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; | |||
} spiffs_page_object_ix; | |||
// callback func for object lookup visitor | |||
typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, | |||
const void *user_const_p, void *user_var_p); | |||
#if SPIFFS_CACHE | |||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \ | |||
spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst)) | |||
#define _spiffs_wr(fs, op, fh, addr, len, src) \ | |||
spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src)) | |||
#else | |||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \ | |||
spiffs_phys_rd((fs), (addr), (len), (dst)) | |||
#define _spiffs_wr(fs, op, fh, addr, len, src) \ | |||
spiffs_phys_wr((fs), (addr), (len), (src)) | |||
#endif | |||
#ifndef MIN | |||
#define MIN(a,b) ((a) < (b) ? (a) : (b)) | |||
#endif | |||
#ifndef MAX | |||
#define MAX(a,b) ((a) > (b) ? (a) : (b)) | |||
#endif | |||
// --------------- | |||
s32_t spiffs_phys_rd( | |||
spiffs *fs, | |||
#if SPIFFS_CACHE | |||
u8_t op, | |||
spiffs_file fh, | |||
#endif | |||
u32_t addr, | |||
u32_t len, | |||
u8_t *dst); | |||
s32_t spiffs_phys_wr( | |||
spiffs *fs, | |||
#if SPIFFS_CACHE | |||
u8_t op, | |||
spiffs_file fh, | |||
#endif | |||
u32_t addr, | |||
u32_t len, | |||
u8_t *src); | |||
s32_t spiffs_phys_cpy( | |||
spiffs *fs, | |||
spiffs_file fh, | |||
u32_t dst, | |||
u32_t src, | |||
u32_t len); | |||
s32_t spiffs_phys_count_free_blocks( | |||
spiffs *fs); | |||
s32_t spiffs_obj_lu_find_entry_visitor( | |||
spiffs *fs, | |||
spiffs_block_ix starting_block, | |||
int starting_lu_entry, | |||
u8_t flags, | |||
spiffs_obj_id obj_id, | |||
spiffs_visitor_f v, | |||
const void *user_const_p, | |||
void *user_var_p, | |||
spiffs_block_ix *block_ix, | |||
int *lu_entry); | |||
s32_t spiffs_erase_block( | |||
spiffs *fs, | |||
spiffs_block_ix bix); | |||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH | |||
s32_t spiffs_probe( | |||
spiffs_config *cfg); | |||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH | |||
// --------------- | |||
s32_t spiffs_obj_lu_scan( | |||
spiffs *fs); | |||
s32_t spiffs_obj_lu_find_free_obj_id( | |||
spiffs *fs, | |||
spiffs_obj_id *obj_id, | |||
const u8_t *conflicting_name); | |||
s32_t spiffs_obj_lu_find_free( | |||
spiffs *fs, | |||
spiffs_block_ix starting_block, | |||
int starting_lu_entry, | |||
spiffs_block_ix *block_ix, | |||
int *lu_entry); | |||
s32_t spiffs_obj_lu_find_id( | |||
spiffs *fs, | |||
spiffs_block_ix starting_block, | |||
int starting_lu_entry, | |||
spiffs_obj_id obj_id, | |||
spiffs_block_ix *block_ix, | |||
int *lu_entry); | |||
s32_t spiffs_obj_lu_find_id_and_span( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_span_ix spix, | |||
spiffs_page_ix exclusion_pix, | |||
spiffs_page_ix *pix); | |||
s32_t spiffs_obj_lu_find_id_and_span_by_phdr( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_span_ix spix, | |||
spiffs_page_ix exclusion_pix, | |||
spiffs_page_ix *pix); | |||
// --------------- | |||
s32_t spiffs_page_allocate_data( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_page_header *ph, | |||
u8_t *data, | |||
u32_t len, | |||
u32_t page_offs, | |||
u8_t finalize, | |||
spiffs_page_ix *pix); | |||
s32_t spiffs_page_move( | |||
spiffs *fs, | |||
spiffs_file fh, | |||
u8_t *page_data, | |||
spiffs_obj_id obj_id, | |||
spiffs_page_header *page_hdr, | |||
spiffs_page_ix src_pix, | |||
spiffs_page_ix *dst_pix); | |||
s32_t spiffs_page_delete( | |||
spiffs *fs, | |||
spiffs_page_ix pix); | |||
// --------------- | |||
s32_t spiffs_object_create( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
const u8_t name[], | |||
const u8_t meta[], | |||
spiffs_obj_type type, | |||
spiffs_page_ix *objix_hdr_pix); | |||
s32_t spiffs_object_update_index_hdr( | |||
spiffs *fs, | |||
spiffs_fd *fd, | |||
spiffs_obj_id obj_id, | |||
spiffs_page_ix objix_hdr_pix, | |||
u8_t *new_objix_hdr_data, | |||
const u8_t name[], | |||
const u8_t meta[], | |||
u32_t size, | |||
spiffs_page_ix *new_pix); | |||
#if SPIFFS_IX_MAP | |||
s32_t spiffs_populate_ix_map( | |||
spiffs *fs, | |||
spiffs_fd *fd, | |||
u32_t vec_entry_start, | |||
u32_t vec_entry_end); | |||
#endif | |||
void spiffs_cb_object_event( | |||
spiffs *fs, | |||
spiffs_page_object_ix *objix, | |||
int ev, | |||
spiffs_obj_id obj_id, | |||
spiffs_span_ix spix, | |||
spiffs_page_ix new_pix, | |||
u32_t new_size); | |||
s32_t spiffs_object_open_by_id( | |||
spiffs *fs, | |||
spiffs_obj_id obj_id, | |||
spiffs_fd *f, | |||
spiffs_flags flags, | |||
spiffs_mode mode); | |||
s32_t spiffs_object_open_by_page( | |||
spiffs *fs, | |||
spiffs_page_ix pix, | |||
spiffs_fd *f, | |||
spiffs_flags flags, | |||
spiffs_mode mode); | |||
s32_t spiffs_object_append( | |||
spiffs_fd *fd, | |||
u32_t offset, | |||
u8_t *data, | |||
u32_t len); | |||
s32_t spiffs_object_modify( | |||
spiffs_fd *fd, | |||
u32_t offset, | |||
u8_t *data, | |||
u32_t len); | |||
s32_t spiffs_object_read( | |||
spiffs_fd *fd, | |||
u32_t offset, | |||
u32_t len, | |||
u8_t *dst); | |||
s32_t spiffs_object_truncate( | |||
spiffs_fd *fd, | |||
u32_t new_len, | |||
u8_t remove_object); | |||
s32_t spiffs_object_find_object_index_header_by_name( | |||
spiffs *fs, | |||
const u8_t name[SPIFFS_OBJ_NAME_LEN], | |||
spiffs_page_ix *pix); | |||
// --------------- | |||
s32_t spiffs_gc_check( | |||
spiffs *fs, | |||
u32_t len); | |||
s32_t spiffs_gc_erase_page_stats( | |||
spiffs *fs, | |||
spiffs_block_ix bix); | |||
s32_t spiffs_gc_find_candidate( | |||
spiffs *fs, | |||
spiffs_block_ix **block_candidate, | |||
int *candidate_count, | |||
char fs_crammed); | |||
s32_t spiffs_gc_clean( | |||
spiffs *fs, | |||
spiffs_block_ix bix); | |||
s32_t spiffs_gc_quick( | |||
spiffs *fs, u16_t max_free_pages); | |||
// --------------- | |||
s32_t spiffs_fd_find_new( | |||
spiffs *fs, | |||
spiffs_fd **fd, | |||
const char *name); | |||
s32_t spiffs_fd_return( | |||
spiffs *fs, | |||
spiffs_file f); | |||
s32_t spiffs_fd_get( | |||
spiffs *fs, | |||
spiffs_file f, | |||
spiffs_fd **fd); | |||
#if SPIFFS_TEMPORAL_FD_CACHE | |||
void spiffs_fd_temporal_cache_rehash( | |||
spiffs *fs, | |||
const char *old_path, | |||
const char *new_path); | |||
#endif | |||
#if SPIFFS_CACHE | |||
void spiffs_cache_init( | |||
spiffs *fs); | |||
void spiffs_cache_drop_page( | |||
spiffs *fs, | |||
spiffs_page_ix pix); | |||
#if SPIFFS_CACHE_WR | |||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd( | |||
spiffs *fs, | |||
spiffs_fd *fd); | |||
void spiffs_cache_fd_release( | |||
spiffs *fs, | |||
spiffs_cache_page *cp); | |||
spiffs_cache_page *spiffs_cache_page_get_by_fd( | |||
spiffs *fs, | |||
spiffs_fd *fd); | |||
#endif | |||
#endif | |||
s32_t spiffs_lookup_consistency_check( | |||
spiffs *fs, | |||
u8_t check_all_objects); | |||
s32_t spiffs_page_consistency_check( | |||
spiffs *fs); | |||
s32_t spiffs_object_index_consistency_check( | |||
spiffs *fs); | |||
#endif /* SPIFFS_NUCLEUS_H_ */ |
@@ -0,0 +1,878 @@ | |||
/* | |||
* spiffs VFS operations | |||
* | |||
* Author: LoBo (loboris@gmail.com / https://github.com/loboris) | |||
* | |||
* Part of this code is copied from or inspired by LUA-RTOS_ESP32 project: | |||
* | |||
* https://github.com/whitecatboard/Lua-RTOS-ESP32 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
*/ | |||
#include <freertos/FreeRTOS.h> | |||
#include <string.h> | |||
#include <stdio.h> | |||
#include <limits.h> | |||
#include "esp_log.h" | |||
#include <sys/stat.h> | |||
#include "esp_vfs.h" | |||
#include "esp_attr.h" | |||
#include <errno.h> | |||
#include <spiffs_vfs.h> | |||
#include <spiffs.h> | |||
#include <esp_spiffs.h> | |||
#include <spiffs_nucleus.h> | |||
#include "list.h" | |||
#include <sys/fcntl.h> | |||
#include <sys/dirent.h> | |||
#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; | |||
} |
@@ -0,0 +1,23 @@ | |||
/* | |||
* spiffs VFS public function | |||
* | |||
* Author: LoBo (loboris@gmail.com / https://github.com/loboris) | |||
* | |||
* Part of this code is copied from or inspired by LUA-RTOS_ESP32 project: | |||
* | |||
* https://github.com/whitecatboard/Lua-RTOS-ESP32 | |||
* IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L. | |||
* Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org) | |||
* | |||
*/ | |||
#define SPIFFS_BASE_PATH "/spiffs" | |||
int spiffs_is_registered; | |||
int spiffs_is_mounted; | |||
void vfs_spiffs_register(); | |||
int spiffs_mount(); | |||
int spiffs_unmount(int unreg); | |||
void spiffs_fs_stat(uint32_t *total, uint32_t *used); |
@@ -0,0 +1,26 @@ | |||
SPIFFS_IMAGE_COMPONENT_PATH := $(COMPONENT_PATH) | |||
ifeq ($(OS),Windows_NT) | |||
MKSPIFFS_BIN="mkspiffs.exe" | |||
else | |||
MKSPIFFS_BIN="mkspiffs" | |||
endif | |||
.PHONY: flashfs | |||
.PHONY: makefs | |||
.PHONY: copyfs | |||
flashfs: $(SDKCONFIG_MAKEFILE) mkspiffs | |||
@echo "Making spiffs image ..." | |||
@echo "$(ESPTOOLPY_WRITE_FLASH)" | |||
$(MKSPIFFS_COMPONENT_PATH)/../mkspiffs/src/$(MKSPIFFS_BIN) -c $(SPIFFS_IMAGE_COMPONENT_PATH)/image -b $(CONFIG_SPIFFS_LOG_BLOCK_SIZE) -p $(CONFIG_SPIFFS_LOG_PAGE_SIZE) -s $(CONFIG_SPIFFS_SIZE) $(BUILD_DIR_BASE)/spiffs_image.img | |||
$(ESPTOOLPY_WRITE_FLASH) $(CONFIG_SPIFFS_BASE_ADDR) $(BUILD_DIR_BASE)/spiffs_image.img | |||
makefs: $(SDKCONFIG_MAKEFILE) mkspiffs | |||
@echo "Making spiffs image ..." | |||
@echo "$(ESPTOOLPY_WRITE_FLASH)" | |||
$(MKSPIFFS_COMPONENT_PATH)/../mkspiffs/src/$(MKSPIFFS_BIN) -c $(SPIFFS_IMAGE_COMPONENT_PATH)/image -b $(CONFIG_SPIFFS_LOG_BLOCK_SIZE) -p $(CONFIG_SPIFFS_LOG_PAGE_SIZE) -s $(CONFIG_SPIFFS_SIZE) $(BUILD_DIR_BASE)/spiffs_image.img | |||
copyfs: | |||
@echo "Flashing spiffs image ..." | |||
@echo "$(ESPTOOLPY_WRITE_FLASH)" | |||
$(ESPTOOLPY_WRITE_FLASH) $(CONFIG_SPIFFS_BASE_ADDR) $(SPIFFS_IMAGE_COMPONENT_PATH)/spiffs_image.img |
@@ -0,0 +1,6 @@ | |||
# | |||
# Component Makefile | |||
# | |||
COMPONENT_SRCDIRS := | |||
COMPONENT_ADD_INCLUDEDIRS := |
@@ -0,0 +1,119 @@ | |||
// OCR_A_Extended_M.c | |||
// Font type : Full (95 characters) | |||
// Font size : 16x24 pixels | |||
// Memory usage : 4564 bytes | |||
#if defined(__AVR__) | |||
#include <avr/pgmspace.h> | |||
#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, // <space> | |||
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, // <backslash> | |||
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, // ~ | |||
}; |
@@ -0,0 +1,11 @@ | |||
INTRODUCTION | |||
Spiffs is a file system intended for SPI NOR flash devices on embedded targets. | |||
Spiffs is designed with following characteristics in mind: | |||
* Small (embedded) targets, sparse RAM without heap | |||
* Only big areas of data (blocks) can be erased | |||
* An erase will reset all bits in block to ones | |||
* Writing pulls one to zeroes | |||
* Zeroes can only be pulled to ones by erase | |||
* Wear leveling |