diff --git a/3rdparty/nanosvg/LICENSE.txt b/3rdparty/nanosvg/LICENSE.txt new file mode 100644 index 0000000..f896f2e --- /dev/null +++ b/3rdparty/nanosvg/LICENSE.txt @@ -0,0 +1,18 @@ +Copyright (c) 2013-14 Mikko Mononen memon@inside.org + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + diff --git a/3rdparty/nanosvg/README.md b/3rdparty/nanosvg/README.md new file mode 100644 index 0000000..0865cbe --- /dev/null +++ b/3rdparty/nanosvg/README.md @@ -0,0 +1,102 @@ +*This project is not actively maintained.* + +Nano SVG +========== + +## Parser + +![screenshot of some splines rendered with the sample program](/example/screenshot-1.png?raw=true) + +NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. + +The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. + +NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! + +The shapes in the SVG images are transformed by the viewBox and converted to specified units. +That is, you should get the same looking data as your designed in your favorite app. + +NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose +to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. + +The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. +DPI (dots-per-inch) controls how the unit conversion is done. + +If you don't know or care about the units stuff, "px" and 96 should get you going. + +## Rasterizer + +![screenshot of tiger.svg rendered with NanoSVG rasterizer](/example/screenshot-2.png?raw=true) + +The parser library is accompanied with really simpler SVG rasterizer. Currently it only renders flat filled shapes. + +The intended usage for the rasterizer is to for example bake icons of different size into a texture. The rasterizer is not particular fast or accurate, but it's small and packed in one header file. + + +## Example Usage + +``` C +// Load +struct NSVGimage* image; +image = nsvgParseFromFile("test.svg", "px", 96); +printf("size: %f x %f\n", image->width, image->height); +// Use... +for (shape = image->shapes; shape != NULL; shape = shape->next) { + for (path = shape->paths; path != NULL; path = path->next) { + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } + } +} +// Delete +nsvgDelete(image); +``` + +## Using NanoSVG in your project + +In order to use NanoSVG in your own project, just copy nanosvg.h to your project. +In one C/C++ define `NANOSVG_IMPLEMENTATION` before including the library to expand the NanoSVG implementation in that file. +NanoSVG depends on `stdio.h` ,`string.h` and `math.h`, they should be included where the implementation is expanded before including NanoSVG. + +``` C +#include +#include +#include +#define NANOSVG_IMPLEMENTATION // Expands implementation +#include "nanosvg.h" +``` + +By default, NanoSVG parses only the most common colors. In order to get support for full list of [SVG color keywords](http://www.w3.org/TR/SVG11/types.html#ColorKeywords), define `NANOSVG_ALL_COLOR_KEYWORDS` before expanding the implementation. + +``` C +#include +#include +#include +#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords. +#define NANOSVG_IMPLEMENTATION // Expands implementation +#include "nanosvg.h" +``` + +## Compiling Example Project + +In order to compile the demo project, your will need to install [GLFW](http://www.glfw.org/) to compile. + +NanoSVG demo project uses [premake4](http://industriousone.com/premake) to build platform specific projects, now is good time to install it if you don't have it already. To build the example, navigate into the root folder in your favorite terminal, then: + +- *OS X*: `premake4 xcode4` +- *Windows*: `premake4 vs2010` +- *Linux*: `premake4 gmake` + +See premake4 documentation for full list of supported build file types. The projects will be created in `build` folder. An example of building and running the example on OS X: + +```bash +$ premake4 gmake +$ cd build/ +$ make +$ ./example +``` + +# License + +The library is licensed under [zlib license](LICENSE.txt) diff --git a/3rdparty/nanosvg/example/23.svg b/3rdparty/nanosvg/example/23.svg new file mode 100644 index 0000000..983e570 --- /dev/null +++ b/3rdparty/nanosvg/example/23.svg @@ -0,0 +1,730 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3rdparty/nanosvg/example/drawing.svg b/3rdparty/nanosvg/example/drawing.svg new file mode 100644 index 0000000..428a8e1 --- /dev/null +++ b/3rdparty/nanosvg/example/drawing.svg @@ -0,0 +1,97 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/3rdparty/nanosvg/example/example1.c b/3rdparty/nanosvg/example/example1.c new file mode 100644 index 0000000..100831b --- /dev/null +++ b/3rdparty/nanosvg/example/example1.c @@ -0,0 +1,258 @@ +// +// Copyright (c) 2013 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#include +#include +#include + +#define NANOSVG_IMPLEMENTATION +#include "nanosvg.h" + +NSVGimage* g_image = NULL; + +static unsigned char bgColor[4] = {205,202,200,255}; +static unsigned char lineColor[4] = {0,160,192,255}; + +static float distPtSeg(float x, float y, float px, float py, float qx, float qy) +{ + float pqx, pqy, dx, dy, d, t; + pqx = qx-px; + pqy = qy-py; + dx = x-px; + dy = y-py; + d = pqx*pqx + pqy*pqy; + t = pqx*dx + pqy*dy; + if (d > 0) t /= d; + if (t < 0) t = 0; + else if (t > 1) t = 1; + dx = px + t*pqx - x; + dy = py + t*pqy - y; + return dx*dx + dy*dy; +} + +static void cubicBez(float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + float tol, int level) +{ + float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; + float d; + + if (level > 12) return; + + x12 = (x1+x2)*0.5f; + y12 = (y1+y2)*0.5f; + x23 = (x2+x3)*0.5f; + y23 = (y2+y3)*0.5f; + x34 = (x3+x4)*0.5f; + y34 = (y3+y4)*0.5f; + x123 = (x12+x23)*0.5f; + y123 = (y12+y23)*0.5f; + x234 = (x23+x34)*0.5f; + y234 = (y23+y34)*0.5f; + x1234 = (x123+x234)*0.5f; + y1234 = (y123+y234)*0.5f; + + d = distPtSeg(x1234, y1234, x1,y1, x4,y4); + if (d > tol*tol) { + cubicBez(x1,y1, x12,y12, x123,y123, x1234,y1234, tol, level+1); + cubicBez(x1234,y1234, x234,y234, x34,y34, x4,y4, tol, level+1); + } else { + glVertex2f(x4, y4); + } +} + +void drawPath(float* pts, int npts, char closed, float tol) +{ + int i; + glBegin(GL_LINE_STRIP); + glColor4ubv(lineColor); + glVertex2f(pts[0], pts[1]); + for (i = 0; i < npts-1; i += 3) { + float* p = &pts[i*2]; + cubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7], tol, 0); + } + if (closed) { + glVertex2f(pts[0], pts[1]); + } + glEnd(); +} + +void drawControlPts(float* pts, int npts) +{ + int i; + + // Control lines + glColor4ubv(lineColor); + glBegin(GL_LINES); + for (i = 0; i < npts-1; i += 3) { + float* p = &pts[i*2]; + glVertex2f(p[0],p[1]); + glVertex2f(p[2],p[3]); + glVertex2f(p[4],p[5]); + glVertex2f(p[6],p[7]); + } + glEnd(); + + // Points + glPointSize(6.0f); + glColor4ubv(lineColor); + + glBegin(GL_POINTS); + glVertex2f(pts[0],pts[1]); + for (i = 0; i < npts-1; i += 3) { + float* p = &pts[i*2]; + glVertex2f(p[6],p[7]); + } + glEnd(); + + // Points + glPointSize(3.0f); + + glBegin(GL_POINTS); + glColor4ubv(bgColor); + glVertex2f(pts[0],pts[1]); + for (i = 0; i < npts-1; i += 3) { + float* p = &pts[i*2]; + glColor4ubv(lineColor); + glVertex2f(p[2],p[3]); + glVertex2f(p[4],p[5]); + glColor4ubv(bgColor); + glVertex2f(p[6],p[7]); + } + glEnd(); +} + +void drawframe(GLFWwindow* window) +{ + int width = 0, height = 0; + float view[4], cx, cy, hw, hh, aspect, px; + NSVGshape* shape; + NSVGpath* path; + + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwGetFramebufferSize(window, &width, &height); + + glViewport(0, 0, width, height); + glClearColor(220.0f/255.0f, 220.0f/255.0f, 220.0f/255.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_TEXTURE_2D); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // Fit view to bounds + cx = g_image->width*0.5f; + cy = g_image->height*0.5f; + hw = g_image->width*0.5f; + hh = g_image->height*0.5f; + + if (width/hw < height/hh) { + aspect = (float)height / (float)width; + view[0] = cx - hw * 1.2f; + view[2] = cx + hw * 1.2f; + view[1] = cy - hw * 1.2f * aspect; + view[3] = cy + hw * 1.2f * aspect; + } else { + aspect = (float)width / (float)height; + view[0] = cx - hh * 1.2f * aspect; + view[2] = cx + hh * 1.2f * aspect; + view[1] = cy - hh * 1.2f; + view[3] = cy + hh * 1.2f; + } + // Size of one pixel. + px = (view[2] - view[1]) / (float)width; + + glOrtho(view[0], view[2], view[3], view[1], -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + glColor4ub(255,255,255,255); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + // Draw bounds + glColor4ub(0,0,0,64); + glBegin(GL_LINE_LOOP); + glVertex2f(0, 0); + glVertex2f(g_image->width, 0); + glVertex2f(g_image->width, g_image->height); + glVertex2f(0, g_image->height); + glEnd(); + + for (shape = g_image->shapes; shape != NULL; shape = shape->next) { + for (path = shape->paths; path != NULL; path = path->next) { + drawPath(path->pts, path->npts, path->closed, px * 1.5f); + drawControlPts(path->pts, path->npts); + } + } + + glfwSwapBuffers(window); +} + +void resizecb(GLFWwindow* window, int width, int height) +{ + // Update and render + NSVG_NOTUSED(width); + NSVG_NOTUSED(height); + drawframe(window); +} + +int main() +{ + GLFWwindow* window; + const GLFWvidmode* mode; + + if (!glfwInit()) + return -1; + + mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + window = glfwCreateWindow(mode->width - 40, mode->height - 80, "Nano SVG", NULL, NULL); + if (!window) + { + printf("Could not open window\n"); + glfwTerminate(); + return -1; + } + + glfwSetFramebufferSizeCallback(window, resizecb); + glfwMakeContextCurrent(window); + glEnable(GL_POINT_SMOOTH); + glEnable(GL_LINE_SMOOTH); + + + g_image = nsvgParseFromFile("../example/nano.svg", "px", 96.0f); + if (g_image == NULL) { + printf("Could not open SVG image.\n"); + glfwTerminate(); + return -1; + } + + while (!glfwWindowShouldClose(window)) + { + drawframe(window); + glfwPollEvents(); + } + + nsvgDelete(g_image); + + glfwTerminate(); + return 0; +} diff --git a/3rdparty/nanosvg/example/example2.c b/3rdparty/nanosvg/example/example2.c new file mode 100644 index 0000000..9ae9b59 --- /dev/null +++ b/3rdparty/nanosvg/example/example2.c @@ -0,0 +1,69 @@ +// +// Copyright (c) 2013 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#include +#include +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" +#define NANOSVG_IMPLEMENTATION +#include "nanosvg.h" +#define NANOSVGRAST_IMPLEMENTATION +#include "nanosvgrast.h" + +int main() +{ + NSVGimage *image = NULL; + NSVGrasterizer *rast = NULL; + unsigned char* img = NULL; + int w, h; + const char* filename = "../example/23.svg"; + + printf("parsing %s\n", filename); + image = nsvgParseFromFile(filename, "px", 96.0f); + if (image == NULL) { + printf("Could not open SVG image.\n"); + goto error; + } + w = (int)image->width; + h = (int)image->height; + + rast = nsvgCreateRasterizer(); + if (rast == NULL) { + printf("Could not init rasterizer.\n"); + goto error; + } + + img = malloc(w*h*4); + if (img == NULL) { + printf("Could not alloc image buffer.\n"); + goto error; + } + + printf("rasterizing image %d x %d\n", w, h); + nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); + + printf("writing svg.png\n"); + stbi_write_png("svg.png", w, h, 4, img, w*4); + +error: + nsvgDeleteRasterizer(rast); + nsvgDelete(image); + + return 0; +} diff --git a/3rdparty/nanosvg/example/nano.svg b/3rdparty/nanosvg/example/nano.svg new file mode 100644 index 0000000..882830f --- /dev/null +++ b/3rdparty/nanosvg/example/nano.svg @@ -0,0 +1,27 @@ + + + + + + diff --git a/3rdparty/nanosvg/example/screenshot-1.png b/3rdparty/nanosvg/example/screenshot-1.png new file mode 100644 index 0000000..f6526cf Binary files /dev/null and b/3rdparty/nanosvg/example/screenshot-1.png differ diff --git a/3rdparty/nanosvg/example/screenshot-2.png b/3rdparty/nanosvg/example/screenshot-2.png new file mode 100644 index 0000000..a4641f8 Binary files /dev/null and b/3rdparty/nanosvg/example/screenshot-2.png differ diff --git a/3rdparty/nanosvg/example/stb_image_write.h b/3rdparty/nanosvg/example/stb_image_write.h new file mode 100644 index 0000000..b9f7aae --- /dev/null +++ b/3rdparty/nanosvg/example/stb_image_write.h @@ -0,0 +1,511 @@ +/* stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 + no warranty implied; use at your own risk + + +Before including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + +in the file that you want to have the implementation. + + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicitly, not optimal image file size + or run-time performance. + +USAGE: + + There are three functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP and TGA formats expand Y to RGB in the file format. BMP does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#include +#include +#include +#include +#include + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void writefv(FILE *f, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; } + case '2': { int x = va_arg(v,int); unsigned char b[2]; + b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8); + fwrite(b,2,1,f); break; } + case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; + b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8); + b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24); + fwrite(b,4,1,f); break; } + default: + assert(0); + return; + } + } +} + +static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + fwrite(arr, 3, 1, f); +} + +static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + stbiw_uint32 zero = 0; + int i,j,k, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + if (write_alpha < 0) + fwrite(&d[comp-1], 1, 1, f); + switch (comp) { + case 1: + case 2: write3(f, d[0],d[0],d[0]); + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k=0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255; + write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]); + break; + } + if (write_alpha > 0) + fwrite(&d[comp-1], 1, 1, f); + } + fwrite(&zero,scanline_pad,1,f); + } +} + +static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...) +{ + FILE *f; + if (y < 0 || x < 0) return 0; + f = fopen(filename, "wb"); + if (f) { + va_list v; + va_start(v, fmt); + writefv(f, fmt, v); + va_end(v); + write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); + fclose(f); + } + return f != NULL; +} + +int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + int has_alpha = !(comp & 1); + return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0, + "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha); +} + +// stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size() +#define stbi__sbraw(a) ((int *) (a) - 2) +#define stbi__sbm(a) stbi__sbraw(a)[0] +#define stbi__sbn(a) stbi__sbraw(a)[1] + +#define stbi__sbneedgrow(a,n) ((a)==0 || stbi__sbn(a)+n >= stbi__sbm(a)) +#define stbi__sbmaybegrow(a,n) (stbi__sbneedgrow(a,(n)) ? stbi__sbgrow(a,n) : 0) +#define stbi__sbgrow(a,n) stbi__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbi__sbpush(a, v) (stbi__sbmaybegrow(a,1), (a)[stbi__sbn(a)++] = (v)) +#define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0) +#define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)),0 : 0) + +static void *stbi__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbi__sbm(*arr)+increment : increment+1; + void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); + assert(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbi__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbi__sbpush(data, (unsigned char) *bitbuffer); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbi__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbi__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbi__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush()) +#define stbi__zlib_huffa(b,c) stbi__zlib_add(stbi__zlib_bitrev(b,c),c) +// default huffman tables +#define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8) +#define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9) +#define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256,7) +#define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280,8) +#define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n)) +#define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n)) + +#define stbi__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack! + if (quality < 5) quality = 5; + + stbi__sbpush(out, 0x78); // DEFLATE 32K window + stbi__sbpush(out, 0x5e); // FLEVEL = 1 + stbi__zlib_add(1,1); // BFINAL = 1 + stbi__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbi__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbi__zhash(data+i)&(stbi__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbi__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbi__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbi__sbn(hash_table[h]) == 2*quality) { + memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbi__sbn(hash_table[h]) = quality; + } + stbi__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbi__zhash(data+i+1)&(stbi__ZHASH-1); + hlist = hash_table[h]; + n = stbi__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbi__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = data+i - bestloc; // distance back + assert(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbi__zlib_huff(j+257); + if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbi__zlib_add(stbi__zlib_bitrev(j,5),5); + if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbi__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbi__zlib_huffb(data[i]); + stbi__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbi__zlib_add(0,1); + + for (i=0; i < stbi__ZHASH; ++i) + (void) stbi__sbfree(hash_table[i]); + + { + // compute adler32 on input + unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552; + int j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbi__sbpush(out, (unsigned char) (s2 >> 8)); + stbi__sbpush(out, (unsigned char) s2); + stbi__sbpush(out, (unsigned char) (s1 >> 8)); + stbi__sbpush(out, (unsigned char) s1); + } + *out_len = stbi__sbn(out); + // make returned pointer freeable + memmove(stbi__sbraw(out), out, *out_len); + return (unsigned char *) stbi__sbraw(out); +} + +unsigned int stbi__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256]; + unsigned int crc = ~0u; + int i,j; + if (crc_table[1] == 0) + for(i=0; i < 256; i++) + for (crc_table[i]=i, j=0; j < 8; ++j) + crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0); + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbi__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4) +#define stbi__wp32(data,v) stbi__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbi__wptag(data,s) stbi__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbi__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbi__crc32(*data - len - 4, len+4); + stbi__wp32(*data, crc); +} + +static unsigned char stbi__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return (unsigned char) a; + if (pb <= pc) return (unsigned char) b; + return (unsigned char) c; +} + +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbi__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbi__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbi__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + memcpy(filt+j*(x*n+1)+1, line_buffer, x*n); + } + free(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + free(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + memcpy(o,sig,8); o+= 8; + stbi__wp32(o, 13); // header length + stbi__wptag(o, "IHDR"); + stbi__wp32(o, x); + stbi__wp32(o, y); + *o++ = 8; + *o++ = (unsigned char) ctype[n]; + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbi__wpcrc(&o,13); + + stbi__wp32(o, zlen); + stbi__wptag(o, "IDAT"); + memcpy(o, zlib, zlen); o += zlen; free(zlib); + stbi__wpcrc(&o, zlen); + + stbi__wp32(o,0); + stbi__wptag(o, "IEND"); + stbi__wpcrc(&o,0); + + assert(o == out + *out_len); + + return out; +} + +int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (!png) return 0; + f = fopen(filename, "wb"); + if (!f) { free(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + free(png); + return 1; +} +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ diff --git a/3rdparty/nanosvg/premake4.lua b/3rdparty/nanosvg/premake4.lua new file mode 100644 index 0000000..8befd82 --- /dev/null +++ b/3rdparty/nanosvg/premake4.lua @@ -0,0 +1,56 @@ + +local action = _ACTION or "" + +solution "nanosvg" + location ( "build" ) + configurations { "Debug", "Release" } + platforms {"native", "x64", "x32"} + + project "example1" + kind "ConsoleApp" + language "C++" + files { "example/example1.c", "example/*.h", "src/*.h" } + includedirs { "example", "src" } + targetdir("build") + + configuration { "linux" } + links { "X11","Xrandr", "rt", "GL", "GLU", "pthread", "glfw" } + + configuration { "windows" } + links { "glu32","opengl32", "gdi32", "winmm", "user32" } + + configuration { "macosx" } + links { "glfw3" } + linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" } + + configuration "Debug" + defines { "DEBUG" } + flags { "Symbols", "ExtraWarnings"} + + configuration "Release" + defines { "NDEBUG" } + flags { "Optimize", "ExtraWarnings"} + + project "example2" + kind "ConsoleApp" + language "C++" + files { "example/example2.c", "example/*.h", "src/*.h" } + includedirs { "example", "src" } + targetdir("build") + + configuration { "linux" } + links { "X11","Xrandr", "rt", "pthread" } + + configuration { "windows" } + links { "winmm", "user32" } + + configuration { "macosx" } + linkoptions { "-framework Cocoa", "-framework IOKit" } + + configuration "Debug" + defines { "DEBUG" } + flags { "Symbols", "ExtraWarnings"} + + configuration "Release" + defines { "NDEBUG" } + flags { "Optimize", "ExtraWarnings"} diff --git a/3rdparty/nanosvg/src/nanosvg.h b/3rdparty/nanosvg/src/nanosvg.h new file mode 100644 index 0000000..22edd71 --- /dev/null +++ b/3rdparty/nanosvg/src/nanosvg.h @@ -0,0 +1,3057 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example + * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) + * + * Arc calculation code based on canvg (https://code.google.com/p/canvg/) + * + * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html + * + */ + +#ifndef NANOSVG_H +#define NANOSVG_H + +#ifndef NANOSVG_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. +// +// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. +// +// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! +// +// The shapes in the SVG images are transformed by the viewBox and converted to specified units. +// That is, you should get the same looking data as your designed in your favorite app. +// +// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose +// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. +// +// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. +// DPI (dots-per-inch) controls how the unit conversion is done. +// +// If you don't know or care about the units stuff, "px" and 96 should get you going. + + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + printf("size: %f x %f\n", image->width, image->height); + // Use... + for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { + for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { + for (int i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } + } + } + // Delete + nsvgDelete(image); +*/ + +enum NSVGpaintType { + NSVG_PAINT_NONE = 0, + NSVG_PAINT_COLOR = 1, + NSVG_PAINT_LINEAR_GRADIENT = 2, + NSVG_PAINT_RADIAL_GRADIENT = 3 +}; + +enum NSVGspreadType { + NSVG_SPREAD_PAD = 0, + NSVG_SPREAD_REFLECT = 1, + NSVG_SPREAD_REPEAT = 2 +}; + +enum NSVGlineJoin { + NSVG_JOIN_MITER = 0, + NSVG_JOIN_ROUND = 1, + NSVG_JOIN_BEVEL = 2 +}; + +enum NSVGlineCap { + NSVG_CAP_BUTT = 0, + NSVG_CAP_ROUND = 1, + NSVG_CAP_SQUARE = 2 +}; + +enum NSVGfillRule { + NSVG_FILLRULE_NONZERO = 0, + NSVG_FILLRULE_EVENODD = 1 +}; + +enum NSVGflags { + NSVG_FLAGS_VISIBLE = 0x01 +}; + +typedef struct NSVGgradientStop { + unsigned int color; + float offset; +} NSVGgradientStop; + +typedef struct NSVGgradient { + float xform[6]; + char spread; + float fx, fy; + int nstops; + NSVGgradientStop stops[1]; +} NSVGgradient; + +typedef struct NSVGpaint { + char type; + union { + unsigned int color; + NSVGgradient* gradient; + }; +} NSVGpaint; + +typedef struct NSVGpath +{ + float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... + int npts; // Total number of bezier points. + char closed; // Flag indicating if shapes should be treated as closed. + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + struct NSVGpath* next; // Pointer to next path, or NULL if last element. +} NSVGpath; + +typedef struct NSVGshape +{ + char id[64]; // Optional 'id' attr of the shape or its group + char title[64]; // Optional 'title' of the shape or its ancestor(s) + NSVGpaint fill; // Fill paint + NSVGpaint stroke; // Stroke paint + float opacity; // Opacity of the shape. + float strokeWidth; // Stroke width (scaled). + float strokeDashOffset; // Stroke dash offset (scaled). + float strokeDashArray[8]; // Stroke dash array (scaled). + char strokeDashCount; // Number of dash values in dash array. + char strokeLineJoin; // Stroke join type. + char strokeLineCap; // Stroke cap type. + float miterLimit; // Miter limit + char fillRule; // Fill rule, see NSVGfillRule. + unsigned char flags; // Logical or of NSVG_FLAGS_* flags + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + NSVGpath* paths; // Linked list of paths in the image. + struct NSVGshape* next; // Pointer to next shape, or NULL if last element. +} NSVGshape; + +typedef struct NSVGimage +{ + float width; // Width of the image. + float height; // Height of the image. + NSVGshape* shapes; // Linked list of shapes in the image. +} NSVGimage; + +// Parses SVG file from a file, returns SVG image as paths. +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); + +// Parses SVG file from a null terminated string, returns SVG image as paths. +// Important note: changes the string. +NSVGimage* nsvgParse(char* input, const char* units, float dpi); + +// Duplicates a path. +NSVGpath* nsvgDuplicatePath(NSVGpath* p); + +// Deletes an image. +void nsvgDelete(NSVGimage* image); + +#ifndef NANOSVG_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#endif // NANOSVG_H + +#ifdef NANOSVG_IMPLEMENTATION + +#include +#include +#include + +#define NSVG_PI (3.14159265358979323846264338327f) +#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. + +#define NSVG_ALIGN_MIN 0 +#define NSVG_ALIGN_MID 1 +#define NSVG_ALIGN_MAX 2 +#define NSVG_ALIGN_NONE 0 +#define NSVG_ALIGN_MEET 1 +#define NSVG_ALIGN_SLICE 2 + +#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) +#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) + +#ifdef _MSC_VER + #pragma warning (disable: 4996) // Switch off security warnings + #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings + #ifdef __cplusplus + #define NSVG_INLINE inline + #else + #define NSVG_INLINE + #endif +#else + #define NSVG_INLINE inline +#endif + + +static int nsvg__isspace(char c) +{ + return strchr(" \t\n\v\f\r", c) != 0; +} + +static int nsvg__isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } +static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } + + +// Simple XML parser + +#define NSVG_XML_TAG 1 +#define NSVG_XML_CONTENT 2 +#define NSVG_XML_MAX_ATTRIBS 256 + +static void nsvg__parseContent(char* s, + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + // Trim start white spaces + while (*s && nsvg__isspace(*s)) s++; + if (!*s) return; + + if (contentCb) + (*contentCb)(ud, s); +} + +static void nsvg__parseElement(char* s, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void* ud) +{ + const char* attr[NSVG_XML_MAX_ATTRIBS]; + int nattr = 0; + char* name; + int start = 0; + int end = 0; + char quote; + + // Skip white space after the '<' + while (*s && nsvg__isspace(*s)) s++; + + // Check if the tag is end tag + if (*s == '/') { + s++; + end = 1; + } else { + start = 1; + } + + // Skip comments, data and preprocessor stuff. + if (!*s || *s == '?' || *s == '!') + return; + + // Get tag name + name = s; + while (*s && !nsvg__isspace(*s)) s++; + if (*s) { *s++ = '\0'; } + + // Get attribs + while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { + char* name = NULL; + char* value = NULL; + + // Skip white space before the attrib name + while (*s && nsvg__isspace(*s)) s++; + if (!*s) break; + if (*s == '/') { + end = 1; + break; + } + name = s; + // Find end of the attrib name. + while (*s && !nsvg__isspace(*s) && *s != '=') s++; + if (*s) { *s++ = '\0'; } + // Skip until the beginning of the value. + while (*s && *s != '\"' && *s != '\'') s++; + if (!*s) break; + quote = *s; + s++; + // Store value and find the end of it. + value = s; + while (*s && *s != quote) s++; + if (*s) { *s++ = '\0'; } + + // Store only well formed attributes + if (name && value) { + attr[nattr++] = name; + attr[nattr++] = value; + } + } + + // List terminator + attr[nattr++] = 0; + attr[nattr++] = 0; + + // Call callbacks. + if (start && startelCb) + (*startelCb)(ud, name, attr); + if (end && endelCb) + (*endelCb)(ud, name); +} + +int nsvg__parseXML(char* input, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + char* s = input; + char* mark = s; + int state = NSVG_XML_CONTENT; + while (*s) { + if (*s == '<' && state == NSVG_XML_CONTENT) { + // Start of a tag + *s++ = '\0'; + nsvg__parseContent(mark, contentCb, ud); + mark = s; + state = NSVG_XML_TAG; + } else if (*s == '>' && state == NSVG_XML_TAG) { + // Start of a content or new tag. + *s++ = '\0'; + nsvg__parseElement(mark, startelCb, endelCb, ud); + mark = s; + state = NSVG_XML_CONTENT; + } else { + s++; + } + } + + return 1; +} + + +/* Simple SVG parser. */ + +#define NSVG_MAX_ATTR 128 + +enum NSVGgradientUnits { + NSVG_USER_SPACE = 0, + NSVG_OBJECT_SPACE = 1 +}; + +#define NSVG_MAX_DASHES 8 + +enum NSVGunits { + NSVG_UNITS_USER, + NSVG_UNITS_PX, + NSVG_UNITS_PT, + NSVG_UNITS_PC, + NSVG_UNITS_MM, + NSVG_UNITS_CM, + NSVG_UNITS_IN, + NSVG_UNITS_PERCENT, + NSVG_UNITS_EM, + NSVG_UNITS_EX +}; + +typedef struct NSVGcoordinate { + float value; + int units; +} NSVGcoordinate; + +typedef struct NSVGlinearData { + NSVGcoordinate x1, y1, x2, y2; +} NSVGlinearData; + +typedef struct NSVGradialData { + NSVGcoordinate cx, cy, r, fx, fy; +} NSVGradialData; + +typedef struct NSVGgradientData +{ + char id[64]; + char ref[64]; + char type; + union { + NSVGlinearData linear; + NSVGradialData radial; + }; + char spread; + char units; + float xform[6]; + int nstops; + NSVGgradientStop* stops; + struct NSVGgradientData* next; +} NSVGgradientData; + +typedef struct NSVGattrib +{ + char id[64]; + char title[64]; + float xform[6]; + unsigned int fillColor; + unsigned int strokeColor; + float opacity; + float fillOpacity; + float strokeOpacity; + char fillGradient[64]; + char strokeGradient[64]; + float strokeWidth; + float strokeDashOffset; + float strokeDashArray[NSVG_MAX_DASHES]; + int strokeDashCount; + char strokeLineJoin; + char strokeLineCap; + float miterLimit; + char fillRule; + float fontSize; + unsigned int stopColor; + float stopOpacity; + float stopOffset; + char hasFill; + char hasStroke; + char visible; +} NSVGattrib; + +typedef struct NSVGparser +{ + NSVGattrib attr[NSVG_MAX_ATTR]; + int attrHead; + float* pts; + int npts; + int cpts; + NSVGpath* plist; + NSVGimage* image; + NSVGgradientData* gradients; + NSVGshape* shapesTail; + float viewMinx, viewMiny, viewWidth, viewHeight; + int alignX, alignY, alignType; + float dpi; + char pathFlag; + char defsFlag; + char titleFlag; + char shapeFlag; +} NSVGparser; + +static void nsvg__xformIdentity(float* t) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetTranslation(float* t, float tx, float ty) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = tx; t[5] = ty; +} + +static void nsvg__xformSetScale(float* t, float sx, float sy) +{ + t[0] = sx; t[1] = 0.0f; + t[2] = 0.0f; t[3] = sy; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewX(float* t, float a) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = tanf(a); t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewY(float* t, float a) +{ + t[0] = 1.0f; t[1] = tanf(a); + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetRotation(float* t, float a) +{ + float cs = cosf(a), sn = sinf(a); + t[0] = cs; t[1] = sn; + t[2] = -sn; t[3] = cs; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformMultiply(float* t, float* s) +{ + float t0 = t[0] * s[0] + t[1] * s[2]; + float t2 = t[2] * s[0] + t[3] * s[2]; + float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; + t[1] = t[0] * s[1] + t[1] * s[3]; + t[3] = t[2] * s[1] + t[3] * s[3]; + t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; + t[0] = t0; + t[2] = t2; + t[4] = t4; +} + +static void nsvg__xformInverse(float* inv, float* t) +{ + double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; + if (det > -1e-6 && det < 1e-6) { + nsvg__xformIdentity(t); + return; + } + invdet = 1.0 / det; + inv[0] = (float)(t[3] * invdet); + inv[2] = (float)(-t[2] * invdet); + inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); + inv[1] = (float)(-t[1] * invdet); + inv[3] = (float)(t[0] * invdet); + inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); +} + +static void nsvg__xformPremultiply(float* t, float* s) +{ + float s2[6]; + memcpy(s2, s, sizeof(float)*6); + nsvg__xformMultiply(s2, t); + memcpy(t, s2, sizeof(float)*6); +} + +static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2] + t[4]; + *dy = x*t[1] + y*t[3] + t[5]; +} + +static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2]; + *dy = x*t[1] + y*t[3]; +} + +#define NSVG_EPSILON (1e-12) + +static int nsvg__ptInBounds(float* pt, float* bounds) +{ + return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; +} + + +static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) +{ + double it = 1.0-t; + return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; +} + +static void nsvg__curveBounds(float* bounds, float* curve) +{ + int i, j, count; + double roots[2], a, b, c, b2ac, t, v; + float* v0 = &curve[0]; + float* v1 = &curve[2]; + float* v2 = &curve[4]; + float* v3 = &curve[6]; + + // Start the bounding box by end points + bounds[0] = nsvg__minf(v0[0], v3[0]); + bounds[1] = nsvg__minf(v0[1], v3[1]); + bounds[2] = nsvg__maxf(v0[0], v3[0]); + bounds[3] = nsvg__maxf(v0[1], v3[1]); + + // Bezier curve fits inside the convex hull of it's control points. + // If control points are inside the bounds, we're done. + if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) + return; + + // Add bezier curve inflection points in X and Y. + for (i = 0; i < 2; i++) { + a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; + b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; + c = 3.0 * v1[i] - 3.0 * v0[i]; + count = 0; + if (fabs(a) < NSVG_EPSILON) { + if (fabs(b) > NSVG_EPSILON) { + t = -c / b; + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } else { + b2ac = b*b - 4.0*c*a; + if (b2ac > NSVG_EPSILON) { + t = (-b + sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + t = (-b - sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } + for (j = 0; j < count; j++) { + v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); + bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); + bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); + } + } +} + +static NSVGparser* nsvg__createParser() +{ + NSVGparser* p; + p = (NSVGparser*)malloc(sizeof(NSVGparser)); + if (p == NULL) goto error; + memset(p, 0, sizeof(NSVGparser)); + + p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); + if (p->image == NULL) goto error; + memset(p->image, 0, sizeof(NSVGimage)); + + // Init style + nsvg__xformIdentity(p->attr[0].xform); + memset(p->attr[0].id, 0, sizeof p->attr[0].id); + p->attr[0].fillColor = NSVG_RGB(0,0,0); + p->attr[0].strokeColor = NSVG_RGB(0,0,0); + p->attr[0].opacity = 1; + p->attr[0].fillOpacity = 1; + p->attr[0].strokeOpacity = 1; + p->attr[0].stopOpacity = 1; + p->attr[0].strokeWidth = 1; + p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; + p->attr[0].strokeLineCap = NSVG_CAP_BUTT; + p->attr[0].miterLimit = 4; + p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; + p->attr[0].hasFill = 1; + p->attr[0].visible = 1; + + return p; + +error: + if (p) { + if (p->image) free(p->image); + free(p); + } + return NULL; +} + +static void nsvg__deletePaths(NSVGpath* path) +{ + while (path) { + NSVGpath *next = path->next; + if (path->pts != NULL) + free(path->pts); + free(path); + path = next; + } +} + +static void nsvg__deletePaint(NSVGpaint* paint) +{ + if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) + free(paint->gradient); +} + +static void nsvg__deleteGradientData(NSVGgradientData* grad) +{ + NSVGgradientData* next; + while (grad != NULL) { + next = grad->next; + free(grad->stops); + free(grad); + grad = next; + } +} + +static void nsvg__deleteParser(NSVGparser* p) +{ + if (p != NULL) { + nsvg__deletePaths(p->plist); + nsvg__deleteGradientData(p->gradients); + nsvgDelete(p->image); + free(p->pts); + free(p); + } +} + +static void nsvg__resetPath(NSVGparser* p) +{ + p->npts = 0; +} + +static void nsvg__addPoint(NSVGparser* p, float x, float y) +{ + if (p->npts+1 > p->cpts) { + p->cpts = p->cpts ? p->cpts*2 : 8; + p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); + if (!p->pts) return; + } + p->pts[p->npts*2+0] = x; + p->pts[p->npts*2+1] = y; + p->npts++; +} + +static void nsvg__moveTo(NSVGparser* p, float x, float y) +{ + if (p->npts > 0) { + p->pts[(p->npts-1)*2+0] = x; + p->pts[(p->npts-1)*2+1] = y; + } else { + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__lineTo(NSVGparser* p, float x, float y) +{ + float px,py, dx,dy; + if (p->npts > 0) { + px = p->pts[(p->npts-1)*2+0]; + py = p->pts[(p->npts-1)*2+1]; + dx = x - px; + dy = y - py; + nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); + nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) +{ + if (p->npts > 0) { + nsvg__addPoint(p, cpx1, cpy1); + nsvg__addPoint(p, cpx2, cpy2); + nsvg__addPoint(p, x, y); + } +} + +static NSVGattrib* nsvg__getAttr(NSVGparser* p) +{ + return &p->attr[p->attrHead]; +} + +static void nsvg__pushAttr(NSVGparser* p) +{ + if (p->attrHead < NSVG_MAX_ATTR-1) { + p->attrHead++; + memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); + } +} + +static void nsvg__popAttr(NSVGparser* p) +{ + if (p->attrHead > 0) + p->attrHead--; +} + +static float nsvg__actualOrigX(NSVGparser* p) +{ + return p->viewMinx; +} + +static float nsvg__actualOrigY(NSVGparser* p) +{ + return p->viewMiny; +} + +static float nsvg__actualWidth(NSVGparser* p) +{ + return p->viewWidth; +} + +static float nsvg__actualHeight(NSVGparser* p) +{ + return p->viewHeight; +} + +static float nsvg__actualLength(NSVGparser* p) +{ + float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); + return sqrtf(w*w + h*h) / sqrtf(2.0f); +} + +static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) +{ + NSVGattrib* attr = nsvg__getAttr(p); + switch (c.units) { + case NSVG_UNITS_USER: return c.value; + case NSVG_UNITS_PX: return c.value; + case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; + case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; + case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; + case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; + case NSVG_UNITS_IN: return c.value * p->dpi; + case NSVG_UNITS_EM: return c.value * attr->fontSize; + case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. + case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; + default: return c.value; + } + return c.value; +} + +static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) +{ + NSVGgradientData* grad = p->gradients; + if (id == NULL || *id == '\0') + return NULL; + while (grad != NULL) { + if (strcmp(grad->id, id) == 0) + return grad; + grad = grad->next; + } + return NULL; +} + +static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGgradientData* data = NULL; + NSVGgradientData* ref = NULL; + NSVGgradientStop* stops = NULL; + NSVGgradient* grad; + float ox, oy, sw, sh, sl; + int nstops = 0; + int refIter; + + data = nsvg__findGradientData(p, id); + if (data == NULL) return NULL; + + // TODO: use ref to fill in all unset values too. + ref = data; + refIter = 0; + while (ref != NULL) { + NSVGgradientData* nextRef = NULL; + if (stops == NULL && ref->stops != NULL) { + stops = ref->stops; + nstops = ref->nstops; + break; + } + nextRef = nsvg__findGradientData(p, ref->ref); + if (nextRef == ref) break; // prevent infite loops on malformed data + ref = nextRef; + refIter++; + if (refIter > 32) break; // prevent infite loops on malformed data + } + if (stops == NULL) return NULL; + + grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); + if (grad == NULL) return NULL; + + // The shape width and height. + if (data->units == NSVG_OBJECT_SPACE) { + ox = localBounds[0]; + oy = localBounds[1]; + sw = localBounds[2] - localBounds[0]; + sh = localBounds[3] - localBounds[1]; + } else { + ox = nsvg__actualOrigX(p); + oy = nsvg__actualOrigY(p); + sw = nsvg__actualWidth(p); + sh = nsvg__actualHeight(p); + } + sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); + + if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { + float x1, y1, x2, y2, dx, dy; + x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); + y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); + x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); + y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); + // Calculate transform aligned to the line + dx = x2 - x1; + dy = y2 - y1; + grad->xform[0] = dy; grad->xform[1] = -dx; + grad->xform[2] = dx; grad->xform[3] = dy; + grad->xform[4] = x1; grad->xform[5] = y1; + } else { + float cx, cy, fx, fy, r; + cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); + cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); + fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); + fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); + r = nsvg__convertToPixels(p, data->radial.r, 0, sl); + // Calculate transform aligned to the circle + grad->xform[0] = r; grad->xform[1] = 0; + grad->xform[2] = 0; grad->xform[3] = r; + grad->xform[4] = cx; grad->xform[5] = cy; + grad->fx = fx / r; + grad->fy = fy / r; + } + + nsvg__xformMultiply(grad->xform, data->xform); + nsvg__xformMultiply(grad->xform, attr->xform); + + grad->spread = data->spread; + memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); + grad->nstops = nstops; + + *paintType = data->type; + + return grad; +} + +static float nsvg__getAverageScale(float* t) +{ + float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); + float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); + return (sx + sy) * 0.5f; +} + +static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) +{ + NSVGpath* path; + float curve[4*2], curveBounds[4]; + int i, first = 1; + for (path = shape->paths; path != NULL; path = path->next) { + nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); + for (i = 0; i < path->npts-1; i += 3) { + nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); + nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); + nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); + nsvg__curveBounds(curveBounds, curve); + if (first) { + bounds[0] = curveBounds[0]; + bounds[1] = curveBounds[1]; + bounds[2] = curveBounds[2]; + bounds[3] = curveBounds[3]; + first = 0; + } else { + bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); + bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); + bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); + bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); + } + curve[0] = curve[6]; + curve[1] = curve[7]; + } + } +} + +static void nsvg__addShape(NSVGparser* p) +{ + NSVGattrib* attr = nsvg__getAttr(p); + float scale = 1.0f; + NSVGshape* shape; + NSVGpath* path; + int i; + + if (p->plist == NULL) + return; + + shape = (NSVGshape*)malloc(sizeof(NSVGshape)); + if (shape == NULL) goto error; + memset(shape, 0, sizeof(NSVGshape)); + + memcpy(shape->id, attr->id, sizeof shape->id); + memcpy(shape->title, attr->title, sizeof shape->title); + scale = nsvg__getAverageScale(attr->xform); + shape->strokeWidth = attr->strokeWidth * scale; + shape->strokeDashOffset = attr->strokeDashOffset * scale; + shape->strokeDashCount = (char)attr->strokeDashCount; + for (i = 0; i < attr->strokeDashCount; i++) + shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; + shape->strokeLineJoin = attr->strokeLineJoin; + shape->strokeLineCap = attr->strokeLineCap; + shape->miterLimit = attr->miterLimit; + shape->fillRule = attr->fillRule; + shape->opacity = attr->opacity; + + shape->paths = p->plist; + p->plist = NULL; + + // Calculate shape bounds + shape->bounds[0] = shape->paths->bounds[0]; + shape->bounds[1] = shape->paths->bounds[1]; + shape->bounds[2] = shape->paths->bounds[2]; + shape->bounds[3] = shape->paths->bounds[3]; + for (path = shape->paths->next; path != NULL; path = path->next) { + shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); + shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); + shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); + shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); + } + + // Set fill + if (attr->hasFill == 0) { + shape->fill.type = NSVG_PAINT_NONE; + } else if (attr->hasFill == 1) { + shape->fill.type = NSVG_PAINT_COLOR; + shape->fill.color = attr->fillColor; + shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; + } else if (attr->hasFill == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type); + if (shape->fill.gradient == NULL) { + shape->fill.type = NSVG_PAINT_NONE; + } + } + + // Set stroke + if (attr->hasStroke == 0) { + shape->stroke.type = NSVG_PAINT_NONE; + } else if (attr->hasStroke == 1) { + shape->stroke.type = NSVG_PAINT_COLOR; + shape->stroke.color = attr->strokeColor; + shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; + } else if (attr->hasStroke == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type); + if (shape->stroke.gradient == NULL) + shape->stroke.type = NSVG_PAINT_NONE; + } + + // Set flags + shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); + + // Add to tail + if (p->image->shapes == NULL) + p->image->shapes = shape; + else + p->shapesTail->next = shape; + p->shapesTail = shape; + + return; + +error: + if (shape) free(shape); +} + +static void nsvg__addPath(NSVGparser* p, char closed) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGpath* path = NULL; + float bounds[4]; + float* curve; + int i; + + if (p->npts < 4) + return; + + if (closed) + nsvg__lineTo(p, p->pts[0], p->pts[1]); + + // Expect 1 + N*3 points (N = number of cubic bezier segments). + if ((p->npts % 3) != 1) + return; + + path = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (path == NULL) goto error; + memset(path, 0, sizeof(NSVGpath)); + + path->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (path->pts == NULL) goto error; + path->closed = closed; + path->npts = p->npts; + + // Transform path. + for (i = 0; i < p->npts; ++i) + nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); + + // Find bounds + for (i = 0; i < path->npts-1; i += 3) { + curve = &path->pts[i*2]; + nsvg__curveBounds(bounds, curve); + if (i == 0) { + path->bounds[0] = bounds[0]; + path->bounds[1] = bounds[1]; + path->bounds[2] = bounds[2]; + path->bounds[3] = bounds[3]; + } else { + path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); + path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); + path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); + path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); + } + } + + path->next = p->plist; + p->plist = path; + + return; + +error: + if (path != NULL) { + if (path->pts != NULL) free(path->pts); + free(path); + } +} + +// We roll our own string to float because the std library one uses locale and messes things up. +static double nsvg__atof(const char* s) +{ + char* cur = (char*)s; + char* end = NULL; + double res = 0.0, sign = 1.0; + long long intPart = 0, fracPart = 0; + char hasIntPart = 0, hasFracPart = 0; + + // Parse optional sign + if (*cur == '+') { + cur++; + } else if (*cur == '-') { + sign = -1; + cur++; + } + + // Parse integer part + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + intPart = strtoll(cur, &end, 10); + if (cur != end) { + res = (double)intPart; + hasIntPart = 1; + cur = end; + } + } + + // Parse fractional part. + if (*cur == '.') { + cur++; // Skip '.' + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + fracPart = strtoll(cur, &end, 10); + if (cur != end) { + res += (double)fracPart / pow(10.0, (double)(end - cur)); + hasFracPart = 1; + cur = end; + } + } + } + + // A valid number should have integer or fractional part. + if (!hasIntPart && !hasFracPart) + return 0.0; + + // Parse optional exponent + if (*cur == 'e' || *cur == 'E') { + long expPart = 0; + cur++; // skip 'E' + expPart = strtol(cur, &end, 10); // Parse digit sequence with sign + if (cur != end) { + res *= pow(10.0, (double)expPart); + } + } + + return res * sign; +} + + +static const char* nsvg__parseNumber(const char* s, char* it, const int size) +{ + const int last = size-1; + int i = 0; + + // sign + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + // integer part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + if (*s == '.') { + // decimal point + if (i < last) it[i++] = *s; + s++; + // fraction part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + // exponent + if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { + if (i < last) it[i++] = *s; + s++; + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + it[i] = '\0'; + + return s; +} + +static const char* nsvg__getNextPathItem(const char* s, char* it) +{ + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + if (!*s) return s; + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { + s = nsvg__parseNumber(s, it, 64); + } else { + // Parse command + it[0] = *s++; + it[1] = '\0'; + return s; + } + + return s; +} + +static unsigned int nsvg__parseColorHex(const char* str) +{ + unsigned int c = 0, r = 0, g = 0, b = 0; + int n = 0; + str++; // skip # + // Calculate number of characters. + while(str[n] && !nsvg__isspace(str[n])) + n++; + if (n == 6) { + sscanf(str, "%x", &c); + } else if (n == 3) { + sscanf(str, "%x", &c); + c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8); + c |= c<<4; + } + r = (c >> 16) & 0xff; + g = (c >> 8) & 0xff; + b = c & 0xff; + return NSVG_RGB(r,g,b); +} + +static unsigned int nsvg__parseColorRGB(const char* str) +{ + int r = -1, g = -1, b = -1; + char s1[32]="", s2[32]=""; + sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b); + if (strchr(s1, '%')) { + return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100); + } else { + return NSVG_RGB(r,g,b); + } +} + +typedef struct NSVGNamedColor { + const char* name; + unsigned int color; +} NSVGNamedColor; + +NSVGNamedColor nsvg__colors[] = { + + { "red", NSVG_RGB(255, 0, 0) }, + { "green", NSVG_RGB( 0, 128, 0) }, + { "blue", NSVG_RGB( 0, 0, 255) }, + { "yellow", NSVG_RGB(255, 255, 0) }, + { "cyan", NSVG_RGB( 0, 255, 255) }, + { "magenta", NSVG_RGB(255, 0, 255) }, + { "black", NSVG_RGB( 0, 0, 0) }, + { "grey", NSVG_RGB(128, 128, 128) }, + { "gray", NSVG_RGB(128, 128, 128) }, + { "white", NSVG_RGB(255, 255, 255) }, + +#ifdef NANOSVG_ALL_COLOR_KEYWORDS + { "aliceblue", NSVG_RGB(240, 248, 255) }, + { "antiquewhite", NSVG_RGB(250, 235, 215) }, + { "aqua", NSVG_RGB( 0, 255, 255) }, + { "aquamarine", NSVG_RGB(127, 255, 212) }, + { "azure", NSVG_RGB(240, 255, 255) }, + { "beige", NSVG_RGB(245, 245, 220) }, + { "bisque", NSVG_RGB(255, 228, 196) }, + { "blanchedalmond", NSVG_RGB(255, 235, 205) }, + { "blueviolet", NSVG_RGB(138, 43, 226) }, + { "brown", NSVG_RGB(165, 42, 42) }, + { "burlywood", NSVG_RGB(222, 184, 135) }, + { "cadetblue", NSVG_RGB( 95, 158, 160) }, + { "chartreuse", NSVG_RGB(127, 255, 0) }, + { "chocolate", NSVG_RGB(210, 105, 30) }, + { "coral", NSVG_RGB(255, 127, 80) }, + { "cornflowerblue", NSVG_RGB(100, 149, 237) }, + { "cornsilk", NSVG_RGB(255, 248, 220) }, + { "crimson", NSVG_RGB(220, 20, 60) }, + { "darkblue", NSVG_RGB( 0, 0, 139) }, + { "darkcyan", NSVG_RGB( 0, 139, 139) }, + { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, + { "darkgray", NSVG_RGB(169, 169, 169) }, + { "darkgreen", NSVG_RGB( 0, 100, 0) }, + { "darkgrey", NSVG_RGB(169, 169, 169) }, + { "darkkhaki", NSVG_RGB(189, 183, 107) }, + { "darkmagenta", NSVG_RGB(139, 0, 139) }, + { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, + { "darkorange", NSVG_RGB(255, 140, 0) }, + { "darkorchid", NSVG_RGB(153, 50, 204) }, + { "darkred", NSVG_RGB(139, 0, 0) }, + { "darksalmon", NSVG_RGB(233, 150, 122) }, + { "darkseagreen", NSVG_RGB(143, 188, 143) }, + { "darkslateblue", NSVG_RGB( 72, 61, 139) }, + { "darkslategray", NSVG_RGB( 47, 79, 79) }, + { "darkslategrey", NSVG_RGB( 47, 79, 79) }, + { "darkturquoise", NSVG_RGB( 0, 206, 209) }, + { "darkviolet", NSVG_RGB(148, 0, 211) }, + { "deeppink", NSVG_RGB(255, 20, 147) }, + { "deepskyblue", NSVG_RGB( 0, 191, 255) }, + { "dimgray", NSVG_RGB(105, 105, 105) }, + { "dimgrey", NSVG_RGB(105, 105, 105) }, + { "dodgerblue", NSVG_RGB( 30, 144, 255) }, + { "firebrick", NSVG_RGB(178, 34, 34) }, + { "floralwhite", NSVG_RGB(255, 250, 240) }, + { "forestgreen", NSVG_RGB( 34, 139, 34) }, + { "fuchsia", NSVG_RGB(255, 0, 255) }, + { "gainsboro", NSVG_RGB(220, 220, 220) }, + { "ghostwhite", NSVG_RGB(248, 248, 255) }, + { "gold", NSVG_RGB(255, 215, 0) }, + { "goldenrod", NSVG_RGB(218, 165, 32) }, + { "greenyellow", NSVG_RGB(173, 255, 47) }, + { "honeydew", NSVG_RGB(240, 255, 240) }, + { "hotpink", NSVG_RGB(255, 105, 180) }, + { "indianred", NSVG_RGB(205, 92, 92) }, + { "indigo", NSVG_RGB( 75, 0, 130) }, + { "ivory", NSVG_RGB(255, 255, 240) }, + { "khaki", NSVG_RGB(240, 230, 140) }, + { "lavender", NSVG_RGB(230, 230, 250) }, + { "lavenderblush", NSVG_RGB(255, 240, 245) }, + { "lawngreen", NSVG_RGB(124, 252, 0) }, + { "lemonchiffon", NSVG_RGB(255, 250, 205) }, + { "lightblue", NSVG_RGB(173, 216, 230) }, + { "lightcoral", NSVG_RGB(240, 128, 128) }, + { "lightcyan", NSVG_RGB(224, 255, 255) }, + { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, + { "lightgray", NSVG_RGB(211, 211, 211) }, + { "lightgreen", NSVG_RGB(144, 238, 144) }, + { "lightgrey", NSVG_RGB(211, 211, 211) }, + { "lightpink", NSVG_RGB(255, 182, 193) }, + { "lightsalmon", NSVG_RGB(255, 160, 122) }, + { "lightseagreen", NSVG_RGB( 32, 178, 170) }, + { "lightskyblue", NSVG_RGB(135, 206, 250) }, + { "lightslategray", NSVG_RGB(119, 136, 153) }, + { "lightslategrey", NSVG_RGB(119, 136, 153) }, + { "lightsteelblue", NSVG_RGB(176, 196, 222) }, + { "lightyellow", NSVG_RGB(255, 255, 224) }, + { "lime", NSVG_RGB( 0, 255, 0) }, + { "limegreen", NSVG_RGB( 50, 205, 50) }, + { "linen", NSVG_RGB(250, 240, 230) }, + { "maroon", NSVG_RGB(128, 0, 0) }, + { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, + { "mediumblue", NSVG_RGB( 0, 0, 205) }, + { "mediumorchid", NSVG_RGB(186, 85, 211) }, + { "mediumpurple", NSVG_RGB(147, 112, 219) }, + { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, + { "mediumslateblue", NSVG_RGB(123, 104, 238) }, + { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, + { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, + { "mediumvioletred", NSVG_RGB(199, 21, 133) }, + { "midnightblue", NSVG_RGB( 25, 25, 112) }, + { "mintcream", NSVG_RGB(245, 255, 250) }, + { "mistyrose", NSVG_RGB(255, 228, 225) }, + { "moccasin", NSVG_RGB(255, 228, 181) }, + { "navajowhite", NSVG_RGB(255, 222, 173) }, + { "navy", NSVG_RGB( 0, 0, 128) }, + { "oldlace", NSVG_RGB(253, 245, 230) }, + { "olive", NSVG_RGB(128, 128, 0) }, + { "olivedrab", NSVG_RGB(107, 142, 35) }, + { "orange", NSVG_RGB(255, 165, 0) }, + { "orangered", NSVG_RGB(255, 69, 0) }, + { "orchid", NSVG_RGB(218, 112, 214) }, + { "palegoldenrod", NSVG_RGB(238, 232, 170) }, + { "palegreen", NSVG_RGB(152, 251, 152) }, + { "paleturquoise", NSVG_RGB(175, 238, 238) }, + { "palevioletred", NSVG_RGB(219, 112, 147) }, + { "papayawhip", NSVG_RGB(255, 239, 213) }, + { "peachpuff", NSVG_RGB(255, 218, 185) }, + { "peru", NSVG_RGB(205, 133, 63) }, + { "pink", NSVG_RGB(255, 192, 203) }, + { "plum", NSVG_RGB(221, 160, 221) }, + { "powderblue", NSVG_RGB(176, 224, 230) }, + { "purple", NSVG_RGB(128, 0, 128) }, + { "rosybrown", NSVG_RGB(188, 143, 143) }, + { "royalblue", NSVG_RGB( 65, 105, 225) }, + { "saddlebrown", NSVG_RGB(139, 69, 19) }, + { "salmon", NSVG_RGB(250, 128, 114) }, + { "sandybrown", NSVG_RGB(244, 164, 96) }, + { "seagreen", NSVG_RGB( 46, 139, 87) }, + { "seashell", NSVG_RGB(255, 245, 238) }, + { "sienna", NSVG_RGB(160, 82, 45) }, + { "silver", NSVG_RGB(192, 192, 192) }, + { "skyblue", NSVG_RGB(135, 206, 235) }, + { "slateblue", NSVG_RGB(106, 90, 205) }, + { "slategray", NSVG_RGB(112, 128, 144) }, + { "slategrey", NSVG_RGB(112, 128, 144) }, + { "snow", NSVG_RGB(255, 250, 250) }, + { "springgreen", NSVG_RGB( 0, 255, 127) }, + { "steelblue", NSVG_RGB( 70, 130, 180) }, + { "tan", NSVG_RGB(210, 180, 140) }, + { "teal", NSVG_RGB( 0, 128, 128) }, + { "thistle", NSVG_RGB(216, 191, 216) }, + { "tomato", NSVG_RGB(255, 99, 71) }, + { "turquoise", NSVG_RGB( 64, 224, 208) }, + { "violet", NSVG_RGB(238, 130, 238) }, + { "wheat", NSVG_RGB(245, 222, 179) }, + { "whitesmoke", NSVG_RGB(245, 245, 245) }, + { "yellowgreen", NSVG_RGB(154, 205, 50) }, +#endif +}; + +static unsigned int nsvg__parseColorName(const char* str) +{ + int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); + + for (i = 0; i < ncolors; i++) { + if (strcmp(nsvg__colors[i].name, str) == 0) { + return nsvg__colors[i].color; + } + } + + return NSVG_RGB(128, 128, 128); +} + +static unsigned int nsvg__parseColor(const char* str) +{ + size_t len = 0; + while(*str == ' ') ++str; + len = strlen(str); + if (len >= 1 && *str == '#') + return nsvg__parseColorHex(str); + else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') + return nsvg__parseColorRGB(str); + return nsvg__parseColorName(str); +} + +static float nsvg__parseOpacity(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + if (val > 1.0f) val = 1.0f; + return val; +} + +static float nsvg__parseMiterLimit(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + return val; +} + +static int nsvg__parseUnits(const char* units) +{ + if (units[0] == 'p' && units[1] == 'x') + return NSVG_UNITS_PX; + else if (units[0] == 'p' && units[1] == 't') + return NSVG_UNITS_PT; + else if (units[0] == 'p' && units[1] == 'c') + return NSVG_UNITS_PC; + else if (units[0] == 'm' && units[1] == 'm') + return NSVG_UNITS_MM; + else if (units[0] == 'c' && units[1] == 'm') + return NSVG_UNITS_CM; + else if (units[0] == 'i' && units[1] == 'n') + return NSVG_UNITS_IN; + else if (units[0] == '%') + return NSVG_UNITS_PERCENT; + else if (units[0] == 'e' && units[1] == 'm') + return NSVG_UNITS_EM; + else if (units[0] == 'e' && units[1] == 'x') + return NSVG_UNITS_EX; + return NSVG_UNITS_USER; +} + +static int nsvg__isCoordinate(const char* s) +{ + // optional sign + if (*s == '-' || *s == '+') + s++; + // must have at least one digit + return nsvg__isdigit(*s); +} + +static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) +{ + NSVGcoordinate coord = {0, NSVG_UNITS_USER}; + char buf[64]; + coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); + coord.value = nsvg__atof(buf); + return coord; +} + +static NSVGcoordinate nsvg__coord(float v, int units) +{ + NSVGcoordinate coord = {v, units}; + return coord; +} + +static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) +{ + NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); + return nsvg__convertToPixels(p, coord, orig, length); +} + +static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) +{ + const char* end; + const char* ptr; + char it[64]; + + *na = 0; + ptr = str; + while (*ptr && *ptr != '(') ++ptr; + if (*ptr == 0) + return 1; + end = ptr; + while (*end && *end != ')') ++end; + if (*end == 0) + return 1; + + while (ptr < end) { + if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { + if (*na >= maxNa) return 0; + ptr = nsvg__parseNumber(ptr, it, 64); + args[(*na)++] = (float)nsvg__atof(it); + } else { + ++ptr; + } + } + return (int)(end - str); +} + + +static int nsvg__parseMatrix(float* xform, const char* str) +{ + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, t, 6, &na); + if (na != 6) return len; + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseTranslate(float* xform, const char* str) +{ + float args[2]; + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = 0.0; + + nsvg__xformSetTranslation(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseScale(float* xform, const char* str) +{ + float args[2]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = args[0]; + nsvg__xformSetScale(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewX(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewY(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseRotate(float* xform, const char* str) +{ + float args[3]; + int na = 0; + float m[6]; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 3, &na); + if (na == 1) + args[1] = args[2] = 0.0f; + nsvg__xformIdentity(m); + + if (na > 1) { + nsvg__xformSetTranslation(t, -args[1], -args[2]); + nsvg__xformMultiply(m, t); + } + + nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); + nsvg__xformMultiply(m, t); + + if (na > 1) { + nsvg__xformSetTranslation(t, args[1], args[2]); + nsvg__xformMultiply(m, t); + } + + memcpy(xform, m, sizeof(float)*6); + + return len; +} + +static void nsvg__parseTransform(float* xform, const char* str) +{ + float t[6]; + int len; + nsvg__xformIdentity(xform); + while (*str) + { + if (strncmp(str, "matrix", 6) == 0) + len = nsvg__parseMatrix(t, str); + else if (strncmp(str, "translate", 9) == 0) + len = nsvg__parseTranslate(t, str); + else if (strncmp(str, "scale", 5) == 0) + len = nsvg__parseScale(t, str); + else if (strncmp(str, "rotate", 6) == 0) + len = nsvg__parseRotate(t, str); + else if (strncmp(str, "skewX", 5) == 0) + len = nsvg__parseSkewX(t, str); + else if (strncmp(str, "skewY", 5) == 0) + len = nsvg__parseSkewY(t, str); + else{ + ++str; + continue; + } + if (len != 0) { + str += len; + } else { + ++str; + continue; + } + + nsvg__xformPremultiply(xform, t); + } +} + +static void nsvg__parseUrl(char* id, const char* str) +{ + int i = 0; + str += 4; // "url("; + if (*str == '#') + str++; + while (i < 63 && *str != ')') { + id[i] = *str++; + i++; + } + id[i] = '\0'; +} + +static char nsvg__parseLineCap(const char* str) +{ + if (strcmp(str, "butt") == 0) + return NSVG_CAP_BUTT; + else if (strcmp(str, "round") == 0) + return NSVG_CAP_ROUND; + else if (strcmp(str, "square") == 0) + return NSVG_CAP_SQUARE; + // TODO: handle inherit. + return NSVG_CAP_BUTT; +} + +static char nsvg__parseLineJoin(const char* str) +{ + if (strcmp(str, "miter") == 0) + return NSVG_JOIN_MITER; + else if (strcmp(str, "round") == 0) + return NSVG_JOIN_ROUND; + else if (strcmp(str, "bevel") == 0) + return NSVG_JOIN_BEVEL; + // TODO: handle inherit. + return NSVG_JOIN_MITER; +} + +static char nsvg__parseFillRule(const char* str) +{ + if (strcmp(str, "nonzero") == 0) + return NSVG_FILLRULE_NONZERO; + else if (strcmp(str, "evenodd") == 0) + return NSVG_FILLRULE_EVENODD; + // TODO: handle inherit. + return NSVG_FILLRULE_NONZERO; +} + +static const char* nsvg__getNextDashItem(const char* s, char* it) +{ + int n = 0; + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + // Advance until whitespace, comma or end. + while (*s && (!nsvg__isspace(*s) && *s != ',')) { + if (n < 63) + it[n++] = *s; + s++; + } + it[n++] = '\0'; + return s; +} + +static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) +{ + char item[64]; + int count = 0, i; + float sum = 0.0f; + + // Handle "none" + if (str[0] == 'n') + return 0; + + // Parse dashes + while (*str) { + str = nsvg__getNextDashItem(str, item); + if (!*item) break; + if (count < NSVG_MAX_DASHES) + strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); + } + + for (i = 0; i < count; i++) + sum += strokeDashArray[i]; + if (sum <= 1e-6f) + count = 0; + + return count; +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str); + +static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) +{ + float xform[6]; + NSVGattrib* attr = nsvg__getAttr(p); + if (!attr) return 0; + + if (strcmp(name, "style") == 0) { + nsvg__parseStyle(p, value); + } else if (strcmp(name, "display") == 0) { + if (strcmp(value, "none") == 0) + attr->visible = 0; + // Don't reset ->visible on display:inline, one display:none hides the whole subtree + + } else if (strcmp(name, "fill") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasFill = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasFill = 2; + nsvg__parseUrl(attr->fillGradient, value); + } else { + attr->hasFill = 1; + attr->fillColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "opacity") == 0) { + attr->opacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "fill-opacity") == 0) { + attr->fillOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasStroke = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasStroke = 2; + nsvg__parseUrl(attr->strokeGradient, value); + } else { + attr->hasStroke = 1; + attr->strokeColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "stroke-width") == 0) { + attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-dasharray") == 0) { + attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); + } else if (strcmp(name, "stroke-dashoffset") == 0) { + attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-opacity") == 0) { + attr->strokeOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke-linecap") == 0) { + attr->strokeLineCap = nsvg__parseLineCap(value); + } else if (strcmp(name, "stroke-linejoin") == 0) { + attr->strokeLineJoin = nsvg__parseLineJoin(value); + } else if (strcmp(name, "stroke-miterlimit") == 0) { + attr->miterLimit = nsvg__parseMiterLimit(value); + } else if (strcmp(name, "fill-rule") == 0) { + attr->fillRule = nsvg__parseFillRule(value); + } else if (strcmp(name, "font-size") == 0) { + attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "transform") == 0) { + nsvg__parseTransform(xform, value); + nsvg__xformPremultiply(attr->xform, xform); + } else if (strcmp(name, "stop-color") == 0) { + attr->stopColor = nsvg__parseColor(value); + } else if (strcmp(name, "stop-opacity") == 0) { + attr->stopOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "offset") == 0) { + attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); + } else if (strcmp(name, "id") == 0) { + strncpy(attr->id, value, 63); + attr->id[63] = '\0'; + } else { + return 0; + } + return 1; +} + +static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) +{ + const char* str; + const char* val; + char name[512]; + char value[512]; + int n; + + str = start; + while (str < end && *str != ':') ++str; + + val = str; + + // Right Trim + while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; + ++str; + + n = (int)(str - start); + if (n > 511) n = 511; + if (n) memcpy(name, start, n); + name[n] = 0; + + while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; + + n = (int)(end - val); + if (n > 511) n = 511; + if (n) memcpy(value, val, n); + value[n] = 0; + + return nsvg__parseAttr(p, name, value); +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str) +{ + const char* start; + const char* end; + + while (*str) { + // Left Trim + while(*str && nsvg__isspace(*str)) ++str; + start = str; + while(*str && *str != ';') ++str; + end = str; + + // Right Trim + while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; + ++end; + + nsvg__parseNameValue(p, start, end); + if (*str) ++str; + } +} + +static void nsvg__parseAttribs(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) + { + if (strcmp(attr[i], "style") == 0) + nsvg__parseStyle(p, attr[i + 1]); + else + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } +} + +static int nsvg__getArgsPerElement(char cmd) +{ + switch (cmd) { + case 'v': + case 'V': + case 'h': + case 'H': + return 1; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + return 2; + case 'q': + case 'Q': + case 's': + case 'S': + return 4; + case 'c': + case 'C': + return 6; + case 'a': + case 'A': + return 7; + case 'z': + case 'Z': + return 0; + } + return -1; +} + +static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__moveTo(p, *cpx, *cpy); +} + +static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpx += args[0]; + else + *cpx = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpy += args[0]; + else + *cpy = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x2, y2, cx1, cy1, cx2, cy2; + + if (rel) { + cx1 = *cpx + args[0]; + cy1 = *cpy + args[1]; + cx2 = *cpx + args[2]; + cy2 = *cpy + args[3]; + x2 = *cpx + args[4]; + y2 = *cpy + args[5]; + } else { + cx1 = args[0]; + cy1 = args[1]; + cx2 = args[2]; + cy2 = args[3]; + x2 = args[4]; + y2 = args[5]; + } + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx2 = *cpx + args[0]; + cy2 = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx2 = args[0]; + cy2 = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + cx1 = 2*x1 - *cpx2; + cy1 = 2*y1 - *cpy2; + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx = *cpx + args[0]; + cy = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx = args[0]; + cy = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + // Convert to cubic bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + x2 = *cpx + args[0]; + y2 = *cpy + args[1]; + } else { + x2 = args[0]; + y2 = args[1]; + } + + cx = 2*x1 - *cpx2; + cy = 2*y1 - *cpy2; + + // Convert to cubix bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static float nsvg__sqr(float x) { return x*x; } +static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } + +static float nsvg__vecrat(float ux, float uy, float vx, float vy) +{ + return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); +} + +static float nsvg__vecang(float ux, float uy, float vx, float vy) +{ + float r = nsvg__vecrat(ux,uy, vx,vy); + if (r < -1.0f) r = -1.0f; + if (r > 1.0f) r = 1.0f; + return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); +} + +static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + // Ported from canvg (https://code.google.com/p/canvg/) + float rx, ry, rotx; + float x1, y1, x2, y2, cx, cy, dx, dy, d; + float x1p, y1p, cxp, cyp, s, sa, sb; + float ux, uy, vx, vy, a1, da; + float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; + float sinrx, cosrx; + int fa, fs; + int i, ndivs; + float hda, kappa; + + rx = fabsf(args[0]); // y radius + ry = fabsf(args[1]); // x radius + rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle + fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc + fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction + x1 = *cpx; // start point + y1 = *cpy; + if (rel) { // end point + x2 = *cpx + args[5]; + y2 = *cpy + args[6]; + } else { + x2 = args[5]; + y2 = args[6]; + } + + dx = x1 - x2; + dy = y1 - y2; + d = sqrtf(dx*dx + dy*dy); + if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { + // The arc degenerates to a line + nsvg__lineTo(p, x2, y2); + *cpx = x2; + *cpy = y2; + return; + } + + sinrx = sinf(rotx); + cosrx = cosf(rotx); + + // Convert to center point parameterization. + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + // 1) Compute x1', y1' + x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; + y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; + d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); + if (d > 1) { + d = sqrtf(d); + rx *= d; + ry *= d; + } + // 2) Compute cx', cy' + s = 0.0f; + sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); + sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); + if (sa < 0.0f) sa = 0.0f; + if (sb > 0.0f) + s = sqrtf(sa / sb); + if (fa == fs) + s = -s; + cxp = s * rx * y1p / ry; + cyp = s * -ry * x1p / rx; + + // 3) Compute cx,cy from cx',cy' + cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; + cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; + + // 4) Calculate theta1, and delta theta. + ux = (x1p - cxp) / rx; + uy = (y1p - cyp) / ry; + vx = (-x1p - cxp) / rx; + vy = (-y1p - cyp) / ry; + a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle + da = nsvg__vecang(ux,uy, vx,vy); // Delta angle + +// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; +// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; + + if (fs == 0 && da > 0) + da -= 2 * NSVG_PI; + else if (fs == 1 && da < 0) + da += 2 * NSVG_PI; + + // Approximate the arc using cubic spline segments. + t[0] = cosrx; t[1] = sinrx; + t[2] = -sinrx; t[3] = cosrx; + t[4] = cx; t[5] = cy; + + // Split arc into max 90 degree segments. + // The loop assumes an iteration per end point (including start and end), this +1. + ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); + hda = (da / (float)ndivs) / 2.0f; + kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda)); + if (da < 0.0f) + kappa = -kappa; + + for (i = 0; i <= ndivs; i++) { + a = a1 + da * ((float)i/(float)ndivs); + dx = cosf(a); + dy = sinf(a); + nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position + nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent + if (i > 0) + nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); + px = x; + py = y; + ptanx = tanx; + ptany = tany; + } + + *cpx = x2; + *cpy = y2; +} + +static void nsvg__parsePath(NSVGparser* p, const char** attr) +{ + const char* s = NULL; + char cmd = '\0'; + float args[10]; + int nargs; + int rargs = 0; + char initPoint; + float cpx, cpy, cpx2, cpy2; + const char* tmp[4]; + char closedFlag; + int i; + char item[64]; + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "d") == 0) { + s = attr[i + 1]; + } else { + tmp[0] = attr[i]; + tmp[1] = attr[i + 1]; + tmp[2] = 0; + tmp[3] = 0; + nsvg__parseAttribs(p, tmp); + } + } + + if (s) { + nsvg__resetPath(p); + cpx = 0; cpy = 0; + cpx2 = 0; cpy2 = 0; + initPoint = 0; + closedFlag = 0; + nargs = 0; + + while (*s) { + s = nsvg__getNextPathItem(s, item); + if (!*item) break; + if (cmd != '\0' && nsvg__isCoordinate(item)) { + if (nargs < 10) + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= rargs) { + switch (cmd) { + case 'm': + case 'M': + nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); + // Moveto can be followed by multiple coordinate pairs, + // which should be treated as linetos. + cmd = (cmd == 'm') ? 'l' : 'L'; + rargs = nsvg__getArgsPerElement(cmd); + cpx2 = cpx; cpy2 = cpy; + initPoint = 1; + break; + case 'l': + case 'L': + nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'H': + case 'h': + nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'V': + case 'v': + nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'C': + case 'c': + nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); + break; + case 'S': + case 's': + nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); + break; + case 'Q': + case 'q': + nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); + break; + case 'T': + case 't': + nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); + break; + case 'A': + case 'a': + nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + default: + if (nargs >= 2) { + cpx = args[nargs-2]; + cpy = args[nargs-1]; + cpx2 = cpx; cpy2 = cpy; + } + break; + } + nargs = 0; + } + } else { + cmd = item[0]; + if (cmd == 'M' || cmd == 'm') { + // Commit path. + if (p->npts > 0) + nsvg__addPath(p, closedFlag); + // Start new subpath. + nsvg__resetPath(p); + closedFlag = 0; + nargs = 0; + } else if (initPoint == 0) { + // Do not allow other commands until initial point has been set (moveTo called once). + cmd = '\0'; + } + if (cmd == 'Z' || cmd == 'z') { + closedFlag = 1; + // Commit path. + if (p->npts > 0) { + // Move current point to first point + cpx = p->pts[0]; + cpy = p->pts[1]; + cpx2 = cpx; cpy2 = cpy; + nsvg__addPath(p, closedFlag); + } + // Start new subpath. + nsvg__resetPath(p); + nsvg__moveTo(p, cpx, cpy); + closedFlag = 0; + nargs = 0; + } + rargs = nsvg__getArgsPerElement(cmd); + if (rargs == -1) { + // Command not recognized + cmd = '\0'; + rargs = 0; + } + } + } + // Commit path. + if (p->npts) + nsvg__addPath(p, closedFlag); + } + + nsvg__addShape(p); +} + +static void nsvg__parseRect(NSVGparser* p, const char** attr) +{ + float x = 0.0f; + float y = 0.0f; + float w = 0.0f; + float h = 0.0f; + float rx = -1.0f; // marks not set + float ry = -1.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); + if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx < 0.0f && ry > 0.0f) rx = ry; + if (ry < 0.0f && rx > 0.0f) ry = rx; + if (rx < 0.0f) rx = 0.0f; + if (ry < 0.0f) ry = 0.0f; + if (rx > w/2.0f) rx = w/2.0f; + if (ry > h/2.0f) ry = h/2.0f; + + if (w != 0.0f && h != 0.0f) { + nsvg__resetPath(p); + + if (rx < 0.00001f || ry < 0.0001f) { + nsvg__moveTo(p, x, y); + nsvg__lineTo(p, x+w, y); + nsvg__lineTo(p, x+w, y+h); + nsvg__lineTo(p, x, y+h); + } else { + // Rounded rectangle + nsvg__moveTo(p, x+rx, y); + nsvg__lineTo(p, x+w-rx, y); + nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); + nsvg__lineTo(p, x+w, y+h-ry); + nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); + nsvg__lineTo(p, x+rx, y+h); + nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); + nsvg__lineTo(p, x, y+ry); + nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); + } + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseCircle(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float r = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); + } + } + + if (r > 0.0f) { + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+r, cy); + nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); + nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); + nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); + nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseEllipse(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float rx = 0.0f; + float ry = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx > 0.0f && ry > 0.0f) { + + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+rx, cy); + nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); + nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); + nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); + nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseLine(NSVGparser* p, const char** attr) +{ + float x1 = 0.0; + float y1 = 0.0; + float x2 = 0.0; + float y2 = 0.0; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + } + } + + nsvg__resetPath(p); + + nsvg__moveTo(p, x1, y1); + nsvg__lineTo(p, x2, y2); + + nsvg__addPath(p, 0); + + nsvg__addShape(p); +} + +static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) +{ + int i; + const char* s; + float args[2]; + int nargs, npts = 0; + char item[64]; + + nsvg__resetPath(p); + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "points") == 0) { + s = attr[i + 1]; + nargs = 0; + while (*s) { + s = nsvg__getNextPathItem(s, item); + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= 2) { + if (npts == 0) + nsvg__moveTo(p, args[0], args[1]); + else + nsvg__lineTo(p, args[0], args[1]); + nargs = 0; + npts++; + } + } + } + } + } + + nsvg__addPath(p, (char)closeFlag); + + nsvg__addShape(p); +} + +static void nsvg__parseSVG(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "width") == 0) { + p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "height") == 0) { + p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "viewBox") == 0) { + const char *s = attr[i + 1]; + char buf[64]; + s = nsvg__parseNumber(s, buf, 64); + p->viewMinx = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewMiny = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewWidth = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewHeight = nsvg__atof(buf); + } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { + if (strstr(attr[i + 1], "none") != 0) { + // No uniform scaling + p->alignType = NSVG_ALIGN_NONE; + } else { + // Parse X align + if (strstr(attr[i + 1], "xMin") != 0) + p->alignX = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "xMid") != 0) + p->alignX = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "xMax") != 0) + p->alignX = NSVG_ALIGN_MAX; + // Parse X align + if (strstr(attr[i + 1], "yMin") != 0) + p->alignY = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "yMid") != 0) + p->alignY = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "yMax") != 0) + p->alignY = NSVG_ALIGN_MAX; + // Parse meet/slice + p->alignType = NSVG_ALIGN_MEET; + if (strstr(attr[i + 1], "slice") != 0) + p->alignType = NSVG_ALIGN_SLICE; + } + } + } + } +} + +static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) +{ + int i; + NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); + if (grad == NULL) return; + memset(grad, 0, sizeof(NSVGgradientData)); + grad->units = NSVG_OBJECT_SPACE; + grad->type = type; + if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { + grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); + grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { + grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + } + + nsvg__xformIdentity(grad->xform); + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "id") == 0) { + strncpy(grad->id, attr[i+1], 63); + grad->id[63] = '\0'; + } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "gradientUnits") == 0) { + if (strcmp(attr[i+1], "objectBoundingBox") == 0) + grad->units = NSVG_OBJECT_SPACE; + else + grad->units = NSVG_USER_SPACE; + } else if (strcmp(attr[i], "gradientTransform") == 0) { + nsvg__parseTransform(grad->xform, attr[i + 1]); + } else if (strcmp(attr[i], "cx") == 0) { + grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "cy") == 0) { + grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "r") == 0) { + grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fx") == 0) { + grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fy") == 0) { + grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x1") == 0) { + grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y1") == 0) { + grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x2") == 0) { + grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y2") == 0) { + grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "spreadMethod") == 0) { + if (strcmp(attr[i+1], "pad") == 0) + grad->spread = NSVG_SPREAD_PAD; + else if (strcmp(attr[i+1], "reflect") == 0) + grad->spread = NSVG_SPREAD_REFLECT; + else if (strcmp(attr[i+1], "repeat") == 0) + grad->spread = NSVG_SPREAD_REPEAT; + } else if (strcmp(attr[i], "xlink:href") == 0) { + const char *href = attr[i+1]; + strncpy(grad->ref, href+1, 62); + grad->ref[62] = '\0'; + } + } + } + + grad->next = p->gradients; + p->gradients = grad; +} + +static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) +{ + NSVGattrib* curAttr = nsvg__getAttr(p); + NSVGgradientData* grad; + NSVGgradientStop* stop; + int i, idx; + + curAttr->stopOffset = 0; + curAttr->stopColor = 0; + curAttr->stopOpacity = 1.0f; + + for (i = 0; attr[i]; i += 2) { + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } + + // Add stop to the last gradient. + grad = p->gradients; + if (grad == NULL) return; + + grad->nstops++; + grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); + if (grad->stops == NULL) return; + + // Insert + idx = grad->nstops-1; + for (i = 0; i < grad->nstops-1; i++) { + if (curAttr->stopOffset < grad->stops[i].offset) { + idx = i; + break; + } + } + if (idx != grad->nstops-1) { + for (i = grad->nstops-1; i > idx; i--) + grad->stops[i] = grad->stops[i-1]; + } + + stop = &grad->stops[idx]; + stop->color = curAttr->stopColor; + stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; + stop->offset = curAttr->stopOffset; +} + +static void nsvg__startElement(void* ud, const char* el, const char** attr) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (p->defsFlag) { + // Skip everything but gradients in defs + if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } + return; + } + + if (strcmp(el, "g") == 0) { + nsvg__pushAttr(p); + nsvg__parseAttribs(p, attr); + } else if (strcmp(el, "path") == 0) { + if (p->pathFlag) // Do not allow nested paths. + return; + nsvg__pushAttr(p); + p->shapeFlag = 1; + nsvg__parsePath(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "rect") == 0) { + nsvg__pushAttr(p); + p->shapeFlag = 1; + nsvg__parseRect(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "circle") == 0) { + nsvg__pushAttr(p); + p->shapeFlag = 1; + nsvg__parseCircle(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "ellipse") == 0) { + nsvg__pushAttr(p); + p->shapeFlag = 1; + nsvg__parseEllipse(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "line") == 0) { + nsvg__pushAttr(p); + p->shapeFlag = 1; + nsvg__parseLine(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "polyline") == 0) { + nsvg__pushAttr(p); + p->shapeFlag = 1; + nsvg__parsePoly(p, attr, 0); + nsvg__popAttr(p); + } else if (strcmp(el, "polygon") == 0) { + nsvg__pushAttr(p); + p->shapeFlag = 1; + nsvg__parsePoly(p, attr, 1); + nsvg__popAttr(p); + } else if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 1; + } else if (strcmp(el, "svg") == 0) { + nsvg__parseSVG(p, attr); + } else if (strcmp(el, "title") == 0) { + p->titleFlag = 1; + } +} + +static void nsvg__endElement(void* ud, const char* el) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (strcmp(el, "g") == 0) { + nsvg__popAttr(p); + } else if (strcmp(el, "path") == 0) { + p->pathFlag = 0; + p->shapeFlag = 0; + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 0; + } else if (strcmp(el, "title") == 0) { + p->titleFlag = 0; + } else if (strcmp(el, "rect") == 0 || + strcmp(el, "circle") == 0 || + strcmp(el, "ellipse") == 0 || + strcmp(el, "line") == 0 || + strcmp(el, "polyline") == 0 || + strcmp(el, "polygon") == 0) { + p->shapeFlag = 0; + } +} + +static void nsvg__content(void* ud, const char* s) +{ + NSVGparser* p = (NSVGparser*)ud; + if (p->titleFlag) { + int len = strlen(s); + const int lim = sizeof(((NSVGshape *)(nullptr))->title); + if(len > lim-1) + len = lim-1; + if (p->shapeFlag) { + NSVGshape *shape = p->image->shapes; + while (shape->next) + shape = shape->next; + if (shape) { + memcpy(shape->title, s, len); + memset(shape->title + len, 0, lim-len); + } + } else { + NSVGattrib* attr = nsvg__getAttr(p); + memcpy(attr->title, s, len); + memset(attr->title + len, 0, lim-len); + } + } +} + +static void nsvg__imageBounds(NSVGparser* p, float* bounds) +{ + NSVGshape* shape; + shape = p->image->shapes; + if (shape == NULL) { + bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; + return; + } + bounds[0] = shape->bounds[0]; + bounds[1] = shape->bounds[1]; + bounds[2] = shape->bounds[2]; + bounds[3] = shape->bounds[3]; + for (shape = shape->next; shape != NULL; shape = shape->next) { + bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); + bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); + bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); + bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); + } +} + +static float nsvg__viewAlign(float content, float container, int type) +{ + if (type == NSVG_ALIGN_MIN) + return 0; + else if (type == NSVG_ALIGN_MAX) + return container - content; + // mid + return (container - content) * 0.5f; +} + +static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) +{ + float t[6]; + nsvg__xformSetTranslation(t, tx, ty); + nsvg__xformMultiply (grad->xform, t); + + nsvg__xformSetScale(t, sx, sy); + nsvg__xformMultiply (grad->xform, t); +} + +static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) +{ + NSVGshape* shape; + NSVGpath* path; + float tx, ty, sx, sy, us, bounds[4], t[6], avgs; + int i; + float* pt; + + // Guess image size if not set completely. + nsvg__imageBounds(p, bounds); + + if (p->viewWidth == 0) { + if (p->image->width > 0) { + p->viewWidth = p->image->width; + } else { + p->viewMinx = bounds[0]; + p->viewWidth = bounds[2] - bounds[0]; + } + } + if (p->viewHeight == 0) { + if (p->image->height > 0) { + p->viewHeight = p->image->height; + } else { + p->viewMiny = bounds[1]; + p->viewHeight = bounds[3] - bounds[1]; + } + } + if (p->image->width == 0) + p->image->width = p->viewWidth; + if (p->image->height == 0) + p->image->height = p->viewHeight; + + tx = -p->viewMinx; + ty = -p->viewMiny; + sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; + sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; + // Unit scaling + us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); + + // Fix aspect ratio + if (p->alignType == NSVG_ALIGN_MEET) { + // fit whole image into viewbox + sx = sy = nsvg__minf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } else if (p->alignType == NSVG_ALIGN_SLICE) { + // fill whole viewbox with image + sx = sy = nsvg__maxf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } + + // Transform + sx *= us; + sy *= us; + avgs = (sx+sy) / 2.0f; + for (shape = p->image->shapes; shape != NULL; shape = shape->next) { + shape->bounds[0] = (shape->bounds[0] + tx) * sx; + shape->bounds[1] = (shape->bounds[1] + ty) * sy; + shape->bounds[2] = (shape->bounds[2] + tx) * sx; + shape->bounds[3] = (shape->bounds[3] + ty) * sy; + for (path = shape->paths; path != NULL; path = path->next) { + path->bounds[0] = (path->bounds[0] + tx) * sx; + path->bounds[1] = (path->bounds[1] + ty) * sy; + path->bounds[2] = (path->bounds[2] + tx) * sx; + path->bounds[3] = (path->bounds[3] + ty) * sy; + for (i =0; i < path->npts; i++) { + pt = &path->pts[i*2]; + pt[0] = (pt[0] + tx) * sx; + pt[1] = (pt[1] + ty) * sy; + } + } + + if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); + memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->fill.gradient->xform, t); + } + if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); + memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->stroke.gradient->xform, t); + } + + shape->strokeWidth *= avgs; + shape->strokeDashOffset *= avgs; + for (i = 0; i < shape->strokeDashCount; i++) + shape->strokeDashArray[i] *= avgs; + } +} + +NSVGimage* nsvgParse(char* input, const char* units, float dpi) +{ + NSVGparser* p; + NSVGimage* ret = 0; + + p = nsvg__createParser(); + if (p == NULL) { + return NULL; + } + p->dpi = dpi; + + nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); + + // Scale to viewBox + nsvg__scaleToViewbox(p, units); + + ret = p->image; + p->image = NULL; + + nsvg__deleteParser(p); + + return ret; +} + +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + NSVGimage* image = NULL; + + fp = fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size+1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + image = nsvgParse(data, units, dpi); + free(data); + + return image; + +error: + if (fp) fclose(fp); + if (data) free(data); + if (image) nsvgDelete(image); + return NULL; +} + +NSVGpath* nsvgDuplicatePath(NSVGpath* p) +{ + NSVGpath* res = NULL; + + if (p == NULL) + return NULL; + + res = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (res == NULL) goto error; + memset(res, 0, sizeof(NSVGpath)); + + res->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (res->pts == NULL) goto error; + memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); + res->npts = p->npts; + + memcpy(res->bounds, p->bounds, sizeof(p->bounds)); + + res->closed = p->closed; + + return res; + +error: + if (res != NULL) { + free(res->pts); + free(res); + } + return NULL; +} + +void nsvgDelete(NSVGimage* image) +{ + NSVGshape *snext, *shape; + if (image == NULL) return; + shape = image->shapes; + while (shape != NULL) { + snext = shape->next; + nsvg__deletePaths(shape->paths); + nsvg__deletePaint(&shape->fill); + nsvg__deletePaint(&shape->stroke); + free(shape); + shape = snext; + } + free(image); +} + +#endif diff --git a/3rdparty/nanosvg/src/nanosvgrast.h b/3rdparty/nanosvg/src/nanosvgrast.h new file mode 100644 index 0000000..b740c31 --- /dev/null +++ b/3rdparty/nanosvg/src/nanosvgrast.h @@ -0,0 +1,1452 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The polygon rasterization is heavily based on stb_truetype rasterizer + * by Sean Barrett - http://nothings.org/ + * + */ + +#ifndef NANOSVGRAST_H +#define NANOSVGRAST_H + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +typedef struct NSVGrasterizer NSVGrasterizer; + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + + // Create rasterizer (can be used to render multiple images). + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + // Allocate memory for image + unsigned char* img = malloc(w*h*4); + // Rasterize + nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); +*/ + +// Allocated rasterizer context. +NSVGrasterizer* nsvgCreateRasterizer(); + +// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) +// r - pointer to rasterizer context +// image - pointer to image to rasterize +// tx,ty - image offset (applied after scaling) +// scale - image scale +// dst - pointer to destination image data, 4 bytes per pixel (RGBA) +// w - width of the image to render +// h - height of the image to render +// stride - number of bytes per scaleline in the destination buffer +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride); + +// Deletes rasterizer context. +void nsvgDeleteRasterizer(NSVGrasterizer*); + + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#endif // NANOSVGRAST_H + +#ifdef NANOSVGRAST_IMPLEMENTATION + +#include + +#define NSVG__SUBSAMPLES 5 +#define NSVG__FIXSHIFT 10 +#define NSVG__FIX (1 << NSVG__FIXSHIFT) +#define NSVG__FIXMASK (NSVG__FIX-1) +#define NSVG__MEMPAGE_SIZE 1024 + +typedef struct NSVGedge { + float x0,y0, x1,y1; + int dir; + struct NSVGedge* next; +} NSVGedge; + +typedef struct NSVGpoint { + float x, y; + float dx, dy; + float len; + float dmx, dmy; + unsigned char flags; +} NSVGpoint; + +typedef struct NSVGactiveEdge { + int x,dx; + float ey; + int dir; + struct NSVGactiveEdge *next; +} NSVGactiveEdge; + +typedef struct NSVGmemPage { + unsigned char mem[NSVG__MEMPAGE_SIZE]; + int size; + struct NSVGmemPage* next; +} NSVGmemPage; + +typedef struct NSVGcachedPaint { + char type; + char spread; + float xform[6]; + unsigned int colors[256]; +} NSVGcachedPaint; + +struct NSVGrasterizer +{ + float px, py; + + float tessTol; + float distTol; + + NSVGedge* edges; + int nedges; + int cedges; + + NSVGpoint* points; + int npoints; + int cpoints; + + NSVGpoint* points2; + int npoints2; + int cpoints2; + + NSVGactiveEdge* freelist; + NSVGmemPage* pages; + NSVGmemPage* curpage; + + unsigned char* scanline; + int cscanline; + + unsigned char* bitmap; + int width, height, stride; +}; + +NSVGrasterizer* nsvgCreateRasterizer() +{ + NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); + if (r == NULL) goto error; + memset(r, 0, sizeof(NSVGrasterizer)); + + r->tessTol = 0.25f; + r->distTol = 0.01f; + + return r; + +error: + nsvgDeleteRasterizer(r); + return NULL; +} + +void nsvgDeleteRasterizer(NSVGrasterizer* r) +{ + NSVGmemPage* p; + + if (r == NULL) return; + + p = r->pages; + while (p != NULL) { + NSVGmemPage* next = p->next; + free(p); + p = next; + } + + if (r->edges) free(r->edges); + if (r->points) free(r->points); + if (r->points2) free(r->points2); + if (r->scanline) free(r->scanline); + + free(r); +} + +static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) +{ + NSVGmemPage *newp; + + // If using existing chain, return the next page in chain + if (cur != NULL && cur->next != NULL) { + return cur->next; + } + + // Alloc new page + newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); + if (newp == NULL) return NULL; + memset(newp, 0, sizeof(NSVGmemPage)); + + // Add to linked list + if (cur != NULL) + cur->next = newp; + else + r->pages = newp; + + return newp; +} + +static void nsvg__resetPool(NSVGrasterizer* r) +{ + NSVGmemPage* p = r->pages; + while (p != NULL) { + p->size = 0; + p = p->next; + } + r->curpage = r->pages; +} + +static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) +{ + unsigned char* buf; + if (size > NSVG__MEMPAGE_SIZE) return NULL; + if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { + r->curpage = nsvg__nextPage(r, r->curpage); + } + buf = &r->curpage->mem[r->curpage->size]; + r->curpage->size += size; + return buf; +} + +static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) +{ + float dx = x2 - x1; + float dy = y2 - y1; + return dx*dx + dy*dy < tol*tol; +} + +static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) +{ + NSVGpoint* pt; + + if (r->npoints > 0) { + pt = &r->points[r->npoints-1]; + if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { + pt->flags = (unsigned char)(pt->flags | flags); + return; + } + } + + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + + pt = &r->points[r->npoints]; + pt->x = x; + pt->y = y; + pt->flags = (unsigned char)flags; + r->npoints++; +} + +static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) +{ + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + r->points[r->npoints] = pt; + r->npoints++; +} + +static void nsvg__duplicatePoints(NSVGrasterizer* r) +{ + if (r->npoints > r->cpoints2) { + r->cpoints2 = r->npoints; + r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); + if (r->points2 == NULL) return; + } + + memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); + r->npoints2 = r->npoints; +} + +static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) +{ + NSVGedge* e; + + // Skip horizontal edges + if (y0 == y1) + return; + + if (r->nedges+1 > r->cedges) { + r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; + r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); + if (r->edges == NULL) return; + } + + e = &r->edges[r->nedges]; + r->nedges++; + + if (y0 < y1) { + e->x0 = x0; + e->y0 = y0; + e->x1 = x1; + e->y1 = y1; + e->dir = 1; + } else { + e->x0 = x1; + e->y0 = y1; + e->x1 = x0; + e->y1 = y0; + e->dir = -1; + } +} + +static float nsvg__normalize(float *x, float* y) +{ + float d = sqrtf((*x)*(*x) + (*y)*(*y)); + if (d > 1e-6f) { + float id = 1.0f / d; + *x *= id; + *y *= id; + } + return d; +} + +static float nsvg__absf(float x) { return x < 0 ? -x : x; } + +static void nsvg__flattenCubicBez(NSVGrasterizer* r, + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + int level, int type) +{ + float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; + float dx,dy,d2,d3; + + if (level > 10) return; + + x12 = (x1+x2)*0.5f; + y12 = (y1+y2)*0.5f; + x23 = (x2+x3)*0.5f; + y23 = (y2+y3)*0.5f; + x34 = (x3+x4)*0.5f; + y34 = (y3+y4)*0.5f; + x123 = (x12+x23)*0.5f; + y123 = (y12+y23)*0.5f; + + dx = x4 - x1; + dy = y4 - y1; + d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); + d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); + + if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { + nsvg__addPathPoint(r, x4, y4, type); + return; + } + + x234 = (x23+x34)*0.5f; + y234 = (y23+y34)*0.5f; + x1234 = (x123+x234)*0.5f; + y1234 = (y123+y234)*0.5f; + + nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); + nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); +} + +static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j; + NSVGpath* path; + + for (path = shape->paths; path != NULL; path = path->next) { + r->npoints = 0; + // Flatten path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); + } + // Close path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + // Build edges + for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) + nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); + } +} + +enum NSVGpointFlags +{ + NSVG_PT_CORNER = 0x01, + NSVG_PT_BEVEL = 0x02, + NSVG_PT_LEFT = 0x04 +}; + +static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + float len = nsvg__normalize(&dx, &dy); + float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x - dx*w, py = p->y - dy*w; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +#ifndef NSVG_PI +#define NSVG_PI (3.14159265358979323846264338327f) +#endif + +static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) +{ + int i; + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; + + for (i = 0; i < ncap; i++) { + float a = (float)i/(float)(ncap-1)*NSVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + float x = px - dlx*ax - dx*ay; + float y = py - dly*ax - dy*ay; + + if (i > 0) + nsvg__addEdge(r, prevx, prevy, x, y); + + prevx = x; + prevy = y; + + if (i == 0) { + lx = x; ly = y; + } else if (i == ncap-1) { + rx = x; ry = y; + } + } + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); + float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); + float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); + float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); + + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0, rx0, lx1, rx1; + float ly0, ry0, ly1, ry1; + + if (p1->flags & NSVG_PT_LEFT) { + lx0 = lx1 = p1->x - p1->dmx * w; + ly0 = ly1 = p1->y - p1->dmy * w; + nsvg__addEdge(r, lx1, ly1, left->x, left->y); + + rx0 = p1->x + (dlx0 * w); + ry0 = p1->y + (dly0 * w); + rx1 = p1->x + (dlx1 * w); + ry1 = p1->y + (dly1 * w); + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + } else { + lx0 = p1->x - (dlx0 * w); + ly0 = p1->y - (dly0 * w); + lx1 = p1->x - (dlx1 * w); + ly1 = p1->y - (dly1 * w); + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + rx0 = rx1 = p1->x + p1->dmx * w; + ry0 = ry1 = p1->y + p1->dmy * w; + nsvg__addEdge(r, right->x, right->y, rx1, ry1); + } + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) +{ + int i, n; + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float a0 = atan2f(dly0, dlx0); + float a1 = atan2f(dly1, dlx1); + float da = a1 - a0; + float lx, ly, rx, ry; + + if (da < NSVG_PI) da += NSVG_PI*2; + if (da > NSVG_PI) da -= NSVG_PI*2; + + n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); + if (n < 2) n = 2; + if (n > ncap) n = ncap; + + lx = left->x; + ly = left->y; + rx = right->x; + ry = right->y; + + for (i = 0; i < n; i++) { + float u = (float)i/(float)(n-1); + float a = a0 + u*da; + float ax = cosf(a) * w, ay = sinf(a) * w; + float lx1 = p1->x - ax, ly1 = p1->y - ay; + float rx1 = p1->x + ax, ry1 = p1->y + ay; + + nsvg__addEdge(r, lx1, ly1, lx, ly); + nsvg__addEdge(r, rx, ry, rx1, ry1); + + lx = lx1; ly = ly1; + rx = rx1; ry = ry1; + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); + float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); + + nsvg__addEdge(r, lx, ly, left->x, left->y); + nsvg__addEdge(r, right->x, right->y, rx, ry); + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static int nsvg__curveDivs(float r, float arc, float tol) +{ + float da = acosf(r / (r + tol)) * 2.0f; + int divs = (int)ceilf(arc / da); + if (divs < 2) divs = 2; + return divs; +} + +static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) +{ + int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. + NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; + NSVGpoint* p0, *p1; + int j, s, e; + + // Build stroke edges + if (closed) { + // Looping + p0 = &points[npoints-1]; + p1 = &points[0]; + s = 0; + e = npoints; + } else { + // Add cap + p0 = &points[0]; + p1 = &points[1]; + s = 1; + e = npoints-1; + } + + if (closed) { + nsvg__initClosed(&left, &right, p0, p1, lineWidth); + firstLeft = left; + firstRight = right; + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); + } + + for (j = s; j < e; ++j) { + if (p1->flags & NSVG_PT_CORNER) { + if (lineJoin == NSVG_JOIN_ROUND) + nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); + else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) + nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); + else + nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); + } else { + nsvg__straightJoin(r, &left, &right, p1, lineWidth); + } + p0 = p1++; + } + + if (closed) { + // Loop it + nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); + nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); + } +} + +static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) +{ + int i, j; + NSVGpoint* p0, *p1; + + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (i = 0; i < r->npoints; i++) { + // Calculate segment direction and length + p0->dx = p1->x - p0->x; + p0->dy = p1->y - p0->y; + p0->len = nsvg__normalize(&p0->dx, &p0->dy); + // Advance + p0 = p1++; + } + + // calculate joins + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (j = 0; j < r->npoints; j++) { + float dlx0, dly0, dlx1, dly1, dmr2, cross; + dlx0 = p0->dy; + dly0 = -p0->dx; + dlx1 = p1->dy; + dly1 = -p1->dx; + // Calculate extrusions + p1->dmx = (dlx0 + dlx1) * 0.5f; + p1->dmy = (dly0 + dly1) * 0.5f; + dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; + if (dmr2 > 0.000001f) { + float s2 = 1.0f / dmr2; + if (s2 > 600.0f) { + s2 = 600.0f; + } + p1->dmx *= s2; + p1->dmy *= s2; + } + + // Clear flags, but keep the corner. + p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; + + // Keep track of left turns. + cross = p1->dx * p0->dy - p0->dx * p1->dy; + if (cross > 0.0f) + p1->flags |= NSVG_PT_LEFT; + + // Check to see if the corner needs to be beveled. + if (p1->flags & NSVG_PT_CORNER) { + if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { + p1->flags |= NSVG_PT_BEVEL; + } + } + + p0 = p1++; + } +} + +static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j, closed; + NSVGpath* path; + NSVGpoint* p0, *p1; + float miterLimit = shape->miterLimit; + int lineJoin = shape->strokeLineJoin; + int lineCap = shape->strokeLineCap; + float lineWidth = shape->strokeWidth * scale; + + for (path = shape->paths; path != NULL; path = path->next) { + // Flatten path + r->npoints = 0; + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER); + } + if (r->npoints < 2) + continue; + + closed = path->closed; + + // If the first and last points are the same, remove the last, mark as closed path. + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { + r->npoints--; + p0 = &r->points[r->npoints-1]; + closed = 1; + } + + if (shape->strokeDashCount > 0) { + int idash = 0, dashState = 1; + float totalDist = 0, dashLen, allDashLen, dashOffset; + NSVGpoint cur; + + if (closed) + nsvg__appendPathPoint(r, r->points[0]); + + // Duplicate points -> points2. + nsvg__duplicatePoints(r); + + r->npoints = 0; + cur = r->points2[0]; + nsvg__appendPathPoint(r, cur); + + // Figure out dash offset. + allDashLen = 0; + for (j = 0; j < shape->strokeDashCount; j++) + allDashLen += shape->strokeDashArray[j]; + if (shape->strokeDashCount & 1) + allDashLen *= 2.0f; + // Find location inside pattern + dashOffset = fmodf(shape->strokeDashOffset, allDashLen); + if (dashOffset < 0.0f) + dashOffset += allDashLen; + + while (dashOffset > shape->strokeDashArray[idash]) { + dashOffset -= shape->strokeDashArray[idash]; + idash = (idash + 1) % shape->strokeDashCount; + } + dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; + + for (j = 1; j < r->npoints2; ) { + float dx = r->points2[j].x - cur.x; + float dy = r->points2[j].y - cur.y; + float dist = sqrtf(dx*dx + dy*dy); + + if ((totalDist + dist) > dashLen) { + // Calculate intermediate point + float d = (dashLen - totalDist) / dist; + float x = cur.x + dx * d; + float y = cur.y + dy * d; + nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); + + // Stroke + if (r->npoints > 1 && dashState) { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } + // Advance dash pattern + dashState = !dashState; + idash = (idash+1) % shape->strokeDashCount; + dashLen = shape->strokeDashArray[idash] * scale; + // Restart + cur.x = x; + cur.y = y; + cur.flags = NSVG_PT_CORNER; + totalDist = 0.0f; + r->npoints = 0; + nsvg__appendPathPoint(r, cur); + } else { + totalDist += dist; + cur = r->points2[j]; + nsvg__appendPathPoint(r, cur); + j++; + } + } + // Stroke any leftover path + if (r->npoints > 1 && dashState) + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } else { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); + } + } +} + +static int nsvg__cmpEdge(const void *p, const void *q) +{ + const NSVGedge* a = (const NSVGedge*)p; + const NSVGedge* b = (const NSVGedge*)q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + + +static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) +{ + NSVGactiveEdge* z; + + if (r->freelist != NULL) { + // Restore from freelist. + z = r->freelist; + r->freelist = z->next; + } else { + // Alloc new edge. + z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); + if (z == NULL) return NULL; + } + + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); +// STBTT_assert(e->y0 <= start_point); + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); + else + z->dx = (int)floorf(NSVG__FIX * dxdy); + z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); +// z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->dir = e->dir; + + return z; +} + +static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) +{ + z->next = r->freelist; + r->freelist = z; +} + +static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) +{ + int i = x0 >> NSVG__FIXSHIFT; + int j = x1 >> NSVG__FIXSHIFT; + if (i < *xmin) *xmin = i; + if (j > *xmax) *xmax = j; + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = (unsigned char)(scanline[i] + maxWeight); + } + } +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) +{ + // non-zero winding fill + int x0 = 0, w = 0; + + if (fillRule == NSVG_FILLRULE_NONZERO) { + // Non-zero + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->dir; + } else { + int x1 = e->x; w += e->dir; + // if we went to zero, we need to draw + if (w == 0) + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } else if (fillRule == NSVG_FILLRULE_EVENODD) { + // Even-odd + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w = 1; + } else { + int x1 = e->x; w = 0; + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } +} + +static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } + +static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return (r) | (g << 8) | (b << 16) | (a << 24); +} + +static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; + int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; + int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; + int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static unsigned int nsvg__applyOpacity(unsigned int c, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (c) & 0xff; + int g = (c>>8) & 0xff; + int b = (c>>16) & 0xff; + int a = (((c>>24) & 0xff)*iu) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static inline int nsvg__div255(int x) +{ + return ((x+1) * 257) >> 16; +} + +static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, + float tx, float ty, float scale, NSVGcachedPaint* cache) +{ + + if (cache->type == NSVG_PAINT_COLOR) { + int i, cr, cg, cb, ca; + cr = cache->colors[0] & 0xff; + cg = (cache->colors[0] >> 8) & 0xff; + cb = (cache->colors[0] >> 16) & 0xff; + ca = (cache->colors[0] >> 24) & 0xff; + + for (i = 0; i < count; i++) { + int r,g,b; + int a = nsvg__div255((int)cover[0] * ca); + int ia = 255 - a; + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + } + } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + float fx, fy, dx, gy; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gy = fx*t[1] + fy*t[3] + t[5]; + c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + // TODO: focus (fx,fy) + float fx, fy, dx, gx, gy, gd; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gx = fx*t[0] + fy*t[2] + t[4]; + gy = fx*t[1] + fy*t[3] + t[5]; + gd = sqrtf(gx*gx + gy*gy); + c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } +} + +static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) +{ + NSVGactiveEdge *active = NULL; + int y, s; + int e = 0; + int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline + int xmin, xmax; + + for (y = 0; y < r->height; y++) { + memset(r->scanline, 0, r->width); + xmin = r->width; + xmax = 0; + for (s = 0; s < NSVG__SUBSAMPLES; ++s) { + // find center of pixel for this scanline + float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; + NSVGactiveEdge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + NSVGactiveEdge *z = *step; + if (z->ey <= scany) { + *step = z->next; // delete from list +// NSVG__assert(z->valid); + nsvg__freeActive(r, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for (;;) { + int changed = 0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + NSVGactiveEdge* t = *step; + NSVGactiveEdge* q = t->next; + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e < r->nedges && r->edges[e].y0 <= scany) { + if (r->edges[e].y1 > scany) { + NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); + if (z == NULL) break; + // find insertion point + if (active == NULL) { + active = z; + } else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + NSVGactiveEdge* p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + e++; + } + + // now process all active edges in non-zero fashion + if (active != NULL) + nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); + } + // Blit + if (xmin < 0) xmin = 0; + if (xmax > r->width-1) xmax = r->width-1; + if (xmin <= xmax) { + nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); + } + } + +} + +static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) +{ + int x,y; + + // Unpremultiply + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = row[0], g = row[1], b = row[2], a = row[3]; + if (a != 0) { + row[0] = (unsigned char)(r*255/a); + row[1] = (unsigned char)(g*255/a); + row[2] = (unsigned char)(b*255/a); + } + row += 4; + } + } + + // Defringe + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = 0, g = 0, b = 0, a = row[3], n = 0; + if (a == 0) { + if (x-1 > 0 && row[-1] != 0) { + r += row[-4]; + g += row[-3]; + b += row[-2]; + n++; + } + if (x+1 < w && row[7] != 0) { + r += row[4]; + g += row[5]; + b += row[6]; + n++; + } + if (y-1 > 0 && row[-stride+3] != 0) { + r += row[-stride]; + g += row[-stride+1]; + b += row[-stride+2]; + n++; + } + if (y+1 < h && row[stride+3] != 0) { + r += row[stride]; + g += row[stride+1]; + b += row[stride+2]; + n++; + } + if (n > 0) { + row[0] = (unsigned char)(r/n); + row[1] = (unsigned char)(g/n); + row[2] = (unsigned char)(b/n); + } + } + row += 4; + } + } +} + + +static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) +{ + int i, j; + NSVGgradient* grad; + + cache->type = paint->type; + + if (paint->type == NSVG_PAINT_COLOR) { + cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); + return; + } + + grad = paint->gradient; + + cache->spread = grad->spread; + memcpy(cache->xform, grad->xform, sizeof(float)*6); + + if (grad->nstops == 0) { + for (i = 0; i < 256; i++) + cache->colors[i] = 0; + } if (grad->nstops == 1) { + for (i = 0; i < 256; i++) + cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); + } else { + unsigned int ca, cb = 0; + float ua, ub, du, u; + int ia, ib, count; + + ca = nsvg__applyOpacity(grad->stops[0].color, opacity); + ua = nsvg__clampf(grad->stops[0].offset, 0, 1); + ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + for (i = 0; i < ia; i++) { + cache->colors[i] = ca; + } + + for (i = 0; i < grad->nstops-1; i++) { + ca = nsvg__applyOpacity(grad->stops[i].color, opacity); + cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); + ua = nsvg__clampf(grad->stops[i].offset, 0, 1); + ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + count = ib - ia; + if (count <= 0) continue; + u = 0; + du = 1.0f / (float)count; + for (j = 0; j < count; j++) { + cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); + u += du; + } + } + + for (i = ib; i < 256; i++) + cache->colors[i] = cb; + } + +} + +/* +static void dumpEdges(NSVGrasterizer* r, const char* name) +{ + float xmin = 0, xmax = 0, ymin = 0, ymax = 0; + NSVGedge *e = NULL; + int i; + if (r->nedges == 0) return; + FILE* fp = fopen(name, "w"); + if (fp == NULL) return; + + xmin = xmax = r->edges[0].x0; + ymin = ymax = r->edges[0].y0; + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + xmin = nsvg__minf(xmin, e->x0); + xmin = nsvg__minf(xmin, e->x1); + xmax = nsvg__maxf(xmax, e->x0); + xmax = nsvg__maxf(xmax, e->x1); + ymin = nsvg__minf(ymin, e->y0); + ymin = nsvg__minf(ymin, e->y1); + ymax = nsvg__maxf(ymax, e->y0); + ymax = nsvg__maxf(ymax, e->y1); + } + + fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); + + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); + } + + for (i = 0; i < r->npoints; i++) { + if (i+1 < r->npoints) + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); + } + + fprintf(fp, ""); + fclose(fp); +} +*/ + +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride) +{ + NSVGshape *shape = NULL; + NSVGedge *e = NULL; + NSVGcachedPaint cache; + int i; + + r->bitmap = dst; + r->width = w; + r->height = h; + r->stride = stride; + + if (w > r->cscanline) { + r->cscanline = w; + r->scanline = (unsigned char*)realloc(r->scanline, w); + if (r->scanline == NULL) return; + } + + for (i = 0; i < h; i++) + memset(&dst[i*stride], 0, w*4); + + for (shape = image->shapes; shape != NULL; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) + continue; + + if (shape->fill.type != NSVG_PAINT_NONE) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShape(r, shape, scale); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->fill, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); + } + if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShapeStroke(r, shape, scale); + +// dumpEdges(r, "edge.svg"); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->stroke, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); + } + } + + nsvg__unpremultiplyAlpha(dst, w, h, stride); + + r->bitmap = NULL; + r->width = 0; + r->height = 0; + r->stride = 0; +} + +#endif diff --git a/output.txt b/output.txt new file mode 100644 index 0000000..72459fb --- /dev/null +++ b/output.txt @@ -0,0 +1,143 @@ +bash$ make -f "$mkfile" -j$(nproc) config=release64 CPPFLAGS=-I/usr/include/x86_64-linux-gnu/ +Creating ../../../../linux_gcc/bin/x64/Release +Compiling 3rdparty/lzma/C/7zAlloc.c... +Compiling 3rdparty/lzma/C/7zArcIn.c... +Compiling 3rdparty/lzma/C/7zBuf.c... +Compiling 3rdparty/lzma/C/7zBuf2.c... +Compiling 3rdparty/lzma/C/7zCrc.c... +Compiling 3rdparty/lzma/C/7zCrcOpt.c... +Compiling 3rdparty/lzma/C/7zDec.c... +Compiling 3rdparty/lzma/C/7zFile.c... +Compiling 3rdparty/lzma/C/7zStream.c... +Compiling 3rdparty/lzma/C/Aes.c... +Compiling 3rdparty/lzma/C/AesOpt.c... +Compiling 3rdparty/lzma/C/Alloc.c... +Compiling 3rdparty/lzma/C/Bcj2.c... +Compiling 3rdparty/lzma/C/Bra.c... +Compiling 3rdparty/lzma/C/Bra86.c... +Compiling 3rdparty/lzma/C/BraIA64.c... +Compiling 3rdparty/lzma/C/CpuArch.c... +Compiling 3rdparty/lzma/C/Delta.c... +Compiling 3rdparty/lzma/C/LzFind.c... +Compiling 3rdparty/lzma/C/Lzma2Dec.c... +Compiling 3rdparty/lzma/C/Lzma2Enc.c... +Compiling 3rdparty/lzma/C/Lzma86Dec.c... +Compiling 3rdparty/lzma/C/Lzma86Enc.c... +Compiling 3rdparty/lzma/C/LzmaDec.c... +Compiling 3rdparty/lzma/C/LzmaEnc.c... +Compiling 3rdparty/lzma/C/Ppmd7.c... +Compiling 3rdparty/lzma/C/Ppmd7Dec.c... +Compiling 3rdparty/lzma/C/Ppmd7Enc.c... +Compiling 3rdparty/lzma/C/Sha256.c... +Compiling 3rdparty/lzma/C/Sort.c... +Archiving lib7z.a... +bash$ mkfile=expat.make +bash$ make -f "$mkfile" -j$(nproc) config=release64 CPPFLAGS=-I/usr/include/x86_64-linux-gnu/ +Compiling 3rdparty/expat/lib/xmlparse.c... +Compiling 3rdparty/expat/lib/xmlrole.c... +Compiling 3rdparty/expat/lib/xmltok.c... +Archiving libexpat.a... +bash$ mkfile=flac.make +bash$ make -f "$mkfile" -j$(nproc) config=release64 CPPFLAGS=-I/usr/include/x86_64-linux-gnu/ +Compiling 3rdparty/libflac/src/libFLAC/bitmath.c... +Compiling 3rdparty/libflac/src/libFLAC/bitreader.c... +Compiling 3rdparty/libflac/src/libFLAC/bitwriter.c... +Compiling 3rdparty/libflac/src/libFLAC/cpu.c... +Compiling 3rdparty/libflac/src/libFLAC/crc.c... +Compiling 3rdparty/libflac/src/libFLAC/fixed.c... +Compiling 3rdparty/libflac/src/libFLAC/float.c... +Compiling 3rdparty/libflac/src/libFLAC/format.c... +Compiling 3rdparty/libflac/src/libFLAC/lpc.c... +Compiling 3rdparty/libflac/src/libFLAC/md5.c... +Compiling 3rdparty/libflac/src/libFLAC/memory.c... +Compiling 3rdparty/libflac/src/libFLAC/stream_decoder.c... +Compiling 3rdparty/libflac/src/libFLAC/stream_encoder.c... +Compiling 3rdparty/libflac/src/libFLAC/stream_encoder_framing.c... +Compiling 3rdparty/libflac/src/libFLAC/window.c... +Archiving libflac.a... +bash$ mkfile=ocore_sdl.make +bash$ make -f "$mkfile" -j$(nproc) config=release64 CPPFLAGS=-I/usr/include/x86_64-linux-gnu/ +Creating ../../../../linux_gcc/bin/x64/Release/mame_mame +Compiling src/osd/modules/osdmodule.cpp... +Compiling src/osd/osdcore.cpp... +Compiling src/osd/osdsync.cpp... +Compiling src/osd/strconv.cpp... +Compiling src/osd/modules/file/posixdir.cpp... +Compiling src/osd/modules/file/posixfile.cpp... +Compiling src/osd/modules/file/posixptty.cpp... +Compiling src/osd/modules/file/posixsocket.cpp... +Compiling src/osd/modules/lib/osdlib_unix.cpp... +Archiving libocore_sdl.a... +bash$ mkfile=utf8proc.make +bash$ make -f "$mkfile" -j$(nproc) config=release64 CPPFLAGS=-I/usr/include/x86_64-linux-gnu/ +Compiling 3rdparty/utf8proc/utf8proc.c... +Archiving libutf8proc.a... +bash$ mkfile=ut +utf8proc.make utils.make +bash$ mkfile=utils.make +bash$ make -f "$mkfile" -j$(nproc) config=release64 CPPFLAGS=-I/usr/include/x86_64-linux-gnu/ +Compiling src/lib/util/avhuff.cpp... +Compiling src/lib/util/aviio.cpp... +Compiling src/lib/util/bitmap.cpp... +Compiling src/lib/util/cdrom.cpp... +Compiling src/lib/util/chd.cpp... +Compiling src/lib/util/chdcd.cpp... +Compiling src/lib/util/chdcodec.cpp... +Compiling src/lib/util/corealloc.cpp... +Compiling src/lib/util/corefile.cpp... +Compiling src/lib/util/corestr.cpp... +Compiling src/lib/util/coreutil.cpp... +Compiling src/lib/util/delegate.cpp... +Compiling src/lib/util/disasmintf.cpp... +Compiling src/lib/util/dynamicclass.cpp... +Compiling src/lib/util/flac.cpp... +Compiling src/lib/util/harddisk.cpp... +Compiling src/lib/util/hash.cpp... +Compiling src/lib/util/hashing.cpp... +Compiling src/lib/util/huffman.cpp... +Compiling src/lib/util/ioprocs.cpp... +Compiling src/lib/util/ioprocsfilter.cpp... +Compiling src/lib/util/jedparse.cpp... +Compiling src/lib/util/language.cpp... +Compiling src/lib/util/md5.cpp... +Compiling src/lib/util/msdib.cpp... +Compiling src/lib/util/nanosvg.cpp... +Compiling src/lib/util/opresolv.cpp... +Compiling src/lib/util/options.cpp... +Compiling src/lib/util/palette.cpp... +Compiling src/lib/util/path.cpp... +Compiling src/lib/util/path_to_regex.cpp... +Compiling src/lib/util/plaparse.cpp... +Compiling src/lib/util/png.cpp... +Compiling src/lib/util/strformat.cpp... +Compiling src/lib/util/timeconv.cpp... +Compiling src/lib/util/un7z.cpp... +Compiling src/lib/util/unicode.cpp... +Compiling src/lib/util/unzip.cpp... +Compiling src/lib/util/vbiparse.cpp... +Compiling src/lib/util/vecstream.cpp... +Compiling src/lib/util/wavwrite.cpp... +Compiling src/lib/util/xmlfile.cpp... +Compiling src/lib/util/zippath.cpp... +Archiving libutils.a... +bash$ mkfile=zlib.make +bash$ make -f "$mkfile" -j$(nproc) config=release64 CPPFLAGS=-I/usr/include/x86_64-linux-gnu/ +Compiling 3rdparty/zlib/adler32.c... +Compiling 3rdparty/zlib/compress.c... +Compiling 3rdparty/zlib/crc32.c... +Compiling 3rdparty/zlib/deflate.c... +Compiling 3rdparty/zlib/infback.c... +Compiling 3rdparty/zlib/inffast.c... +Compiling 3rdparty/zlib/inflate.c... +Compiling 3rdparty/zlib/inftrees.c... +Compiling 3rdparty/zlib/trees.c... +Compiling 3rdparty/zlib/uncompr.c... +Compiling 3rdparty/zlib/zutil.c... +Archiving libzlib.a... +bash$ mkfile=chdman.make +bash$ make -f "$mkfile" -j$(nproc) config=release64 CPPFLAGS=-I/usr/include/x86_64-linux-gnu/ +Compiling src/tools/chdman.cpp... +Compiling generated/version.cpp... +Linking chdman... +bash$ + diff --git a/src/icludes/frontend/mame/audit.cpp b/src/icludes/frontend/mame/audit.cpp new file mode 100644 index 0000000..0a37c4a --- /dev/null +++ b/src/icludes/frontend/mame/audit.cpp @@ -0,0 +1,702 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + audit.cpp + + ROM set auditing functions. + +***************************************************************************/ + +#include "emu.h" +#include "audit.h" + +#include "sound/samples.h" + +#include "emuopts.h" +#include "drivenum.h" +#include "romload.h" +#include "softlist_dev.h" + +#include "chd.h" + +#include + +//#define VERBOSE 1 +#define LOG_OUTPUT_FUNC osd_printf_verbose +#include "logmacro.h" + + +namespace { + +struct parent_rom +{ + parent_rom(device_type t, rom_entry const *r) : type(t), name(r->name()), hashes(r->hashdata()), length(rom_file_size(r)) { } + + std::reference_wrapper > type; + std::string name; + util::hash_collection hashes; + uint64_t length; +}; + + +class parent_rom_vector : public std::vector +{ +public: + using std::vector::vector; + + void remove_redundant_parents() + { + while (!empty()) + { + // find where the next parent starts + auto const last( + std::find_if( + std::next(cbegin()), + cend(), + [this] (parent_rom const &r) { return &front().type.get() != &r.type.get(); })); + + // examine dumped ROMs in this generation + for (auto i = cbegin(); last != i; ++i) + { + if (!i->hashes.flag(util::hash_collection::FLAG_NO_DUMP)) + { + auto const match( + std::find_if( + last, + cend(), + [&i] (parent_rom const &r) { return (i->length == r.length) && (i->hashes == r.hashes); })); + if (cend() == match) + return; + } + } + erase(cbegin(), last); + } + } + + std::add_pointer_t find_shared_device(device_t ¤t, std::string_view name, util::hash_collection const &hashes, uint64_t length) const + { + // if we're examining a child device, it will always have a perfect match + if (current.owner()) + return ¤t.type(); + + // scan backwards through parents for a matching definition + bool const dumped(!hashes.flag(util::hash_collection::FLAG_NO_DUMP)); + std::add_pointer_t best(nullptr); + for (const_reverse_iterator it = crbegin(); crend() != it; ++it) + { + if (it->length == length) + { + if (dumped) + { + if (it->hashes == hashes) + return &it->type.get(); + } + else if (it->name == name) + { + if (it->hashes.flag(util::hash_collection::FLAG_NO_DUMP)) + return &it->type.get(); + else if (!best) + best = &it->type.get(); + } + } + } + return best; + } + + std::pair, bool> actual_matches_shared(device_t ¤t, media_auditor::audit_record const &record) + { + // no result if no matching file was found + if ((record.status() != media_auditor::audit_status::GOOD) && (record.status() != media_auditor::audit_status::FOUND_INVALID)) + return std::make_pair(nullptr, false); + + // if we're examining a child device, scan it first + bool matches_device_undumped(false); + if (current.owner()) + { + for (const rom_entry *region = rom_first_region(current); region; region = rom_next_region(region)) + { + for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + if (rom_file_size(rom) == record.actual_length()) + { + util::hash_collection const hashes(rom->hashdata()); + if (hashes == record.actual_hashes()) + return std::make_pair(¤t.type(), empty()); + else if (hashes.flag(util::hash_collection::FLAG_NO_DUMP) && (rom->name() == record.name())) + matches_device_undumped = true; + } + } + } + } + + // look for a matching parent ROM + std::add_pointer_t closest_bad(nullptr); + for (const_reverse_iterator it = crbegin(); crend() != it; ++it) + { + if (it->length == record.actual_length()) + { + if (it->hashes == record.actual_hashes()) + return std::make_pair(&it->type.get(), it->type.get() == front().type.get()); + else if (it->hashes.flag(util::hash_collection::FLAG_NO_DUMP) && (it->name == record.name())) + closest_bad = &it->type.get(); + } + } + + // fall back to the nearest bad dump + if (closest_bad) + return std::make_pair(closest_bad, front().type.get() == *closest_bad); + else if (matches_device_undumped) + return std::make_pair(¤t.type(), empty()); + else + return std::make_pair(nullptr, false); + } +}; + +} // anonymous namespace + + + +//************************************************************************** +// CORE FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// media_auditor - constructor +//------------------------------------------------- + +media_auditor::media_auditor(const driver_enumerator &enumerator) + : m_enumerator(enumerator) + , m_validation(AUDIT_VALIDATE_FULL) +{ +} + + +//------------------------------------------------- +// audit_media - audit the media described by the +// currently-enumerated driver +//------------------------------------------------- + +media_auditor::summary media_auditor::audit_media(const char *validation) +{ + // start fresh + m_record_list.clear(); + + // store validation for later + m_validation = validation; + + // first walk the parent chain for required ROMs + parent_rom_vector parentroms; + for (auto drvindex = m_enumerator.find(m_enumerator.driver().parent); 0 <= drvindex; drvindex = m_enumerator.find(m_enumerator.driver(drvindex).parent)) + { + game_driver const &parent(m_enumerator.driver(drvindex)); + LOG("Checking parent %s for ROM files\n", parent.type.shortname()); + std::vector const roms(rom_build_entries(parent.rom)); + for (rom_entry const *region = rom_first_region(&roms.front()); region; region = rom_next_region(region)) + { + for (rom_entry const *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + LOG("Adding parent ROM %s\n", rom->name()); + parentroms.emplace_back(parent.type, rom); + } + } + } + parentroms.remove_redundant_parents(); + + // count ROMs required/found + std::size_t found(0); + std::size_t required(0); + std::size_t shared_found(0); + std::size_t shared_required(0); + std::size_t parent_found(0); + + // iterate over devices and regions + std::vector searchpath; + for (device_t &device : device_enumerator(m_enumerator.config()->root_device())) + { + searchpath.clear(); + + // now iterate over regions and ROMs within + for (const rom_entry *region = rom_first_region(device); region; region = rom_next_region(region)) + { + for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + if (searchpath.empty()) + { + LOG("Audit media for device %s(%s)\n", device.shortname(), device.tag()); + searchpath = device.searchpath(); + } + + // look for a matching parent or device ROM + std::string const &name(rom->name()); + util::hash_collection const hashes(rom->hashdata()); + bool const dumped(!hashes.flag(util::hash_collection::FLAG_NO_DUMP)); + std::add_pointer_t const shared_device(parentroms.find_shared_device(device, name, hashes, rom_file_size(rom))); + if (shared_device) + LOG("File '%s' %s%sdumped shared with %s\n", name, ROM_ISOPTIONAL(rom) ? "optional " : "", dumped ? "" : "un", shared_device->shortname()); + else + LOG("File '%s' %s%sdumped\n", name, ROM_ISOPTIONAL(rom) ? "optional " : "", dumped ? "" : "un"); + + // count the number of files with hashes + if (dumped && !ROM_ISOPTIONAL(rom)) + { + required++; + if (shared_device) + shared_required++; + } + + audit_record *record(nullptr); + if (ROMREGION_ISROMDATA(region)) + record = &audit_one_rom(searchpath, rom); + else if (ROMREGION_ISDISKDATA(region)) + record = &audit_one_disk(rom, device); + + if (record) + { + // see if the actual content found belongs to a parent + auto const matchesshared(parentroms.actual_matches_shared(device, *record)); + if (matchesshared.first) + LOG("Actual ROM file shared with %sparent %s\n", matchesshared.second ? "immediate " : "", matchesshared.first->shortname()); + + // count the number of files that are found. + if ((record->status() == audit_status::GOOD) || ((record->status() == audit_status::FOUND_INVALID) && !matchesshared.first)) + { + found++; + if (shared_device) + shared_found++; + if (matchesshared.second) + parent_found++; + } + + record->set_shared_device(shared_device); + } + } + } + + if (!searchpath.empty()) + LOG("Total required=%u (shared=%u) found=%u (shared=%u parent=%u)\n", required, shared_required, found, shared_found, parent_found); + } + + // if we only find files that are in the parent & either the set has no unique files or the parent is not found, then assume we don't have the set at all + if ((found == shared_found) && required && ((required != shared_required) || !parent_found)) + { + m_record_list.clear(); + return NOTFOUND; + } + + // return a summary + return summarize(m_enumerator.driver().name); +} + + +//------------------------------------------------- +// audit_device - audit the device +//------------------------------------------------- + +media_auditor::summary media_auditor::audit_device(device_t &device, const char *validation) +{ + // start fresh + m_record_list.clear(); + + // store validation for later + m_validation = validation; + + std::size_t found = 0; + std::size_t required = 0; + + std::vector searchpath; + audit_regions( + [this, &device, &searchpath] (rom_entry const *region, rom_entry const *rom) -> audit_record const * + { + if (ROMREGION_ISROMDATA(region)) + { + if (searchpath.empty()) + searchpath = device.searchpath(); + return &audit_one_rom(searchpath, rom); + } + else if (ROMREGION_ISDISKDATA(region)) + { + return &audit_one_disk(rom, device); + } + else + { + return nullptr; + } + }, + rom_first_region(device), + found, + required); + + if ((found == 0) && (required > 0)) + { + m_record_list.clear(); + return NOTFOUND; + } + + // return a summary + return summarize(device.shortname()); +} + + +//------------------------------------------------- +// audit_software +//------------------------------------------------- +media_auditor::summary media_auditor::audit_software(software_list_device &swlist, const software_info &swinfo, const char *validation) +{ + // start fresh + m_record_list.clear(); + + // store validation for later + m_validation = validation; + + std::size_t found = 0; + std::size_t required = 0; + + // now iterate over software parts + std::vector searchpath; + auto const do_audit = + [this, &swlist, &swinfo, &searchpath] (rom_entry const *region, rom_entry const *rom) -> audit_record const * + { + if (ROMREGION_ISROMDATA(region)) + { + if (searchpath.empty()) + searchpath = rom_load_manager::get_software_searchpath(swlist, swinfo); + return &audit_one_rom(searchpath, rom); + } + else if (ROMREGION_ISDISKDATA(region)) + { + return &audit_one_disk(rom, swlist, swinfo); + } + else + { + return nullptr; + } + }; + for (const software_part &part : swinfo.parts()) + audit_regions(do_audit, part.romdata().data(), found, required); + + if ((found == 0) && (required > 0)) + { + m_record_list.clear(); + return NOTFOUND; + } + + // return a summary + return summarize(swlist.list_name().c_str()); +} + + +//------------------------------------------------- +// audit_samples - validate the samples for the +// currently-enumerated driver +//------------------------------------------------- + +media_auditor::summary media_auditor::audit_samples() +{ + // start fresh + m_record_list.clear(); + + std::size_t required = 0; + std::size_t found = 0; + + // iterate over sample entries + for (samples_device &device : samples_device_enumerator(m_enumerator.config()->root_device())) + { + // by default we just search using the driver name + std::string searchpath(m_enumerator.driver().name); + + // add the alternate path if present + samples_iterator iter(device); + if (iter.altbasename() != nullptr) + searchpath.append(";").append(iter.altbasename()); + + // iterate over samples in this entry + for (const char *samplename = iter.first(); samplename; samplename = iter.next()) + { + required++; + + // create a new record + audit_record &record = *m_record_list.emplace(m_record_list.end(), samplename, media_type::SAMPLE); + + // look for the files + emu_file file(m_enumerator.options().sample_path(), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD); + path_iterator path(searchpath); + std::string curpath; + while (path.next(curpath, samplename)) + { + // attempt to access the file (.flac) or (.wav) + std::error_condition filerr = file.open(curpath + ".flac"); + if (filerr) + filerr = file.open(curpath + ".wav"); + + if (!filerr) + { + record.set_status(audit_status::GOOD, audit_substatus::GOOD); + found++; + } + else + { + record.set_status(audit_status::NOT_FOUND, audit_substatus::NOT_FOUND); + } + } + } + } + + if ((found == 0) && (required > 0)) + { + m_record_list.clear(); + return NOTFOUND; + } + + // return a summary + return summarize(m_enumerator.driver().name); +} + + +//------------------------------------------------- +// summary - generate a summary, with an optional +// string format +//------------------------------------------------- + +media_auditor::summary media_auditor::summarize(const char *name, std::ostream *output) const +{ + if (m_record_list.empty()) + return NONE_NEEDED; + + // loop over records + summary overall_status = CORRECT; + for (audit_record const &record : m_record_list) + { + // skip anything that's fine + if (record.substatus() == audit_substatus::GOOD) + continue; + + // output the game name, file name, and length (if applicable) + if (output) + { + if (name) + util::stream_format(*output, "%-12s: %s", name, record.name()); + else + util::stream_format(*output, "%s", record.name()); + if (record.expected_length() > 0) + util::stream_format(*output, " (%d bytes)", record.expected_length()); + *output << " - "; + } + + // use the substatus for finer details + summary best_new_status = INCORRECT; + switch (record.substatus()) + { + case audit_substatus::GOOD_NEEDS_REDUMP: + if (output) *output << "NEEDS REDUMP\n"; + best_new_status = BEST_AVAILABLE; + break; + + case audit_substatus::FOUND_NODUMP: + if (output) *output << "NO GOOD DUMP KNOWN\n"; + best_new_status = BEST_AVAILABLE; + break; + + case audit_substatus::FOUND_BAD_CHECKSUM: + if (output) + { + util::stream_format(*output, "INCORRECT CHECKSUM:\n"); + util::stream_format(*output, "EXPECTED: %s\n", record.expected_hashes().macro_string()); + util::stream_format(*output, " FOUND: %s\n", record.actual_hashes().macro_string()); + } + break; + + case audit_substatus::FOUND_WRONG_LENGTH: + if (output) util::stream_format(*output, "INCORRECT LENGTH: %d bytes\n", record.actual_length()); + break; + + case audit_substatus::NOT_FOUND: + if (output) + { + std::add_pointer_t const shared_device = record.shared_device(); + if (shared_device) + util::stream_format(*output, "NOT FOUND (%s)\n", shared_device->shortname()); + else + util::stream_format(*output, "NOT FOUND\n"); + } + break; + + case audit_substatus::NOT_FOUND_NODUMP: + if (output) *output << "NOT FOUND - NO GOOD DUMP KNOWN\n"; + best_new_status = BEST_AVAILABLE; + break; + + case audit_substatus::NOT_FOUND_OPTIONAL: + if (output) *output << "NOT FOUND BUT OPTIONAL\n"; + best_new_status = BEST_AVAILABLE; + break; + + default: + assert(false); + } + + // downgrade the overall status if necessary + overall_status = (std::max)(overall_status, best_new_status); + } + return overall_status; +} + + +//------------------------------------------------- +// audit_regions - validate/count for regions +//------------------------------------------------- + +template +void media_auditor::audit_regions(T do_audit, const rom_entry *region, std::size_t &found, std::size_t &required) +{ + // now iterate over regions + std::vector searchpath; + for ( ; region; region = rom_next_region(region)) + { + // now iterate over rom definitions + for (rom_entry const *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + // count the number of files with hashes + util::hash_collection const hashes(rom->hashdata()); + if (!hashes.flag(util::hash_collection::FLAG_NO_DUMP) && !ROM_ISOPTIONAL(rom)) + required++; + + audit_record const *const record = do_audit(region, rom); + + // count the number of files that are found. + if (record && ((record->status() == audit_status::GOOD) || (record->status() == audit_status::FOUND_INVALID))) + found++; + } + } +} + + +//------------------------------------------------- +// audit_one_rom - validate a single ROM entry +//------------------------------------------------- + +media_auditor::audit_record &media_auditor::audit_one_rom(const std::vector &searchpath, const rom_entry *rom) +{ + // allocate and append a new record + audit_record &record = *m_record_list.emplace(m_record_list.end(), *rom, media_type::ROM); + + // see if we have a CRC and extract it if so + uint32_t crc = 0; + bool const has_crc = record.expected_hashes().crc(crc); + + // find the file and checksum it, getting the file length along the way + emu_file file(m_enumerator.options().media_path(), searchpath, OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD); + file.set_restrict_to_mediapath(1); + + // open the file if we can + std::error_condition filerr; + if (has_crc) + filerr = file.open(record.name(), crc); + else + filerr = file.open(record.name()); + + // if it worked, get the actual length and hashes, then stop + if (!filerr) + record.set_actual(file.hashes(m_validation), file.size()); + + // compute the final status + compute_status(record, rom, record.actual_length() != 0); + return record; +} + + +//------------------------------------------------- +// audit_one_disk - validate a single disk entry +//------------------------------------------------- + +template +media_auditor::audit_record &media_auditor::audit_one_disk(const rom_entry *rom, T &&... args) +{ + // allocate and append a new record + audit_record &record = *m_record_list.emplace(m_record_list.end(), *rom, media_type::DISK); + + // open the disk + chd_file source; + const std::error_condition err = rom_load_manager::open_disk_image(m_enumerator.options(), std::forward(args)..., rom, source); + + // if we succeeded, get the hashes + if (!err) + { + util::hash_collection hashes; + + // if there's a SHA1 hash, add them to the output hash + if (source.sha1() != util::sha1_t::null) + hashes.add_sha1(source.sha1()); + + // update the actual values + record.set_actual(hashes); + } + + // compute the final status + compute_status(record, rom, !err); + return record; +} + + +//------------------------------------------------- +// compute_status - compute a detailed status +// based on the information we have +//------------------------------------------------- + +void media_auditor::compute_status(audit_record &record, const rom_entry *rom, bool found) +{ + // if not found, provide more details + if (!found) + { + if (record.expected_hashes().flag(util::hash_collection::FLAG_NO_DUMP)) + record.set_status(audit_status::NOT_FOUND, audit_substatus::NOT_FOUND_NODUMP); + else if (ROM_ISOPTIONAL(rom)) + record.set_status(audit_status::NOT_FOUND, audit_substatus::NOT_FOUND_OPTIONAL); + else + record.set_status(audit_status::NOT_FOUND, audit_substatus::NOT_FOUND); + } + else + { + if (record.expected_length() != record.actual_length()) + record.set_status(audit_status::FOUND_INVALID, audit_substatus::FOUND_WRONG_LENGTH); + else if (record.expected_hashes().flag(util::hash_collection::FLAG_NO_DUMP)) + record.set_status(audit_status::GOOD, audit_substatus::FOUND_NODUMP); + else if (record.expected_hashes() != record.actual_hashes()) + record.set_status(audit_status::FOUND_INVALID, audit_substatus::FOUND_BAD_CHECKSUM); + else if (record.expected_hashes().flag(util::hash_collection::FLAG_BAD_DUMP)) + record.set_status(audit_status::GOOD, audit_substatus::GOOD_NEEDS_REDUMP); + else + record.set_status(audit_status::GOOD, audit_substatus::GOOD); + } +} + + +//------------------------------------------------- +// audit_record - constructor +//------------------------------------------------- + +media_auditor::audit_record::audit_record(const rom_entry &media, media_type type) + : m_type(type) + , m_status(audit_status::UNVERIFIED) + , m_substatus(audit_substatus::UNVERIFIED) + , m_name(media.name()) + , m_explength(rom_file_size(&media)) + , m_length(0) + , m_exphashes(media.hashdata()) + , m_hashes() + , m_shared_device(nullptr) +{ +} + +media_auditor::audit_record::audit_record(const char *name, media_type type) + : m_type(type) + , m_status(audit_status::UNVERIFIED) + , m_substatus(audit_substatus::UNVERIFIED) + , m_name(name) + , m_explength(0) + , m_length(0) + , m_exphashes() + , m_hashes() + , m_shared_device(nullptr) +{ +} diff --git a/src/icludes/frontend/mame/audit.h b/src/icludes/frontend/mame/audit.h new file mode 100644 index 0000000..10a3292 --- /dev/null +++ b/src/icludes/frontend/mame/audit.h @@ -0,0 +1,177 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + audit.h + + ROM, disk, and sample auditing functions. + +***************************************************************************/ +#ifndef MAME_FRONTEND_AUDIT_H +#define MAME_FRONTEND_AUDIT_H + +#pragma once + +#include +#include +#include + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// hashes to use for validation +#define AUDIT_VALIDATE_FAST "R" /* CRC only */ +#define AUDIT_VALIDATE_FULL "RS" /* CRC + SHA1 */ + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// forward declarations +class driver_enumerator; +class software_list_device; + + + +// ======================> media_auditor + +// class which manages auditing of items +class media_auditor +{ +public: + enum class media_type + { + ROM = 0, + DISK, + SAMPLE + }; + + // status values + enum class audit_status + { + GOOD = 0, + FOUND_INVALID, + NOT_FOUND, + UNVERIFIED = 100 + }; + + // substatus values + enum class audit_substatus + { + GOOD = 0, + GOOD_NEEDS_REDUMP, + FOUND_NODUMP, + FOUND_BAD_CHECKSUM, + FOUND_WRONG_LENGTH, + NOT_FOUND, + NOT_FOUND_NODUMP, + NOT_FOUND_OPTIONAL, + UNVERIFIED = 100 + }; + + // summary values + enum summary + { + CORRECT = 0, + NONE_NEEDED, + BEST_AVAILABLE, + INCORRECT, + NOTFOUND + }; + + // holds the result of auditing a single item + class audit_record + { + public: + // media types + // construction/destruction + audit_record(const rom_entry &media, media_type type); + audit_record(const char *name, media_type type); + audit_record(const audit_record &) = default; + audit_record(audit_record &&) = default; + audit_record &operator=(const audit_record &) = default; + audit_record &operator=(audit_record &&) = default; + + // getters + media_type type() const { return m_type; } + audit_status status() const { return m_status; } + audit_substatus substatus() const { return m_substatus; } + const std::string &name() const { return m_name; } + uint64_t expected_length() const { return m_explength; } + uint64_t actual_length() const { return m_length; } + const util::hash_collection &expected_hashes() const { return m_exphashes; } + const util::hash_collection &actual_hashes() const { return m_hashes; } + std::add_pointer_t shared_device() const { return m_shared_device; } + + // setters + void set_status(audit_status status, audit_substatus substatus) + { + m_status = status; + m_substatus = substatus; + } + + void set_actual(const util::hash_collection &hashes, uint64_t length = 0) + { + m_hashes = hashes; + m_length = length; + } + + void set_actual(util::hash_collection &&hashes, uint64_t length = 0) + { + m_hashes = std::move(hashes); + m_length = length; + } + + void set_shared_device(std::add_pointer_t shared_device) + { + m_shared_device = shared_device; + } + + private: + // internal state + media_type m_type; // type of item that was audited + audit_status m_status; // status of audit on this item + audit_substatus m_substatus; // finer-detail status + std::string m_name; // name of item + uint64_t m_explength; // expected length of item + uint64_t m_length; // actual length of item + util::hash_collection m_exphashes; // expected hash data + util::hash_collection m_hashes; // actual hash information + std::add_pointer_t m_shared_device; // device that shares the ROM + }; + using record_list = std::list; + + // construction/destruction + media_auditor(const driver_enumerator &enumerator); + + // getters + const record_list &records() const { return m_record_list; } + + // audit operations + summary audit_media(const char *validation = AUDIT_VALIDATE_FULL); + summary audit_device(device_t &device, const char *validation = AUDIT_VALIDATE_FULL); + summary audit_software(software_list_device &swlist, const software_info &swinfo, const char *validation = AUDIT_VALIDATE_FULL); + summary audit_samples(); + summary summarize(const char *name, std::ostream *output = nullptr) const; + +private: + // internal helpers + template void audit_regions(T do_audit, const rom_entry *region, std::size_t &found, std::size_t &required); + audit_record &audit_one_rom(const std::vector &searchpath, const rom_entry *rom); + template audit_record &audit_one_disk(const rom_entry *rom, T &&... args); + void compute_status(audit_record &record, const rom_entry *rom, bool found); + + // internal state + record_list m_record_list; + const driver_enumerator & m_enumerator; + const char * m_validation; +}; + + +#endif // MAME_FRONTEND_AUDIT_H diff --git a/src/icludes/frontend/mame/cheat.cpp b/src/icludes/frontend/mame/cheat.cpp new file mode 100644 index 0000000..dbbd0ed --- /dev/null +++ b/src/icludes/frontend/mame/cheat.cpp @@ -0,0 +1,1461 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + cheat.c + + MAME cheat system. + +**************************************************************************** + + Cheat XML format: + + + + + text + ... + + + ... + + ... text ... + + + ... + + +**************************************************************************** + + Expressions are standard debugger expressions. Note that & and + < must be escaped per XML rules. Within attributes you must use + & and <. For tags, you can also use . + + Each cheat has its own context-specific variables: + + temp0-temp9 -- 10 temporary variables for any use + param -- the current value of the cheat parameter + frame -- the current frame index + argindex -- for arguments with multiple iterations, this is the index + + By default, each cheat has 10 temporary variables that are + persistent while executing its scripts. Additional temporary + variables may be requested via the 'tempvariables' attribute + on the cheat. + +**************************************************************************** + + Cheats are generally broken down into categories based on + which actions are defined and whether or not there is a + parameter present: + + ---- Actions ----- + On Off Run Chg Param? Type + === === === === ====== ================================= + N N N ? None Text-only (displays text in menu) + Y N N ? None Oneshot (select to activate) + Y Y N ? None On/Off (select to toggle) + ? ? Y ? None On/Off (select to toggle) + + ? N N Y Any Oneshot parameter (select to alter) + ? Y ? ? Value Value parameter (off or a live value) + ? ? Y ? Value Value parameter (off or a live value) + ? Y ? ? List Item list parameter (off or a live value) + ? ? Y ? List Item list parameter (off or a live value) + +***************************************************************************/ + +#include "emu.h" +#include "cheat.h" + +#include "mame.h" +#include "ui/ui.h" +#include "ui/menu.h" + +#include "corestr.h" +#include "emuopts.h" + +#include +#include +#include + +#include + + + +//************************************************************************** +// PARAMETERS +//************************************************************************** + +// turn this on to enable removing duplicate cheats; not sure if we should +#define REMOVE_DUPLICATE_CHEATS 0 + + + +//************************************************************************** +// NUMBER AND FORMAT +//************************************************************************** + +//------------------------------------------------- +// format - format an integer according to +// the format +//------------------------------------------------- + +inline std::string number_and_format::format() const +{ + switch (m_format) + { + default: + case util::xml::data_node::int_format::DECIMAL: + return string_format("%d", uint32_t(m_value)); + + case util::xml::data_node::int_format::DECIMAL_HASH: + return string_format("#%d", uint32_t(m_value)); + + case util::xml::data_node::int_format::HEX_DOLLAR: + return string_format("$%X", uint32_t(m_value)); + + case util::xml::data_node::int_format::HEX_C: + return string_format("0x%X", uint32_t(m_value)); + } +} + + + +//************************************************************************** +// CHEAT PARAMETER +//************************************************************************** + +//------------------------------------------------- +// cheat_parameter - constructor +//------------------------------------------------- + +cheat_parameter::cheat_parameter(cheat_manager &manager, symbol_table &symbols, std::string const &filename, util::xml::data_node const ¶mnode) + : m_minval(number_and_format(paramnode.get_attribute_int("min", 0), paramnode.get_attribute_int_format("min"))) + , m_maxval(number_and_format(paramnode.get_attribute_int("max", 0), paramnode.get_attribute_int_format("max"))) + , m_stepval(number_and_format(paramnode.get_attribute_int("step", 1), paramnode.get_attribute_int_format("step"))) + , m_value(0) + , m_curtext() + , m_itemlist() +{ + // iterate over items + for (util::xml::data_node const *itemnode = paramnode.get_child("item"); itemnode != nullptr; itemnode = itemnode->get_next_sibling("item")) + { + // check for nullptr text + if (!itemnode->get_value() || !itemnode->get_value()[0]) + throw emu_fatalerror("%s.xml(%d): item is missing text\n", filename, itemnode->line); + + // check for non-existent value + if (!itemnode->has_attribute("value")) + throw emu_fatalerror("%s.xml(%d): item is value\n", filename, itemnode->line); + + // extract the parameters + uint64_t const value(itemnode->get_attribute_int("value", 0)); + util::xml::data_node::int_format const format(itemnode->get_attribute_int_format("value")); + + // allocate and append a new item + item &curitem(m_itemlist.emplace_back(itemnode->get_value(), value, format)); + + // ensure the maximum expands to suit + m_maxval = std::max(m_maxval, curitem.value()); + } + + // add a variable to the symbol table for our value + symbols.add("param", symbol_table::READ_ONLY, &m_value); +} + + +//------------------------------------------------- +// text - return the current text for a parameter +//------------------------------------------------- + +const char *cheat_parameter::text() +{ + // are we a value cheat? + if (!has_itemlist()) + { + m_curtext = string_format("%u (0x%X)", uint64_t(m_value), uint64_t(m_value)); + } + else + { + // if not, we're an item cheat + m_curtext = string_format("??? (%u)", uint64_t(m_value)); + for (item const &curitem : m_itemlist) + { + if (curitem.value() == m_value) + { + m_curtext = curitem.text(); + break; + } + } + } + return m_curtext.c_str(); +} + + +//------------------------------------------------- +// save - save a single cheat parameter +//------------------------------------------------- + +void cheat_parameter::save(emu_file &cheatfile) const +{ + // output the parameter tag + cheatfile.printf("\t\t\n"); + } + else + { + // iterate over items + cheatfile.printf(">\n"); + for (item const &curitem : m_itemlist) + cheatfile.printf("\t\t\t%s\n", curitem.value().format(), curitem.text()); + cheatfile.printf("\t\t\n"); + } +} + + +//------------------------------------------------- +// set_minimum_state - set the minimum state +//------------------------------------------------- + +bool cheat_parameter::set_minimum_state() +{ + uint64_t const origvalue(m_value); + + // set based on whether we have an item list + m_value = !has_itemlist() ? m_minval : m_itemlist.front().value(); + + return m_value != origvalue; +} + + +//------------------------------------------------- +// set_minimum_state - set the previous state +//------------------------------------------------- + +bool cheat_parameter::set_prev_state() +{ + uint64_t const origvalue(m_value); + + if (!has_itemlist()) + { + // are we a value cheat? + if (m_value < (m_minval + m_stepval)) + m_value = m_minval; + else + m_value -= m_stepval; + } + else + { + // if not, we're an item cheat + std::vector::const_iterator it; + for (it = m_itemlist.begin(); (m_itemlist.end() != it) && (it->value() != m_value); ++it) { } + if (m_itemlist.begin() != it) + m_value = std::prev(it)->value(); + } + + return m_value != origvalue; +} + + +//------------------------------------------------- +// set_next_state - advance to the next state +//------------------------------------------------- + +bool cheat_parameter::set_next_state() +{ + uint64_t const origvalue(m_value); + + if (!has_itemlist()) + { + // are we a value cheat? + if (m_value > m_maxval - m_stepval) + m_value = m_maxval; + else + m_value += m_stepval; + } + else + { + // if not, we're an item cheat + std::vector::const_iterator it; + for (it = m_itemlist.begin(); (m_itemlist.end() != it) && (it->value() != m_value); ++it) { } + if ((m_itemlist.end() != it) && (m_itemlist.end() != ++it)) + m_value = it->value(); + } + + return m_value != origvalue; +} + + + +//************************************************************************** +// CHEAT SCRIPT +//************************************************************************** + +constexpr int cheat_script::script_entry::MAX_ARGUMENTS; + + +//------------------------------------------------- +// cheat_script - constructor +//------------------------------------------------- + +cheat_script::cheat_script( + cheat_manager &manager, + symbol_table &symbols, + std::string const &filename, + util::xml::data_node const &scriptnode) + : m_state(SCRIPT_STATE_RUN) +{ + // read the core attributes + char const *const state(scriptnode.get_attribute_string("state", "run")); + if (!std::strcmp(state, "on")) + m_state = SCRIPT_STATE_ON; + else if (!std::strcmp(state, "off")) + m_state = SCRIPT_STATE_OFF; + else if (!std::strcmp(state, "change")) + m_state = SCRIPT_STATE_CHANGE; + else if (std::strcmp(state, "run")) + throw emu_fatalerror("%s.xml(%d): invalid script state '%s'\n", filename, scriptnode.line, state); + + // iterate over nodes within the script + for (util::xml::data_node const *entrynode = scriptnode.get_first_child(); entrynode != nullptr; entrynode = entrynode->get_next_sibling()) + { + if (!std::strcmp(entrynode->get_name(), "action")) // handle action nodes + m_entrylist.push_back(std::make_unique(manager, symbols, filename, *entrynode, true)); + else if (!std::strcmp(entrynode->get_name(), "output")) // handle output nodes + m_entrylist.push_back(std::make_unique(manager, symbols, filename, *entrynode, false)); + else // anything else is ignored + osd_printf_warning("%s.xml(%d): unknown script item '%s' will be lost if saved\n", filename, entrynode->line, entrynode->get_name()); + } +} + + +//------------------------------------------------- +// execute - execute ourself +//------------------------------------------------- + +void cheat_script::execute(cheat_manager &manager, uint64_t &argindex) +{ + // do nothing if disabled + if (!manager.enabled()) + return; + + // iterate over entries + for (auto &entry : m_entrylist) + entry->execute(manager, argindex); +} + + +//------------------------------------------------- +// save - save a single cheat script +//------------------------------------------------- + +void cheat_script::save(emu_file &cheatfile) const +{ + // output the script tag + cheatfile.printf("\t\t\n"); + + // output entries + for (auto &entry : m_entrylist) + entry->save(cheatfile); + + // close the tag + cheatfile.printf("\t\t\n"); +} + + +//------------------------------------------------- +// script_entry - constructor +//------------------------------------------------- + +cheat_script::script_entry::script_entry( + cheat_manager &manager, + symbol_table &symbols, + std::string const &filename, + util::xml::data_node const &entrynode, + bool isaction) + : m_condition(symbols) + , m_expression(symbols) +{ + char const *expression(nullptr); + try + { + // read the condition if present + expression = entrynode.get_attribute_string("condition", nullptr); + if (expression) + m_condition.parse(expression); + + if (isaction) + { + // if this is an action, parse the expression + expression = entrynode.get_value(); + if (!expression || !expression[0]) + throw emu_fatalerror("%s.xml(%d): missing expression in action tag\n", filename, entrynode.line); + m_expression.parse(expression); + + // initialise these to defautlt values + m_line = 0; + m_justify = ui::text_layout::text_justify::LEFT; + } + else + { + // otherwise, parse the attributes and arguments + + // extract format + char const *const format(entrynode.get_attribute_string("format", nullptr)); + if (!format || !format[0]) + throw emu_fatalerror("%s.xml(%d): missing format in output tag\n", filename, entrynode.line); + m_format = format; + + // extract other attributes + m_line = entrynode.get_attribute_int("line", 0); + m_justify = ui::text_layout::text_justify::LEFT; + char const *const align(entrynode.get_attribute_string("align", "left")); + if (!std::strcmp(align, "center")) + m_justify = ui::text_layout::text_justify::CENTER; + else if (!std::strcmp(align, "right")) + m_justify = ui::text_layout::text_justify::RIGHT; + else if (std::strcmp(align, "left")) + throw emu_fatalerror("%s.xml(%d): invalid alignment '%s' specified\n", filename, entrynode.line, align); + + // then parse arguments + int totalargs(0); + for (util::xml::data_node const *argnode = entrynode.get_child("argument"); argnode != nullptr; argnode = argnode->get_next_sibling("argument")) + { + auto curarg = std::make_unique(manager, symbols, filename, *argnode); + // verify we didn't overrun the argument count + totalargs += curarg->count(); + + m_arglist.push_back(std::move(curarg)); + + if (totalargs > MAX_ARGUMENTS) + throw emu_fatalerror("%s.xml(%d): too many arguments (found %d, max is %d)\n", filename, argnode->line, totalargs, MAX_ARGUMENTS); + } + + // validate the format against the arguments + validate_format(filename, entrynode.line); + } + } + catch (expression_error const &err) + { + throw emu_fatalerror("%s.xml(%d): error parsing cheat expression \"%s\" (%s)\n", filename, entrynode.line, expression, err.code_string()); + } +} + + +//------------------------------------------------- +// execute - execute a single script entry +//------------------------------------------------- + +void cheat_script::script_entry::execute(cheat_manager &manager, uint64_t &argindex) +{ + // evaluate the condition + if (!m_condition.is_empty()) + { + try + { + uint64_t const result(m_condition.execute()); + if (!result) + return; + } + catch (expression_error const &err) + { + osd_printf_warning("Error executing conditional expression \"%s\": %s\n", m_condition.original_string(), err.code_string()); + return; + } + } + + // if there is an action, execute it + if (!m_expression.is_empty()) + { + try + { + m_expression.execute(); + } + catch (expression_error &err) + { + osd_printf_warning("Error executing expression \"%s\": %s\n", m_expression.original_string(), err.code_string()); + } + } + + // if there is a string to display, compute it + if (!m_format.empty()) + { + // iterate over arguments and evaluate them + uint64_t params[MAX_ARGUMENTS]; + int curarg = 0; + for (auto &arg : m_arglist) + curarg += arg->values(argindex, ¶ms[curarg]); + + // generate the astring + manager.get_output_string(m_line, m_justify) = string_format(m_format, + params[0], params[1], params[2], params[3], + params[4], params[5], params[6], params[7], + params[8], params[9], params[10], params[11], + params[12], params[13], params[14], params[15], + params[16], params[17], params[18], params[19], + params[20], params[21], params[22], params[23], + params[24], params[25], params[26], params[27], + params[28], params[29], params[30], params[31]); + } +} + + +//------------------------------------------------- +// save - save a single action or output +//------------------------------------------------- + +void cheat_script::script_entry::save(emu_file &cheatfile) const +{ + if (m_format.empty()) + { + // output an action + cheatfile.printf("\t\t\t%s\n", cheat_manager::quote_expression(m_expression)); + } + else + { + // output an output + cheatfile.printf("\t\t\t\n"); + } + else + { + // output arguments + cheatfile.printf(">\n"); + for (auto &curarg : m_arglist) + curarg->save(cheatfile); + cheatfile.printf("\t\t\t\n"); + } + } +} + + +//------------------------------------------------- +// validate_format - check that a format string +// has the correct number and type of arguments +//------------------------------------------------- + +void cheat_script::script_entry::validate_format(std::string const &filename, int line) +{ + // first count arguments + int argsprovided(0); + for (auto &curarg : m_arglist) + argsprovided += curarg->count(); + + // now scan the string for valid argument usage + int argscounted = 0; + for (char const *p = strchr(m_format.c_str(), '%'); p; ++argscounted, p = strchr(p, '%')) + { + // skip past any valid attributes + for (++p; strchr("lh0123456789.-+ #", *p); ++p) { } + + // look for a valid type + if (!strchr("cdiouxX", *p)) + throw emu_fatalerror("%s.xml(%d): invalid format specification \"%s\"\n", filename, line, m_format); + } + + // did we match? + if (argscounted < argsprovided) + throw emu_fatalerror("%s.xml(%d): too many arguments provided (%d) for format \"%s\"\n", filename, line, argsprovided, m_format); + if (argscounted > argsprovided) + throw emu_fatalerror("%s.xml(%d): not enough arguments provided (%d) for format \"%s\"\n", filename, line, argsprovided, m_format); +} + + +//------------------------------------------------- +// output_argument - constructor +//------------------------------------------------- + +cheat_script::script_entry::output_argument::output_argument( + cheat_manager &manager, + symbol_table &symbols, + std::string const &filename, + util::xml::data_node const &argnode) + : m_expression(symbols) + , m_count(0) +{ + // first extract attributes + m_count = argnode.get_attribute_int("count", 1); + + // read the expression + char const *const expression(argnode.get_value()); + if (!expression || !expression[0]) + throw emu_fatalerror("%s.xml(%d): missing expression in argument tag\n", filename, argnode.line); + + // parse it + try + { + m_expression.parse(expression); + } + catch (expression_error const &err) + { + throw emu_fatalerror("%s.xml(%d): error parsing cheat expression \"%s\" (%s)\n", filename, argnode.line, expression, err.code_string()); + } +} + + +//------------------------------------------------- +// value - return the evaluated value of the +// given output argument +//------------------------------------------------- + +int cheat_script::script_entry::output_argument::values(uint64_t &argindex, uint64_t *result) +{ + for (argindex = 0; argindex < m_count; argindex++) + { + try + { + result[argindex] = m_expression.execute(); + } + catch (expression_error const &err) + { + osd_printf_warning("Error executing argument expression \"%s\": %s\n", m_expression.original_string(), err.code_string()); + } + } + return m_count; +} + + +//------------------------------------------------- +// save - save a single output argument +//------------------------------------------------- + +void cheat_script::script_entry::output_argument::save(emu_file &cheatfile) const +{ + cheatfile.printf("\t\t\t\t%s\n", cheat_manager::quote_expression(m_expression)); +} + + + +//************************************************************************** +// CHEAT ENTRY +//************************************************************************** + +//------------------------------------------------- +// cheat_entry - constructor +//------------------------------------------------- + +cheat_entry::cheat_entry(cheat_manager &manager, symbol_table &globaltable, std::string const &filename, util::xml::data_node const &cheatnode) + : m_manager(manager) + , m_symbols(manager.machine(), &globaltable) + , m_state(SCRIPT_STATE_OFF) + , m_numtemp(DEFAULT_TEMP_VARIABLES) + , m_argindex(0) +{ + // pull the variable count out ahead of things + int const tempcount(cheatnode.get_attribute_int("tempvariables", DEFAULT_TEMP_VARIABLES)); + if (tempcount < 1) + throw emu_fatalerror("%s.xml(%d): invalid tempvariables attribute (%d)\n", filename, cheatnode.line, tempcount); + + // allocate memory for the cheat + m_numtemp = tempcount; + + // get the description + char const *const description(cheatnode.get_attribute_string("desc", nullptr)); + if (!description || !description[0]) + throw emu_fatalerror("%s.xml(%d): empty or missing desc attribute on cheat\n", filename, cheatnode.line); + m_description = description; + + // create the symbol table + m_symbols.add("argindex", symbol_table::READ_ONLY, &m_argindex); + for (int curtemp = 0; curtemp < tempcount; curtemp++) + m_symbols.add(string_format("temp%d", curtemp).c_str(), symbol_table::READ_WRITE); + + // read the first comment node + util::xml::data_node const *const commentnode(cheatnode.get_child("comment")); + if (commentnode != nullptr) + { + // set the value if not nullptr + if (commentnode->get_value() && commentnode->get_value()[0]) + m_comment.assign(commentnode->get_value()); + + // only one comment is kept + util::xml::data_node const *const nextcomment(commentnode->get_next_sibling("comment")); + if (nextcomment) + osd_printf_warning("%s.xml(%d): only one comment node is retained; ignoring additional nodes\n", filename, nextcomment->line); + } + + // read the first parameter node + util::xml::data_node const *const paramnode(cheatnode.get_child("parameter")); + if (paramnode != nullptr) + { + // load this parameter + m_parameter.reset(new cheat_parameter(manager, m_symbols, filename, *paramnode)); + + // only one parameter allowed + util::xml::data_node const *const nextparam(paramnode->get_next_sibling("parameter")); + if (nextparam) + osd_printf_warning("%s.xml(%d): only one parameter node allowed; ignoring additional nodes\n", filename, nextparam->line); + } + + // read the script nodes + for (util::xml::data_node const *scriptnode = cheatnode.get_child("script"); scriptnode != nullptr; scriptnode = scriptnode->get_next_sibling("script")) + { + // load this entry + std::unique_ptr curscript(new cheat_script(manager, m_symbols, filename, *scriptnode)); + + // if we have a script already for this slot, it is an error + std::unique_ptr &slot = script_for_state(curscript->state()); + if (slot) + osd_printf_warning("%s.xml(%d): only one on script allowed; ignoring additional scripts\n", filename, scriptnode->line); + else + slot = std::move(curscript); + } +} + + +//------------------------------------------------- +// ~cheat_entry - destructor +//------------------------------------------------- + +cheat_entry::~cheat_entry() +{ +} + + +//------------------------------------------------- +// save - save a single cheat entry +//------------------------------------------------- + +void cheat_entry::save(emu_file &cheatfile) const +{ + // determine if we have scripts + bool const has_scripts(m_off_script || m_on_script || m_run_script || m_change_script); + + // output the cheat tag + cheatfile.printf("\t\n"); + } + else + { + cheatfile.printf(">\n"); + + // save the comment + if (!m_comment.empty()) + cheatfile.printf("\t\t\n", m_comment); + + // output the parameter, if present + if (m_parameter) m_parameter->save(cheatfile); + + // output the script nodes + if (m_on_script) m_on_script->save(cheatfile); + if (m_off_script) m_off_script->save(cheatfile); + if (m_change_script) m_change_script->save(cheatfile); + if (m_run_script) m_run_script->save(cheatfile); + + // close the cheat tag + cheatfile.printf("\t\n"); + } +} + + +//------------------------------------------------- +// activate - activate a oneshot cheat +//------------------------------------------------- + +bool cheat_entry::activate() +{ + bool changed(false); + + // if cheats have been toggled off no point in even trying to do anything + if (!m_manager.enabled()) + return changed; + + if (is_oneshot()) + { + // if we're a oneshot cheat, execute the "on" script and indicate change + execute_on_script(); + changed = true; + m_manager.machine().popmessage("Activated %s", m_description); + } + else if (is_oneshot_parameter() && (m_state != SCRIPT_STATE_OFF)) + { + // if we're a oneshot parameter cheat and we're active, execute the "state change" script and indicate change + execute_change_script(); + changed = true; + m_manager.machine().popmessage("Activated\n %s = %s", m_description, m_parameter->text()); + } + + return changed; +} + + +//------------------------------------------------- +// select_default_state - select the default +// state for a cheat, or activate a oneshot cheat +//------------------------------------------------- + +bool cheat_entry::select_default_state() +{ + bool changed(false); + + if (is_oneshot()) + { + // if we're a oneshot cheat, there is no default state + } + else + { + // all other types switch to the "off" state + changed = set_state(SCRIPT_STATE_OFF); + } + + return changed; +} + + +//------------------------------------------------- +// select_previous_state - select the previous +// state for a cheat +//------------------------------------------------- + +bool cheat_entry::select_previous_state() +{ + bool changed(false); + + if (is_oneshot()) + { + // if we're a oneshot, there is no previous state + } + else if (is_onoff()) + { + // if we're on/off, toggle to off + changed = set_state(SCRIPT_STATE_OFF); + } + else if (m_parameter != nullptr) + { + // if we have a parameter, set the previous state + + // if we're at our minimum, turn off + if (m_parameter->is_minimum()) + { + changed = set_state(SCRIPT_STATE_OFF); + } + else + { + // if we changed, ensure we are in the running state and signal state change + changed = m_parameter->set_prev_state(); + if (changed) + { + set_state(SCRIPT_STATE_RUN); + if (!is_oneshot_parameter()) + execute_change_script(); + } + } + } + + return changed; +} + + +//------------------------------------------------- +// select_next_state - select the next state for +// a cheat +//------------------------------------------------- + +bool cheat_entry::select_next_state() +{ + bool changed(false); + + if (is_oneshot()) + { + // if we're a oneshot, there is no next state + } + else if (is_onoff()) + { + // if we're on/off, toggle to running state + changed = set_state(SCRIPT_STATE_RUN); + } + else if (m_parameter != nullptr) + { + // if we have a parameter, set the next state + if (m_state == SCRIPT_STATE_OFF) + { + // if we're off, switch on to the minimum state + changed = set_state(SCRIPT_STATE_RUN); + m_parameter->set_minimum_state(); + } + else + { + // otherwise, switch to the next state + changed = m_parameter->set_next_state(); + } + + // if we changed, signal a state change + if (changed && !is_oneshot_parameter()) + execute_change_script(); + } + + return changed; +} + + +//------------------------------------------------- +// menu_text - return the text needed to display +// this cheat in a menu item +//------------------------------------------------- + +void cheat_entry::menu_text(std::string &description, std::string &state, uint32_t &flags) +{ + // description is standard + description = m_description; + state.clear(); + flags = 0; + + if (is_text_only()) + { + // some cheat entries are just text for display + if (!description.empty()) + { + description = strtrimspace(description); + if (description.empty()) + description = MENU_SEPARATOR_ITEM; + } + flags = ui::menu::FLAG_DISABLE; + } + else if (is_oneshot()) + { + // if we have no parameter and no run or off script, it's a oneshot cheat + state = "Set"; + } + else if (is_onoff()) + { + // if we have no parameter, it's just on/off + state = (m_state == SCRIPT_STATE_RUN) ? "On" : "Off"; + flags = (m_state != 0) ? ui::menu::FLAG_LEFT_ARROW : ui::menu::FLAG_RIGHT_ARROW; + } + else if (m_parameter != nullptr) + { + // if we have a value parameter, compute it + if (m_state == SCRIPT_STATE_OFF) + { + state = is_oneshot_parameter() ? "Set" : "Off"; + flags = ui::menu::FLAG_RIGHT_ARROW; + } + else + { + state = m_parameter->text(); + flags = ui::menu::FLAG_LEFT_ARROW; + if (!m_parameter->is_maximum()) + flags |= ui::menu::FLAG_RIGHT_ARROW; + } + } +} + + +//------------------------------------------------- +// set_state - switch to the given state +//------------------------------------------------- + +bool cheat_entry::set_state(script_state newstate) +{ + // if we're already in the state, indicate no change + if (m_state == newstate) + return false; + + // change to the state and run the appropriate script + m_state = newstate; + if (newstate == SCRIPT_STATE_OFF) + execute_off_script(); + else if ((newstate == SCRIPT_STATE_ON) || (newstate == SCRIPT_STATE_RUN)) + execute_on_script(); + + return true; +} + + +//------------------------------------------------- +// script_for_state - get a reference to the +// given script pointer +//------------------------------------------------- + +std::unique_ptr &cheat_entry::script_for_state(script_state state) +{ + switch (state) + { + case SCRIPT_STATE_ON: return m_on_script; + case SCRIPT_STATE_OFF: return m_off_script; + case SCRIPT_STATE_CHANGE: return m_change_script; + default: + case SCRIPT_STATE_RUN: return m_run_script; + } +} + + +//------------------------------------------------- +// is_duplicate - determine if a new cheat entry +// is already included in the list +//------------------------------------------------- + +bool cheat_entry::is_duplicate() const +{ + for (auto &scannode : manager().entries()) + { + if (!std::strcmp(scannode->description(), description())) + return true; + } + return false; +} + + +//************************************************************************** +// CHEAT MANAGER +//************************************************************************** + +constexpr int cheat_manager::CHEAT_VERSION; + + +//------------------------------------------------- +// cheat_manager - constructor +//------------------------------------------------- + +cheat_manager::cheat_manager(running_machine &machine) + : m_machine(machine) + , m_framecount(0) + , m_numlines(0) + , m_lastline(0) + , m_disabled(true) + , m_symtable(machine) +{ + // if the cheat engine is disabled, we're done + if (!machine.options().cheat()) + return; + + // in its current form, cheat_manager is tightly coupled to mame_ui_manager; therefore we + // expect this call to succeed + mame_ui_manager *ui = dynamic_cast(&machine.ui()); + assert(ui); + + int target_font_rows = ui->options().font_rows(); + m_output.resize(target_font_rows * 2); + m_justify.resize(target_font_rows * 2); + + // request a callback + machine.add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&cheat_manager::frame_update, this)); + + // create a global symbol table + m_symtable.add("frame", symbol_table::READ_ONLY, &m_framecount); + m_symtable.add("frombcd", 1, 1, execute_frombcd); + m_symtable.add("tobcd", 1, 1, execute_tobcd); + + // load the cheats + reload(); +} + + +//------------------------------------------------- +// set_enable - globally enable or disable the +// cheat engine +//------------------------------------------------- + +void cheat_manager::set_enable(bool enable) +{ + // if the cheat engine is disabled, we're done + if (!machine().options().cheat()) + return; + + if (!m_disabled && !enable) + { + // if we're enabled currently and we don't want to be, turn things off + + // iterate over running cheats and execute any OFF Scripts + for (auto &cheat : m_cheatlist) + { + if (cheat->state() == SCRIPT_STATE_RUN) + cheat->execute_off_script(); + } + machine().popmessage("Cheats Disabled"); + m_disabled = true; + } + else if (m_disabled && enable) + { + // if we're disabled currently and we want to be enabled, turn things on + + // iterate over running cheats and execute any ON Scripts + m_disabled = false; + for (auto &cheat : m_cheatlist) + { + if (cheat->state() == SCRIPT_STATE_RUN) + cheat->execute_on_script(); + } + machine().popmessage("Cheats Enabled"); + } +} + + +//------------------------------------------------- +// reload - re-initialize the cheat engine, and +// and reload the cheat file(s) +//------------------------------------------------- + +void cheat_manager::reload() +{ + // if the cheat engine is disabled, we're done + if (!machine().options().cheat()) + return; + + // free everything + m_cheatlist.clear(); + + // reset state + m_framecount = 0; + m_numlines = 0; + m_lastline = 0; + m_disabled = false; + + // load the cheat file, if it's a system that has a software list then try softlist_name/shortname.xml first, + // if it fails to load then try to load via crc32 - basename/crc32.xml ( eg. 01234567.xml ) + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) + { + if (image.exists()) + { + // if we are loading through a software list, try to load softlist_name/shortname.xml + // this allows the coexistence of arcade cheats with cheats for home conversions which + // have the same shortname + if (image.loaded_through_softlist()) + { + load_cheats(string_format("%s" PATH_SEPARATOR "%s", image.software_list_name(), image.basename())); + break; + } + // else we are loading outside the software list, try to load machine_basename/crc32.xml + else + { + uint32_t crc = image.crc(); + if (crc != 0) + { + load_cheats(string_format("%s" PATH_SEPARATOR "%08X", machine().basename(), crc)); + break; + } + } + } + } + + // if we haven't found the cheats yet, load by basename + if (m_cheatlist.empty()) + load_cheats(machine().basename()); + + // temporary: save the file back out as output.xml for comparison + if (m_cheatlist.size() != 0) + save_all("output"); +} + + +//------------------------------------------------- +// cheat_list_save - save a cheat file from +// memory to the given filename +//------------------------------------------------- + +bool cheat_manager::save_all(std::string const &filename) +{ + // open the file with the proper name + emu_file cheatfile(machine().options().cheat_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + std::error_condition const filerr(cheatfile.open(filename + ".xml")); + + // if that failed, return nothing + if (filerr) + return false; + + // wrap the rest of catch errors + try + { + // output the outer layers + cheatfile.printf("\n"); + cheatfile.printf("\n"); + cheatfile.printf("\n", CHEAT_VERSION); + + // iterate over cheats in the list and save them + for (auto &cheat : m_cheatlist) + cheat->save(cheatfile); + + // close out the file + cheatfile.printf("\n"); + return true; + } + catch (emu_fatalerror const &err) + { + // catch errors and cleanup + osd_printf_error("%s\n", err.what()); + cheatfile.remove_on_close(); + } + return false; +} + + +//------------------------------------------------- +// render_text - called by the UI system to +// render text +//------------------------------------------------- + +void cheat_manager::render_text(mame_ui_manager &mui, render_container &container) +{ + // render any text and free it along the way + for (int linenum = 0; linenum < m_output.size(); linenum++) + { + if (!m_output[linenum].empty()) + { + // output the text + mui.draw_text_full( + container, + m_output[linenum], + 0.0f, float(linenum) * mui.get_line_height(), 1.0f, + m_justify[linenum], ui::text_layout::word_wrapping::NEVER, + mame_ui_manager::OPAQUE_, rgb_t::white(), rgb_t::black(), + nullptr, nullptr); + } + } +} + + +//------------------------------------------------- +// get_output_string - return a reference to +// the given row's string, and set the +// justification +//------------------------------------------------- + +std::string &cheat_manager::get_output_string(int row, ui::text_layout::text_justify justify) +{ + // if the row is not specified, grab the next one + if (row == 0) + row = (m_lastline >= 0) ? (m_lastline + 1) : (m_lastline - 1); + + // remember the last request + m_lastline = row; + + // invert if negative + row = (row < 0) ? (m_numlines + row) : (row - 1); + + // clamp within range + assert(m_numlines > 0); + row = std::clamp(row, 0, m_numlines - 1); + + // return the appropriate string + m_justify[row] = justify; + return m_output[row]; +} + + +//------------------------------------------------- +// quote_expression - quote an expression +// string so that it is valid to embed in an XML +// document +//------------------------------------------------- + +std::string cheat_manager::quote_expression(const parsed_expression &expression) +{ + std::string str = expression.original_string(); + + strreplace(str, " && ", " and "); + strreplace(str, " &&", " and "); + strreplace(str, "&& ", " and "); + strreplace(str, "&&", " and "); + + strreplace(str, " & ", " band "); + strreplace(str, " &", " band "); + strreplace(str, "& ", " band "); + strreplace(str, "&", " band "); + + strreplace(str, " <= ", " le "); + strreplace(str, " <=", " le "); + strreplace(str, "<= ", " le "); + strreplace(str, "<=", " le "); + + strreplace(str, " < ", " lt "); + strreplace(str, " <", " lt "); + strreplace(str, "< ", " lt "); + strreplace(str, "<", " lt "); + + strreplace(str, " << ", " lshift "); + strreplace(str, " <<", " lshift "); + strreplace(str, "<< ", " lshift "); + strreplace(str, "<<", " lshift "); + + return str; +} + + +//------------------------------------------------- +// execute_frombcd - convert a value from BCD +//------------------------------------------------- + +uint64_t cheat_manager::execute_frombcd(int params, const uint64_t *param) +{ + uint64_t value(param[0]); + uint64_t multiplier(1); + uint64_t result(0); + + while (value != 0) + { + result += (value & 0x0f) * multiplier; + value >>= 4; + multiplier *= 10; + } + return result; +} + + +//------------------------------------------------- +// execute_tobcd - convert a value to BCD +//------------------------------------------------- + +uint64_t cheat_manager::execute_tobcd(int params, const uint64_t *param) +{ + uint64_t value(param[0]); + uint64_t result(0); + uint8_t shift(0); + + while (value != 0) + { + result += (value % 10) << shift; + value /= 10; + shift += 4; + } + return result; +} + + +//------------------------------------------------- +// frame_update - per-frame callback +//------------------------------------------------- + +void cheat_manager::frame_update() +{ + // set up for accumulating output + m_lastline = 0; + m_numlines = floor(1.0f / mame_machine_manager::instance()->ui().get_line_height()); + m_numlines = std::min(m_numlines, m_output.size()); + for (auto & elem : m_output) + elem.clear(); + + // iterate over running cheats and execute them + for (auto &cheat : m_cheatlist) + cheat->frame_update(); + + // increment the frame counter + m_framecount++; +} + + +//------------------------------------------------- +// load_cheats - load a cheat file into memory +// and create the cheat entry list +//------------------------------------------------- + +void cheat_manager::load_cheats(std::string const &filename) +{ + std::string searchstr(machine().options().cheat_path()); + std::string curpath; + for (path_iterator path(searchstr); path.next(curpath); ) + { + searchstr.append(";").append(curpath).append(PATH_SEPARATOR "cheat"); + } + emu_file cheatfile(std::move(searchstr), OPEN_FLAG_READ); + try + { + // loop over all instrances of the files found in our search paths + for (std::error_condition filerr = cheatfile.open(filename + ".xml"); !filerr; filerr = cheatfile.open_next()) + { + osd_printf_verbose("Loading cheats file from %s\n", cheatfile.fullpath()); + + // read the XML file into internal data structures + util::xml::parse_options options = { nullptr }; + util::xml::parse_error error; + options.error = &error; + util::xml::file::ptr const rootnode(util::xml::file::read(cheatfile, &options)); + + // if unable to parse the file, just bail + if (!rootnode) + throw emu_fatalerror("%s.xml(%d): error parsing XML (%s)\n", filename, error.error_line, error.error_message); + + // find the layout node + util::xml::data_node const *const mamecheatnode(rootnode->get_child("mamecheat")); + if (mamecheatnode == nullptr) + throw emu_fatalerror("%s.xml: missing mamecheatnode node", filename); + + // validate the config data version + int const version(mamecheatnode->get_attribute_int("version", 0)); + if (version != CHEAT_VERSION) + throw emu_fatalerror("%s.xml(%d): Invalid cheat XML file: unsupported version", filename, mamecheatnode->line); + + // parse all the elements + for (util::xml::data_node const *cheatnode = mamecheatnode->get_child("cheat"); cheatnode != nullptr; cheatnode = cheatnode->get_next_sibling("cheat")) + { + try + { + // load this entry + auto curcheat = std::make_unique(*this, m_symtable, filename, *cheatnode); + + // make sure we're not a duplicate + if (REMOVE_DUPLICATE_CHEATS && curcheat->is_duplicate()) + { + osd_printf_verbose("Ignoring duplicate cheat '%s' from file %s\n", curcheat->description(), cheatfile.fullpath()); + } + else + { + // add to the end of the list + m_cheatlist.push_back(std::move(curcheat)); + } + } + catch (emu_fatalerror const &err) + { + // just move on to the next cheat + osd_printf_error("%s\n", err.what()); + } + } + } + } + catch (emu_fatalerror const &err) + { + // handle errors cleanly + osd_printf_error("%s\n", err.what()); + m_cheatlist.clear(); + } +} diff --git a/src/icludes/frontend/mame/cheat.h b/src/icludes/frontend/mame/cheat.h new file mode 100644 index 0000000..b8f3e89 --- /dev/null +++ b/src/icludes/frontend/mame/cheat.h @@ -0,0 +1,353 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + cheat.h + + Cheat system. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_CHEAT_H +#define MAME_FRONTEND_CHEAT_H + +#pragma once + +#include "debug/express.h" +#include "ui/text.h" +#include "xmlfile.h" + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +enum script_state +{ + SCRIPT_STATE_OFF = 0, + SCRIPT_STATE_ON, + SCRIPT_STATE_RUN, + SCRIPT_STATE_CHANGE, + SCRIPT_STATE_COUNT +}; +DECLARE_ENUM_INCDEC_OPERATORS(script_state) + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class cheat_manager; +class mame_ui_manager; + + +// ======================> number_and_format + +// helper class to remember a format along with a number +class number_and_format +{ +public: + // construction/destruction + constexpr number_and_format( + uint64_t value = 0, + util::xml::data_node::int_format format = util::xml::data_node::int_format::DECIMAL) + : m_value(value) + , m_format(format) + { + } + + // copyable/movable + constexpr number_and_format(number_and_format const &) = default; + number_and_format(number_and_format &&) = default; + number_and_format &operator=(number_and_format const &) = default; + number_and_format &operator=(number_and_format &&) = default; + + // pass-through to look like a regular number + operator uint64_t &() { return m_value; } + operator const uint64_t &() const { return m_value; } + + // format the number according to its format + std::string format() const; + +private: + // internal state + uint64_t m_value; + util::xml::data_node::int_format m_format; +}; + + +// ======================> cheat_parameter + +// a parameter for a cheat, which can be set in the UI +class cheat_parameter +{ +public: + // construction/destruction + cheat_parameter( + cheat_manager &manager, + symbol_table &symbols, + std::string const &filename, + util::xml::data_node const ¶mnode); + + // queries + char const *text(); + bool has_itemlist() const { return !m_itemlist.empty(); } + bool is_minimum() const { return (m_itemlist.empty() ? m_minval : m_itemlist.front().value()) == m_value; } + bool is_maximum() const { return (m_itemlist.empty() ? m_maxval : m_itemlist.back().value()) == m_value; } + + // state setters + bool set_minimum_state(); + bool set_prev_state(); + bool set_next_state(); + + // actions + void save(emu_file &cheatfile) const; + +private: + // a single item in a parameter item list + class item + { + public: + // construction/destruction + item(const char *text, uint64_t value, util::xml::data_node::int_format valformat) + : m_text(text) + , m_value(value, valformat) + { + } + + // copyable/movable + item(item const &) = default; + item(item &&) = default; + item &operator=(item const &) = default; + item &operator=(item &&) = default; + + // getters + number_and_format const &value() const { return m_value; } + char const *text() const { return m_text.c_str(); } + + private: + // internal state + std::string m_text; // name of the item + number_and_format m_value; // value of the item + }; + + // internal state + number_and_format m_minval; // minimum value + number_and_format m_maxval; // maximum value + number_and_format m_stepval; // step value + uint64_t m_value; // live value of the parameter + std::string m_curtext; // holding for a value string + std::vector m_itemlist; // list of items +}; + + +// ======================> cheat_script + +// a script entry, specifying which state to execute under +class cheat_script +{ +public: + // construction/destruction + cheat_script( + cheat_manager &manager, + symbol_table &symbols, + std::string const &filename, + util::xml::data_node const &scriptnode); + + // getters + script_state state() const { return m_state; } + + // actions + void execute(cheat_manager &manager, uint64_t &argindex); + void save(emu_file &cheatfile) const; + +private: + // an entry within the script + class script_entry + { + public: + // construction/destruction + script_entry( + cheat_manager &manager, + symbol_table &symbols, + std::string const &filename, + util::xml::data_node const &entrynode, + bool isaction); + + // actions + void execute(cheat_manager &manager, uint64_t &argindex); + void save(emu_file &cheatfile) const; + + private: + // an argument for output + class output_argument + { + public: + // construction/destruction + output_argument( + cheat_manager &manager, + symbol_table &symbols, + std::string const &filename, + util::xml::data_node const &argnode); + + // getters + int count() const { return m_count; } + int values(uint64_t &argindex, uint64_t *result); + + // actions + void save(emu_file &cheatfile) const; + + private: + // internal state + parsed_expression m_expression; // expression for argument + uint64_t m_count; // number of repetitions + }; + + // internal helpers + void validate_format(std::string const &filename, int line); + + // internal state + parsed_expression m_condition; // condition under which this is executed + parsed_expression m_expression; // expression to execute + std::string m_format; // string format to print + std::vector> m_arglist; // list of arguments + int8_t m_line; // which line to print on + ui::text_layout::text_justify m_justify; // justification when printing + + // constants + static constexpr int MAX_ARGUMENTS = 32; + }; + + // internal state + std::vector> m_entrylist; // list of actions to perform + script_state m_state; // which state this script is for +}; + + +// ======================> cheat_entry + +// a single cheat +class cheat_entry +{ +public: + // construction/destruction + cheat_entry(cheat_manager &manager, symbol_table &globaltable, std::string const &filename, util::xml::data_node const &cheatnode); + ~cheat_entry(); + + // getters + cheat_manager &manager() const { return m_manager; } + script_state state() const { return m_state; } + const char *description() const { return m_description.c_str(); } + const char *comment() const { return m_comment.c_str(); } + + // script detection + bool has_run_script() const { return (m_run_script != nullptr); } + bool has_on_script() const { return (m_on_script != nullptr); } + bool has_off_script() const { return (m_off_script != nullptr); } + bool has_change_script() const { return (m_change_script != nullptr); } + + // script execution + void execute_off_script() { if (has_off_script()) m_off_script->execute(m_manager, m_argindex); } + void execute_on_script() { if (has_on_script()) m_on_script->execute(m_manager, m_argindex); } + void execute_run_script() { if (has_run_script()) m_run_script->execute(m_manager, m_argindex); } + void execute_change_script() { if (has_change_script()) m_change_script->execute(m_manager, m_argindex); } + + // cheat classification + bool is_text_only() const { return (m_parameter == nullptr && !has_run_script() && !has_off_script() && !has_on_script()); } + bool is_oneshot() const { return (m_parameter == nullptr && !has_run_script() && !has_off_script() && has_on_script()); } + bool is_onoff() const { return (m_parameter == nullptr && (has_run_script() || (has_off_script() && has_on_script()))); } + bool is_value_parameter() const { return (m_parameter != nullptr && !m_parameter->has_itemlist()); } + bool is_itemlist_parameter() const { return (m_parameter != nullptr && m_parameter->has_itemlist()); } + bool is_oneshot_parameter() const { return (m_parameter != nullptr && !has_run_script() && !has_off_script() && has_change_script()); } + bool is_duplicate() const; + + // actions + bool activate(); + bool select_default_state(); + bool select_previous_state(); + bool select_next_state(); + void save(emu_file &cheatfile) const; + + // UI helpers + void menu_text(std::string &description, std::string &state, uint32_t &flags); + + // per-frame update + void frame_update() { if (m_state == SCRIPT_STATE_RUN) execute_run_script(); } + +private: + // internal helpers + bool set_state(script_state newstate); + std::unique_ptr &script_for_state(script_state state); + + // internal state + cheat_manager & m_manager; // reference to our manager + std::string m_description; // string description/menu title + std::string m_comment; // comment data + std::unique_ptr m_parameter; // parameter + std::unique_ptr m_on_script; // script to run when turning on + std::unique_ptr m_off_script; // script to run when turning off + std::unique_ptr m_change_script; // script to run when value changes + std::unique_ptr m_run_script; // script to run each frame when on + symbol_table m_symbols; // symbol table for this cheat + script_state m_state; // current cheat state + uint32_t m_numtemp; // number of temporary variables + uint64_t m_argindex; // argument index variable + + // constants + static constexpr int DEFAULT_TEMP_VARIABLES = 10; +}; + + +// ======================> cheat_manager + +// private machine-global data +class cheat_manager +{ +public: + // construction/destruction + cheat_manager(running_machine &machine); + + // getters + running_machine &machine() const { return m_machine; } + bool enabled() const { return !m_disabled; } + std::vector> const &entries() const { return m_cheatlist; } + + // setters + void set_enable(bool enable); + + // actions + void reload(); + bool save_all(std::string const &filename); + void render_text(mame_ui_manager &mui, render_container &container); + + // output helpers + std::string &get_output_string(int row, ui::text_layout::text_justify justify); + + // global helpers + static std::string quote_expression(parsed_expression const &expression); + static uint64_t execute_frombcd(int params, uint64_t const *param); + static uint64_t execute_tobcd(int params, uint64_t const *param); + +private: + // internal helpers + void frame_update(); + void load_cheats(std::string const &filename); + + // internal state + running_machine & m_machine; // reference to our machine + std::vector> m_cheatlist; // cheat list + uint64_t m_framecount; // frame count + std::vector m_output; // array of output strings + std::vector m_justify; // justification for each string + uint8_t m_numlines; // number of lines available for output + int8_t m_lastline; // last line used for output + bool m_disabled; // true if the cheat engine is disabled + symbol_table m_symtable; // global symbol table + + // constants + static constexpr int CHEAT_VERSION = 1; +}; + + +#endif /* MAME_FRONTEND_CHEAT_H */ diff --git a/src/icludes/frontend/mame/clifront.cpp b/src/icludes/frontend/mame/clifront.cpp new file mode 100644 index 0000000..312c96e --- /dev/null +++ b/src/icludes/frontend/mame/clifront.cpp @@ -0,0 +1,1788 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + clifront.cpp + + Command-line interface frontend for MAME. + +***************************************************************************/ + +#include "emu.h" +#include "clifront.h" + +#include "ui/moptions.h" + +#include "audit.h" +#include "infoxml.h" +#include "language.h" +#include "luaengine.h" +#include "mame.h" +#include "mameopts.h" +#include "media_ident.h" +#include "pluginopts.h" + +#include "emuopts.h" +#include "romload.h" +#include "softlist_dev.h" +#include "validity.h" +#include "sound/samples.h" + +#include "chd.h" +#include "corestr.h" +#include "unzip.h" +#include "xmlfile.h" + +#include "osdepend.h" + +#include +#include +#include +#include +#include + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// core commands +#define CLICOMMAND_HELP "help" +#define CLICOMMAND_VALIDATE "validate" + +// configuration commands +#define CLICOMMAND_CREATECONFIG "createconfig" +#define CLICOMMAND_SHOWCONFIG "showconfig" +#define CLICOMMAND_SHOWUSAGE "showusage" + +// frontend commands +#define CLICOMMAND_LISTXML "listxml" +#define CLICOMMAND_LISTFULL "listfull" +#define CLICOMMAND_LISTSOURCE "listsource" +#define CLICOMMAND_LISTCLONES "listclones" +#define CLICOMMAND_LISTBROTHERS "listbrothers" +#define CLICOMMAND_LISTCRC "listcrc" +#define CLICOMMAND_LISTROMS "listroms" +#define CLICOMMAND_LISTSAMPLES "listsamples" +#define CLICOMMAND_VERIFYROMS "verifyroms" +#define CLICOMMAND_VERIFYSAMPLES "verifysamples" +#define CLICOMMAND_ROMIDENT "romident" +#define CLICOMMAND_LISTDEVICES "listdevices" +#define CLICOMMAND_LISTSLOTS "listslots" +#define CLICOMMAND_LISTMEDIA "listmedia" +#define CLICOMMAND_LISTSOFTWARE "listsoftware" +#define CLICOMMAND_VERIFYSOFTWARE "verifysoftware" +#define CLICOMMAND_GETSOFTLIST "getsoftlist" +#define CLICOMMAND_VERIFYSOFTLIST "verifysoftlist" +#define CLICOMMAND_VERSION "version" + +// command options +#define CLIOPTION_DTD "dtd" + + +namespace { + +//************************************************************************** +// COMMAND-LINE OPTIONS +//************************************************************************** + +const options_entry cli_option_entries[] = +{ + /* core commands */ + { nullptr, nullptr, OPTION_HEADER, "CORE COMMANDS" }, + { CLICOMMAND_HELP ";h;?", "0", OPTION_COMMAND, "show help message" }, + { CLICOMMAND_VALIDATE ";valid", "0", OPTION_COMMAND, "perform validation on system drivers and devices" }, + + /* configuration commands */ + { nullptr, nullptr, OPTION_HEADER, "CONFIGURATION COMMANDS" }, + { CLICOMMAND_CREATECONFIG ";cc", "0", OPTION_COMMAND, "create the default configuration file" }, + { CLICOMMAND_SHOWCONFIG ";sc", "0", OPTION_COMMAND, "display running parameters" }, + { CLICOMMAND_SHOWUSAGE ";su", "0", OPTION_COMMAND, "show this help" }, + + /* frontend commands */ + { nullptr, nullptr, OPTION_HEADER, "FRONTEND COMMANDS" }, + { CLICOMMAND_LISTXML ";lx", "0", OPTION_COMMAND, "all available info on driver in XML format" }, + { CLICOMMAND_LISTFULL ";ll", "0", OPTION_COMMAND, "short name, full name" }, + { CLICOMMAND_LISTSOURCE ";ls", "0", OPTION_COMMAND, "driver sourcefile" }, + { CLICOMMAND_LISTCLONES ";lc", "0", OPTION_COMMAND, "show clones" }, + { CLICOMMAND_LISTBROTHERS ";lb", "0", OPTION_COMMAND, "show \"brothers\", or other drivers from same sourcefile" }, + { CLICOMMAND_LISTCRC, "0", OPTION_COMMAND, "CRC-32s" }, + { CLICOMMAND_LISTROMS ";lr", "0", OPTION_COMMAND, "list required ROMs for a driver" }, + { CLICOMMAND_LISTSAMPLES, "0", OPTION_COMMAND, "list optional samples for a driver" }, + { CLICOMMAND_VERIFYROMS, "0", OPTION_COMMAND, "report romsets that have problems" }, + { CLICOMMAND_VERIFYSAMPLES, "0", OPTION_COMMAND, "report samplesets that have problems" }, + { CLICOMMAND_ROMIDENT, "0", OPTION_COMMAND, "compare files with known MAME ROMs" }, + { CLICOMMAND_LISTDEVICES ";ld", "0", OPTION_COMMAND, "list available devices" }, + { CLICOMMAND_LISTSLOTS ";lslot", "0", OPTION_COMMAND, "list available slots and slot devices" }, + { CLICOMMAND_LISTMEDIA ";lm", "0", OPTION_COMMAND, "list available media for the system" }, + { CLICOMMAND_LISTSOFTWARE ";lsoft", "0", OPTION_COMMAND, "list known software for the system" }, + { CLICOMMAND_VERIFYSOFTWARE ";vsoft", "0", OPTION_COMMAND, "verify known software for the system" }, + { CLICOMMAND_GETSOFTLIST ";glist", "0", OPTION_COMMAND, "retrieve software list by name" }, + { CLICOMMAND_VERIFYSOFTLIST ";vlist", "0", OPTION_COMMAND, "verify software list by name" }, + { CLICOMMAND_VERSION, "0", OPTION_COMMAND, "get MAME version" }, + + { nullptr, nullptr, OPTION_HEADER, "FRONTEND COMMAND OPTIONS" }, + { CLIOPTION_DTD, "1", OPTION_BOOLEAN, "include DTD in XML output" }, + { nullptr } +}; + + +void print_summary( + const media_auditor &auditor, media_auditor::summary summary, bool record_none_needed, + const char *type, const char *name, const char *parent, + unsigned &correct, unsigned &incorrect, unsigned ¬found, + util::ovectorstream &buffer) +{ + if (summary == media_auditor::NOTFOUND) + { + // if not found, count that and leave it at that + ++notfound; + } + else if (record_none_needed || (summary != media_auditor::NONE_NEEDED)) + { + // output the summary of the audit + buffer.clear(); + buffer.seekp(0); + auditor.summarize(name, &buffer); + buffer.put('\0'); + osd_printf_info("%s", &buffer.vec()[0]); + + // output the name of the driver and its parent + osd_printf_info("%sset %s ", type, name); + if (parent) + osd_printf_info("[%s] ", parent); + + // switch off of the result + switch (summary) + { + case media_auditor::INCORRECT: + osd_printf_info("is bad\n"); + ++incorrect; + return; + + case media_auditor::CORRECT: + osd_printf_info("is good\n"); + ++correct; + return; + + case media_auditor::BEST_AVAILABLE: + case media_auditor::NONE_NEEDED: + osd_printf_info("is best available\n"); + ++correct; + return; + + case media_auditor::NOTFOUND: + osd_printf_info("not found\n"); + return; + } + assert(false); + osd_printf_error("has unknown status (%u)\n", unsigned(summary)); + } +} + +} // anonymous namespace + + +//************************************************************************** +// CLI FRONTEND +//************************************************************************** + +//------------------------------------------------- +// cli_frontend - constructor +//------------------------------------------------- + +cli_frontend::cli_frontend(emu_options &options, osd_interface &osd) + : m_options(options) + , m_osd(osd) + , m_result(EMU_ERR_NONE) +{ + m_options.add_entries(cli_option_entries); +} + + +//------------------------------------------------- +// ~cli_frontend - destructor +//------------------------------------------------- + +cli_frontend::~cli_frontend() +{ +} + +void cli_frontend::start_execution(mame_machine_manager *manager, const std::vector &args) +{ + std::ostringstream option_errors; + + // because softlist evaluation relies on hashpath being populated, we are going to go through + // a special step to force it to be evaluated + mame_options::populate_hashpath_from_args_and_inis(m_options, args); + + // parse the command line, adding any system-specific options + try + { + m_options.parse_command_line(args, OPTION_PRIORITY_CMDLINE); + } + catch (options_warning_exception &ex) + { + osd_printf_error("%s", ex.message()); + } + catch (options_exception &ex) + { + // if we failed, check for no command and a system name first; in that case error on the name + if (m_options.command().empty() && mame_options::system(m_options) == nullptr && !m_options.attempted_system_name().empty()) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "Unknown system '%s'", m_options.attempted_system_name()); + + // otherwise, error on the options + throw emu_fatalerror(EMU_ERR_INVALID_CONFIG, "%s", ex.message()); + } + m_osd.set_verbose(m_options.verbose()); + + // determine the base name of the EXE + std::string_view exename = core_filename_extract_base(args[0], true); + + // if we have a command, execute that + if (!m_options.command().empty()) + { + execute_commands(exename); + return; + } + + // read INI's, if appropriate + if (m_options.read_config()) + { + mame_options::parse_standard_inis(m_options, option_errors); + m_osd.set_verbose(m_options.verbose()); + } + + // otherwise, check for a valid system + load_translation(m_options); + + manager->start_http_server(); + + manager->start_luaengine(); + + if (option_errors.tellp() > 0) + osd_printf_error("Error in command line:\n%s\n", strtrimspace(option_errors.str())); + + // if we can't find it, give an appropriate error + const game_driver *system = mame_options::system(m_options); + if (system == nullptr && *(m_options.system_name()) != 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "Unknown system '%s'", m_options.system_name()); + + // otherwise just run the game + m_result = manager->execute(); +} + +//------------------------------------------------- +// execute - execute a game via the standard +// command line interface +//------------------------------------------------- + +int cli_frontend::execute(std::vector &args) +{ + // wrap the core execution in a try/catch to field all fatal errors + m_result = EMU_ERR_NONE; + mame_machine_manager *manager = mame_machine_manager::instance(m_options, m_osd); + + try + { + start_execution(manager, args); + } + // handle exceptions of various types + catch (emu_fatalerror &fatal) + { + osd_printf_error("%s\n", strtrimspace(fatal.what())); + m_result = (fatal.exitcode() != 0) ? fatal.exitcode() : EMU_ERR_FATALERROR; + + // if a game was specified, wasn't a wildcard, and our error indicates this was the + // reason for failure, offer some suggestions + if (m_result == EMU_ERR_NO_SUCH_SYSTEM + && !m_options.attempted_system_name().empty() + && !core_iswildstr(m_options.attempted_system_name().c_str()) + && mame_options::system(m_options) == nullptr) + { + // get the top 16 approximate matches + driver_enumerator drivlist(m_options); + int matches[16]; + drivlist.find_approximate_matches(m_options.attempted_system_name(), std::size(matches), matches); + + // work out how wide the titles need to be + int titlelen(0); + for (int match : matches) + if (0 <= match) + titlelen = (std::max)(titlelen, int(strlen(drivlist.driver(match).type.fullname()))); + + // print them out + osd_printf_error("\n\"%s\" approximately matches the following\n" + "supported machines (best match first):\n\n", m_options.attempted_system_name()); + for (int match : matches) + { + if (0 <= match) + { + game_driver const &drv(drivlist.driver(match)); + osd_printf_error("%-18s%-*s(%s, %s)\n", drv.name, titlelen + 2, drv.type.fullname(), drv.manufacturer, drv.year); + } + } + } + } + catch (emu_exception &) + { + osd_printf_error("Caught unhandled emulator exception\n"); + m_result = EMU_ERR_FATALERROR; + } + catch (tag_add_exception &aex) + { + osd_printf_error("Tag '%s' already exists in tagged map\n", aex.tag()); + m_result = EMU_ERR_FATALERROR; + } + catch (std::exception &ex) + { + osd_printf_error("Caught unhandled %s exception: %s\n", typeid(ex).name(), ex.what()); + m_result = EMU_ERR_FATALERROR; + } + catch (...) + { + osd_printf_error("Caught unhandled exception\n"); + m_result = EMU_ERR_FATALERROR; + } + + util::archive_file::cache_clear(); + delete manager; + + return m_result; +} + + +//------------------------------------------------- +// listxml - output the XML data for one or more +// games +//------------------------------------------------- + +void cli_frontend::listxml(const std::vector &args) +{ + // create the XML and print it to stdout + info_xml_creator creator(m_options, m_options.bool_value(CLIOPTION_DTD)); + creator.output(std::cout, args); +} + + +//------------------------------------------------- +// listfull - output the name and description of +// one or more games +//------------------------------------------------- + +void cli_frontend::listfull(const std::vector &args) +{ + auto const list_system_name = [] (device_type type, bool first) + { + // print the header + if (first) + osd_printf_info("Name: Description:\n"); + + osd_printf_info("%-17s \"%s\"\n", type.shortname(), type.fullname()); + }; + apply_action( + args, + [&list_system_name] (driver_enumerator &drivlist, bool first) + { list_system_name(drivlist.driver().type, first); }, + [&list_system_name] (device_type type, bool first) + { list_system_name(type, first); }); +} + + +//------------------------------------------------- +// listsource - output the name and source +// filename of one or more games +//------------------------------------------------- + +void cli_frontend::listsource(const std::vector &args) +{ + auto const list_system_source = [] (device_type type) + { + osd_printf_info("%-16s %s\n", type.shortname(), core_filename_extract_base(type.source())); + }; + apply_action( + args, + [&list_system_source] (driver_enumerator &drivlist, bool first) + { list_system_source(drivlist.driver().type); }, + [&list_system_source] (device_type type, bool first) + { list_system_source(type); }); +} + + +//------------------------------------------------- +// listclones - output the name and parent of all +// clones matching the given pattern +//------------------------------------------------- + +void cli_frontend::listclones(const std::vector &args) +{ + const char *gamename = args.empty() ? nullptr : args[0].c_str(); + + // start with a filtered list of drivers + driver_enumerator drivlist(m_options, gamename); + int const original_count = drivlist.count(); + + // iterate through the remaining ones to see if their parent matches + while (drivlist.next_excluded()) + { + // if we have a non-bios clone and it matches, keep it + int const clone_of = drivlist.clone(); + if ((clone_of >= 0) && !(drivlist.driver(clone_of).flags & machine_flags::IS_BIOS_ROOT)) + if (drivlist.matches(gamename, drivlist.driver(clone_of).name)) + drivlist.include(); + } + + // return an error if none found + if (drivlist.count() == 0) + { + // see if we match but just weren't a clone + if (original_count == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + else + osd_printf_info("Found %lu match(es) for '%s' but none were clones\n", (unsigned long)drivlist.count(), gamename); // FIXME: this never gets hit + return; + } + + // print the header + osd_printf_info("Name: Clone of:\n"); + + // iterate through drivers and output the info + drivlist.reset(); + while (drivlist.next()) + { + int clone_of = drivlist.clone(); + if ((clone_of >= 0) && !(drivlist.driver(clone_of).flags & machine_flags::IS_BIOS_ROOT)) + osd_printf_info("%-16s %s\n", drivlist.driver().name, drivlist.driver(clone_of).name); + } +} + + +//------------------------------------------------- +// listbrothers - for each matching game, output +// the list of other games that share the same +// source file +//------------------------------------------------- + +void cli_frontend::listbrothers(const std::vector &args) +{ + const char *gamename = args.empty() ? nullptr : args[0].c_str(); + + // start with a filtered list of drivers; return an error if none found + driver_enumerator initial_drivlist(m_options, gamename); + if (initial_drivlist.count() == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + + // for the final list, start with an empty driver list + driver_enumerator drivlist(m_options); + drivlist.exclude_all(); + + // scan through the initially-selected drivers + while (initial_drivlist.next()) + { + // if we are already marked in the final list, we don't need to do anything + if (drivlist.included(initial_drivlist.current())) + continue; + + // otherwise, walk excluded items in the final list and mark any that match + drivlist.reset(); + while (drivlist.next_excluded()) + if (strcmp(drivlist.driver().type.source(), initial_drivlist.driver().type.source()) == 0) + drivlist.include(); + } + + // print the header + osd_printf_info("%-20s %-16s %s\n", "Source file:", "Name:", "Parent:"); + + // output the entries found + drivlist.reset(); + while (drivlist.next()) + { + int clone_of = drivlist.clone(); + if (clone_of != -1) + osd_printf_info("%-20s %-16s %s\n", core_filename_extract_base(drivlist.driver().type.source()), drivlist.driver().name, (clone_of == -1 ? "" : drivlist.driver(clone_of).name)); + else + osd_printf_info("%-20s %s\n", core_filename_extract_base(drivlist.driver().type.source()), drivlist.driver().name); + } +} + + +//------------------------------------------------- +// listcrc - output the CRC and name of all ROMs +// referenced by the emulator +//------------------------------------------------- + +void cli_frontend::listcrc(const std::vector &args) +{ + apply_device_action( + args, + [] (device_t &root, char const *type, bool first) + { + for (device_t const &device : device_enumerator(root)) + { + for (tiny_rom_entry const *rom = device.rom_region(); rom && !ROMENTRY_ISEND(rom); ++rom) + { + if (ROMENTRY_ISFILE(rom)) + { + // if we have a CRC, display it + uint32_t crc; + if (util::hash_collection(rom->hashdata).crc(crc)) + osd_printf_info("%08x %-32s\t%-16s\t%s\n", crc, rom->name, device.shortname(), device.name()); + } + } + } + }); +} + + +//------------------------------------------------- +// listroms - output the list of ROMs referenced +// by matching systems/devices +//------------------------------------------------- + +void cli_frontend::listroms(const std::vector &args) +{ + apply_device_action( + args, + [] (device_t &root, char const *type, bool first) + { + // space between items + if (!first) + osd_printf_info("\n"); + + // iterate through ROMs + std::list> entries; + std::set devnames; + for (device_t const &device : device_enumerator(root)) + { + bool hasroms = false; + for (const rom_entry *region = rom_first_region(device); region; region = rom_next_region(region)) + { + for (const rom_entry *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + if (!hasroms) + { + hasroms = true; + if (&device != &root) + devnames.insert(device.shortname()); + } + + // accumulate the total length of all chunks + int64_t length = -1; + if (ROMREGION_ISROMDATA(region)) + length = rom_file_size(rom); + + entries.emplace_back(rom->name(), length, rom->hashdata()); + } + } + } + + // print results + if (entries.empty()) + osd_printf_info("No ROMs required for %s \"%s\".\n", type, root.shortname()); + else + { + // print a header + osd_printf_info("ROMs required for %s \"%s\"", type, root.shortname()); + if (!devnames.empty()) + { + osd_printf_info(" (including device%s", devnames.size() > 1 ? "s" : ""); + bool first = true; + for (const std::string_view &devname : devnames) + { + if (first) + first = false; + else + osd_printf_info(","); + osd_printf_info(" \"%s\"", devname); + } + osd_printf_info(")"); + } + osd_printf_info(".\n%-32s %10s %s\n", "Name", "Size", "Checksum"); + + for (auto &entry : entries) + { + // start with the name + osd_printf_info("%-32s ", std::get<0>(entry)); + + // output the length next + int64_t length = std::get<1>(entry); + if (length >= 0) + osd_printf_info("%10u", unsigned(uint64_t(length))); + else + osd_printf_info("%10s", ""); + + // output the hash data + util::hash_collection hashes(std::get<2>(entry)); + if (!hashes.flag(util::hash_collection::FLAG_NO_DUMP)) + { + if (hashes.flag(util::hash_collection::FLAG_BAD_DUMP)) + osd_printf_info(" BAD"); + osd_printf_info(" %s", hashes.macro_string()); + } + else + osd_printf_info(" NO GOOD DUMP KNOWN"); + + // end with a CR + osd_printf_info("\n"); + } + } + }); +} + + +//------------------------------------------------- +// listsamples - output the list of samples +// referenced by a given game or set of games +//------------------------------------------------- + +void cli_frontend::listsamples(const std::vector &args) +{ + const char *gamename = args.empty() ? nullptr : args[0].c_str(); + + // determine which drivers to output; return an error if none found + driver_enumerator drivlist(m_options, gamename); + if (drivlist.count() == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + + // iterate over drivers, looking for SAMPLES devices + bool first = true; + while (drivlist.next()) + { + // see if we have samples + samples_device_enumerator iter(drivlist.config()->root_device()); + if (iter.count() == 0) + continue; + + // print a header + if (!first) + osd_printf_info("\n"); + first = false; + osd_printf_info("Samples required for driver \"%s\".\n", drivlist.driver().name); + + // iterate over samples devices and print the samples from each one + for (samples_device &device : iter) + { + samples_iterator sampiter(device); + for (const char *samplename = sampiter.first(); samplename != nullptr; samplename = sampiter.next()) + osd_printf_info("%s\n", samplename); + } + } +} + + +//------------------------------------------------- +// listdevices - output the list of devices +// referenced by a given game or set of games +//------------------------------------------------- + +void cli_frontend::listdevices(const std::vector &args) +{ + const char *gamename = args.empty() ? nullptr : args[0].c_str(); + + // determine which drivers to output; return an error if none found + driver_enumerator drivlist(m_options, gamename); + if (drivlist.count() == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + + // iterate over drivers, looking for SAMPLES devices + bool first = true; + while (drivlist.next()) + { + // print a header + if (!first) + printf("\n"); + first = false; + printf("Driver %s (%s):\n", drivlist.driver().name, drivlist.driver().type.fullname()); + + // build a list of devices + std::vector device_list; + for (device_t &device : device_enumerator(drivlist.config()->root_device())) + device_list.push_back(&device); + + // sort them by tag + std::sort(device_list.begin(), device_list.end(), [](device_t *dev1, device_t *dev2) { + // end of string < ':' < '0' + const char *tag1 = dev1->tag(); + const char *tag2 = dev2->tag(); + while (*tag1 == *tag2 && *tag1 != '\0' && *tag2 != '\0') + { + tag1++; + tag2++; + } + return (*tag1 == ':' ? ' ' : *tag1) < (*tag2 == ':' ? ' ' : *tag2); + }); + + // dump the results + for (auto device : device_list) + { + // extract the tag, stripping the leading colon + const char *tag = device->tag(); + if (*tag == ':') + tag++; + + // determine the depth + int depth = 1; + if (*tag == 0) + { + tag = ""; + depth = 0; + } + else + { + for (const char *c = tag; *c != 0; c++) + if (*c == ':') + { + tag = c + 1; + depth++; + } + } + printf(" %*s%-*s %s", depth * 2, "", 30 - depth * 2, tag, device->name()); + + // add more information + uint32_t clock = device->clock(); + if (clock >= 1000000000) + printf(" @ %d.%02d GHz\n", clock / 1000000000, (clock / 10000000) % 100); + else if (clock >= 1000000) + printf(" @ %d.%02d MHz\n", clock / 1000000, (clock / 10000) % 100); + else if (clock >= 1000) + printf(" @ %d.%02d kHz\n", clock / 1000, (clock / 10) % 100); + else if (clock > 0) + printf(" @ %d Hz\n", clock); + else + printf("\n"); + } + } +} + + +//------------------------------------------------- +// listslots - output the list of slot devices +// referenced by a given game or set of games +//------------------------------------------------- + +void cli_frontend::listslots(const std::vector &args) +{ + const char *gamename = args.empty() ? nullptr : args[0].c_str(); + + // determine which drivers to output; return an error if none found + driver_enumerator drivlist(m_options, gamename); + if (drivlist.count() == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + + // print header + printf("%-16s %-16s %-16s %s\n", "SYSTEM", "SLOT NAME", "SLOT OPTIONS", "SLOT DEVICE NAME"); + printf("%s %s %s %s\n", std::string(16,'-').c_str(), std::string(16,'-').c_str(), std::string(16,'-').c_str(), std::string(28,'-').c_str()); + + // iterate over drivers + while (drivlist.next()) + { + // iterate + bool first = true; + for (const device_slot_interface &slot : slot_interface_enumerator(drivlist.config()->root_device())) + { + if (slot.fixed()) continue; + + // build a list of user-selectable options + std::vector option_list; + for (auto &option : slot.option_list()) + if (option.second->selectable()) + option_list.push_back(option.second.get()); + + // sort them by name + std::sort(option_list.begin(), option_list.end(), [](device_slot_interface::slot_option const *opt1, device_slot_interface::slot_option const *opt2) { + return strcmp(opt1->name(), opt2->name()) < 0; + }); + + + // output the line, up to the list of extensions + printf("%-16s %-16s ", first ? drivlist.driver().name : "", slot.device().tag()+1); + + bool first_option = true; + + // get the options and print them + for (device_slot_interface::slot_option const *opt : option_list) + { + if (first_option) + printf("%-16s %s\n", opt->name(), opt->devtype().fullname()); + else + printf("%-34s%-16s %s\n", "", opt->name(), opt->devtype().fullname()); + + first_option = false; + } + if (first_option) + printf("%-16s %s\n", "[none]","No options available"); + // end the line + printf("\n"); + first = false; + } + + // if we didn't get any at all, just print a none line + if (first) + printf("%-16s (none)\n", drivlist.driver().name); + } +} + + +//------------------------------------------------- +// listmedia - output the list of image devices +// referenced by a given game or set of games +//------------------------------------------------- + +void cli_frontend::listmedia(const std::vector &args) +{ + const char *gamename = args.empty() ? nullptr : args[0].c_str(); + + // determine which drivers to output; return an error if none found + driver_enumerator drivlist(m_options, gamename); + if (drivlist.count() == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + + // print header + printf("%-16s %-16s %-10s %s\n", "SYSTEM", "MEDIA NAME", "(brief)", "IMAGE FILE EXTENSIONS SUPPORTED"); + printf("%s %s-%s %s\n", std::string(16,'-').c_str(), std::string(16,'-').c_str(), std::string(10,'-').c_str(), std::string(31,'-').c_str()); + + // iterate over drivers + while (drivlist.next()) + { + // iterate + bool first = true; + for (const device_image_interface &imagedev : image_interface_enumerator(drivlist.config()->root_device())) + { + if (!imagedev.user_loadable()) + continue; + + // extract the shortname with parentheses + std::string paren_shortname = string_format("(%s)", imagedev.brief_instance_name()); + + // output the line, up to the list of extensions + printf("%-16s %-16s %-10s ", drivlist.driver().name, imagedev.instance_name().c_str(), paren_shortname.c_str()); + + // get the extensions and print them + std::string extensions(imagedev.file_extensions()); + for (int start = 0, end = extensions.find_first_of(',');; start = end + 1, end = extensions.find_first_of(',', start)) + { + std::string curext(extensions, start, (end == -1) ? extensions.length() - start : end - start); + printf(".%-5s", curext.c_str()); + if (end == -1) + break; + } + + // end the line + printf("\n"); + first = false; + } + + // if we didn't get any at all, just print a none line + if (first) + printf("%-16s (none)\n", drivlist.driver().name); + } +} + +//------------------------------------------------- +// verifyroms - verify the ROM sets of one or +// more games +//------------------------------------------------- +void cli_frontend::verifyroms(const std::vector &args) +{ + bool const iswild((1U != args.size()) || core_iswildstr(args[0].c_str())); + std::vector matched(args.size(), false); + unsigned matchcount = 0; + auto const included = [&args, &matched, &matchcount] (char const *name) -> bool + { + if (args.empty()) + { + ++matchcount; + return true; + } + + bool result = false; + auto it = matched.begin(); + for (std::string const &pat : args) + { + if (!core_strwildcmp(pat.c_str(), name)) + { + ++matchcount; + result = true; + *it = true; + } + ++it; + } + return result; + }; + + unsigned correct = 0; + unsigned incorrect = 0; + unsigned notfound = 0; + + // iterate over drivers + driver_enumerator drivlist(m_options); + media_auditor auditor(drivlist); + util::ovectorstream summary_string; + while (drivlist.next()) + { + if (included(drivlist.driver().name)) + { + // audit the ROMs in this set + media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST); + + auto const clone_of = drivlist.clone(); + print_summary( + auditor, summary, true, + "rom", drivlist.driver().name, (clone_of >= 0) ? drivlist.driver(clone_of).name : nullptr, + correct, incorrect, notfound, + summary_string); + + // if it wasn't a wildcard, there can only be one + if (!iswild) + break; + } + } + + if (iswild || !matchcount) + { + machine_config config(GAME_NAME(___empty), m_options); + machine_config::token const tok(config.begin_configuration(config.root_device())); + for (device_type type : registered_device_types) + { + if (included(type.shortname())) + { + // audit the ROMs in this set + device_t *const dev = config.device_add("_tmp", type, 0); + media_auditor::summary summary = auditor.audit_device(*dev, AUDIT_VALIDATE_FAST); + + print_summary( + auditor, summary, false, + "rom", dev->shortname(), nullptr, + correct, incorrect, notfound, + summary_string); + config.device_remove("_tmp"); + + // if it wasn't a wildcard, there can only be one + if (!iswild) + break; + } + } + } + + // clear out any cached files + util::archive_file::cache_clear(); + + // return an error if none found + auto it = matched.begin(); + for (std::string const &pat : args) + { + if (!*it) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", pat); + + ++it; + } + + if ((1U == args.size()) && (matchcount > 0) && (correct == 0) && (incorrect == 0)) + { + // if we didn't get anything at all, display a generic end message + if (notfound > 0) + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "romset \"%s\" not found!\n", args[0]); + else + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "romset \"%s\" has no roms!\n", args[0]); + } + else + { + // otherwise, print a summary + if (incorrect > 0) + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "%u romsets found, %u were OK.\n", correct + incorrect, correct); + else + osd_printf_info("%u romsets found, %u were OK.\n", correct, correct); + } +} + + +//------------------------------------------------- +// info_verifysamples - verify the sample sets of +// one or more games +//------------------------------------------------- + +void cli_frontend::verifysamples(const std::vector &args) +{ + const char *gamename = args.empty() ? "*" : args[0].c_str(); + + // determine which drivers to output; return an error if none found + driver_enumerator drivlist(m_options, gamename); + + unsigned correct = 0; + unsigned incorrect = 0; + unsigned notfound = 0; + unsigned matched = 0; + + // iterate over drivers + media_auditor auditor(drivlist); + util::ovectorstream summary_string; + while (drivlist.next()) + { + matched++; + + // audit the samples in this set + media_auditor::summary summary = auditor.audit_samples(); + + auto const clone_of = drivlist.clone(); + print_summary( + auditor, summary, false, + "sample", drivlist.driver().name, (clone_of >= 0) ? drivlist.driver(clone_of).name : nullptr, + correct, incorrect, notfound, + summary_string); + } + + // clear out any cached files + util::archive_file::cache_clear(); + + // return an error if none found + if (matched == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + + // if we didn't get anything at all, display a generic end message + if (matched > 0 && correct == 0 && incorrect == 0) + { + if (notfound > 0) + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "sampleset \"%s\" not found!\n", gamename); + else + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "sampleset \"%s\" not required!\n", gamename); + } + + // otherwise, print a summary + else + { + if (incorrect > 0) + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "%u samplesets found, %u were OK.\n", correct + incorrect, correct); + osd_printf_info("%u samplesets found, %u were OK.\n", correct, correct); + } +} + +const char cli_frontend::s_softlist_xml_dtd[] = + "\n" \ + "\n" \ + "\t\n" \ + "\t\t\n" \ + "\t\t\n" \ + "\t\t\n" \ + "\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "\t\t\t\t\t\t\n" \ + "]>\n\n"; + +void cli_frontend::output_single_softlist(std::ostream &out, software_list_device &swlistdev) +{ + util::stream_format(out, "\t\n", swlistdev.list_name(), util::xml::normalize_string(swlistdev.description().c_str())); + for (const software_info &swinfo : swlistdev.get_info()) + { + util::stream_format(out, "\t\t\n"; + util::stream_format(out, "\t\t\t%s\n", util::xml::normalize_string(swinfo.longname().c_str())); + util::stream_format(out, "\t\t\t%s\n", util::xml::normalize_string(swinfo.year().c_str())); + util::stream_format(out, "\t\t\t%s\n", util::xml::normalize_string(swinfo.publisher().c_str())); + + for (const auto &flist : swinfo.info()) + util::stream_format(out, "\t\t\t\n", flist.name(), util::xml::normalize_string(flist.value().c_str())); + + for (const auto &flist : swinfo.shared_features()) + util::stream_format(out, "\t\t\t\n", flist.name(), util::xml::normalize_string(flist.value().c_str())); + + for (const software_part &part : swinfo.parts()) + { + util::stream_format(out, "\t\t\t\n"; + + for (const auto &flist : part.features()) + util::stream_format(out, "\t\t\t\t\n", flist.name(), util::xml::normalize_string(flist.value().c_str())); + + // TODO: display ROM region information + for (const rom_entry *region = part.romdata().data(); region; region = rom_next_region(region)) + { + int is_disk = ROMREGION_ISDISKDATA(region); + + if (!is_disk) + util::stream_format(out, "\t\t\t\t\n", util::xml::normalize_string(region->name().c_str()), region->get_length()); + else + util::stream_format(out, "\t\t\t\t\n", util::xml::normalize_string(region->name().c_str())); + + for (const rom_entry *rom = rom_first_file(region); rom && !ROMENTRY_ISREGIONEND(rom); rom++) + { + if (ROMENTRY_ISFILE(rom)) + { + if (!is_disk) + util::stream_format(out, "\t\t\t\t\thashdata()); + if (!hashes.flag(util::hash_collection::FLAG_NO_DUMP)) + util::stream_format(out, " %s", hashes.attribute_string()); + else + out << " status=\"nodump\""; + + if (is_disk) + util::stream_format(out, " writeable=\"%s\"", (ROM_GETFLAGS(rom) & DISK_READONLYMASK) ? "no" : "yes"); + + if ((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_SKIP(1)) + out << " loadflag=\"load16_byte\""; + + if ((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_SKIP(3)) + out << " loadflag=\"load32_byte\""; + + if (((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_SKIP(2)) && ((ROM_GETFLAGS(rom) & ROM_GROUPMASK) == ROM_GROUPWORD)) + { + if (!(ROM_GETFLAGS(rom) & ROM_REVERSEMASK)) + out << " loadflag=\"load32_word\""; + else + out << " loadflag=\"load32_word_swap\""; + } + + if (((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_SKIP(6)) && ((ROM_GETFLAGS(rom) & ROM_GROUPMASK) == ROM_GROUPWORD)) + { + if (!(ROM_GETFLAGS(rom) & ROM_REVERSEMASK)) + out << " loadflag=\"load64_word\""; + else + out << " loadflag=\"load64_word_swap\""; + } + + if (((ROM_GETFLAGS(rom) & ROM_SKIPMASK) == ROM_NOSKIP) && ((ROM_GETFLAGS(rom) & ROM_GROUPMASK) == ROM_GROUPWORD)) + { + if (!(ROM_GETFLAGS(rom) & ROM_REVERSEMASK)) + out << " loadflag=\"load32_dword\""; + else + out << " loadflag=\"load16_word_swap\""; + } + + out << "/>\n"; + } + else if (ROMENTRY_ISRELOAD(rom)) + { + util::stream_format(out, "\t\t\t\t\t\n", ROM_GETLENGTH(rom), ROM_GETOFFSET(rom)); + } + else if (ROMENTRY_ISFILL(rom)) + { + util::stream_format(out, "\t\t\t\t\t\n", ROM_GETLENGTH(rom), ROM_GETOFFSET(rom)); + } + } + + if (!is_disk) + out << "\t\t\t\t\n"; + else + out << "\t\t\t\t\n"; + } + + out << "\t\t\t\n"; + } + + out << "\t\t\n"; + } + out << "\t\n"; +} + + +/*------------------------------------------------- + info_listsoftware - output the list of + software supported by a given game or set of + games + TODO: Add all information read from the source files +-------------------------------------------------*/ + +void cli_frontend::listsoftware(const std::vector &args) +{ + std::unordered_set list_map; + bool firstlist(true); + apply_device_action( + args, + [this, &list_map, &firstlist] (device_t &root, char const *type, bool first) + { + for (software_list_device &swlistdev : software_list_device_enumerator(root)) + { + if (list_map.insert(swlistdev.list_name()).second) + { + if (!swlistdev.get_info().empty()) + { + if (firstlist) + { + if (m_options.bool_value(CLIOPTION_DTD)) + std::cout << s_softlist_xml_dtd; + std::cout << "\n"; + firstlist = false; + } + output_single_softlist(std::cout, swlistdev); + } + } + } + }); + + if (!firstlist) + std::cout << "\n"; + else + fprintf(stdout, "No software lists found for this system\n"); // TODO: should this go to stderr instead? +} + + +/*------------------------------------------------- + verifysoftware - verify ROMs from the software + list of the specified driver(s) +-------------------------------------------------*/ +void cli_frontend::verifysoftware(const std::vector &args) +{ + const char *gamename = args.empty() ? "*" : args[0].c_str(); + + std::unordered_set list_map; + + unsigned correct = 0; + unsigned incorrect = 0; + unsigned notfound = 0; + unsigned matched = 0; + unsigned nrlists = 0; + + // determine which drivers to process; return an error if none found + driver_enumerator drivlist(m_options, gamename); + if (drivlist.count() == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + + media_auditor auditor(drivlist); + util::ovectorstream summary_string; + while (drivlist.next()) + { + matched++; + + for (software_list_device &swlistdev : software_list_device_enumerator(drivlist.config()->root_device())) + { + if (swlistdev.is_original()) + { + if (list_map.insert(swlistdev.list_name()).second) + { + if (!swlistdev.get_info().empty()) + { + nrlists++; + for (const software_info &swinfo : swlistdev.get_info()) + { + media_auditor::summary summary = auditor.audit_software(swlistdev, swinfo, AUDIT_VALIDATE_FAST); + + print_summary( + auditor, summary, false, + "rom", util::string_format("%s:%s", swlistdev.list_name(), swinfo.shortname()).c_str(), nullptr, + correct, incorrect, notfound, + summary_string); + } + } + } + } + } + } + + // clear out any cached files + util::archive_file::cache_clear(); + + // return an error if none found + if (matched == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename); + + // if we didn't get anything at all, display a generic end message + if (matched > 0 && correct == 0 && incorrect == 0) + { + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "romset \"%s\" has no software entries defined!\n", gamename); + } + // otherwise, print a summary + else + { + if (incorrect > 0) + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "%u romsets found in %u software lists, %u were OK.\n", correct + incorrect, nrlists, correct); + osd_printf_info("%u romsets found in %u software lists, %u romsets were OK.\n", correct, nrlists, correct); + } + +} + + +/*------------------------------------------------- + getsoftlist - retrieve software list by name +-------------------------------------------------*/ + +void cli_frontend::getsoftlist(const std::vector &args) +{ + const char *gamename = args.empty() ? "*" : args[0].c_str(); + + std::unordered_set list_map; + bool firstlist(true); + apply_device_action( + std::vector(), + [this, gamename, &list_map, &firstlist] (device_t &root, char const *type, bool first) + { + for (software_list_device &swlistdev : software_list_device_enumerator(root)) + { + if (core_strwildcmp(gamename, swlistdev.list_name().c_str()) == 0 && list_map.insert(swlistdev.list_name()).second) + { + if (!swlistdev.get_info().empty()) + { + if (firstlist) + { + if (m_options.bool_value(CLIOPTION_DTD)) + std::cout << s_softlist_xml_dtd; + std::cout << "\n"; + firstlist = false; + } + output_single_softlist(std::cout, swlistdev); + } + } + } + }); + + if (!firstlist) + std::cout << "\n"; + else + fprintf(stdout, "No such software lists found\n"); // TODO: should this go to stderr instead? +} + + +/*------------------------------------------------- + verifysoftlist - verify software list by name +-------------------------------------------------*/ +void cli_frontend::verifysoftlist(const std::vector &args) +{ + const char *gamename = args.empty() ? "*" : args[0].c_str(); + + std::unordered_set list_map; + unsigned correct = 0; + unsigned incorrect = 0; + unsigned notfound = 0; + unsigned matched = 0; + + driver_enumerator drivlist(m_options); + media_auditor auditor(drivlist); + util::ovectorstream summary_string; + + while (drivlist.next()) + { + for (software_list_device &swlistdev : software_list_device_enumerator(drivlist.config()->root_device())) + { + if (core_strwildcmp(gamename, swlistdev.list_name().c_str()) == 0 && list_map.insert(swlistdev.list_name()).second) + { + if (!swlistdev.get_info().empty()) + { + matched++; + + // Get the actual software list contents + for (const software_info &swinfo : swlistdev.get_info()) + { + media_auditor::summary summary = auditor.audit_software(swlistdev, swinfo, AUDIT_VALIDATE_FAST); + + print_summary( + auditor, summary, false, + "rom", util::string_format("%s:%s", swlistdev.list_name(), swinfo.shortname()).c_str(), nullptr, + correct, incorrect, notfound, + summary_string); + } + } + } + } + } + + // clear out any cached files + util::archive_file::cache_clear(); + + // return an error if none found + if (matched == 0) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching software lists found for '%s'", gamename); + + // if we didn't get anything at all, display a generic end message + if (matched > 0 && correct == 0 && incorrect == 0) + { + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "no romsets found for software list \"%s\"!\n", gamename); + } + // otherwise, print a summary + else + { + if (incorrect > 0) + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "%u romsets found in %u software lists, %u were OK.\n", correct + incorrect, matched, correct); + osd_printf_info("%u romsets found in %u software lists, %u romsets were OK.\n", correct, matched, correct); + } +} + + +//------------------------------------------------- +// version - emit MAME version to stdout +//------------------------------------------------- + +void cli_frontend::version(const std::vector &args) +{ + osd_printf_info("%s", emulator_info::get_build_version()); +} + + +//------------------------------------------------- +// romident - identify ROMs by looking for +// matches in our internal database +//------------------------------------------------- + +void cli_frontend::romident(const std::vector &args) +{ + const char *filename = args[0].c_str(); + + // create our own copy of options for the purposes of ROM identification + // so we are not "polluted" with driver-specific slot/image options + emu_options options; + options.set_value(OPTION_HASHPATH, m_options.hash_path(), OPTION_PRIORITY_DEFAULT); + + media_identifier ident(options); + + // identify the file, then output results + osd_printf_info("Identifying %s....\n", filename); + ident.identify(filename); + + // return the appropriate error code + if (ident.total() == 0) + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "No files found.\n"); + else if (ident.matches() == ident.total()) + return; + else if (ident.matches() == ident.total() - ident.nonroms()) + throw emu_fatalerror(EMU_ERR_IDENT_NONROMS, "Out of %d files, %d matched, %d are not roms.\n", ident.total(), ident.matches(), ident.nonroms()); + else if (ident.matches() > 0) + throw emu_fatalerror(EMU_ERR_IDENT_PARTIAL, "Out of %d files, %d matched, %d did not match.\n", ident.total(), ident.matches(), ident.total() - ident.matches()); + else + throw emu_fatalerror(EMU_ERR_IDENT_NONE, "No roms matched.\n"); +} + + +//------------------------------------------------- +// apply_action - apply action to matching +// systems/devices +//------------------------------------------------- + +template void cli_frontend::apply_action(const std::vector &args, T &&drvact, U &&devact) + +{ + bool const iswild((1U != args.size()) || core_iswildstr(args[0].c_str())); + std::vector matched(args.size(), false); + auto const included = [&args, &matched] (char const *name) -> bool + { + if (args.empty()) + return true; + + bool result = false; + auto it = matched.begin(); + for (std::string const &pat : args) + { + if (!core_strwildcmp(pat.c_str(), name)) + { + result = true; + *it = true; + } + ++it; + } + return result; + }; + + // determine which drivers to output + driver_enumerator drivlist(m_options); + + // iterate through matches + bool first(true); + while (drivlist.next()) + { + if (included(drivlist.driver().name)) + { + drvact(drivlist, first); + first = false; + + // if it wasn't a wildcard, there can only be one + if (!iswild) + break; + } + } + + if (iswild || first) + { + for (device_type type : registered_device_types) + { + if (included(type.shortname())) + { + devact(type, first); + first = false; + + // if it wasn't a wildcard, there can only be one + if (!iswild) + break; + } + } + } + + // return an error if none found + auto it = matched.begin(); + for (std::string const &pat : args) + { + if (!*it) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", pat); + + ++it; + } +} + + +//------------------------------------------------- +// apply_device_action - apply action to matching +// systems/devices +//------------------------------------------------- + +template void cli_frontend::apply_device_action(const std::vector &args, T &&action) +{ + machine_config config(GAME_NAME(___empty), m_options); + machine_config::token const tok(config.begin_configuration(config.root_device())); + apply_action( + args, + [&action] (driver_enumerator &drivlist, bool first) + { + action(drivlist.config()->root_device(), "driver", first); + }, + [&action, &config] (device_type type, bool first) + { + device_t *const dev = config.device_add("_tmp", type, 0); + action(*dev, "device", first); + config.device_remove("_tmp"); + }); +} + + +//------------------------------------------------- +// find_command +//------------------------------------------------- + +const cli_frontend::info_command_struct *cli_frontend::find_command(const std::string &s) +{ + static const info_command_struct s_info_commands[] = + { + { CLICOMMAND_LISTXML, 0, -1, &cli_frontend::listxml, "[pattern] ..." }, + { CLICOMMAND_LISTFULL, 0, -1, &cli_frontend::listfull, "[pattern] ..." }, + { CLICOMMAND_LISTSOURCE, 0, -1, &cli_frontend::listsource, "[system name]" }, + { CLICOMMAND_LISTCLONES, 0, 1, &cli_frontend::listclones, "[system name]" }, + { CLICOMMAND_LISTBROTHERS, 0, 1, &cli_frontend::listbrothers, "[system name]" }, + { CLICOMMAND_LISTCRC, 0, -1, &cli_frontend::listcrc, "[system name]" }, + { CLICOMMAND_LISTDEVICES, 0, 1, &cli_frontend::listdevices, "[system name]" }, + { CLICOMMAND_LISTSLOTS, 0, 1, &cli_frontend::listslots, "[system name]" }, + { CLICOMMAND_LISTROMS, 0, -1, &cli_frontend::listroms, "[pattern] ..." }, + { CLICOMMAND_LISTSAMPLES, 0, 1, &cli_frontend::listsamples, "[system name]" }, + { CLICOMMAND_VERIFYROMS, 0, -1, &cli_frontend::verifyroms, "[pattern] ..." }, + { CLICOMMAND_VERIFYSAMPLES, 0, 1, &cli_frontend::verifysamples, "[system name|*]" }, + { CLICOMMAND_LISTMEDIA, 0, 1, &cli_frontend::listmedia, "[system name]" }, + { CLICOMMAND_LISTSOFTWARE, 0, 1, &cli_frontend::listsoftware, "[system name]" }, + { CLICOMMAND_VERIFYSOFTWARE, 0, 1, &cli_frontend::verifysoftware, "[system name|*]" }, + { CLICOMMAND_ROMIDENT, 1, 1, &cli_frontend::romident, "(file or directory path)" }, + { CLICOMMAND_GETSOFTLIST, 0, 1, &cli_frontend::getsoftlist, "[system name|*]" }, + { CLICOMMAND_VERIFYSOFTLIST, 0, 1, &cli_frontend::verifysoftlist, "[system name|*]" }, + { CLICOMMAND_VERSION, 0, 0, &cli_frontend::version, "" } + }; + + for (const auto &info_command : s_info_commands) + { + if (s == info_command.option) + return &info_command; + } + return nullptr; +} + + +//------------------------------------------------- +// execute_commands - execute various frontend +// commands +//------------------------------------------------- + +void cli_frontend::execute_commands(std::string_view exename) +{ + // help? + if (m_options.command() == CLICOMMAND_HELP) + { + display_help(exename); + return; + } + + // showusage? + if (m_options.command() == CLICOMMAND_SHOWUSAGE) + { + osd_printf_info("Usage: %s [machine] [media] [software] [options]",exename); + osd_printf_info("\n\nOptions:\n%s", m_options.output_help()); + return; + } + + // validate? + if (m_options.command() == CLICOMMAND_VALIDATE) + { + if (m_options.command_arguments().size() > 1) + { + osd_printf_error("Auxiliary verb -validate takes at most 1 argument\n"); + return; + } + validity_checker valid(m_options, false); + const char *sysname = m_options.command_arguments().empty() ? nullptr : m_options.command_arguments()[0].c_str(); + bool result = valid.check_all_matching(sysname); + if (!result) + throw emu_fatalerror(EMU_ERR_FAILED_VALIDITY, "Validity check failed (%d errors, %d warnings in total)\n", valid.errors(), valid.warnings()); + return; + } + + // other commands need the INIs parsed + std::ostringstream option_errors; + mame_options::parse_standard_inis(m_options,option_errors); + if (option_errors.tellp() > 0) + osd_printf_error("%s\n", option_errors.str()); + + // createconfig? + if (m_options.command() == CLICOMMAND_CREATECONFIG) + { + // attempt to open the output file + emu_file file(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (file.open(std::string(emulator_info::get_configname()) + ".ini")) + throw emu_fatalerror("Unable to create file %s.ini\n",emulator_info::get_configname()); + + // generate the updated INI + file.puts(m_options.output_ini()); + + ui_options ui_opts; + emu_file file_ui(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (file_ui.open("ui.ini")) + throw emu_fatalerror("Unable to create file ui.ini\n"); + + // generate the updated INI + file_ui.puts(ui_opts.output_ini()); + + plugin_options plugin_opts; + path_iterator iter(m_options.plugins_path()); + std::string pluginpath; + while (iter.next(pluginpath)) + { + osd_subst_env(pluginpath, pluginpath); + plugin_opts.scan_directory(pluginpath, true); + } + emu_file file_plugin(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (file_plugin.open("plugin.ini")) + throw emu_fatalerror("Unable to create file plugin.ini\n"); + + // generate the updated INI + file_plugin.puts(plugin_opts.output_ini()); + + return; + } + + // showconfig? + if (m_options.command() == CLICOMMAND_SHOWCONFIG) + { + // print the INI text + printf("%s\n", m_options.output_ini().c_str()); + return; + } + + // all other commands call out to one of the info_commands helpers; first + // find the command + const auto *info_command = find_command(m_options.command()); + if (info_command) + { + // validate argument count + const char *error_message = nullptr; + if (m_options.command_arguments().size() < info_command->min_args) + error_message = "Auxiliary verb -%s requires at least %d argument(s)\n"; + if ((info_command->max_args >= 0) && (m_options.command_arguments().size() > info_command->max_args)) + error_message = "Auxiliary verb -%s takes at most %d argument(s)\n"; + if (error_message) + { + osd_printf_info(error_message, info_command->option, info_command->max_args); + osd_printf_info("\n"); + osd_printf_info("Usage: %s -%s %s\n", exename, info_command->option, info_command->usage); + return; + } + + // invoke the auxiliary command! + (this->*info_command->function)(m_options.command_arguments()); + return; + } + + if (!m_osd.execute_command(m_options.command().c_str())) + // if we get here, we don't know what has been requested + throw emu_fatalerror(EMU_ERR_INVALID_CONFIG, "Unknown command '%s' specified", m_options.command()); +} + + +//------------------------------------------------- +// display_help - display help to standard +// output +//------------------------------------------------- + +void cli_frontend::display_help(std::string_view exename) +{ + osd_printf_info( + "%3$s v%2$s\n" + "%5$s\n" + "\n" + "This software reproduces, more or less faithfully, the behaviour of a wide range\n" + "of machines. But hardware is useless without software, so images of the ROMs and\n" + "other media which run on that hardware are also required.\n" + "\n" + "Usage: %1$s [machine] [media] [software] [options]\n" + "\n" + " %1$s -showusage for a list of options\n" + " %1$s -showconfig to show current configuration in %4$s.ini format\n" + " %1$s -listmedia for a full list of supported media\n" + " %1$s -createconfig to create a %4$s.ini file\n" + "\n" + "For usage instructions, please visit https://docs.mamedev.org/\n", + exename, + build_version, + emulator_info::get_appname(), + emulator_info::get_configname(), + emulator_info::get_copyright_info()); +} diff --git a/src/icludes/frontend/mame/clifront.h b/src/icludes/frontend/mame/clifront.h new file mode 100644 index 0000000..bc0ab3b --- /dev/null +++ b/src/icludes/frontend/mame/clifront.h @@ -0,0 +1,85 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + clifront.h + + Command-line interface frontend for MAME. + +***************************************************************************/ +#ifndef MAME_FRONTEND_CLIFRONT_H +#define MAME_FRONTEND_CLIFRONT_H + +#pragma once + +#include "emuopts.h" + +// don't include osd_interface in header files +class osd_interface; +class mame_machine_manager; + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// cli_frontend handles command-line processing and emulator execution +class cli_frontend +{ + static const char s_softlist_xml_dtd[]; + +public: + // construction/destruction + cli_frontend(emu_options &options, osd_interface &osd); + ~cli_frontend(); + + // execute based on the incoming argc/argv + int execute(std::vector &args); + +private: + struct info_command_struct + { + const char *option; + int min_args; + int max_args; + void (cli_frontend::*function)(const std::vector &args); + const char *usage; + }; + + // commands + void listxml(const std::vector &args); + void listfull(const std::vector &args); + void listsource(const std::vector &args); + void listclones(const std::vector &args); + void listbrothers(const std::vector &args); + void listcrc(const std::vector &args); + void listroms(const std::vector &args); + void listsamples(const std::vector &args); + void listdevices(const std::vector &args); + void listslots(const std::vector &args); + void listmedia(const std::vector &args); + void listsoftware(const std::vector &args); + void verifysoftware(const std::vector &args); + void verifyroms(const std::vector &args); + void verifysamples(const std::vector &args); + void romident(const std::vector &args); + void getsoftlist(const std::vector &args); + void verifysoftlist(const std::vector &args); + void version(const std::vector &args); + + // internal helpers + template void apply_action(const std::vector &args, T &&drvact, U &&devact); + template void apply_device_action(const std::vector &args, T &&action); + void execute_commands(std::string_view exename); + void display_help(std::string_view exename); + void output_single_softlist(std::ostream &out, software_list_device &swlist); + void start_execution(mame_machine_manager *manager, const std::vector &args); + static const info_command_struct *find_command(const std::string &s); + + // internal state + emu_options & m_options; + osd_interface & m_osd; + int m_result; +}; + +#endif // MAME_FRONTEND_CLIFRONT_H diff --git a/src/icludes/frontend/mame/infoxml.cpp b/src/icludes/frontend/mame/infoxml.cpp new file mode 100644 index 0000000..6adb5f5 --- /dev/null +++ b/src/icludes/frontend/mame/infoxml.cpp @@ -0,0 +1,2233 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles,Paul Priest +/*************************************************************************** + + info.cpp + + Dumps the MAME internal data as an XML file. + +***************************************************************************/ + +#include "emu.h" +#include "infoxml.h" + +#include "mameopts.h" + +#include "machine/ram.h" +#include "sound/samples.h" + +#include "config.h" +#include "drivenum.h" +#include "romload.h" +#include "screen.h" +#include "softlist_dev.h" +#include "speaker.h" + +#include "corestr.h" +#include "xmlfile.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define XML_ROOT "mame" +#define XML_TOP "machine" + + +namespace { + +//************************************************************************** +// ANONYMOUS NAMESPACE PROTOTYPES +//************************************************************************** + +class device_type_compare +{ +public: + bool operator()(const std::add_pointer_t &lhs, const std::add_pointer_t &rhs) const; +}; + + +class device_filter +{ +public: + device_filter(const std::function &callback) + : m_callback(callback) + , m_done(false) + { + } + + // methods + bool filter(const char *shortname); + + // accessors + bool done() const { return m_done; } + +private: + const std::function & m_callback; + bool m_done; +}; + + +class filtered_driver_enumerator +{ +public: + filtered_driver_enumerator(driver_enumerator &drivlist, device_filter &devfilter) + : m_drivlist(drivlist) + , m_devfilter(devfilter) + , m_done(false) + { + } + + // methods + std::vector> next(int count); + + // accessors + bool done() const { return m_done || m_devfilter.done(); } + +private: + driver_enumerator & m_drivlist; + device_filter & m_devfilter; + bool m_done; +}; + + +typedef std::set, device_type_compare> device_type_set; + +std::string normalize_string(const char *string); +std::string normalize_string(std::string_view string); + +// internal helper +void output_header(std::ostream &out, bool dtd); +void output_footer(std::ostream &out); + +void output_one(std::ostream &out, driver_enumerator &drivlist, const game_driver &driver, device_type_set *devtypes); +void output_sampleof(std::ostream &out, device_t &device); +void output_bios(std::ostream &out, device_t const &device); +void output_rom(std::ostream &out, machine_config &config, driver_list const *drivlist, const game_driver *driver, device_t &device); +void output_device_refs(std::ostream &out, device_t &root); +void output_sample(std::ostream &out, device_t &device); +void output_chips(std::ostream &out, device_t &device, const char *root_tag); +void output_display(std::ostream &out, device_t &device, machine_flags::type const *flags, const char *root_tag); +void output_sound(std::ostream &out, device_t &device); +void output_ioport_condition(std::ostream &out, const ioport_condition &condition, unsigned indent); +void output_input(std::ostream &out, const ioport_list &portlist); +void output_switches(std::ostream &out, const ioport_list &portlist, const char *root_tag, int type, const char *outertag, const char *loctag, const char *innertag); +void output_ports(std::ostream &out, const ioport_list &portlist); +void output_adjusters(std::ostream &out, const ioport_list &portlist); +void output_driver(std::ostream &out, game_driver const &driver, device_t::feature_type unemulated, device_t::feature_type imperfect); +void output_features(std::ostream &out, device_type type, device_t::feature_type unemulated, device_t::feature_type imperfect); +void output_images(std::ostream &out, device_t &device, const char *root_tag); +void output_slots(std::ostream &out, machine_config &config, device_t &device, const char *root_tag, device_type_set *devtypes); +void output_software_lists(std::ostream &out, device_t &root, const char *root_tag); +void output_ramoptions(std::ostream &out, device_t &root); + +void output_one_device(std::ostream &out, machine_config &config, device_t &device, const char *devtag); +void output_devices(std::ostream &out, emu_options &lookup_options, device_type_set const *filter); + +char const *get_merge_name(driver_list const &drivlist, game_driver const &driver, util::hash_collection const &romhashes); +char const *get_merge_name(machine_config &config, device_t const &device, util::hash_collection const &romhashes); +char const *get_merge_name(tiny_rom_entry const *roms, util::hash_collection const &romhashes); + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// DTD string describing the data +constexpr char f_dtd_string[] = + "\n" + "\t\n" + "\t\n" + "\t\n" + "\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "]>"; + + +// XML feature names +constexpr std::pair f_feature_names[] = { + { device_t::feature::PROTECTION, "protection" }, + { device_t::feature::TIMING, "timing" }, + { device_t::feature::GRAPHICS, "graphics" }, + { device_t::feature::PALETTE, "palette" }, + { device_t::feature::SOUND, "sound" }, + { device_t::feature::CAPTURE, "capture" }, + { device_t::feature::CAMERA, "camera" }, + { device_t::feature::MICROPHONE, "microphone" }, + { device_t::feature::CONTROLS, "controls" }, + { device_t::feature::KEYBOARD, "keyboard" }, + { device_t::feature::MOUSE, "mouse" }, + { device_t::feature::MEDIA, "media" }, + { device_t::feature::DISK, "disk" }, + { device_t::feature::PRINTER, "printer" }, + { device_t::feature::TAPE, "tape" }, + { device_t::feature::PUNCH, "punch" }, + { device_t::feature::DRUM, "drum" }, + { device_t::feature::ROM, "rom" }, + { device_t::feature::COMMS, "comms" }, + { device_t::feature::LAN, "lan" }, + { device_t::feature::WAN, "wan" } }; + +} // anonymous namespace + + +//************************************************************************** +// INFO XML CREATOR +//************************************************************************** + + +//------------------------------------------------- +// get_feature_name - get XML name for feature +//------------------------------------------------- + +char const *info_xml_creator::feature_name(device_t::feature_type feature) +{ + auto const found = std::lower_bound( + std::begin(f_feature_names), + std::end(f_feature_names), + std::underlying_type_t(feature), + [] (auto const &a, auto const &b) + { + return std::underlying_type_t(a.first) < b; + }); + return ((std::end(f_feature_names) != found) && (found->first == feature)) ? found->second : nullptr; +} + + +//------------------------------------------------- +// info_xml_creator - constructor +//------------------------------------------------- + +info_xml_creator::info_xml_creator(emu_options const &options, bool dtd) + : m_dtd(dtd) +{ +} + + +//------------------------------------------------- +// output - print the XML information for all +// known machines matching a pattern +//------------------------------------------------- + +void info_xml_creator::output(std::ostream &out, const std::vector &patterns) +{ + if (patterns.empty()) + { + // no patterns specified - show everything + output(out); + } + else + { + // patterns specified - we have to filter output + std::vector matched(patterns.size(), false); + size_t exact_matches = 0; + const auto filter = [&patterns, &matched, &exact_matches](const char *shortname, bool &done) -> bool + { + bool result = false; + auto it = matched.begin(); + for (const std::string &pat : patterns) + { + if (!core_strwildcmp(pat.c_str(), shortname)) + { + // this driver matches the pattern - tell the caller + result = true; + + // did we see this particular pattern before? if not, track that we have + if (!*it) + { + *it = true; + if (!core_iswildstr(pat.c_str())) + { + exact_matches++; + + // stop looking if we found everything specified + if (exact_matches == patterns.size()) + done = true; + } + } + } + it++; + } + return result; + }; + output(out, filter); + + // throw an error if there were unmatched patterns + auto iter = std::find(matched.begin(), matched.end(), false); + if (iter != matched.end()) + { + int index = iter - matched.begin(); + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching machines found for '%s'", patterns[index]); + } + } +} + + +//------------------------------------------------- +// output - print the XML information for all +// known (and filtered) machines +//------------------------------------------------- + +void info_xml_creator::output(std::ostream &out, const std::function &filter, bool include_devices) +{ + struct prepared_info + { + prepared_info() = default; + prepared_info(const prepared_info &) = delete; + prepared_info(prepared_info &&) = default; + prepared_info &operator=(const prepared_info &) = delete; + + std::string m_xml_snippet; + device_type_set m_dev_set; + }; + + // TODO: maybe not the best place for this as it affects the stream passed in + // if the device part is threaded, the local streams used by the tasks can be + // imbued and the stream passed in can be left alone + out.imbue(std::locale::classic()); + + // prepare a driver enumerator and the queue + driver_enumerator drivlist(m_lookup_options); + device_filter devfilter(filter); + filtered_driver_enumerator filtered_drivlist(drivlist, devfilter); + bool header_outputted = false; + + // essentially a local method to emit the header if necessary + auto output_header_if_necessary = [this, &header_outputted](std::ostream &out) + { + if (!header_outputted) + { + output_header(out, m_dtd); + header_outputted = true; + } + }; + + // only keep a device set when we're asked to track it + std::optional devset; + if (include_devices && filter) + devset.emplace(); + + // prepare a queue of tasks - this is a FIFO queue because of the + // need to be deterministic + std::queue> tasks; + + // while we want to be deterministic, asynchronous task scheduling is not; so we want to + // track the amount of active tasks so that we can keep on spawning tasks even if we're + // waiting on the task in the front of the queue + std::atomic active_task_count = 0; + unsigned int maximum_active_task_count = std::thread::hardware_concurrency() + 10; + unsigned int maximum_outstanding_task_count = maximum_active_task_count + 20; + + // loop until we're done enumerating drivers, and until there are no outstanding tasks + while (!filtered_drivlist.done() || !tasks.empty()) + { + // loop until there are as many outstanding tasks as possible (we want to separately cap outstanding + // tasks and active tasks) + while (!filtered_drivlist.done() + && active_task_count < maximum_active_task_count + && tasks.size() < maximum_outstanding_task_count) + { + // we want to launch a task; grab a packet of drivers to process + std::vector> drivers = filtered_drivlist.next(20); + if (drivers.empty()) + break; + + // do the dirty work asychronously + auto task_proc = [&drivlist, drivers{ std::move(drivers) }, include_devices, &active_task_count] + { + prepared_info result; + std::ostringstream stream; + stream.imbue(std::locale::classic()); + + // output each of the drivers + for (const game_driver &driver : drivers) + output_one(stream, drivlist, driver, include_devices ? &result.m_dev_set : nullptr); + + // capture the XML snippet + result.m_xml_snippet = stream.str(); + + // we're done with the task; decrement the counter and return + active_task_count--; + return result; + }; + + // add this task to the queue + active_task_count++; + tasks.emplace(std::async(std::launch::async, std::move(task_proc))); + } + + // we've put as many outstanding tasks out as we can; are there any tasks outstanding? + if (!tasks.empty()) + { + // wait for the task at the front of the queue to complete and get the info, in the + // spirit of determinism + prepared_info pi = tasks.front().get(); + tasks.pop(); + + // emit whatever XML we accumulated in the task + output_header_if_necessary(out); + out << pi.m_xml_snippet; + + // merge devices into devset, if appropriate + if (devset) + { + for (const auto &x : pi.m_dev_set) + devset->insert(x); + } + } + } + + // iterate through the device types if not everything matches a driver + if (devset && !devfilter.done()) + { + for (device_type type : registered_device_types) + { + if (devfilter.filter(type.shortname())) + devset->insert(&type); + + if (devfilter.done()) + break; + } + } + + // output devices (both devices with roms and slot devices) + if (include_devices && (!devset || !devset->empty())) + { + output_header_if_necessary(out); + output_devices(out, m_lookup_options, devset ? &*devset : nullptr); + } + + if (header_outputted) + output_footer(out); +} + + +//************************************************************************** +// ANONYMOUS NAMESPACE IMPLEMENTATION +//************************************************************************** + +namespace +{ + +//------------------------------------------------- +// normalize_string +//------------------------------------------------- + +std::string normalize_string(const char *string) +{ + if (string) + return normalize_string(std::string_view(string)); + else + return std::string(); +} + +std::string normalize_string(std::string_view string) +{ + std::string result; + result.reserve(string.length()); + + for (char ch : string) + { + switch (ch) + { + case '\"': result.append("""); break; + case '&': result.append("&"); break; + case '<': result.append("<"); break; + case '>': result.append(">"); break; + default: result.append(1, ch); break; + } + } + + return result; +} + + +//------------------------------------------------- +// device_filter::filter - apply the filter, if +// present +//------------------------------------------------- + +bool device_filter::filter(const char *shortname) +{ + return !m_done && (!m_callback || m_callback(shortname, m_done)); +} + + +//------------------------------------------------- +// filtered_driver_enumerator::next - take a number +// of game_drivers, while applying filters +//------------------------------------------------- + +std::vector> filtered_driver_enumerator::next(int count) +{ + std::vector> results; + while (!done() && results.size() < count) + { + if (!m_drivlist.next()) + { + // at this point we are done enumerating through drivlist and it is no + // longer safe to call next(), so record that we're done + m_done = true; + } + else if (m_devfilter.filter(m_drivlist.driver().name)) + { + const game_driver &driver(m_drivlist.driver()); + results.push_back(driver); + } + } + return results; +} + + +//------------------------------------------------- +// output_header - print the XML DTD and open +// the root element +//------------------------------------------------- + +void output_header(std::ostream &out, bool dtd) +{ + if (dtd) + { + // output the DTD + out << "\n"; + std::string dtd(f_dtd_string); + strreplace(dtd, "__XML_ROOT__", XML_ROOT); + strreplace(dtd, "__XML_TOP__", XML_TOP); + + out << dtd << "\n\n"; + } + + // top-level tag + util::stream_format(out, + "<%s build=\"%s\" debug=\"" +#ifdef MAME_DEBUG + "yes" +#else + "no" +#endif + "\" mameconfig=\"%d\">\n", + XML_ROOT, + normalize_string(emulator_info::get_build_version()), + configuration_manager::CONFIG_VERSION); +} + + +//------------------------------------------------- +// output_header - close the root element +//------------------------------------------------- + +void output_footer(std::ostream &out) +{ + // close the top level tag + util::stream_format(out, "\n", XML_ROOT); +} + + +//------------------------------------------------- +// output_one - print the XML information +// for one particular machine driver +//------------------------------------------------- + +void output_one(std::ostream &out, driver_enumerator &drivlist, const game_driver &driver, device_type_set *devtypes) +{ + machine_config config(driver, drivlist.options()); + device_enumerator iter(config.root_device()); + + // allocate input ports and build overall emulation status + ioport_list portlist; + std::string errors; + device_t::feature_type overall_unemulated(driver.type.unemulated_features()); + device_t::feature_type overall_imperfect(driver.type.imperfect_features()); + for (device_t &device : iter) + { + portlist.append(device, errors); + overall_unemulated |= device.type().unemulated_features(); + overall_imperfect |= device.type().imperfect_features(); + + if (devtypes && device.owner()) + devtypes->insert(&device.type()); + } + + // renumber player numbers for controller ports + int player_offset = 0; + // but treat keyboard count separately from players' number + int kbd_offset = 0; + for (device_t &device : iter) + { + int nplayers = 0; + bool new_kbd = false; + for (auto &port : portlist) + if (&port.second->device() == &device) + for (ioport_field &field : port.second->fields()) + if (field.type() >= IPT_START && field.type() < IPT_ANALOG_LAST) + { + if (field.type() == IPT_KEYBOARD) + { + if (!new_kbd) + new_kbd = true; + field.set_player(field.player() + kbd_offset); + } + else + { + nplayers = std::max(nplayers, field.player() + 1); + field.set_player(field.player() + player_offset); + } + } + player_offset += nplayers; + if (new_kbd) kbd_offset++; + } + + // print the header and the machine name + util::stream_format(out, "\t<%s name=\"%s\"", XML_TOP, normalize_string(driver.name)); + + // strip away any path information from the source_file and output it + const char *start = strrchr(driver.type.source(), '/'); + if (!start) + start = strrchr(driver.type.source(), '\\'); + start = start ? (start + 1) : driver.type.source(); + util::stream_format(out, " sourcefile=\"%s\"", normalize_string(start)); + + // append bios and runnable flags + if (driver.flags & machine_flags::IS_BIOS_ROOT) + out << " isbios=\"yes\""; + if (driver.flags & machine_flags::MECHANICAL) + out << " ismechanical=\"yes\""; + + // display clone information + int clone_of = drivlist.find(driver.parent); + if (clone_of != -1 && !(drivlist.driver(clone_of).flags & machine_flags::IS_BIOS_ROOT)) + util::stream_format(out, " cloneof=\"%s\"", normalize_string(drivlist.driver(clone_of).name)); + if (clone_of != -1) + util::stream_format(out, " romof=\"%s\"", normalize_string(drivlist.driver(clone_of).name)); + + // display sample information and close the game tag + output_sampleof(out, config.root_device()); + out << ">\n"; + + // output game description + if (driver.type.fullname() != nullptr) + util::stream_format(out, "\t\t%s\n", normalize_string(driver.type.fullname())); + + // print the year only if is a number or another allowed character (? or +) + if (driver.year && strspn(driver.year, "0123456789?+") == strlen(driver.year)) + util::stream_format(out, "\t\t%s\n", normalize_string(driver.year)); + + // print the manufacturer information + if (driver.manufacturer != nullptr) + util::stream_format(out, "\t\t%s\n", normalize_string(driver.manufacturer)); + + // now print various additional information + output_bios(out, config.root_device()); + output_rom(out, config, &drivlist, &driver, config.root_device()); + output_device_refs(out, config.root_device()); + output_sample(out, config.root_device()); + output_chips(out, config.root_device(), ""); + output_display(out, config.root_device(), &driver.flags, ""); + output_sound(out, config.root_device()); + output_input(out, portlist); + output_switches(out, portlist, "", IPT_DIPSWITCH, "dipswitch", "diplocation", "dipvalue"); + output_switches(out, portlist, "", IPT_CONFIG, "configuration", "conflocation", "confsetting"); + output_ports(out, portlist); + output_adjusters(out, portlist); + output_driver(out, driver, overall_unemulated, overall_imperfect); + output_features(out, driver.type, overall_unemulated, overall_imperfect); + output_images(out, config.root_device(), ""); + output_slots(out, config, config.root_device(), "", devtypes); + output_software_lists(out, config.root_device(), ""); + output_ramoptions(out, config.root_device()); + + // close the topmost tag + util::stream_format(out, "\t\n", XML_TOP); +} + + +//------------------------------------------------- +// output_one_device - print the XML info for +// a single device +//------------------------------------------------- + +void output_one_device(std::ostream &out, machine_config &config, device_t &device, const char *devtag) +{ + bool has_speaker = false, has_input = false; + // check if the device adds speakers to the system + sound_interface_enumerator snditer(device); + if (snditer.first() != nullptr) + has_speaker = true; + + // generate input list and build overall emulation status + ioport_list portlist; + std::string errors; + device_t::feature_type overall_unemulated(device.type().unemulated_features()); + device_t::feature_type overall_imperfect(device.type().imperfect_features()); + for (device_t &dev : device_enumerator(device)) + { + portlist.append(dev, errors); + overall_unemulated |= dev.type().unemulated_features(); + overall_imperfect |= dev.type().imperfect_features(); + } + + // check if the device adds player inputs (other than dsw and configs) to the system + for (auto &port : portlist) + for (ioport_field const &field : port.second->fields()) + if (field.type() >= IPT_START1 && field.type() < IPT_UI_FIRST) + { + has_input = true; + break; + } + + // start to output info + util::stream_format(out, "\t<%s name=\"%s\"", XML_TOP, normalize_string(device.shortname())); + std::string src(device.source()); + strreplace(src,"../", ""); + util::stream_format(out, " sourcefile=\"%s\" isdevice=\"yes\" runnable=\"no\"", normalize_string(src)); + auto const parent(device.type().parent_rom_device_type()); + if (parent) + util::stream_format(out, " romof=\"%s\"", normalize_string(parent->shortname())); + output_sampleof(out, device); + out << ">\n"; + util::stream_format(out, "\t\t%s\n", normalize_string(device.name())); + + output_bios(out, device); + output_rom(out, config, nullptr, nullptr, device); + output_device_refs(out, device); + + if (device.type().type() != typeid(samples_device)) // ignore samples_device itself + output_sample(out, device); + + output_chips(out, device, devtag); + output_display(out, device, nullptr, devtag); + if (has_speaker) + output_sound(out, device); + if (has_input) + output_input(out, portlist); + output_switches(out, portlist, devtag, IPT_DIPSWITCH, "dipswitch", "diplocation", "dipvalue"); + output_switches(out, portlist, devtag, IPT_CONFIG, "configuration", "conflocation", "confsetting"); + output_adjusters(out, portlist); + output_features(out, device.type(), overall_unemulated, overall_imperfect); + output_images(out, device, devtag); + output_slots(out, config, device, devtag, nullptr); + output_software_lists(out, device, devtag); + util::stream_format(out, "\t\n", XML_TOP); +} + + +//------------------------------------------------- +// output_devices - print the XML info for +// registered device types +//------------------------------------------------- + +void output_devices(std::ostream &out, emu_options &lookup_options, device_type_set const *filter) +{ + // get config for empty machine + machine_config config(GAME_NAME(___empty), lookup_options); + + auto const action = [&config, &out] (device_type type) + { + // add it at the root of the machine config + device_t *dev; + { + machine_config::token const tok(config.begin_configuration(config.root_device())); + dev = config.device_add("_tmp", type, 0); + } + + // notify this device and all its subdevices that they are now configured + for (device_t &device : device_enumerator(*dev)) + if (!device.configured()) + device.config_complete(); + + // print details and remove it + output_one_device(out, config, *dev, dev->tag()); + machine_config::token const tok(config.begin_configuration(config.root_device())); + config.device_remove("_tmp"); + }; + + // run through devices + if (filter) + { + for (std::add_pointer_t type : *filter) action(*type); + } + else + { + for (device_type type : registered_device_types) action(type); + } +} + + +//------------------------------------------------ +// output_device_refs - when a machine uses a +// subdevice, print a reference +//------------------------------------------------- + +void output_device_refs(std::ostream &out, device_t &root) +{ + for (device_t &device : device_enumerator(root)) + if (&device != &root) + util::stream_format(out, "\t\t\n", normalize_string(device.shortname())); +} + + +//------------------------------------------------ +// output_sampleof - print the 'sampleof' +// attribute, if appropriate +//------------------------------------------------- + +void output_sampleof(std::ostream &out, device_t &device) +{ + // iterate over sample devices + for (samples_device &samples : samples_device_enumerator(device)) + { + samples_iterator sampiter(samples); + if (sampiter.altbasename() != nullptr) + { + util::stream_format(out, " sampleof=\"%s\"", normalize_string(sampiter.altbasename())); + + // must stop here, as there can only be one attribute of the same name + return; + } + } +} + + +//------------------------------------------------- +// output_bios - print BIOS sets for a device +//------------------------------------------------- + +void output_bios(std::ostream &out, device_t const &device) +{ + // first determine the default BIOS name + char const *defaultname(nullptr); + for (tiny_rom_entry const *rom = device.rom_region(); rom && !ROMENTRY_ISEND(rom); ++rom) + { + if (ROMENTRY_ISDEFAULT_BIOS(rom)) + defaultname = rom->name; + } + + // iterate over ROM entries and look for BIOSes + for (romload::system_bios const &bios : romload::entries(device.rom_region()).get_system_bioses()) + { + // output extracted name and descriptions' + out << "\t\t\n"; + } +} + + +//------------------------------------------------- +// output_rom - print the roms section of +// the XML output +//------------------------------------------------- + +void output_rom(std::ostream &out, machine_config &config, driver_list const *drivlist, const game_driver *driver, device_t &device) +{ + enum class type { BIOS, NORMAL, DISK }; + std::map biosnames; + bool bios_scanned(false); + auto const get_biosname = + [&biosnames, &bios_scanned] (tiny_rom_entry const *rom) -> char const * + { + u32 const biosflags(ROM_GETBIOSFLAGS(rom)); + std::map::const_iterator const found(biosnames.find(biosflags)); + if (biosnames.end() != found) + return found->second; + + char const *result(nullptr); + if (!bios_scanned) + { + for (++rom; !ROMENTRY_ISEND(rom); ++rom) + { + if (ROMENTRY_ISSYSTEM_BIOS(rom)) + { + u32 const biosno(ROM_GETBIOSFLAGS(rom)); + biosnames.emplace(biosno, rom->name); + if (biosflags == biosno) + result = rom->name; + } + } + bios_scanned = true; + } + return result; + }; + auto const rom_file_size = // FIXME: need a common way to do this without the cost of allocating rom_entry + [] (tiny_rom_entry const *romp) -> u32 + { + u32 maxlength = 0; + + // loop until we run out of reloads + do + { + // loop until we run out of continues/ignores + u32 curlength(ROM_GETLENGTH(romp++)); + while (ROMENTRY_ISCONTINUE(romp) || ROMENTRY_ISIGNORE(romp)) + curlength += ROM_GETLENGTH(romp++); + + // track the maximum length + maxlength = (std::max)(maxlength, curlength); + } + while (ROMENTRY_ISRELOAD(romp)); + + return maxlength; + }; + + // iterate over 3 different ROM "types": BIOS, ROMs, DISKs + bool const driver_merge = drivlist && dynamic_cast(&device); + for (type pass : { type::BIOS, type::NORMAL, type::DISK }) + { + tiny_rom_entry const *region(nullptr); + for (tiny_rom_entry const *rom = device.rom_region(); rom && !ROMENTRY_ISEND(rom); ++rom) + { + if (ROMENTRY_ISREGION(rom)) + region = rom; + else if (ROMENTRY_ISSYSTEM_BIOS(rom)) + biosnames.emplace(ROM_GETBIOSFLAGS(rom), rom->name); + + if (!ROMENTRY_ISFILE(rom)) + continue; + + // only list disks on the disk pass + bool const is_disk = ROMREGION_ISDISKDATA(region); + if ((type::DISK == pass) != is_disk) + continue; + + // BIOS ROMs only apply to BIOSes + // FIXME: disk images associated with a system BIOS will never be listed + u32 const biosno(ROM_GETBIOSFLAGS(rom)); + if ((type::BIOS == pass) != bool(biosno)) + continue; + char const *const bios_name((!is_disk && biosno) ? get_biosname(rom) : nullptr); + + // if we have a valid ROM and we are a clone, see if we can find the parent ROM + util::hash_collection const hashes(rom->hashdata); + char const *const merge_name( + hashes.flag(util::hash_collection::FLAG_NO_DUMP) ? nullptr : + driver_merge ? get_merge_name(*drivlist, *driver, hashes) : + get_merge_name(config, device, hashes)); + + // opening tag + if (is_disk) + out << "\t\tname); + if (name && name[0]) + util::stream_format(out, " name=\"%s\"", normalize_string(name)); + if (merge_name) + util::stream_format(out, " merge=\"%s\"", normalize_string(merge_name)); + if (bios_name) + util::stream_format(out, " bios=\"%s\"", normalize_string(bios_name)); + if (!is_disk) + util::stream_format(out, " size=\"%u\"", rom_file_size(rom)); + + // dump checksum information only if there is a known dump + if (!hashes.flag(util::hash_collection::FLAG_NO_DUMP)) + out << ' ' << hashes.attribute_string(); // iterate over hash function types and print m_output their values + else + out << " status=\"nodump\""; + + // append a region name + util::stream_format(out, " region=\"%s\"", region->name); + + if (!is_disk) + { + // for non-disk entries, print offset + util::stream_format(out, " offset=\"%x\"", ROM_GETOFFSET(rom)); + } + else + { + // for disk entries, add the disk index + util::stream_format(out, " index=\"%x\" writable=\"%s\"", DISK_GETINDEX(rom), DISK_ISREADONLY(rom) ? "no" : "yes"); + } + + // add optional flag + if (ROM_ISOPTIONAL(rom)) + out << " optional=\"yes\""; + + out << "/>\n"; + } + bios_scanned = true; + } +} + + +//------------------------------------------------- +// output_sample - print a list of all +// samples referenced by a game_driver +//------------------------------------------------- + +void output_sample(std::ostream &out, device_t &device) +{ + // iterate over sample devices + for (samples_device &samples : samples_device_enumerator(device)) + { + samples_iterator iter(samples); + std::unordered_set already_printed; + for (const char *samplename = iter.first(); samplename != nullptr; samplename = iter.next()) + { + // filter out duplicates + if (!already_printed.insert(samplename).second) + continue; + + // output the sample name + util::stream_format(out, "\t\t\n", normalize_string(samplename)); + } + } +} + + +/*------------------------------------------------- + output_chips - print a list of CPU and + sound chips used by a game +-------------------------------------------------*/ + +void output_chips(std::ostream &out, device_t &device, const char *root_tag) +{ + // iterate over executable devices + for (device_execute_interface &exec : execute_interface_enumerator(device)) + { + if (strcmp(exec.device().tag(), device.tag())) + { + std::string newtag(exec.device().tag()), oldtag(":"); + newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); + + out << "\t\t\n"; + } + } + + // iterate over sound devices + for (device_sound_interface &sound : sound_interface_enumerator(device)) + { + if (strcmp(sound.device().tag(), device.tag()) != 0 && sound.issound()) + { + std::string newtag(sound.device().tag()), oldtag(":"); + newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); + + out << "\t\t\n"; + } + } +} + + +//------------------------------------------------- +// output_display - print a list of all the +// displays +//------------------------------------------------- + +void output_display(std::ostream &out, device_t &device, machine_flags::type const *flags, const char *root_tag) +{ + // iterate over screens + for (const screen_device &screendev : screen_device_enumerator(device)) + { + if (strcmp(screendev.tag(), device.tag())) + { + std::string newtag(screendev.tag()), oldtag(":"); + newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); + + util::stream_format(out, "\t\t\n"; + } + } +} + + +//------------------------------------------------- +// output_sound - print a list of all the +// speakers +//------------------------------------------------ + +void output_sound(std::ostream &out, device_t &device) +{ + speaker_device_enumerator spkiter(device); + int speakers = spkiter.count(); + + // if we have no sound, zero m_output the speaker count + sound_interface_enumerator snditer(device); + if (snditer.first() == nullptr) + speakers = 0; + + util::stream_format(out, "\t\t\n", speakers); +} + + +//------------------------------------------------- +// output_ioport_condition - print condition +// required to use I/O port field/setting +//------------------------------------------------- + +void output_ioport_condition(std::ostream &out, const ioport_condition &condition, unsigned indent) +{ + for (unsigned i = 0; indent > i; ++i) + out << '\t'; + + char const *rel(nullptr); + switch (condition.condition()) + { + case ioport_condition::ALWAYS: throw false; + case ioport_condition::EQUALS: rel = "eq"; break; + case ioport_condition::NOTEQUALS: rel = "ne"; break; + case ioport_condition::GREATERTHAN: rel = "gt"; break; + case ioport_condition::NOTGREATERTHAN: rel = "le"; break; + case ioport_condition::LESSTHAN: rel = "lt"; break; + case ioport_condition::NOTLESSTHAN: rel = "ge"; break; + } + + util::stream_format(out, "\n", normalize_string(condition.tag()), condition.mask(), rel, condition.value()); +} + +//------------------------------------------------- +// output_input - print a summary of a game's +// input +//------------------------------------------------- + +void output_input(std::ostream &out, const ioport_list &portlist) +{ + // enumerated list of control types + // NOTE: the order is chosen so that 'spare' button inputs are assigned to the + // most-likely-correct input device when info is output (you can think of it as + // a sort of likelihood order of having buttons) + enum + { + CTRL_DIGITAL_BUTTONS, + CTRL_DIGITAL_JOYSTICK, + CTRL_ANALOG_JOYSTICK, + CTRL_ANALOG_LIGHTGUN, + CTRL_ANALOG_DIAL, + CTRL_ANALOG_POSITIONAL, + CTRL_ANALOG_TRACKBALL, + CTRL_ANALOG_MOUSE, + CTRL_ANALOG_PADDLE, + CTRL_ANALOG_PEDAL, + CTRL_DIGITAL_KEYPAD, + CTRL_DIGITAL_KEYBOARD, + CTRL_DIGITAL_MAHJONG, + CTRL_DIGITAL_HANAFUDA, + CTRL_DIGITAL_GAMBLING, + CTRL_COUNT + }; + + enum + { + CTRL_P1, + CTRL_P2, + CTRL_P3, + CTRL_P4, + CTRL_P5, + CTRL_P6, + CTRL_P7, + CTRL_P8, + CTRL_P9, + CTRL_P10, + CTRL_PCOUNT + }; + + // directions + const uint8_t DIR_UP = 0x01; + const uint8_t DIR_DOWN = 0x02; + const uint8_t DIR_LEFT = 0x04; + const uint8_t DIR_RIGHT = 0x08; + + // initialize the list of control types + struct + { + const char * type; // general type of input + int player; // player which the input belongs to + int nbuttons; // total number of buttons + int reqbuttons; // total number of non-optional buttons + int maxbuttons; // max index of buttons (using IPT_BUTTONn) [probably to be removed soonish] + int ways; // directions for joystick + bool analog; // is analog input? + uint8_t helper[3]; // for dual joysticks [possibly to be removed soonish] + int32_t min; // analog minimum value + int32_t max; // analog maximum value + int32_t sensitivity; // default analog sensitivity + int32_t keydelta; // default analog keydelta + bool reverse; // default analog reverse setting + } control_info[CTRL_COUNT * CTRL_PCOUNT]; + + memset(&control_info, 0, sizeof(control_info)); + + // tracking info as we iterate + int nplayer = 0; + int ncoin = 0; + bool service = false; + bool tilt = false; + + // iterate over the ports + for (auto &port : portlist) + { + int ctrl_type = CTRL_DIGITAL_BUTTONS; + bool ctrl_analog = false; + for (ioport_field const &field : port.second->fields()) + { + // track the highest player number + if (nplayer < field.player() + 1) + nplayer = field.player() + 1; + + // switch off of the type + switch (field.type()) + { + // map joysticks + case IPT_JOYSTICK_UP: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[0] |= DIR_UP; + break; + case IPT_JOYSTICK_DOWN: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[0] |= DIR_DOWN; + break; + case IPT_JOYSTICK_LEFT: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[0] |= DIR_LEFT; + break; + case IPT_JOYSTICK_RIGHT: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[0] |= DIR_RIGHT; + break; + + case IPT_JOYSTICKLEFT_UP: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[1] |= DIR_UP; + break; + case IPT_JOYSTICKLEFT_DOWN: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[1] |= DIR_DOWN; + break; + case IPT_JOYSTICKLEFT_LEFT: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[1] |= DIR_LEFT; + break; + case IPT_JOYSTICKLEFT_RIGHT: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[1] |= DIR_RIGHT; + break; + + case IPT_JOYSTICKRIGHT_UP: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[2] |= DIR_UP; + break; + case IPT_JOYSTICKRIGHT_DOWN: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[2] |= DIR_DOWN; + break; + case IPT_JOYSTICKRIGHT_LEFT: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[2] |= DIR_LEFT; + break; + case IPT_JOYSTICKRIGHT_RIGHT: + ctrl_type = CTRL_DIGITAL_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); + control_info[field.player() * CTRL_COUNT + ctrl_type].helper[2] |= DIR_RIGHT; + break; + + // map analog inputs + case IPT_AD_STICK_X: + case IPT_AD_STICK_Y: + case IPT_AD_STICK_Z: + ctrl_analog = true; + ctrl_type = CTRL_ANALOG_JOYSTICK; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "stick"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = true; + break; + + case IPT_PADDLE: + case IPT_PADDLE_V: + ctrl_analog = true; + ctrl_type = CTRL_ANALOG_PADDLE; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "paddle"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = true; + break; + + case IPT_PEDAL: + case IPT_PEDAL2: + case IPT_PEDAL3: + ctrl_analog = true; + ctrl_type = CTRL_ANALOG_PEDAL; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "pedal"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = true; + break; + + case IPT_LIGHTGUN_X: + case IPT_LIGHTGUN_Y: + ctrl_analog = true; + ctrl_type = CTRL_ANALOG_LIGHTGUN; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "lightgun"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = true; + break; + + case IPT_POSITIONAL: + case IPT_POSITIONAL_V: + ctrl_analog = true; + ctrl_type = CTRL_ANALOG_POSITIONAL; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "positional"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = true; + break; + + case IPT_DIAL: + case IPT_DIAL_V: + ctrl_analog = true; + ctrl_type = CTRL_ANALOG_DIAL; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "dial"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = true; + break; + + case IPT_TRACKBALL_X: + case IPT_TRACKBALL_Y: + ctrl_analog = true; + ctrl_type = CTRL_ANALOG_TRACKBALL; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "trackball"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = true; + break; + + case IPT_MOUSE_X: + case IPT_MOUSE_Y: + ctrl_analog = true; + ctrl_type = CTRL_ANALOG_MOUSE; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "mouse"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = true; + break; + + // map buttons + case IPT_BUTTON1: + case IPT_BUTTON2: + case IPT_BUTTON3: + case IPT_BUTTON4: + case IPT_BUTTON5: + case IPT_BUTTON6: + case IPT_BUTTON7: + case IPT_BUTTON8: + case IPT_BUTTON9: + case IPT_BUTTON10: + case IPT_BUTTON11: + case IPT_BUTTON12: + case IPT_BUTTON13: + case IPT_BUTTON14: + case IPT_BUTTON15: + case IPT_BUTTON16: + ctrl_analog = false; + if (control_info[field.player() * CTRL_COUNT + ctrl_type].type == nullptr) + { + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "only_buttons"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].analog = false; + } + control_info[field.player() * CTRL_COUNT + ctrl_type].maxbuttons = std::max(control_info[field.player() * CTRL_COUNT + ctrl_type].maxbuttons, field.type() - IPT_BUTTON1 + 1); + control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; + if (!field.optional()) + control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; + break; + + // track maximum coin index + case IPT_COIN1: + case IPT_COIN2: + case IPT_COIN3: + case IPT_COIN4: + case IPT_COIN5: + case IPT_COIN6: + case IPT_COIN7: + case IPT_COIN8: + case IPT_COIN9: + case IPT_COIN10: + case IPT_COIN11: + case IPT_COIN12: + ncoin = std::max(ncoin, field.type() - IPT_COIN1 + 1); + break; + + // track presence of keypads and keyboards + case IPT_KEYPAD: + ctrl_type = CTRL_DIGITAL_KEYPAD; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "keypad"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; + if (!field.optional()) + control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; + break; + + case IPT_KEYBOARD: + ctrl_type = CTRL_DIGITAL_KEYBOARD; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "keyboard"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; + if (!field.optional()) + control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; + break; + + // additional types + case IPT_SERVICE: + service = true; + break; + + case IPT_TILT: + tilt = true; + break; + + default: + if (field.type() > IPT_MAHJONG_FIRST && field.type() < IPT_MAHJONG_LAST) + { + ctrl_type = CTRL_DIGITAL_MAHJONG; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "mahjong"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; + if (!field.optional()) + control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; + } + else if (field.type() > IPT_HANAFUDA_FIRST && field.type() < IPT_HANAFUDA_LAST) + { + ctrl_type = CTRL_DIGITAL_HANAFUDA; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "hanafuda"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; + if (!field.optional()) + control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; + } + else if (field.type() > IPT_GAMBLING_FIRST && field.type() < IPT_GAMBLING_LAST) + { + ctrl_type = CTRL_DIGITAL_GAMBLING; + control_info[field.player() * CTRL_COUNT + ctrl_type].type = "gambling"; + control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; + control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; + if (!field.optional()) + control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; + } + break; + } + + if (ctrl_analog) + { + // get the analog stats + if (field.minval() != 0) + control_info[field.player() * CTRL_COUNT + ctrl_type].min = field.minval(); + if (field.maxval() != 0) + control_info[field.player() * CTRL_COUNT + ctrl_type].max = field.maxval(); + if (field.sensitivity() != 0) + control_info[field.player() * CTRL_COUNT + ctrl_type].sensitivity = field.sensitivity(); + if (field.delta() != 0) + control_info[field.player() * CTRL_COUNT + ctrl_type].keydelta = field.delta(); + if (field.analog_reverse() != 0) + control_info[field.player() * CTRL_COUNT + ctrl_type].reverse = true; + } + } + } + + // Clean-up those entries, if any, where buttons were defined in a separate port than the actual controller they belong to. + // This is quite often the case, especially for arcades where controls can be easily mapped to separate input ports on PCB. + // If such situation would only happen for joystick, it would be possible to work it around by initializing differently + // ctrl_type above, but it is quite common among analog inputs as well (for instance, this is the tipical situation + // for lightguns) and therefore we really need this separate loop. + for (int i = 0; i < CTRL_PCOUNT; i++) + { + bool fix_done = false; + for (int j = 1; j < CTRL_COUNT; j++) + if (control_info[i * CTRL_COUNT].type != nullptr && control_info[i * CTRL_COUNT + j].type != nullptr && !fix_done) + { + control_info[i * CTRL_COUNT + j].nbuttons += control_info[i * CTRL_COUNT].nbuttons; + control_info[i * CTRL_COUNT + j].reqbuttons += control_info[i * CTRL_COUNT].reqbuttons; + control_info[i * CTRL_COUNT + j].maxbuttons = std::max(control_info[i * CTRL_COUNT + j].maxbuttons, control_info[i * CTRL_COUNT].maxbuttons); + + memset(&control_info[i * CTRL_COUNT], 0, sizeof(control_info[0])); + fix_done = true; + } + } + + // Output the input info + // First basic info + out << "\t\t\n"; + + // Then controller specific ones + for (auto & elem : control_info) + if (elem.type != nullptr) + { + //printf("type %s - player %d - buttons %d\n", elem.type, elem.player, elem.nbuttons); + if (elem.analog) + { + util::stream_format(out, "\t\t\t 1) + util::stream_format(out, " player=\"%d\"", elem.player); + if (elem.nbuttons > 0) + { + util::stream_format(out, " buttons=\"%d\"", strcmp(elem.type, "stick") ? elem.nbuttons : elem.maxbuttons); + if (elem.reqbuttons < elem.nbuttons) + util::stream_format(out, " reqbuttons=\"%d\"", elem.reqbuttons); + } + if (elem.min != 0 || elem.max != 0) + util::stream_format(out, " minimum=\"%d\" maximum=\"%d\"", elem.min, elem.max); + if (elem.sensitivity != 0) + util::stream_format(out, " sensitivity=\"%d\"", elem.sensitivity); + if (elem.keydelta != 0) + util::stream_format(out, " keydelta=\"%d\"", elem.keydelta); + if (elem.reverse) + out << " reverse=\"yes\""; + + out << "/>\n"; + } + else + { + if (elem.helper[1] == 0 && elem.helper[2] != 0) { elem.helper[1] = elem.helper[2]; elem.helper[2] = 0; } + if (elem.helper[0] == 0 && elem.helper[1] != 0) { elem.helper[0] = elem.helper[1]; elem.helper[1] = 0; } + if (elem.helper[1] == 0 && elem.helper[2] != 0) { elem.helper[1] = elem.helper[2]; elem.helper[2] = 0; } + const char *joys = (elem.helper[2] != 0) ? "triple" : (elem.helper[1] != 0) ? "double" : ""; + util::stream_format(out, "\t\t\t 1) + util::stream_format(out, " player=\"%d\"", elem.player); + if (elem.nbuttons > 0) + { + util::stream_format(out, " buttons=\"%d\"", strcmp(elem.type, "joy") ? elem.nbuttons : elem.maxbuttons); + if (elem.reqbuttons < elem.nbuttons) + util::stream_format(out, " reqbuttons=\"%d\"", elem.reqbuttons); + } + for (int lp = 0; lp < 3 && elem.helper[lp] != 0; lp++) + { + const char *plural = (lp==2) ? "3" : (lp==1) ? "2" : ""; + const char *ways; + std::string helper; + switch (elem.helper[lp] & (DIR_UP | DIR_DOWN | DIR_LEFT | DIR_RIGHT)) + { + case DIR_UP | DIR_DOWN | DIR_LEFT | DIR_RIGHT: + helper = util::string_format(std::locale::classic(), "%d", (elem.ways == 0) ? 8 : elem.ways); + ways = helper.c_str(); + break; + case DIR_LEFT | DIR_RIGHT: + ways = "2"; + break; + case DIR_UP | DIR_DOWN: + ways = "vertical2"; + break; + case DIR_UP: + case DIR_DOWN: + case DIR_LEFT: + case DIR_RIGHT: + ways = "1"; + break; + case DIR_UP | DIR_DOWN | DIR_LEFT: + case DIR_UP | DIR_DOWN | DIR_RIGHT: + case DIR_UP | DIR_LEFT | DIR_RIGHT: + case DIR_DOWN | DIR_LEFT | DIR_RIGHT: + ways = (elem.ways == 4) ? "3 (half4)" : "5 (half8)"; + break; + default: + ways = "strange2"; + break; + } + util::stream_format(out, " ways%s=\"%s\"", plural, ways); + } + out << "/>\n"; + } + } + + out << "\t\t\n"; +} + + +//------------------------------------------------- +// output_switches - print the configurations or +// DIP switch settings +//------------------------------------------------- + +void output_switches(std::ostream &out, const ioport_list &portlist, const char *root_tag, int type, const char *outertag, const char *loctag, const char *innertag) +{ + // iterate looking for DIP switches + for (auto &port : portlist) + for (ioport_field const &field : port.second->fields()) + if (field.type() == type) + { + std::string newtag(port.second->tag()), oldtag(":"); + newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); + + // output the switch name information + std::string const normalized_field_name(normalize_string(field.specific_name())); + std::string const normalized_newtag(normalize_string(newtag)); + util::stream_format(out, "\t\t<%s name=\"%s\" tag=\"%s\" mask=\"%u\">\n", outertag, normalized_field_name, normalized_newtag, field.mask()); + if (!field.condition().none()) + output_ioport_condition(out, field.condition(), 3); + + // loop over locations + for (ioport_diplocation const &diploc : field.diplocations()) + { + util::stream_format(out, "\t\t\t<%s name=\"%s\" number=\"%u\"", loctag, normalize_string(diploc.name()), diploc.number()); + if (diploc.inverted()) + out << " inverted=\"yes\""; + out << "/>\n"; + } + + // loop over settings + for (ioport_setting const &setting : field.settings()) + { + util::stream_format(out, "\t\t\t<%s name=\"%s\" value=\"%u\"", innertag, normalize_string(setting.name()), setting.value()); + if (setting.value() == field.defvalue()) + out << " default=\"yes\""; + if (setting.condition().none()) + { + out << "/>\n"; + } + else + { + out << ">\n"; + output_ioport_condition(out, setting.condition(), 4); + util::stream_format(out, "\t\t\t\n", innertag); + } + } + + // terminate the switch entry + util::stream_format(out, "\t\t\n", outertag); + } +} + +//------------------------------------------------- +// output_ports - print the structure of input +// ports in the driver +//------------------------------------------------- +void output_ports(std::ostream &out, const ioport_list &portlist) +{ + // cycle through ports + for (auto &port : portlist) + { + util::stream_format(out, "\t\t\n", normalize_string(port.second->tag())); + for (ioport_field const &field : port.second->fields()) + { + if (field.is_analog()) + util::stream_format(out, "\t\t\t\n", field.mask()); + } + util::stream_format(out, "\t\t\n"); + } + +} + +//------------------------------------------------- +// output_adjusters - print the Analog +// Adjusters for a game +//------------------------------------------------- + +void output_adjusters(std::ostream &out, const ioport_list &portlist) +{ + // iterate looking for Adjusters + for (auto &port : portlist) + for (ioport_field const &field : port.second->fields()) + if (field.type() == IPT_ADJUSTER) + { + util::stream_format(out, "\t\t\n", normalize_string(field.specific_name()), field.defvalue()); + } +} + + +//------------------------------------------------- +// output_driver - print driver status +//------------------------------------------------- + +void output_driver(std::ostream &out, game_driver const &driver, device_t::feature_type unemulated, device_t::feature_type imperfect) +{ + out << "\t\t\n"; +} + + +//------------------------------------------------- +// output_features - print emulation features of +// +//------------------------------------------------- + +void output_features(std::ostream &out, device_type type, device_t::feature_type unemulated, device_t::feature_type imperfect) +{ + device_t::feature_type const flags(type.unemulated_features() | type.imperfect_features() | unemulated | imperfect); + for (auto const &feature : f_feature_names) + { + if (flags & feature.first) + { + util::stream_format(out, "\t\t\n"; + } + } +} + + +//------------------------------------------------- +// output_images - prints m_output all info on +// image devices +//------------------------------------------------- + +void output_images(std::ostream &out, device_t &device, const char *root_tag) +{ + for (const device_image_interface &imagedev : image_interface_enumerator(device)) + { + if (strcmp(imagedev.device().tag(), device.tag())) + { + bool loadable = imagedev.user_loadable(); + std::string newtag(imagedev.device().tag()), oldtag(":"); + newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); + + // print m_output device type + util::stream_format(out, "\t\t\n"; + + if (loadable) + { + char const *const name = imagedev.instance_name().c_str(); + char const *const shortname = imagedev.brief_instance_name().c_str(); + + out << "\t\t\t\n"; + + char const *extensions(imagedev.file_extensions()); + while (extensions) + { + char const *end(extensions); + while (*end && (',' != *end)) + ++end; + util::stream_format(out, "\t\t\t\n", normalize_string(std::string_view(extensions, end - extensions))); + extensions = *end ? (end + 1) : nullptr; + } + } + out << "\t\t\n"; + } + } +} + + +//------------------------------------------------- +// output_slots - prints all info about slots +//------------------------------------------------- + +void output_slots(std::ostream &out, machine_config &config, device_t &device, const char *root_tag, device_type_set *devtypes) +{ + for (device_slot_interface &slot : slot_interface_enumerator(device)) + { + // shall we list fixed slots as non-configurable? + bool const listed(!slot.fixed() && strcmp(slot.device().tag(), device.tag())); + + if (devtypes || listed) + { + machine_config::token const tok(config.begin_configuration(slot.device())); + std::string newtag(slot.device().tag()), oldtag(":"); + newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); + + // print m_output device type + if (listed) + util::stream_format(out, "\t\t\n", normalize_string(newtag)); + + for (auto &option : slot.option_list()) + { + if (devtypes || (listed && option.second->selectable())) + { + device_t *const dev = config.device_add("_dummy", option.second->devtype(), option.second->clock()); + if (!dev->configured()) + dev->config_complete(); + + if (devtypes) + for (device_t &subdevice : device_enumerator(*dev)) devtypes->insert(&subdevice.type()); + + if (listed && option.second->selectable()) + { + util::stream_format(out, "\t\t\tname())); + util::stream_format(out, " devname=\"%s\"", normalize_string(dev->shortname())); + if (slot.default_option() && !strcmp(slot.default_option(), option.second->name())) + out << " default=\"yes\""; + out << "/>\n"; + } + + config.device_remove("_dummy"); + } + } + + if (listed) + out << "\t\t\n"; + } + } +} + + +//------------------------------------------------- +// output_software_lists - print the information +// for all known software lists for this system +//------------------------------------------------- + +void output_software_lists(std::ostream &out, device_t &root, const char *root_tag) +{ + for (const software_list_device &swlist : software_list_device_enumerator(root)) + { + if (&static_cast(swlist) == &root) + { + assert(swlist.list_name().empty()); + continue; + } + + std::string newtag(swlist.tag()), oldtag(":"); + newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); + util::stream_format(out, "\t\t\n"; + } +} + + + +//------------------------------------------------- +// output_ramoptions - prints m_output all RAM +// options for this system +//------------------------------------------------- + +void output_ramoptions(std::ostream &out, device_t &root) +{ + for (const ram_device &ram : ram_device_enumerator(root, 1)) + { + if (!std::strcmp(ram.tag(), ":" RAM_TAG)) + { + uint32_t const defsize(ram.default_size()); + bool havedefault(false); + for (ram_device::extra_option const &option : ram.extra_options()) + { + if (defsize == option.second) + { + assert(!havedefault); + havedefault = true; + util::stream_format(out, "\t\t%u\n", normalize_string(option.first), option.second); + } + else + { + util::stream_format(out, "\t\t%u\n", normalize_string(option.first), option.second); + } + } + if (!havedefault) + util::stream_format(out, "\t\t%u\n", ram.default_size_string(), defsize); + break; + } + } +} + + +//------------------------------------------------- +// get_merge_name - get the rom name from a +// parent set +//------------------------------------------------- + +char const *get_merge_name(driver_list const &drivlist, game_driver const &driver, util::hash_collection const &romhashes) +{ + char const *result = nullptr; + + // walk the parent chain + for (int clone_of = drivlist.find(driver.parent); !result && (0 <= clone_of); clone_of = drivlist.find(drivlist.driver(clone_of).parent)) + result = get_merge_name(drivlist.driver(clone_of).rom, romhashes); + + return result; +} + + +char const *get_merge_name(machine_config &config, device_t const &device, util::hash_collection const &romhashes) +{ + char const *result = nullptr; + + // check for a parent type + auto const parenttype(device.type().parent_rom_device_type()); + if (parenttype) + { + // instantiate the parent device + machine_config::token const tok(config.begin_configuration(config.root_device())); + device_t *const parent = config.device_add("_parent", *parenttype, 0); + + // look in the parent's ROMs + result = get_merge_name(parent->rom_region(), romhashes); + + // remember to remove the device + config.device_remove("_parent"); + } + + return result; +} + + +char const *get_merge_name(tiny_rom_entry const *roms, util::hash_collection const &romhashes) +{ + for (romload::region const &pregion : romload::entries(roms).get_regions()) + { + for (romload::file const &prom : pregion.get_files()) + { + // stop when we find a match + util::hash_collection const phashes(prom.get_hashdata()); + if (!phashes.flag(util::hash_collection::FLAG_NO_DUMP) && (romhashes == phashes)) + return prom.get_name(); + } + } + + return nullptr; +} + + +//------------------------------------------------- +// device_type_compare::operator() +//------------------------------------------------- + +bool device_type_compare::operator()(const std::add_pointer_t &lhs, const std::add_pointer_t &rhs) const +{ + return strcmp(lhs->shortname(), rhs->shortname()) < 0; +} + +} diff --git a/src/icludes/frontend/mame/infoxml.h b/src/icludes/frontend/mame/infoxml.h new file mode 100644 index 0000000..00a614b --- /dev/null +++ b/src/icludes/frontend/mame/infoxml.h @@ -0,0 +1,45 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + info.h + + Dumps the MAME internal data as an XML file. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_MAME_INFOXML_H +#define MAME_FRONTEND_MAME_INFOXML_H + +#pragma once + +#include "emuopts.h" + +#include +#include + + +//************************************************************************** +// FUNCTION PROTOTYPES +//************************************************************************** + +// helper class to putput +class info_xml_creator +{ +public: + // construction/destruction + info_xml_creator(emu_options const &options, bool dtd = true); + + // output + void output(std::ostream &out, const std::vector &patterns); + void output(std::ostream &out, const std::function &filter = { }, bool include_devices = true); + + static char const *feature_name(device_t::feature_type feature); + +private: + // internal state + emu_options m_lookup_options; + bool m_dtd; +}; + +#endif // MAME_FRONTEND_MAME_INFOXML_H diff --git a/src/icludes/frontend/mame/iptseqpoll.cpp b/src/icludes/frontend/mame/iptseqpoll.cpp new file mode 100644 index 0000000..7808256 --- /dev/null +++ b/src/icludes/frontend/mame/iptseqpoll.cpp @@ -0,0 +1,514 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb, Aaron Giles + +#include "emu.h" +#include "iptseqpoll.h" + +#include "inputdev.h" + +#include +#include + + +input_code_poller::input_code_poller(input_manager &manager) noexcept : + m_manager(manager), + m_axis_memory(), + m_switch_memory() +{ +} + + +input_code_poller::~input_code_poller() +{ +} + + +void input_code_poller::reset() +{ + // iterate over device classes and devices + m_axis_memory.clear(); + m_switch_memory.clear(); + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) + { + input_class &devclass(m_manager.device_class(classno)); + if (devclass.enabled()) + { + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (device) + { + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; device->maxitem() >= itemid; ++itemid) + { + // for any non-switch items, set memory to the current value + input_device_item *const item(device->item(itemid)); + if (item && (item->itemclass() != ITEM_CLASS_SWITCH)) + m_axis_memory.emplace_back(item, m_manager.code_value(item->code())); + } + } + } + } + } + std::sort(m_axis_memory.begin(), m_axis_memory.end()); +} + + +bool input_code_poller::code_pressed_once(input_code code, bool moved) +{ + // look for the code in the memory + bool const pressed(m_manager.code_pressed(code)); + auto const found(std::lower_bound(m_switch_memory.begin(), m_switch_memory.end(), code)); + if ((m_switch_memory.end() != found) && (*found == code)) + { + // if no longer pressed, clear entry + if (!pressed) + m_switch_memory.erase(found); + + // always return false + return false; + } + + // if we get here, we were not previously pressed; if still not pressed, return false + if (!pressed || !moved) + return false; + + // otherwise, add the code to the memory and return true + m_switch_memory.emplace(found, code); + return true; +} + + + +axis_code_poller::axis_code_poller(input_manager &manager) noexcept : + input_code_poller(manager), + m_axis_active() +{ +} + + +void axis_code_poller::reset() +{ + input_code_poller::reset(); + m_axis_active.clear(); + m_axis_active.resize(m_axis_memory.size(), false); +} + + +input_code axis_code_poller::poll() +{ + // iterate over the axis items we found + for (std::size_t i = 0; m_axis_memory.size() > i; ++i) + { + auto &memory = m_axis_memory[i]; + input_code code = memory.first->code(); + if (!memory.first->check_axis(code.item_modifier(), memory.second)) + { + m_axis_active[i] = false; + } + else if (!m_axis_active[i]) + { + if (code.item_class() == ITEM_CLASS_ABSOLUTE) + { + m_axis_active[i] = true; + } + else + { + // can only cycle modifiers on a relative item with append + m_axis_memory.erase(m_axis_memory.begin() + i); + m_axis_active.erase(m_axis_active.begin() + i); + } + if (!m_manager.device_class(memory.first->device().devclass()).multi()) + code.set_device_index(0); + return code; + } + } + + // iterate over device classes and devices, skipping disabled classes + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) + { + input_class &devclass(m_manager.device_class(classno)); + if (!devclass.enabled()) + continue; + + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (!device) + continue; + + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; device->maxitem() >= itemid; ++itemid) + { + input_device_item *const item(device->item(itemid)); + if (!item) + continue; + + input_code code = item->code(); + if (item->itemclass() == ITEM_CLASS_SWITCH) + { + // item is natively a switch, poll it + if (code_pressed_once(code, true)) + return code; + else + continue; + } + } + } + } + + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +switch_code_poller::switch_code_poller(input_manager &manager) noexcept : + input_code_poller(manager) +{ +} + + +input_code switch_code_poller::poll() +{ + // iterate over device classes and devices, skipping disabled classes + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) + { + input_class &devclass(m_manager.device_class(classno)); + if (!devclass.enabled()) + continue; + + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (!device) + continue; + + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; device->maxitem() >= itemid; ++itemid) + { + input_device_item *const item(device->item(itemid)); + if (!item) + continue; + + input_code code = item->code(); + if (item->itemclass() == ITEM_CLASS_SWITCH) + { + // item is natively a switch, poll it + if (code_pressed_once(code, true)) + return code; + else + continue; + } + + auto const memory(std::lower_bound( + m_axis_memory.begin(), + m_axis_memory.end(), + item, + [] (auto const &x, auto const &y) { return x.first < y; })); + if ((m_axis_memory.end() == memory) || (item != memory->first)) + continue; + + // poll axes digitally + bool const moved(item->check_axis(code.item_modifier(), memory->second)); + code.set_item_class(ITEM_CLASS_SWITCH); + if ((classno == DEVICE_CLASS_JOYSTICK) && (code.item_id() == ITEM_ID_XAXIS)) + { + // joystick X axis - check with left/right modifiers + code.set_item_modifier(ITEM_MODIFIER_LEFT); + if (code_pressed_once(code, moved)) + return code; + code.set_item_modifier(ITEM_MODIFIER_RIGHT); + if (code_pressed_once(code, moved)) + return code; + } + else if ((classno == DEVICE_CLASS_JOYSTICK) && (code.item_id() == ITEM_ID_YAXIS)) + { + // if this is a joystick Y axis, check with up/down modifiers + code.set_item_modifier(ITEM_MODIFIER_UP); + if (code_pressed_once(code, moved)) + return code; + code.set_item_modifier(ITEM_MODIFIER_DOWN); + if (code_pressed_once(code, moved)) + return code; + } + else + { + // any other axis, check with pos/neg modifiers + code.set_item_modifier(ITEM_MODIFIER_POS); + if (code_pressed_once(code, moved)) + return code; + code.set_item_modifier(ITEM_MODIFIER_NEG); + if (code_pressed_once(code, moved)) + return code; + } + } + } + } + + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +keyboard_code_poller::keyboard_code_poller(input_manager &manager) noexcept : + input_code_poller(manager) +{ +} + + +input_code keyboard_code_poller::poll() +{ + // iterate over devices in keyboard class + input_class &devclass = m_manager.device_class(DEVICE_CLASS_KEYBOARD); + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // fetch the device; ignore if nullptr + input_device *const device(devclass.device(devnum)); + if (device) + { + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) + { + // iterate over items within each device + for (input_item_id itemid = ITEM_ID_FIRST_VALID; device->maxitem() >= itemid; ++itemid) + { + input_device_item *const item = device->item(itemid); + if (item && (item->itemclass() == ITEM_CLASS_SWITCH)) + { + input_code const code = item->code(); + if (code_pressed_once(code, true)) + return code; + } + } + } + } + } + + // if nothing, return an invalid code + return INPUT_CODE_INVALID; +} + + + +input_sequence_poller::input_sequence_poller() noexcept : + m_sequence(), + m_last_ticks(0), + m_modified(false) +{ +} + + +input_sequence_poller::~input_sequence_poller() +{ +} + + +void input_sequence_poller::start() +{ + // start with an empty sequence + m_sequence.reset(); + + // reset the recording count and the clock + m_last_ticks = 0; + m_modified = false; + do_start(); +} + + +void input_sequence_poller::start(input_seq const &startseq) +{ + // grab the starting sequence to append to, and append an OR if it isn't empty + m_sequence = startseq; + if (input_seq::end_code != m_sequence[0]) + m_sequence += input_seq::or_code; + + // reset the recording count and the clock + m_last_ticks = 0; + m_modified = false; + do_start(); +} + + +bool input_sequence_poller::poll() +{ + // if we got a new code to append it, append it and reset the timer + input_code const newcode = do_poll(); + osd_ticks_t const newticks = osd_ticks(); + if (INPUT_CODE_INVALID != newcode) + { + m_sequence += newcode; + m_last_ticks = newticks; + m_modified = true; + } + + // if we've recorded at least one item and one second has passed, we're done + if (m_last_ticks && ((m_last_ticks + osd_ticks_per_second()) < newticks)) + return true; + + // return false to indicate we are still polling + return false; +} + + + +axis_sequence_poller::axis_sequence_poller(input_manager &manager) noexcept : + input_sequence_poller(), + m_code_poller(manager) +{ +} + + +void axis_sequence_poller::do_start() +{ + // wait for any inputs that are already active to be released + m_code_poller.reset(); + for (input_code dummycode = KEYCODE_ENTER; INPUT_CODE_INVALID != dummycode; ) + dummycode = m_code_poller.poll(); +} + + +input_code axis_sequence_poller::do_poll() +{ + // absolute/relative case: see if we have an analog change of sufficient amount + int const curlen = m_sequence.length(); + input_code lastcode = m_sequence[curlen - 1]; + bool const has_or = input_seq::or_code == lastcode; + if (has_or) + lastcode = m_sequence[curlen - 2]; + input_code newcode = m_code_poller.poll(); + + // if not empty, see if it's the same control again to cycle modifiers + if ((INPUT_CODE_INVALID != newcode) && curlen) + { + input_item_class const newclass = newcode.item_class(); + input_code last_nomodifier = lastcode; + last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE); + if (newcode == last_nomodifier) + { + if (ITEM_CLASS_ABSOLUTE == newclass) + { + // increment the modifier, wrapping back to none + switch (lastcode.item_modifier()) + { + case ITEM_MODIFIER_NONE: + newcode.set_item_modifier(ITEM_MODIFIER_POS); + break; + case ITEM_MODIFIER_POS: + newcode.set_item_modifier(ITEM_MODIFIER_NEG); + break; + case ITEM_MODIFIER_NEG: + newcode.set_item_modifier(ITEM_MODIFIER_REVERSE); + break; + default: + case ITEM_MODIFIER_REVERSE: + newcode.set_item_modifier(ITEM_MODIFIER_NONE); + break; + } + + // back up over the previous code so we can re-append + if (has_or) + m_sequence.backspace(); + m_sequence.backspace(); + } + else if (ITEM_CLASS_RELATIVE == newclass) + { + // increment the modifier, wrapping back to none + switch (lastcode.item_modifier()) + { + case ITEM_MODIFIER_NONE: + newcode.set_item_modifier(ITEM_MODIFIER_REVERSE); + break; + default: + case ITEM_MODIFIER_REVERSE: + newcode.set_item_modifier(ITEM_MODIFIER_NONE); + break; + } + + // back up over the previous code so we can re-append + if (has_or) + m_sequence.backspace(); + m_sequence.backspace(); + } + else if (!has_or && (ITEM_CLASS_SWITCH == newclass)) + { + // back up over the existing code + m_sequence.backspace(); + + // if there was a NOT preceding it, delete it as well, otherwise append a fresh one + if (m_sequence[curlen - 2] == input_seq::not_code) + m_sequence.backspace(); + else + m_sequence += input_seq::not_code; + } + } + else if (!has_or && (ITEM_CLASS_SWITCH == newclass)) + { + // ignore switches following axes + if (ITEM_CLASS_SWITCH != lastcode.item_class()) + { + // hack to stop it timing out so user can cancel + m_sequence.backspace(); + newcode = lastcode; + } + } + } + + // hack to stop it timing out before assigning an axis + if (ITEM_CLASS_SWITCH == newcode.item_class()) + { + m_sequence += newcode; + set_modified(); + return INPUT_CODE_INVALID; + } + else + { + return newcode; + } +} + + + +switch_sequence_poller::switch_sequence_poller(input_manager &manager) noexcept : + input_sequence_poller(), + m_code_poller(manager) +{ +} + + +void switch_sequence_poller::do_start() +{ + // wait for any inputs that are already active to be released + m_code_poller.reset(); + for (input_code dummycode = KEYCODE_ENTER; INPUT_CODE_INVALID != dummycode; ) + dummycode = m_code_poller.poll(); +} + + +input_code switch_sequence_poller::do_poll() +{ + // switch case: see if we have a new code to process + int const curlen = m_sequence.length(); + input_code lastcode = m_sequence[curlen - 1]; + input_code newcode = m_code_poller.poll(); + if (INPUT_CODE_INVALID != newcode) + { + // if code is duplicate, toggle the NOT state on the code + if (curlen && (newcode == lastcode)) + { + // back up over the existing code + m_sequence.backspace(); + + // if there was a NOT preceding it, delete it as well, otherwise append a fresh one + if (m_sequence[curlen - 2] == input_seq::not_code) + m_sequence.backspace(); + else + m_sequence += input_seq::not_code; + } + } + return newcode; +} diff --git a/src/icludes/frontend/mame/iptseqpoll.h b/src/icludes/frontend/mame/iptseqpoll.h new file mode 100644 index 0000000..c43dd39 --- /dev/null +++ b/src/icludes/frontend/mame/iptseqpoll.h @@ -0,0 +1,123 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb,Aaron Giles +/*************************************************************************** + + iptseqpoll.h + + Helper for letting the user select input sequences. + +***************************************************************************/ +#ifndef MAME_FRONTEND_IPTSEQPOLL_H +#define MAME_FRONTEND_IPTSEQPOLL_H + +#pragma once + +#include +#include + + +class input_code_poller +{ +public: + virtual ~input_code_poller(); + + virtual void reset(); + virtual input_code poll() = 0; + +protected: + input_code_poller(input_manager &manager) noexcept; + + bool code_pressed_once(input_code code, bool moved); + + input_manager &m_manager; + std::vector > m_axis_memory; + std::vector m_switch_memory; +}; + + +class axis_code_poller : public input_code_poller +{ +public: + axis_code_poller(input_manager &manager) noexcept; + + virtual void reset() override; + virtual input_code poll() override; + +private: + std::vector m_axis_active; +}; + + +class switch_code_poller : public input_code_poller +{ +public: + switch_code_poller(input_manager &manager) noexcept; + + virtual input_code poll() override; +}; + + +class keyboard_code_poller : public input_code_poller +{ +public: + keyboard_code_poller(input_manager &manager) noexcept; + + virtual input_code poll() override; +}; + + +class input_sequence_poller +{ +public: + virtual ~input_sequence_poller(); + + void start(); + void start(input_seq const &startseq); + bool poll(); + + input_seq const &sequence() const noexcept { return m_sequence; } + bool valid() const noexcept { return m_sequence.is_valid(); } + bool modified() const noexcept { return m_modified; } + +protected: + input_sequence_poller() noexcept; + + void set_modified() noexcept { m_modified = true; } + + input_seq m_sequence; + +private: + virtual void do_start() = 0; + virtual input_code do_poll() = 0; + + osd_ticks_t m_last_ticks; + bool m_modified; +}; + + +class axis_sequence_poller : public input_sequence_poller +{ +public: + axis_sequence_poller(input_manager &manager) noexcept; + +private: + virtual void do_start() override; + virtual input_code do_poll() override; + + axis_code_poller m_code_poller; +}; + + +class switch_sequence_poller : public input_sequence_poller +{ +public: + switch_sequence_poller(input_manager &manager) noexcept; + +private: + virtual void do_start() override; + virtual input_code do_poll() override; + + switch_code_poller m_code_poller; +}; + +#endif // MAME_FRONTEND_IPTSEQPOLL_H diff --git a/src/icludes/frontend/mame/language.cpp b/src/icludes/frontend/mame/language.cpp new file mode 100644 index 0000000..6602dff --- /dev/null +++ b/src/icludes/frontend/mame/language.cpp @@ -0,0 +1,41 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + language.cpp + + Multi-language support. + +***************************************************************************/ + +#include "emu.h" +#include "language.h" + +#include "emuopts.h" + +#include "corestr.h" + +#include + + +void load_translation(emu_options &m_options) +{ + util::unload_translation(); + + std::string name = m_options.language(); + if (name.empty()) + return; + + strreplace(name, " ", "_"); + strreplace(name, "(", ""); + strreplace(name, ")", ""); + emu_file file(m_options.language_path(), OPEN_FLAG_READ); + if (file.open(name + PATH_SEPARATOR "strings.mo")) + { + osd_printf_error("Error opening translation file %s\n", name); + return; + } + + osd_printf_verbose("Loading translation file %s\n", file.fullpath()); + util::load_translation(file); +} diff --git a/src/icludes/frontend/mame/language.h b/src/icludes/frontend/mame/language.h new file mode 100644 index 0000000..100d367 --- /dev/null +++ b/src/icludes/frontend/mame/language.h @@ -0,0 +1,22 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + language.h + + Multi-language support. + +***************************************************************************/ +#ifndef MAME_FRONTEND_MAME_LANGUAGE_H +#define MAME_FRONTEND_MAME_LANGUAGE_H + +#pragma once + +#include "util/language.h" + + +void load_translation(emu_options &options); + +using util::lang_translate; + +#endif // MAME_FRONTEND_MAME_LANGUAGE_H diff --git a/src/icludes/frontend/mame/luaengine.cpp b/src/icludes/frontend/mame/luaengine.cpp new file mode 100644 index 0000000..2cc98b9 --- /dev/null +++ b/src/icludes/frontend/mame/luaengine.cpp @@ -0,0 +1,1941 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic,Luca Bruno +/*************************************************************************** + + luaengine.cpp + + Controls execution of the core MAME system. + +***************************************************************************/ + +#include "emu.h" +#include "luaengine.ipp" + +#include "mame.h" +#include "pluginopts.h" +#include "ui/pluginopt.h" +#include "ui/ui.h" + +#include "imagedev/cassette.h" + +#include "debugger.h" +#include "drivenum.h" +#include "emuopts.h" +#include "inputdev.h" +#include "natkeyboard.h" +#include "softlist.h" +#include "uiinput.h" + +#include "corestr.h" + +#include +#include + + +//************************************************************************** +// LUA ENGINE +//************************************************************************** + +extern "C" { + +int luaopen_zlib(lua_State *L); +int luaopen_lfs(lua_State *L); +int luaopen_linenoise(lua_State *L); +int luaopen_lsqlite3(lua_State *L); + +} // extern "C" + + +template +struct lua_engine::devenum +{ + template devenum(device_t &d, U &&... args) : device(d), iter(d, std::forward(args)...) { } + + device_t &device; + T iter; + int count = -1; +}; + + +namespace { + +struct image_interface_formats +{ + image_interface_formats(device_image_interface &i) : image(i) { } + device_image_interface::formatlist_type const &items() { return image.formatlist(); } + + static image_device_format const &unwrap(device_image_interface::formatlist_type::const_iterator const &it) { return **it; } + static int push_key(lua_State *L, device_image_interface::formatlist_type::const_iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, (*it)->name()); } + + device_image_interface ℑ +}; + + +struct plugin_options_plugins +{ + plugin_options_plugins(plugin_options &o) : options(o) { } + std::list &items() { return options.plugins(); } + + static plugin_options::plugin const &unwrap(std::list::const_iterator const &it) { return *it; } + static int push_key(lua_State *L, std::list::const_iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, it->m_name); } + + plugin_options &options; +}; + +} // anonymous namespace + + +namespace sol +{ + +template <> struct is_container : std::true_type { }; +template <> struct is_container : std::true_type { }; + + +sol::buffer *sol_lua_get(sol::types, lua_State *L, int index, sol::stack::record &tracking) +{ + return new sol::buffer(stack::get(L, index), L); +} + +int sol_lua_push(sol::types, lua_State *L, buffer *value) +{ + delete value; + return 1; +} + + +template +struct usertype_container > : lua_engine::immutable_collection_helper, T> +{ +private: + using enumerator = lua_engine::devenum; + + template + static int next_pairs(lua_State *L) + { + typename usertype_container::indexed_iterator &i(stack::unqualified_get >(L, 1)); + if (i.src.end() == i.it) + return stack::push(L, lua_nil); + int result; + if constexpr (Indexed) + result = stack::push(L, i.ix + 1); + else + result = stack::push(L, i.it->tag()); + result += stack::push_reference(L, *i.it); + ++i; + return result; + } + + template + static int start_pairs(lua_State *L) + { + enumerator &self(usertype_container::get_self(L)); + stack::push(L, next_pairs); + stack::push >(L, self.iter, self.iter.begin()); + stack::push(L, lua_nil); + return 3; + } + +public: + static int at(lua_State *L) + { + enumerator &self(usertype_container::get_self(L)); + std::ptrdiff_t const index(stack::unqualified_get(L, 2)); + auto const dev(self.iter.byindex(index - 1)); + if (dev) + return stack::push_reference(L, *dev); + else + return stack::push(L, lua_nil); + } + + static int get(lua_State *L) + { + enumerator &self(usertype_container::get_self(L)); + char const *const tag(stack::unqualified_get(L)); + device_t *const dev(self.device.subdevice(tag)); + if (dev) + { + auto *const check(T(*dev, 0).first()); + bool match; + if constexpr (std::is_base_of_v) + match = check && (static_cast(check) == dev); + else if constexpr (std::is_base_of_v) + match = check && (&check->device() == dev); + else + match = check && (dynamic_cast(check) == dev); + if (match) + return stack::push_reference(L, *check); + } + return stack::push(L, lua_nil); + } + + static int index_get(lua_State *L) + { + return get(L); + } + + static int index_of(lua_State *L) + { + enumerator &self(usertype_container::get_self(L)); + auto &dev(stack::unqualified_get(L, 2)); + std::ptrdiff_t found(self.iter.indexof(dev)); + if (0 > found) + return stack::push(L, lua_nil); + else + return stack::push(L, found + 1); + } + + static int size(lua_State *L) + { + enumerator &self(usertype_container::get_self(L)); + if (0 > self.count) + self.count = self.iter.count(); + return stack::push(L, self.count); + } + + static int empty(lua_State *L) + { + enumerator &self(usertype_container::get_self(L)); + if (0 > self.count) + self.count = self.iter.count(); + return stack::push(L, !self.count); + } + + static int next(lua_State *L) { return stack::push(L, next_pairs); } + static int pairs(lua_State *L) { return start_pairs(L); } + static int ipairs(lua_State *L) { return start_pairs(L); } +}; + + +template <> +struct usertype_container : lua_engine::immutable_sequence_helper +{ +private: + using format_list = device_image_interface::formatlist_type; + +public: + static int get(lua_State *L) + { + image_interface_formats &self(get_self(L)); + char const *const name(stack::unqualified_get(L)); + auto const found(std::find_if( + self.image.formatlist().begin(), + self.image.formatlist().end(), + [&name] (std::unique_ptr const &v) { return v->name() == name; })); + if (self.image.formatlist().end() != found) + return stack::push_reference(L, **found); + else + return stack::push(L, lua_nil); + } + + static int index_get(lua_State *L) + { + return get(L); + } +}; + + +template <> +struct usertype_container : lua_engine::immutable_sequence_helper > +{ +private: + using plugin_list = std::list; + +public: + static int get(lua_State *L) + { + plugin_options_plugins &self(get_self(L)); + char const *const name(stack::unqualified_get(L)); + auto const found(std::find_if( + self.options.plugins().begin(), + self.options.plugins().end(), + [&name] (plugin_options::plugin const &p) { return p.m_name == name; })); + if (self.options.plugins().end() != found) + return stack::push_reference(L, *found); + else + return stack::push(L, lua_nil); + } + + static int index_get(lua_State *L) + { + return get(L); + } +}; + +} // namespace sol + + +int sol_lua_push(sol::types, lua_State *L, std::error_condition &&value) +{ + if (!value) + return sol::stack::push(L, sol::lua_nil); + else + return sol::stack::push(L, value.message()); +} + + +int sol_lua_push(sol::types, lua_State *L, screen_type_enum &&value) +{ + switch (value) + { + case SCREEN_TYPE_INVALID: return sol::stack::push(L, "invalid"); + case SCREEN_TYPE_RASTER: return sol::stack::push(L, "raster"); + case SCREEN_TYPE_VECTOR: return sol::stack::push(L, "vector"); + case SCREEN_TYPE_LCD: return sol::stack::push(L, "svg"); + case SCREEN_TYPE_SVG: return sol::stack::push(L, "none"); + } + return sol::stack::push(L, "unknown"); +} + +int sol_lua_push(sol::types, lua_State *L, image_init_result &&value) +{ + switch (value) + { + case image_init_result::PASS: return sol::stack::push(L, "pass"); + case image_init_result::FAIL: return sol::stack::push(L, "fail"); + } + return sol::stack::push(L, "invalid"); +} + +int sol_lua_push(sol::types, lua_State *L, image_verify_result &&value) +{ + switch (value) + { + case image_verify_result::PASS: return sol::stack::push(L, "pass"); + case image_verify_result::FAIL: return sol::stack::push(L, "fail"); + } + return sol::stack::push(L, "invalid"); +} + + +//------------------------------------------------- +// process_snapshot_filename - processes a snapshot +// filename +//------------------------------------------------- + +static std::string process_snapshot_filename(running_machine &machine, const char *s) +{ + std::string result(s); + if (!osd_is_absolute_path(s)) + { + strreplace(result, "/", PATH_SEPARATOR); + strreplace(result, "%g", machine.basename()); + } + return result; +} + + +//------------------------------------------------- +// lua_engine - constructor +//------------------------------------------------- + +lua_engine::lua_engine() +{ + m_machine = nullptr; + m_lua_state = luaL_newstate(); /* create state */ + m_sol_state = std::make_unique(m_lua_state); // create sol view + + luaL_checkversion(m_lua_state); + lua_gc(m_lua_state, LUA_GCSTOP, 0); /* stop collector during initialization */ + sol().open_libraries(); + + // Get package.preload so we can store builtins in it. + sol()["package"]["preload"]["zlib"] = &luaopen_zlib; + sol()["package"]["preload"]["lfs"] = &luaopen_lfs; + sol()["package"]["preload"]["linenoise"] = &luaopen_linenoise; + sol()["package"]["preload"]["lsqlite3"] = &luaopen_lsqlite3; + + lua_gc(m_lua_state, LUA_GCRESTART, 0); +} + +//------------------------------------------------- +// ~lua_engine - destructor +//------------------------------------------------- + +lua_engine::~lua_engine() +{ + close(); +} + +sol::object lua_engine::call_plugin(const std::string &name, sol::object in) +{ + std::string field = "cb_" + name; + sol::object obj = sol().registry()[field]; + if(obj.is()) + { + auto res = invoke(obj.as(), in); + if(!res.valid()) + { + sol::error err = res; + osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what()); + } + else + return res.get(); + } + return sol::lua_nil; +} + +std::optional lua_engine::menu_populate(const std::string &menu, std::vector > &menu_list, std::string &flags) +{ + std::string field = "menu_pop_" + menu; + sol::object obj = sol().registry()[field]; + if (obj.is()) + { + auto res = invoke(obj.as()); + if (!res.valid()) + { + sol::error err = res; + osd_printf_error("[LUA ERROR] in menu_populate: %s\n", err.what()); + } + else + { + std::tuple, std::string> table = res; + for (auto &entry : std::get<0>(table)) + { + if (entry.second.is()) + { + sol::table enttable = entry.second.as(); + menu_list.emplace_back(enttable.get(1, 2, 3)); + } + } + flags = std::get<2>(table); + return std::get<1>(table); + } + } + flags.clear(); + return std::nullopt; +} + +std::pair > lua_engine::menu_callback(const std::string &menu, int index, const std::string &event) +{ + std::string field = "menu_cb_" + menu; + std::pair > ret(false, std::nullopt); + sol::object obj = sol().registry()[field]; + if (obj.is()) + { + auto res = invoke(obj.as(), index, event); + if (!res.valid()) + { + sol::error err = res; + osd_printf_error("[LUA ERROR] in menu_callback: %s\n", err.what()); + } + else + { + ret = res; + } + } + return ret; +} + +void lua_engine::set_machine(running_machine *machine) +{ + m_machine = machine; +} + +int lua_engine::enumerate_functions(const char *id, std::function &&callback) +{ + int count = 0; + sol::object functable = sol().registry()[id]; + if (functable.is()) + { + for (auto &func : functable.as()) + { + if (func.second.is()) + { + bool cont = callback(func.second.as()); + count++; + if (!cont) + break; + } + } + return true; + } + return count; +} + +bool lua_engine::execute_function(const char *id) +{ + int count = enumerate_functions(id, [this](const sol::protected_function &func) + { + auto ret = invoke(func); + if(!ret.valid()) + { + sol::error err = ret; + osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what()); + } + return true; + }); + return count > 0; +} + +void lua_engine::register_function(sol::function func, const char *id) +{ + sol::object functable = sol().registry()[id]; + if(functable.is()) + functable.as().add(func); + else + sol().registry().create_named(id, 1, func); +} + +void lua_engine::on_machine_prestart() +{ + execute_function("LUA_ON_PRESTART"); +} + +void lua_engine::on_machine_start() +{ + execute_function("LUA_ON_START"); +} + +void lua_engine::on_machine_stop() +{ + execute_function("LUA_ON_STOP"); +} + +void lua_engine::on_machine_before_load_settings() +{ + execute_function("LUA_ON_BEFORE_LOAD_SETTINGS"); +} + +void lua_engine::on_machine_pause() +{ + execute_function("LUA_ON_PAUSE"); +} + +void lua_engine::on_machine_resume() +{ + execute_function("LUA_ON_RESUME"); +} + +void lua_engine::on_machine_frame() +{ + execute_function("LUA_ON_FRAME"); +} + +void lua_engine::on_frame_done() +{ + execute_function("LUA_ON_FRAME_DONE"); +} + +void lua_engine::on_sound_update() +{ + execute_function("LUA_ON_SOUND_UPDATE"); +} + +void lua_engine::on_periodic() +{ + execute_function("LUA_ON_PERIODIC"); +} + +bool lua_engine::on_missing_mandatory_image(const std::string &instance_name) +{ + bool handled = false; + enumerate_functions("LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE", [this, &instance_name, &handled](const sol::protected_function &func) + { + auto ret = invoke(func, instance_name); + + if(!ret.valid()) + { + sol::error err = ret; + osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what()); + } + else if (ret.get()) + { + handled = true; + } + return !handled; + }); + return handled; +} + +void lua_engine::attach_notifiers() +{ + machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_prestart, this), true); + machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_start, this)); + machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&lua_engine::on_machine_stop, this)); + machine().add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&lua_engine::on_machine_pause, this)); + machine().add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&lua_engine::on_machine_resume, this)); + machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&lua_engine::on_machine_frame, this)); +} + +//------------------------------------------------- +// initialize - initialize lua hookup to emu engine +//------------------------------------------------- + +void lua_engine::initialize() +{ + + static const enum_parser s_movie_recording_format_parser = + { + { "avi", movie_recording::format::AVI }, + { "mng", movie_recording::format::MNG } + }; + + + static const enum_parser s_seek_parser = + { + { "set", SEEK_SET }, + { "cur", SEEK_CUR }, + { "end", SEEK_END } + }; + + +/* emu library + * + * emu.app_name() - return application name + * emu.app_version() - return application version + * emu.gamename() - return game full name + * emu.romname() - return game ROM name + * emu.softname() - return softlist name + * emu.time() - return emulation time + * emu.pid() - return frontend process ID + * + * emu.driver_find(driver_name) - find and return game_driver for driver_name + * emu.start(driver_name) - start given driver_name + * emu.pause() - pause emulation + * emu.unpause() - unpause emulation + * emu.step() - advance one frame + * emu.keypost(keys) - post keys to natural keyboard + * emu.wait(len) - wait for len within coroutine + * emu.lang_translate(str) - get translation for str if available + * emu.subst_env(str) - substitute environment variables with values for str (semantics are OS-specific) + * + * emu.register_prestart(callback) - register callback before reset + * emu.register_start(callback) - register callback after reset + * emu.register_stop(callback) - register callback after stopping + * emu.register_pause(callback) - register callback at pause + * emu.register_resume(callback) - register callback at resume + * emu.register_frame(callback) - register callback at end of frame + * emu.register_frame_done(callback) - register callback after frame is drawn to screen (for overlays) + * emu.register_sound_update(callback) - register callback after sound update has generated new samples + * emu.register_periodic(callback) - register periodic callback while program is running + * emu.register_callback(callback, name) - register callback to be used by MAME via lua_engine::call_plugin() + * emu.register_menu(event_callback, populate_callback, name) - register callbacks for plugin menu + * emu.register_mandatory_file_manager_override(callback) - register callback invoked to override mandatory file manager + * emu.register_before_load_settings(callback) - register callback to be run before settings are loaded + * emu.show_menu(menu_name) - show menu by name and pause the machine + * + * emu.print_verbose(str) - output to stderr at verbose level + * emu.print_error(str) - output to stderr at error level + * emu.print_info(str) - output to stderr at info level + * emu.print_debug(str) - output to stderr at debug level + * + * emu.device_enumerator(dev) - get device enumerator starting at arbitrary point in tree + * emu.screen_enumerator(dev) - get screen device enumerator starting at arbitrary point in tree + * emu.image_enumerator(dev) - get image interface enumerator starting at arbitrary point in tree + * emu.image_enumerator(dev) - get image interface enumerator starting at arbitrary point in tree + */ + + sol::table emu = sol().create_named_table("emu"); + emu["app_name"] = &emulator_info::get_appname_lower; + emu["app_version"] = &emulator_info::get_bare_build_version; + emu["gamename"] = [this] () { return machine().system().type.fullname(); }; + emu["romname"] = [this] () { return machine().basename(); }; + emu["softname"] = [this] () { return machine().options().software_name(); }; + emu["keypost"] = [this] (const char *keys) { machine().natkeyboard().post_utf8(keys); }; + emu["time"] = [this] () { return machine().time().as_double(); }; + emu["start"] = + [this](const char *driver) + { + int i = driver_list::find(driver); + if (i != -1) + { + mame_machine_manager::instance()->schedule_new_driver(driver_list::driver(i)); + machine().schedule_hard_reset(); + } + return 1; + }; + emu["pause"] = [this] () { return machine().pause(); }; + emu["unpause"] = [this] () { return machine().resume(); }; + emu["step"] = + [this] () + { + mame_machine_manager::instance()->ui().set_single_step(true); + machine().resume(); + }; + emu["register_prestart"] = [this] (sol::function func) { register_function(func, "LUA_ON_PRESTART"); }; + emu["register_start"] = [this] (sol::function func) { register_function(func, "LUA_ON_START"); }; + emu["register_stop"] = [this] (sol::function func) { register_function(func, "LUA_ON_STOP"); }; + emu["register_pause"] = [this] (sol::function func) { register_function(func, "LUA_ON_PAUSE"); }; + emu["register_resume"] = [this] (sol::function func) { register_function(func, "LUA_ON_RESUME"); }; + emu["register_frame"] = [this] (sol::function func) { register_function(func, "LUA_ON_FRAME"); }; + emu["register_frame_done"] = [this] (sol::function func) { register_function(func, "LUA_ON_FRAME_DONE"); }; + emu["register_sound_update"] = [this] (sol::function func) { register_function(func, "LUA_ON_SOUND_UPDATE"); }; + emu["register_periodic"] = [this] (sol::function func) { register_function(func, "LUA_ON_PERIODIC"); }; + emu["register_mandatory_file_manager_override"] = [this] (sol::function func) { register_function(func, "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE"); }; + emu["register_before_load_settings"] = [this](sol::function func) { register_function(func, "LUA_ON_BEFORE_LOAD_SETTINGS"); }; + emu["register_menu"] = + [this] (sol::function cb, sol::function pop, const std::string &name) + { + std::string cbfield = "menu_cb_" + name; + std::string popfield = "menu_pop_" + name; + sol().registry()[cbfield] = cb; + sol().registry()[popfield] = pop; + m_menu.push_back(name); + }; + emu["show_menu"] = + [this](const char *name) + { + mame_ui_manager &mui = mame_machine_manager::instance()->ui(); + render_container &container = machine().render().ui_container(); + ui::menu_plugin::show_menu(mui, container, (char *)name); + }; + emu["register_callback"] = + [this] (sol::function cb, const std::string &name) + { + std::string field = "cb_" + name; + sol().registry()[field] = cb; + }; + emu["print_verbose"] = [] (const char *str) { osd_printf_verbose("%s\n", str); }; + emu["print_error"] = [] (const char *str) { osd_printf_error("%s\n", str); }; + emu["print_info"] = [] (const char *str) { osd_printf_info("%s\n", str); }; + emu["print_debug"] = [] (const char *str) { osd_printf_debug("%s\n", str); }; + emu["osd_ticks"] = &osd_ticks; + emu["osd_ticks_per_second"] = &osd_ticks_per_second; + emu["driver_find"] = + [] (sol::this_state s, const char *driver) -> sol::object + { + const int i = driver_list::find(driver); + if (i < 0) + return sol::lua_nil; + return sol::make_object(s, driver_list::driver(i)); + }; + emu["wait"] = lua_CFunction( + [] (lua_State *L) + { + lua_engine *engine = mame_machine_manager::instance()->lua(); + luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected"); + int ret = lua_pushthread(L); + if (ret == 1) + return luaL_error(L, "cannot wait from outside coroutine"); + int ref = luaL_ref(L, LUA_REGISTRYINDEX); + engine->machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), engine), ref, nullptr); + return lua_yield(L, 0); + }); + emu["lang_translate"] = sol::overload( + static_cast(&lang_translate), + static_cast(&lang_translate)); + emu["pid"] = &osd_getpid; + emu["subst_env"] = + [] (const std::string &str) + { + std::string result; + osd_subst_env(result, str); + return result; + }; + emu["device_enumerator"] = sol::overload( + [] (device_t &dev) { return devenum(dev); }, + [] (device_t &dev, int maxdepth) { return devenum(dev, maxdepth); }); + emu["screen_enumerator"] = sol::overload( + [] (device_t &dev) { return devenum(dev); }, + [] (device_t &dev, int maxdepth) { return devenum(dev, maxdepth); }); + emu["cassette_enumerator"] = sol::overload( + [] (device_t &dev) { return devenum(dev); }, + [] (device_t &dev, int maxdepth) { return devenum(dev, maxdepth); }); + emu["image_enumerator"] = sol::overload( + [] (device_t &dev) { return devenum(dev); }, + [] (device_t &dev, int maxdepth) { return devenum(dev, maxdepth); }); + emu["slot_enumerator"] = sol::overload( + [] (device_t &dev) { return devenum(dev); }, + [] (device_t &dev, int maxdepth) { return devenum(dev, maxdepth); }); + + + auto attotime_type = emu.new_usertype( + "attotime", + sol::call_constructor, sol::constructors()); + attotime_type["from_double"] = &attotime::from_double; + attotime_type["from_ticks"] = static_cast(&attotime::from_ticks); + attotime_type["from_seconds"] = &attotime::from_seconds; + attotime_type["from_msec"] = &attotime::from_msec; + attotime_type["from_usec"] = &attotime::from_usec; + attotime_type["from_nsec"] = &attotime::from_nsec; + attotime_type["as_double"] = &attotime::as_double; + attotime_type["as_hz"] = &attotime::as_hz; + attotime_type["as_khz"] = &attotime::as_khz; + attotime_type["as_mhz"] = &attotime::as_mhz; + attotime_type["as_ticks"] = static_cast(&attotime::as_ticks); + attotime_type["is_zero"] = sol::property(&attotime::is_zero); + attotime_type["is_never"] = sol::property(&attotime::is_never); + attotime_type["attoseconds"] = sol::property(&attotime::attoseconds); + attotime_type["seconds"] = sol::property(&attotime::seconds); + attotime_type["msec"] = sol::property([] (attotime const &t) { return t.attoseconds() / ATTOSECONDS_PER_MILLISECOND; }); + attotime_type["usec"] = sol::property([] (attotime const &t) { return t.attoseconds() / ATTOSECONDS_PER_MICROSECOND; }); + attotime_type["nsec"] = sol::property([] (attotime const &t) { return t.attoseconds() / ATTOSECONDS_PER_NANOSECOND; }); + attotime_type[sol::meta_function::to_string] = &attotime::to_string; + attotime_type[sol::meta_function::addition] = static_cast(&operator+); + attotime_type[sol::meta_function::subtraction] = static_cast(&operator-); + attotime_type[sol::meta_function::multiplication] = static_cast(&operator*); + attotime_type[sol::meta_function::division] = static_cast(&operator/); + + +/* emu_file library + * + * emu.file([opt] searchpath, flags) - flags can be as in osdcore "OPEN_FLAG_*" or lua style + * with 'rwc' with addtional c for create *and truncate* + * (be careful) support zipped files on the searchpath + * + * file:open(name) - open first file matching name in searchpath. supports read + * and write sockets as "socket.127.0.0.1:1234" + * file:open_next() - open next file matching name in searchpath + * file:read(len) - only reads len bytes, doesn't do lua style formats + * file:write(data) - write data to file + * file:seek(offset, whence) - whence is as C "SEEK_*" int + * file:seek([opt] whence, [opt] offset) - lua style "set"|"cur"|"end", returns cur offset + * file:size() - file size in bytes + * file:filename() - name of current file, container name if file is in zip + * file:fullpath() - +*/ + + auto file_type = emu.new_usertype("file", sol::call_constructor, sol::initializers( + [](emu_file &file, u32 flags) { new (&file) emu_file(flags); }, + [](emu_file &file, const char *path, u32 flags) { new (&file) emu_file(path, flags); }, + [](emu_file &file, const char *mode) { + int flags = 0; + for(int i = 0; i < 3 && mode[i]; i++) // limit to three chars + { + switch(mode[i]) + { + case 'r': + flags |= OPEN_FLAG_READ; + break; + case 'w': + flags |= OPEN_FLAG_WRITE; + break; + case 'c': + flags |= OPEN_FLAG_CREATE; + break; + } + } + new (&file) emu_file(flags); + }, + [](emu_file &file, const char *path, const char* mode) { + int flags = 0; + for(int i = 0; i < 3 && mode[i]; i++) // limit to three chars + { + switch(mode[i]) + { + case 'r': + flags |= OPEN_FLAG_READ; + break; + case 'w': + flags |= OPEN_FLAG_WRITE; + break; + case 'c': + flags |= OPEN_FLAG_CREATE; + break; + } + } + new (&file) emu_file(path, flags); + })); + file_type.set("read", [](emu_file &file, sol::buffer *buff) { buff->set_len(file.read(buff->get_ptr(), buff->get_len())); return buff; }); + file_type.set("write", [](emu_file &file, const std::string &data) { return file.write(data.data(), data.size()); }); + file_type.set("puts", &emu_file::puts); + file_type.set("open", static_cast(&emu_file::open)); + file_type.set("open_next", &emu_file::open_next); + file_type.set("close", &emu_file::close); + file_type.set("seek", sol::overload( + [](emu_file &file) { return file.tell(); }, + [this] (emu_file &file, s64 offset, int whence) -> sol::object { + if(file.seek(offset, whence)) + return sol::lua_nil; + else + return sol::make_object(sol(), file.tell()); + }, + [this](emu_file &file, const char* whence) -> sol::object { + int wval = s_seek_parser(whence); + if(wval < 0 || wval >= 3) + return sol::lua_nil; + if(file.seek(0, wval)) + return sol::lua_nil; + return sol::make_object(sol(), file.tell()); + }, + [this](emu_file &file, const char* whence, s64 offset) -> sol::object { + int wval = s_seek_parser(whence); + if(wval < 0 || wval >= 3) + return sol::lua_nil; + if(file.seek(offset, wval)) + return sol::lua_nil; + return sol::make_object(sol(), file.tell()); + })); + file_type.set("size", &emu_file::size); + file_type.set("filename", &emu_file::filename); + file_type.set("fullpath", &emu_file::fullpath); + + +/* thread library + * + * emu.thread() + * + * thread:start(scr) - run scr (lua code as string) in a separate thread + * in a new empty (other than modules) lua context. + * thread runs until yield() and/or terminates on return. + * thread:continue(val) - resume thread that has yielded and pass val to it + * + * thread.result - get result of a terminated thread as string + * thread.busy - check if thread is running + * thread.yield - check if thread is yielded + */ + + auto thread_type = emu.new_usertype("thread", sol::call_constructor, sol::constructors>()); + thread_type.set("start", [](context &ctx, const char *scr) { + std::string script(scr); + if (ctx.busy) + return false; + std::thread th([&ctx, script]() { + sol::state thstate; + thstate.open_libraries(); + thstate["package"]["preload"]["zlib"] = &luaopen_zlib; + thstate["package"]["preload"]["lfs"] = &luaopen_lfs; + thstate["package"]["preload"]["linenoise"] = &luaopen_linenoise; + sol::load_result res = thstate.load(script); + if(res.valid()) + { + sol::protected_function func = res.get(); + thstate["yield"] = [&ctx, &thstate]() { + std::mutex m; + std::unique_lock lock(m); + ctx.result = thstate["status"]; + ctx.yield = true; + ctx.sync.wait(lock); + ctx.yield = false; + thstate["status"] = ctx.result; + }; + auto ret = func(); + if (ret.valid()) + { + const char *tmp = ret.get(); + if (tmp != nullptr) + ctx.result = tmp; + else + osd_printf_error("[LUA ERROR] in thread: return value must be string\n"); + } + else + { + sol::error err = ret; + osd_printf_error("[LUA ERROR] in thread: %s\n", err.what()); + } + } + else + { + sol::error err = res; + osd_printf_error("[LUA ERROR] when loading script for thread: %s\n", err.what()); + } + ctx.busy = false; + }); + ctx.busy = true; + ctx.yield = false; + th.detach(); + return true; + }); + thread_type.set("continue", [](context &ctx, const char *val) { + if (!ctx.yield) + return; + ctx.result = val; + ctx.sync.notify_all(); + }); + thread_type.set("result", sol::property([](context &ctx) -> std::string { + if (ctx.busy && !ctx.yield) + return ""; + return ctx.result; + })); + thread_type.set("busy", sol::readonly(&context::busy)); + thread_type.set("yield", sol::readonly(&context::yield)); + + +/* save_item library + * + * emu.item(item_index) + * + * item.size - size of the raw data type + * item.count - number of entries + * + * item:read(offset) - read entry value by index + * item:read_block(offset, count) - read a block of entry values as a string (byte addressing) + * item:write(offset, value) - write entry value by index + */ + + auto item_type = emu.new_usertype("item", sol::call_constructor, sol::initializers([this](save_item &item, int index) { + if(machine().save().indexed_item(index, item.base, item.size, item.valcount, item.blockcount, item.stride)) + { + item.count = item.valcount * item.blockcount; + } + else + { + item.base = nullptr; + item.size = 0; + item.count = 0; + item.valcount = 0; + item.blockcount = 0; + item.stride = 0; + } + })); + item_type.set("size", sol::readonly(&save_item::size)); + item_type.set("count", sol::readonly(&save_item::count)); + item_type.set("read", [this](save_item &item, int offset) -> sol::object { + if(!item.base || (offset >= item.count)) + return sol::lua_nil; + const void *const data = reinterpret_cast(item.base) + (item.stride * (offset / item.valcount)); + uint64_t ret = 0; + switch(item.size) + { + case 1: + default: + ret = reinterpret_cast(data)[offset % item.valcount]; + break; + case 2: + ret = reinterpret_cast(data)[offset % item.valcount]; + break; + case 4: + ret = reinterpret_cast(data)[offset % item.valcount]; + break; + case 8: + ret = reinterpret_cast(data)[offset % item.valcount]; + break; + } + return sol::make_object(sol(), ret); + }); + item_type.set("read_block", [](save_item &item, int offset, sol::buffer *buff) { + if(!item.base || ((offset + buff->get_len()) > (item.size * item.count))) + { + buff->set_len(0); + } + else + { + const uint32_t blocksize = item.size * item.valcount; + uint32_t remaining = buff->get_len(); + uint8_t *dest = reinterpret_cast(buff->get_ptr()); + while(remaining) + { + const uint32_t blockno = offset / blocksize; + const uint32_t available = blocksize - (offset % blocksize); + const uint32_t chunk = (available < remaining) ? available : remaining; + const void *const source = reinterpret_cast(item.base) + (blockno * item.stride) + (offset % blocksize); + std::memcpy(dest, source, chunk); + offset += chunk; + remaining -= chunk; + dest += chunk; + } + } + return buff; + }); + item_type.set("write", [](save_item &item, int offset, uint64_t value) { + if(!item.base || (offset >= item.count)) + return; + void *const data = reinterpret_cast(item.base) + (item.stride * (offset / item.valcount)); + switch(item.size) + { + case 1: + default: + reinterpret_cast(data)[offset % item.valcount] = uint8_t(value); + break; + case 2: + reinterpret_cast(data)[offset % item.valcount] = uint16_t(value); + break; + case 4: + reinterpret_cast(data)[offset % item.valcount] = uint32_t(value); + break; + case 8: + reinterpret_cast(data)[offset % item.valcount] = uint64_t(value); + break; + } + }); + + +/* core_options library + * + * manager:options() + * manager:machine():options() + * manager:ui():options() + * manager:plugins() + * + * options:help() - get help for options + * options:command(command) - return output for command + * + * options.entries[] - get table of option entries (k=name, v=core_options::entry) + */ + + auto core_options_type = sol().registry().new_usertype("core_options", "new", sol::no_constructor); + core_options_type.set("help", &core_options::output_help); + core_options_type.set("command", &core_options::command); + core_options_type.set("entries", sol::property([this](core_options &options) { + sol::table table = sol().create_table(); + int unadorned_index = 0; + for (auto &curentry : options.entries()) + { + const char *name = curentry->names().size() > 0 + ? curentry->name().c_str() + : nullptr; + bool is_unadorned = false; + // check if it's unadorned + if (name && strlen(name) && !strcmp(name, options.unadorned(unadorned_index))) + { + unadorned_index++; + is_unadorned = true; + } + if (curentry->type() != core_options::option_type::HEADER && curentry->type() != core_options::option_type::COMMAND && !is_unadorned) + table[name] = &*curentry; + } + return table; + })); + + +/* emu_options library + * + * manager:options() + * manager:machine():options() + * + * options:slot_option(tag) - retrieves a specific slot option + */ + + auto emu_options_type = sol().registry().new_usertype("emu_options", sol::no_constructor, sol::base_classes, sol::bases()); + emu_options_type["slot_option"] = [] (emu_options &opts, std::string const &name) { return opts.find_slot_option(name); }; + + +/* slot_option library + * + * manager:options():slot_option("name") + * manager:machine():options():slot_option("name") + * + * slot_option:specify(card, bios) - specifies the value of the slot, potentially causing a recalculation + * + * slot_option.value - the actual value of the option, after being interpreted + * slot_option.specified_value - the value of the option, as specified from outside + * slot_option.bios - the bios, if any, associated with the slot + * slot_option.default_card_software - the software list item that is associated with this option, by default + */ + + auto slot_option_type = sol().registry().new_usertype("slot_option", sol::no_constructor); + slot_option_type["specify"] = + [] (slot_option &opt, std::string &&text, char const *bios) + { + opt.specify(std::move(text)); + if (bios) + opt.set_bios(bios); + }; + slot_option_type["value"] = sol::property(&slot_option::value); + slot_option_type["specified_value"] = sol::property(&slot_option::specified_value); + slot_option_type["bios"] = sol::property(&slot_option::bios); + slot_option_type["default_card_software"] = sol::property(&slot_option::default_card_software); + + +/* core_options::entry library + * + * options.entries[entry_name] + * + * entry:value() - get value of entry + * entry:value(val) - set entry to val + * entry:description() - get info about entry + * entry:default_value() - get default for entry + * entry:minimum() - get min value for entry + * entry:maximum() - get max value for entry + * entry:has_range() - are min and max valid for entry + */ + + auto core_options_entry_type = sol().registry().new_usertype("core_options_entry", "new", sol::no_constructor); + core_options_entry_type.set("value", sol::overload( + [this](core_options::entry &e, bool val) { + if(e.type() != OPTION_BOOLEAN) + luaL_error(m_lua_state, "Cannot set option to wrong type"); + else + e.set_value(val ? "1" : "0", OPTION_PRIORITY_CMDLINE); + }, + [this](core_options::entry &e, float val) { + if(e.type() != OPTION_FLOAT) + luaL_error(m_lua_state, "Cannot set option to wrong type"); + else + e.set_value(string_format("%f", val), OPTION_PRIORITY_CMDLINE); + }, + [this](core_options::entry &e, int val) { + if(e.type() != OPTION_INTEGER) + luaL_error(m_lua_state, "Cannot set option to wrong type"); + else + e.set_value(string_format("%d", val), OPTION_PRIORITY_CMDLINE); + }, + [this](core_options::entry &e, const char *val) { + if(e.type() != OPTION_STRING) + luaL_error(m_lua_state, "Cannot set option to wrong type"); + else + e.set_value(val, OPTION_PRIORITY_CMDLINE); + }, + [this](core_options::entry &e) -> sol::object { + if (e.type() == core_options::option_type::INVALID) + return sol::lua_nil; + switch(e.type()) + { + case core_options::option_type::BOOLEAN: + return sol::make_object(sol(), atoi(e.value()) != 0); + case core_options::option_type::INTEGER: + return sol::make_object(sol(), atoi(e.value())); + case core_options::option_type::FLOAT: + return sol::make_object(sol(), atof(e.value())); + default: + return sol::make_object(sol(), e.value()); + } + })); + core_options_entry_type.set("description", &core_options::entry::description); + core_options_entry_type.set("default_value", &core_options::entry::default_value); + core_options_entry_type.set("minimum", &core_options::entry::minimum); + core_options_entry_type.set("maximum", &core_options::entry::maximum); + core_options_entry_type.set("has_range", &core_options::entry::has_range); + + + auto machine_type = sol().registry().new_usertype("machine", sol::no_constructor); + machine_type["exit"] = &running_machine::schedule_exit; + machine_type["hard_reset"] = &running_machine::schedule_hard_reset; + machine_type["soft_reset"] = &running_machine::schedule_soft_reset; + machine_type["save"] = &running_machine::schedule_save; // TODO: some kind of completion notification? + machine_type["load"] = &running_machine::schedule_load; // TODO: some kind of completion notification? + machine_type["buffer_save"] = + [] (running_machine &m, sol::this_state s) + { + // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow + // right now it's broken by anonymous timers, synchronize, etc. + lua_State *L = s; + luaL_Buffer buff; + int size = ram_state::get_size(m.save()); + u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size); + save_error error = m.save().write_buffer(ptr, size); + if (error == STATERR_NONE) + { + luaL_pushresultsize(&buff, size); + return sol::make_reference(L, sol::stack_reference(L, -1)); + } + luaL_error(L, "State save error."); + return sol::make_reference(L, nullptr); + }; + machine_type["buffer_load"] = + [] (running_machine &m, sol::this_state s, std::string str) + { + // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow + // right now it's broken by anonymous timers, synchronize, etc. + save_error error = m.save().read_buffer((u8 *)str.data(), str.size()); + if (error == STATERR_NONE) + { + return true; + } + else + { + luaL_error(s,"State load error."); + return false; + } + }; + machine_type["popmessage"] = + [] (running_machine &m, const char *str) + { + if (str) + m.popmessage("%s", str); + else + m.popmessage(); + }; + machine_type["logerror"] = [] (running_machine &m, std::string const *str) { m.logerror("[luaengine] %s\n", str); }; + machine_type["time"] = sol::property(&running_machine::time); + machine_type["system"] = sol::property(&running_machine::system); + machine_type["parameters"] = sol::property(&running_machine::parameters); + machine_type["video"] = sol::property(&running_machine::video); + machine_type["sound"] = sol::property(&running_machine::sound); + machine_type["output"] = sol::property(&running_machine::output); + machine_type["memory"] = sol::property(&running_machine::memory); + machine_type["ioport"] = sol::property(&running_machine::ioport); + machine_type["input"] = sol::property(&running_machine::input); + machine_type["natkeyboard"] = sol::property(&running_machine::natkeyboard); + machine_type["uiinput"] = sol::property(&running_machine::ui_input); + machine_type["render"] = sol::property(&running_machine::render); + machine_type["debugger"] = sol::property( + [] (running_machine &m, sol::this_state s) -> sol::object + { + if (m.debug_flags & DEBUG_FLAG_ENABLED) + return sol::make_object(s, &m.debugger()); + else + return sol::lua_nil; + }); + machine_type["options"] = sol::property(&running_machine::options); + machine_type["samplerate"] = sol::property(&running_machine::sample_rate); + machine_type["paused"] = sol::property(&running_machine::paused); + machine_type["exit_pending"] = sol::property(&running_machine::exit_pending); + machine_type["hard_reset_pending"] = sol::property(&running_machine::hard_reset_pending); + machine_type["devices"] = sol::property([] (running_machine &m) { return devenum(m.root_device()); }); + machine_type["screens"] = sol::property([] (running_machine &m) { return devenum(m.root_device()); }); + machine_type["cassettes"] = sol::property([] (running_machine &m) { return devenum(m.root_device()); }); + machine_type["images"] = sol::property([] (running_machine &m) { return devenum(m.root_device()); }); + machine_type["slots"] = sol::property([](running_machine &m) { return devenum(m.root_device()); }); + + + auto game_driver_type = sol().registry().new_usertype("game_driver", sol::no_constructor); + game_driver_type["name"] = sol::property([] (game_driver const &driver) { return &driver.name[0]; }); + game_driver_type["description"] = sol::property([] (game_driver const &driver) { return &driver.type.fullname()[0]; }); + game_driver_type["year"] = sol::readonly(&game_driver::year); + game_driver_type["manufacturer"] = sol::readonly(&game_driver::manufacturer); + game_driver_type["parent"] = sol::readonly(&game_driver::parent); + game_driver_type["compatible_with"] = sol::property([] (game_driver const &driver) { return strcmp(driver.compatible_with, "0") ? driver.compatible_with : nullptr; }); + game_driver_type["source_file"] = sol::property([] (game_driver const &driver) { return &driver.type.source()[0]; }); + game_driver_type["orientation"] = sol::property( + [] (game_driver const &driver) + { + // FIXME: this works differently to the screen orientation function and the render target orientation property + // it should probably be made consistent with one of them + std::string rot; + switch (driver.flags & machine_flags::MASK_ORIENTATION) + { + case machine_flags::ROT0: + rot = "rot0"; + break; + case machine_flags::ROT90: + rot = "rot90"; + break; + case machine_flags::ROT180: + rot = "rot180"; + break; + case machine_flags::ROT270: + rot = "rot270"; + break; + default: + rot = "undefined"; + break; + } + return rot; + }); + game_driver_type["type"] = sol::property( + [] (game_driver const &driver) + { + // FIXME: this shouldn't be called type - there's potendial for confusion with the device type + // also, this should eventually go away in favour of richer flags + std::string type; + switch (driver.flags & machine_flags::MASK_TYPE) + { + case machine_flags::TYPE_ARCADE: + type = "arcade"; + break; + case machine_flags::TYPE_CONSOLE: + type = "console"; + break; + case machine_flags::TYPE_COMPUTER: + type = "computer"; + break; + default: + type = "other"; + break; + } + return type; + }); + game_driver_type["not_working"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NOT_WORKING) != 0; }); + game_driver_type["supports_save"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::SUPPORTS_SAVE) != 0; }); + game_driver_type["no_cocktail"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) != 0; }); + game_driver_type["is_bios_root"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) != 0; }); + game_driver_type["requires_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) != 0; }); + game_driver_type["clickable_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::CLICKABLE_ARTWORK) != 0; }); + game_driver_type["unofficial"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) != 0; }); + game_driver_type["no_sound_hw"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) != 0; }); + game_driver_type["mechanical"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) != 0; }); + game_driver_type["is_incomplete"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_INCOMPLETE) != 0; }); + + + auto device_type = sol().registry().new_usertype("device", sol::no_constructor); + device_type["subtag"] = &device_t::subtag; + device_type["siblingtag"] = &device_t::siblingtag; + device_type["memregion"] = &device_t::memregion; + device_type["memshare"] = &device_t::memshare; + device_type["membank"] = &device_t::membank; + device_type["ioport"] = &device_t::ioport; + device_type["subdevice"] = static_cast(&device_t::subdevice); + device_type["siblingdevice"] = static_cast(&device_t::siblingdevice); + device_type["parameter"] = &device_t::parameter; + device_type["tag"] = sol::property(&device_t::tag); + device_type["basetag"] = sol::property(&device_t::basetag); + device_type["name"] = sol::property(&device_t::name); + device_type["shortname"] = sol::property(&device_t::shortname); + device_type["owner"] = sol::property(&device_t::owner); + device_type["configured"] = sol::property(&device_t::configured); + device_type["started"] = sol::property(&device_t::started); + device_type["debug"] = sol::property( + [] (device_t &dev, sol::this_state s) -> sol::object + { + if (!(dev.machine().debug_flags & DEBUG_FLAG_ENABLED) || !dynamic_cast(&dev)) // debugger not enabled or not CPU + return sol::lua_nil; + return sol::make_object(s, dev.debug()); + }); + device_type["spaces"] = sol::property( + [this] (device_t &dev) + { + device_memory_interface *const memdev = dynamic_cast(&dev); + sol::table sp_table = sol().create_table(); + if (!memdev) + return sp_table; + for (int sp = 0; sp < memdev->max_space_count(); ++sp) + { + if (memdev->has_space(sp)) + sp_table[memdev->space(sp).name()] = addr_space(memdev->space(sp), *memdev); + } + return sp_table; + }); + // FIXME: improve this + device_type["state"] = sol::property( + [this] (device_t &dev) + { + sol::table st_table = sol().create_table(); + const device_state_interface *state; + if(!dev.interface(state)) + return st_table; + // XXX: refrain from exporting non-visible entries? + for(auto &s : state->state_entries()) + st_table[s->symbol()] = s.get(); + return st_table; + }); + // FIXME: turn into a wrapper - it's stupid slow to walk on every property access + // also, this mixes up things like RAM areas with stuff saved by the device itself, so there's potential for key conflicts + device_type["items"] = sol::property( + [this] (device_t &dev) + { + sol::table table = sol().create_table(); + std::string const tag = dev.tag(); + for (int i = 0; ; i++) + { + char const *item; + void *base; + uint32_t size, valcount, blockcount, stride; + item = dev.machine().save().indexed_item(i, base, size, valcount, blockcount, stride); + if (!item) + break; + + char const *name = &strchr(item, '/')[1]; + if (!strncmp(tag.c_str(), name, tag.length()) && (name[tag.length()] == '/')) + table[name + tag.length() + 1] = i; + } + return table; + }); + // FIXME: this is useless in its current form + device_type["roms"] = sol::property( + [this] (device_t &dev) + { + sol::table table = sol().create_table(); + for (auto rom : dev.rom_region_vector()) + if (!rom.name().empty()) + table[rom.name()] = rom; + return table; + }); + + + auto screen_dev_type = sol().registry().new_usertype( + "screen_dev", + sol::no_constructor, + sol::base_classes, sol::bases()); + screen_dev_type["draw_box"] = + [] (screen_device &sdev, float x1, float y1, float x2, float y2, std::optional fgcolor, std::optional bgcolor) + { + float const sc_width(sdev.visible_area().width()); + float const sc_height(sdev.visible_area().height()); + x1 = std::clamp(x1, 0.0f, sc_width) / sc_width; + y1 = std::clamp(y1, 0.0f, sc_height) / sc_height; + x2 = std::clamp(x2, 0.0f, sc_width) / sc_width; + y2 = std::clamp(y2, 0.0f, sc_height) / sc_height; + mame_ui_manager &ui(mame_machine_manager::instance()->ui()); + if (!fgcolor) + fgcolor = ui.colors().text_color(); + if (!bgcolor) + bgcolor = ui.colors().background_color(); + ui.draw_outlined_box(sdev.container(), x1, y1, x2, y2, *fgcolor, *bgcolor); + }; + screen_dev_type["draw_line"] = + [] (screen_device &sdev, float x1, float y1, float x2, float y2, std::optional color) + { + float const sc_width(sdev.visible_area().width()); + float const sc_height(sdev.visible_area().height()); + x1 = std::clamp(x1, 0.0f, sc_width) / sc_width; + y1 = std::clamp(y1, 0.0f, sc_height) / sc_height; + x2 = std::clamp(x2, 0.0f, sc_width) / sc_width; + y2 = std::clamp(y2, 0.0f, sc_height) / sc_height; + if (!color) + color = mame_machine_manager::instance()->ui().colors().text_color(); + sdev.container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(*color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + }; + screen_dev_type["draw_text"] = + [this] (screen_device &sdev, sol::object xobj, float y, char const *msg, std::optional fgcolor, std::optional bgcolor) + { + float const sc_width(sdev.visible_area().width()); + float const sc_height(sdev.visible_area().height()); + auto justify = ui::text_layout::text_justify::LEFT; + float x = 0; + if (xobj.is()) + { + x = std::clamp(xobj.as(), 0.0f, sc_width) / sc_width; + } + else if (xobj.is()) + { + char const *const justifystr(xobj.as()); + if (!strcmp(justifystr, "left")) + justify = ui::text_layout::text_justify::LEFT; + else if (!strcmp(justifystr, "right")) + justify = ui::text_layout::text_justify::RIGHT; + else if (!strcmp(justifystr, "center")) + justify = ui::text_layout::text_justify::CENTER; + } + else + { + luaL_error(m_lua_state, "Error in param 1 to draw_text"); + return; + } + y = std::clamp(y, 0.0f, sc_height) / sc_height; + mame_ui_manager &ui(mame_machine_manager::instance()->ui()); + if (!fgcolor) + fgcolor = ui.colors().text_color(); + if (!bgcolor) + bgcolor = 0; + ui.draw_text_full( + sdev.container(), + msg, + x, y, (1.0f - x), + justify, ui::text_layout::word_wrapping::WORD, + mame_ui_manager::OPAQUE_, *fgcolor, *bgcolor); + }; + screen_dev_type["orientation"] = + [] (screen_device &sdev) + { + uint32_t flags = sdev.orientation(); + int rotation_angle = 0; + switch (flags) + { + case ORIENTATION_SWAP_XY: + case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X: + rotation_angle = 90; + flags ^= ORIENTATION_FLIP_X; + break; + case ORIENTATION_FLIP_Y: + case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y: + rotation_angle = 180; + flags ^= ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y; + break; + case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y: + case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y: + rotation_angle = 270; + flags ^= ORIENTATION_FLIP_Y; + break; + } + return std::tuple(rotation_angle, flags & ORIENTATION_FLIP_X, flags & ORIENTATION_FLIP_Y); + }; + screen_dev_type["time_until_pos"] = sol::overload( + [] (screen_device &sdev, int vpos) { return sdev.time_until_pos(vpos).as_double(); }, + [] (screen_device &sdev, int vpos, int hpos) { return sdev.time_until_pos(vpos, hpos).as_double(); }); + screen_dev_type["time_until_vblank_start"] = &screen_device::time_until_vblank_start; + screen_dev_type["time_until_vblank_end"] = &screen_device::time_until_vblank_end; + screen_dev_type["snapshot"] = + [this] (screen_device &sdev, char const *filename) -> sol::object + { + // FIXME: this shouldn't be a member of the screen device + // the screen is only used as a hint when configured for native snapshots and may be ignored + std::string snapstr; + bool is_absolute_path = false; + if (filename) + { + // a filename was specified; if it isn't absolute post-process it + snapstr = process_snapshot_filename(machine(), filename); + is_absolute_path = osd_is_absolute_path(snapstr); + } + + // open the file + emu_file file(is_absolute_path ? "" : machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + std::error_condition filerr; + if (!snapstr.empty()) + filerr = file.open(snapstr); + else + filerr = machine().video().open_next(file, "png"); + if (filerr) + return sol::make_object(sol(), filerr); + + // and save the snapshot + machine().video().save_snapshot(&sdev, file); + return sol::lua_nil; + }; + screen_dev_type["pixel"] = [] (screen_device &sdev, s32 x, s32 y) { return sdev.pixel(x, y); }; + screen_dev_type["pixels"] = + [] (screen_device &sdev, sol::this_state s) + { + // TODO: would be better if this could return a tuple of (buffer, width, height) + const rectangle &visarea = sdev.visible_area(); + luaL_Buffer buff; + int size = visarea.height() * visarea.width() * 4; + u32 *ptr = (u32 *)luaL_buffinitsize(s, &buff, size); + sdev.pixels(ptr); + luaL_pushresultsize(&buff, size); + return sol::make_reference(s, sol::stack_reference(s, -1)); + }; + screen_dev_type["screen_type"] = sol::property(&screen_device::screen_type); + screen_dev_type["width"] = sol::property([] (screen_device &sdev) { return sdev.visible_area().width(); }); + screen_dev_type["height"] = sol::property([] (screen_device &sdev) { return sdev.visible_area().height(); }); + screen_dev_type["refresh"] = sol::property([] (screen_device &sdev) { return ATTOSECONDS_TO_HZ(sdev.refresh_attoseconds()); }); + screen_dev_type["refresh_attoseconds"] = sol::property([] (screen_device &sdev) { return sdev.refresh_attoseconds(); }); + screen_dev_type["xofffset"] = sol::property(&screen_device::xoffset); + screen_dev_type["yofffset"] = sol::property(&screen_device::yoffset); + screen_dev_type["xscale"] = sol::property(&screen_device::xscale); + screen_dev_type["yscale"] = sol::property(&screen_device::yscale); + screen_dev_type["pixel_period"] = sol::property([] (screen_device &sdev) { return sdev.pixel_period().as_double(); }); + screen_dev_type["scan_period"] = sol::property([] (screen_device &sdev) { return sdev.scan_period().as_double(); }); + screen_dev_type["frame_period"] = sol::property([] (screen_device &sdev) { return sdev.frame_period().as_double(); }); + screen_dev_type["frame_number"] = &screen_device::frame_number; + screen_dev_type["container"] = sol::property(&screen_device::container); + + + auto cass_type = sol().registry().new_usertype( + "cassette", + sol::no_constructor, + sol::base_classes, sol::bases()); + cass_type["stop"] = [] (cassette_image_device &c) { c.change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); }; + cass_type["play"] = [] (cassette_image_device &c) { c.change_state(CASSETTE_PLAY, CASSETTE_MASK_UISTATE); }; + cass_type["record"] = [] (cassette_image_device &c) { c.change_state(CASSETTE_RECORD, CASSETTE_MASK_UISTATE); }; + cass_type["forward"] = &cassette_image_device::go_forward; + cass_type["reverse"] = &cassette_image_device::go_reverse; + cass_type["seek"] = [] (cassette_image_device &c, double time, const char* origin) { if (c.exists()) c.seek(time, s_seek_parser(origin)); }; + cass_type["is_stopped"] = sol::property(&cassette_image_device::is_stopped); + cass_type["is_playing"] = sol::property(&cassette_image_device::is_playing); + cass_type["is_recording"] = sol::property(&cassette_image_device::is_recording); + cass_type["motor_state"] = sol::property(&cassette_image_device::motor_on, &cassette_image_device::set_motor); + cass_type["speaker_state"] = sol::property(&cassette_image_device::speaker_on, &cassette_image_device::set_speaker); + cass_type["position"] = sol::property(&cassette_image_device::get_position); + cass_type["length"] = sol::property([] (cassette_image_device &c) { return c.exists() ? c.get_length() : 0.0; }); + + + auto image_type = sol().registry().new_usertype("image", sol::no_constructor); + image_type["load"] = &device_image_interface::load; + image_type["load_software"] = static_cast(&device_image_interface::load_software); + image_type["unload"] = &device_image_interface::unload; + image_type["create"] = static_cast(&device_image_interface::create); + image_type["display"] = &device_image_interface::call_display; + image_type["is_readable"] = sol::property(&device_image_interface::is_readable); + image_type["is_writeable"] = sol::property(&device_image_interface::is_writeable); + image_type["is_creatable"] = sol::property(&device_image_interface::is_creatable); + image_type["must_be_loaded"] = sol::property(&device_image_interface::must_be_loaded); + image_type["is_reset_on_load"] = sol::property(&device_image_interface::is_reset_on_load); + image_type["image_type_name"] = sol::property(&device_image_interface::image_type_name); + image_type["instance_name"] = sol::property(&device_image_interface::instance_name); + image_type["brief_instance_name"] = sol::property(&device_image_interface::brief_instance_name); + image_type["formatlist"] = sol::property([] (device_image_interface &image) { return image_interface_formats(image); }); + image_type["exists"] = sol::property(&device_image_interface::exists); + image_type["readonly"] = sol::property(&device_image_interface::is_readonly); + image_type["filename"] = sol::property(&device_image_interface::filename); + image_type["crc"] = sol::property(&device_image_interface::crc); + image_type["loaded_through_softlist"] = sol::property(&device_image_interface::loaded_through_softlist); + image_type["software_list_name"] = sol::property(&device_image_interface::software_list_name); + image_type["software_longname"] = sol::property( + [] (device_image_interface &di) + { + software_info const *const si(di.software_entry()); + return si ? si->longname().c_str() : nullptr; + }); + image_type["software_publisher"] = sol::property( + [] (device_image_interface &di) + { + software_info const *const si(di.software_entry()); + return si ? si->publisher().c_str() : nullptr; + }); + image_type["software_year"] = sol::property( + [] (device_image_interface &di) + { + software_info const *const si(di.software_entry()); + return si ? si->year().c_str() : nullptr; + }); + image_type["software_parent"] = sol::property( + [] (device_image_interface &di) + { + software_info const *const si(di.software_entry()); + return si ? si->parentname().c_str() : nullptr; + }); + image_type["device"] = sol::property(static_cast(&device_image_interface::device)); + + + auto format_type = sol().registry().new_usertype("image_format", sol::no_constructor); + format_type["name"] = sol::property(&image_device_format::name); + format_type["description"] = sol::property(&image_device_format::description); + format_type["extensions"] = sol::property( + [this] (image_device_format const &format) + { + int index = 1; + sol::table option_table = sol().create_table(); + for (std::string const &ext : format.extensions()) + option_table[index++] = ext; + return option_table; + }); + format_type["option_spec"] = sol::property(&image_device_format::optspec); + + + auto slot_type = sol().registry().new_usertype("slot", sol::no_constructor); + slot_type["fixed"] = sol::property(&device_slot_interface::fixed); + slot_type["has_selectable_options"] = sol::property(&device_slot_interface::has_selectable_options); + slot_type["default_option"] = sol::property(&device_slot_interface::default_option); + slot_type["options"] = sol::property([] (device_slot_interface const &slot) { return standard_tag_object_ptr_map(slot.option_list()); }); + slot_type["device"] = sol::property(static_cast(&device_slot_interface::device)); + + + auto dislot_option_type = sol().registry().new_usertype("dislot_option", sol::no_constructor); + dislot_option_type["name"] = sol::property(&device_slot_interface::slot_option::name); + dislot_option_type["device_fullname"] = sol::property([] (device_slot_interface::slot_option &opt) { return opt.devtype().fullname(); }); + dislot_option_type["device_shortname"] = sol::property([] (device_slot_interface::slot_option &opt) { return opt.devtype().shortname(); }); + dislot_option_type["selectable"] = sol::property(&device_slot_interface::slot_option::selectable); + dislot_option_type["default_bios"] = sol::property(static_cast(&device_slot_interface::slot_option::default_bios)); + dislot_option_type["clock"] = sol::property(static_cast(&device_slot_interface::slot_option::clock)); + + + auto parameters_type = sol().registry().new_usertype("parameters", sol::no_constructor); + parameters_type["add"] = ¶meters_manager::add; + parameters_type["lookup"] = ¶meters_manager::lookup; + + + auto video_type = sol().registry().new_usertype("video", sol::no_constructor); + video_type["frame_update"] = [] (video_manager &vm) { vm.frame_update(true); }; + video_type["snapshot"] = &video_manager::save_active_screen_snapshots; + video_type["begin_recording"] = + [this] (video_manager &vm, const char *filename, const char *format_string) + { + // FIXME: the filename substitution shouldn't be done here + std::string fn; + movie_recording::format format = movie_recording::format::AVI; + if (filename) + fn = process_snapshot_filename(machine(), filename); + if (format_string) + format = s_movie_recording_format_parser(format_string); + vm.begin_recording(filename ? fn.c_str() : nullptr, format); + }; + video_type["end_recording"] = &video_manager::end_recording; + video_type["snapshot_size"] = + [] (video_manager &vm) + { + s32 width, height; + vm.compute_snapshot_size(width, height); + return std::make_tuple(width, height); + }; + video_type["snapshot_pixels"] = + [] (video_manager &vm, sol::this_state s) + { + // TODO: would be better if this could return a tuple of (buffer, width, height) + s32 width, height; + vm.compute_snapshot_size(width, height); + int size = width * height * 4; + luaL_Buffer buff; + u32 *ptr = (u32 *)luaL_buffinitsize(s, &buff, size); + vm.pixels(ptr); + luaL_pushresultsize(&buff, size); + return sol::make_reference(s, sol::stack_reference(s, -1)); + }; + video_type["speed_factor"] = sol::property(&video_manager::speed_factor); + video_type["throttled"] = sol::property(&video_manager::throttled, &video_manager::set_throttled); + video_type["throttle_rate"] = sol::property(&video_manager::throttle_rate, &video_manager::set_throttle_rate); + video_type["frameskip"] = sol::property(&video_manager::frameskip, &video_manager::set_frameskip); + video_type["speed_percent"] = sol::property(&video_manager::speed_percent); + video_type["effective_frameskip"] = sol::property(&video_manager::effective_frameskip); + video_type["skip_this_frame"] = sol::property(&video_manager::skip_this_frame); + video_type["snap_native"] = sol::property(&video_manager::snap_native); + video_type["is_recording"] = sol::property(&video_manager::is_recording); + video_type["snapshot_target"] = sol::property(&video_manager::snapshot_target); + + + auto sound_type = sol().registry().new_usertype("sound", sol::no_constructor); + sound_type["start_recording"] = + [] (sound_manager &sm, char const *filename) + { + return filename ? sm.start_recording(filename) : sm.start_recording(); + }; + sound_type["stop_recording"] = &sound_manager::stop_recording; + sound_type["get_samples"] = + [] (sound_manager &sm, sol::this_state s) + { + luaL_Buffer buff; + s32 const count = sm.sample_count() * 2 * 2; // 2 channels, 2 bytes per sample + s16 *const ptr = (s16 *)luaL_buffinitsize(s, &buff, count); + sm.samples(ptr); + luaL_pushresultsize(&buff, count); + return sol::make_reference(s, sol::stack_reference(s, -1)); + }; + sound_type["muted"] = sol::property(&sound_manager::muted); + sound_type["ui_mute"] = sol::property( + static_cast(&sound_manager::ui_mute), + static_cast(&sound_manager::ui_mute)); + sound_type["debugger_mute"] = sol::property( + static_cast(&sound_manager::debugger_mute), + static_cast(&sound_manager::debugger_mute)); + sound_type["system_mute"] = sol::property( + static_cast(&sound_manager::system_mute), + static_cast(&sound_manager::system_mute)); + sound_type["attenuation"] = sol::property( + &sound_manager::attenuation, + &sound_manager::set_attenuation); + sound_type["recording"] = sol::property(&sound_manager::is_recording); + + + auto ui_type = sol().registry().new_usertype("ui", sol::no_constructor); + // sol converts char32_t to a string + ui_type["get_char_width"] = [] (mame_ui_manager &m, uint32_t utf8char) { return m.get_char_width(utf8char); }; + ui_type["get_string_width"] = &mame_ui_manager::get_string_width; + ui_type["set_aggressive_input_focus"] = [](mame_ui_manager &m, bool aggressive_focus) { osd_set_aggressive_input_focus(aggressive_focus); }; + ui_type["get_general_input_setting"] = sol::overload( + // TODO: overload with sequence type string - parser isn't available here + [] (mame_ui_manager &ui, ioport_type type, int player) { return ui.get_general_input_setting(type, player, SEQ_TYPE_STANDARD); }, + [] (mame_ui_manager &ui, ioport_type type) { return ui.get_general_input_setting(type, 0, SEQ_TYPE_STANDARD); }); + ui_type["options"] = sol::property([] (mame_ui_manager &m) { return static_cast(&m.options()); }); + ui_type["line_height"] = sol::property(&mame_ui_manager::get_line_height); + ui_type["menu_active"] = sol::property(&mame_ui_manager::is_menu_active); + ui_type["single_step"] = sol::property(&mame_ui_manager::single_step, &mame_ui_manager::set_single_step); + ui_type["show_fps"] = sol::property(&mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps); + ui_type["show_profiler"] = sol::property(&mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler); + + +/* device_state_entry library + * + * manager:machine().devices[device_tag].state[state_name] + * + * state:name() - get device state name + * state:is_visible() - is state visible in debugger + * state:is_divider() - is state a divider + * + * state.value - get device state value + */ + + auto dev_state_type = sol().registry().new_usertype("dev_state", "new", sol::no_constructor); + dev_state_type.set("name", &device_state_entry::symbol); + dev_state_type.set("value", sol::property( + [this](device_state_entry &entry) -> uint64_t { + device_state_interface *state = entry.parent_state(); + if(state) + { + machine().save().dispatch_presave(); + return state->state_int(entry.index()); + } + return 0; + }, + [this](device_state_entry &entry, uint64_t val) { + device_state_interface *state = entry.parent_state(); + if(state) + { + state->set_state_int(entry.index(), val); + machine().save().dispatch_presave(); + } + })); + dev_state_type.set("is_visible", &device_state_entry::visible); + dev_state_type.set("is_divider", &device_state_entry::divider); + + +/* rom_entry library + * + * manager:machine().devices[device_tag].roms[rom] + * + * rom:name() + * rom:hashdata() - see hash.h + * rom:offset() + * rom:length() + * rom:flags() - see romentry.h + */ + + auto rom_entry_type = sol().registry().new_usertype("rom_entry", "new", sol::no_constructor); + rom_entry_type.set("name", &rom_entry::name); + rom_entry_type.set("hashdata", &rom_entry::hashdata); + rom_entry_type.set("offset", &rom_entry::get_offset); + rom_entry_type.set("length", &rom_entry::get_length); + rom_entry_type.set("flags", &rom_entry::get_flags); + + + auto output_type = sol().registry().new_usertype("output", sol::no_constructor); + output_type["set_value"] = &output_manager::set_value; + output_type["set_indexed_value"] = + [] (output_manager &o, char const *basename, int index, int value) + { + o.set_value(util::string_format("%s%d", basename, index), value); + }; + output_type["get_value"] = &output_manager::get_value; + output_type["get_indexed_value"] = + [] (output_manager &o, char const *basename, int index) + { + return o.get_value(util::string_format("%s%d", basename, index)); + }; + output_type["name_to_id"] = &output_manager::name_to_id; + output_type["id_to_name"] = &output_manager::id_to_name; + + + auto mame_manager_type = sol().registry().new_usertype("manager", sol::no_constructor); + mame_manager_type["machine"] = sol::property(&mame_machine_manager::machine); + mame_manager_type["ui"] = sol::property(&mame_machine_manager::ui); + mame_manager_type["options"] = sol::property(&mame_machine_manager::options); + mame_manager_type["plugins"] = sol::property([] (mame_machine_manager &m) { return plugin_options_plugins(m.plugins()); }); + sol()["manager"] = std::ref(*mame_machine_manager::instance()); + sol()["mame_manager"] = std::ref(*mame_machine_manager::instance()); + + + auto plugin_type = sol().registry().new_usertype("plugin", sol::no_constructor); + plugin_type["name"] = sol::readonly(&plugin_options::plugin::m_name); + plugin_type["description"] = sol::readonly(&plugin_options::plugin::m_description); + plugin_type["type"] = sol::readonly(&plugin_options::plugin::m_type); + plugin_type["directory"] = sol::readonly(&plugin_options::plugin::m_directory); + plugin_type["start"] = sol::readonly(&plugin_options::plugin::m_start); + + + // set up other user types + initialize_debug(emu); + initialize_input(emu); + initialize_memory(emu); + initialize_render(emu); +} + +//------------------------------------------------- +// frame_hook - called at each frame refresh, used to draw a HUD +//------------------------------------------------- +bool lua_engine::frame_hook() +{ + return execute_function("LUA_ON_FRAME_DONE"); +} + +//------------------------------------------------- +// close - close and cleanup of lua engine +//------------------------------------------------- + +void lua_engine::close() +{ + m_sol_state.reset(); + if (m_lua_state) + { + lua_settop(m_lua_state, 0); /* clear stack */ + lua_close(m_lua_state); + m_lua_state = nullptr; + } +} + +void lua_engine::resume(void *ptr, int nparam) +{ + lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, nparam); + lua_State *L = lua_tothread(m_lua_state, -1); + lua_pop(m_lua_state, 1); + int stat = lua_resume(L, nullptr, 0); + if((stat != LUA_OK) && (stat != LUA_YIELD)) + { + osd_printf_error("[LUA ERROR] in resume: %s\n", lua_tostring(L, -1)); + lua_pop(L, 1); + } + luaL_unref(m_lua_state, LUA_REGISTRYINDEX, nparam); +} + +void lua_engine::run(sol::load_result res) +{ + if(res.valid()) + { + auto ret = invoke(res.get()); + if(!ret.valid()) + { + sol::error err = ret; + osd_printf_error("[LUA ERROR] in run: %s\n", err.what()); + } + } + else + osd_printf_error("[LUA ERROR] %d loading Lua script\n", (int)res.status()); +} + +//------------------------------------------------- +// execute - load and execute script +//------------------------------------------------- + +void lua_engine::load_script(const char *filename) +{ + run(sol().load_file(filename)); +} + +//------------------------------------------------- +// execute_string - execute script from string +//------------------------------------------------- + +void lua_engine::load_string(const char *value) +{ + run(sol().load(value)); +} diff --git a/src/icludes/frontend/mame/luaengine.h b/src/icludes/frontend/mame/luaengine.h new file mode 100644 index 0000000..f61baa8 --- /dev/null +++ b/src/icludes/frontend/mame/luaengine.h @@ -0,0 +1,181 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic +/*************************************************************************** + + luaengine.h + + Controls execution of the core MAME system. + +***************************************************************************/ +#ifndef MAME_FRONTEND_MAME_LUAENGINE_H +#define MAME_FRONTEND_MAME_LUAENGINE_H + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOL_SAFE_USERTYPE 1 +#include "sol/sol.hpp" + +struct lua_State; + +class lua_engine +{ +public: + // helper structures + template struct devenum; + template struct simple_list_wrapper; + template struct tag_object_ptr_map; + template using standard_tag_object_ptr_map = tag_object_ptr_map > >; + template struct immutable_container_helper; + template struct immutable_collection_helper; + template struct immutable_sequence_helper; + + // construction/destruction + lua_engine(); + ~lua_engine(); + + void initialize(); + void load_script(const char *filename); + void load_string(const char *value); + + bool frame_hook(); + + std::optional menu_populate(const std::string &menu, std::vector > &menu_list, std::string &flags); + std::pair > menu_callback(const std::string &menu, int index, const std::string &event); + + void set_machine(running_machine *machine); + std::vector &get_menu() { return m_menu; } + void attach_notifiers(); + void on_frame_done(); + void on_sound_update(); + void on_periodic(); + bool on_missing_mandatory_image(const std::string &instance_name); + void on_machine_before_load_settings(); + + template + bool call_plugin(const std::string &name, T &&in, U &out) + { + bool ret = false; + sol::object outobj = call_plugin(name, sol::make_object(sol(), std::forward(in))); + if (outobj.is()) + { + out = outobj.as(); + ret = true; + } + return ret; + } + + template + bool call_plugin(const std::string &name, T &&in, std::vector &out) + { + bool ret = false; + sol::object outobj = call_plugin(name, sol::make_object(sol(), std::forward(in))); + if (outobj.is()) + { + for (auto &entry : outobj.as()) + { + if (entry.second.template is()) + { + out.push_back(entry.second.template as()); + ret = true; + } + } + } + return ret; + } + + // this can also check if a returned table contains type T + template + bool call_plugin_check(const std::string &name, U &&in, bool table = false) + { + bool ret = false; + sol::object outobj = call_plugin(name, sol::make_object(sol(), std::forward(in))); + if (outobj.is() && !table) + ret = true; + else if (outobj.is() && table) + { + // check just one entry, checking the whole thing shouldn't be necessary as this only supports homogeneous tables + if (outobj.as().begin().operator*().second.template is()) + ret = true; + } + return ret; + } + + template + void call_plugin_set(const std::string &name, T &&in) + { + call_plugin(name, sol::make_object(sol(), std::forward(in))); + } + + sol::state_view &sol() const { return *m_sol_state; } + +private: + template class enum_parser; + + struct addr_space; + + struct save_item { + void *base; + unsigned int size; + unsigned int count; + unsigned int valcount; + unsigned int blockcount; + unsigned int stride; + }; + + struct context + { + context() { busy = false; yield = false; } + std::string result; + std::condition_variable sync; + bool busy; + bool yield; + }; + + // internal state + lua_State *m_lua_state; + std::unique_ptr m_sol_state; + running_machine *m_machine; + + std::vector m_menu; + + template + auto make_simple_callback_setter(void (T::*setter)(delegate &&), D &&dflt, const char *name, const char *desc); + + running_machine &machine() const { return *m_machine; } + + void on_machine_prestart(); + void on_machine_start(); + void on_machine_stop(); + void on_machine_pause(); + void on_machine_resume(); + void on_machine_frame(); + + void resume(void *ptr, int nparam); + void register_function(sol::function func, const char *id); + int enumerate_functions(const char *id, std::function &&callback); + bool execute_function(const char *id); + sol::object call_plugin(const std::string &name, sol::object in); + + void close(); + + void run(sol::load_result res); + + template + sol::protected_function_result invoke(TFunc &&func, TArgs&&... args); + + void initialize_debug(sol::table &emu); + void initialize_input(sol::table &emu); + void initialize_memory(sol::table &emu); + void initialize_render(sol::table &emu); +}; + +#endif // MAME_FRONTEND_MAME_LUAENGINE_H diff --git a/src/icludes/frontend/mame/luaengine.ipp b/src/icludes/frontend/mame/luaengine.ipp new file mode 100644 index 0000000..10d1f55 --- /dev/null +++ b/src/icludes/frontend/mame/luaengine.ipp @@ -0,0 +1,536 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic +/*************************************************************************** + + luaengine.ipp + + Controls execution of the core MAME system. + +***************************************************************************/ +#ifndef MAME_FRONTEND_MAME_LUAENGINE_IPP +#define MAME_FRONTEND_MAME_LUAENGINE_IPP + +#include "luaengine.h" + +#include "options.h" + +#include + +#include + + + +template +struct lua_engine::simple_list_wrapper +{ + simple_list_wrapper(simple_list const &l) : list(l) { } + + simple_list const &list; +}; + + +template +struct lua_engine::tag_object_ptr_map +{ + tag_object_ptr_map(T const &m) : map(m) { } + + T const ↦ +}; + + +namespace sol { + +class buffer +{ +public: + // sol does lua_settop(0), save userdata buffer in registry if necessary + buffer(int size, lua_State *L) + { + ptr = luaL_buffinitsize(L, &buff, size); + len = size; + if(buff.b != buff.initb) + { + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp"); + } + } + ~buffer() + { + lua_State *L = buff.L; + if(lua_getfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp") != LUA_TNIL) + { + lua_pushnil(L); + lua_setfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp"); + } + else + lua_pop(L, -1); + + luaL_pushresultsize(&buff, len); + } + + void set_len(int size) { len = size; } + int get_len() { return len; } + char *get_ptr() { return ptr; } + +private: + luaL_Buffer buff; + int len; + char *ptr; +}; + + +// don't convert core_optons to a table directly +template <> struct is_container : std::false_type { }; + +// buffer customisation +sol::buffer *sol_lua_get(sol::types, lua_State *L, int index, sol::stack::record &tracking); +int sol_lua_push(sol::types, lua_State *L, buffer *value); + + +// these things should be treated as containers +template struct is_container > : std::true_type { }; +template struct is_container > : std::true_type { }; +template struct is_container > : std::true_type { }; + + +template struct usertype_container >; + + +template +struct usertype_container > : lua_engine::immutable_collection_helper, simple_list const, typename simple_list::auto_iterator> +{ +private: + static int next_pairs(lua_State *L) + { + typename usertype_container::indexed_iterator &i(stack::unqualified_get >(L, 1)); + if (i.src.end() == i.it) + return stack::push(L, lua_nil); + int result; + result = stack::push(L, i.ix + 1); + result += stack::push_reference(L, *i.it); + ++i; + return result; + } + +public: + static int at(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + std::ptrdiff_t const index(stack::unqualified_get(L, 2)); + if ((0 >= index) || (self.list.count() < index)) + return stack::push(L, lua_nil); + else + return stack::push_reference(L, *self.list.find(index - 1)); + } + + static int get(lua_State *L) { return at(L); } + static int index_get(lua_State *L) { return at(L); } + + static int index_of(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + T &target(stack::unqualified_get(L, 2)); + int const found(self.list.indexof(target)); + if (0 > found) + return stack::push(L, lua_nil); + else + return stack::push(L, found + 1); + } + + static int size(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + return stack::push(L, self.list.count()); + } + + static int empty(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + return stack::push(L, self.list.empty()); + } + + static int next(lua_State *L) { return stack::push(L, next_pairs); } + static int pairs(lua_State *L) { return ipairs(L); } + + static int ipairs(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + stack::push(L, next_pairs); + stack::push >(L, self.list, self.list.begin()); + stack::push(L, lua_nil); + return 3; + } +}; + + +template +struct usertype_container > : lua_engine::immutable_collection_helper, T const, typename T::const_iterator> +{ +private: + template + static int next_pairs(lua_State *L) + { + typename usertype_container::indexed_iterator &i(stack::unqualified_get >(L, 1)); + if (i.src.end() == i.it) + return stack::push(L, lua_nil); + int result; + if constexpr (Indexed) + result = stack::push(L, i.ix + 1); + else + result = stack::push(L, i.it->first); + result += stack::push_reference(L, *i.it->second); + ++i; + return result; + } + + template + static int start_pairs(lua_State *L) + { + lua_engine::tag_object_ptr_map &self(usertype_container::get_self(L)); + stack::push(L, next_pairs); + stack::push >(L, self.map, self.map.begin()); + stack::push(L, lua_nil); + return 3; + } + +public: + static int at(lua_State *L) + { + lua_engine::tag_object_ptr_map &self(usertype_container::get_self(L)); + std::ptrdiff_t const index(stack::unqualified_get(L, 2)); + if ((0 >= index) || (self.map.size() < index)) + return stack::push(L, lua_nil); + auto const found(std::next(self.map.begin(), index - 1)); + if (!found->second) + return stack::push(L, lua_nil); + else + return stack::push_reference(L, *found->second); + } + + static int get(lua_State *L) + { + lua_engine::tag_object_ptr_map &self(usertype_container::get_self(L)); + char const *const tag(stack::unqualified_get(L)); + auto const found(self.map.find(tag)); + if ((self.map.end() == found) || !found->second) + return stack::push(L, lua_nil); + else + return stack::push_reference(L, *found->second); + } + + static int index_get(lua_State *L) + { + return get(L); + } + + static int index_of(lua_State *L) + { + lua_engine::tag_object_ptr_map &self(usertype_container::get_self(L)); + auto &obj(stack::unqualified_getsecond)>(L, 2)); + auto it(self.map.begin()); + std::ptrdiff_t ix(0); + while ((self.map.end() != it) && (it->second.get() != &obj)) + { + ++it; + ++ix; + } + if (self.map.end() == it) + return stack::push(L, lua_nil); + else + return stack::push(L, ix + 1); + } + + static int size(lua_State *L) + { + lua_engine::tag_object_ptr_map &self(usertype_container::get_self(L)); + return stack::push(L, self.map.size()); + } + + static int empty(lua_State *L) + { + lua_engine::tag_object_ptr_map &self(usertype_container::get_self(L)); + return stack::push(L, self.map.empty()); + } + + static int next(lua_State *L) { return stack::push(L, next_pairs); } + static int pairs(lua_State *L) { return start_pairs(L); } + static int ipairs(lua_State *L) { return start_pairs(L); } +}; + +} // namespace sol + + +// automatically convert std::error_condition to string +int sol_lua_push(sol::types, lua_State &L, std::error_condition &&value); + +// enums to automatically convert to strings +int sol_lua_push(sol::types, lua_State *L, map_handler_type &&value); +int sol_lua_push(sol::types, lua_State *L, image_init_result &&value); +int sol_lua_push(sol::types, lua_State *L, image_verify_result &&value); +int sol_lua_push(sol::types, lua_State *L, endianness_t &&value); + + +template +struct lua_engine::immutable_container_helper +{ +protected: + static T &get_self(lua_State *L) + { + auto p(sol::stack::unqualified_check_get(L, 1)); + if (!p) + luaL_error(L, "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)", sol::detail::demangle().c_str()); + if (!*p) + luaL_error(L, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a '%s' type", sol::detail::demangle().c_str()); + return **p; + } + +public: + static int set(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'set(key, value)' on type '%s': container is not modifiable", sol::detail::demangle().c_str()); + } + + static int index_set(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s': container is not modifiable", sol::detail::demangle().c_str()); + } + + static int add(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'add' on type '%s': container is not modifiable", sol::detail::demangle().c_str()); + } + + static int insert(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'insert' on type '%s': container is not modifiable", sol::detail::demangle().c_str()); + } + + static int find(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'find' on type '%s': no supported comparison operator for the value type", sol::detail::demangle().c_str()); + } + + static int index_of(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'index_of' on type '%s': no supported comparison operator for the value type", sol::detail::demangle().c_str()); + } + + static int clear(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'clear' on type '%s': container is not modifiable", sol::detail::demangle().c_str()); + } + + static int erase(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'erase' on type '%s': container is not modifiable", sol::detail::demangle().c_str()); + } +}; + + +template +struct lua_engine::immutable_collection_helper : immutable_container_helper +{ +protected: + using iterator = I; + + struct indexed_iterator + { + indexed_iterator(C &s, iterator i) : src(s), it(i), ix(0U) { } + + C &src; + iterator it; + std::size_t ix; + + indexed_iterator &operator++() + { + ++it; + ++ix; + return *this; + } + }; +}; + + +template +struct lua_engine::immutable_sequence_helper : immutable_collection_helper +{ +protected: + template + static int next_pairs(lua_State *L) + { + auto &i(sol::stack::unqualified_get >(L, 1)); + if (i.src.end() == i.it) + return sol::stack::push(L, sol::lua_nil); + int result; + if constexpr (Indexed) + result = sol::stack::push(L, i.ix + 1); + else + result = T::push_key(L, i.it, i.ix); + result += sol::stack::push_reference(L, T::unwrap(i.it)); + ++i; + return result; + } + + template + static int start_pairs(lua_State *L) + { + T &self(immutable_sequence_helper::get_self(L)); + sol::stack::push(L, next_pairs); + sol::stack::push >(L, self.items(), self.items().begin()); + sol::stack::push(L, sol::lua_nil); + return 3; + } + +public: + static int at(lua_State *L) + { + T &self(immutable_sequence_helper::get_self(L)); + std::ptrdiff_t const index(sol::stack::unqualified_get(L, 2)); + if ((0 >= index) || (self.items().size() < index)) + return sol::stack::push(L, sol::lua_nil); + else + return sol::stack::push_reference(L, T::unwrap(std::next(self.items().begin(), index - 1))); + } + + static int index_of(lua_State *L) + { + T &self(immutable_sequence_helper::get_self(L)); + auto it(self.items().begin()); + std::ptrdiff_t ix(0); + auto const &item(sol::stack::unqualified_get(L, 2)); + while ((self.items().end() != it) && (&item != &T::unwrap(it))) + { + ++it; + ++ix; + } + if (self.items().end() == it) + return sol::stack::push(L, sol::lua_nil); + else + return sol::stack::push(L, ix + 1); + } + + static int size(lua_State *L) + { + T &self(immutable_sequence_helper::get_self(L)); + return sol::stack::push(L, self.items().size()); + } + + static int empty(lua_State *L) + { + T &self(immutable_sequence_helper::get_self(L)); + return sol::stack::push(L, self.items().empty()); + } + + static int next(lua_State *L) { return sol::stack::push(L, next_pairs); } + static int pairs(lua_State *L) { return start_pairs(L); } + static int ipairs(lua_State *L) { return start_pairs(L); } +}; + + + +struct lua_engine::addr_space +{ + addr_space(address_space &s, device_memory_interface &d) : space(s), dev(d) { } + + template T mem_read(offs_t address); + template void mem_write(offs_t address, T val); + template T log_mem_read(offs_t address); + template void log_mem_write(offs_t address, T val); + template T direct_mem_read(offs_t address); + template void direct_mem_write(offs_t address, T val); + + address_space &space; + device_memory_interface &dev; +}; + + +template +class lua_engine::enum_parser +{ +public: + constexpr enum_parser(std::initializer_list > values) + { + if (values.size() != SIZE) + throw false && "size template argument incorrectly specified"; + std::copy(values.begin(), values.end(), m_map.begin()); + } + + T operator()(std::string_view text) const + { + auto iter = std::find_if( + m_map.begin() + 1, + m_map.end(), + [&text] (const auto &x) { return text == x.first; }); + if (iter == m_map.end()) + iter = m_map.begin(); + return iter->second; + } + +private: + std::array, SIZE> m_map; +}; + + +//------------------------------------------------- +// make_simple_callback_setter - make a callback +// setter for simple cases +//------------------------------------------------- + +template +auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate &&), D &&dflt, const char *name, const char *desc) +{ + return + [this, setter, dflt, name, desc] (T &self, sol::object cb) + { + if (cb == sol::lua_nil) + { + (self.*setter)(delegate()); + } + else if (cb.is()) + { + (self.*setter)(delegate( + [this, dflt, desc, cbfunc = cb.as()] () -> R + { + if constexpr (std::is_same_v) + { + (void)dflt; + (void)desc; + invoke(cbfunc); + } + else + { + auto result(invoke(cbfunc).get >()); + if (result) + { + return *result; + } + else + { + osd_printf_error("[LUA ERROR] invalid return from %s callback\n", desc); + return dflt(); + } + } + })); + } + else + { + osd_printf_error("[LUA ERROR] must call %s with function or nil\n", name); + } + }; +} + + +//------------------------------------------------- +// invoke - invokes a function, wrapping profiler +//------------------------------------------------- + +template +inline sol::protected_function_result lua_engine::invoke(TFunc &&func, TArgs &&... args) +{ + g_profiler.start(PROFILER_LUA); + sol::protected_function_result result = func(std::forward(args)...); + g_profiler.stop(); + return result; +} + +#endif // MAME_FRONTEND_MAME_LUAENGINE_IPP diff --git a/src/icludes/frontend/mame/luaengine_debug.cpp b/src/icludes/frontend/mame/luaengine_debug.cpp new file mode 100644 index 0000000..8349cbd --- /dev/null +++ b/src/icludes/frontend/mame/luaengine_debug.cpp @@ -0,0 +1,241 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic,Luca Bruno +/*************************************************************************** + + luaengine.cpp + + Controls execution of the core MAME system. + +***************************************************************************/ + +#include "emu.h" +#include "luaengine.ipp" + +#include "debug/debugcon.h" +#include "debug/debugcpu.h" +#include "debug/debugvw.h" +#include "debug/points.h" +#include "debug/textbuf.h" +#include "debugger.h" + + +namespace { + +struct wrap_textbuf +{ + wrap_textbuf(text_buffer const &buf) : textbuf(buf) { } + + std::reference_wrapper textbuf; +}; + + +template +sol::object do_breakpoint_enable(device_debug &dev, sol::this_state s, sol::object index) +{ + if (index == sol::lua_nil) + { + dev.breakpoint_enable_all(Enable); + dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY); + dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS); + return sol::lua_nil; + } + else if (index.is()) + { + bool result(dev.breakpoint_enable(index.as(), Enable)); + if (result) + { + dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY); + dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS); + } + return sol::make_object(s, result); + } + else + { + osd_printf_error("[LUA ERROR] must call bpenable with integer or nil\n"); + return sol::lua_nil; + } +} + + +template +sol::object do_watchpoint_enable(device_debug &dev, sol::this_state s, sol::object index) +{ + if (index == sol::lua_nil) + { + dev.watchpoint_enable_all(Enable); + dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS); + return sol::lua_nil; + } + else if (index.is()) + { + bool result(dev.watchpoint_enable(index.as(), Enable)); + if (result) + dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS); + return sol::make_object(s, result); + } + else + { + osd_printf_error("[LUA ERROR] must call wpenable with integer or nil"); + return sol::lua_nil; + } +} + +} // anonymous namespace + + +void lua_engine::initialize_debug(sol::table &emu) +{ + + static const enum_parser s_read_or_write_parser = + { + { "r", read_or_write::READ }, + { "w", read_or_write::WRITE }, + { "rw", read_or_write::READWRITE }, + { "wr", read_or_write::READWRITE } + }; + + + auto debugger_type = sol().registry().new_usertype("debugger", sol::no_constructor); + debugger_type["command"] = [] (debugger_manager &debug, std::string const &cmd) { debug.console().execute_command(cmd, false); }; + debugger_type["consolelog"] = sol::property([] (debugger_manager &debug) { return wrap_textbuf(debug.console().get_console_textbuf()); }); + debugger_type["errorlog"] = sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_errorlog_textbuf()); }); + debugger_type["visible_cpu"] = sol::property( + [](debugger_manager &debug) { return debug.console().get_visible_cpu(); }, + [](debugger_manager &debug, device_t &dev) { debug.console().set_visible_cpu(&dev); }); + debugger_type["execution_state"] = sol::property( + [] (debugger_manager &debug) { return debug.cpu().is_stopped() ? "stop" : "run"; }, + [] (debugger_manager &debug, std::string const &state) + { + if (state == "stop") + debug.cpu().set_execution_stopped(); + else + debug.cpu().set_execution_running(); + }); + + +/* wrap_textbuf library (requires debugger to be active) + * + * manager:machine():debugger().consolelog + * manager:machine():debugger().errorlog + * + * log[index] - get log entry + * #log - entry count + */ + + sol().registry().new_usertype("text_buffer", "new", sol::no_constructor, + "__metatable", [](){}, + "__newindex", [](){}, + "__index", [](wrap_textbuf &buf, int index) { return text_buffer_get_seqnum_line(buf.textbuf, index - 1); }, + "__len", [](wrap_textbuf &buf) { return text_buffer_num_lines(buf.textbuf) + text_buffer_line_index_to_seqnum(buf.textbuf, 0) - 1; }); + + + auto device_debug_type = sol().registry().new_usertype("device_debug", sol::no_constructor); + device_debug_type["step"] = + [] (device_debug &dev, sol::object num) + { + int steps = 1; + if (num.is()) + steps = num.as(); + dev.single_step(steps); + }; + device_debug_type["go"] = &device_debug::go; + device_debug_type["bpset"] = + [] (device_debug &dev, offs_t address, char const *cond, char const *act) + { + int result(dev.breakpoint_set(address, cond, act)); + dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY); + dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS); + return result; + }; + device_debug_type["bpclear"] = sol::overload( + [] (device_debug &dev, int index) + { + bool result(dev.breakpoint_clear(index)); + if (result) + { + dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY); + dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS); + } + return result; + }, + [] (device_debug &dev) + { + dev.breakpoint_clear_all(); + dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY); + dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS); + }); + device_debug_type["bpenable"] = &do_breakpoint_enable; + device_debug_type["bpdisable"] = &do_breakpoint_enable; + device_debug_type["bplist"] = + [this] (device_debug &dev) + { + sol::table table = sol().create_table(); + for (auto const &bpp : dev.breakpoint_list()) + table[bpp.second->index()] = sol::make_reference(sol(), *bpp.second); + return table; + }; + device_debug_type["wpset"] = + [] (device_debug &dev, addr_space &sp, std::string const &type, offs_t addr, offs_t len, char const *cond, char const *act) + { + read_or_write wptype = s_read_or_write_parser(type); + int result(dev.watchpoint_set(sp.space, wptype, addr, len, cond, act)); + dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS); + return result; + }; + device_debug_type["wpclear"] = sol::overload( + [] (device_debug &dev, int index) + { + bool result(dev.watchpoint_clear(index)); + if (result) + dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS); + return result; + }, + [] (device_debug &dev) + { + dev.watchpoint_clear_all(); + dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS); + }); + device_debug_type["wpenable"] = &do_watchpoint_enable; + device_debug_type["wpdisable"] = &do_watchpoint_enable; + device_debug_type["wplist"] = + [this] (device_debug &dev, addr_space &sp) + { + sol::table table = sol().create_table(); + for (auto &wpp : dev.watchpoint_vector(sp.space.spacenum())) + table[wpp->index()] = sol::make_reference(sol(), *wpp); + return table; + }; + + + auto breakpoint_type = sol().registry().new_usertype("breakpoint", sol::no_constructor); + breakpoint_type["index"] = sol::property(&debug_breakpoint::index); + breakpoint_type["enabled"] = sol::property(&debug_breakpoint::enabled); + breakpoint_type["address"] = sol::property(&debug_breakpoint::address); + breakpoint_type["condition"] = sol::property(&debug_breakpoint::condition); + breakpoint_type["action"] = sol::property(&debug_breakpoint::action); + + + auto watchpoint_type = sol().registry().new_usertype("watchpoint", sol::no_constructor); + watchpoint_type["index"] = sol::property(&debug_watchpoint::index); + watchpoint_type["enabled"] = sol::property(&debug_watchpoint::enabled); + watchpoint_type["type"] = sol::property( + [] (debug_watchpoint &wp) -> char const * + { + switch (wp.type()) + { + case read_or_write::READ: + return "r"; + case read_or_write::WRITE: + return "w"; + case read_or_write::READWRITE: + return "rw"; + default: // huh? + return ""; + } + }); + watchpoint_type["address"] = sol::property(&debug_watchpoint::address); + watchpoint_type["length"] = sol::property(&debug_watchpoint::length); + watchpoint_type["condition"] = sol::property(&debug_watchpoint::condition); + watchpoint_type["action"] = sol::property(&debug_watchpoint::action); + +} diff --git a/src/icludes/frontend/mame/luaengine_input.cpp b/src/icludes/frontend/mame/luaengine_input.cpp new file mode 100644 index 0000000..f32858c --- /dev/null +++ b/src/icludes/frontend/mame/luaengine_input.cpp @@ -0,0 +1,481 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic,Luca Bruno +/*************************************************************************** + + luaengine_input.cpp + + Controls execution of the core MAME system. + +***************************************************************************/ + +#include "emu.h" +#include "luaengine.ipp" + +#include "iptseqpoll.h" + +#include "inputdev.h" +#include "natkeyboard.h" +#include "render.h" +#include "uiinput.h" + +#include + + +namespace { + +struct natkbd_kbd_dev +{ + natkbd_kbd_dev(natural_keyboard &m, std::size_t i) : manager(m), index(i) { } + + natural_keyboard &manager; + std::size_t index; +}; + + +struct natkbd_kbd_list +{ + natkbd_kbd_list(natural_keyboard &m) : manager(m) { } + + natural_keyboard &manager; +}; + +} // anonymous namespace + + +namespace sol { + +template <> struct is_container : std::true_type { }; + + +template <> +struct usertype_container : lua_engine::immutable_container_helper +{ +private: + template + static int next_pairs(lua_State *L) + { + natkbd_kbd_dev &i(stack::unqualified_get >(L, 1)); + if (i.manager.keyboard_count() <= i.index) + return stack::push(L, lua_nil); + int result; + if constexpr (Indexed) + result = stack::push(L, i.index + 1); + else + result = stack::push(L, i.manager.keyboard_device(i.index).tag()); + result += stack::push(L, i); + ++i.index; + return result; + } + + template + static int start_pairs(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + stack::push(L, next_pairs); + stack::push >(L, self.manager, 0); + stack::push(L, lua_nil); + return 3; + } + +public: + static int at(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + std::ptrdiff_t const index(stack::unqualified_get(L, 2)); + if ((0 < index) && (self.manager.keyboard_count() >= index)) + return stack::push(L, natkbd_kbd_dev(self.manager, index - 1)); + else + return stack::push(L, lua_nil); + } + + static int get(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + char const *const tag(stack::unqualified_get(L)); + for (std::size_t i = 0; self.manager.keyboard_count() > i; ++i) + { + if (!std::strcmp(self.manager.keyboard_device(i).tag(), tag)) + return stack::push(L, natkbd_kbd_dev(self.manager, i)); + } + return stack::push(L, lua_nil); + } + + static int index_get(lua_State *L) + { + return get(L); + } + + static int size(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + return stack::push(L, self.manager.keyboard_count()); + } + + static int empty(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + return stack::push(L, !self.manager.keyboard_count()); + } + + static int next(lua_State *L) { return stack::push(L, next_pairs); } + static int pairs(lua_State *L) { return start_pairs(L); } + static int ipairs(lua_State *L) { return start_pairs(L); } +}; + +} // namespace sol + + +//------------------------------------------------- +// initialize_input - register input user types +//------------------------------------------------- + +void lua_engine::initialize_input(sol::table &emu) +{ + + static const enum_parser s_seq_type_parser = + { + { "standard", SEQ_TYPE_STANDARD }, + { "increment", SEQ_TYPE_INCREMENT }, + { "decrement", SEQ_TYPE_DECREMENT }, + }; + + + auto ioport_manager_type = sol().registry().new_usertype("ioport", sol::no_constructor); + ioport_manager_type["count_players"] = &ioport_manager::count_players; + ioport_manager_type["type_pressed"] = sol::overload( + &ioport_manager::type_pressed, + [] (ioport_manager &im, ioport_type type) { return im.type_pressed(type, 0); }); + ioport_manager_type["type_name"] = sol::overload( + &ioport_manager::type_name, + [] (ioport_manager &im, ioport_type type) { return im.type_name(type, 0); }); + ioport_manager_type["type_group"] = sol::overload( + &ioport_manager::type_group, + [] (ioport_manager &im, ioport_type type) { return im.type_group(type, 0); }); + ioport_manager_type["type_seq"] = + [] (ioport_manager &im, ioport_type type, std::optional player, std::optional seq_type_string) + { + if (!player) + player = 0; + input_seq_type seq_type = seq_type_string ? s_seq_type_parser(*seq_type_string) : SEQ_TYPE_STANDARD; + return im.type_seq(type, *player, seq_type); + }; + ioport_manager_type["set_type_seq"] = + [] (ioport_manager &im, ioport_type type, std::optional player, std::optional seq_type_string, input_seq const &seq) + { + if (!player) + player = 0; + input_seq_type seq_type = seq_type_string ? s_seq_type_parser(*seq_type_string) : SEQ_TYPE_STANDARD; + im.set_type_seq(type, *player, seq_type, seq); + }; + ioport_manager_type["token_to_input_type"] = + [] (ioport_manager &im, std::string const &string) + { + int player; + ioport_type const type = im.token_to_input_type(string.c_str(), player); + return std::make_tuple(type, player); + }; + ioport_manager_type["input_type_to_token"] = sol::overload( + &ioport_manager::input_type_to_token, + [] (ioport_manager &im, ioport_type type) { return im.input_type_to_token(type, 0); }); + ioport_manager_type["ports"] = sol::property([] (ioport_manager &im) { return tag_object_ptr_map(im.ports()); }); + + + auto natkeyboard_type = sol().registry().new_usertype("natkeyboard", sol::no_constructor); + natkeyboard_type["post"] = [] (natural_keyboard &nat, std::string const &text) { nat.post_utf8(text); }; + natkeyboard_type["post_coded"] = [] (natural_keyboard &nat, std::string const &text) { nat.post_coded(text); }; + natkeyboard_type["paste"] = &natural_keyboard::paste; + natkeyboard_type["dump"] = static_cast(&natural_keyboard::dump); + natkeyboard_type["empty"] = sol::property(&natural_keyboard::empty); + natkeyboard_type["full"] = sol::property(&natural_keyboard::full); + natkeyboard_type["can_post"] = sol::property(&natural_keyboard::can_post); + natkeyboard_type["is_posting"] = sol::property(&natural_keyboard::is_posting); + natkeyboard_type["in_use"] = sol::property(&natural_keyboard::in_use, &natural_keyboard::set_in_use); + natkeyboard_type["keyboards"] = sol::property([] (natural_keyboard &nat) { return natkbd_kbd_list(nat); }); + + + auto natkbddev_type = sol().registry().new_usertype("natkeyboard_device", sol::no_constructor); + natkbddev_type["device"] = sol::property([] (natkbd_kbd_dev const &kbd) -> device_t & { return kbd.manager.keyboard_device(kbd.index); }); + natkbddev_type["tag"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).tag(); }); + natkbddev_type["basetag"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).basetag(); }); + natkbddev_type["name"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).name(); }); + natkbddev_type["shortname"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).shortname(); }); + natkbddev_type["is_keypad"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_is_keypad(kbd.index); }); + natkbddev_type["enabled"] = sol::property( + [] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_enabled(kbd.index); }, + [] (natkbd_kbd_dev &kbd, bool enable) + { + if (enable) + kbd.manager.enable_keyboard(kbd.index); + else + kbd.manager.disable_keyboard(kbd.index); + }); + + + auto ioport_port_type = sol().registry().new_usertype("ioport_port", sol::no_constructor); + ioport_port_type["read"] = &ioport_port::read; + ioport_port_type["write"] = &ioport_port::write; + ioport_port_type["field"] = &ioport_port::field; + ioport_port_type["device"] = sol::property(&ioport_port::device); + ioport_port_type["tag"] = sol::property(&ioport_port::tag); + ioport_port_type["active"] = sol::property(&ioport_port::active); + ioport_port_type["live"] = sol::property(&ioport_port::live); + ioport_port_type["fields"] = sol::property( + [this] (ioport_port &p) + { + sol::table f_table = sol().create_table(); + // parse twice for custom and default names, default has priority + for (ioport_field &field : p.fields()) + { + if (field.type_class() != INPUT_CLASS_INTERNAL) + f_table[field.name()] = &field; + } + for (ioport_field &field : p.fields()) + { + if (field.type_class() != INPUT_CLASS_INTERNAL) + { + if (field.specific_name()) + f_table[field.specific_name()] = &field; + else + f_table[field.manager().type_name(field.type(), field.player())] = &field; + } + } + return f_table; + }); + + + auto ioport_field_type = sol().registry().new_usertype("ioport_field", sol::no_constructor); + ioport_field_type["set_value"] = &ioport_field::set_value; + ioport_field_type["set_input_seq"] = + [] (ioport_field &f, std::string const &seq_type_string, const input_seq &seq) + { + input_seq_type seq_type = s_seq_type_parser(seq_type_string); + ioport_field::user_settings settings; + f.get_user_settings(settings); + settings.seq[seq_type] = seq; + if (seq.is_default()) + settings.cfg[seq_type].clear(); + else if (!seq.length()) + settings.cfg[seq_type] = "NONE"; + else + settings.cfg[seq_type] = f.port().device().machine().input().seq_to_tokens(seq); + f.set_user_settings(settings); + }; + ioport_field_type["input_seq"] = + [] (ioport_field &f, std::string const &seq_type_string) + { + input_seq_type seq_type = s_seq_type_parser(seq_type_string); + return f.seq(seq_type); + }; + ioport_field_type["set_default_input_seq"] = + [] (ioport_field &f, std::string const &seq_type_string, input_seq const &seq) + { + input_seq_type seq_type = s_seq_type_parser(seq_type_string); + f.set_defseq(seq_type, seq); + }; + ioport_field_type["default_input_seq"] = + [] (ioport_field &f, const std::string &seq_type_string) + { + input_seq_type seq_type = s_seq_type_parser(seq_type_string); + return f.defseq(seq_type); + }; + ioport_field_type["keyboard_codes"] = + [this] (ioport_field &f, int which) + { + sol::table result = sol().create_table(); + int index = 1; + for (char32_t code : f.keyboard_codes(which)) + result[index++] = code; + return result; + }; + ioport_field_type["device"] = sol::property(&ioport_field::device); + ioport_field_type["port"] = sol::property(&ioport_field::port); + ioport_field_type["live"] = sol::property(&ioport_field::live); + ioport_field_type["type"] = sol::property(&ioport_field::type); + ioport_field_type["name"] = sol::property(&ioport_field::name); + ioport_field_type["default_name"] = sol::property( + [] (ioport_field &f) + { + return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player()); + }); + ioport_field_type["player"] = sol::property(&ioport_field::player, &ioport_field::set_player); + ioport_field_type["mask"] = sol::property(&ioport_field::mask); + ioport_field_type["defvalue"] = sol::property(&ioport_field::defvalue); + ioport_field_type["sensitivity"] = sol::property(&ioport_field::sensitivity); + ioport_field_type["way"] = sol::property(&ioport_field::way); + ioport_field_type["type_class"] = sol::property( + [] (ioport_field &f) + { + switch (f.type_class()) + { + case INPUT_CLASS_KEYBOARD: return "keyboard"; + case INPUT_CLASS_CONTROLLER: return "controller"; + case INPUT_CLASS_CONFIG: return "config"; + case INPUT_CLASS_DIPSWITCH: return "dipswitch"; + case INPUT_CLASS_MISC: return "misc"; + default: break; + } + throw false; + }); + ioport_field_type["is_analog"] = sol::property(&ioport_field::is_analog); + ioport_field_type["is_digital_joystick"] = sol::property(&ioport_field::is_digital_joystick); + ioport_field_type["enabled"] = sol::property(&ioport_field::enabled); + ioport_field_type["optional"] = sol::property(&ioport_field::optional); + ioport_field_type["cocktail"] = sol::property(&ioport_field::cocktail); + ioport_field_type["toggle"] = sol::property(&ioport_field::toggle); + ioport_field_type["rotated"] = sol::property(&ioport_field::rotated); + ioport_field_type["analog_reverse"] = sol::property(&ioport_field::analog_reverse); + ioport_field_type["analog_reset"] = sol::property(&ioport_field::analog_reset); + ioport_field_type["analog_wraps"] = sol::property(&ioport_field::analog_wraps); + ioport_field_type["analog_invert"] = sol::property(&ioport_field::analog_invert); + ioport_field_type["impulse"] = sol::property(&ioport_field::impulse); + ioport_field_type["crosshair_scale"] = sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale); + ioport_field_type["crosshair_offset"] = sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset); + ioport_field_type["user_value"] = sol::property( + [] (ioport_field &f) + { + ioport_field::user_settings settings; + f.get_user_settings(settings); + return settings.value; + }, + [] (ioport_field &f, ioport_value val) + { + ioport_field::user_settings settings; + f.get_user_settings(settings); + settings.value = val; + f.set_user_settings(settings); + }); + ioport_field_type["settings"] = sol::property( + [this] (ioport_field &f) + { + sol::table result = sol().create_table(); + for (ioport_setting const &setting : f.settings()) + if (setting.enabled()) + result[setting.value()] = setting.name(); + return result; + }); + + + auto ioport_field_live_type = sol().registry().new_usertype("ioport_field_live", sol::no_constructor); + ioport_field_live_type["name"] = &ioport_field_live::name; + + + auto input_type = sol().registry().new_usertype("input", sol::no_constructor); + input_type["code_value"] = &input_manager::code_value; + input_type["code_pressed"] = &input_manager::code_pressed; + input_type["code_pressed_once"] = &input_manager::code_pressed_once; + input_type["code_name"] = &input_manager::code_name; + input_type["code_to_token"] = &input_manager::code_to_token; + input_type["code_from_token"] = &input_manager::code_from_token; + input_type["seq_pressed"] = &input_manager::seq_pressed; + input_type["seq_clean"] = &input_manager::seq_clean; + input_type["seq_name"] = &input_manager::seq_name; + input_type["seq_to_tokens"] = &input_manager::seq_to_tokens; + input_type["seq_from_tokens"] = + [] (input_manager &input, std::string_view tokens) + { + input_seq seq; + input.seq_from_tokens(seq, tokens); + return seq; + }; + input_type["axis_code_poller"] = [] (input_manager &input) { return std::unique_ptr(new axis_code_poller(input)); }; + input_type["switch_code_poller"] = [] (input_manager &input) { return std::unique_ptr(new switch_code_poller(input)); }; + input_type["keyboard_code_poller"] = [] (input_manager &input) { return std::unique_ptr(new keyboard_code_poller(input)); }; + input_type["axis_sequence_poller"] = [] (input_manager &input) { return std::unique_ptr(new axis_sequence_poller(input)); }; + input_type["switch_sequence_poller"] = [] (input_manager &input) { return std::unique_ptr(new switch_sequence_poller(input)); }; + input_type["device_classes"] = sol::property( + [this] (input_manager &input) + { + sol::table result = sol().create_table(); + for (input_device_class devclass_id = DEVICE_CLASS_FIRST_VALID; devclass_id <= DEVICE_CLASS_LAST_VALID; devclass_id++) + { + input_class &devclass = input.device_class(devclass_id); + result[devclass.name()] = &devclass; + } + return result; + }); + + + auto codepoll_type = sol().registry().new_usertype("input_code_poller", sol::no_constructor); + codepoll_type["reset"] = &input_code_poller::reset; + codepoll_type["poll"] = &input_code_poller::poll; + + + auto seqpoll_type = sol().registry().new_usertype("input_seq_poller", sol::no_constructor); + seqpoll_type["start"] = sol::overload( + [] (input_sequence_poller &poller) { return poller.start(); }, + [] (input_sequence_poller &poller, input_seq const &seq) { return poller.start(seq); }); + seqpoll_type["poll"] = &input_sequence_poller::poll; + seqpoll_type["sequence"] = sol::property(&input_sequence_poller::sequence); + seqpoll_type["valid"] = sol::property(&input_sequence_poller::valid); + seqpoll_type["modified"] = sol::property(&input_sequence_poller::modified); + + + auto iptseq_type = emu.new_usertype( + "input_seq", + sol::call_constructor, sol::constructors()); + iptseq_type["reset"] = &input_seq::reset; + iptseq_type["set_default"] = &input_seq::set_default; + iptseq_type["empty"] = sol::property(&input_seq::empty); + iptseq_type["length"] = sol::property(&input_seq::length); + iptseq_type["is_valid"] = sol::property(&input_seq::is_valid); + iptseq_type["is_default"] = sol::property(&input_seq::is_default); + + + auto input_class_type = sol().registry().new_usertype("input_class", sol::no_constructor); + input_class_type["name"] = sol::property(&input_class::name); + input_class_type["enabled"] = sol::property(&input_class::enabled); + input_class_type["multi"] = sol::property(&input_class::multi); + input_class_type["devices"] = sol::property( + [this] (input_class &devclass) + { + sol::table result = sol().create_table(); + int index = 1; + for (int devindex = 0; devindex <= devclass.maxindex(); devindex++) + { + input_device *const dev = devclass.device(devindex); + if (dev) + result[index++] = dev; + } + return result; + }); + + + auto input_device_type = sol().registry().new_usertype("input_device", sol::no_constructor); + input_device_type["name"] = sol::property(&input_device::name); + input_device_type["id"] = sol::property(&input_device::id); + input_device_type["devindex"] = sol::property(&input_device::devindex); + input_device_type["items"] = sol::property( + [this] (input_device &dev) + { + sol::table result = sol().create_table(); + for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++) + { + input_device_item *item = dev.item(id); + if (item) + result[id] = dev.item(id); + } + return result; + }); + + + auto input_device_item_type = sol().registry().new_usertype("input_device_item", sol::no_constructor); + input_device_item_type["name"] = sol::property(&input_device_item::name); + input_device_item_type["code"] = sol::property(&input_device_item::code); + input_device_item_type["token"] = sol::property(&input_device_item::token); + input_device_item_type["current"] = sol::property(&input_device_item::current); + + + auto uiinput_type = sol().registry().new_usertype("uiinput", sol::no_constructor); + uiinput_type["find_mouse"] = + [] (ui_input_manager &ui) + { + int32_t x, y; + bool button; + render_target *rt = ui.find_mouse(&x, &y, &button); + return std::make_tuple(x, y, button, rt); + }; + uiinput_type["pressed"] = &ui_input_manager::pressed; + uiinput_type["pressed_repeat"] = &ui_input_manager::pressed_repeat; + uiinput_type["presses_enabled"] = sol::property(&ui_input_manager::presses_enabled, &ui_input_manager::set_presses_enabled); + +} diff --git a/src/icludes/frontend/mame/luaengine_mem.cpp b/src/icludes/frontend/mame/luaengine_mem.cpp new file mode 100644 index 0000000..f95ebdb --- /dev/null +++ b/src/icludes/frontend/mame/luaengine_mem.cpp @@ -0,0 +1,608 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic,Luca Bruno +/*************************************************************************** + + luaengine_input.cpp + + Controls execution of the core MAME system. + +***************************************************************************/ + +#include "emu.h" +#include "luaengine.ipp" + + +namespace { + +//------------------------------------------------- +// region_read - templated region readers for , +// -> manager:machine():memory().regions[":maincpu"]:read_i8(0xC000) +//------------------------------------------------- + +template +T region_read(memory_region ®ion, offs_t address) +{ + T mem_content = 0; + const offs_t lowmask = region.bytewidth() - 1; + for (int i = 0; i < sizeof(T); i++) + { + int addr = region.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i; + if (addr < region.bytes()) + { + if constexpr (sizeof(T) > 1) + mem_content <<= 8; + if (region.endianness() == ENDIANNESS_BIG) + mem_content |= region.as_u8((BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)); + else + mem_content |= region.as_u8((BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)); + } + } + + return mem_content; +} + +//------------------------------------------------- +// region_write - templated region writer for , +// -> manager:machine():memory().regions[":maincpu"]:write_u16(0xC000, 0xF00D) +//------------------------------------------------- + +template +void region_write(memory_region ®ion, offs_t address, T val) +{ + const offs_t lowmask = region.bytewidth() - 1; + for (int i = 0; i < sizeof(T); i++) + { + int addr = region.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i; + if (addr < region.bytes()) + { + if (region.endianness() == ENDIANNESS_BIG) + region.base()[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff; + else + region.base()[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff; + if constexpr (sizeof(T) > 1) + val >>= 8; + } + } +} + +//------------------------------------------------- +// share_read - templated share readers for , +// -> manager:machine():memory().shares[":maincpu"]:read_i8(0xC000) +//------------------------------------------------- + +template +T share_read(memory_share &share, offs_t address) +{ + T mem_content = 0; + const offs_t lowmask = share.bytewidth() - 1; + u8 *ptr = (u8 *)share.ptr(); + for (int i = 0; i < sizeof(T); i++) + { + int addr = share.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i; + if (addr < share.bytes()) + { + if constexpr (sizeof(T) > 1) + mem_content <<= 8; + if (share.endianness() == ENDIANNESS_BIG) + mem_content |= ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)]; + else + mem_content |= ptr[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)]; + } + } + + return mem_content; +} + +//------------------------------------------------- +// share_write - templated share writer for , +// -> manager:machine():memory().shares[":maincpu"]:write_u16(0xC000, 0xF00D) +//------------------------------------------------- + +template +void share_write(memory_share &share, offs_t address, T val) +{ + const offs_t lowmask = share.bytewidth() - 1; + u8 *ptr = (u8 *)share.ptr(); + for (int i = 0; i < sizeof(T); i++) + { + int addr = share.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i; + if (addr < share.bytes()) + { + if (share.endianness() == ENDIANNESS_BIG) + ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff; + else + ptr[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff; + if constexpr (sizeof(T) > 1) + val >>= 8; + } + } +} + +} // anonymous namespace + + + +//------------------------------------------------- +// sol_lua_push - automatically convert +// map_handler_type to a string +//------------------------------------------------- + +int sol_lua_push(sol::types, lua_State *L, map_handler_type &&value) +{ + const char *typestr; + switch(value) + { + case AMH_NONE: + typestr = "none"; + break; + case AMH_RAM: + typestr = "ram"; + break; + case AMH_ROM: + typestr = "rom"; + break; + case AMH_NOP: + typestr = "nop"; + break; + case AMH_UNMAP: + typestr = "unmap"; + break; + case AMH_DEVICE_DELEGATE: + case AMH_DEVICE_DELEGATE_M: + case AMH_DEVICE_DELEGATE_S: + case AMH_DEVICE_DELEGATE_SM: + case AMH_DEVICE_DELEGATE_MO: + case AMH_DEVICE_DELEGATE_SMO: + typestr = "delegate"; + break; + case AMH_PORT: + typestr = "port"; + break; + case AMH_BANK: + typestr = "bank"; + break; + case AMH_DEVICE_SUBMAP: + typestr = "submap"; + break; + default: + typestr = "unknown"; + break; + } + return sol::stack::push(L, typestr); +} + + +//------------------------------------------------- +// sol_lua_push - automatically convert +// endianness_t to a string +//------------------------------------------------- + +int sol_lua_push(sol::types, lua_State *L, endianness_t &&value) +{ + return sol::stack::push(L, util::endian_to_string_view(value)); +} + + +//------------------------------------------------- +// mem_read - templated memory readers for , +// -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000) +//------------------------------------------------- + +template +T lua_engine::addr_space::mem_read(offs_t address) +{ + T mem_content = 0; + switch (sizeof(mem_content) * 8) + { + case 8: + mem_content = space.read_byte(address); + break; + case 16: + if (WORD_ALIGNED(address)) + mem_content = space.read_word(address); + else + mem_content = space.read_word_unaligned(address); + break; + case 32: + if (DWORD_ALIGNED(address)) + mem_content = space.read_dword(address); + else + mem_content = space.read_dword_unaligned(address); + break; + case 64: + if (QWORD_ALIGNED(address)) + mem_content = space.read_qword(address); + else + mem_content = space.read_qword_unaligned(address); + break; + default: + break; + } + + return mem_content; +} + +//------------------------------------------------- +// mem_write - templated memory writer for , +// -> manager:machine().devices[":maincpu"].spaces["program"]:write_u16(0xC000, 0xF00D) +//------------------------------------------------- + +template +void lua_engine::addr_space::mem_write(offs_t address, T val) +{ + switch (sizeof(val) * 8) + { + case 8: + space.write_byte(address, val); + break; + case 16: + if (WORD_ALIGNED(address)) + space.write_word(address, val); + else + space.write_word_unaligned(address, val); + break; + case 32: + if (DWORD_ALIGNED(address)) + space.write_dword(address, val); + else + space.write_dword_unaligned(address, val); + break; + case 64: + if (QWORD_ALIGNED(address)) + space.write_qword(address, val); + else + space.write_qword_unaligned(address, val); + break; + default: + break; + } +} + +//------------------------------------------------- +// log_mem_read - templated logical memory readers for , +// -> manager:machine().devices[":maincpu"].spaces["program"]:read_log_i8(0xC000) +//------------------------------------------------- + +template +T lua_engine::addr_space::log_mem_read(offs_t address) +{ + if (!dev.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address)) + return 0; + + T mem_content = 0; + switch (sizeof(mem_content) * 8) + { + case 8: + mem_content = space.read_byte(address); + break; + case 16: + if (WORD_ALIGNED(address)) + mem_content = space.read_word(address); + else + mem_content = space.read_word_unaligned(address); + break; + case 32: + if (DWORD_ALIGNED(address)) + mem_content = space.read_dword(address); + else + mem_content = space.read_dword_unaligned(address); + break; + case 64: + if (QWORD_ALIGNED(address)) + mem_content = space.read_qword(address); + else + mem_content = space.read_qword_unaligned(address); + break; + default: + break; + } + + return mem_content; +} + +//------------------------------------------------- +// log_mem_write - templated logical memory writer for , +// -> manager:machine().devices[":maincpu"].spaces["program"]:write_log_u16(0xC000, 0xF00D) +//------------------------------------------------- + +template +void lua_engine::addr_space::log_mem_write(offs_t address, T val) +{ + if (!dev.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address)) + return; + + switch (sizeof(val) * 8) + { + case 8: + space.write_byte(address, val); + break; + case 16: + if (WORD_ALIGNED(address)) + space.write_word(address, val); + else + space.write_word_unaligned(address, val); + break; + case 32: + if (DWORD_ALIGNED(address)) + space.write_dword(address, val); + else + space.write_dword_unaligned(address, val); + break; + case 64: + if (QWORD_ALIGNED(address)) + space.write_qword(address, val); + else + space.write_qword_unaligned(address, val); + break; + default: + break; + } +} + +//------------------------------------------------- +// mem_direct_read - templated direct memory readers for , +// -> manager:machine().devices[":maincpu"].spaces["program"]:read_direct_i8(0xC000) +//------------------------------------------------- + +template +T lua_engine::addr_space::direct_mem_read(offs_t address) +{ + T mem_content = 0; + const offs_t lowmask = space.data_width() / 8 - 1; + for (int i = 0; i < sizeof(T); i++) + { + int addr = space.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i; + u8 *base = (u8 *)space.get_read_ptr(addr & ~lowmask); + if (base) + { + if constexpr (sizeof(T) > 1) + mem_content <<= 8; + if (space.endianness() == ENDIANNESS_BIG) + mem_content |= base[BYTE8_XOR_BE(addr) & lowmask]; + else + mem_content |= base[BYTE8_XOR_LE(addr) & lowmask]; + } + } + + return mem_content; +} + +//------------------------------------------------- +// mem_direct_write - templated memory writer for , +// -> manager:machine().devices[":maincpu"].spaces["program"]:write_direct_u16(0xC000, 0xF00D) +//------------------------------------------------- + +template +void lua_engine::addr_space::direct_mem_write(offs_t address, T val) +{ + const offs_t lowmask = space.data_width() / 8 - 1; + for (int i = 0; i < sizeof(T); i++) + { + int addr = space.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i; + u8 *base = (u8 *)space.get_read_ptr(addr & ~lowmask); + if (base) + { + if (space.endianness() == ENDIANNESS_BIG) + base[BYTE8_XOR_BE(addr) & lowmask] = val & 0xff; + else + base[BYTE8_XOR_LE(addr) & lowmask] = val & 0xff; + if constexpr (sizeof(T) > 1) + val >>= 8; + } + } +} + +//------------------------------------------------- +// initialize_memory - register memory user types +//------------------------------------------------- + +void lua_engine::initialize_memory(sol::table &emu) +{ + + auto addr_space_type = sol().registry().new_usertype("addr_space", sol::no_constructor); + addr_space_type["read_i8"] = &addr_space::mem_read; + addr_space_type["read_u8"] = &addr_space::mem_read; + addr_space_type["read_i16"] = &addr_space::mem_read; + addr_space_type["read_u16"] = &addr_space::mem_read; + addr_space_type["read_i32"] = &addr_space::mem_read; + addr_space_type["read_u32"] = &addr_space::mem_read; + addr_space_type["read_i64"] = &addr_space::mem_read; + addr_space_type["read_u64"] = &addr_space::mem_read; + addr_space_type["write_i8"] = &addr_space::mem_write; + addr_space_type["write_u8"] = &addr_space::mem_write; + addr_space_type["write_i16"] = &addr_space::mem_write; + addr_space_type["write_u16"] = &addr_space::mem_write; + addr_space_type["write_i32"] = &addr_space::mem_write; + addr_space_type["write_u32"] = &addr_space::mem_write; + addr_space_type["write_i64"] = &addr_space::mem_write; + addr_space_type["write_u64"] = &addr_space::mem_write; + addr_space_type["readv_i8"] = &addr_space::log_mem_read; + addr_space_type["readv_u8"] = &addr_space::log_mem_read; + addr_space_type["readv_i16"] = &addr_space::log_mem_read; + addr_space_type["readv_u16"] = &addr_space::log_mem_read; + addr_space_type["readv_i32"] = &addr_space::log_mem_read; + addr_space_type["readv_u32"] = &addr_space::log_mem_read; + addr_space_type["readv_i64"] = &addr_space::log_mem_read; + addr_space_type["readv_u64"] = &addr_space::log_mem_read; + addr_space_type["writev_i8"] = &addr_space::log_mem_write; + addr_space_type["writev_u8"] = &addr_space::log_mem_write; + addr_space_type["writev_i16"] = &addr_space::log_mem_write; + addr_space_type["writev_u16"] = &addr_space::log_mem_write; + addr_space_type["writev_i32"] = &addr_space::log_mem_write; + addr_space_type["writev_u32"] = &addr_space::log_mem_write; + addr_space_type["writev_i64"] = &addr_space::log_mem_write; + addr_space_type["writev_u64"] = &addr_space::log_mem_write; + addr_space_type["read_direct_i8"] = &addr_space::direct_mem_read; + addr_space_type["read_direct_u8"] = &addr_space::direct_mem_read; + addr_space_type["read_direct_i16"] = &addr_space::direct_mem_read; + addr_space_type["read_direct_u16"] = &addr_space::direct_mem_read; + addr_space_type["read_direct_i32"] = &addr_space::direct_mem_read; + addr_space_type["read_direct_u32"] = &addr_space::direct_mem_read; + addr_space_type["read_direct_i64"] = &addr_space::direct_mem_read; + addr_space_type["read_direct_u64"] = &addr_space::direct_mem_read; + addr_space_type["write_direct_i8"] = &addr_space::direct_mem_write; + addr_space_type["write_direct_u8"] = &addr_space::direct_mem_write; + addr_space_type["write_direct_i16"] = &addr_space::direct_mem_write; + addr_space_type["write_direct_u16"] = &addr_space::direct_mem_write; + addr_space_type["write_direct_i32"] = &addr_space::direct_mem_write; + addr_space_type["write_direct_u32"] = &addr_space::direct_mem_write; + addr_space_type["write_direct_i64"] = &addr_space::direct_mem_write; + addr_space_type["write_direct_u64"] = &addr_space::direct_mem_write; + addr_space_type["read_range"] = + [] (addr_space &sp, sol::this_state s, u64 first, u64 last, int width, sol::object opt_step) -> sol::object + { + lua_State *L = s; + luaL_Buffer buff; + offs_t space_size = sp.space.addrmask(); + u64 step = 1; + if (opt_step.is()) + { + step = opt_step.as(); + if (step < 1 || step > last - first) + { + luaL_error(L, "Invalid step"); + return sol::lua_nil; + } + } + if (first > space_size || last > space_size || last < first) + { + luaL_error(L, "Invalid offset"); + return sol::lua_nil; + } + int byte_count = width / 8 * (last - first + 1) / step; + switch (width) + { + case 8: + { + u8 *dest = (u8 *)luaL_buffinitsize(L, &buff, byte_count); + for ( ; first <= last; first += step) + *dest++ = sp.mem_read(first); + break; + } + case 16: + { + u16 *dest = (u16 *)luaL_buffinitsize(L, &buff, byte_count); + for ( ; first <= last; first += step) + *dest++ = sp.mem_read(first); + break; + } + case 32: + { + u32 *dest = (u32 *)luaL_buffinitsize(L, &buff, byte_count); + for(; first <= last; first += step) + *dest++ = sp.mem_read(first); + break; + } + case 64: + { + u64 *dest = (u64 *)luaL_buffinitsize(L, &buff, byte_count); + for(; first <= last; first += step) + *dest++ = sp.mem_read(first); + break; + } + default: + luaL_error(L, "Invalid width. Must be 8/16/32/64"); + return sol::lua_nil; + } + luaL_pushresultsize(&buff, byte_count); + return sol::make_reference(L, sol::stack_reference(L, -1)); + }; + addr_space_type["name"] = sol::property([] (addr_space &sp) { return sp.space.name(); }); + addr_space_type["shift"] = sol::property([] (addr_space &sp) { return sp.space.addr_shift(); }); + addr_space_type["index"] = sol::property([] (addr_space &sp) { return sp.space.spacenum(); }); + addr_space_type["address_mask"] = sol::property([] (addr_space &sp) { return sp.space.addrmask(); }); + addr_space_type["data_width"] = sol::property([] (addr_space &sp) { return sp.space.data_width(); }); + addr_space_type["endianness"] = sol::property([] (addr_space &sp) { return sp.space.endianness(); }); + addr_space_type["map"] = sol::property([] (addr_space &sp) { return sp.space.map(); }); + + + auto addrmap_type = sol().registry().new_usertype("addrmap", sol::no_constructor); + addrmap_type["spacenum"] = sol::readonly(&address_map::m_spacenum); + addrmap_type["device"] = sol::readonly(&address_map::m_device); + addrmap_type["unmap_value"] = sol::readonly(&address_map::m_unmapval); + addrmap_type["global_mask"] = sol::readonly(&address_map::m_globalmask); + addrmap_type["entries"] = sol::property([] (address_map &m) { return simple_list_wrapper(m.m_entrylist); }); + + + auto mapentry_type = sol().registry().new_usertype("mapentry", sol::no_constructor); + mapentry_type["address_start"] = sol::readonly(&address_map_entry::m_addrstart); + mapentry_type["address_end"] = sol::readonly(&address_map_entry::m_addrend); + mapentry_type["address_mirror"] = sol::readonly(&address_map_entry::m_addrmirror); + mapentry_type["address_mask"] = sol::readonly(&address_map_entry::m_addrmask); + mapentry_type["mask"] = sol::readonly(&address_map_entry::m_mask); + mapentry_type["cswidth"] = sol::readonly(&address_map_entry::m_cswidth); + mapentry_type["read"] = sol::readonly(&address_map_entry::m_read); + mapentry_type["write"] = sol::readonly(&address_map_entry::m_write); + mapentry_type["share"] = sol::readonly(&address_map_entry::m_share); + mapentry_type["region"] = sol::readonly(&address_map_entry::m_region); + mapentry_type["region_offset"] = sol::readonly(&address_map_entry::m_rgnoffs); + + + auto handler_data_type = sol().registry().new_usertype("handlerdata", sol::no_constructor); + handler_data_type["handlertype"] = sol::property([] (map_handler_data const &hd) { return hd.m_type; }); // can't use member pointer or won't be converted to string + handler_data_type["bits"] = sol::readonly(&map_handler_data::m_bits); + handler_data_type["name"] = sol::readonly(&map_handler_data::m_name); + handler_data_type["tag"] = sol::readonly(&map_handler_data::m_tag); + + + auto memory_type = sol().registry().new_usertype("memory", sol::no_constructor); + memory_type["banks"] = sol::property([] (memory_manager &mm) { return standard_tag_object_ptr_map(mm.banks()); }); + memory_type["regions"] = sol::property([] (memory_manager &mm) { return standard_tag_object_ptr_map(mm.regions()); }); + memory_type["shares"] = sol::property([] (memory_manager &mm) { return standard_tag_object_ptr_map(mm.shares()); }); + + + auto bank_type = sol().registry().new_usertype("membank", sol::no_constructor); + bank_type["tag"] = sol::property(&memory_bank::tag); + bank_type["entry"] = sol::property(&memory_bank::entry, &memory_bank::set_entry); + + + auto region_type = sol().registry().new_usertype("region", sol::no_constructor); + region_type["read_i8"] = ®ion_read; + region_type["read_u8"] = ®ion_read; + region_type["read_i16"] = ®ion_read; + region_type["read_u16"] = ®ion_read; + region_type["read_i32"] = ®ion_read; + region_type["read_u32"] = ®ion_read; + region_type["read_i64"] = ®ion_read; + region_type["read_u64"] = ®ion_read; + region_type["write_i8"] = ®ion_write; + region_type["write_u8"] = ®ion_write; + region_type["write_i16"] = ®ion_write; + region_type["write_u16"] = ®ion_write; + region_type["write_i32"] = ®ion_write; + region_type["write_u32"] = ®ion_write; + region_type["write_i64"] = ®ion_write; + region_type["write_u64"] = ®ion_write; + region_type["tag"] = sol::property(&memory_region::name); + region_type["size"] = sol::property(&memory_region::bytes); + region_type["length"] = sol::property([] (memory_region &r) { return r.bytes() / r.bytewidth(); }); + region_type["endianness"] = sol::property(&memory_region::endianness); + region_type["bitwidth"] = sol::property(&memory_region::bitwidth); + region_type["bytewidth"] = sol::property(&memory_region::bytewidth); + + + auto share_type = sol().registry().new_usertype("share", sol::no_constructor); + share_type["read_i8"] = &share_read; + share_type["read_u8"] = &share_read; + share_type["read_i16"] = &share_read; + share_type["read_u16"] = &share_read; + share_type["read_i32"] = &share_read; + share_type["read_u32"] = &share_read; + share_type["read_i64"] = &share_read; + share_type["read_u64"] = &share_read; + share_type["write_i8"] = &share_write; + share_type["write_u8"] = &share_write; + share_type["write_i16"] = &share_write; + share_type["write_u16"] = &share_write; + share_type["write_i32"] = &share_write; + share_type["write_u32"] = &share_write; + share_type["write_i64"] = &share_write; + share_type["write_u64"] = &share_write; + share_type["tag"] = sol::property(&memory_share::name); + share_type["size"] = sol::property(&memory_share::bytes); + share_type["length"] = sol::property([] (memory_share &s) { return s.bytes() / s.bytewidth(); }); + share_type["endianness"] = sol::property(&memory_share::endianness); + share_type["bitwidth"] = sol::property(&memory_share::bitwidth); + share_type["bytewidth"] = sol::property(&memory_share::bytewidth); + +} diff --git a/src/icludes/frontend/mame/luaengine_render.cpp b/src/icludes/frontend/mame/luaengine_render.cpp new file mode 100644 index 0000000..21a68dc --- /dev/null +++ b/src/icludes/frontend/mame/luaengine_render.cpp @@ -0,0 +1,518 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + luaengine_render.cpp + + Controls execution of the core MAME system. + +***************************************************************************/ + +#include "emu.h" +#include "luaengine.ipp" + +#include "mame.h" +#include "ui/ui.h" + +#include "render.h" +#include "rendlay.h" + +#include + + +namespace { + +struct layout_file_views +{ + layout_file_views(layout_file &f) : file(f) { } + layout_file::view_list &items() { return file.views(); } + + static layout_view &unwrap(layout_file::view_list::iterator const &it) { return *it; } + static int push_key(lua_State *L, layout_file::view_list::iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, it->unqualified_name()); } + + layout_file &file; +}; + + +struct layout_view_items +{ + layout_view_items(layout_view &v) : view(v) { } + layout_view::item_list &items() { return view.items(); } + + static layout_view_item &unwrap(layout_view::item_list::iterator const &it) { return *it; } + static int push_key(lua_State *L, layout_view::item_list::iterator const &it, std::size_t ix) { return sol::stack::push(L, ix + 1); } + + layout_view &view; +}; + + +struct render_target_view_names +{ + render_target_view_names(render_target &t) : target(t), count(-1) { } + + render_target ⌖ + int count; +}; + +} // anonymous namespace + + +namespace sol { + +template <> struct is_container : std::true_type { }; +template <> struct is_container : std::true_type { }; +template <> struct is_container : std::true_type { }; + + +template <> +struct usertype_container : lua_engine::immutable_sequence_helper +{ +public: + static int get(lua_State *L) + { + layout_file_views &self(get_self(L)); + char const *const name(stack::unqualified_get(L)); + auto const found(std::find_if( + self.file.views().begin(), + self.file.views().end(), + [&name] (layout_view &v) { return v.unqualified_name() == name; })); + if (self.file.views().end() != found) + return stack::push_reference(L, *found); + else + return stack::push(L, lua_nil); + } + + static int index_get(lua_State *L) + { + return get(L); + } +}; + + +template <> +struct usertype_container : lua_engine::immutable_sequence_helper +{ +public: + static int get(lua_State *L) + { + layout_view_items &self(get_self(L)); + char const *const id(stack::unqualified_get(L)); + layout_view_item *const item(self.view.get_item(id)); + if (item) + return stack::push_reference(L, *item); + else + return stack::push(L, lua_nil); + } + + static int index_get(lua_State *L) + { + return get(L); + } + + static int pairs(lua_State *L) + { + return luaL_error(L, "sol: cannot call 'pairs' on type '%s': not iterable by ID", sol::detail::demangle().c_str()); + } +}; + + +template <> +struct usertype_container : lua_engine::immutable_container_helper +{ +private: + struct iterator + { + iterator(render_target &t, unsigned i) : target(t), index(i) { } + + render_target ⌖ + unsigned index; + }; + + static int next_pairs(lua_State *L) + { + iterator &i(stack::unqualified_get >(L, 1)); + char const *name(i.target.view_name(i.index)); + if (!name) + return stack::push(L, lua_nil); + int result = stack::push(L, i.index + 1); + result += stack::push(L, name); + ++i.index; + return result; + } + +public: + static int at(lua_State *L) + { + render_target_view_names &self(get_self(L)); + unsigned const index(stack::unqualified_get(L, 2)); + return stack::push(L, self.target.view_name(index - 1)); + } + + static int get(lua_State *L) + { + return at(L); + } + + static int index_get(lua_State *L) + { + return at(L); + } + + static int find(lua_State *L) + { + render_target_view_names &self(get_self(L)); + char const *const key(stack::unqualified_get(L, 2)); + for (unsigned i = 0; ; ++i) + { + char const *const name(self.target.view_name(i)); + if (!name) + return stack::push(L, lua_nil); + else if (!std::strcmp(key, name)) + return stack::push(L, i + 1); + } + } + + static int index_of(lua_State *L) + { + return find(L); + } + + static int size(lua_State *L) + { + render_target_view_names &self(get_self(L)); + if (0 > self.count) + for (self.count = 0; self.target.view_name(self.count); ++self.count) { } + return stack::push(L, self.count); + } + + static int empty(lua_State *L) + { + render_target_view_names &self(get_self(L)); + return stack::push(L, !self.target.view_name(0)); + } + + static int next(lua_State *L) + { + return stack::push(L, next_pairs); + } + + static int pairs(lua_State *L) + { + render_target_view_names &self(get_self(L)); + stack::push(L, next_pairs); + stack::push >(L, self.target, 0); + stack::push(L, lua_nil); + return 3; + } + + static int ipairs(lua_State *L) + { + return pairs(L); + } +}; + +} // namespace sol + + +//------------------------------------------------- +// initialize_render - register render user types +//------------------------------------------------- + +void lua_engine::initialize_render(sol::table &emu) +{ + + auto bounds_type = emu.new_usertype( + "render_bounds", + sol::call_constructor, sol::initializers( + [] (render_bounds &b) { new (&b) render_bounds{ 0.0F, 0.0F, 1.0F, 1.0F }; }, + [] (render_bounds &b, float x0, float y0, float x1, float y1) { new (&b) render_bounds{ x0, y0, x1, y1 }; })); + bounds_type["includes"] = &render_bounds::includes; + bounds_type["set_xy"] = &render_bounds::set_xy; + bounds_type["set_wh"] = &render_bounds::set_wh; + bounds_type["x0"] = &render_bounds::x0; + bounds_type["y0"] = &render_bounds::y0; + bounds_type["x1"] = &render_bounds::x1; + bounds_type["y1"] = &render_bounds::y1; + bounds_type["width"] = sol::property(&render_bounds::width, [] (render_bounds &b, float w) { b.x1 = b.x0 + w; }); + bounds_type["height"] = sol::property(&render_bounds::height, [] (render_bounds &b, float h) { b.y1 = b.y0 + h; }); + bounds_type["aspect"] = sol::property(&render_bounds::aspect); + + + auto color_type = emu.new_usertype( + "render_color", + sol::call_constructor, sol::initializers( + [] (render_color &c) { new (&c) render_color{ 1.0F, 1.0F, 1.0F, 1.0F }; }, + [] (render_color &c, float a, float r, float g, float b) { new (&c) render_color{ a, r, g, b }; })); + color_type["set"] = &render_color::set; + color_type["a"] = &render_color::a; + color_type["r"] = &render_color::r; + color_type["g"] = &render_color::g; + color_type["b"] = &render_color::b; + + + auto layout_view_type = sol().registry().new_usertype("layout_view", sol::no_constructor); + layout_view_type["has_screen"] = &layout_view::has_screen; + layout_view_type["set_prepare_items_callback"] = + make_simple_callback_setter( + &layout_view::set_prepare_items_callback, + nullptr, + "set_prepare_items_callback", + nullptr); + layout_view_type["set_preload_callback"] = + make_simple_callback_setter( + &layout_view::set_preload_callback, + nullptr, + "set_preload_callback", + nullptr); + layout_view_type["set_recomputed_callback"] = + make_simple_callback_setter( + &layout_view::set_recomputed_callback, + nullptr, + "set_recomputed_callback", + nullptr); + layout_view_type["items"] = sol::property([] (layout_view &v) { return layout_view_items(v); }); + layout_view_type["name"] = sol::property(&layout_view::name); + layout_view_type["unqualified_name"] = sol::property(&layout_view::unqualified_name); + layout_view_type["visible_screen_count"] = sol::property(&layout_view::visible_screen_count); + layout_view_type["effective_aspect"] = sol::property(&layout_view::effective_aspect); + layout_view_type["bounds"] = sol::property(&layout_view::bounds); + layout_view_type["has_art"] = sol::property(&layout_view::has_art); + + + auto layout_view_item_type = sol().registry().new_usertype("layout_item", sol::no_constructor); + layout_view_item_type["set_state"] = &layout_view_item::set_state; + layout_view_item_type["set_element_state_callback"] = + make_simple_callback_setter( + &layout_view_item::set_element_state_callback, + [] () { return 0; }, + "set_element_state_callback", + "element state"); + layout_view_item_type["set_animation_state_callback"] = + make_simple_callback_setter( + &layout_view_item::set_animation_state_callback, + [] () { return 0; }, + "set_animation_state_callback", + "animation state"); + layout_view_item_type["set_bounds_callback"] = + make_simple_callback_setter( + &layout_view_item::set_bounds_callback, + [] () { return render_bounds{ 0.0f, 0.0f, 1.0f, 1.0f }; }, + "set_bounds_callback", + "bounds"); + layout_view_item_type["set_color_callback"] = + make_simple_callback_setter( + &layout_view_item::set_color_callback, + [] () { return render_color{ 1.0f, 1.0f, 1.0f, 1.0f }; }, + "set_color_callback", + "color"); + layout_view_item_type["set_scroll_size_x_callback"] = + make_simple_callback_setter( + &layout_view_item::set_scroll_size_x_callback, + [] () { return 1.0f; }, + "set_scroll_size_x_callback", + "horizontal scroll window size"); + layout_view_item_type["set_scroll_size_y_callback"] = + make_simple_callback_setter( + &layout_view_item::set_scroll_size_y_callback, + [] () { return 1.0f; }, + "set_scroll_size_y_callback", + "vertical scroll window size"); + layout_view_item_type["set_scroll_pos_x_callback"] = + make_simple_callback_setter( + &layout_view_item::set_scroll_pos_x_callback, + [] () { return 1.0f; }, + "set_scroll_pos_x_callback", + "horizontal scroll position"); + layout_view_item_type["set_scroll_pos_y_callback"] = + make_simple_callback_setter( + &layout_view_item::set_scroll_pos_y_callback, + [] () { return 1.0f; }, + "set_scroll_pos_y_callback", + "vertical scroll position"); + layout_view_item_type["id"] = sol::property( + [] (layout_view_item &i, sol::this_state s) -> sol::object + { + if (i.id().empty()) + return sol::lua_nil; + else + return sol::make_object(s, i.id()); + }); + layout_view_item_type["bounds_animated"] = sol::property(&layout_view_item::bounds_animated); + layout_view_item_type["color_animated"] = sol::property(&layout_view_item::color_animated); + layout_view_item_type["bounds"] = sol::property(&layout_view_item::bounds); + layout_view_item_type["color"] = sol::property(&layout_view_item::color); + layout_view_item_type["scroll_wrap_x"] = sol::property(&layout_view_item::scroll_wrap_x); + layout_view_item_type["scroll_wrap_y"] = sol::property(&layout_view_item::scroll_wrap_y); + layout_view_item_type["scroll_size_x"] = sol::property( + &layout_view_item::scroll_size_x, + &layout_view_item::set_scroll_size_x); + layout_view_item_type["scroll_size_y"] = sol::property( + &layout_view_item::scroll_size_y, + &layout_view_item::set_scroll_size_y); + layout_view_item_type["scroll_pos_x"] = sol::property( + &layout_view_item::scroll_pos_x, + &layout_view_item::set_scroll_pos_y); + layout_view_item_type["scroll_pos_y"] = sol::property( + &layout_view_item::scroll_pos_y, + &layout_view_item::set_scroll_pos_y); + layout_view_item_type["blend_mode"] = sol::property(&layout_view_item::blend_mode); + layout_view_item_type["orientation"] = sol::property(&layout_view_item::orientation); + layout_view_item_type["element_state"] = sol::property(&layout_view_item::element_state); + layout_view_item_type["animation_state"] = sol::property(&layout_view_item::animation_state); + + + auto layout_file_type = sol().registry().new_usertype("layout_file", sol::no_constructor); + layout_file_type["set_resolve_tags_callback"] = + make_simple_callback_setter( + &layout_file::set_resolve_tags_callback, + nullptr, + "set_resolve_tags_callback", + nullptr); + layout_file_type["device"] = sol::property(&layout_file::device); + layout_file_type["views"] = sol::property([] (layout_file &f) { return layout_file_views(f); }); + + + auto target_type = sol().registry().new_usertype("target", sol::no_constructor); + target_type["index"] = sol::property([] (render_target const &t) { return t.index() + 1; }); + target_type["width"] = sol::property(&render_target::width); + target_type["height"] = sol::property(&render_target::height); + target_type["pixel_aspect"] = sol::property(&render_target::pixel_aspect); + target_type["hidden"] = sol::property(&render_target::hidden); + target_type["is_ui_target"] = sol::property(&render_target::is_ui_target); + target_type["max_update_rate"] = sol::property(&render_target::max_update_rate, &render_target::set_max_update_rate); + target_type["orientation"] = sol::property(&render_target::orientation, &render_target::set_orientation); + target_type["view_names"] = sol::property([] (render_target &t) { return render_target_view_names(t); }); + target_type["current_view"] = sol::property(&render_target::current_view); + target_type["view_index"] = sol::property( + [] (render_target const &t) { return t.view() + 1; }, + [] (render_target &t, unsigned v) { t.set_view(v - 1); }); + target_type["visibility_mask"] = sol::property(&render_target::visibility_mask); + target_type["screen_overlay"] = sol::property(&render_target::screen_overlay_enabled, &render_target::set_screen_overlay_enabled); + target_type["zoom_to_screen"] = sol::property(&render_target::zoom_to_screen, &render_target::set_zoom_to_screen); + + + auto render_container_type = sol().registry().new_usertype("render_container", sol::no_constructor); + render_container_type["draw_box"] = + [] (render_container &ctnr, float x1, float y1, float x2, float y2, std::optional fgcolor, std::optional bgcolor) + { + x1 = std::clamp(x1, 0.0f, 1.0f); + y1 = std::clamp(y1, 0.0f, 1.0f); + x2 = std::clamp(x2, 0.0f, 1.0f); + y2 = std::clamp(y2, 0.0f, 1.0f); + mame_ui_manager &ui(mame_machine_manager::instance()->ui()); + if (!fgcolor) + fgcolor = ui.colors().text_color(); + if (!bgcolor) + bgcolor = ui.colors().background_color(); + ui.draw_outlined_box(ctnr, x1, y1, x2, y2, *fgcolor, *bgcolor); + }; + render_container_type["draw_line"] = + [] (render_container &ctnr, float x1, float y1, float x2, float y2, std::optional color) + { + x1 = std::clamp(x1, 0.0f, 1.0f); + y1 = std::clamp(y1, 0.0f, 1.0f); + x2 = std::clamp(x2, 0.0f, 1.0f); + y2 = std::clamp(y2, 0.0f, 1.0f); + if (!color) + color = mame_machine_manager::instance()->ui().colors().text_color(); + ctnr.add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(*color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + }; + render_container_type["draw_text"] = + [this] (render_container &ctnr, sol::object xobj, float y, char const *msg, std::optional fgcolor, std::optional bgcolor) + { + auto justify = ui::text_layout::text_justify::LEFT; + float x = 0; + if (xobj.is()) + { + x = std::clamp(xobj.as(), 0.0f, 1.0f); + } + else if (xobj.is()) + { + char const *const justifystr(xobj.as()); + if (!strcmp(justifystr, "left")) + justify = ui::text_layout::text_justify::LEFT; + else if (!strcmp(justifystr, "right")) + justify = ui::text_layout::text_justify::RIGHT; + else if (!strcmp(justifystr, "center")) + justify = ui::text_layout::text_justify::CENTER; + } + else + { + luaL_error(m_lua_state, "Error in param 1 to draw_text"); + return; + } + y = std::clamp(y, 0.0f, 1.0f); + mame_ui_manager &ui(mame_machine_manager::instance()->ui()); + if (!fgcolor) + fgcolor = ui.colors().text_color(); + if (!bgcolor) + bgcolor = 0; + ui.draw_text_full( + ctnr, + msg, + x, y, (1.0f - x), + justify, ui::text_layout::word_wrapping::WORD, + mame_ui_manager::OPAQUE_, *fgcolor, *bgcolor); + }; + render_container_type["user_settings"] = sol::property(&render_container::get_user_settings, &render_container::set_user_settings); + render_container_type["orientation"] = sol::property( + &render_container::orientation, + [] (render_container &c, int v) + { + render_container::user_settings s(c.get_user_settings()); + s.m_orientation = v; + c.set_user_settings(s); + }); + render_container_type["xscale"] = sol::property( + &render_container::xscale, + [] (render_container &c, float v) + { + render_container::user_settings s(c.get_user_settings()); + s.m_xscale = v; + c.set_user_settings(s); + }); + render_container_type["yscale"] = sol::property( + &render_container::yscale, + [] (render_container &c, float v) + { + render_container::user_settings s(c.get_user_settings()); + s.m_yscale = v; + c.set_user_settings(s); + }); + render_container_type["xoffset"] = sol::property( + &render_container::xoffset, + [] (render_container &c, float v) + { + render_container::user_settings s(c.get_user_settings()); + s.m_xoffset = v; + c.set_user_settings(s); + }); + render_container_type["yoffset"] = sol::property( + &render_container::yoffset, + [] (render_container &c, float v) + { + render_container::user_settings s(c.get_user_settings()); + s.m_yoffset = v; + c.set_user_settings(s); + }); + render_container_type["is_empty"] = sol::property(&render_container::is_empty); + + + auto user_settings_type = sol().registry().new_usertype("render_container_settings", sol::no_constructor); + user_settings_type["orientation"] = &render_container::user_settings::m_orientation; + user_settings_type["brightness"] = &render_container::user_settings::m_brightness; + user_settings_type["contrast"] = &render_container::user_settings::m_contrast; + user_settings_type["gamma"] = &render_container::user_settings::m_gamma; + user_settings_type["xscale"] = &render_container::user_settings::m_xscale; + user_settings_type["yscale"] = &render_container::user_settings::m_yscale; + user_settings_type["xoffset"] = &render_container::user_settings::m_xoffset; + user_settings_type["yoffset"] = &render_container::user_settings::m_yoffset; + + + auto render_type = sol().registry().new_usertype("render", sol::no_constructor); + render_type["max_update_rate"] = sol::property(&render_manager::max_update_rate); + render_type["ui_target"] = sol::property(&render_manager::ui_target); + render_type["ui_container"] = sol::property(&render_manager::ui_container); + render_type["targets"] = sol::property([] (render_manager &m) { return simple_list_wrapper(m.targets()); }); + +} diff --git a/src/icludes/frontend/mame/mame.cpp b/src/icludes/frontend/mame/mame.cpp new file mode 100644 index 0000000..c7bc087 --- /dev/null +++ b/src/icludes/frontend/mame/mame.cpp @@ -0,0 +1,446 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles +/*************************************************************************** + + mame.c + + Controls execution of the core MAME system. + +***************************************************************************/ + +#include "emu.h" +#include "mame.h" + +#include "ui/inifile.h" +#include "ui/selgame.h" +#include "ui/simpleselgame.h" +#include "ui/ui.h" + +#include "cheat.h" +#include "clifront.h" +#include "emuopts.h" +#include "luaengine.h" +#include "mameopts.h" +#include "pluginopts.h" +#include "rendlay.h" +#include "validity.h" + +#include "corestr.h" +#include "xmlfile.h" + +#include "osdepend.h" + +#include + + +//************************************************************************** +// MACHINE MANAGER +//************************************************************************** + +mame_machine_manager *mame_machine_manager::s_manager = nullptr; + +mame_machine_manager* mame_machine_manager::instance(emu_options &options, osd_interface &osd) +{ + if (!s_manager) + s_manager = new mame_machine_manager(options, osd); + + return s_manager; +} + +mame_machine_manager* mame_machine_manager::instance() +{ + return s_manager; +} + +//------------------------------------------------- +// mame_machine_manager - constructor +//------------------------------------------------- + +mame_machine_manager::mame_machine_manager(emu_options &options,osd_interface &osd) : + machine_manager(options, osd), + m_plugins(std::make_unique()), + m_lua(std::make_unique()), + m_new_driver_pending(nullptr), + m_firstrun(true), + m_autoboot_timer(nullptr) +{ +} + + +//------------------------------------------------- +// ~mame_machine_manager - destructor +//------------------------------------------------- + +mame_machine_manager::~mame_machine_manager() +{ + m_lua.reset(); + s_manager = nullptr; +} + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +//------------------------------------------------- +// mame_schedule_new_driver - schedule a new game to +// be loaded +//------------------------------------------------- + +void mame_machine_manager::schedule_new_driver(const game_driver &driver) +{ + m_new_driver_pending = &driver; +} + + +/*************************************************************************** + CORE IMPLEMENTATION +***************************************************************************/ + +//------------------------------------------------- +// update_machine +//------------------------------------------------- + +void mame_machine_manager::update_machine() +{ + m_lua->set_machine(m_machine); + m_lua->attach_notifiers(); +} + + +//------------------------------------------------- +// split +//------------------------------------------------- + +static std::vector split(const std::string &text, char sep) +{ + std::vector tokens; + std::size_t start = 0, end = 0; + while ((end = text.find(sep, start)) != std::string::npos) + { + std::string temp = text.substr(start, end - start); + if (temp != "") tokens.push_back(temp); + start = end + 1; + } + std::string temp = text.substr(start); + if (temp != "") tokens.push_back(temp); + return tokens; +} + + +//------------------------------------------------- +// start_luaengine +//------------------------------------------------- + +void mame_machine_manager::start_luaengine() +{ + if (options().plugins()) + { + // scan all plugin directories + path_iterator iter(options().plugins_path()); + std::string pluginpath; + while (iter.next(pluginpath)) + { + // user may specify environment variables; subsitute them + osd_subst_env(pluginpath, pluginpath); + + // and then scan the directory recursively + m_plugins->scan_directory(pluginpath, true); + } + + { + // parse the file + // attempt to open the output file + emu_file file(options().ini_path(), OPEN_FLAG_READ); + if (!file.open("plugin.ini")) + { + try + { + m_plugins->parse_ini_file((util::core_file&)file); + } + catch (options_exception &) + { + osd_printf_error("**Error loading plugin.ini**\n"); + } + } + } + + // process includes + for (const std::string &incl : split(options().plugin(), ',')) + { + plugin_options::plugin *p = m_plugins->find(incl); + if (!p) + fatalerror("Fatal error: Could not load plugin: %s\n", incl); + p->m_start = true; + } + + // process excludes + for (const std::string &excl : split(options().no_plugin(), ',')) + { + plugin_options::plugin *p = m_plugins->find(excl); + if (!p) + fatalerror("Fatal error: Unknown plugin: %s\n", excl); + p->m_start = false; + } + } + + // we have a special way to open the console plugin + if (options().console()) + { + plugin_options::plugin *p = m_plugins->find(OPTION_CONSOLE); + if (!p) + fatalerror("Fatal error: Console plugin not found.\n"); + + p->m_start = true; + } + + m_lua->initialize(); + + { + emu_file file(options().plugins_path(), OPEN_FLAG_READ); + std::error_condition const filerr = file.open("boot.lua"); + if (!filerr) + { + std::string exppath; + osd_subst_env(exppath, std::string(file.fullpath())); + m_lua->load_script(exppath.c_str()); + } + } +} + + +//------------------------------------------------- +// execute - run the core emulation +//------------------------------------------------- + +int mame_machine_manager::execute() +{ + bool started_empty = false; + + bool firstgame = true; + + // loop across multiple hard resets + bool exit_pending = false; + int error = EMU_ERR_NONE; + + while (error == EMU_ERR_NONE && !exit_pending) + { + m_new_driver_pending = nullptr; + + // if no driver, use the internal empty driver + const game_driver *system = mame_options::system(m_options); + if (system == nullptr) + { + system = &GAME_NAME(___empty); + if (firstgame) + started_empty = true; + } + + firstgame = false; + + // parse any INI files as the first thing + if (m_options.read_config()) + { + // but first, revert out any potential game-specific INI settings from previous runs via the internal UI + m_options.revert(OPTION_PRIORITY_INI); + + std::ostringstream errors; + mame_options::parse_standard_inis(m_options, errors); + } + + // otherwise, perform validity checks before anything else + bool is_empty = (system == &GAME_NAME(___empty)); + if (!is_empty) + { + validity_checker valid(m_options, true); + valid.set_verbose(false); + valid.check_shared_source(*system); + } + + // create the machine configuration + machine_config config(*system, m_options); + + // create the machine structure and driver + running_machine machine(config, *this); + + set_machine(&machine); + + // run the machine + error = machine.run(is_empty); + m_firstrun = false; + + // check the state of the machine + if (m_new_driver_pending) + { + // set up new system name and adjust device options accordingly + m_options.set_system_name(m_new_driver_pending->name); + m_firstrun = true; + } + else + { + if (machine.exit_pending()) + m_options.set_system_name(""); + } + + if (machine.exit_pending() && (!started_empty || is_empty)) + exit_pending = true; + + // machine will go away when we exit scope + set_machine(nullptr); + } + // return an error + return error; +} + +TIMER_CALLBACK_MEMBER(mame_machine_manager::autoboot_callback) +{ + if (strlen(options().autoboot_script())!=0) { + mame_machine_manager::instance()->lua()->load_script(options().autoboot_script()); + } + else if (strlen(options().autoboot_command())!=0) { + std::string cmd = std::string(options().autoboot_command()); + strreplace(cmd, "'", "\\'"); + std::string val = std::string("emu.keypost('").append(cmd).append("')"); + mame_machine_manager::instance()->lua()->load_string(val.c_str()); + } +} + +void mame_machine_manager::reset() +{ + // setup autoboot if needed + m_autoboot_timer->adjust(attotime(options().autoboot_delay(),0),0); +} + +ui_manager* mame_machine_manager::create_ui(running_machine& machine) +{ + m_ui = std::make_unique(machine); + m_ui->init(); + + machine.add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&mame_machine_manager::reset, this)); + + m_ui->set_startup_text("Initializing...", true); + + return m_ui.get(); +} + +void mame_machine_manager::ui_initialize(running_machine& machine) +{ + m_ui->initialize(machine); + + // display the startup screens + m_ui->display_startup_screens(m_firstrun); +} + +void mame_machine_manager::before_load_settings(running_machine& machine) +{ + m_lua->on_machine_before_load_settings(); +} + +void mame_machine_manager::create_custom(running_machine& machine) +{ + // start the inifile manager + m_inifile = std::make_unique(m_ui->options()); + + // allocate autoboot timer + m_autoboot_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(mame_machine_manager::autoboot_callback), this)); + + // start favorite manager + m_favorite = std::make_unique(m_ui->options()); +} + +void mame_machine_manager::load_cheatfiles(running_machine& machine) +{ + // set up the cheat engine + m_cheat = std::make_unique(machine); +} + +//------------------------------------------------- +// missing_mandatory_images - search for devices +// which need an image to be loaded +//------------------------------------------------- + +std::vector> mame_machine_manager::missing_mandatory_images() +{ + std::vector> results; + assert(m_machine); + + // make sure that any required image has a mounted file + for (device_image_interface &image : image_interface_enumerator(m_machine->root_device())) + { + if (image.must_be_loaded()) + { + if (m_machine->options().image_option(image.instance_name()).value().empty()) + { + // this is a missing image; give LUA plugins a chance to handle it + if (!lua()->on_missing_mandatory_image(image.instance_name())) + results.push_back(std::reference_wrapper(image.instance_name())); + } + } + } + return results; +} + +const char * emulator_info::get_bare_build_version() { return bare_build_version; } +const char * emulator_info::get_build_version() { return build_version; } + +void emulator_info::display_ui_chooser(running_machine& machine) +{ + // force the UI to show the game select screen + mame_ui_manager &mui = mame_machine_manager::instance()->ui(); + render_container &container = machine.render().ui_container(); + if (machine.options().ui() == emu_options::UI_SIMPLE) + ui::simple_menu_select_game::force_game_select(mui, container); + else + ui::menu_select_game::force_game_select(mui, container); +} + +int emulator_info::start_frontend(emu_options &options, osd_interface &osd, std::vector &args) +{ + cli_frontend frontend(options, osd); + return frontend.execute(args); +} + +int emulator_info::start_frontend(emu_options &options, osd_interface &osd, int argc, char *argv[]) +{ + std::vector args(argv, argv + argc); + return start_frontend(options, osd, args); +} + +void emulator_info::draw_user_interface(running_machine& machine) +{ + mame_machine_manager::instance()->ui().update_and_render(machine.render().ui_container()); +} + +void emulator_info::periodic_check() +{ + return mame_machine_manager::instance()->lua()->on_periodic(); +} + +bool emulator_info::frame_hook() +{ + return mame_machine_manager::instance()->lua()->frame_hook(); +} + +void emulator_info::sound_hook() +{ + return mame_machine_manager::instance()->lua()->on_sound_update(); +} + +void emulator_info::layout_script_cb(layout_file &file, const char *script) +{ + // TODO: come up with a better way to pass multiple arguments to plugin + //mame_machine_manager::instance()->lua()->call_plugin_set("layout", std::make_tuple(&file, script->get_value())); + auto &lua(mame_machine_manager::instance()->lua()->sol()); + sol::object obj = lua.registry()["cb_layout"]; + if (obj.is()) + { + auto res = obj.as()(sol::make_reference(lua, &file), sol::make_reference(lua, script)); + if (!res.valid()) + { + sol::error err = res; + osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what()); + } + } +} + +bool emulator_info::standalone() { return false; } diff --git a/src/icludes/frontend/mame/mame.h b/src/icludes/frontend/mame/mame.h new file mode 100644 index 0000000..5fb5a2f --- /dev/null +++ b/src/icludes/frontend/mame/mame.h @@ -0,0 +1,97 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles +/*************************************************************************** + + mame.h + + Controls execution of the core MAME system. +***************************************************************************/ + +#ifndef MAME_FRONTEND_MAME_MAME_H +#define MAME_FRONTEND_MAME_MAME_H + +#pragma once + +class plugin_options; +class osd_interface; + +class lua_engine; +class cheat_manager; +class inifile_manager; +class favorite_manager; +class mame_ui_manager; + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> machine_manager + +class mame_machine_manager : public machine_manager +{ +public: + static mame_machine_manager *instance(emu_options &options, osd_interface &osd); + static mame_machine_manager *instance(); + ~mame_machine_manager(); + + plugin_options &plugins() const { return *m_plugins; } + lua_engine *lua() { return m_lua.get(); } + + virtual void update_machine() override; + + void reset(); + TIMER_CALLBACK_MEMBER(autoboot_callback); + + virtual ui_manager* create_ui(running_machine& machine) override; + + virtual void create_custom(running_machine& machine) override; + + virtual void load_cheatfiles(running_machine& machine) override; + + virtual void ui_initialize(running_machine& machine) override; + + virtual void before_load_settings(running_machine& machine) override; + + std::vector> missing_mandatory_images(); + + /* execute as configured by the OPTION_SYSTEMNAME option on the specified options */ + int execute(); + void start_luaengine(); + void schedule_new_driver(const game_driver &driver); + mame_ui_manager& ui() const { assert(m_ui != nullptr); return *m_ui; } + cheat_manager &cheat() const { assert(m_cheat != nullptr); return *m_cheat; } + inifile_manager &inifile() const { assert(m_inifile != nullptr); return *m_inifile; } + favorite_manager &favorite() const { assert(m_favorite != nullptr); return *m_favorite; } + +private: + // construction + mame_machine_manager(emu_options &options, osd_interface &osd); + mame_machine_manager(mame_machine_manager const &) = delete; + mame_machine_manager(mame_machine_manager &&) = delete; + mame_machine_manager &operator=(mame_machine_manager const &) = delete; + mame_machine_manager &operator=(mame_machine_manager &&) = delete; + + std::unique_ptr m_plugins; // pointer to plugin options + std::unique_ptr m_lua; + + const game_driver * m_new_driver_pending; // pointer to the next pending driver + bool m_firstrun; + + static mame_machine_manager *s_manager; + emu_timer *m_autoboot_timer; // autoboot timer + std::unique_ptr m_ui; // internal data from ui.cpp + std::unique_ptr m_cheat; // internal data from cheat.cpp + std::unique_ptr m_inifile; // internal data from inifile.c for INIs + std::unique_ptr m_favorite; // internal data from inifile.c for favorites + +}; + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +extern const char build_version[]; +extern const char bare_build_version[]; +extern const char bare_vcs_revision[]; + +#endif // MAME_FRONTEND_MAME_MAME_H diff --git a/src/icludes/frontend/mame/mameopts.cpp b/src/icludes/frontend/mame/mameopts.cpp new file mode 100644 index 0000000..a1d370d --- /dev/null +++ b/src/icludes/frontend/mame/mameopts.cpp @@ -0,0 +1,210 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + mameopts.cpp + + Options file and command line management. + +***************************************************************************/ + +#include "emu.h" +#include "mameopts.h" + +#include "drivenum.h" +#include "screen.h" +#include "softlist_dev.h" +#include "zippath.h" +#include "hashfile.h" +#include "clifront.h" + +#include +#include + + +//------------------------------------------------- +// parse_standard_inis - parse the standard set +// of INI files +//------------------------------------------------- + +void mame_options::parse_standard_inis(emu_options &options, std::ostream &error_stream, const game_driver *driver) +{ + // parse the INI file defined by the platform (e.g., "mame.ini") + // we do this twice so that the first file can change the INI path + parse_one_ini(options, emulator_info::get_configname(), OPTION_PRIORITY_MAME_INI); + parse_one_ini(options, emulator_info::get_configname(), OPTION_PRIORITY_MAME_INI, &error_stream); + + // debug mode: parse "debug.ini" as well + if (options.debug()) + parse_one_ini(options, "debug", OPTION_PRIORITY_DEBUG_INI, &error_stream); + + // if we have a valid system driver, parse system-specific INI files + game_driver const *const cursystem = !driver ? system(options) : driver; + if (!cursystem) + return; + + // parse "vertical.ini" or "horizont.ini" + if (cursystem->flags & ORIENTATION_SWAP_XY) + parse_one_ini(options, "vertical", OPTION_PRIORITY_ORIENTATION_INI, &error_stream); + else + parse_one_ini(options, "horizont", OPTION_PRIORITY_ORIENTATION_INI, &error_stream); + + switch (cursystem->flags & machine_flags::MASK_TYPE) + { + case machine_flags::TYPE_ARCADE: + parse_one_ini(options, "arcade", OPTION_PRIORITY_SYSTYPE_INI, &error_stream); + break; + case machine_flags::TYPE_CONSOLE: + parse_one_ini(options ,"console", OPTION_PRIORITY_SYSTYPE_INI, &error_stream); + break; + case machine_flags::TYPE_COMPUTER: + parse_one_ini(options, "computer", OPTION_PRIORITY_SYSTYPE_INI, &error_stream); + break; + case machine_flags::TYPE_OTHER: + parse_one_ini(options, "othersys", OPTION_PRIORITY_SYSTYPE_INI, &error_stream); + break; + default: + break; + } + + machine_config config(*cursystem, options); + for (const screen_device &device : screen_device_enumerator(config.root_device())) + { + // parse "raster.ini" for raster games + if (device.screen_type() == SCREEN_TYPE_RASTER) + { + parse_one_ini(options, "raster", OPTION_PRIORITY_SCREEN_INI, &error_stream); + break; + } + // parse "vector.ini" for vector games + if (device.screen_type() == SCREEN_TYPE_VECTOR) + { + parse_one_ini(options, "vector", OPTION_PRIORITY_SCREEN_INI, &error_stream); + break; + } + // parse "lcd.ini" for lcd games + if (device.screen_type() == SCREEN_TYPE_LCD) + { + parse_one_ini(options, "lcd", OPTION_PRIORITY_SCREEN_INI, &error_stream); + break; + } + } + + // next parse "source/.ini" + std::string sourcename = std::string(core_filename_extract_base(cursystem->type.source(), true)).insert(0, "source" PATH_SEPARATOR); + parse_one_ini(options, sourcename.c_str(), OPTION_PRIORITY_SOURCE_INI, &error_stream); + + // then parse the grandparent, parent, and system-specific INIs + int parent = driver_list::clone(*cursystem); + int gparent = (parent != -1) ? driver_list::clone(parent) : -1; + if (gparent != -1) + parse_one_ini(options, driver_list::driver(gparent).name, OPTION_PRIORITY_GPARENT_INI, &error_stream); + if (parent != -1) + parse_one_ini(options, driver_list::driver(parent).name, OPTION_PRIORITY_PARENT_INI, &error_stream); + parse_one_ini(options, cursystem->name, OPTION_PRIORITY_DRIVER_INI, &error_stream); +} + + +//------------------------------------------------- +// system - return a pointer to the specified +// system driver, or nullptr if no match +//------------------------------------------------- + +const game_driver *mame_options::system(const emu_options &options) +{ + int index = driver_list::find(std::string(core_filename_extract_base(options.system_name(), true)).c_str()); + return (index != -1) ? &driver_list::driver(index) : nullptr; +} + + +//------------------------------------------------- +// parse_one_ini - parse a single INI file +//------------------------------------------------- + +void mame_options::parse_one_ini(emu_options &options, const char *basename, int priority, std::ostream *error_stream) +{ + // don't parse if it has been disabled + if (!options.read_config()) + return; + + // open the file; if we fail, that's ok + emu_file file(options.ini_path(), OPEN_FLAG_READ); + osd_printf_verbose("Attempting load of %s.ini\n", basename); + std::error_condition const filerr = file.open(std::string(basename) + ".ini"); + if (filerr) + return; + + // parse the file + osd_printf_verbose("Parsing %s.ini\n", basename); + try + { + options.parse_ini_file((util::core_file&)file, priority, priority < OPTION_PRIORITY_DRIVER_INI, false); + } + catch (options_exception &ex) + { + if (error_stream) + util::stream_format(*error_stream, "While parsing %s:\n%s\n", file.fullpath(), ex.message()); + return; + } + +} + + +//------------------------------------------------- +// populate_hashpath_from_args_and_inis +//------------------------------------------------- + +void mame_options::populate_hashpath_from_args_and_inis(emu_options &options, const std::vector &args) +{ + // The existence of this function comes from the fact that for softlist options to be properly + // evaluated, we need to have the hashpath variable set. The problem is that the hashpath may + // be set anywhere on the command line, but also in any of the myriad INI files that we parse, some + // of which may be system specific (e.g. - nes.ini) or otherwise influenced by the system (e.g. - vector.ini) + // + // I think that it is terrible that we have to do a completely independent pass on the command line and every + // argument simply because any one of these things might be setting - hashpath.Unless we invest the effort in + // building some sort of "late binding" apparatus for options(e.g. - delay evaluation of softlist options until + // we've scoured all INIs for hashpath) that can completely straddle the command line and the INI worlds, doing + // this is the best that we can do IMO. + + // parse the command line + emu_options temp_options(emu_options::option_support::GENERAL_AND_SYSTEM); + + // pick up whatever changes the osd did to the default inipath + temp_options.set_default_value(OPTION_INIPATH, options.ini_path()); + + try + { + temp_options.parse_command_line(args, OPTION_PRIORITY_CMDLINE, true); + } + catch (options_exception &) + { + // Something is very long; we have bigger problems than -hashpath possibly + // being in never-never land. Punt and let the main code fail + return; + } + + // if we have an auxillary verb, hashpath is irrelevant + if (!temp_options.command().empty()) + return; + + // read INI files + if (temp_options.read_config()) + { + std::ostringstream error_stream; + parse_standard_inis(temp_options, error_stream); + } + + // and fish out hashpath + const auto entry = temp_options.get_entry(OPTION_HASHPATH); + if (entry) + { + try + { + options.set_value(OPTION_HASHPATH, entry->value(), entry->priority()); + } + catch (options_exception &) + { + } + } +} diff --git a/src/icludes/frontend/mame/mameopts.h b/src/icludes/frontend/mame/mameopts.h new file mode 100644 index 0000000..2b178fb --- /dev/null +++ b/src/icludes/frontend/mame/mameopts.h @@ -0,0 +1,64 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + mameopts.h + + Options file and command line management. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_MAMEOPTS_H +#define MAME_FRONTEND_MAMEOPTS_H + +#pragma once + +#include "emuopts.h" + +//************************************************************************** +// CONSTANTS +//************************************************************************** +#undef OPTION_PRIORITY_CMDLINE + +// option priorities +enum +{ + // command-line options are HIGH priority + OPTION_PRIORITY_SUBCMD = OPTION_PRIORITY_HIGH, + OPTION_PRIORITY_CMDLINE, + + // INI-based options are NORMAL priority, in increasing order: + OPTION_PRIORITY_MAME_INI = OPTION_PRIORITY_NORMAL + 1, + OPTION_PRIORITY_DEBUG_INI, + OPTION_PRIORITY_ORIENTATION_INI, + OPTION_PRIORITY_SYSTYPE_INI, + OPTION_PRIORITY_SCREEN_INI, + OPTION_PRIORITY_SOURCE_INI, + OPTION_PRIORITY_GPARENT_INI, + OPTION_PRIORITY_PARENT_INI, + OPTION_PRIORITY_DRIVER_INI, + OPTION_PRIORITY_INI, +}; + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// forward references +class game_driver; +class software_part; + +class mame_options +{ +public: + // parsing wrappers + static void parse_standard_inis(emu_options &options, std::ostream &error_stream, const game_driver *driver = nullptr); + static const game_driver *system(const emu_options &options); + static void populate_hashpath_from_args_and_inis(emu_options &options, const std::vector &args); + +private: + // INI parsing helper + static void parse_one_ini(emu_options &options, const char *basename, int priority, std::ostream *error_stream = nullptr); +}; + +#endif // MAME_FRONTEND_MAMEOPTS_H diff --git a/src/icludes/frontend/mame/media_ident.cpp b/src/icludes/frontend/mame/media_ident.cpp new file mode 100644 index 0000000..b1a18ed --- /dev/null +++ b/src/icludes/frontend/mame/media_ident.cpp @@ -0,0 +1,436 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + media_ident.c + + Media identify. + +***************************************************************************/ + +#include "emu.h" +#include "drivenum.h" +#include "media_ident.h" +#include "softlist_dev.h" + +#include "jedparse.h" +#include "unzip.h" + + +//************************************************************************** +// MEDIA IDENTIFIER +//************************************************************************** + +void media_identifier::file_info::match( + device_t const &device, + romload::file const &rom, + util::hash_collection const &hashes) +{ + if (hashes == m_hashes) + { + m_matches.emplace_back( + device.shortname(), + device.name(), + rom.get_name(), + hashes.flag(util::hash_collection::FLAG_BAD_DUMP), + device.owner()); + } +} + +void media_identifier::file_info::match( + std::string const &list, + software_info const &software, + rom_entry const &rom, + util::hash_collection const &hashes) +{ + if (hashes == m_hashes) + { + m_matches.emplace_back( + util::string_format("%s:%s", list, software.shortname()), + std::string(software.longname()), + std::string(rom.name()), + hashes.flag(util::hash_collection::FLAG_BAD_DUMP), + false); + } +} + + +//------------------------------------------------- +// media_identifier - constructor +//------------------------------------------------- + +media_identifier::media_identifier(emu_options &options) + : m_drivlist(options) + , m_total(0) + , m_matches(0) + , m_nonroms(0) +{ +} + + +//------------------------------------------------- +// identify - identify a directory, ZIP file, +// or raw file +//------------------------------------------------- + +void media_identifier::identify(const char *filename) +{ + std::vector info; + collect_files(info, filename); + match_hashes(info); + print_results(info); +} + + +//------------------------------------------------- +// identify_file - identify a file +//------------------------------------------------- + +void media_identifier::identify_file(const char *name) +{ + std::vector info; + digest_file(info, name); + match_hashes(info); + print_results(info); +} + + +//------------------------------------------------- +// identify_data - identify a buffer full of +// data; if it comes from a .JED file, parse the +// fusemap into raw data first +//------------------------------------------------- + +void media_identifier::identify_data(const char *name, const uint8_t *data, std::size_t length) +{ + assert(data != nullptr && length != 0); + + std::vector info; + digest_data(info, name, data, length); + match_hashes(info); + print_results(info); +} + + +//------------------------------------------------- +// collect_files - pre-process files for +// identification +//------------------------------------------------- + +void media_identifier::collect_files(std::vector &info, char const *path) +{ + // first try to open as a directory + osd::directory::ptr const directory = osd::directory::open(path); + if (directory) + { + // iterate over all files in the directory + for (osd::directory::entry const *entry = directory->read(); entry; entry = directory->read()) + { + if (entry->type == osd::directory::entry::entry_type::FILE) + { + std::string const curfile = std::string(path).append(PATH_SEPARATOR).append(entry->name); + collect_files(info, curfile.c_str()); + } + } + } + else if (core_filename_ends_with(path, ".7z") || core_filename_ends_with(path, ".zip")) + { + // first attempt to examine it as a valid zip/7z file + util::archive_file::ptr archive; + std::error_condition err; + if (core_filename_ends_with(path, ".7z")) + err = util::archive_file::open_7z(path, archive); + else + err = util::archive_file::open_zip(path, archive); + + if (!err && archive) + { + std::vector data; + + // loop over entries in the .7z, skipping empty files and directories + for (int i = archive->first_file(); i >= 0; i = archive->next_file()) + { + std::uint64_t const length(archive->current_uncompressed_length()); + if (!archive->current_is_directory() && length) + { + std::string const curfile = std::string(path).append(PATH_SEPARATOR).append(archive->current_name()); + if (std::uint32_t(length) == length) + { + // decompress data into RAM and identify it + try + { + data.resize(std::size_t(length)); + err = archive->decompress(&data[0], std::uint32_t(length)); + if (!err) + digest_data(info, curfile.c_str(), &data[0], length); + else + osd_printf_error("%s: error decompressing file (%s:%d %s)\n", curfile, err.category().name(), err.value(), err.message()); + } + catch (...) + { + // resizing the buffer could cause a bad_alloc if archive contains large files + osd_printf_error("%s: error decompressing file\n", curfile); + } + data.clear(); + } + else + { + osd_printf_error("%s: file too large to decompress into memory\n", curfile); + } + } + } + } + else + { + osd_printf_error("%s: error opening archive\n", path); + } + + // clear out any cached files + util::archive_file::cache_clear(); + } + else + { + // otherwise, identify as a raw file + digest_file(info, path); + } +} + + +//------------------------------------------------- +// digest_file - calculate hashes for a single +// file +//------------------------------------------------- + +void media_identifier::digest_file(std::vector &info, char const *path) +{ + // CHD files need to be parsed and their hashes extracted from the header + if (core_filename_ends_with(path, ".chd")) + { + // attempt to open as a CHD; fail if not + chd_file chd; + std::error_condition const err = chd.open(path); + m_total++; + if (err) + { + osd_printf_info("%-20s NOT A CHD\n", core_filename_extract_base(path)); + m_nonroms++; + } + else if (!chd.compressed()) + { + osd_printf_info("%-20s is a writeable CHD\n", core_filename_extract_base(path)); + } + else + { + // otherwise, get the hash collection for this CHD + util::hash_collection hashes; + if (chd.sha1() != util::sha1_t::null) + hashes.add_sha1(chd.sha1()); + info.emplace_back(path, chd.logical_bytes(), std::move(hashes), file_flavour::CHD); + } + } + else + { + // if this is a '.jed' file, process it into raw bits first + if (core_filename_ends_with(path, ".jed")) + { + // load the file and process if it opens and has a valid length + util::core_file::ptr file; + if (!util::core_file::open(path, OPEN_FLAG_READ, file)) + { + jed_data jed; + if (JEDERR_NONE == jed_parse(*file, &jed)) + { + try + { + // now determine the new data length and allocate temporary memory for it + std::vector tempjed(jedbin_output(&jed, nullptr, 0)); + jedbin_output(&jed, &tempjed[0], tempjed.size()); + util::hash_collection hashes; + hashes.compute(&tempjed[0], tempjed.size(), util::hash_collection::HASH_TYPES_CRC_SHA1); + info.emplace_back(path, tempjed.size(), std::move(hashes), file_flavour::JED); + m_total++; + return; + } + catch (...) + { + } + } + } + } + + // load the file and process if it opens and has a valid length + util::core_file::ptr file; + if (util::core_file::open(path, OPEN_FLAG_READ, file) || !file) + { + osd_printf_error("%s: error opening file\n", path); + return; + } + std::uint64_t length; + if (file->length(length)) + { + osd_printf_error("%s: error getting file length\n", path); + return; + } + util::hash_collection hashes; + hashes.begin(util::hash_collection::HASH_TYPES_CRC_SHA1); + std::uint8_t buf[1024]; + for (std::uint64_t remaining = length; remaining; ) + { + std::size_t const block = std::min(remaining, sizeof(buf)); + std::size_t actual; + if (file->read(buf, block, actual) || !actual) + { + osd_printf_error("%s: error reading file\n", path); + return; + } + remaining -= actual; + hashes.buffer(buf, actual); + } + hashes.end(); + info.emplace_back(path, length, std::move(hashes), file_flavour::RAW); + m_total++; + } +} + + +//------------------------------------------------- +// digest_data - calculate hashes for data in +// memory +//------------------------------------------------- + +void media_identifier::digest_data(std::vector &info, char const *name, void const *data, std::uint64_t length) +{ + util::hash_collection hashes; + + // if this is a '.jed' file, process it into raw bits first + if (core_filename_ends_with(name, ".jed")) + { + jed_data jed; + if (JEDERR_NONE == jed_parse(*util::ram_read(data, length), &jed)) + { + try + { + // now determine the new data length and allocate temporary memory for it + std::vector tempjed(jedbin_output(&jed, nullptr, 0)); + jedbin_output(&jed, &tempjed[0], tempjed.size()); + hashes.compute(&tempjed[0], tempjed.size(), util::hash_collection::HASH_TYPES_CRC_SHA1); + info.emplace_back(name, tempjed.size(), std::move(hashes), file_flavour::JED); + m_total++; + return; + } + catch (...) + { + } + } + } + + hashes.compute(reinterpret_cast(data), length, util::hash_collection::HASH_TYPES_CRC_SHA1); + info.emplace_back(name, length, std::move(hashes), file_flavour::RAW); + m_total++; +} + + +//------------------------------------------------- +// match_hashes - find known dumps that mach +// collected hashes +//------------------------------------------------- + +void media_identifier::match_hashes(std::vector &info) +{ + if (info.empty()) + return; + + auto match_device = + [&info, listnames = std::unordered_set()] (device_t &device) mutable + { + // iterate over regions and files within the region + for (romload::region const ®ion : romload::entries(device.rom_region()).get_regions()) + { + for (romload::file const &rom : region.get_files()) + { + util::hash_collection const romhashes(rom.get_hashdata()); + if (!romhashes.flag(util::hash_collection::FLAG_NO_DUMP)) + { + for (file_info &file : info) + file.match(device, rom, romhashes); + } + } + } + + // next iterate over softlists + for (software_list_device &swlistdev : software_list_device_enumerator(device)) + { + if (!listnames.insert(swlistdev.list_name()).second) + continue; + + for (software_info const &swinfo : swlistdev.get_info()) + { + for (software_part const &part : swinfo.parts()) + { + for (rom_entry const *region = part.romdata().data(); region; region = rom_next_region(region)) + { + for (rom_entry const *rom = rom_first_file(region); rom; rom = rom_next_file(rom)) + { + util::hash_collection romhashes(rom->hashdata()); + if (!romhashes.flag(util::hash_collection::FLAG_NO_DUMP)) + { + for (file_info &file : info) + file.match(swlistdev.list_name(), swinfo, *rom, romhashes); + } + } + } + } + } + } + }; + + // iterate over drivers + m_drivlist.reset(); + while (m_drivlist.next()) + match_device(m_drivlist.config()->root_device()); + + // iterator over registered device types + machine_config config(GAME_NAME(___empty), m_drivlist.options()); + machine_config::token const tok(config.begin_configuration(config.root_device())); + for (device_type type : registered_device_types) + { + match_device(*config.device_add("_tmp", type, 0)); + config.device_remove("_tmp"); + } +} + + +//------------------------------------------------- +// print_results - print info on files that were +// found to match known dumps +//------------------------------------------------- + +void media_identifier::print_results(std::vector const &info) +{ + for (file_info const &file : info) + { + osd_printf_info("%-20s ", core_filename_extract_base(file.name())); + if (file.matches().empty()) + { + osd_printf_info("NO MATCH\n"); + } + else + { + bool first = true; + m_matches++; + for (match_data const &match : file.matches()) + { + if (!first) + osd_printf_info("%-20s ", ""); + first = false; + osd_printf_info( + "= %s%-20s %-10s %s%s\n", + match.bad() ? "(BAD) " : "", + match.romname().c_str(), + match.shortname().c_str(), + match.fullname().c_str(), + match.device() ? " (device)" : ""); + } + } + } +} diff --git a/src/icludes/frontend/mame/media_ident.h b/src/icludes/frontend/mame/media_ident.h new file mode 100644 index 0000000..dbdeec1 --- /dev/null +++ b/src/icludes/frontend/mame/media_ident.h @@ -0,0 +1,132 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + media_ident.h + + Media identify. + +***************************************************************************/ +#ifndef MAME_FRONTEND_MEDIA_IDENT_H +#define MAME_FRONTEND_MEDIA_IDENT_H + +#include "drivenum.h" +#include "romload.h" + +#include + + +// media_identifier class identifies media by hash via a search in +// the driver database +class media_identifier +{ +public: + // construction/destruction + media_identifier(emu_options &options); + + // getters + unsigned total() const { return m_total; } + unsigned matches() const { return m_matches; } + unsigned nonroms() const { return m_nonroms; } + + // operations + void reset() { m_total = m_matches = m_nonroms = 0; } + void identify(const char *name); + void identify_file(const char *name); + void identify_data(const char *name, const uint8_t *data, std::size_t length); + +private: + enum class file_flavour + { + RAW, + JED, + CHD + }; + + class match_data + { + public: + match_data( + std::string &&shortname, + std::string &&fullname, + std::string &&romname, + bool bad, + bool device) + : m_shortname(std::move(shortname)) + , m_fullname(std::move(fullname)) + , m_romname(std::move(romname)) + , m_bad(bad) + , m_device(device) + { + } + + match_data(match_data const &) = default; + match_data(match_data &&) = default; + match_data &operator=(match_data const &) = default; + match_data &operator=(match_data &&) = default; + + std::string const &shortname() const { return m_shortname; } + std::string const &fullname() const { return m_fullname; } + std::string const &romname() const { return m_romname; } + bool bad() const { return m_bad; } + bool device() const { return m_device; } + + private: + std::string m_shortname; + std::string m_fullname; + std::string m_romname; + bool m_bad; + bool m_device; + }; + + class file_info + { + public: + file_info( + std::string &&name, + std::uint64_t length, + util::hash_collection &&hashes, + file_flavour flavour) + : m_name(std::move(name)) + , m_length(length) + , m_hashes(std::move(hashes)) + , m_flavour(flavour) + { + } + + file_info(file_info const &) = default; + file_info(file_info &&) = default; + file_info &operator=(file_info const &) = default; + file_info &operator=(file_info &&) = default; + + std::string const &name() const { return m_name; } + std::uint64_t length() const { return m_length; } + util::hash_collection const &hashes() const { return m_hashes; } + file_flavour flavour() const { return m_flavour; } + std::vector const &matches() const { return m_matches; } + + void match(device_t const &device, romload::file const &rom, util::hash_collection const &hashes); + void match(std::string const &list, software_info const &software, rom_entry const &rom, util::hash_collection const &hashes); + + private: + std::string m_name; + std::uint64_t m_length; + util::hash_collection m_hashes; + file_flavour m_flavour; + std::vector m_matches; + }; + + void collect_files(std::vector &info, char const *path); + void digest_file(std::vector &info, char const *path); + void digest_data(std::vector &info, char const *name, void const *data, std::uint64_t length); + void match_hashes(std::vector &info); + void print_results(std::vector const &info); + + driver_enumerator m_drivlist; + unsigned m_total; + unsigned m_matches; + unsigned m_nonroms; +}; + + +#endif /* MAME_FRONTEND_MEDIA_IDENT_H */ diff --git a/src/icludes/frontend/mame/pluginopts.cpp b/src/icludes/frontend/mame/pluginopts.cpp new file mode 100644 index 0000000..51fa39c --- /dev/null +++ b/src/icludes/frontend/mame/pluginopts.cpp @@ -0,0 +1,180 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic +/*************************************************************************** + + pluginopts.cpp + + Plugin options manager. + +***************************************************************************/ + +#include "emu.h" +#include "pluginopts.h" +#include "options.h" + +#include +#include +#include + +#include + + +//************************************************************************** +// PLUGIN OPTIONS +//************************************************************************** + +//------------------------------------------------- +// plugin_options - constructor +//------------------------------------------------- + +plugin_options::plugin_options() +{ +} + + +//------------------------------------------------- +// scan_directory +//------------------------------------------------- + +void plugin_options::scan_directory(const std::string &path, bool recursive) +{ + // first try to open as a directory + osd::directory::ptr directory = osd::directory::open(path); + if (directory) + { + // iterate over all files in the directory + for (const osd::directory::entry *entry = directory->read(); entry != nullptr; entry = directory->read()) + { + if (entry->type == osd::directory::entry::entry_type::FILE && !strcmp(entry->name, "plugin.json")) + { + std::string curfile = std::string(path).append(PATH_SEPARATOR).append(entry->name); + load_plugin(curfile); + } + else if (entry->type == osd::directory::entry::entry_type::DIR) + { + if (recursive && strcmp(entry->name, ".") && strcmp(entry->name, "..")) + scan_directory(path + PATH_SEPARATOR + entry->name, recursive); + } + } + } +} + + +//------------------------------------------------- +// load_plugin +//------------------------------------------------- + +bool plugin_options::load_plugin(const std::string &path) +{ + std::ifstream ifs(path); + rapidjson::IStreamWrapper isw(ifs); + rapidjson::Document document; + document.ParseStream<0>(isw); + + if (document.HasParseError()) + { + const std::string error(GetParseError_En(document.GetParseError())); + osd_printf_error("Unable to parse plugin definition file %s. Errors returned:\n%s", path, error); + return false; + } + + if (!document["plugin"].IsObject()) + { + osd_printf_error("Bad plugin definition file %s:\n", path); + return false; + } + + size_t last_path_sep = path.find_last_of(PATH_SEPARATOR[0]); + std::string dir = last_path_sep != std::string::npos + ? path.substr(0, last_path_sep) + : "."; + + plugin p; + p.m_name = document["plugin"]["name"].GetString(); + p.m_description = document["plugin"]["description"].GetString(); + p.m_type = document["plugin"]["type"].GetString(); + p.m_directory = std::move(dir); + p.m_start = false; + if (document["plugin"].HasMember("start") && (std::string(document["plugin"]["start"].GetString()) == "true")) + p.m_start = true; + + m_plugins.push_back(std::move(p)); + return true; +} + + +//------------------------------------------------- +// find +//------------------------------------------------- + +plugin_options::plugin *plugin_options::find(const std::string &name) +{ + auto iter = std::find_if( + m_plugins.begin(), + m_plugins.end(), + [&name](const plugin &p) { return name == p.m_name; }); + + return iter != m_plugins.end() + ? &*iter + : nullptr; +} + + +//------------------------------------------------- +// create_core_options +//------------------------------------------------- + +static core_options create_core_options(const plugin_options &plugin_opts) +{ + // we're sort of abusing core_options to just get INI file parsing, so we'll build a + // core_options structure for the sole purpose of parsing an INI file, and then reflect + // the data back + static const options_entry s_option_entries[] = + { + { nullptr, nullptr, OPTION_HEADER, "PLUGINS OPTIONS" }, + { nullptr } + }; + + core_options opts; + opts.add_entries(s_option_entries); + + // create an entry for each option + for (const plugin_options::plugin &p : plugin_opts.plugins()) + { + opts.add_entry( + { p.m_name }, + nullptr, + core_options::option_type::BOOLEAN, + p.m_start ? "1" : "0"); + } + + return opts; +} + + +//------------------------------------------------- +// parse_ini_file +//------------------------------------------------- + +void plugin_options::parse_ini_file(util::core_file &inifile) +{ + core_options opts = create_core_options(*this); + + // parse the INI file + opts.parse_ini_file(inifile, OPTION_PRIORITY_NORMAL, true, true); + + // and reflect these options back + for (plugin &p : m_plugins) + p.m_start = opts.bool_value(p.m_name); +} + + +//------------------------------------------------- +// output_ini +//------------------------------------------------- + +std::string plugin_options::output_ini() const +{ + core_options opts = create_core_options(*this); + return opts.output_ini(); +} diff --git a/src/icludes/frontend/mame/pluginopts.h b/src/icludes/frontend/mame/pluginopts.h new file mode 100644 index 0000000..41c688b --- /dev/null +++ b/src/icludes/frontend/mame/pluginopts.h @@ -0,0 +1,53 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic +/*************************************************************************** + + pluginopts.cpp + + Plugin options manager. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_PLUGINOPTS_H +#define MAME_FRONTEND_PLUGINOPTS_H + +#pragma once + +#include +#include + + +// ======================> plugin_options + +class plugin_options +{ +public: + struct plugin + { + std::string m_name; + std::string m_description; + std::string m_type; + std::string m_directory; + bool m_start; + }; + + plugin_options(); + + // accessors + std::list &plugins() { return m_plugins; } + const std::list &plugins() const { return m_plugins; } + + // methods + void scan_directory(const std::string &path, bool recursive); + bool load_plugin(const std::string &path); + plugin *find(const std::string &name); + + // INI functionality + void parse_ini_file(util::core_file &inifile); + std::string output_ini() const; + +private: + std::list m_plugins; +}; + +#endif // MAME_FRONTEND_PLUGINOPTS_H diff --git a/src/icludes/frontend/mame/ui/about.cpp b/src/icludes/frontend/mame/ui/about.cpp new file mode 100644 index 0000000..05ba893 --- /dev/null +++ b/src/icludes/frontend/mame/ui/about.cpp @@ -0,0 +1,123 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ui/about.cpp + + About box + +***************************************************************************/ + +#include "emu.h" +#include "ui/about.h" + +#include "ui/ui.h" + +#include "mame.h" + + +namespace ui { + +namespace { + +#include "copying.ipp" + +} // anonymous namespace + + +/************************************************** + ABOUT BOX +**************************************************/ + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_about::menu_about(mame_ui_manager &mui, render_container &container) + : menu_textbox(mui, container) + , m_header{ + util::string_format( +#ifdef MAME_DEBUG + _("about-header", "%1$s %2$s (%3$s%4$sP%5$s, debug)"), +#else + _("about-header", "%1$s %2$s (%3$s%4$sP%5$s)"), +#endif + emulator_info::get_appname(), + bare_build_version, + (sizeof(int) == sizeof(void *)) ? "I" : "", + (sizeof(long) == sizeof(void *)) ? "L" : (sizeof(long long) == sizeof(void *)) ? "LL" : "", + sizeof(void *) * 8), + util::string_format(_("about-header", "Revision: %1$s"), bare_vcs_revision) } +{ + set_process_flags(PROCESS_CUSTOM_NAV); +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_about::~menu_about() +{ +} + + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_about::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + // draw the title + draw_text_box( + std::begin(m_header), std::end(m_header), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + + +//------------------------------------------------- +// populate_text - populate the about box text +//------------------------------------------------- + +void menu_about::populate_text(std::optional &layout, float &width, int &lines) +{ + if (!layout || (layout->width() != width)) + { + rgb_t const color = ui().colors().text_color(); + layout.emplace(ui().create_layout(container(), width)); + for (char const *const *line = copying_text; *line; ++line) + { + layout->add_text(*line, color); + layout->add_text("\n", color); + } + lines = layout->lines(); + } + width = layout->actual_width(); +} + + +//------------------------------------------------- +// populate - populates the about modal +//------------------------------------------------- + +void menu_about::populate(float &customtop, float &custombottom) +{ + // make space for the title and revision + customtop = (ui().get_line_height() * m_header.size()) + (ui().box_tb_border() * 3.0f); +} + + +//------------------------------------------------- +// handle - manages inputs in the about modal +//------------------------------------------------- + +void menu_about::handle(event const *ev) +{ + if (ev) + handle_key(ev->iptkey); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/about.h b/src/icludes/frontend/mame/ui/about.h new file mode 100644 index 0000000..13fc8e3 --- /dev/null +++ b/src/icludes/frontend/mame/ui/about.h @@ -0,0 +1,45 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ui/about.h + + About box + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_ABOUT_H +#define MAME_FRONTEND_UI_ABOUT_H + +#pragma once + +#include "ui/text.h" +#include "ui/textbox.h" + +#include +#include +#include + + +namespace ui { + +class menu_about : public menu_textbox +{ +public: + menu_about(mame_ui_manager &mui, render_container &container); + virtual ~menu_about() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + + virtual void populate_text(std::optional &layout, float &width, int &lines) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + std::vector const m_header; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_ABOUT_H diff --git a/src/icludes/frontend/mame/ui/analogipt.cpp b/src/icludes/frontend/mame/ui/analogipt.cpp new file mode 100644 index 0000000..6dfe416 --- /dev/null +++ b/src/icludes/frontend/mame/ui/analogipt.cpp @@ -0,0 +1,478 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Vas Crabb +/********************************************************************* + + ui/analogipt.cpp + + Analog inputs menu. + +*********************************************************************/ + +#include "emu.h" +#include "ui/analogipt.h" + +#include +#include +#include +#include + + +namespace ui { + +inline menu_analog::item_data::item_data(ioport_field &f, int t) noexcept + : field(f) + , type(t) + , defvalue( + (ANALOG_ITEM_KEYSPEED == t) ? f.delta() : + (ANALOG_ITEM_CENTERSPEED == t) ? f.centerdelta() : + (ANALOG_ITEM_REVERSE == t) ? f.analog_reverse() : + (ANALOG_ITEM_SENSITIVITY == t) ? f.sensitivity() : + -1) + , min((ANALOG_ITEM_SENSITIVITY == t) ? 1 : 0) + , max((ANALOG_ITEM_REVERSE == t) ? 1 : std::max(defvalue * 4, 255)) + , cur(-1) +{ +} + + +inline menu_analog::field_data::field_data(ioport_field &f) noexcept + : field(f) + , range(f.maxval() - f.minval()) + , neutral(float(f.analog_reverse() ? (f.maxval() - f.defvalue()) : (f.defvalue() - f.minval())) / range) + , origin((f.analog_wraps() && (f.defvalue() != f.minval()) && (f.defvalue() != f.maxval())) ? 0.0f : neutral) + , shift(0U) + , show_neutral((f.defvalue() != f.minval()) && (f.defvalue() != f.maxval())) +{ + for (ioport_value m = f.mask(); m && !BIT(m, 0); m >>= 1, ++shift) { } +} + + +menu_analog::menu_analog(mame_ui_manager &mui, render_container &container) + : menu(mui, container) + , m_item_data() + , m_field_data() + , m_prompt() + , m_visible_fields(0U) + , m_top_field(0) + , m_hide_menu(false) +{ + set_process_flags(PROCESS_LR_REPEAT); +} + + +menu_analog::~menu_analog() +{ +} + + +void menu_analog::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) +{ + // work out how much space to use for field names + float const aspect(machine().render().ui_aspect(&container())); + float const extrawidth(0.4f + (((ui().box_lr_border() * 2.0f) + ui().get_line_height()) * aspect)); + float const nameavail(1.0f - (ui().box_lr_border() * 2.0f * aspect) - extrawidth); + float namewidth(0.0f); + for (field_data &data : m_field_data) + namewidth = (std::min)((std::max)(ui().get_string_width(data.field.get().name()), namewidth), nameavail); + + // make a box or two + rgb_t const fgcolor(ui().colors().text_color()); + float const lineheight(ui().get_line_height()); + float const border(ui().box_tb_border()); + float const boxleft((1.0f - namewidth - extrawidth) / 2.0f); + float const boxright(boxleft + namewidth + extrawidth); + float boxtop; + float boxbottom; + float firstliney; + int visible_fields; + if (m_hide_menu) + { + if (m_prompt.empty()) + m_prompt = util::string_format(_("Press %s to show menu"), ui().get_general_input_setting(IPT_UI_ON_SCREEN_DISPLAY)); + draw_text_box( + &m_prompt, &m_prompt + 1, + boxleft, boxright, y - top, y - top + lineheight + (border * 2.0f), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + fgcolor, ui().colors().background_color(), 1.0f); + boxtop = y - top + lineheight + (border * 3.0f); + firstliney = y - top + lineheight + (border * 4.0f); + visible_fields = std::min(m_field_data.size(), int((y2 + bottom - border - firstliney) / lineheight)); + boxbottom = firstliney + (lineheight * visible_fields) + border; + } + else + { + boxtop = y2 + border; + boxbottom = y2 + bottom; + firstliney = y2 + (border * 2.0f); + visible_fields = m_visible_fields; + } + ui().draw_outlined_box(container(), boxleft, boxtop, boxright, boxbottom, ui().colors().background_color()); + + // force the field being configured to be visible + ioport_field *const selfield(selectedref ? &reinterpret_cast(selectedref)->field.get() : nullptr); + if (!m_hide_menu && selfield) + { + auto const found( + std::find_if( + m_field_data.begin(), + m_field_data.end(), + [selfield] (field_data const &d) { return &d.field.get() == selfield; })); + if (m_field_data.end() != found) + { + auto const i(std::distance(m_field_data.begin(), found)); + if (m_top_field > i) + m_top_field = i; + if ((m_top_field + visible_fields) <= i) + m_top_field = i - m_visible_fields + 1; + } + } + if (0 > m_top_field) + m_top_field = 0; + if ((m_top_field + visible_fields) > m_field_data.size()) + m_top_field = m_field_data.size() - visible_fields; + + // show live fields + namewidth += lineheight * aspect; + float const nameleft(boxleft + (ui().box_lr_border() * aspect)); + float const indleft(nameleft + namewidth); + float const indright(indleft + 0.4f); + for (unsigned line = 0; visible_fields > line; ++line) + { + // draw arrows if scrolling is possible and menu is hidden + float const liney(firstliney + (lineheight * line)); + if (m_hide_menu) + { + bool const uparrow(!line && m_top_field); + bool const downarrow(((visible_fields - 1) == line) && ((m_field_data.size() - 1) > (line + m_top_field))); + if (uparrow || downarrow) + { + float const arrowwidth = lineheight * aspect; + draw_arrow( + 0.5f * (nameleft + indright - arrowwidth), liney + (0.25f * lineheight), + 0.5f * (nameleft + indright + arrowwidth), liney + (0.75f * lineheight), + fgcolor, line ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0); + continue; + } + } + + // draw the field name, using the selected colour if it's being configured + field_data &data(m_field_data[line + m_top_field]); + bool const selected(&data.field.get() == selfield); + rgb_t const fieldcolor(selected ? ui().colors().selected_color() : fgcolor); + ui().draw_text_full( + container(), + data.field.get().name(), + nameleft, liney, namewidth, + text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, fieldcolor, ui().colors().text_bg_color()); + + ioport_value cur(0U); + data.field.get().live().analog->read(cur); + cur = (cur >> data.shift) - data.field.get().minval(); + float fill(float(cur) / data.range); + if (data.field.get().analog_reverse()) + fill = 1.0f - fill; + + float const indtop(liney + (lineheight * 0.2f)); + float const indbottom(liney + (lineheight * 0.8f)); + if (data.origin > fill) + container().add_rect(indleft + (fill * 0.4f), indtop, indleft + (data.origin * 0.4f), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + else + container().add_rect(indleft + (data.origin * 0.4f), indtop, indleft + (fill * 0.4f), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_line(indleft, indtop, indright, indtop, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_line(indright, indtop, indright, indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_line(indright, indbottom, indleft, indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_line(indleft, indbottom, indleft, indtop, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + if (data.show_neutral) + container().add_line(indleft + (data.neutral * 0.4f), indtop, indleft + (data.neutral * 0.4f), indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } +} + + +void menu_analog::menu_activated() +{ + // scripts could have changed something in the mean time + reset(reset_options::REMEMBER_POSITION); +} + + +void menu_analog::handle(event const *ev) +{ + // handle events + if (ev) + { + if (IPT_UI_ON_SCREEN_DISPLAY == ev->iptkey) + { + m_hide_menu = !m_hide_menu; + set_process_flags(PROCESS_LR_REPEAT | (m_hide_menu ? (PROCESS_CUSTOM_NAV | PROCESS_CUSTOM_ONLY) : 0)); + } + else if (m_hide_menu) + { + switch (ev->iptkey) + { + case IPT_UI_UP: + --m_top_field; + break; + case IPT_UI_DOWN: + ++m_top_field; + break; + case IPT_UI_HOME: + m_top_field = 0; + break; + case IPT_UI_END: + m_top_field = m_field_data.size(); + break; + } + } + else if (ev->itemref) + { + item_data &data(*reinterpret_cast(ev->itemref)); + int newval(data.cur); + + switch (ev->iptkey) + { + // if selected, reset to default value + case IPT_UI_SELECT: + case IPT_UI_CLEAR: + newval = data.defvalue; + break; + + // left decrements + case IPT_UI_LEFT: + newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; + break; + + // right increments + case IPT_UI_RIGHT: + newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; + break; + + // move to first item for previous device + case IPT_UI_PREV_GROUP: + { + auto current = std::distance(m_item_data.data(), &data); + device_t const *dev(&data.field.get().device()); + bool found_break = false; + while (0 < current) + { + if (!found_break) + { + device_t const *prev(&m_item_data[--current].field.get().device()); + if (prev != dev) + { + dev = prev; + found_break = true; + } + } + else if (&m_item_data[current - 1].field.get().device() != dev) + { + set_selection(&m_item_data[current]); + set_top_line(selected_index() - 1); + break; + } + else + { + --current; + } + if (found_break && !current) + { + set_selection(&m_item_data[current]); + set_top_line(selected_index() - 1); + break; + } + } + } + break; + + // move to first item for next device + case IPT_UI_NEXT_GROUP: + { + auto current = std::distance(m_item_data.data(), &data); + device_t const *const dev(&data.field.get().device()); + while (m_item_data.size() > ++current) + { + if (&m_item_data[current].field.get().device() != dev) + { + set_selection(&m_item_data[current]); + set_top_line(selected_index() - 1); + break; + } + } + } + break; + } + + // clamp to range + newval = std::clamp(newval, data.min, data.max); + + // if things changed, update + if (newval != data.cur) + { + ioport_field::user_settings settings; + + // get the settings and set the new value + data.field.get().get_user_settings(settings); + switch (data.type) + { + case ANALOG_ITEM_KEYSPEED: settings.delta = newval; break; + case ANALOG_ITEM_CENTERSPEED: settings.centerdelta = newval; break; + case ANALOG_ITEM_REVERSE: settings.reverse = newval; break; + case ANALOG_ITEM_SENSITIVITY: settings.sensitivity = newval; break; + } + data.field.get().set_user_settings(settings); + data.cur = newval; + + // update the menu item + ev->item->set_subtext(item_text(data.type, newval)); + ev->item->set_flags((data.cur <= data.min) ? FLAG_RIGHT_ARROW : (data.cur >= data.max) ? FLAG_LEFT_ARROW : FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW); + } + } + } +} + + +void menu_analog::populate(float &customtop, float &custombottom) +{ + // loop over input ports + if (m_item_data.empty()) + find_fields(); + + device_t *prev_owner(nullptr); + ioport_field *field(nullptr); + ioport_field::user_settings settings; + + // add the items + std::string text; + for (item_data &data : m_item_data) + { + // get the user settings + if (&data.field.get() != field) + { + field = &data.field.get(); + field->get_user_settings(settings); + + if (&field->device() != prev_owner) + { + prev_owner = &field->device(); + if (prev_owner->owner()) + item_append(string_format(_("%1$s [root%2$s]"), prev_owner->type().fullname(), prev_owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + else + item_append(string_format(_("[root%1$s]"), prev_owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + } + } + + // determine the properties of this item + switch (data.type) + { + default: + case ANALOG_ITEM_KEYSPEED: + text = string_format(_("%1$s Increment/Decrement Speed"), field->name()); + data.cur = settings.delta; + break; + + case ANALOG_ITEM_CENTERSPEED: + text = string_format(_("%1$s Auto-centering Speed"), field->name()); + data.cur = settings.centerdelta; + break; + + case ANALOG_ITEM_REVERSE: + text = string_format(_("%1$s Reverse"), field->name()); + data.cur = settings.reverse; + break; + + case ANALOG_ITEM_SENSITIVITY: + text = string_format(_("%1$s Sensitivity"), field->name()); + data.cur = settings.sensitivity; + break; + } + + // append a menu item + item_append( + std::move(text), + item_text(data.type, data.cur), + (data.cur <= data.min) ? FLAG_RIGHT_ARROW : (data.cur >= data.max) ? FLAG_LEFT_ARROW : FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, + &data); + } + + item_append(menu_item_type::SEPARATOR); + + // space for live display + custombottom = (ui().get_line_height() * m_visible_fields) + (ui().box_tb_border() * 3.0f); +} + + +void menu_analog::find_fields() +{ + assert(m_field_data.empty()); + + // collect analog fields + for (auto &port : machine().ioport().ports()) + { + for (ioport_field &field : port.second->fields()) + { + if (field.is_analog() && field.enabled()) + { + // based on the type, determine if we enable autocenter + bool use_autocenter = false; + switch (field.type()) + { + case IPT_POSITIONAL: + case IPT_POSITIONAL_V: + use_autocenter = !field.analog_wraps(); + break; + + case IPT_AD_STICK_X: + case IPT_AD_STICK_Y: + case IPT_AD_STICK_Z: + case IPT_PADDLE: + case IPT_PADDLE_V: + case IPT_PEDAL: + case IPT_PEDAL2: + case IPT_PEDAL3: + use_autocenter = true; + break; + + default: + break; + } + + // iterate over types + for (int type = 0; type < ANALOG_ITEM_COUNT; type++) + { + if ((ANALOG_ITEM_CENTERSPEED != type) || use_autocenter) + m_item_data.emplace_back(field, type); + } + + m_field_data.emplace_back(field); + } + } + } + + // restrict live display to 40% height plus borders + if ((ui().get_line_height() * m_field_data.size()) > 0.4f) + m_visible_fields = unsigned(0.4f / ui().get_line_height()); + else + m_visible_fields = m_field_data.size(); +} + + +std::string menu_analog::item_text(int type, int value) +{ + switch (type) + { + default: + case ANALOG_ITEM_KEYSPEED: + return string_format("%d", value); + + case ANALOG_ITEM_CENTERSPEED: + return string_format("%d", value); + + case ANALOG_ITEM_REVERSE: + return value ? _("On") : _("Off"); + + case ANALOG_ITEM_SENSITIVITY: + return string_format("%d", value); + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/analogipt.h b/src/icludes/frontend/mame/ui/analogipt.h new file mode 100644 index 0000000..f680794 --- /dev/null +++ b/src/icludes/frontend/mame/ui/analogipt.h @@ -0,0 +1,86 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Vas Crabb +/*************************************************************************** + + ui/analogipt.h + + Analog inputs menu. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_ANALOGIPT_H +#define MAME_FRONTEND_UI_ANALOGIPT_H + +#pragma once + +#include "ui/menu.h" + +#include +#include + + +namespace ui { + +class menu_analog : public menu +{ +public: + menu_analog(mame_ui_manager &mui, render_container &container); + virtual ~menu_analog() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void menu_activated() override; + +private: + enum + { + ANALOG_ITEM_KEYSPEED = 0, + ANALOG_ITEM_CENTERSPEED, + ANALOG_ITEM_REVERSE, + ANALOG_ITEM_SENSITIVITY, + ANALOG_ITEM_COUNT + }; + + struct item_data + { + item_data(ioport_field &f, int t) noexcept; + + std::reference_wrapper field; + int type; + int defvalue; + int min, max; + int cur; + }; + + struct field_data + { + field_data(ioport_field &f) noexcept; + + std::reference_wrapper field; + float range; + float neutral; + float origin; + u8 shift; + bool show_neutral; + }; + + using item_data_vector = std::vector; + using field_data_vector = std::vector; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void find_fields(); + + static std::string item_text(int type, int value); + + item_data_vector m_item_data; + field_data_vector m_field_data; + std::string m_prompt; + unsigned m_visible_fields; + int m_top_field; + bool m_hide_menu; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_ANALOGIPT_H diff --git a/src/icludes/frontend/mame/ui/auditmenu.cpp b/src/icludes/frontend/mame/ui/auditmenu.cpp new file mode 100644 index 0000000..a5cb46f --- /dev/null +++ b/src/icludes/frontend/mame/ui/auditmenu.cpp @@ -0,0 +1,240 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/********************************************************************* + + ui/auditmenu.cpp + + Internal UI user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/auditmenu.h" + +#include "ui/systemlist.h" +#include "ui/ui.h" + +#include "audit.h" + +#include "drivenum.h" +#include "uiinput.h" + +#include "util/corestr.h" + +#include +#include +#include + + +extern const char UI_VERSION_TAG[]; + +namespace ui { + +namespace { + +void *const ITEMREF_START_FULL = reinterpret_cast(std::uintptr_t(1)); +void *const ITEMREF_START_FAST = reinterpret_cast(std::uintptr_t(2)); + +} // anonymous namespace + + +menu_audit::menu_audit(mame_ui_manager &mui, render_container &container) + : menu(mui, container) + , m_availablesorted(system_list::instance().sorted_list()) + , m_unavailable( + std::accumulate( + m_availablesorted.begin(), + m_availablesorted.end(), + std::size_t(0), + [] (std::size_t n, ui_system_info const &info) { return n + (info.available ? 0 : 1); })) + , m_future() + , m_next(0) + , m_audited(0) + , m_current(nullptr) + , m_cancel(false) + , m_phase(phase::CONFIRMATION) + , m_fast(true) +{ + std::string filename(emulator_info::get_configname()); + filename += "_avail.ini"; + m_prompt = util::string_format(_("Results will be saved to %1$s"), filename); +} + +menu_audit::~menu_audit() +{ + m_cancel.store(true); + for (auto &future : m_future) + { + if (future.valid()) + future.wait(); + } +} + + +void menu_audit::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) +{ + switch (m_phase) + { + case phase::CONFIRMATION: + if ((ITEMREF_START_FAST == selectedref) || (ITEMREF_START_FULL == selectedref)) + { + draw_text_box( + &m_prompt, &m_prompt + 1, + x, x2, y2 + ui().box_tb_border(), y2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); + } + break; + + case phase::AUDIT: + { + // there's a race here between the total audited being updated and the next driver pointer being loaded + // it doesn't matter because we redraw on every frame anyway so it sorts itself out very quickly + ui_system_info const *const system(m_current.load()); + std::size_t const audited(m_audited.load()); + std::size_t const total(m_fast ? m_unavailable : m_availablesorted.size()); + std::ostringstream text; + util::stream_format(text, + _("Auditing media for machine %2$u of %3$u...\n%1$s"), + system ? std::string_view(system->description) : std::string_view(), + (std::min)(audited + 1, total), + total); + text << '\n' << m_prompt; + ui().draw_text_box( + container(), + std::move(text).str(), + text_layout::text_justify::CENTER, + 0.5f, 0.5f, + ui().colors().background_color()); + } + break; + + case phase::CANCELLATION: + ui().draw_text_box( + container(), + util::string_format( + _("Cancel audit?\n\nPress %1$s to cancel\nPress %2$s to continue"), + ui().get_general_input_setting(IPT_UI_SELECT), + ui().get_general_input_setting(IPT_UI_CANCEL)), + text_layout::text_justify::CENTER, + 0.5f, 0.5f, + UI_RED_COLOR); + break; + } +} + + +bool menu_audit::custom_ui_cancel() +{ + return m_phase != phase::CONFIRMATION; +} + + +void menu_audit::populate(float &customtop, float &custombottom) +{ + if (m_unavailable && (m_availablesorted.size() != m_unavailable)) + item_append(util::string_format(_("Audit media for %1$u machines marked unavailable"), m_unavailable), 0, ITEMREF_START_FAST); + item_append(util::string_format(_("Audit media for all %1$u machines"), m_availablesorted.size()), 0, ITEMREF_START_FULL); + item_append(menu_item_type::SEPARATOR, 0); + custombottom = (ui().get_line_height() * 1.0f) + (ui().box_tb_border() * 3.0f); +} + +void menu_audit::handle(event const *ev) +{ + switch (m_phase) + { + case phase::CONFIRMATION: + if (ev && (IPT_UI_SELECT == ev->iptkey)) + { + if ((ITEMREF_START_FULL == ev->itemref) || (ITEMREF_START_FAST == ev->itemref)) + { + set_process_flags(PROCESS_CUSTOM_ONLY | PROCESS_NOINPUT); + m_phase = phase::AUDIT; + m_fast = ITEMREF_START_FAST == ev->itemref; + m_prompt = util::string_format(_("Press %1$s to cancel\n"), ui().get_general_input_setting(IPT_UI_CANCEL)); + m_future.resize(std::thread::hardware_concurrency()); + for (auto &future : m_future) + future = std::async(std::launch::async, [this] () { return do_audit(); }); + } + } + break; + + case phase::AUDIT: + case phase::CANCELLATION: + if ((m_next.load() >= m_availablesorted.size()) || m_cancel.load()) + { + bool done(true); + for (auto &future : m_future) + done = future.get() && done; + m_future.clear(); + if (done) + { + save_available_machines(); + reset_parent(reset_options::SELECT_FIRST); + } + stack_pop(); + } + else if (machine().ui_input().pressed(IPT_UI_CANCEL)) + { + if (phase::AUDIT == m_phase) + m_phase = phase::CANCELLATION; + else + m_phase = phase::AUDIT; + } + else if ((phase::CANCELLATION == m_phase) && machine().ui_input().pressed(IPT_UI_SELECT)) + { + m_cancel.store(true); + } + break; + } +} + +bool menu_audit::do_audit() +{ + while (true) + { + std::size_t const i(m_next.fetch_add(1)); + if (m_availablesorted.size() <= i) + return true; + + ui_system_info &info(m_availablesorted[i]); + if (!m_fast || !info.available) + { + if (m_cancel.load()) + return false; + + m_current.store(&info); + driver_enumerator enumerator(machine().options(), info.driver->name); + enumerator.next(); + media_auditor auditor(enumerator); + media_auditor::summary const summary(auditor.audit_media(AUDIT_VALIDATE_FAST)); + info.available = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED); + + // if everything looks good, include the driver + info.available = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED); + ++m_audited; + } + } +} + +void menu_audit::save_available_machines() +{ + // attempt to open the output file + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(std::string(emulator_info::get_configname()) + "_avail.ini")) + { + // generate header + file.printf("#\n%s%s\n#\n\n", UI_VERSION_TAG, emulator_info::get_bare_build_version()); + + // generate available list + for (ui_system_info const &info : m_availablesorted) + { + if (info.available) + file.printf("%s\n", info.driver->name); + } + + file.close(); + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/auditmenu.h b/src/icludes/frontend/mame/ui/auditmenu.h new file mode 100644 index 0000000..de42c33 --- /dev/null +++ b/src/icludes/frontend/mame/ui/auditmenu.h @@ -0,0 +1,58 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/*************************************************************************** + + ui/auditmenu.h + + Internal UI user interface. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_AUDITMENU_H +#define MAME_FRONTEND_UI_AUDITMENU_H + +#pragma once + +#include "ui/menu.h" +#include "ui/utils.h" + +#include +#include +#include + + +namespace ui { + +class menu_audit : public menu +{ +public: + menu_audit(mame_ui_manager &mui, render_container &container); + virtual ~menu_audit() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual bool custom_ui_cancel() override; + +private: + enum class phase { CONFIRMATION, AUDIT, CANCELLATION }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + bool do_audit(); + void save_available_machines(); + + std::string m_prompt; + std::vector > const &m_availablesorted; + std::size_t const m_unavailable; + std::vector > m_future; + std::atomic m_next; + std::atomic m_audited; + std::atomic m_current; + std::atomic m_cancel; + phase m_phase; + bool m_fast; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_AUDITMENU_H diff --git a/src/icludes/frontend/mame/ui/barcode.cpp b/src/icludes/frontend/mame/ui/barcode.cpp new file mode 100644 index 0000000..7181445 --- /dev/null +++ b/src/icludes/frontend/mame/ui/barcode.cpp @@ -0,0 +1,146 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*************************************************************************** + + ui/barcode.cpp + + "Barcode Reader" control + +***************************************************************************/ + +#include "emu.h" +#include "ui/barcode.h" + +#include "ui/ui.h" +#include "ui/utils.h" + + +namespace ui { + +// itemrefs for key menu items +#define ITEMREF_NEW_BARCODE ((void *) 0x0001) +#define ITEMREF_ENTER_BARCODE ((void *) 0x0002) +#define ITEMREF_SELECT_READER ((void *) 0x0003) + + +/************************************************** + + BARCODE READER MENU + + **************************************************/ + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_barcode_reader::menu_barcode_reader(mame_ui_manager &mui, render_container &container, barcode_reader_device *device) + : menu_device_control(mui, container, device) +{ + set_process_flags(PROCESS_LR_REPEAT); +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_barcode_reader::~menu_barcode_reader() +{ +} + +//------------------------------------------------- +// populate - populates the barcode input menu +//------------------------------------------------- + +void menu_barcode_reader::populate(float &customtop, float &custombottom) +{ + if (current_device()) + { + std::string buffer; + const char *new_barcode; + + // selected device + item_append(current_display_name(), current_display_flags(), ITEMREF_SELECT_READER); + + // append the "New Barcode" item + if (get_selection_ref() == ITEMREF_NEW_BARCODE) + { + buffer.append(m_barcode_buffer); + new_barcode = buffer.c_str(); + } + else + { + new_barcode = m_barcode_buffer.c_str(); + } + + item_append(_("New Barcode:"), new_barcode, 0, ITEMREF_NEW_BARCODE); + + // finish up the menu + item_append(_("Enter Code"), 0, ITEMREF_ENTER_BARCODE); + item_append(menu_item_type::SEPARATOR); + + customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); + } +} + + +//------------------------------------------------- +// handle - manages inputs in the barcode input menu +//------------------------------------------------- + +void menu_barcode_reader::handle(event const *ev) +{ + // process the event + if (ev) + { + // handle selections + switch (ev->iptkey) + { + case IPT_UI_LEFT: + if (ev->itemref == ITEMREF_SELECT_READER) + previous(); + break; + + case IPT_UI_RIGHT: + if (ev->itemref == ITEMREF_SELECT_READER) + next(); + break; + + case IPT_UI_SELECT: + if (ev->itemref == ITEMREF_ENTER_BARCODE) + { + std::string tmp_file(m_barcode_buffer); + //printf("code %s\n", m_barcode_buffer); + if (!current_device()->is_valid(tmp_file.length())) + ui().popup_time(5, "%s", _("Barcode length invalid!")); + else + { + current_device()->write_code(tmp_file.c_str(), tmp_file.length()); + // if sending was successful, reset char buffer + m_barcode_buffer.clear(); + reset(reset_options::REMEMBER_POSITION); + } + } + break; + + case IPT_UI_CLEAR: + if (get_selection_ref() == ITEMREF_NEW_BARCODE) + { + m_barcode_buffer.clear(); + reset(reset_options::REMEMBER_POSITION); + } + break; + + case IPT_SPECIAL: + if (get_selection_ref() == ITEMREF_NEW_BARCODE) + { + if (input_character(m_barcode_buffer, ev->unichar, uchar_is_digit)) + reset(reset_options::REMEMBER_POSITION); + } + break; + } + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/barcode.h b/src/icludes/frontend/mame/ui/barcode.h new file mode 100644 index 0000000..e171485 --- /dev/null +++ b/src/icludes/frontend/mame/ui/barcode.h @@ -0,0 +1,35 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*************************************************************************** + + ui/barcode.h + + "Barcode Reader" control + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_BARCODE_H +#define MAME_FRONTEND_UI_BARCODE_H + +#pragma once + +#include "machine/bcreader.h" +#include "ui/devctrl.h" + +namespace ui { + +class menu_barcode_reader : public menu_device_control { +public: + menu_barcode_reader(mame_ui_manager &mui, render_container &container, barcode_reader_device *device); + virtual ~menu_barcode_reader() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + std::string m_barcode_buffer; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_BARCODE_H diff --git a/src/icludes/frontend/mame/ui/cheatopt.cpp b/src/icludes/frontend/mame/ui/cheatopt.cpp new file mode 100644 index 0000000..9aae811 --- /dev/null +++ b/src/icludes/frontend/mame/ui/cheatopt.cpp @@ -0,0 +1,147 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/********************************************************************* + + ui/cheatopt.cpp + + Internal menu for the cheat interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/cheatopt.h" + +#include "ui/ui.h" + +#include "cheat.h" +#include "mame.h" + + +namespace ui { + +// itemrefs for key menu items +#define ITEMREF_CHEATS_RESET_ALL ((void *) 0x0001) +#define ITEMREF_CHEATS_RELOAD_ALL ((void *) 0x0002) +#define ITEMREF_CHEATS_FIRST_ITEM ((void *) 0x0003) + + +/*------------------------------------------------- + menu_cheat - handle the cheat menu +-------------------------------------------------*/ + +void menu_cheat::handle(event const *ev) +{ + // handle events + if (ev && ev->itemref) + { + bool changed = false; + + // clear cheat comment on any movement or keypress + machine().popmessage(); + + if ((ev->itemref == ITEMREF_CHEATS_RESET_ALL || ev->itemref == ITEMREF_CHEATS_RELOAD_ALL) && ev->iptkey == IPT_UI_SELECT) + { + // handle reset all + reset all cheats for reload all option + for (auto &curcheat : mame_machine_manager::instance()->cheat().entries()) + if (curcheat->select_default_state()) + changed = true; + } + else if (ev->itemref >= ITEMREF_CHEATS_FIRST_ITEM) + { + // handle individual cheats + cheat_entry *curcheat = reinterpret_cast(ev->itemref); + const char *string; + switch (ev->iptkey) + { + // if selected, activate a oneshot + case IPT_UI_SELECT: + changed = curcheat->activate(); + break; + + // if cleared, reset to default value + case IPT_UI_CLEAR: + changed = curcheat->select_default_state(); + break; + + // left decrements + case IPT_UI_LEFT: + changed = curcheat->select_previous_state(); + break; + + // right increments + case IPT_UI_RIGHT: + changed = curcheat->select_next_state(); + break; + + // bring up display comment if one exists + case IPT_UI_DISPLAY_COMMENT: + case IPT_UI_UP: + case IPT_UI_DOWN: + string = curcheat->comment(); + if (string && *string) + machine().popmessage(_("Cheat Comment:\n%s"), string); + break; + } + } + + // handle reload all + if (ev->itemref == ITEMREF_CHEATS_RELOAD_ALL && ev->iptkey == IPT_UI_SELECT) + { + // re-init cheat engine and thus reload cheats/cheats have already been turned off by here + mame_machine_manager::instance()->cheat().reload(); + + // display the reloaded cheats + reset(reset_options::REMEMBER_REF); + machine().popmessage(_("All cheats reloaded")); + } + + // if things changed, update + if (changed) + reset(reset_options::REMEMBER_REF); + } +} + + +/*------------------------------------------------- + menu_cheat_populate - populate the cheat menu +-------------------------------------------------*/ + +menu_cheat::menu_cheat(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ + set_process_flags(PROCESS_LR_REPEAT); +} + +void menu_cheat::populate(float &customtop, float &custombottom) +{ + /* iterate over cheats */ + std::string text; + std::string subtext; + + // add cheats + if (!mame_machine_manager::instance()->cheat().entries().empty()) { + for (auto &curcheat : mame_machine_manager::instance()->cheat().entries()) + { + uint32_t flags; + curcheat->menu_text(text, subtext, flags); + if (text == MENU_SEPARATOR_ITEM) + item_append(menu_item_type::SEPARATOR, flags); + else + item_append(text, subtext, flags, curcheat.get()); + } + + /* add a separator */ + item_append(menu_item_type::SEPARATOR); + + /* add a reset all option */ + item_append(_("Reset All"), 0, (void *)ITEMREF_CHEATS_RESET_ALL); + + /* add a reload all cheats option */ + item_append(_("Reload All"), 0, (void *)ITEMREF_CHEATS_RELOAD_ALL); + } +} + +menu_cheat::~menu_cheat() +{ +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/cheatopt.h b/src/icludes/frontend/mame/ui/cheatopt.h new file mode 100644 index 0000000..c01ec15 --- /dev/null +++ b/src/icludes/frontend/mame/ui/cheatopt.h @@ -0,0 +1,33 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/cheatopt.h + + Internal menu for the cheat interface. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_CHEATOPT_H +#define MAME_FRONTEND_UI_CHEATOPT_H + +#pragma once + +#include "ui/menu.h" + + +namespace ui { + +class menu_cheat : public menu +{ +public: + menu_cheat(mame_ui_manager &mui, render_container &container); + virtual ~menu_cheat() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_CHEATOPT_H diff --git a/src/icludes/frontend/mame/ui/confswitch.cpp b/src/icludes/frontend/mame/ui/confswitch.cpp new file mode 100644 index 0000000..aeed002 --- /dev/null +++ b/src/icludes/frontend/mame/ui/confswitch.cpp @@ -0,0 +1,514 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Vas Crabb +/********************************************************************* + + ui/confswitch.cpp + + Configuration/DIP switches menu. + +*********************************************************************/ + +#include "emu.h" +#include "ui/confswitch.h" + +#include +#include + + +namespace ui { + +namespace { + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +// DIP switch rendering parameters in terms of line height +constexpr float DIP_SWITCH_HEIGHT = 1.6f; +constexpr float DIP_SWITCH_SPACING = DIP_SWITCH_HEIGHT / 5.0f; +constexpr float SINGLE_TOGGLE_SWITCH_FIELD_WIDTH = DIP_SWITCH_HEIGHT / 2.0f; +constexpr float SINGLE_TOGGLE_SWITCH_WIDTH = SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * 0.8f; +// make the switch nub 80% of the width space and 1/2 of the switch height +constexpr float SINGLE_TOGGLE_SWITCH_HEIGHT = (DIP_SWITCH_HEIGHT / 2.0f) * 0.8f; + +} // anonymous namespace + + + +/*------------------------------------------------- + menu_confswitch +-------------------------------------------------*/ + +menu_confswitch::field_descriptor::field_descriptor(ioport_field &f) noexcept + : field(f) +{ +} + + +menu_confswitch::switch_group_descriptor::switch_group_descriptor(ioport_field const &f, ioport_diplocation const &loc) noexcept + : name(loc.name()) + , owner(f.device()) + , mask(0U) + , state(0U) +{ + std::fill(std::begin(toggles), std::end(toggles), toggle{ nullptr, 0U }); +} + + +inline bool menu_confswitch::switch_group_descriptor::matches(ioport_field const &f, ioport_diplocation const &loc) const noexcept +{ + return (&owner.get() == &f.device()) && !strcmp(loc.name(), name); +} + + +inline unsigned menu_confswitch::switch_group_descriptor::switch_count() const noexcept +{ + return (sizeof(mask) * 8) - count_leading_zeros_32(mask); +} + + +menu_confswitch::menu_confswitch(mame_ui_manager &mui, render_container &container, uint32_t type) + : menu(mui, container) + , m_fields() + , m_switch_groups() + , m_active_switch_groups(0U) + , m_type(type) +{ +} + + +menu_confswitch::~menu_confswitch() +{ +} + + +void menu_confswitch::menu_activated() +{ + // switches can have input assignments, and scripts are a thing + reset(reset_options::REMEMBER_REF); +} + + +void menu_confswitch::populate(float &customtop, float &custombottom) +{ + // locate relevant fields if necessary + if (m_fields.empty()) + find_fields(); + + // reset switch group masks + m_active_switch_groups = 0U; + for (switch_group_descriptor &group : m_switch_groups) + group.mask = group.state = 0U; + + // loop over input ports and set up the current values + device_t *prev_owner(nullptr); + for (field_descriptor &desc : m_fields) + { + ioport_field &field(desc.field); + if (field.enabled()) + { + if (!field.settings().empty()) + { + // add a device heading if necessary + if (&field.device() != prev_owner) + { + prev_owner = &field.device(); + if (prev_owner->owner()) + item_append(string_format(_("%1$s [root%2$s]"), prev_owner->type().fullname(), prev_owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + else + item_append(string_format(_("[root%1$s]"), prev_owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + } + + // set the left/right flags appropriately + uint32_t flags(0U); + if (field.has_previous_setting()) + flags |= FLAG_LEFT_ARROW; + if (field.has_next_setting()) + flags |= FLAG_RIGHT_ARROW; + + // add the menu item + item_append(field.name(), field.setting_name(), flags, &field); + } + + // track switch groups + if (!field.diplocations().empty()) + { + // get current settings + ioport_field::user_settings settings; + field.get_user_settings(settings); + + // iterate over each bit in the field + ioport_value accummask(field.mask()); + for (ioport_diplocation const &loc : field.diplocations()) + { + // find the matching switch group + switch_group_descriptor &group( + *std::find_if( + m_switch_groups.begin(), + m_switch_groups.end(), [&field, &loc] (switch_group_descriptor const &sw) { return sw.matches(field, loc); })); + + // count if this is the first switch in the group + if (!group.mask) + ++m_active_switch_groups; + + // apply the bits + ioport_value const mask(accummask & ~(accummask - 1)); + group.toggles[loc.number() - 1].field = &field; + group.toggles[loc.number() - 1].mask = mask; + group.mask |= uint32_t(1) << (loc.number() - 1); + if (((settings.value & mask) && !loc.inverted()) || (!(settings.value & mask) && loc.inverted())) + group.state |= uint32_t(1) << (loc.number() - 1); + + // clear the relevant bit in the accumulated mask + accummask &= ~mask; + } + } + } + } + + item_append(menu_item_type::SEPARATOR); + item_append(_("Reset Machine"), 0, (void *)1); +} + + +void menu_confswitch::handle(event const *ev) +{ + // handle events + if (ev && ev->itemref) + { + if (uintptr_t(ev->itemref) == 1U) + { + // reset + if (ev->iptkey == IPT_UI_SELECT) + machine().schedule_hard_reset(); + } + else + { + // actual settings + ioport_field &field(*reinterpret_cast(ev->itemref)); + bool changed(false); + + switch (ev->iptkey) + { + // if selected, reset to default value + case IPT_UI_SELECT: + { + ioport_field::user_settings settings; + field.get_user_settings(settings); + settings.value = field.defvalue(); + field.set_user_settings(settings); + } + changed = true; + break; + + // left goes to previous setting + case IPT_UI_LEFT: + field.select_previous_setting(); + changed = true; + break; + + // right goes to next setting + case IPT_UI_RIGHT: + field.select_next_setting(); + changed = true; + break; + + // trick to get previous group - depend on headings having null reference + case IPT_UI_PREV_GROUP: + { + auto current = selected_index(); + bool found_break = false; + while (0 < current) + { + if (!found_break) + { + if (!item(--current).ref()) + found_break = true; + } + else if (!item(current - 1).ref()) + { + set_selected_index(current); + set_top_line(current - 1); + break; + } + else + { + --current; + } + } + } + break; + + // trick to get next group - depend on special item references + case IPT_UI_NEXT_GROUP: + { + auto current = selected_index(); + while (item_count() > ++current) + { + if (!item(current).ref()) + { + if ((item_count() > (current + 1)) && (uintptr_t(item(current + 1).ref()) != 1)) + { + set_selected_index(current + 1); + set_top_line(current); + } + break; + } + } + } + break; + } + + // if anything changed, rebuild the menu, trying to stay on the same field + if (changed) + reset(reset_options::REMEMBER_REF); + } + } +} + + +void menu_confswitch::find_fields() +{ + assert(m_fields.empty()); + assert(m_switch_groups.empty()); + + // find relevant input ports + for (auto &port : machine().ioport().ports()) + { + for (ioport_field &field : port.second->fields()) + { + if (field.type() == m_type) + { + m_fields.emplace_back(field); + + // iterate over locations + for (ioport_diplocation const &loc : field.diplocations()) + { + auto const found( + std::find_if( + m_switch_groups.begin(), + m_switch_groups.end(), [&field, &loc] (switch_group_descriptor const &sw) { return sw.matches(field, loc); })); + if (m_switch_groups.end() == found) + m_switch_groups.emplace_back(field, loc); + } + } + } + } +} + + + +/*------------------------------------------------- + menu_settings_dip_switches +-------------------------------------------------*/ + +menu_settings_dip_switches::menu_settings_dip_switches(mame_ui_manager &mui, render_container &container) + : menu_confswitch(mui, container, IPT_DIPSWITCH) + , m_switch_group_y() + , m_visible_switch_groups(0U) + , m_single_width(0.0f) + , m_nub_width(0.0f) + , m_first_nub(0.0f) + , m_clickable_height(0.0f) +{ +} + + +menu_settings_dip_switches::~menu_settings_dip_switches() +{ +} + + +void menu_settings_dip_switches::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) +{ + // catch if no DIP locations have to be drawn + if (!m_visible_switch_groups) + return; + + // calculate optimal width + float const aspect(machine().render().ui_aspect(&container())); + float const lineheight(ui().get_line_height()); + float const maxwidth(1.0f - (ui().box_lr_border() * 2.0f * aspect)); + m_single_width = (lineheight * SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * aspect); + float width(0.0f); + unsigned maxswitches(0U); + for (switch_group_descriptor const &group : switch_groups()) + { + if (group.mask) + { + maxswitches = (std::max)(group.switch_count(), maxswitches); + float const namewidth(ui().get_string_width(group.name)); + float const switchwidth(m_single_width * maxswitches); + width = (std::min)((std::max)(namewidth + switchwidth + (lineheight * aspect), width), maxwidth); + } + } + + // draw extra menu area + float const boxwidth((std::max)(width + (ui().box_lr_border() * 2.0f * aspect), x2 - x1)); + float const boxleft((1.0f - boxwidth) * 0.5f); + ui().draw_outlined_box(container(), boxleft, y2 + ui().box_tb_border(), boxleft + boxwidth, y2 + bottom, ui().colors().background_color()); + + // calculate centred layout + float const nameleft((1.0f - width) * 0.5f); + float const switchleft(nameleft + width - (m_single_width * maxswitches)); + float const namewidth(width - (m_single_width * maxswitches) - (lineheight * aspect)); + + // iterate over switch groups + ioport_field *const field((uintptr_t(selectedref) != 1U) ? reinterpret_cast(selectedref) : nullptr); + float const nubheight(lineheight * SINGLE_TOGGLE_SWITCH_HEIGHT); + m_nub_width = lineheight * SINGLE_TOGGLE_SWITCH_WIDTH * aspect; + float const ygap(lineheight * ((DIP_SWITCH_HEIGHT * 0.5f) - SINGLE_TOGGLE_SWITCH_HEIGHT) * 0.5f); + float const xgap((m_single_width + (UI_LINE_WIDTH * 0.5f) - m_nub_width) * 0.5f); + m_first_nub = switchleft + xgap; + m_clickable_height = (lineheight * DIP_SWITCH_HEIGHT) - (ygap * 2.0f); + unsigned line(0U); + for (unsigned n = 0; switch_groups().size() > n; ++n) + { + switch_group_descriptor const &group(switch_groups()[n]); + if (group.mask) + { + // determine the mask of selected bits + uint32_t selectedmask(0U); + if (field) + { + for (ioport_diplocation const &loc : field->diplocations()) + if (group.matches(*field, loc)) + selectedmask |= uint32_t(1) << (loc.number() - 1); + } + + // draw the name + float const liney(y2 + (ui().box_tb_border() * 2.0f) + (lineheight * (DIP_SWITCH_HEIGHT + DIP_SWITCH_SPACING) * line)); + ui().draw_text_full( + container(), + group.name, + nameleft, liney + (lineheight * (DIP_SWITCH_HEIGHT - 1.0f) / 2.0f), namewidth, + text_layout::text_justify::RIGHT, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + + // draw the group outline + float const switchbottom(liney + (DIP_SWITCH_HEIGHT * lineheight)); + unsigned const cnt(group.switch_count()); + ui().draw_outlined_box( + container(), + switchleft, liney, switchleft + (m_single_width * cnt), switchbottom, + ui().colors().background_color()); + for (unsigned i = 1; cnt > i; ++i) + { + container().add_line( + switchleft + (m_single_width * i), liney, switchleft + (m_single_width * i), switchbottom, + UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } + + // compute top and bottom for on and off positions + float const yoff(liney + UI_LINE_WIDTH + ygap); + float const yon(switchbottom - UI_LINE_WIDTH - ygap - nubheight); + m_switch_group_y[n] = yoff; + + // draw the switch nubs + for (unsigned toggle = 0; cnt > toggle; ++toggle) + { + float const nubleft(switchleft + (m_single_width * toggle) + xgap); + if (BIT(group.mask, toggle)) + { + float const nubtop(BIT(group.state, toggle) ? yon : yoff); + container().add_rect( + nubleft, nubtop, nubleft + m_nub_width, nubtop + nubheight, + BIT(selectedmask, toggle) ? ui().colors().dipsw_color() : ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } + else + { + container().add_rect( + nubleft, yoff, nubleft + m_nub_width, yon + nubheight, + ui().colors().unavailable_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } + } + + // limit the number of visible switch groups + if (++line >= m_visible_switch_groups) + break; + } + } +} + + +bool menu_settings_dip_switches::custom_mouse_down() +{ + if (!m_visible_switch_groups || (get_mouse_x() < m_first_nub)) + return false; + + float const x(get_mouse_x() - m_first_nub); + float const y(get_mouse_y()); + for (unsigned n = 0U, line = 0U; (switch_groups().size() > n) && (m_visible_switch_groups > line); ++n) + { + switch_group_descriptor const &group(switch_groups()[n]); + if (group.mask) + { + ++line; + if ((y >= m_switch_group_y[n]) && (y < (m_switch_group_y[n] + m_clickable_height))) + { + unsigned const cnt(group.switch_count()); + for (unsigned i = 0U; cnt > i; ++i) + { + if (BIT(group.mask, i)) + { + float const xstart(float(i) * m_single_width); + if ((x >= xstart) && (x < (xstart + m_nub_width))) + { + ioport_field::user_settings settings; + group.toggles[i].field->get_user_settings(settings); + settings.value ^= group.toggles[i].mask; + group.toggles[i].field->set_user_settings(settings); + reset(reset_options::REMEMBER_REF); + return true; + } + } + } + } + } + } + + return false; +} + + +void menu_settings_dip_switches::populate(float &customtop, float &custombottom) +{ + // let the base class add items + menu_confswitch::populate(customtop, custombottom); + + // use up to about 70% of height for DIP switch display + if (active_switch_groups()) + { + m_switch_group_y.resize(switch_groups().size()); + float const lineheight(ui().get_line_height()); + float const groupheight(DIP_SWITCH_HEIGHT * lineheight); + float const groupspacing(DIP_SWITCH_SPACING * lineheight); + if ((active_switch_groups() * (groupheight + groupspacing)) > 0.7f) + m_visible_switch_groups = unsigned(0.7f / (groupheight + groupspacing)); + else + m_visible_switch_groups = active_switch_groups(); + custombottom = (m_visible_switch_groups * groupheight) + ((m_visible_switch_groups - 1) * groupspacing) + (ui().box_tb_border() * 3.0f); + } + else + { + m_visible_switch_groups = 0U; + custombottom = 0.0f; + } +} + + + +/*------------------------------------------------- + menu_settings_machine_config +-------------------------------------------------*/ + +menu_settings_machine_config::menu_settings_machine_config(mame_ui_manager &mui, render_container &container) : menu_confswitch(mui, container, IPT_CONFIG) +{ +} + +menu_settings_machine_config::~menu_settings_machine_config() +{ +} + + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/confswitch.h b/src/icludes/frontend/mame/ui/confswitch.h new file mode 100644 index 0000000..598b020 --- /dev/null +++ b/src/icludes/frontend/mame/ui/confswitch.h @@ -0,0 +1,109 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Vas Crabb +/*************************************************************************** + + ui/confswitch.h + + Configuration/DIP switches menu. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_CONFSWITCH_H +#define MAME_FRONTEND_UI_CONFSWITCH_H + +#include "ui/menu.h" + +#include +#include + + +namespace ui { + +class menu_confswitch : public menu +{ +public: + virtual ~menu_confswitch() override; + +protected: + struct field_descriptor + { + field_descriptor(ioport_field &f) noexcept; + + std::reference_wrapper field; + }; + + struct switch_group_descriptor + { + struct toggle + { + ioport_field *field; + ioport_value mask; + }; + + switch_group_descriptor(ioport_field const &f, ioport_diplocation const &loc) noexcept; + + bool matches(ioport_field const &f, ioport_diplocation const &loc) const noexcept; + unsigned switch_count() const noexcept; + + char const *name; + std::reference_wrapper owner; + toggle toggles[32]; + uint32_t mask; + uint32_t state; + }; + + using field_vector = std::vector; + using switch_group_vector = std::vector; + + menu_confswitch(mame_ui_manager &mui, render_container &container, uint32_t type); + + virtual void menu_activated() override; + virtual void populate(float &customtop, float &custombottom) override; + + field_vector const &fields() { return m_fields; } + switch_group_vector const &switch_groups() { return m_switch_groups; } + unsigned active_switch_groups() const { return m_active_switch_groups; } + +private: + virtual void handle(event const *ev) override; + + void find_fields(); + + field_vector m_fields; + switch_group_vector m_switch_groups; + unsigned m_active_switch_groups; + int const m_type; +}; + + +class menu_settings_dip_switches : public menu_confswitch +{ +public: + menu_settings_dip_switches(mame_ui_manager &mui, render_container &container); + virtual ~menu_settings_dip_switches() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual bool custom_mouse_down() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + + std::vector m_switch_group_y; + unsigned m_visible_switch_groups; + float m_single_width; + float m_nub_width; + float m_first_nub; + float m_clickable_height; +}; + + +class menu_settings_machine_config : public menu_confswitch +{ +public: + menu_settings_machine_config(mame_ui_manager &mui, render_container &container); + virtual ~menu_settings_machine_config(); +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_CONFSWITCH_H diff --git a/src/icludes/frontend/mame/ui/custui.cpp b/src/icludes/frontend/mame/ui/custui.cpp new file mode 100644 index 0000000..02da931 --- /dev/null +++ b/src/icludes/frontend/mame/ui/custui.cpp @@ -0,0 +1,1136 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/********************************************************************* + + ui/custui.cpp + + Internal UI user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/custui.h" + +#include "ui/selector.h" +#include "ui/ui.h" +#include "ui/utils.h" + +#include "drivenum.h" +#include "emuopts.h" +#include "uiinput.h" + +#include "corestr.h" +#include "osdepend.h" + +#include +#include +#include +#include +#include + + +namespace ui { + +namespace { + +enum +{ + LANGUAGE_MENU = 1, + SYSNAMES_MENU, + FONT_MENU, + COLORS_MENU, + HIDE_MENU, + + INFOS_SIZE = 1, + FONT_SIZE, + MUI_FNT, + MUI_BOLD, + MUI_ITALIC +}; + +const char *const HIDE_STATUS[] = { + N_("Show All"), + N_("Hide Filters"), + N_("Hide Info/Image"), + N_("Hide Both") }; + +template +T parse_number(U &&s) +{ + T result(T(0)); + std::istringstream ss(std::forward(s)); + ss.imbue(std::locale::classic()); + ss >> result; + return result; +} + +} // anonymous namespace + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_custom_ui::menu_custom_ui(mame_ui_manager &mui, render_container &container, std::function &&handler) + : menu(mui, container) + , m_handler(std::move(handler)) + , m_currlang(0) + , m_currsysnames(0) +{ + set_process_flags(PROCESS_LR_REPEAT); + find_languages(); + find_sysnames(); +} + +//------------------------------------------------- +// menu dismissed +//------------------------------------------------- + +void menu_custom_ui::menu_dismissed() +{ + ui().options().set_value(OPTION_HIDE_PANELS, ui_globals::panels_status, OPTION_PRIORITY_CMDLINE); + + machine().options().set_value(OPTION_LANGUAGE, m_currlang ? m_languages[m_currlang] : "", OPTION_PRIORITY_CMDLINE); + load_translation(machine().options()); + + ui().options().set_value(OPTION_SYSTEM_NAMES, m_currsysnames ? m_sysnames[m_currsysnames] : "", OPTION_PRIORITY_CMDLINE); + + ui_globals::reset = true; + + if (m_handler) + m_handler(); +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_custom_ui::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + switch ((uintptr_t)ev->itemref) + { + case FONT_MENU: + if (ev->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), nullptr); + break; + case COLORS_MENU: + if (ev->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container()); + break; + case LANGUAGE_MENU: + if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR)) + { + if (ev->iptkey == IPT_UI_LEFT) + --m_currlang; + else if (ev->iptkey == IPT_UI_RIGHT) + ++m_currlang; + else + m_currlang = 0; + ev->item->set_subtext(m_languages[m_currlang]); + ev->item->set_flags(get_arrow_flags(0, m_languages.size() - 1, m_currlang)); + } + else if (ev->iptkey == IPT_UI_SELECT) + { + // copying list of language names - expensive + menu::stack_push( + ui(), container(), std::vector(m_languages), m_currlang, + [this, item = ev->item] (int selection) + { + m_currlang = selection; + item->set_subtext(m_languages[selection]); + item->set_flags(get_arrow_flags(0, m_languages.size() - 1, selection)); + }); + } + break; + case SYSNAMES_MENU: + if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR)) + { + if (ev->iptkey == IPT_UI_LEFT) + --m_currsysnames; + else if (ev->iptkey == IPT_UI_RIGHT) + ++m_currsysnames; + else + m_currsysnames = 0; + ev->item->set_subtext(m_sysnames[m_currsysnames]); + ev->item->set_flags(get_arrow_flags(0, m_sysnames.size() - 1, m_currsysnames)); + } + else if (ev->iptkey == IPT_UI_SELECT) + { + // copying list of file names - expensive + menu::stack_push( + ui(), container(), std::vector(m_sysnames), m_currsysnames, + [this, item = ev->item] (int selection) + { + m_currsysnames = selection; + item->set_subtext(m_sysnames[selection]); + item->set_flags(get_arrow_flags(0, m_sysnames.size() - 1, selection)); + }); + } + break; + case HIDE_MENU: + if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR)) + { + if (ev->iptkey == IPT_UI_LEFT) + --ui_globals::panels_status; + else if (ev->iptkey == IPT_UI_RIGHT) + ++ui_globals::panels_status; + else + ui_globals::panels_status = 0; + ev->item->set_subtext(_(HIDE_STATUS[ui_globals::panels_status])); + ev->item->set_flags(get_arrow_flags(0, HIDE_BOTH, ui_globals::panels_status)); + } + else if (ev->iptkey == IPT_UI_SELECT) + { + std::vector s_sel(std::size(HIDE_STATUS)); + std::transform(std::begin(HIDE_STATUS), std::end(HIDE_STATUS), s_sel.begin(), [](auto &s) { return _(s); }); + menu::stack_push( + ui(), container(), std::move(s_sel), ui_globals::panels_status, + [item = ev->item] (int selection) + { + ui_globals::panels_status = selection; + item->set_subtext(_(HIDE_STATUS[selection])); + item->set_flags(get_arrow_flags(0, HIDE_BOTH, selection)); + }); + } + break; + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_custom_ui::populate(float &customtop, float &custombottom) +{ + uint32_t arrow_flags; + item_append(_("Fonts"), 0, (void *)(uintptr_t)FONT_MENU); + item_append(_("Colors"), 0, (void *)(uintptr_t)COLORS_MENU); + + arrow_flags = get_arrow_flags(0, m_languages.size() - 1, m_currlang); + item_append(_("Language"), m_languages[m_currlang], arrow_flags, (void *)(uintptr_t)LANGUAGE_MENU); + + arrow_flags = get_arrow_flags(0, m_sysnames.size() - 1, m_currsysnames); + item_append(_("System Names"), m_sysnames[m_currsysnames], arrow_flags, (void *)(uintptr_t)SYSNAMES_MENU); + + arrow_flags = get_arrow_flags(0, HIDE_BOTH, ui_globals::panels_status); + item_append(_("Show side panels"), _(HIDE_STATUS[ui_globals::panels_status]), arrow_flags, (void *)(uintptr_t)HIDE_MENU); + + item_append(menu_item_type::SEPARATOR); + customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_custom_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + char const *const text[] = { _("UI Customization Settings") }; + draw_text_box( + std::begin(text), std::end(text), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + +//------------------------------------------------- +// find UI translation files +//------------------------------------------------- + +void menu_custom_ui::find_languages() +{ + m_languages.emplace_back(_("[built-in]")); + + file_enumerator path(machine().options().language_path()); + osd::directory::entry const *dirent; + std::string name; + while ((dirent = path.next())) + { + if (dirent->type == osd::directory::entry::entry_type::DIR && strcmp(dirent->name, ".") != 0 && strcmp(dirent->name, "..") != 0) + { + name = dirent->name; + auto i = strreplace(name, "_", " ("); + if (i > 0) + name.append(")"); + m_languages.emplace_back(std::move(name)); + } + } + std::sort( + std::next(m_languages.begin()), + m_languages.end(), + [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); }); + + char const *const lang = machine().options().language(); + if (*lang) + { + auto const found = std::lower_bound( + std::next(m_languages.begin()), + m_languages.end(), + lang, + [] (std::string const &x, char const *y) { return 0 > core_stricmp(x.c_str(), y); }); + if ((m_languages.end() != found) && !core_stricmp(found->c_str(), lang)) + m_currlang = std::distance(m_languages.begin(), found); + } + else + { + m_currlang = 0; + } +} + +//------------------------------------------------- +// find translated system names +//------------------------------------------------- + +void menu_custom_ui::find_sysnames() +{ + m_sysnames.emplace_back(_("[built-in]")); + + path_iterator search(ui().options().history_path()); + std::string path; + while (search.next(path)) + { + file_enumerator dir(path); + osd::directory::entry const *dirent; + while ((dirent = dir.next())) + { + if (dirent->type == osd::directory::entry::entry_type::FILE && core_filename_ends_with(dirent->name, ".lst")) + m_sysnames.emplace_back(dirent->name); + } + } + std::sort( + m_sysnames.begin(), + m_sysnames.end(), + [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); }); + + char const *const names = ui().options().system_names(); + if (*names) + { + auto const found = std::lower_bound( + std::next(m_sysnames.begin()), + m_sysnames.end(), + names, + [] (std::string const &x, char const *y) { return 0 > core_stricmp(x.c_str(), y); }); + m_currsysnames = std::distance(m_sysnames.begin(), found); + if ((m_sysnames.end() == found) || core_stricmp(found->c_str(), names)) + m_sysnames.emplace(found, names); + } + else + { + m_currsysnames = 0; + } +} + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_font_ui::menu_font_ui(mame_ui_manager &mui, render_container &container, std::function &&handler) + : menu(mui, container) + , m_handler(std::move(handler)) + , m_fonts() + , m_font_min(parse_number(mui.options().get_entry(OPTION_FONT_ROWS)->minimum())) + , m_font_max(parse_number(mui.options().get_entry(OPTION_FONT_ROWS)->maximum())) + , m_font_size(mui.options().font_rows()) + , m_info_min(parse_number(mui.options().get_entry(OPTION_INFOS_SIZE)->minimum())) + , m_info_max(parse_number(mui.options().get_entry(OPTION_INFOS_SIZE)->maximum())) + , m_info_size(mui.options().infos_size()) + , m_changed(false) + , m_actual(0U) +{ + set_process_flags(PROCESS_LR_REPEAT); + + std::string name(mui.machine().options().ui_font()); + list(); + +#ifdef UI_WINDOWS + m_bold = (strreplace(name, "[B]", "") + strreplace(name, "[b]", "") > 0); + m_italic = (strreplace(name, "[I]", "") + strreplace(name, "[i]", "") > 0); +#endif + + for (std::size_t index = 0; index < m_fonts.size(); index++) + { + if (m_fonts[index].first == name) + { + m_actual = index; + break; + } + } +} + +//------------------------------------------------- +// create fonts list +//------------------------------------------------- + +void menu_font_ui::list() +{ + machine().osd().get_font_families(machine().options().font_path(), m_fonts); + + // add default string to the top of array + m_fonts.emplace(m_fonts.begin(), std::string("default"), std::string(_("default"))); +} + +//------------------------------------------------- +// menu dismissed +//------------------------------------------------- + +void menu_font_ui::menu_dismissed() +{ + if (m_changed) + { + ui_options &moptions = ui().options(); + + std::string name(m_fonts[m_actual].first); +#ifdef UI_WINDOWS + if (name != "default") + { + if (m_italic) + name.insert(0, "[I]"); + if (m_bold) + name.insert(0, "[B]"); + } +#endif + machine().options().set_value(OPTION_UI_FONT, name, OPTION_PRIORITY_CMDLINE); + moptions.set_value(OPTION_INFOS_SIZE, m_info_size, OPTION_PRIORITY_CMDLINE); + moptions.set_value(OPTION_FONT_ROWS, m_font_size, OPTION_PRIORITY_CMDLINE); + + // OPTION_FONT_ROWS was changed; update the font info + ui().update_target_font_height(); + } + + if (m_handler) + m_handler(m_changed); +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_font_ui::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + switch ((uintptr_t)ev->itemref) + { + case FONT_SIZE: + if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR)) + { + m_changed = true; + if (ev->iptkey == IPT_UI_LEFT) + --m_font_size; + else if (ev->iptkey == IPT_UI_RIGHT) + ++m_font_size; + else + m_font_size = parse_number(ui().options().get_entry(OPTION_FONT_ROWS)->default_value().c_str()); + ev->item->set_subtext(string_format("%d", m_font_size)); + ev->item->set_flags(get_arrow_flags(m_font_min, m_font_max, m_font_size)); + } + break; + + case INFOS_SIZE: + if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR)) + { + m_changed = true; + if (ev->iptkey == IPT_UI_LEFT) + m_info_size -= 0.05f; + else if (ev->iptkey == IPT_UI_RIGHT) + m_info_size += 0.05f; + else + m_info_size = parse_number(ui().options().get_entry(OPTION_INFOS_SIZE)->default_value().c_str()); + ev->item->set_subtext(string_format("%.2f", m_info_size)); + ev->item->set_flags(get_arrow_flags(m_info_min, m_info_max, m_info_size)); + } + break; + + case MUI_FNT: + if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR)) + { + m_changed = true; + if (ev->iptkey == IPT_UI_LEFT) + --m_actual; + else if (ev->iptkey == IPT_UI_RIGHT) + ++m_actual; + else + m_actual = 0; + reset(reset_options::REMEMBER_REF); + } + else if (ev->iptkey == IPT_UI_SELECT) + { + std::vector display_names; + display_names.reserve(m_fonts.size()); + for (auto const &font : m_fonts) + display_names.emplace_back(font.second); + menu::stack_push( + ui(), container(), std::move(display_names), m_actual, + [this] (int selection) + { + m_changed = true; + m_actual = selection; + reset(reset_options::REMEMBER_REF); + }); + } + break; + +#ifdef UI_WINDOWS + case MUI_BOLD: + case MUI_ITALIC: + if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_SELECT) || (ev->iptkey == IPT_UI_CLEAR)) + { + m_changed = true; + bool &val = ((uintptr_t)ev->itemref == MUI_BOLD) ? m_bold : m_italic; + if (ev->iptkey == IPT_UI_CLEAR) + val = false; + else + val = !val; + ev->item->set_subtext(val ? _("On") : _("Off")); + ev->item->set_flags(val ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW); + } + break; +#endif + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_font_ui::populate(float &customtop, float &custombottom) +{ + // set filter arrow + uint32_t arrow_flags; + + arrow_flags = get_arrow_flags(0, m_fonts.size() - 1, m_actual); + item_append(_("UI Font"), m_fonts[m_actual].second, arrow_flags, (void *)(uintptr_t)MUI_FNT); + +#ifdef UI_WINDOWS + if (m_fonts[m_actual].first != "default") + { + item_append_on_off(_("Bold"), m_bold, 0, (void *)(uintptr_t)MUI_BOLD); + item_append_on_off(_("Italic"), m_italic, 0, (void *)(uintptr_t)MUI_ITALIC); + } +#endif + + arrow_flags = get_arrow_flags(m_font_min, m_font_max, m_font_size); + item_append(_("Lines"), string_format("%d", m_font_size), arrow_flags, (void *)(uintptr_t)FONT_SIZE); + + item_append(menu_item_type::SEPARATOR); + + arrow_flags = get_arrow_flags(m_info_min, m_info_max, m_info_size); + item_append(_("Infos text size"), string_format("%.2f", m_info_size), arrow_flags, (void *)(uintptr_t)INFOS_SIZE); + + item_append(menu_item_type::SEPARATOR); + + custombottom = customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_font_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + // top text + char const *const toptext[] = { _("UI Fonts Settings") }; + draw_text_box( + std::begin(toptext), std::end(toptext), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); + + if (uintptr_t(selectedref) == INFOS_SIZE) + { + char const *const bottomtext[] = { _("Sample text - Lorem ipsum dolor sit amet, consectetur adipiscing elit.") }; + draw_text_box( + std::begin(bottomtext), std::end(bottomtext), + origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom, + text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), UI_GREEN_COLOR, m_info_size); + } +} + +//------------------------------------------------- +// ctor +//------------------------------------------------- +#define SET_COLOR_UI(var, opt) var[M##opt].color = mui.options().rgb_value(OPTION_##opt); var[M##opt].option = OPTION_##opt + +menu_colors_ui::menu_colors_ui(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ + SET_COLOR_UI(m_color_table, UI_BACKGROUND_COLOR); + SET_COLOR_UI(m_color_table, UI_BORDER_COLOR); + SET_COLOR_UI(m_color_table, UI_CLONE_COLOR); + SET_COLOR_UI(m_color_table, UI_DIPSW_COLOR); + SET_COLOR_UI(m_color_table, UI_GFXVIEWER_BG_COLOR); + SET_COLOR_UI(m_color_table, UI_MOUSEDOWN_BG_COLOR); + SET_COLOR_UI(m_color_table, UI_MOUSEDOWN_COLOR); + SET_COLOR_UI(m_color_table, UI_MOUSEOVER_BG_COLOR); + SET_COLOR_UI(m_color_table, UI_MOUSEOVER_COLOR); + SET_COLOR_UI(m_color_table, UI_SELECTED_BG_COLOR); + SET_COLOR_UI(m_color_table, UI_SELECTED_COLOR); + SET_COLOR_UI(m_color_table, UI_SLIDER_COLOR); + SET_COLOR_UI(m_color_table, UI_SUBITEM_COLOR); + SET_COLOR_UI(m_color_table, UI_TEXT_BG_COLOR); + SET_COLOR_UI(m_color_table, UI_TEXT_COLOR); + SET_COLOR_UI(m_color_table, UI_UNAVAILABLE_COLOR); +} + +//------------------------------------------------- +// menu dismissed +//------------------------------------------------- + +void menu_colors_ui::menu_dismissed() +{ + std::string dec_color; + for (int index = 1; index < MUI_RESTORE; index++) + { + dec_color = string_format("%x", (uint32_t)m_color_table[index].color); + ui().options().set_value(m_color_table[index].option, dec_color, OPTION_PRIORITY_CMDLINE); + } + + // refresh our cached colors + ui().colors().refresh(ui().options()); +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_colors_ui::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT) + { + if ((uintptr_t)ev->itemref != MUI_RESTORE) + { + menu::stack_push(ui(), container(), &m_color_table[(uintptr_t)ev->itemref].color, std::string(selected_item().text())); + } + else + { + restore_colors(); + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_colors_ui::populate(float &customtop, float &custombottom) +{ + item_append(_("color-option", "Normal text"), 0, (void *)(uintptr_t)MUI_TEXT_COLOR); + item_append(_("color-option", "Selected color"), 0, (void *)(uintptr_t)MUI_SELECTED_COLOR); + item_append(_("color-option", "Normal text background"), 0, (void *)(uintptr_t)MUI_TEXT_BG_COLOR); + item_append(_("color-option", "Selected background color"), 0, (void *)(uintptr_t)MUI_SELECTED_BG_COLOR); + item_append(_("color-option", "Subitem color"), 0, (void *)(uintptr_t)MUI_SUBITEM_COLOR); + item_append(_("color-option", "Clone"), 0, (void *)(uintptr_t)MUI_CLONE_COLOR); + item_append(_("color-option", "Border"), 0, (void *)(uintptr_t)MUI_BORDER_COLOR); + item_append(_("color-option", "Background"), 0, (void *)(uintptr_t)MUI_BACKGROUND_COLOR); + item_append(_("color-option", "DIP switch"), 0, (void *)(uintptr_t)MUI_DIPSW_COLOR); + item_append(_("color-option", "Unavailable color"), 0, (void *)(uintptr_t)MUI_UNAVAILABLE_COLOR); + item_append(_("color-option", "Slider color"), 0, (void *)(uintptr_t)MUI_SLIDER_COLOR); + item_append(_("color-option", "Graphics viewer background"), 0, (void *)(uintptr_t)MUI_GFXVIEWER_BG_COLOR); + item_append(_("color-option", "Mouse over color"), 0, (void *)(uintptr_t)MUI_MOUSEOVER_COLOR); + item_append(_("color-option", "Mouse over background color"), 0, (void *)(uintptr_t)MUI_MOUSEOVER_BG_COLOR); + item_append(_("color-option", "Mouse down color"), 0, (void *)(uintptr_t)MUI_MOUSEDOWN_COLOR); + item_append(_("color-option", "Mouse down background color"), 0, (void *)(uintptr_t)MUI_MOUSEDOWN_BG_COLOR); + + item_append(menu_item_type::SEPARATOR); + + item_append(_("Restore default colors"), 0, (void *)(uintptr_t)MUI_RESTORE); + + custombottom = customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_colors_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + // top text + char const *const toptext[] = { _("UI Color Settings") }; + draw_text_box( + std::begin(toptext), std::end(toptext), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); + + // bottom text + // get the text for 'UI Select' + std::string const bottomtext[] = { util::string_format(_("Double-click or press %1$s to change color"), ui().get_general_input_setting(IPT_UI_SELECT)) }; + draw_text_box( + std::begin(bottomtext), std::end(bottomtext), + origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); + + // compute maxwidth + char const *const topbuf = _("Menu Preview"); + + const float lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container()); + float width; + ui().draw_text_full( + container(), + topbuf, + 0.0f, 0.0f, 1.0f, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &width, nullptr); + float maxwidth = width + 2.0f * lr_border; + + std::string sampletxt[5]; + + sampletxt[0] = _("color-sample", "Normal"); + sampletxt[1] = _("color-sample", "Subitem"); + sampletxt[2] = _("color-sample", "Selected"); + sampletxt[3] = _("color-sample", "Mouse Over"); + sampletxt[4] = _("color-sample", "Clone"); + + for (auto & elem: sampletxt) + { + ui().draw_text_full( + container(), + elem, + 0.0f, 0.0f, 1.0f, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &width, nullptr); + width += 2 * lr_border; + maxwidth = std::max(maxwidth, width); + } + + // compute our bounds for header + float x1 = origx2 + 2.0f * lr_border; + float x2 = x1 + maxwidth; + float y1 = origy1; + float y2 = y1 + bottom - ui().box_tb_border(); + + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, UI_GREEN_COLOR); + + // take off the borders + x1 += lr_border; + x2 -= lr_border; + y1 += ui().box_tb_border(); + y2 -= ui().box_tb_border(); + + // draw the text within it + ui().draw_text_full( + container(), + topbuf, + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(), nullptr, nullptr); + + // compute our bounds for menu preview + float line_height = ui().get_line_height(); + x1 -= lr_border; + x2 += lr_border; + y1 = y2 + 2.0f * ui().box_tb_border(); + y2 = y1 + 5.0f * line_height + 2.0f * ui().box_tb_border(); + + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, m_color_table[MUI_BACKGROUND_COLOR].color); + + // take off the borders + x1 += lr_border; + x2 -= lr_border; + y1 += ui().box_tb_border(); + + // draw normal text + ui().draw_text_full( + container(), + sampletxt[0], + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, m_color_table[MUI_TEXT_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color, nullptr, nullptr); + y1 += line_height; + + // draw subitem text + ui().draw_text_full( + container(), + sampletxt[1], + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, m_color_table[MUI_SUBITEM_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color, nullptr, nullptr); + y1 += line_height; + + // draw selected text + highlight(x1, y1, x2, y1 + line_height, m_color_table[MUI_SELECTED_BG_COLOR].color); + ui().draw_text_full( + container(), + sampletxt[2], + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, m_color_table[MUI_SELECTED_COLOR].color, m_color_table[MUI_SELECTED_BG_COLOR].color, nullptr, nullptr); + y1 += line_height; + + // draw mouse over text + highlight(x1, y1, x2, y1 + line_height, m_color_table[MUI_MOUSEOVER_BG_COLOR].color); + ui().draw_text_full( + container(), + sampletxt[3], + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, m_color_table[MUI_MOUSEOVER_COLOR].color, m_color_table[MUI_MOUSEOVER_BG_COLOR].color, nullptr, nullptr); + y1 += line_height; + + // draw clone text + ui().draw_text_full( + container(), + sampletxt[4], + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, m_color_table[MUI_CLONE_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color, nullptr, nullptr); + +} + +//------------------------------------------------- +// restore original colors +//------------------------------------------------- + +void menu_colors_ui::restore_colors() +{ + ui_options options; + for (int index = 1; index < MUI_RESTORE; index++) + m_color_table[index].color = rgb_t((uint32_t)strtoul(options.value(m_color_table[index].option), nullptr, 16)); +} + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_rgb_ui::menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_t *color, std::string &&title) + : menu(mui, container) + , m_color(color) + , m_search() + , m_key_active(false) + , m_lock_ref(0) + , m_title(std::move(title)) +{ + set_process_flags(PROCESS_LR_REPEAT); +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_rgb_ui::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + switch (ev->iptkey) + { + case IPT_UI_LEFT: + case IPT_UI_RIGHT: + { + bool changed = false; + int updated = (IPT_UI_LEFT == ev->iptkey) ? -1 : 1; + switch (uintptr_t(ev->itemref)) + { + case RGB_ALPHA: + updated += m_color->a(); + if ((0 <= updated) && (255 >= updated)) + { + m_color->set_a(updated); + changed = true; + } + break; + case RGB_RED: + updated += m_color->r(); + if ((0 <= updated) && (255 >= updated)) + { + m_color->set_r(updated); + changed = true; + } + break; + case RGB_GREEN: + updated += m_color->g(); + if ((0 <= updated) && (255 >= updated)) + { + m_color->set_g(updated); + changed = true; + } + break; + case RGB_BLUE: + updated += m_color->b(); + if ((0 <= updated) && (255 >= updated)) + { + m_color->set_b(updated); + changed = true; + } + break; + } + if (changed) + { + ev->item->set_subtext(string_format("%3u", updated)); + ev->item->set_flags(get_arrow_flags(0, 255, updated)); + } + } + break; + + case IPT_UI_SELECT: + if (uintptr_t(ev->itemref) == PALETTE_CHOOSE) + { + menu::stack_push(ui(), container(), *m_color); + break; + } + [[fallthrough]]; + case IPT_SPECIAL: + switch (uintptr_t(ev->itemref)) + { + case RGB_ALPHA: + case RGB_RED: + case RGB_GREEN: + case RGB_BLUE: + inkey_special(ev); + break; + } + break; + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_rgb_ui::populate(float &customtop, float &custombottom) +{ + // set filter arrow + std::string s_text = std::string(m_search).append("_"); + item_append(_("ARGB Settings"), FLAG_DISABLE | FLAG_UI_HEADING, nullptr); + + if (m_lock_ref != RGB_ALPHA) + { + uint32_t arrow_flags = get_arrow_flags(0, 255, m_color->a()); + item_append(_("color-channel", "Alpha"), string_format("%3u", m_color->a()), arrow_flags, (void *)(uintptr_t)RGB_ALPHA); + } + else + item_append(_("color-channel", "Alpha"), s_text, 0, (void *)(uintptr_t)RGB_ALPHA); + + if (m_lock_ref != RGB_RED) + { + uint32_t arrow_flags = get_arrow_flags(0, 255, m_color->r()); + item_append(_("color-channel", "Red"), string_format("%3u", m_color->r()), arrow_flags, (void *)(uintptr_t)RGB_RED); + } + else + item_append(_("color-channel", "Red"), s_text, 0, (void *)(uintptr_t)RGB_RED); + + if (m_lock_ref != RGB_GREEN) + { + uint32_t arrow_flags = get_arrow_flags(0, 255, m_color->g()); + item_append(_("color-channel", "Green"), string_format("%3u", m_color->g()), arrow_flags, (void *)(uintptr_t)RGB_GREEN); + } + else + item_append(_("color-channel", "Green"), s_text, 0, (void *)(uintptr_t)RGB_GREEN); + + if (m_lock_ref != RGB_BLUE) + { + uint32_t arrow_flags = get_arrow_flags(0, 255, m_color->b()); + item_append(_("color-channel", "Blue"), string_format("%3u", m_color->b()), arrow_flags, (void *)(uintptr_t)RGB_BLUE); + } + else + item_append(_("color-channel", "Blue"), s_text, 0, (void *)(uintptr_t)RGB_BLUE); + + item_append(menu_item_type::SEPARATOR); + item_append(_("Choose from palette"), 0, (void *)(uintptr_t)PALETTE_CHOOSE); + item_append(menu_item_type::SEPARATOR); + + custombottom = customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_rgb_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + float width, maxwidth = origx2 - origx1; + + // top text + ui().draw_text_full( + container(), + m_title, + 0.0f, 0.0f, 1.0f, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &width); + const float lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container()); + width += 2 * lr_border; + maxwidth = std::max(maxwidth, width); + + // compute our bounds + float x1 = 0.5f - 0.5f * maxwidth; + float x2 = x1 + maxwidth; + float y1 = origy1 - top; + float y2 = origy1 - ui().box_tb_border(); + + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, UI_GREEN_COLOR); + + // take off the borders + x1 += lr_border; + x2 -= lr_border; + y1 += ui().box_tb_border(); + + // draw the text within it + ui().draw_text_full( + container(), + m_title, + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color()); + + std::string sampletxt(_("Color preview:")); + ui().draw_text_full( + container(), + sampletxt, + 0.0f, 0.0f, 1.0f, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), + &width); + width += 2 * lr_border; + maxwidth = std::max(origx2 - origx1, width); + + // compute our bounds + x1 = 0.5f - 0.5f * maxwidth; + x2 = x1 + maxwidth; + y1 = origy2 + ui().box_tb_border(); + y2 = origy2 + bottom; + + // draw a box - force black to ensure the text is legible + ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t::black()); + + // take off the borders + x1 += lr_border; + y1 += ui().box_tb_border(); + + // draw the text label - force white to ensure it's legible + ui().draw_text_full( + container(), + sampletxt, + x1, y1, width - lr_border, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, rgb_t::white(), rgb_t::black()); + + x1 += width + (lr_border * 2.0f); + x2 -= lr_border; + y2 -= ui().box_tb_border(); + + // add white under half the sample swatch to make alpha effects visible + container().add_rect((x1 + x2) * 0.5f, y1, x2, y2, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_rect(x1, y1, x2, y2, *m_color, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); +} + +//------------------------------------------------- +// handle special key event +//------------------------------------------------- + +void menu_rgb_ui::inkey_special(const event *menu_event) +{ + if (menu_event->iptkey == IPT_UI_SELECT) + { + m_key_active = !m_key_active; + set_process_flags(m_key_active ? PROCESS_ONLYCHAR : PROCESS_LR_REPEAT); + m_lock_ref = (uintptr_t)menu_event->itemref; + + if (!m_key_active) + { + int val = atoi(m_search.data()); + val = m_color->clamp(val); + + switch ((uintptr_t)menu_event->itemref) + { + case RGB_ALPHA: + m_color->set_a(val); + break; + + case RGB_RED: + m_color->set_r(val); + break; + + case RGB_GREEN: + m_color->set_g(val); + break; + + case RGB_BLUE: + m_color->set_b(val); + break; + } + + m_search.erase(); + m_lock_ref = 0; + + menu_event->item->set_subtext(string_format("%3u", val)); + menu_event->item->set_flags(get_arrow_flags(0, 255, val)); + } + else + { + menu_event->item->set_subtext("_"); + menu_event->item->set_flags(0); + } + } + else if (m_key_active) + { + input_character(m_search, 3, menu_event->unichar, uchar_is_digit); + menu_event->item->set_subtext(m_search + "_"); + } +} + +std::pair const menu_palette_sel::s_palette[] = { + { N_p("color-preset", "White"), "FFFFFFFF" }, + { N_p("color-preset", "Silver"), "FFC0C0C0" }, + { N_p("color-preset", "Gray"), "FF808080" }, + { N_p("color-preset", "Black"), "FF000000" }, + { N_p("color-preset", "Red"), "FFFF0000" }, + { N_p("color-preset", "Orange"), "FFFFA500" }, + { N_p("color-preset", "Yellow"), "FFFFFF00" }, + { N_p("color-preset", "Green"), "FF00FF00" }, + { N_p("color-preset", "Blue"), "FF0000FF" }, + { N_p("color-preset", "Violet"), "FF8F00FF" } +}; + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_palette_sel::menu_palette_sel(mame_ui_manager &mui, render_container &container, rgb_t &_color) + : menu(mui, container), m_original(_color) +{ +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_palette_sel::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + if (ev->iptkey == IPT_UI_SELECT) + { + m_original = rgb_t(uint32_t(strtoul(selected_item().subtext().c_str(), nullptr, 16))); + reset_parent(reset_options::REMEMBER_REF); + stack_pop(); + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_palette_sel::populate(float &customtop, float &custombottom) +{ + for (unsigned x = 0; x < std::size(s_palette); ++x) + item_append(_("color-preset", s_palette[x].first), s_palette[x].second, FLAG_COLOR_BOX, (void *)(uintptr_t)(x + 1)); + + item_append(menu_item_type::SEPARATOR); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/custui.h b/src/icludes/frontend/mame/ui/custui.h new file mode 100644 index 0000000..168934b --- /dev/null +++ b/src/icludes/frontend/mame/ui/custui.h @@ -0,0 +1,185 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/*************************************************************************** + + ui/custui.h + + Internal UI user interface. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_CUSTUI_H +#define MAME_FRONTEND_UI_CUSTUI_H + +#pragma once + +#include "ui/menu.h" + +#include + + +namespace ui { + +//------------------------------------------------- +// Custom UI menu +//------------------------------------------------- + +class menu_custom_ui : public menu +{ +public: + menu_custom_ui(mame_ui_manager &mui, render_container &container, std::function &&handler); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void menu_dismissed() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void find_languages(); + void find_sysnames(); + + std::function m_handler; + std::vector m_languages; + std::vector m_sysnames; + std::size_t m_currlang; + std::size_t m_currsysnames; +}; + +//------------------------------------------------- +// Font UI menu +//------------------------------------------------- + +class menu_font_ui : public menu +{ +public: + menu_font_ui(mame_ui_manager &mui, render_container &container, std::function &&handler); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void menu_dismissed() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void list(); + + std::function m_handler; + std::vector > m_fonts; + int const m_font_min, m_font_max; + int m_font_size; + float const m_info_min, m_info_max; + float m_info_size; + bool m_changed; + + std::uint16_t m_actual; +#ifdef UI_WINDOWS + bool m_bold, m_italic; +#endif + +}; + +//------------------------------------------------- +// Colors UI menu +//------------------------------------------------- + +class menu_colors_ui : public menu +{ +public: + menu_colors_ui(mame_ui_manager &mui, render_container &container); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void menu_dismissed() override; + +private: + enum + { + MUI_BACKGROUND_COLOR = 1, + MUI_BORDER_COLOR, + MUI_CLONE_COLOR, + MUI_DIPSW_COLOR, + MUI_GFXVIEWER_BG_COLOR, + MUI_MOUSEDOWN_BG_COLOR, + MUI_MOUSEDOWN_COLOR, + MUI_MOUSEOVER_BG_COLOR, + MUI_MOUSEOVER_COLOR, + MUI_SELECTED_BG_COLOR, + MUI_SELECTED_COLOR, + MUI_SLIDER_COLOR, + MUI_SUBITEM_COLOR, + MUI_TEXT_BG_COLOR, + MUI_TEXT_COLOR, + MUI_UNAVAILABLE_COLOR, + MUI_RESTORE + }; + + struct s_color_table + { + rgb_t color; + const char *option; + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + s_color_table m_color_table[MUI_RESTORE]; + void restore_colors(); +}; + +//------------------------------------------------- +// ARGB UI menu +//------------------------------------------------- + +class menu_rgb_ui : public menu +{ +public: + menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_t *color, std::string &&title); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + enum + { + RGB_ALPHA = 1, + RGB_RED, + RGB_GREEN, + RGB_BLUE, + PALETTE_CHOOSE + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void inkey_special(const event *menu_event); + + rgb_t *m_color; + std::string m_search; + bool m_key_active; + int m_lock_ref; + std::string m_title; +}; + +//------------------------------------------------- +// Palette UI menu +//------------------------------------------------- + +class menu_palette_sel : public menu +{ +public: + menu_palette_sel(mame_ui_manager &mui, render_container &container, rgb_t &_color); + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + static std::pair const s_palette[]; + rgb_t &m_original; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_CUSTUI_H diff --git a/src/icludes/frontend/mame/ui/datmenu.cpp b/src/icludes/frontend/mame/ui/datmenu.cpp new file mode 100644 index 0000000..df8ac0a --- /dev/null +++ b/src/icludes/frontend/mame/ui/datmenu.cpp @@ -0,0 +1,411 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/********************************************************************* + + ui/datmenu.cpp + + Internal UI user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/datmenu.h" + +#include "ui/systemlist.h" +#include "ui/ui.h" +#include "ui/utils.h" + +#include "luaengine.h" +#include "mame.h" + +#include "drivenum.h" +#include "rendfont.h" +#include "softlist.h" +#include "uiinput.h" + +#include +#include +#include + + +namespace ui { + +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_system_info *system) + : menu_textbox(mui, container) + , m_system(!system ? &system_list::instance().systems()[driver_list::find(mui.machine().system().name)] : system) + , m_swinfo(nullptr) + , m_issoft(false) + , m_actual(0) + +{ + set_process_flags(PROCESS_LR_ALWAYS | PROCESS_CUSTOM_NAV); + for (device_image_interface& image : image_interface_enumerator(mui.machine().root_device())) + { + if (image.filename()) + { + m_list = strensure(image.software_list_name()); + m_short = image.software_entry()->shortname(); + m_long = image.software_entry()->longname(); + m_parent = image.software_entry()->parentname(); + } + } + std::vector lua_list; + if (mame_machine_manager::instance()->lua()->call_plugin("data_list", system ? system->driver->name : "", lua_list)) + { + int count = 0; + for (std::string& item : lua_list) + { + std::string version; + mame_machine_manager::instance()->lua()->call_plugin("data_version", count, version); + m_items_list.emplace_back(item.c_str(), count, std::move(version)); + count++; + } + } +} + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_software_info &swinfo) + : menu_textbox(mui, container) + , m_system(nullptr) + , m_swinfo(&swinfo) + , m_issoft(true) + , m_actual(0) + , m_list(swinfo.listname) + , m_short(swinfo.shortname) + , m_long(swinfo.longname) + , m_parent(swinfo.parentname) + +{ + set_process_flags(PROCESS_LR_ALWAYS | PROCESS_CUSTOM_NAV); + if (!swinfo.infotext.empty()) + m_items_list.emplace_back(_("Software List Info"), 0, ""); + std::vector lua_list; + if (mame_machine_manager::instance()->lua()->call_plugin("data_list", std::string(m_short).append(1, ',').append(m_list).c_str(), lua_list)) + { + int count = 1; + for (std::string &item : lua_list) + { + std::string version; + mame_machine_manager::instance()->lua()->call_plugin("data_version", count - 1, version); + m_items_list.emplace_back(item.c_str(), count, std::move(version)); + count++; + } + } +} + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_dats_view::~menu_dats_view() +{ +} + +//------------------------------------------------- +// add text to layout +//------------------------------------------------- + +void menu_dats_view::add_info_text(text_layout &layout, std::string_view text, rgb_t color, float size) +{ + char justify = 'l'; // left justify by default + if ((text.length() > 3) && (text[0] == '#') && (text[1] == 'j')) + { + auto const eol = text.find('\n'); + if ((std::string_view::npos != eol) && (2 < eol)) + { + justify = text[2]; + text.remove_prefix(eol + 1); + } + } + + if ('2' == justify) + { + while (!text.empty()) + { + // pop a line from the front + auto const eol = text.find('\n'); + std::string_view const line = (std::string_view::npos != eol) + ? text.substr(0, eol + 1) + : text; + text.remove_prefix(line.length()); + + // split on the first tab + auto const split = line.find('\t'); + if (std::string_view::npos != split) + { + layout.add_text(line.substr(0, split), text_layout::text_justify::LEFT, color, rgb_t::transparent(), size); + layout.add_text(" ", text_layout::text_justify::LEFT, color, rgb_t::transparent(), size); + layout.add_text(line.substr(split + 1), text_layout::text_justify::RIGHT, color, rgb_t::transparent(), size); + } + else + { + layout.add_text(line, text_layout::text_justify::LEFT, color, rgb_t::transparent(), size); + } + } + } + else + { + // use the same alignment for all the text + auto const j = + ('c' == justify) ? text_layout::text_justify::CENTER : + ('r' == justify) ? text_layout::text_justify::RIGHT : + text_layout::text_justify::LEFT; + layout.add_text(text, j, color, rgb_t::transparent(), size); + } + +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_dats_view::handle(event const *ev) +{ + if (ev) + { + switch (ev->iptkey) + { + case IPT_UI_LEFT: + if (m_actual > 0) + { + m_actual--; + reset_layout(); + } + break; + + case IPT_UI_RIGHT: + if ((m_actual + 1) < m_items_list.size()) + { + m_actual++; + reset_layout(); + } + break; + + default: + handle_key(ev->iptkey); + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_dats_view::populate(float &customtop, float &custombottom) +{ + customtop = 2.0f * ui().get_line_height() + 4.0f * ui().box_tb_border(); + custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_dats_view::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + float maxwidth = origx2 - origx1; + float width; + std::string_view const driver = m_issoft ? m_swinfo->longname : m_system->description; + + float const lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container()); + ui().draw_text_full( + container(), + driver, + 0.0f, 0.0f, 1.0f, + ui::text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &width, nullptr); + width += 2 * lr_border; + maxwidth = std::max(maxwidth, width); + + // compute our bounds + float x1 = 0.5f - 0.5f * maxwidth; + float x2 = x1 + maxwidth; + float y1 = origy1 - top; + float y2 = origy1 - 2.0f * ui().box_tb_border() - ui().get_line_height(); + + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, UI_GREEN_COLOR); + + // take off the borders + x1 += lr_border; + x2 -= lr_border; + y1 += ui().box_tb_border(); + + ui().draw_text_full( + container(), + driver, + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, ui::text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color()); + + maxwidth = 0; + for (auto const &elem : m_items_list) + { + ui().draw_text_full( + container(), + elem.label, + 0.0f, 0.0f, 1.0f, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), + &width, nullptr); + maxwidth += width; + } + + float space = (1.0f - maxwidth) / (m_items_list.size() * 2); + + // compute our bounds + x1 -= lr_border; + x2 += lr_border; + y1 = y2 + ui().box_tb_border(); + y2 += ui().get_line_height() + 2.0f * ui().box_tb_border(); + + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color()); + + // take off the borders + y1 += ui().box_tb_border(); + + // draw the text within it + int x = 0; + for (auto const &elem : m_items_list) + { + x1 += space; + if (mouse_in_rect(x1 - (space / 2), y1, x1 + width + (space / 2), y2)) + set_hover(HOVER_INFO_TEXT + 1 + x); + + rgb_t const fcolor = (m_actual == x) ? rgb_t(0xff, 0xff, 0xff, 0x00) : ui().colors().text_color(); + rgb_t const bcolor = (m_actual == x) ? rgb_t(0xff, 0xff, 0xff, 0xff) : ui().colors().text_bg_color(); + ui().draw_text_full( + container(), + elem.label, + x1, y1, 1.0f, + text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER, + mame_ui_manager::NONE, fcolor, bcolor, + &width, nullptr); + + if (bcolor != ui().colors().text_bg_color()) + { + ui().draw_textured_box( + container(), + x1 - (space / 2), y1, x1 + width + (space / 2), y2, + bcolor, rgb_t(255, 43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + + ui().draw_text_full( + container(), + elem.label, + x1, y1, 1.0f, + text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, fcolor, bcolor, + &width, nullptr); + x1 += width + space; + ++x; + } + + // bottom + if (!m_items_list.empty()) + { + std::string const revision(util::string_format(_("Revision: %1$s"), m_items_list[m_actual].revision)); + ui().draw_text_full( + container(), + revision, + 0.0f, 0.0f, 1.0f, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), + &width, nullptr); + width += 2 * lr_border; + maxwidth = std::max(origx2 - origx1, width); + + // compute our bounds + x1 = 0.5f - 0.5f * maxwidth; + x2 = x1 + maxwidth; + y1 = origy2 + ui().box_tb_border(); + y2 = origy2 + bottom; + + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, UI_GREEN_COLOR); + + // take off the borders + x1 += lr_border; + x2 -= lr_border; + y1 += ui().box_tb_border(); + + // draw the text within it + ui().draw_text_full( + container(), + revision, + x1, y1, x2 - x1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color()); + } +} + +//------------------------------------------------- +// custom mouse click handling +//------------------------------------------------- + +bool menu_dats_view::custom_mouse_down() +{ + if ((hover() > HOVER_INFO_TEXT) && ((hover() - HOVER_INFO_TEXT) <= m_items_list.size())) + { + if ((hover() - HOVER_INFO_TEXT - 1) != m_actual) + { + m_actual = hover() - HOVER_INFO_TEXT - 1; + reset(reset_options::SELECT_FIRST); + } + return true; + } + else + { + return false; + } +} + +//------------------------------------------------- +// populate selected DAT text +//------------------------------------------------- + +void menu_dats_view::populate_text(std::optional &layout, float &width, int &lines) +{ + if (!layout || (layout->width() != width)) + { + std::string buffer; + if (!m_items_list.empty()) + { + if (m_issoft) + get_data_sw(buffer); + else + get_data(buffer); + } + layout.emplace(ui().create_layout(container(), width)); + add_info_text(*layout, buffer, ui().colors().text_color()); + lines = std::numeric_limits::max(); + } +} + +//------------------------------------------------- +// load data from DATs +//------------------------------------------------- + +void menu_dats_view::get_data(std::string &buffer) +{ + mame_machine_manager::instance()->lua()->call_plugin("data", m_items_list[m_actual].option, buffer); +} + +void menu_dats_view::get_data_sw(std::string &buffer) +{ + if (m_items_list[m_actual].option == 0) + buffer = m_swinfo->infotext; + else + mame_machine_manager::instance()->lua()->call_plugin("data", m_items_list[m_actual].option - 1, buffer); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/datmenu.h b/src/icludes/frontend/mame/ui/datmenu.h new file mode 100644 index 0000000..79b9bd8 --- /dev/null +++ b/src/icludes/frontend/mame/ui/datmenu.h @@ -0,0 +1,77 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/*************************************************************************** + + ui/datmenu.h + + Internal UI user interface. + + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_DATMENU_H +#define MAME_FRONTEND_UI_DATMENU_H + +#pragma once + +#include "ui/text.h" +#include "ui/textbox.h" + +#include +#include +#include +#include + + +struct ui_software_info; +struct ui_system_info; + + +namespace ui { + +//------------------------------------------------- +// class dats menu +//------------------------------------------------- + +class menu_dats_view : public menu_textbox +{ +public: + menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_software_info &swinfo); + menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_system_info *system = nullptr); + virtual ~menu_dats_view() override; + + static void add_info_text(text_layout &layout, std::string_view text, rgb_t color, float size = 1.0f); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual bool custom_mouse_down() override; + + virtual void populate_text(std::optional &layout, float &width, int &lines) override; + +private: + struct list_items + { + list_items(std::string &&l, int i, std::string &&rev) : label(std::move(l)), option(i), revision(std::move(rev)) { } + + std::string label; + int option; + std::string revision; + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void get_data(std::string &buffer); + void get_data_sw(std::string &buffer); + + ui_system_info const *const m_system; + ui_software_info const *const m_swinfo; + bool const m_issoft; + int m_actual; + std::string m_list, m_short, m_long, m_parent; + std::vector m_items_list; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_DATMENU_H diff --git a/src/icludes/frontend/mame/ui/defimg.ipp b/src/icludes/frontend/mame/ui/defimg.ipp new file mode 100644 index 0000000..396c2ec --- /dev/null +++ b/src/icludes/frontend/mame/ui/defimg.ipp @@ -0,0 +1,273 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +#ifndef MAME_FRONTEND_UI_DEFIMG_IPP +#define MAME_FRONTEND_UI_DEFIMG_IPP +#pragma once + +namespace ui { +namespace { +// TODO: move this to an external image file and zlib compress it into a souce file as part of the build process + +uint32_t const no_avail_bmp[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x01231f20, 0x04231f20, 0x11231f20, 0x2e231f20, 0x62231f20, 0x8e231f20, 0xb4231f20, 0xd4231f20, 0xe5231f20, 0xf2231f20, 0xfd231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfd231f20, 0xf2231f20, 0xe5231f20, 0xd4231f20, 0xb4231f20, 0x8e231f20, 0x62231f20, 0x2e231f20, 0x11231f20, 0x04231f20, 0x01231f20, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00231f20, 0x00000000, 0x00000000, 0x06231f20, 0x1c231f20, 0x49231f20, 0x8c231f20, 0xc4231f20, 0xf0231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf0231f20, 0xc4231f20, 0x8c231f20, 0x49231f20, 0x1c231f20, 0x06231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x04231f20, 0x32231f20, 0x7b231f20, 0xc2231f20, 0xf3231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf3231f20, 0xc2231f20, 0x7b231f20, 0x32231f20, 0x04231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x30231f20, 0x8e231f20, 0xd6231f20, 0xfd231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfd231f20, 0xd6231f20, 0x8e231f20, 0x30231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x0e231f20, 0x73231f20, 0xd8231f20, 0xfb231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfb231f20, 0xd8231f20, 0x73231f20, 0x0e231f20, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00231f20, 0x00000000, 0x29231f20, 0xb6231f20, 0xfb231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfb231f20, 0xb6231f20, 0x29231f20, 0x00000000, 0x00231f20, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x03231f20, 0x4f231f20, 0xdd231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xdd231f20, 0x4f231f20, 0x03231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x0a231f20, 0x72231f20, 0xf4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf4231f20, 0x72231f20, 0x0a231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10231f20, 0x84231f20, 0xf5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff1e191a, 0xff1e1a1b, 0xff211c1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf5231f20, 0x84231f20, 0x10231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x0a231f20, 0x84231f20, 0xf5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1c1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff1b1718, 0xff1b1718, 0xff1c1819, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff393637, 0xff7c7a7a, 0xff827f80, 0xff7d7b7c, 0xff494647, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf5231f20, 0x84231f20, 0x0a231f20, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x03231f20, 0x74231f20, 0xf5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff3b3839, 0xff585656, 0xff5a5757, 0xff5a5757, 0xff5b5858, 0xff4b4949, 0xff272325, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff464243, 0xff5a5858, 0xff5a5758, 0xff565253, 0xff312d2e, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff575355, 0xffececec, 0xfff7f7f7, 0xffededed, 0xff7a7878, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf5231f20, 0x74231f20, 0x03231f20, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x51231f20, 0xf4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff807d7e, 0xfff1f1f1, 0xfff6f6f6, 0xfff6f6f6, 0xfff9f9f9, 0xffe1e0e0, 0xff565354, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffa9a7a7, 0xfff6f6f6, 0xfff6f6f6, 0xffe6e6e6, 0xff575455, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf4231f20, 0x51231f20, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2a231f20, 0xde231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff848283, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa6a4a5, 0xff2c2729, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffedecec, 0xff5a5758, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff595556, 0xfff6f6f6, 0xffffffff, 0xfff6f6f6, 0xff7e7c7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xde231f20, 0x2a231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x0e231f20, 0xb5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xff585556, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff524f50, 0xffdbdbdb, 0xffe5e4e5, 0xffdcdbdb, 0xff737071, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xb5231f20, 0x0e231f20, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, + 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x73231f20, 0xfb231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb4b3b4, 0xff282425, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2e2b2c, 0xff4f4c4d, 0xff524f50, 0xff504d4e, 0xff363334, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfb231f20, 0x73231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, + 0x00000000, 0x00231f20, 0x00000000, 0x30231f20, 0xd8231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfffdfdfd, 0xffededed, 0xfffdfdfd, 0xffffffff, 0xfff9fafa, 0xff646162, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff1e1a1b, 0xff1e1a1b, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xd8231f20, 0x30231f20, 0x00000000, 0x00231f20, 0x00000000, + 0x00231f20, 0x00000000, 0x05231f20, 0x8e231f20, 0xfa231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xffadacac, 0xffdbdbdb, 0xffffffff, 0xffffffff, 0xffc8c7c7, 0xff2b2628, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1c1718, 0xff1a1617, 0xff1b1718, 0xff1b1718, 0xff1a1617, 0xff1d191a, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1d191a, 0xff1a1617, 0xff1b1718, 0xff1a1617, 0xff1b1718, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1c1819, 0xff1a1517, 0xff1b1718, 0xff1a1517, 0xff1c1719, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff1c1819, 0xff191516, 0xff1a1617, 0xff1a1617, 0xff191516, 0xff1b1718, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1c1819, 0xff1a1617, 0xff1b1718, 0xff1a1617, 0xff1a1617, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1d191a, 0xff1a1516, 0xff1a1617, 0xff1b1718, 0xff191516, 0xff1b1718, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfa231f20, 0x8e231f20, 0x05231f20, 0x00000000, 0x00231f20, + 0x00000000, 0x00000000, 0x32231f20, 0xd8231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff7f7f7, 0xff858384, 0xff949393, 0xfffdfefd, 0xffffffff, 0xfffdfdfd, 0xff767475, 0xff1c1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff252122, 0xff413e3f, 0xff6e6c6c, 0xff8f8d8d, 0xff9c9b9b, 0xff9b9999, 0xff898787, 0xff646061, 0xff383435, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff292526, 0xff393637, 0xff3b3839, 0xff3a3738, 0xff2c292a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231e1f, 0xff2f2b2c, 0xff3a3738, 0xff3b3839, 0xff393536, 0xff282425, 0xff221e1f, 0xff201c1d, 0xff302d2e, 0xff5f5c5d, 0xff8a8788, 0xff9b9a9a, 0xff989797, 0xff7c7a7a, 0xff454243, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff373334, 0xff666465, 0xff8e8c8c, 0xff9b999a, 0xff918f8f, 0xff716f6f, 0xff3b3839, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff231f20, 0xff302c2d, 0xff4e4a4b, 0xff706e6f, 0xff8b898a, 0xff999797, 0xff999797, 0xff8e8c8d, 0xff767374, 0xff494646, 0xff2b2728, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff332f30, 0xff636061, 0xff8d8b8b, 0xff9c9a9b, 0xff989697, 0xff807f7f, 0xff524f4f, 0xff2a2627, 0xff211d1e, 0xff221e1f, 0xff2e2a2b, 0xff3b3738, 0xff3b3839, 0xff383536, 0xff292526, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff393636, 0xff646162, 0xff878485, 0xff989697, 0xff9d9b9b, 0xff908e8e, 0xff716e6e, 0xff423f3f, 0xff252122, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xd8231f20, 0x32231f20, 0x00000000, 0x00000000, + 0x00000000, 0x06231f20, 0x7c231f20, 0xfc231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7b797a, 0xff4a4647, 0xffdedede, 0xffffffff, 0xffffffff, 0xffd1d0d1, 0xff363233, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff252122, 0xff504e4f, 0xffaaa9a9, 0xffececed, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe1e0e1, 0xff949293, 0xff403c3d, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1718, 0xff555253, 0xffe9e9e9, 0xfff4f4f4, 0xffebebeb, 0xff787676, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff8b898a, 0xffeff0f0, 0xfff3f3f3, 0xffdddddd, 0xff4c4a4a, 0xff242021, 0xff716f6f, 0xffd4d2d3, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xff9c9b9c, 0xff393536, 0xff201c1d, 0xff231f20, 0xff211d1e, 0xff322f30, 0xff848282, 0xffe0dede, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff838182, 0xff2f2b2c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2b2728, 0xff605d5e, 0xffa2a1a1, 0xffd8d7d7, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff6f5f5, 0xffc4c3c3, 0xff6c696a, 0xff2d292a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2f2b2c, 0xff7c797a, 0xffd9d8d8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff8f7f7, 0xffbdbcbc, 0xff5a5757, 0xff1f1a1b, 0xff817e7f, 0xfff2f1f1, 0xfff3f2f2, 0xffdedede, 0xff524e4f, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff242021, 0xff474344, 0xff9a9899, 0xffe3e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffeeedee, 0xffaaa9a9, 0xff4e4b4b, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfc231f20, 0x7c231f20, 0x06231f20, 0x00000000, + 0x00000000, 0x1c231f20, 0xc3231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7e7c7d, 0xff1d181a, 0xff9a9898, 0xfffbfbfb, 0xffffffff, 0xfff9f9f9, 0xff848182, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff272324, 0xff7d7a7b, 0xffe9e9e9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xfffdfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd4d3d4, 0xff605d5e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff595656, 0xfff7f7f7, 0xffffffff, 0xfff7f7f7, 0xff7e7c7d, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff939191, 0xfffbfbfb, 0xffffffff, 0xffeae9e9, 0xff5d5a5b, 0xff8c8a8a, 0xfff8f7f7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbab9b9, 0xff3b3738, 0xff1b1718, 0xff3c3939, 0xffb3b2b2, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xff959293, 0xff2b2728, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff484445, 0xffe6e6e6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfefe, 0xfffafafa, 0xfffcfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff8f8f8, 0xff9d9b9b, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff383435, 0xffa7a6a6, 0xfffefdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xfff8f8f8, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffe7e7e7, 0xff696767, 0xff8b8989, 0xfffcfcfc, 0xffffffff, 0xffeaeaea, 0xff555253, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff262222, 0xff727071, 0xffdedede, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e4e4, 0xff757273, 0xff252122, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xc3231f20, 0x1c231f20, 0x00000000, + 0x01231f20, 0x49231f20, 0xf4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff807d7e, 0xff161213, 0xff474444, 0xffdfdedf, 0xffffffff, 0xffffffff, 0xffd1d0d0, 0xff413d3e, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff828080, 0xfff4f3f3, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xffcecdcd, 0xff9c9a9a, 0xff868585, 0xff8a8787, 0xffa6a4a4, 0xffdddcdc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe0dfe0, 0xff5f5c5d, 0xff1f1b1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffececec, 0xffadabac, 0xfff2f2f2, 0xffffffff, 0xffd3d2d2, 0xffa6a4a5, 0xff908e8f, 0xff929191, 0xffb4b3b3, 0xffeeeeee, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xff959394, 0xff363333, 0xffb0afb0, 0xffffffff, 0xfffafafa, 0xffc9c8c9, 0xffa09e9f, 0xff8f8c8d, 0xff969494, 0xffbdbcbc, 0xfff6f5f6, 0xffffffff, 0xffffffff, 0xfff7f7f7, 0xff686667, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff4b4949, 0xffefeeef, 0xffffffff, 0xffdddddd, 0xffbab9ba, 0xff9e9c9d, 0xff8b898a, 0xff848182, 0xff868484, 0xff999797, 0xffbebdbe, 0xfff0f0ef, 0xffffffff, 0xffffffff, 0xffffffff, 0xff949293, 0xff262223, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2e2a2b, 0xff9f9d9e, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xfff1f1f1, 0xffb7b5b6, 0xff8d8b8c, 0xff7f7c7d, 0xff878586, 0xffaaa8a9, 0xffe2e1e2, 0xffffffff, 0xffdbdbdb, 0xffbcbbbb, 0xfffcfcfc, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff221e1f, 0xff807e7e, 0xfff0f0f0, 0xffffffff, 0xffffffff, 0xfff7f7f7, 0xffcac9c9, 0xff9e9c9c, 0xff888686, 0xff858282, 0xff959393, 0xffc0bfbf, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xffecebeb, 0xff726f70, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf4231f20, 0x49231f20, 0x01231f20, + 0x04231f20, 0x8c231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff807d7e, 0xff1b1718, 0xff1e191a, 0xff8f8d8e, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xff888687, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d1a1b, 0xff5c5959, 0xffe6e5e5, 0xffffffff, 0xffffffff, 0xfff6f5f6, 0xff979495, 0xff3f3c3c, 0xff272424, 0xff221f20, 0xff221e1f, 0xff2b2728, 0xff4d494a, 0xffb6b5b5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc9c8c8, 0xff3f3b3c, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xfffcfcfc, 0xfff9f9f9, 0xfff4f4f4, 0xff918e8f, 0xff413e3f, 0xff2b2728, 0xff242021, 0xff242021, 0xff2d2a2a, 0xff6c6a6a, 0xffeae9e9, 0xffffffff, 0xffffffff, 0xffdcdcdc, 0xffb1afb0, 0xfff7f7f7, 0xffececec, 0xff7b797a, 0xff393536, 0xff292526, 0xff231f20, 0xff262223, 0xff343031, 0xff7f7d7d, 0xfff4f4f4, 0xffffffff, 0xffffffff, 0xffc1c0c0, 0xff292526, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff454243, 0xffb1afb0, 0xff7e7b7c, 0xff494647, 0xff332f30, 0xff292526, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff272224, 0xff343031, 0xff656263, 0xffd1d0d0, 0xffffffff, 0xffffffff, 0xfff2f2f2, 0xff545152, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff6c6a6b, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xffe4e3e4, 0xff6b6869, 0xff302c2d, 0xff241f20, 0xff221e1f, 0xff221e1f, 0xff2c2829, 0xff565253, 0xffc2c2c2, 0xffffffff, 0xfff8f8f8, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191b, 0xff666264, 0xffe9e8e9, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff7b7879, 0xff3b3738, 0xff282425, 0xff221e1f, 0xff221e1f, 0xff252122, 0xff353132, 0xff777575, 0xffe9e9e9, 0xffffffff, 0xffffffff, 0xffd5d4d4, 0xff4a4647, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0x8c231f20, 0x04231f20, + 0x11231f20, 0xc4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff1f1b1c, 0xff383535, 0xffdcdbdb, 0xffffffff, 0xffffffff, 0xffd3d3d3, 0xff434041, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2c2829, 0xffb0afaf, 0xfffefefe, 0xffffffff, 0xfffcfcfc, 0xff9e9d9d, 0xff282525, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff383435, 0xffc7c7c7, 0xffffffff, 0xffffffff, 0xfff8f8f8, 0xff848383, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffffffff, 0xfffaf9f9, 0xff918f8f, 0xff2a2627, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff8a8889, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xfffdfdfd, 0xfff6f7f7, 0xff6b6969, 0xff211d1e, 0xff221d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2a2727, 0xffa3a1a1, 0xffffffff, 0xffffffff, 0xfff6f6f6, 0xff4d4a4b, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff282425, 0xff302c2d, 0xff242021, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231e1f, 0xff221e1f, 0xff221e1f, 0xff524f4f, 0xffdddddd, 0xffffffff, 0xffffffff, 0xffa5a4a4, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff312d2e, 0xffbfbebe, 0xffffffff, 0xffffffff, 0xfff5f4f5, 0xff625f60, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff4b4849, 0xffd5d4d4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff363333, 0xffc1c1c1, 0xffffffff, 0xffffffff, 0xffededed, 0xff666464, 0xff211d1e, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1f1b1c, 0xff706e6e, 0xfff9f9f9, 0xffffffff, 0xfffdfdfd, 0xff908e8f, 0xff272223, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xc4231f20, 0x11231f20, + 0x2e231f20, 0xf0231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff1c1819, 0xff7d7b7b, 0xffffffff, 0xffffffff, 0xffffffff, 0xff8a8788, 0xff252122, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff535051, 0xffe2e1e1, 0xffffffff, 0xffffffff, 0xffd9d9d9, 0xff393636, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff5b5859, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff383435, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffffffff, 0xffcbcacb, 0xff3b3839, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff3f3b3c, 0xffeeedee, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa6a5a5, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221f1f, 0xff615e5e, 0xfff2f2f2, 0xffffffff, 0xffffffff, 0xff797677, 0xff1b1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff262223, 0xff939191, 0xffffffff, 0xffffffff, 0xffdedede, 0xff2f2b2c, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff575354, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xffa1a09f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201d1e, 0xff817f81, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211c1e, 0xff6c696a, 0xffefefef, 0xffffffff, 0xffffffff, 0xff989797, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xffb1b0b0, 0xffffffff, 0xffffffff, 0xffcccbcb, 0xff3b3738, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf0231f20, 0x2e231f20, + 0x62231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff211d1e, 0xff2f2c2d, 0xffcdcdcd, 0xffffffff, 0xffffffff, 0xffdddddd, 0xff423f40, 0xff211d1e, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff7d7b7b, 0xfff9f9f9, 0xffffffff, 0xffffffff, 0xff8f8e8e, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffbdbcbd, 0xffffffff, 0xffffffff, 0xffeaeaea, 0xff565353, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xfff9f9f9, 0xff918f90, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff282425, 0xffcdcccc, 0xffffffff, 0xffffffff, 0xfff7f7f7, 0xff5d5b5b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff474444, 0xffdad9d9, 0xffffffff, 0xffffffff, 0xff999898, 0xff1b1618, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff1f1b1c, 0xff696768, 0xfff0f0ef, 0xffffffff, 0xfff5f5f5, 0xff4c494a, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff8a8989, 0xffffffff, 0xffffffff, 0xfff7f7f7, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff454242, 0xffdedede, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2a2627, 0xffa19fa0, 0xffffffff, 0xffffffff, 0xffeeeded, 0xff444040, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff626060, 0xffffffff, 0xffffffff, 0xffeeeeee, 0xff575354, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0x62231f20, + 0x8e231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff6e6b6b, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xff959394, 0xff211d1e, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2a2627, 0xffa09f9f, 0xffffffff, 0xffffffff, 0xfff8f8f8, 0xff535051, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1a1617, 0xff7e7b7c, 0xffffffff, 0xffffffff, 0xfffefefe, 0xff737172, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xfff2f2f1, 0xff686666, 0xff1c1719, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xffb7b5b5, 0xffffffff, 0xffffffff, 0xffdbdbdb, 0xff423f3f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff3b3738, 0xffcecdcd, 0xffffffff, 0xffffffff, 0xffa7a6a6, 0xff1e1a1b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1f1b1c, 0xff2c2729, 0xff3b3839, 0xff4c4849, 0xff575455, 0xff5c595a, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5d5a5b, 0xff8b898a, 0xffefefef, 0xffffffff, 0xfffbfbfb, 0xff646162, 0xff1b1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xffb2b1b2, 0xffffffff, 0xffffffff, 0xffd5d4d4, 0xff373435, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff272324, 0xffbebdbd, 0xfffefefe, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff383435, 0xffc7c7c7, 0xffffffff, 0xffffffff, 0xffc3c2c2, 0xff201c1d, 0xff1d191a, 0xff1f1b1c, 0xff1e1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1c1819, 0xff3a3738, 0xffeeeeee, 0xffffffff, 0xffffffff, 0xff706e6e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0x8e231f20, + 0xb4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff312e2e, 0xffbcbbbb, 0xffffffff, 0xffffffff, 0xffebebeb, 0xff4a4748, 0xff1e1a1b, 0xff221e1f, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff302d2e, 0xffb9b8b9, 0xffffffff, 0xffffffff, 0xffe6e6e6, 0xff343031, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff565254, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xff8f8c8d, 0xff252122, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffededed, 0xff575454, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201d1e, 0xffafadae, 0xffffffff, 0xffffffff, 0xffcac9ca, 0xff373334, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff383535, 0xffc9c8c8, 0xffffffff, 0xffffffff, 0xffafadae, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff393536, 0xff777475, 0xffacaaaa, 0xffcccacb, 0xffdddddc, 0xffe5e5e5, 0xffe9e9e9, 0xffebebeb, 0xffebebeb, 0xffebebeb, 0xffebeaeb, 0xfff0f0f0, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xff706d6e, 0xff191516, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2c292a, 0xffcccbcc, 0xffffffff, 0xffffffff, 0xffbab9b9, 0xff2b2829, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xffa2a0a0, 0xfffcfcfc, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff484445, 0xffdcdcdc, 0xffffffff, 0xffffffff, 0xffc8c7c8, 0xff716f6f, 0xff747172, 0xff747273, 0xff747273, 0xff757273, 0xff757273, 0xff757273, 0xff767373, 0xff757374, 0xff767374, 0xff757273, 0xff828081, 0xffeeedee, 0xffffffff, 0xffffffff, 0xff858283, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xb4231f20, + 0xd4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff686566, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xffabaaaa, 0xff211c1d, 0xff211d1e, 0xff221e1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff343031, 0xffc6c5c5, 0xffffffff, 0xffffffff, 0xffd4d4d4, 0xff2a2627, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff413e3f, 0xfff6f6f5, 0xffffffff, 0xffffffff, 0xff9f9e9e, 0xff272223, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff524e4f, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadabac, 0xffffffff, 0xffffffff, 0xffc4c3c4, 0xff322f30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff5d5a5b, 0xffc8c7c8, 0xfff3f3f3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff322e2f, 0xffdbdada, 0xffffffff, 0xffffffff, 0xffaeacac, 0xff282425, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d191a, 0xff908e8f, 0xfffbfbfb, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff545151, 0xffe9e9e9, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xfff1f1f1, 0xfff2f1f2, 0xfff2f1f2, 0xfff2f1f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff3f3f3, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xff8d8c8c, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xd4231f20, + 0xe5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff322e2f, 0xffb6b5b5, 0xffffffff, 0xffffffff, 0xfff3f3f3, 0xff5a5858, 0xff1a1617, 0xff221f1f, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373434, 0xffcac9c9, 0xffffffff, 0xffffffff, 0xffcbcacb, 0xff282425, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff3b3738, 0xfff2f1f2, 0xffffffff, 0xffffffff, 0xffa5a4a4, 0xff262223, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff565454, 0xffe2e1e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff3f3f3, 0xffd1d0d0, 0xffb3b1b1, 0xffa19f9f, 0xff989696, 0xff939191, 0xff929091, 0xff918f90, 0xffafaeaf, 0xfff4f4f4, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff322f30, 0xffdfdede, 0xffffffff, 0xffffffff, 0xffa9a7a8, 0xff282425, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1c1819, 0xff8b8989, 0xfffbfbfb, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff595758, 0xfff0efef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff929091, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xe5231f20, + 0xf2231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff676566, 0xffececec, 0xffffffff, 0xffffffff, 0xffb9b8b8, 0xff262223, 0xff201c1c, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff353233, 0xffc7c6c7, 0xffffffff, 0xffffffff, 0xffd2d1d1, 0xff2a2627, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff3f3b3c, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xffa2a0a1, 0xff262223, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2e2a2b, 0xffc2c1c1, 0xffffffff, 0xffffffff, 0xfff5f4f4, 0xff9b9a9a, 0xff464344, 0xff2a2627, 0xff221e1f, 0xff1d191a, 0xff1c1819, 0xff1c1819, 0xff1c1819, 0xff1a1516, 0xff5c595a, 0xffe9e8e9, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff302c2d, 0xffd7d7d7, 0xffffffff, 0xffffffff, 0xffb1b0b0, 0xff292526, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e191a, 0xff959393, 0xfffcfcfc, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff585556, 0xffeeedee, 0xffffffff, 0xffffffff, 0xffc1c0c0, 0xff848282, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff898788, 0xff8b898a, 0xff565253, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf2231f20, + 0xfd231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff2c2829, 0xffb6b5b5, 0xfffefefe, 0xffffffff, 0xfff1f1f1, 0xff666364, 0xff1c1819, 0xffafadad, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff312e2f, 0xffbdbcbd, 0xffffffff, 0xffffffff, 0xffe1e0e1, 0xff302c2d, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff4f4b4c, 0xfffaf9f9, 0xffffffff, 0xffffffff, 0xff949292, 0xff262223, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff5d5a5b, 0xfffffefe, 0xffffffff, 0xfffefefe, 0xff8c898a, 0xff201c1d, 0xff1d191a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff6a6768, 0xffefefef, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2a2627, 0xffc6c5c5, 0xffffffff, 0xffffffff, 0xffc1c0c0, 0xff2e2a2b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xffa9a8a8, 0xfffdfdfd, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff4f4c4d, 0xffe4e4e4, 0xffffffff, 0xffffffff, 0xff999797, 0xff100c0d, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff181415, 0xff171314, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfd231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff5f5d5d, 0xffeeeeee, 0xffffffff, 0xfffefefe, 0xffb9b7b8, 0xff302d2e, 0xffacabab, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2c2829, 0xffa8a6a6, 0xffffffff, 0xffffffff, 0xfff4f4f4, 0xff494647, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff726f70, 0xffffffff, 0xffffffff, 0xffffffff, 0xff7a7878, 0xff241f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff908e8e, 0xffffffff, 0xffffffff, 0xffdcdbdb, 0xff434041, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff7f7c7d, 0xfff9f9f9, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xffa8a6a7, 0xffffffff, 0xffffffff, 0xffe0dfdf, 0xff3f3b3c, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff2d2a2b, 0xffc8c8c8, 0xfffefefe, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff3f3c3d, 0xffd3d2d2, 0xffffffff, 0xffffffff, 0xffc7c5c6, 0xff262223, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242021, 0xffaeacad, 0xffffffff, 0xffffffff, 0xffeeeeee, 0xff6c6a6a, 0xffadabab, 0xfffdfdfd, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242121, 0xff868484, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xff7e7b7c, 0xff191516, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xffacaaab, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xff5d595a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xffa7a5a6, 0xffffffff, 0xffffffff, 0xffc6c5c5, 0xff343132, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2b2829, 0xffa9a8a8, 0xffffffff, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff7b7878, 0xffffffff, 0xffffffff, 0xfffefefe, 0xff696667, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff555153, 0xffe6e5e5, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2f2b2c, 0xffb2b1b1, 0xffffffff, 0xffffffff, 0xfff1f1f1, 0xff4a4748, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff4e4a4b, 0xffefeeee, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xffc1c0c1, 0xfffcfcfc, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff5d5a5b, 0xffe9e8e9, 0xffffffff, 0xffffffff, 0xffc7c6c6, 0xff2b2627, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff464243, 0xffebebeb, 0xffffffff, 0xffffffff, 0xffcfcece, 0xff3e3a3b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xffa8a6a6, 0xffffffff, 0xffffffff, 0xffcbcaca, 0xff383435, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff4e4b4b, 0xffe3e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff484546, 0xffe8e8e8, 0xffffffff, 0xffffffff, 0xffc3c2c2, 0xff292627, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff262223, 0xff9e9c9c, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff7f7c7d, 0xfff8f7f8, 0xffffffff, 0xffffffff, 0xffa3a1a1, 0xff1e1a1c, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff9c9a9a, 0xffffffff, 0xffffffff, 0xfff5f5f5, 0xfff0f0f0, 0xfffefefe, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff343031, 0xffbfbebe, 0xffffffff, 0xffffffff, 0xfff8f8f8, 0xff807e7f, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff262223, 0xffafadae, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xff949293, 0xff272324, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff949192, 0xffffffff, 0xffffffff, 0xffecebec, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2c2829, 0xffb1afaf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2a2627, 0xffa6a4a5, 0xffffffff, 0xffffffff, 0xffffffff, 0xff939192, 0xff201c1d, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff211d1e, 0xff6c6a6a, 0xffebebeb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff444041, 0xffd5d4d4, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xff716e6f, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff221e1f, 0xff2d292a, 0xff252122, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff403d3e, 0xffe4e4e4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff706d6e, 0xfff0eff0, 0xffffffff, 0xffffffff, 0xffe9e9e9, 0xff706e6f, 0xff282425, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff302c2d, 0xff969494, 0xfffafaf9, 0xffffffff, 0xffffffff, 0xffdad9d9, 0xff4d494a, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xfffdfcfc, 0xffffffff, 0xffffffff, 0xffbbb9b9, 0xff3a3738, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff373334, 0xffa5a3a3, 0xffffffff, 0xfffefefe, 0xfffefefe, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff534f50, 0xffe0dfe0, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xffa2a0a1, 0xff433f40, 0xff2a2627, 0xff252122, 0xff282425, 0xff3a3738, 0xff807d7e, 0xffeaeaea, 0xfffefefe, 0xffeeeded, 0xfffefefe, 0xffffffff, 0xffe9e8e8, 0xff555252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201b1c, 0xff7e7b7c, 0xfff3f3f3, 0xffffffff, 0xffffffff, 0xffe9e9e9, 0xff7c7a7a, 0xff302c2d, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff242021, 0xff2e2a2b, 0xff4f4c4d, 0xff9f9e9f, 0xff9c9a9b, 0xff2f2b2c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231e1f, 0xff1e1b1c, 0xff848282, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xff7f7d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff8b898a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffececec, 0xff595658, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff272425, 0xffa3a1a1, 0xfffafafa, 0xffffffff, 0xffffffff, 0xfff3f2f3, 0xffaaa9a9, 0xff6c6a6a, 0xff585556, 0xff5b5859, 0xff797677, 0xffc2c1c1, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffefeeee, 0xff7b7979, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xfff5f5f5, 0xffffffff, 0xfff5f5f5, 0xff7d7b7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff929090, 0xfffafafa, 0xffffffff, 0xffe9e9e9, 0xff514d4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffadacad, 0xffffffff, 0xffffffff, 0xffc4c3c3, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373435, 0xffc8c7c7, 0xffffffff, 0xffffffff, 0xffb0aeaf, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2e2a2b, 0xffc4c3c3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcccccc, 0xff7b7878, 0xff575354, 0xff524e4f, 0xff5c595a, 0xff848282, 0xffd2d1d1, 0xffffffff, 0xfff5f5f5, 0xffcbcbcb, 0xffeeeeee, 0xffffffff, 0xffffffff, 0xff777475, 0xff181415, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff242021, 0xff7c797a, 0xffedeced, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xffbebdbe, 0xffaeadae, 0xffb8b7b7, 0xffdddcdc, 0xffffffff, 0xffffffff, 0xffbfbebf, 0xffa5a3a4, 0xfffbfbfb, 0xffffffff, 0xffe9e8e8, 0xff545252, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff282325, 0xff9e9d9d, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xffc6c5c5, 0xff888687, 0xff636161, 0xff585556, 0xff575455, 0xff5e5b5c, 0xff726f70, 0xff959293, 0xffc3c2c2, 0xfff4f3f3, 0xffffffff, 0xffb5b4b4, 0xff2f2b2c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff858383, 0xfff8f7f7, 0xffffffff, 0xfff9f9f9, 0xff807d7e, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff3f3c3c, 0xffd5d4d4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffededed, 0xff5a5758, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff332f30, 0xffa4a3a3, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xfff0f1f1, 0xfff3f3f3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffeae9e9, 0xff858383, 0xff252222, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585656, 0xfff6f6f6, 0xffffffff, 0xfff6f6f6, 0xff7e7c7c, 0xff201d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff939191, 0xfffbfbfb, 0xffffffff, 0xffeaeaea, 0xff524e4f, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffaeacad, 0xffffffff, 0xffffffff, 0xffc5c4c4, 0xff322e30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff383435, 0xffc9c8c9, 0xffffffff, 0xffffffff, 0xffb1afb0, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff585556, 0xffdedddd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xffe9e9e9, 0xfff4f4f4, 0xffffffff, 0xffffffff, 0xfffafafa, 0xffa4a3a3, 0xff706d6e, 0xffe6e6e6, 0xffffffff, 0xffffffff, 0xff787576, 0xff181415, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff272324, 0xff7e7b7d, 0xffdfdede, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xffc2c1c1, 0xff494747, 0xff8a8788, 0xfffbfbfb, 0xffffffff, 0xffe7e6e6, 0xff514e4f, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff2c2829, 0xff918f8f, 0xffe9e9e9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffafafa, 0xfff0f0f0, 0xffefefef, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffadabac, 0xff2e2a2b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff828181, 0xfff5f5f5, 0xfffcfcfc, 0xfff7f7f7, 0xff7e7b7c, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xff817f7f, 0xfff8f8f8, 0xfffdfefe, 0xfffcfcfc, 0xfffcfdfc, 0xffebeaeb, 0xff595656, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201b1d, 0xff2e2a2b, 0xff787575, 0xffcccbcc, 0xfffbfafb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff4f4f3, 0xffbab9b9, 0xff625e5f, 0xff252122, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585555, 0xfff2f2f2, 0xfffefdfd, 0xfff3f4f4, 0xff7c7a7a, 0xff211c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff908e8f, 0xfff9f8f8, 0xfffcfcfc, 0xffe7e6e6, 0xff504d4e, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211c1d, 0xffacaaaa, 0xffffffff, 0xffffffff, 0xffc3c2c2, 0xff322e2f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff383435, 0xffc7c5c6, 0xffffffff, 0xffffffff, 0xffafadad, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff232020, 0xff5b5959, 0xffc0bfbf, 0xfffafafa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdddcdd, 0xff888686, 0xff302c2d, 0xff5a5657, 0xffe7e7e7, 0xfffefefe, 0xfffffffe, 0xff767475, 0xff191415, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242021, 0xff535051, 0xff9d9b9b, 0xffd2d1d2, 0xffeeeeee, 0xfff9fafa, 0xfff7f7f7, 0xffe7e7e7, 0xffc4c3c4, 0xff858384, 0xff3d3a3b, 0xff1b1819, 0xff989797, 0xfffcfcfc, 0xffffffff, 0xffe1e0e1, 0xff4a4647, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff272324, 0xff5e5b5c, 0xffafadae, 0xffeaeaea, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff5f5f5, 0xffd1d1d1, 0xff9f9d9e, 0xff565454, 0xff262223, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff3b3738, 0xff575455, 0xff585556, 0xff575556, 0xff393637, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff312d2e, 0xff545152, 0xff595657, 0xff585556, 0xff585556, 0xff545152, 0xff302c2d, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff242021, 0xff3a3637, 0xff656263, 0xff949292, 0xffb1b0b0, 0xffbdbcbc, 0xffbcbaba, 0xffacabab, 0xff898787, 0xff595656, 0xff322e2f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff302c2d, 0xff565353, 0xff595556, 0xff565454, 0xff393536, 0xff221f1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff3e3a3b, 0xff585556, 0xff585556, 0xff535051, 0xff2e2b2b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff454142, 0xff5a5859, 0xff5a5758, 0xff4a4748, 0xff272324, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff282425, 0xff4b4748, 0xff595657, 0xff5a5758, 0xff454243, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff363233, 0xff666464, 0xff9a9798, 0xffb4b3b4, 0xffbdbcbc, 0xffb7b6b6, 0xffa09e9e, 0xff737172, 0xff434041, 0xff262223, 0xff211d1e, 0xff312d2e, 0xff535051, 0xff595656, 0xff595556, 0xff383435, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff292526, 0xff3b3738, 0xff4e4b4c, 0xff565454, 0xff555253, 0xff4a4647, 0xff353132, 0xff252122, 0xff1f1b1c, 0xff231e1f, 0xffb5b4b4, 0xfffefefe, 0xffffffff, 0xffd5d4d4, 0xff3a3738, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff2e2a2b, 0xff4d4a4a, 0xff787676, 0xff9e9c9d, 0xffb5b4b4, 0xffbdbcbd, 0xffbababa, 0xffb1b0b1, 0xff9c9a9b, 0xff7d7b7b, 0xff575455, 0xff3b3838, 0xff292526, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff221f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff252122, 0xff292526, 0xff272425, 0xff231f20, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211e1f, 0xff211d1e, 0xff221e1f, 0xff221f1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221f1f, 0xff221d1f, 0xff221e1f, 0xff252122, 0xff282425, 0xff262223, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff3c393a, 0xffd5d5d5, 0xffffffff, 0xfffefefe, 0xffbebdbe, 0xff272324, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff262223, 0xff292526, 0xff272425, 0xff242021, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff7b7879, 0xfff4f3f3, 0xffffffff, 0xfffbfbfb, 0xff8e8b8c, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff322e2f, 0xff312d2e, 0xff211d1e, 0xff1f1b1c, 0xff201c1d, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff211d1e, 0xff1f1b1c, 0xff221e1f, 0xff555253, 0xffd6d5d5, 0xffffffff, 0xffffffff, 0xffeaeaea, 0xff4c484a, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff727070, 0xffafadae, 0xff777475, 0xff494647, 0xff302c2d, 0xff231f20, 0xff221e1f, 0xff211e1f, 0xff2a2627, 0xff434040, 0xff7b7879, 0xffd5d4d4, 0xffffffff, 0xffffffff, 0xffffffff, 0xff9b9999, 0xff201d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff838181, 0xfffafafa, 0xfff4f3f4, 0xffdbd9da, 0xffc0bfbf, 0xffacabab, 0xffa3a2a2, 0xffa8a6a6, 0xffb8b6b7, 0xffd5d4d4, 0xfff4f4f4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbcbbbc, 0xff353133, 0xff201b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff828080, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xfffcfcfc, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfefe, 0xffacaaab, 0xff3c3839, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff434041, 0xff939191, 0xffcdcccd, 0xffedecec, 0xfff7f7f7, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xffebebeb, 0xffbcbbbc, 0xff696666, 0xff2a2627, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1d191a, 0xff272324, 0xff3d3a3b, 0xff5a5758, 0xff6e6c6c, 0xff797677, 0xff7b797a, 0xff737172, 0xff605d5e, 0xff3f3b3b, 0xff231f20, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1e1a1b, 0xff1b1718, 0xff191516, 0xff181415, 0xff181415, 0xff181415, 0xff1b1718, 0xff1f1a1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2d292a, 0xff5e5c5d, 0xff696566, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696666, 0xff696566, 0xff696666, 0xff514e4f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff787576, 0xffe8e7e7, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f3f3, 0xfff4f4f4, 0xffd1d1d1, 0xff464344, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff4b4849, 0xffd6d6d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xffa2a1a1, 0xff292526, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2e2a2b, 0xffaaa8a9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefdfd, 0xfff7f6f7, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfffafafa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffeaeaea, 0xff656263, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff737071, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e3e4, 0xff686566, 0xff423e3f, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff454142, 0xff464344, 0xffa19f9f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff383435, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff464344, 0xffdedddd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdedede, 0xff484546, 0xff1a1617, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1e1a1b, 0xff201c1d, 0xff8d8a8b, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xff828080, 0xff252122, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2b2829, 0xffb4b2b3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdfdedf, 0xff4c494a, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff908d8d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff4b4748, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2e2a2b, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464243, 0xff464244, 0xff423f40, 0xff2b2728, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d191a, 0xff868485, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdfdedf, 0xff4c494a, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff8f8d8d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb4b3b3, 0xff272425, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff838181, 0xffdadada, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d8, 0xffd9d8d9, 0xffcfcecf, 0xff676464, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1f1a1b, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1e1a1b, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff585556, 0xffeaeaea, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdfdedf, 0xff4c494a, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff8f8d8d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xff787677, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff332f30, 0xffd7d5d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa9a7a8, 0xff2b2728, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2e2b2b, 0xff413d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff423e3f, 0xff383535, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff343132, 0xffc5c3c3, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdfdedf, 0xff4c494a, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff8f8d8d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdfdfdf, 0xff423e3f, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff737072, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdcdbdb, 0xff4a4748, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211e1f, 0xff969394, 0xfffaf9f9, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff2f2f2, 0xfff8f8f8, 0xffcdcccc, 0xff332f30, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff252021, 0xff908e8e, 0xfff9f8f8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdfdedf, 0xff4c484a, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff242021, 0xff8f8d8d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffa8a7a7, 0xff262122, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2b2728, 0xffbbb9b9, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xff7d7b7b, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff312d2e, 0xffd6d5d5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff8f7f7, 0xff4c494a, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff5d5a5b, 0xffe5e5e5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdfdedf, 0xff4c4849, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff242021, 0xff8f8d8e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffefeeef, 0xff696667, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff5a5758, 0xffe8e7e8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbfbdbe, 0xff302c2d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff5b5858, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff787676, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff393536, 0xffc0bebf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffededed, 0xff9b999a, 0xff828080, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848283, 0xff848282, 0xff868383, 0xffc1c0c0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc5c3c4, 0xff3c3839, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff262323, 0xff9a9999, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff3f3f3, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d191a, 0xff9c9b9a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa8a6a6, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221f1f, 0xff231f20, 0xff908e8e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffeffff, 0xfffefefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xfffffefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffafafa, 0xff8a8889, 0xff252122, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff444142, 0xffd4d3d4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff9b999a, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff312d2e, 0xffdfdede, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdad9da, 0xff2c2829, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff676565, 0xfffafafa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffebeaea, 0xff585555, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xff807e7e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffeae9e9, 0xff3d393a, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff332f30, 0xff777575, 0xff7d7b7c, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7c7a7a, 0xff7e7b7c, 0xff757273, 0xff2b2728, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff767374, 0xffa19f9f, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xff9c9a9b, 0xffa09e9f, 0xff615f60, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff272223, 0xff605d5e, 0xff8a8888, 0xff878686, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff878585, 0xff888686, 0xff878485, 0xff3e3a3b, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1b1718, 0xff1c1819, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e191a, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1e1a1b, 0xff221f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d191a, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff1a1617, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff201b1c, 0xff242021, 0xff302c2d, 0xff3e3b3c, 0xff4a4747, 0xff555253, 0xff5e5b5b, 0xff5f5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5f5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5e5c5d, 0xff5f5c5d, 0xff5e5a5b, 0xff555252, 0xff4a4647, 0xff3e3a3b, 0xff2f2b2c, 0xff231f20, 0xff201c1d, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1d191a, 0xff2d2a2b, 0xff575455, 0xff848283, 0xffb4b3b4, 0xffd2d1d2, 0xffdfdede, 0xffeaeaea, 0xfff4f4f4, 0xfff6f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff5f5f5, 0xfff4f3f4, 0xffeaeaea, 0xffdeddde, 0xffd0d0d0, 0xffb2b1b1, 0xff818080, 0xff555253, 0xff2b2829, 0xff1d191a, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e191a, 0xff242021, 0xff605d5e, 0xffb5b3b3, 0xffe5e5e5, 0xfffaf9fa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xffe4e3e4, 0xffb0afb0, 0xff5c5959, 0xff231e20, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff3f3b3c, 0xffaaa9a9, 0xffececec, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xffebeaea, 0xffa6a4a6, 0xff3c3839, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff4e4a4b, 0xffcccacb, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xffc8c7c7, 0xff4b4848, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff585556, 0xffdbdadb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd8d7d8, 0xff555253, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1c1c, 0xff484546, 0xffd7d6d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd4d3d3, 0xff474344, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff383434, 0xffc3c2c2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff373333, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff9c9a9a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff9a9898, 0xff1e1a1b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff514e4e, 0xffebebeb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffeaeaea, 0xff504c4d, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff252122, 0xffaaa9aa, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xffaaa9a9, 0xff252122, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff4a4748, 0xffe0dfe0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe0dfdf, 0xff4a4747, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff787676, 0xfff4f3f3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff3f3f3, 0xff777575, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2a2627, 0xffa8a6a6, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffa6a5a5, 0xff292526, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff373435, 0xffc5c4c4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc3c2c2, 0xff363333, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff3f3c3d, 0xffd2d1d2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd2d1d1, 0xff3f3b3c, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff4a4748, 0xffdcdbdb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdcdbdb, 0xff4a4748, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211c1e, 0xff535052, 0xffe3e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e2e2, 0xff535051, 0xff211c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff555252, 0xffe4e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff555253, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xfff5f4f5, 0xffececec, 0xffe4e3e4, 0xffdcdbdb, 0xffdbdada, 0xffe3e3e3, 0xffececec, 0xfff5f4f5, 0xfffcfcfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211c1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff1f1f1, 0xffd2d1d1, 0xffafaeae, 0xff8a8889, 0xff6f6d6e, 0xff646162, 0xff5c595a, 0xff555253, 0xff4e4a4b, 0xff464344, 0xff454343, 0xff4d4a4b, 0xff555252, 0xff5c595a, 0xff646263, 0xff706e6f, 0xff898787, 0xffaeadad, 0xffd1d0d0, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e2e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xffd0cfd0, 0xff949293, 0xff696667, 0xff474444, 0xff343031, 0xff272324, 0xff242021, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff282324, 0xff343031, 0xff464243, 0xff686566, 0xff929091, 0xffcccbcb, 0xfff9f9f9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545151, 0xffe2e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xfff8f7f7, 0xffdddcdc, 0xffa09f9f, 0xff5a5757, 0xff2b2829, 0xff1d191a, 0xff1e1a1b, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff1e1a1b, 0xff1d191a, 0xff2a2627, 0xff575454, 0xff9d9b9c, 0xffdcdadb, 0xfff7f7f7, 0xfffefdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff535051, 0xffe2e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffebebeb, 0xffb6b5b5, 0xff6c696a, 0xff353132, 0xff1d191a, 0xff1c1819, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1d191a, 0xff1c1819, 0xff333030, 0xff6a6767, 0xffb1b0b0, 0xffe9e9e9, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211c1e, 0xff525050, 0xffe2e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e7e7, 0xffaeacad, 0xff605d5e, 0xff272324, 0xff1a1617, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff1a1617, 0xff252223, 0xff5c595a, 0xffa9a8a8, 0xffe5e5e5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff524f50, 0xffe2e1e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffefeeef, 0xffa7a6a6, 0xff5d5a5a, 0xff2c2829, 0xff1e1a1b, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1e1a1b, 0xff2b2728, 0xff5a5757, 0xffa3a1a2, 0xffecebeb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff524f50, 0xffe2e1e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcdcccd, 0xff605d5d, 0xff2b2728, 0xff211d1e, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221d1e, 0xff211d1e, 0xff2a2627, 0xff595657, 0xffc3c3c3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff524f50, 0xffe2e1e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff7f7f7, 0xff999797, 0xff343132, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff332f30, 0xff928f90, 0xfff3f2f3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff524f50, 0xffe1e1e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffd6d5d5, 0xff666464, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff221e1f, 0xff646061, 0xffd3d2d2, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff524e4f, 0xffe1e1e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffaf9fa, 0xffb6b4b5, 0xff423f3f, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1c1718, 0xff3e3a3b, 0xffb2b0b1, 0xfff9f9f9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff514e4f, 0xffe1e0e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff8f7f8, 0xffa2a0a1, 0xff353233, 0xff1c191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff302c2d, 0xff9a9899, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff504e4e, 0xffe1e0e0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff4f3f3, 0xff888687, 0xff2d292a, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff2a2728, 0xff827f7f, 0xffefefef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff504d4e, 0xffe1e0e0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffecebec, 0xff716e6f, 0xff272324, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff272425, 0xff6f6d6e, 0xffe7e7e7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff504d4e, 0xffe0e0e0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff1f0f1, 0xff726f70, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff716f6f, 0xffeeeeee, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff504d4e, 0xffe0dfe0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff4f4f4, 0xff8a8888, 0xff262223, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221d1e, 0xff888686, 0xfff4f4f4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff504d4d, 0xffe0dfdf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xff9f9d9d, 0xff2b2727, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff201c1d, 0xff211d1e, 0xff252222, 0xff2e2a2b, 0xff333031, 0xff363334, 0xff3a3738, 0xff3b3738, 0xff373334, 0xff343031, 0xff2f2b2c, 0xff262223, 0xff211e1f, 0xff1f1b1c, 0xff201b1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff242122, 0xff999797, 0xfffafafa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff504c4d, 0xffe0dfdf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffaeacac, 0xff312d2e, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1d1819, 0xff242021, 0xff3c3839, 0xff5d5b5b, 0xff7b7979, 0xff959393, 0xffafadad, 0xffbebdbd, 0xffc6c5c5, 0xffcdcccc, 0xffcecdcd, 0xffc7c6c6, 0xffbfbebe, 0xffb1afaf, 0xff979595, 0xff7d7a7c, 0xff615d5e, 0xff3e3a3b, 0xff252122, 0xff1d191a, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff2a2728, 0xffa4a2a2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff504c4d, 0xffe0dfdf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcbcaca, 0xff383535, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1c1819, 0xff343031, 0xff6f6c6d, 0xffa6a5a5, 0xffcecdce, 0xffe8e8e8, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff7f7f7, 0xffe9e9e9, 0xffd0d0d0, 0xffa9a8a8, 0xff737070, 0xff363334, 0xff1c1819, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff333031, 0xffbbb9ba, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff4f4c4c, 0xffdfdfdf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffefefef, 0xff555253, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1b1718, 0xff3b3839, 0xff8b8888, 0xffd2d1d1, 0xfff3f3f3, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xfff4f4f4, 0xffd6d5d5, 0xff8f8d8e, 0xff403d3d, 0xff1d1819, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221d1e, 0xff4e4b4c, 0xffe2e1e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff4f4b4c, 0xffdfdedf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff868384, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff2f2b2c, 0xff807d7d, 0xffe0e0e0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffe3e3e3, 0xff858384, 0xff322e2f, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xff7f7c7d, 0xfff8f8f8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff4e4b4c, 0xffdfdede, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbab9b9, 0xff312d2e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff272324, 0xff5b5858, 0xffc6c5c5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcbcbcb, 0xff5f5c5d, 0xff282425, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff332f30, 0xffb5b3b4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e4e4, 0xff555252, 0xff211c1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff423e3f, 0xffaaa8a9, 0xffc2c1c1, 0xffc0bebf, 0xffc0bfbf, 0xffc0bfbf, 0xffc0bfbf, 0xffc0bfbf, 0xffc0bfbf, 0xffc0bfbf, 0xffc0bfbf, 0xffc1bfbf, 0xffc1bfbf, 0xffc1bfbf, 0xffc1bfbf, 0xffc1bfbf, 0xffc1bfbf, 0xffc1bfc0, 0xffc1bfc0, 0xffc1c0c0, 0xffc1c0c0, 0xffc1c0c0, 0xffc1c0c0, 0xffc1c0c0, 0xffc1c0c0, 0xffc1c0c0, 0xffc1c0c0, 0xffc1c0c1, 0xffc1c0c1, 0xffc1c0c1, 0xffc1c0c1, 0xffc1c0c1, 0xffc2c1c1, 0xffc2c1c1, 0xffc2c1c1, 0xffc2c1c1, 0xffc2c1c1, 0xffc2c1c1, 0xffc2c1c1, 0xffc2c1c1, 0xffc3c1c2, 0xffc3c1c2, 0xffc3c2c2, 0xffc3c2c2, 0xffc3c2c2, 0xffc3c2c2, 0xffc3c2c2, 0xffc3c2c2, 0xffc3c2c2, 0xffc3c2c2, 0xffc3c2c3, 0xffc3c2c3, 0xffc3c2c3, 0xffc3c2c3, 0xffc3c2c3, 0xffc3c3c3, 0xffc3c3c3, 0xffc3c3c3, 0xffc4c3c3, 0xffc4c3c3, 0xffc4c3c3, 0xffc4c3c3, 0xffc4c3c3, 0xffc4c3c3, 0xffc5c3c4, 0xffc5c3c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc5c4c4, 0xffc7c6c6, 0xffbab9ba, 0xff524f50, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff343031, 0xff888686, 0xffeaeaea, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xff8f8e8e, 0xff383435, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1a1b, 0xff535051, 0xffc0bfc0, 0xffd2d1d1, 0xffd1d1d1, 0xffd1d1d1, 0xffd1d1d1, 0xffd2d1d1, 0xffd2d1d1, 0xffd2d1d1, 0xffd2d1d1, 0xffd2d1d1, 0xffd3d2d2, 0xffd3d2d2, 0xffd3d2d2, 0xffd3d2d2, 0xffd3d2d3, 0xffd3d2d3, 0xffd3d2d3, 0xffd3d3d3, 0xffd3d3d3, 0xffd3d3d3, 0xffd4d3d3, 0xffd4d3d3, 0xffd3d3d3, 0xffd4d4d4, 0xffd5d4d4, 0xffd5d4d4, 0xffd5d4d4, 0xffd5d4d4, 0xffd5d4d4, 0xffd5d4d4, 0xffd5d4d4, 0xffd5d4d4, 0xffd5d4d5, 0xffd5d5d5, 0xffd5d5d5, 0xffd6d5d5, 0xffd6d5d6, 0xffd6d6d6, 0xffd6d6d6, 0xffd6d6d6, 0xffd7d6d6, 0xffd7d6d6, 0xffd7d6d6, 0xffd7d6d6, 0xffdad9d9, 0xffc3c2c2, 0xff4b4849, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xff2d292a, 0xff2e2b2c, 0xff2f2a2c, 0xff2f2b2c, 0xff2f2b2c, 0xff2f2b2c, 0xff2f2b2c, 0xff2f2b2c, 0xff2f2b2c, 0xff2f2b2c, 0xff2f2c2c, 0xff2f2c2c, 0xff2f2c2c, 0xff2f2c2d, 0xff2f2c2d, 0xff2f2c2d, 0xff2f2c2d, 0xff2f2c2d, 0xff302c2d, 0xff302c2d, 0xff302c2d, 0xff302c2d, 0xff302c2d, 0xff302c2d, 0xff302c2d, 0xff302c2d, 0xff302d2e, 0xff302d2e, 0xff302d2e, 0xff302d2e, 0xff302d2e, 0xff312d2e, 0xff312d2e, 0xff312d2e, 0xff312d2e, 0xff312d2e, 0xff312d2e, 0xff312d2e, 0xff312d2e, 0xff312d2e, 0xff312d2e, 0xff312e2f, 0xff312e2f, 0xff312e2f, 0xff312e2f, 0xff312e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322e2f, 0xff322f2f, 0xff322f30, 0xff322f30, 0xff322f30, 0xff322f30, 0xff322f30, 0xff332f30, 0xff332f30, 0xff332f30, 0xff332f30, 0xff332f30, 0xff332f30, 0xff332f30, 0xff332f30, 0xff333030, 0xff333030, 0xff343030, 0xff343031, 0xff2f2c2d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff413e3f, 0xffb2b1b2, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefffe, 0xffbbbaba, 0xff4b4748, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xff373435, 0xff3e3a3b, 0xff3e3a3b, 0xff3e3a3b, 0xff3e3b3b, 0xff3e3b3b, 0xff3f3b3b, 0xff3f3b3c, 0xff3f3b3c, 0xff3f3c3c, 0xff3f3b3c, 0xff3f3b3d, 0xff3f3c3d, 0xff3f3c3d, 0xff403c3d, 0xff403c3d, 0xff403c3d, 0xff403c3d, 0xff403d3d, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff403d3e, 0xff413d3e, 0xff413d3e, 0xff413d3e, 0xff413e3f, 0xff413e3f, 0xff413e3f, 0xff413e3f, 0xff413e3f, 0xff413e3f, 0xff423e3f, 0xff423e3f, 0xff423f3f, 0xff423f40, 0xff423f40, 0xff423f40, 0xff423f40, 0xff423f40, 0xff433f40, 0xff433f40, 0xff433f40, 0xff433f40, 0xff434041, 0xff3f3c3d, 0xff2a2627, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211c1e, 0xff211c1d, 0xff211c1d, 0xff211c1d, 0xff211c1d, 0xff211c1d, 0xff211c1d, 0xff211c1d, 0xff211c1d, 0xff211c1d, 0xff211c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff211c1d, 0xff231e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff4f4c4d, 0xffcac8c8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcfcece, 0xff585556, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff4f4c4d, 0xffd3d3d3, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd9d8d8, 0xff555152, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff252122, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff454242, 0xffcacaca, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd1d1d1, 0xff464343, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff444041, 0xffa3a1a2, 0xffb6b4b5, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb4b2b3, 0xffb6b4b5, 0xff5b5859, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff373334, 0xffb8b7b7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc4c3c4, 0xff373334, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff464243, 0xffadabab, 0xffb0afaf, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb0aeae, 0xffb1b0b0, 0xffa2a0a1, 0xff474445, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff575454, 0xffeae9ea, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff1f1f1, 0xff464344, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff262223, 0xff928f90, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa19fa0, 0xff262223, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff383435, 0xffdedddd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff0efef, 0xff5e5b5c, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211c1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa6a5a5, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff696667, 0xfff4f4f4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff8f8f8, 0xff7a7778, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff8d8b8c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff5c5959, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xff585556, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454143, 0xffdeddde, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e2e2, 0xff535050, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff504c4d, 0xffe9e8e9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff5c5959, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc5c5c5, 0xff2c2829, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xffadabab, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xffb3b1b1, 0xff2a2627, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2f2b2c, 0xffb5b4b4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xff5c5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff8e8b8c, 0xff211e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff5a5758, 0xfff0f0f0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffebebeb, 0xff636161, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff838182, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e8e8, 0xff5b5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff7f7f7, 0xff5b5959, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2a2627, 0xffb2b1b2, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb2b0b0, 0xff312d2e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff5c5959, 0xffe9e8e8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e8e8, 0xff5b5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd4d4d4, 0xff3c3839, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff5c5a5b, 0xffe9e9e9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffecebec, 0xff5d5a5b, 0xff221d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff3d393a, 0xffcccbcb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e8e8, 0xff5b5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa9a7a8, 0xff2b2728, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff252122, 0xff989696, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff979596, 0xff272324, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff282325, 0xffa6a4a4, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e8e8, 0xff5b5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff828080, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff3d393a, 0xffcccacb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd3d2d2, 0xff3a3637, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201b1d, 0xff817f80, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e8, 0xff5b5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff3f3f3, 0xff6a6767, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff666364, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xff666465, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff656263, 0xffeeeded, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e8, 0xff5b5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e2e2, 0xff545152, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff272224, 0xff908e8f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff9b9999, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff4c494a, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e8, 0xff5a5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd2d2d2, 0xff413d3e, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2e292a, 0xffb5b4b4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc4c2c3, 0xff292526, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff373334, 0xffd1d0d0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e8, 0xff5a5859, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbebdbd, 0xff343031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff3d3a3a, 0xffd7d6d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffeae9e9, 0xff3b3838, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff262223, 0xffbdbcbc, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e7, 0xff5a5758, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb1afb0, 0xff2d292a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff514d4e, 0xfff3f3f3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfcfc, 0xff5a5758, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xffadabac, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e7, 0xff5a5758, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa9a8a8, 0xff292627, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff5f5c5d, 0xfffdfefd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff706e6f, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201b1d, 0xffa2a0a0, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e7, 0xff5a5758, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa1a0a0, 0xff252122, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff6c6a6b, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff7e7c7d, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff979595, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e7, 0xff5a5758, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xff999798, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff7b7879, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff8d8b8b, 0xff191516, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d1819, 0xff8c8a8b, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe8e7e7, 0xff5a5757, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xff929191, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff868384, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff989697, 0xff1b1618, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff848182, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e7e7, 0xff5a5757, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xff969595, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff827f80, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff949292, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff888687, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e7e7, 0xff5a5757, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xff9e9d9d, 0xff241f21, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff777576, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff8a8788, 0xff191516, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d191a, 0xff929090, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e7e7, 0xff5a5757, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa5a3a5, 0xff272324, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff6a6768, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff7c7a7a, 0xff1b1618, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1a1b, 0xff9b9a9a, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e7e7, 0xff5a5757, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffadabab, 0xff2b2728, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545051, 0xfff7f6f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xff615e5e, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xffa4a3a4, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e7e7, 0xff5a5757, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb9b8b8, 0xff312e2e, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff444243, 0xffe2e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff5f5f5, 0xff444142, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xffb5b4b4, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff5a5657, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcecdcd, 0xff3d393a, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff353132, 0xffc9c7c8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdbdada, 0xff312e2f, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff2f2b2c, 0xffcbcaca, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff595657, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdedddd, 0xff4c494a, 0xff211d1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff292526, 0xffa1a0a0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffadacac, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff423f3f, 0xffdbdada, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff595657, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffececec, 0xff605d5d, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff767374, 0xfffafafa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff7a7778, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff565354, 0xffe8e8e8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff595657, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffafafa, 0xff767475, 0xff221f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff4e4a4b, 0xffdfdede, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e9e9, 0xff4a4748, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e191b, 0xff6f6d6d, 0xfff1f0f0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff595657, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff9c9b9b, 0xff282526, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2e2a2b, 0xffb2b0b1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb5b3b4, 0xff2d292a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff969495, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff595657, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc6c5c5, 0xff343031, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff797677, 0xfff3f3f3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffafafa, 0xff777575, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff312e2f, 0xffbbbaba, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff595657, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e8e9, 0xff4c4849, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff3c393a, 0xffd0cfcf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcfcece, 0xff413e3f, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff494646, 0xffdbdada, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff595657, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff716e6f, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d1a1b, 0xff878585, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xff8a8989, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff6a6768, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e6e6, 0xff585656, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffafadae, 0xff252123, 0xff231e20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff373335, 0xffd2d2d2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd2d2d2, 0xff423e3f, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff282425, 0xff9c9a9a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e6e6, 0xff585556, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffebebeb, 0xff423f3f, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff686566, 0xfffafbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff8f8f8, 0xff797677, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff3e3a3b, 0xffd4d3d3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e6e6, 0xff585556, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff838081, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xffa7a5a6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbab9b9, 0xff292525, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff6a6768, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e6, 0xff585556, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcbcaca, 0xff292627, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff393536, 0xffcecdce, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdbdbdb, 0xff434041, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff262223, 0xffaeadad, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e6, 0xff585556, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffafafa, 0xff656264, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff595657, 0xffe2e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffececec, 0xff636162, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff4d4a4a, 0xffeeeeee, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e6, 0xff585556, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff292525, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff71706f, 0xffebeaea, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff1f0f0, 0xff787576, 0xff221f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xffa5a4a4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e5, 0xff585555, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff2f2f2, 0xff676465, 0xff1c1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff262223, 0xff7e7c7c, 0xfff2f2f2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff4f4f4, 0xff827f80, 0xff282425, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff4d4a4b, 0xfff0f0f0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e5, 0xff585555, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffb8b7b7, 0xff2b2729, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff242021, 0xff797777, 0xfff2f2f1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff2f1f2, 0xff807e7f, 0xff282425, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201b1c, 0xffa9a7a7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e5, 0xff585555, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffefefef, 0xff6d6b6b, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff6c696a, 0xffe7e7e7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffeae9e9, 0xff777475, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff5c595a, 0xfff0efef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e5, 0xff585455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc4c3c4, 0xff3b3839, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1c1819, 0xff4f4c4d, 0xffc4c3c3, 0xfffbfafb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xffcccacb, 0xff585656, 0xff1c1819, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff322e2f, 0xffbebdbd, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e5, 0xff585455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xff858384, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff373435, 0xff929091, 0xffe1e0e0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff999797, 0xff3b3839, 0xff1c1819, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff7d7a7a, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e5e5, 0xff575455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe1e0e0, 0xff4e4b4c, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201b1c, 0xff252122, 0xff565354, 0xffa9a7a8, 0xffededed, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xffaeacad, 0xff5a5758, 0xff272324, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff484546, 0xffd4d3d3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e5e5, 0xff575455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffc1c0c0, 0xff302d2e, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff2d292a, 0xff575354, 0xffa5a4a4, 0xffebebeb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffefefef, 0xffadabac, 0xff5b5959, 0xff2e2b2c, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2e2b2b, 0xffaeacad, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e5e5, 0xff575455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff9d9b9c, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff272324, 0xff444142, 0xff838182, 0xffcac9ca, 0xfff2f2f2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff3f3f4, 0xffcdcccd, 0xff888686, 0xff474444, 0xff282425, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff838182, 0xfffdfefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e5e5, 0xff575455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9fafa, 0xff747172, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1e191a, 0xff272425, 0xff454243, 0xff767373, 0xffa3a1a2, 0xffc4c3c3, 0xffdddbdc, 0xffe7e6e6, 0xffebebeb, 0xffefefef, 0xffefefef, 0xffecebeb, 0xffe7e7e7, 0xffdedede, 0xffc5c4c5, 0xffa6a4a5, 0xff7a7777, 0xff494546, 0xff292526, 0xff1e1a1b, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1a1c, 0xff5c595a, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e4e5, 0xff575455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe6e5e5, 0xff5f5c5d, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e191a, 0xff1a1517, 0xff1d191a, 0xff242021, 0xff302c2d, 0xff413d3e, 0xff4a4747, 0xff524f50, 0xff535051, 0xff4b4748, 0xff423e3f, 0xff312e2e, 0xff252122, 0xff1e1a1b, 0xff1a1517, 0xff1d191a, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231e20, 0xff1f1b1b, 0xff4e4c4c, 0xffdddcdc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e4e5, 0xff575455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd7d7d7, 0xff565354, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1f1b1c, 0xff1e1a1b, 0xff1d191a, 0xff1c1819, 0xff1c1819, 0xff1d1819, 0xff1e1a1b, 0xff1f1b1c, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff4b4849, 0xffd0d0d0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e5e5, 0xff575455, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcccccc, 0xff4a4748, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff444041, 0xffc5c4c4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e5e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcccbcb, 0xff484546, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff433f40, 0xffc1c0c0, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e5e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdad9d9, 0xff504d4e, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff454243, 0xffcccacb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e4e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff5e5c5c, 0xff211e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221d1e, 0xff504d4e, 0xffd6d6d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e4e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e9ea, 0xff7d7b7b, 0xff2e2a2b, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff2a2627, 0xff726f6f, 0xffe2e2e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e4e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff6f5f6, 0xffa4a3a4, 0xff423e3f, 0xff1e1a1b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d191a, 0xff3c3839, 0xff9b9a9a, 0xfff3f2f2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe5e4e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffcac9ca, 0xff666464, 0xff231e1f, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff211d1e, 0xff5d5b5b, 0xffc3c2c2, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff0efef, 0xffa9a8a9, 0xff433f40, 0xff1c1718, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1b1718, 0xff393637, 0xff9d9c9c, 0xffececec, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffe3e1e1, 0xff7e7b7b, 0xff2f2b2c, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201b1d, 0xff2b2628, 0xff737171, 0xffdcdcdc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcccbcb, 0xff727070, 0xff363233, 0xff241f20, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff332f30, 0xff6b696a, 0xffc5c4c5, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff565354, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcac9ca, 0xff7d7a7b, 0xff484445, 0xff2b2728, 0xff201c1d, 0xff1f1b1c, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff1f1b1c, 0xff2a2627, 0xff444142, 0xff767374, 0xffc3c2c2, 0xfffcfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff565254, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfcfd, 0xffdad9d9, 0xffa9a7a8, 0xff716e6f, 0xff413d3e, 0xff252122, 0xff1d191a, 0xff1b1718, 0xff1f1b1c, 0xff221d1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff1c1819, 0xff1d1819, 0xff242021, 0xff3d3a3b, 0xff6c6a6b, 0xffa4a2a3, 0xffd6d5d6, 0xfff9fafa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff565254, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff1f1f1, 0xffd5d3d3, 0xffb0aeaf, 0xff838182, 0xff575455, 0xff322e30, 0xff221d1f, 0xff1c1819, 0xff191516, 0xff1b1718, 0xff1e1a1b, 0xff1f1b1c, 0xff201c1d, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211c1d, 0xff201c1d, 0xff1f1b1c, 0xff1e1a1b, 0xff1b1718, 0xff191516, 0xff1b1718, 0xff201c1d, 0xff302d2d, 0xff545253, 0xff7f7d7d, 0xffadabab, 0xffd2d1d1, 0xffefefef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff565253, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xfff8f8f8, 0xffeeeeee, 0xffd8d7d7, 0xffb7b6b6, 0xff989697, 0xff7a7778, 0xff5d595a, 0xff444041, 0xff363334, 0xff2f2c2d, 0xff2c2829, 0xff2a2526, 0xff292627, 0xff2c2829, 0xff2f2b2c, 0xff363234, 0xff413e3f, 0xff5b5758, 0xff777576, 0xff959494, 0xffb5b4b4, 0xffd5d5d5, 0xffededed, 0xfff8f7f7, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e3, 0xff565253, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xfff0f0f0, 0xffececec, 0xffe7e7e7, 0xffdedede, 0xffd4d3d3, 0xffd3d2d2, 0xffdddddc, 0xffe7e6e6, 0xffebebeb, 0xffefefef, 0xfff8f8f8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff555253, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e3e4, 0xff555253, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff545152, 0xffe3e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e3e4, 0xff555253, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211c1d, 0xff555252, 0xffe4e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe4e3e4, 0xff565253, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff524f50, 0xffe2e1e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe2e1e2, 0xff524f50, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff484445, 0xffdad9d9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdad9d9, 0xff474445, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff3d3a3b, 0xffd0cfd0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcfcfcf, 0xff3d3a3a, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff363233, 0xffc1bfc0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbfbebe, 0xff343132, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff272324, 0xff9e9c9d, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xff9d9b9b, 0xff272223, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff6d6a6b, 0xfff0f0f0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xff6c696a, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff403d3e, 0xffd9d8d8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd9d8d8, 0xff403c3d, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff969495, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xff959394, 0xff201b1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff3e3a3b, 0xffdedddd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffdddcdd, 0xff3d393a, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff807e7f, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xff7e7c7c, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff292526, 0xffa5a3a4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa2a1a1, 0xff282525, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff363233, 0xffb9b8b8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb6b4b5, 0xff343132, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff423e3f, 0xffb8b6b6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb3b2b2, 0xff3f3c3c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff353132, 0xffa4a3a4, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffaf9fa, 0xffa09e9f, 0xff333031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff292526, 0xff7c7979, 0xffd9d9d9, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xffd7d5d6, 0xff777474, 0xff272324, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1b1617, 0xff3a3636, 0xff8e8c8d, 0xffd1d0d0, 0xffebebeb, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffafafa, 0xffeaeaea, 0xffcecdce, 0xff8a8888, 0xff373333, 0xff1a1617, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1f1a1c, 0xff393536, 0xff625f60, 0xff929090, 0xffb2b0b1, 0xffc2c0c1, 0xffcecece, 0xffd7d6d6, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd8d7d7, 0xffd7d6d6, 0xffcecdcd, 0xffc0bfc0, 0xffb1afb0, 0xff908e8e, 0xff605c5d, 0xff373334, 0xff1e1a1b, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff1e1a1b, 0xff242021, 0xff2e2a2b, 0xff363233, 0xff3d393a, 0xff434041, 0xff454242, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454142, 0xff454242, 0xff434041, 0xff3c393a, 0xff363233, 0xff2e2a2b, 0xff231f20, 0xff1e1a1b, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff232021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff232020, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff232020, 0xff232021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff232021, 0xff232021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xff252122, 0xff252122, 0xff262223, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff393536, 0xffa1a0a0, 0xffb3b2b3, 0xffb1b0b0, 0xff726f70, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff4c4849, 0xffa9a7a7, 0xffb3b2b2, 0xffb2b0b0, 0xff5b5859, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff787576, 0xffb4b2b2, 0xffb4b2b3, 0xffa09e9f, 0xff312d2e, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff242021, 0xff8c8a8b, 0xffb5b3b3, 0xffb4b2b3, 0xff898788, 0xff2e2a2b, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff322e2f, 0xff807d7e, 0xff8d8b8c, 0xff8d8b8b, 0xff8e8c8d, 0xff625f60, 0xff262323, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff464343, 0xffebebeb, 0xffffffff, 0xffffffff, 0xffa09e9f, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff646162, 0xfff3f3f3, 0xffffffff, 0xffffffff, 0xff7c7a7b, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa9a7a7, 0xffffffff, 0xffffffff, 0xffe8e8e8, 0xff393536, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff252022, 0xffcac9ca, 0xffffffff, 0xffffffff, 0xffc5c4c5, 0xff343132, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff6b6869, 0xfff4f4f4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffcbcaca, 0xff3b3839, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff383535, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c2c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff262223, 0xffacabab, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff6f5f5, 0xff666364, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff464343, 0xffebebea, 0xffffffff, 0xffffffff, 0xffa09e9e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff383535, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff4a4747, 0xffdddcdc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa6a4a5, 0xff282425, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff393536, 0xffa4a2a3, 0xffb6b4b5, 0xffb4b2b3, 0xff727071, 0xff211e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff383535, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211e1f, 0xff7e7c7e, 0xfff6f6f6, 0xffffffff, 0xfff8f8f8, 0xffd8d7d7, 0xfffdfdfd, 0xffffffff, 0xffe4e3e3, 0xff413d3e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff242021, 0xff241f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff383535, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff322e2f, 0xffb8b7b7, 0xffffffff, 0xffffffff, 0xffd7d7d7, 0xff656263, 0xfff0f0f0, 0xffffffff, 0xffffffff, 0xff7c797a, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff383535, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff211d1e, 0xff211c1d, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff555353, 0xffe7e7e6, 0xffffffff, 0xffffffff, 0xff939292, 0xff2c2829, 0xffc3c1c2, 0xffffffff, 0xffffffff, 0xffc7c6c6, 0xff292627, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff1d191a, 0xff211d1e, 0xff282425, 0xff343031, 0xff3b3738, 0xff3e3a3b, 0xff3a3738, 0xff353132, 0xff282425, 0xff201b1d, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1e1a1b, 0xff1f1a1b, 0xff242021, 0xff2e2a2b, 0xff383435, 0xff3d393a, 0xff3d393a, 0xff383536, 0xff2f2c2d, 0xff221f20, 0xff1e1a1b, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff383535, 0xff1f1a1c, 0xff1f1b1c, 0xff201c1d, 0xff2c282a, 0xff393637, 0xff3e3a3b, 0xff3c3839, 0xff312d2e, 0xff231f20, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1e1a1b, 0xff211d1e, 0xff2c292a, 0xff383535, 0xff3d3a3a, 0xff3e3a3b, 0xff373435, 0xff2b2728, 0xff201c1d, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xff8d8b8c, 0xffffffff, 0xffffffff, 0xfff3f4f4, 0xff4d494b, 0xff201c1d, 0xff868485, 0xfffefefe, 0xffffffff, 0xfff9f8f8, 0xff585556, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff454243, 0xff8a8889, 0xff8b8989, 0xff8b898a, 0xff464344, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff565354, 0xff8d8b8c, 0xff8b8a89, 0xff848283, 0xff363233, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1f, 0xff373334, 0xff5f5c5d, 0xff878586, 0xffa8a7a7, 0xffbebcbd, 0xffcbc9ca, 0xffd0d0d0, 0xffcbcaca, 0xffbfbebe, 0xffa6a4a4, 0xff7b7879, 0xff3f3c3d, 0xff1e1a1b, 0xff221d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211c1d, 0xff332f30, 0xff7d7b7c, 0xff8a8888, 0xff898787, 0xff5b5858, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff282425, 0xff4a4647, 0xff727070, 0xff999797, 0xffb3b1b2, 0xffc5c4c4, 0xffcfcece, 0xffcfcdce, 0xffc6c5c5, 0xffb6b4b5, 0xff939292, 0xff5f5d5d, 0xff2b2728, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff373434, 0xff1a1617, 0xff3c3839, 0xff7d7b7b, 0xffaeadad, 0xffc7c6c7, 0xffd1d0d0, 0xffcccbcb, 0xffb9b8b8, 0xff8f8d8e, 0xff4d494a, 0xff1f1b1c, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff242021, 0xff514d4e, 0xff8a8788, 0xffb0aeaf, 0xffc5c4c4, 0xffcfcece, 0xffd0cfcf, 0xffc5c4c4, 0xffacabab, 0xff807d7e, 0xff423f40, 0xff1d1a1b, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff363233, 0xffcdcccc, 0xffffffff, 0xffffffff, 0xffbdbcbc, 0xff272324, 0xff201c1d, 0xff524f50, 0xffe3e2e3, 0xffffffff, 0xffffffff, 0xffa4a2a2, 0xff1e191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff4e4b4b, 0xffe1e0e0, 0xfffcfcfc, 0xfffefefe, 0xff9d9b9c, 0xff1e1a1b, 0xff231e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff282425, 0xffbab9b9, 0xfffefefe, 0xfffefefe, 0xffcccbcb, 0xff363233, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221d1f, 0xff7e7c7d, 0xffcfcece, 0xffeaeaea, 0xfff7f7f8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xfff5f5f5, 0xffd7d7d7, 0xff7c7a7b, 0xff221f20, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e191a, 0xff434041, 0xffdfdedf, 0xfff9f8f8, 0xfff5f5f5, 0xff989697, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff413e3e, 0xffabaaaa, 0xffdfdfdf, 0xfff1f1f1, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xffebebeb, 0xffb4b3b3, 0xff454142, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff363334, 0xff585556, 0xffcac9c9, 0xfff5f4f5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffafafa, 0xffdfdede, 0xff858283, 0xff252223, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff393536, 0xff9f9d9d, 0xffe3e2e3, 0xfff9f8f9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff6f5f6, 0xffd7d6d6, 0xff7c7a7a, 0xff252021, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff625f60, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xff757374, 0xff1f1b1c, 0xff221e1f, 0xff302c2d, 0xffb6b5b5, 0xffffffff, 0xffffffff, 0xffe3e3e3, 0xff3b3738, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff302c2d, 0xffb4b3b3, 0xffffffff, 0xffffffff, 0xffe2e2e2, 0xff363233, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff4d4a4b, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xff929090, 0xff252223, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff231f20, 0xffb7b7b7, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff8f8f8, 0xfff3f3f4, 0xfff2f2f2, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff8f8f8, 0xff949393, 0xff221d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b9a9a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff585556, 0xffe5e4e4, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xfff5f6f5, 0xfff2f2f3, 0xfff3f3f3, 0xfffbfafb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd8d7d7, 0xff484445, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e1e1, 0xff6b6869, 0xffdadada, 0xffffffff, 0xffffffff, 0xfff9faf9, 0xfff1f1f1, 0xfff2f2f2, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff5f4f5, 0xff918f90, 0xff262122, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff444142, 0xffcdcccd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xfff3f3f3, 0xfff3f3f3, 0xfffcfcfc, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff4f3f4, 0xff929091, 0xff272324, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xffa9a8a9, 0xffffffff, 0xffffffff, 0xffe0dfdf, 0xff403c3d, 0xff221e1f, 0xff231f20, 0xff201c1d, 0xff7e7c7c, 0xfff5f5f5, 0xffffffff, 0xfffcfcfc, 0xff7b787a, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff797878, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xff747272, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff949293, 0xffffffff, 0xffffffff, 0xffebeaeb, 0xff585455, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff231f20, 0xffb8b6b7, 0xfff7f7f7, 0xffd5d5d5, 0xff9c9b9b, 0xff6f6d6d, 0xff514e4f, 0xff413e3f, 0xff3e3a3b, 0xff4b4849, 0xff726f70, 0xffb6b5b5, 0xfff2f2f2, 0xffffffff, 0xffffffff, 0xfff5f5f5, 0xff6f6c6d, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff595656, 0xffe4e4e4, 0xffedecec, 0xffbbbaba, 0xff848282, 0xff5f5d5d, 0xff484445, 0xff3e3a3b, 0xff423e3f, 0xff5a5758, 0xff8e8c8d, 0xffd8d8d8, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffc5c4c4, 0xff2e2a2b, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xfff0f0f0, 0xffd7d7d7, 0xffffffff, 0xffeeeded, 0xff9e9c9c, 0xff575354, 0xff3a3738, 0xff3d3a3b, 0xff646061, 0xffb7b5b5, 0xfff7f6f6, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xff716e6f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff3c3839, 0xffd1d0d1, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xffd7d6d6, 0xff8d8b8b, 0xff575455, 0xff413e3e, 0xff413e3f, 0xff5d5a5b, 0xffa2a0a0, 0xffedecec, 0xffffffff, 0xffffffff, 0xfff2f2f2, 0xff777575, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff403c3d, 0xffe9e9e9, 0xffffffff, 0xffffffff, 0xffa5a4a4, 0xff292525, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff484546, 0xffdedddd, 0xffffffff, 0xfffefefe, 0xffbfbebe, 0xff2a2627, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff494546, 0xffd9d8d8, 0xffffffff, 0xffffffff, 0xffbcbbbc, 0xff242122, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff322e2f, 0xffd9dad9, 0xffffffff, 0xffffffff, 0xffbdbcbd, 0xff343132, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff262122, 0xff817f7f, 0xff6a6768, 0xff2d292a, 0xff1b1718, 0xff1a1517, 0xff1d191a, 0xff1f1b1c, 0xff1f1b1c, 0xff1e1a1b, 0xff1a1617, 0xff231f20, 0xff767274, 0xffe8e8e8, 0xffffffff, 0xffffffff, 0xffc7c5c6, 0xff332f31, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff494647, 0xff858383, 0xff484546, 0xff231e20, 0xff191516, 0xff1c1819, 0xff1e1a1b, 0xff1f1b1c, 0xff1f1b1c, 0xff1c1819, 0xff1b1718, 0xff3d393a, 0xffb0afaf, 0xfffbfbfb, 0xffffffff, 0xfffdfdfd, 0xff777576, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe2e1e1, 0xff686666, 0xff1f1b1c, 0xff1c1819, 0xff1f1b1c, 0xff1f1b1c, 0xff1a1617, 0xff242021, 0xff8c8a8b, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xffcdcccc, 0xff3f3c3d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff282425, 0xffadabab, 0xffffffff, 0xffffffff, 0xfff8f7f8, 0xffa7a6a6, 0xff393637, 0xff1b1618, 0xff1c1819, 0xff1f1b1c, 0xff1f1b1c, 0xff1b1718, 0xff1b1718, 0xff605d5e, 0xffe0dfe0, 0xffffffff, 0xffffffff, 0xffd4d3d3, 0xff434041, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff838181, 0xffffffff, 0xffffffff, 0xfff5f5f5, 0xff686566, 0xff221f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff252223, 0xffaeadae, 0xfffdfdfd, 0xffffffff, 0xffe8e8e8, 0xff575455, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff282526, 0xffa9a8a8, 0xfffdfdfd, 0xffffffff, 0xffededed, 0xff4f4b4c, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff6b696a, 0xfffcfcfc, 0xffffffff, 0xfff9f9f9, 0xff858283, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff221e1f, 0xff1c1819, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1b1718, 0xff888687, 0xfffbfbfb, 0xffffffff, 0xffecebeb, 0xff625e60, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff241f21, 0xff1e1a1b, 0xff1d191a, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff3e3a3b, 0xffcfcece, 0xffffffff, 0xffffffff, 0xffbebdbe, 0xff272324, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffffffff, 0xfff3f3f3, 0xff7e7b7c, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff221e20, 0xffa5a3a3, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xff7a7878, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff656262, 0xffeeeeee, 0xffffffff, 0xfffefefe, 0xffb6b4b5, 0xff302d2e, 0xff1d1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1a1516, 0xff686566, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xff807e7f, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff2a2728, 0xffcecdcd, 0xffffffff, 0xffffffff, 0xffcecdcd, 0xff3e3b3b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff6d6b6c, 0xfff6f6f6, 0xffffffff, 0xfffafafa, 0xff939292, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff6d6a6b, 0xfff2f2f2, 0xffffffff, 0xfffbfbfb, 0xff918e8f, 0xff1e1a1b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff221e1f, 0xffb4b2b2, 0xffffffff, 0xffffffff, 0xffe0dfdf, 0xff504c4d, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff3a3637, 0xffe6e5e6, 0xffffffff, 0xfff9f9f9, 0xff898788, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1c1819, 0xff8f8c8d, 0xfffafafa, 0xffffffff, 0xffe0dfdf, 0xff423f40, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xfffefefe, 0xffc9c8c9, 0xff343031, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff423e3f, 0xffe7e7e7, 0xffffffff, 0xffffffff, 0xffb9b8b8, 0xff2e2a2b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff282526, 0xffa9a7a7, 0xfffdfdfd, 0xffffffff, 0xffededed, 0xff545152, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff252222, 0xffb5b4b3, 0xffffffff, 0xffffffff, 0xffbcbcbc, 0xff2e2a2b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, + 0xfd231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff5f5d5d, 0xfff8f8f8, 0xffffffff, 0xfffefefe, 0xff999798, 0xff272324, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff343132, 0xffdad9d9, 0xffffffff, 0xffffffff, 0xffcac9c9, 0xff3b3738, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff383435, 0xffd4d3d4, 0xffffffff, 0xffffffff, 0xffcac9c9, 0xff353132, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff484545, 0xffe5e5e5, 0xffffffff, 0xfffefefe, 0xffb4b2b2, 0xff2b2727, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1d191a, 0xff191516, 0xff191516, 0xff1b1718, 0xff1c1819, 0xff1d191a, 0xff1d191a, 0xff1b1718, 0xff201b1c, 0xffc9c8c9, 0xffffffff, 0xffffffff, 0xffa4a3a3, 0xff292526, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1b1718, 0xff181415, 0xff1a1517, 0xff1c1819, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff130f10, 0xff686566, 0xfff6f6f6, 0xffffffff, 0xffededed, 0xff5d5a5a, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xfffbfbfb, 0xff8e8c8d, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xffaba9aa, 0xffffffff, 0xffffffff, 0xffe6e5e5, 0xff413f40, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff413e3f, 0xffd3d2d3, 0xffffffff, 0xffffffff, 0xffbfbebe, 0xff252122, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff757374, 0xffffffff, 0xffffffff, 0xffe4e4e4, 0xff3f3c3d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfd231f20, + 0xf2231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201b1c, 0xffa8a7a7, 0xffffffff, 0xffffffff, 0xffeaeaea, 0xff625f60, 0xff201b1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff999898, 0xffffffff, 0xffffffff, 0xffeeeeee, 0xff676466, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff999898, 0xfffdfdfd, 0xffffffff, 0xffebebeb, 0xff636161, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1d191b, 0xff858384, 0xfff8f7f7, 0xffffffff, 0xfff5f5f5, 0xff767475, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff2d292a, 0xff504d4e, 0xff767474, 0xff8e8c8d, 0xff989797, 0xffa09e9f, 0xffa1a0a0, 0xffa1a0a0, 0xffa09f9f, 0xffa1a0a0, 0xffe4e4e4, 0xffffffff, 0xffffffff, 0xffb1afb0, 0xff2e2b2c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201b1c, 0xff221f20, 0xff3c3839, 0xff636061, 0xff838182, 0xff949292, 0xff9d9c9c, 0xffa1a0a0, 0xffa1a0a0, 0xffa1a0a0, 0xff9d9c9c, 0xffbdbcbc, 0xfffbfbfb, 0xffffffff, 0xfff1f1f1, 0xff6a6768, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xfff7f7f7, 0xff636060, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff7b7979, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xff5a5758, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff5c5959, 0xffe7e7e7, 0xffffffff, 0xffffffff, 0xff908e8f, 0xff171213, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff201c1d, 0xff1e1a1b, 0xff555253, 0xfffbfbfb, 0xffffffff, 0xfffafafa, 0xff514e4f, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf2231f20, + 0xe5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff403d3e, 0xffdfdfdf, 0xffffffff, 0xffffffff, 0xffc8c7c7, 0xff353233, 0xff201b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff514f4f, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xffa19f9f, 0xff2a2627, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff555153, 0xfff2f1f1, 0xffffffff, 0xfffdfdfd, 0xff9d9b9b, 0xff272324, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff302c2d, 0xffc0bfbf, 0xffffffff, 0xffffffff, 0xffdcdcdc, 0xff3d3a3b, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff373334, 0xff888586, 0xffd3d2d2, 0xfff9f8f8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbab9b9, 0xff332f30, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff262223, 0xff5a5757, 0xffb0afaf, 0xffebeaea, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff3f3f3, 0xff747172, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffebebeb, 0xff494647, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff5a5859, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xff737171, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff706e6f, 0xfff3f3f3, 0xffffffff, 0xffffffff, 0xffcdcccc, 0xffa6a4a4, 0xffaaa9a9, 0xffaaa8aa, 0xffaaa9a9, 0xffaaa9a9, 0xffaba9a9, 0xffaba9aa, 0xffabaaaa, 0xffababaa, 0xffacabaa, 0xffabaaab, 0xffbdbcbc, 0xfffcfcfc, 0xffffffff, 0xfffefeff, 0xff625e5f, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xe5231f20, + 0xd4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff7d7a7b, 0xfff7f7f7, 0xffffffff, 0xfffafafa, 0xff959393, 0xff242021, 0xff282425, 0xff292526, 0xff292526, 0xff292526, 0xff292526, 0xff292526, 0xff272324, 0xff2b2728, 0xffc7c6c6, 0xffffffff, 0xffffffff, 0xffd9d8d8, 0xff433f40, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff252223, 0xffc5c3c4, 0xffffffff, 0xffffffff, 0xffd0d0d0, 0xff403d3e, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff5a5758, 0xffe8e7e7, 0xffffffff, 0xffffffff, 0xffa3a2a2, 0xff1e1a1b, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff565354, 0xffcfcece, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffefefe, 0xfff7f7f7, 0xfff1f1f1, 0xffeeedee, 0xffececec, 0xffecebeb, 0xffecebeb, 0xffecebeb, 0xfffaf9f9, 0xffffffff, 0xffffffff, 0xffbcbbbc, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2f2b2c, 0xff8f8d8d, 0xfff3f2f2, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xfff4f4f4, 0xffefefef, 0xffededed, 0xffecebeb, 0xffecebeb, 0xffebeaeb, 0xfff1f1f1, 0xfffefefe, 0xffffffff, 0xfff4f4f4, 0xff787676, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe5e5e5, 0xff3e3b3b, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff4f4c4c, 0xfff8f8f8, 0xffffffff, 0xffffffff, 0xff827f80, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff7c7a7a, 0xfffbfafa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff686565, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xd4231f20, + 0xb4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2e2a2b, 0xffbab8b9, 0xfffefefe, 0xffffffff, 0xfffafafa, 0xffbbb9ba, 0xff979696, 0xff9b9a9a, 0xff9b9a9a, 0xff9b9a9a, 0xff9b9a9a, 0xff9b9a9a, 0xff9b9a9a, 0xff9c9a9a, 0xff979696, 0xffcfcfcf, 0xffffffff, 0xffffffff, 0xfffefefe, 0xff757273, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1717, 0xff7a7778, 0xffffffff, 0xffffffff, 0xfff4f4f4, 0xff6d6a6b, 0xff231e1f, 0xff231f20, 0xff221e1f, 0xff252122, 0xff918f8f, 0xfffcfbfb, 0xffffffff, 0xfff7f7f7, 0xff5b5859, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff494647, 0xffd3d2d2, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xffcccccc, 0xffa1a0a0, 0xff848383, 0xff726f70, 0xff676465, 0xff625f60, 0xff605d5e, 0xff5f5c5d, 0xff625e5f, 0xffd3d2d2, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff272324, 0xff858383, 0xfff8f8f8, 0xffffffff, 0xffffffff, 0xffe8e7e7, 0xffb6b5b5, 0xff929191, 0xff7b7879, 0xff6c6969, 0xff656263, 0xff615e5f, 0xff615e5e, 0xff5a5758, 0xff908e8e, 0xfff8f8f8, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe3e3e3, 0xff3b3838, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221d1e, 0xff4d4a4b, 0xfff6f6f6, 0xffffffff, 0xffffffff, 0xff848182, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff807d7e, 0xfffdfdfd, 0xffffffff, 0xffffffff, 0xffe9e8e8, 0xffdfdede, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe0dfdf, 0xffe1e0e0, 0xffe0e0e0, 0xff5e5b5c, 0xff211c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xb4231f20, + 0x8e231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff565354, 0xffe4e3e3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffb9b8b8, 0xff2a2627, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff3a3738, 0xffe4e3e4, 0xffffffff, 0xffffffff, 0xffa8a6a7, 0xff2b2728, 0xff231f20, 0xff221e1f, 0xff3a3637, 0xffc7c6c6, 0xffffffff, 0xffffffff, 0xffcbcbcb, 0xff292527, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff272324, 0xff9b9999, 0xfffdfdfd, 0xffffffff, 0xfff9f8f8, 0xff949192, 0xff3d393a, 0xff262223, 0xff201c1d, 0xff1e1a1b, 0xff1e1a1b, 0xff1e191a, 0xff1d191a, 0xff1b1718, 0xff1f1b1c, 0xffc6c5c5, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff484445, 0xffdadada, 0xffffffff, 0xffffffff, 0xffd4d2d2, 0xff5c595a, 0xff2f2b2c, 0xff221e1f, 0xff1f1b1c, 0xff1e1a1b, 0xff1e1a1b, 0xff1d191a, 0xff1d191a, 0xff140f10, 0xff656363, 0xfff5f5f5, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe7e7e7, 0xff423e3f, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff534f50, 0xfffafbfb, 0xffffffff, 0xffffffff, 0xff7c797a, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff7b7879, 0xfffafafa, 0xffffffff, 0xffffffff, 0xff8c8a8a, 0xff484545, 0xff504d4e, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4d, 0xff504d4e, 0xff504d4e, 0xff312e2e, 0xff221f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0x8e231f20, + 0x62231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xff8e8b8b, 0xfffcfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff2f1f2, 0xff4d4a4b, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xffa2a1a1, 0xffffffff, 0xffffffff, 0xffe1e0e0, 0xff434041, 0xff221e1f, 0xff221e1f, 0xff625f5f, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xff818080, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff3f3b3c, 0xffcfcecf, 0xffffffff, 0xffffffff, 0xffbdbcbc, 0xff292526, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff292627, 0xffd8d7d7, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff232021, 0xff757273, 0xfffdfdfd, 0xffffffff, 0xfffafafa, 0xff5f5d5d, 0xff1e1a1b, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff787676, 0xfff7f7f7, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xfff1f1f1, 0xff545152, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff686566, 0xffffffff, 0xffffffff, 0xffffffff, 0xff676566, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff6d6a6b, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xff858384, 0xff171213, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff211d1e, 0xff231e20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0x62231f20, + 0x2e231f20, 0xf0231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff393536, 0xffc6c5c5, 0xffffffff, 0xffffffff, 0xfff0f0f0, 0xffa6a4a5, 0xffa2a0a1, 0xffa3a1a1, 0xffa3a1a1, 0xffa3a1a1, 0xffa3a1a1, 0xffa3a1a1, 0xffa3a1a1, 0xffa3a1a1, 0xffa3a1a1, 0xffa3a1a1, 0xffa2a1a2, 0xffb6b4b4, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xff929192, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff5b5758, 0xfff9fafa, 0xffffffff, 0xffffffff, 0xff7a7878, 0xff211d1e, 0xff272324, 0xff9d9b9c, 0xffffffff, 0xffffffff, 0xffebeaea, 0xff3f3c3d, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff535051, 0xffe2e2e2, 0xffffffff, 0xffffffff, 0xff7f7d7d, 0xff1a1517, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffededed, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xff979697, 0xffffffff, 0xffffffff, 0xffd5d5d6, 0xff312d2e, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201b1c, 0xff9c9a9b, 0xfffbfbfb, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xfff9f9f9, 0xff747272, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201b1c, 0xff8f8d8e, 0xffffffff, 0xffffffff, 0xfff5f4f4, 0xff4d4a4b, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff555152, 0xffe3e2e2, 0xffffffff, 0xffffffff, 0xffb7b6b6, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf0231f20, 0x2e231f20, + 0x11231f20, 0xc4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221f20, 0xff605e5f, 0xfff1f1f1, 0xffffffff, 0xffffffff, 0xffacabab, 0xff1a1718, 0xff1c1819, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff1d191a, 0xff353232, 0xffcccccc, 0xffffffff, 0xffffffff, 0xffdad9da, 0xff322e2f, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff312e2f, 0xffc9c8c8, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff292526, 0xff3c3839, 0xffdad9da, 0xffffffff, 0xffffffff, 0xffaba9aa, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff5e5b5c, 0xffeaeaea, 0xffffffff, 0xffffffff, 0xff6d6b6b, 0xff1b1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1b1718, 0xff7e7c7d, 0xfffefdfe, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252223, 0xffa6a4a5, 0xffffffff, 0xffffffff, 0xffc6c4c4, 0xff2b2829, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff363233, 0xffcacaca, 0xffffffff, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xfffdfdfd, 0xffaaa9aa, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff2c2829, 0xffc9c7c8, 0xffffffff, 0xffffffff, 0xffd1d1d1, 0xff383435, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff383536, 0xffc6c5c6, 0xffffffff, 0xffffffff, 0xffe7e7e7, 0xff464344, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xc4231f20, 0x11231f20, + 0x04231f20, 0x8c231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff272324, 0xff9f9d9d, 0xffffffff, 0xffffffff, 0xfffefefe, 0xff646262, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff272324, 0xff959393, 0xffffffff, 0xffffffff, 0xffffffff, 0xff6d6a6b, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff878586, 0xffffffff, 0xffffffff, 0xfff6f6f6, 0xff4d4a4a, 0xff6a6869, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xff656262, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff595657, 0xffe7e7e6, 0xffffffff, 0xffffffff, 0xff858384, 0xff181416, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff393536, 0xffcdcccc, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff252122, 0xffa09e9e, 0xffffffff, 0xffffffff, 0xffdad9d9, 0xff332f30, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff201c1d, 0xff777575, 0xfff2f2f2, 0xffffffff, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffe0e0e0, 0xff4f4c4d, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff686566, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xff9c9b9b, 0xff272324, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff908f8f, 0xfffafafa, 0xffffffff, 0xfffcfcfc, 0xffa09e9e, 0xff262223, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0x8c231f20, 0x04231f20, + 0x01231f20, 0x49231f20, 0xf4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff3d3a3b, 0xffdddddd, 0xffffffff, 0xffffffff, 0xffd8d7d7, 0xff322e2f, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221d1e, 0xff605d5e, 0xffececec, 0xffffffff, 0xffffffff, 0xffb9b7b8, 0xff221e20, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff504c4d, 0xffe6e5e5, 0xffffffff, 0xffffffff, 0xff9c9a9b, 0xffb4b2b2, 0xffffffff, 0xffffffff, 0xffd0cfcf, 0xff373334, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff474344, 0xffd8d7d7, 0xffffffff, 0xffffffff, 0xffcbcaca, 0xff322e2f, 0xff1b1718, 0xff211d1e, 0xff221e1f, 0xff221e1f, 0xff211d1e, 0xff1c1819, 0xff2f2b2c, 0xffa09e9f, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff242021, 0xff828081, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xff737171, 0xff1b1617, 0xff1f1b1c, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff1f1b1c, 0xff1f1b1c, 0xff575555, 0xffd7d6d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffffffff, 0xfffefefe, 0xffafaeaf, 0xff343132, 0xff1d191a, 0xff211d1e, 0xff221e1f, 0xff221e1f, 0xff201c1d, 0xff1a1617, 0xff474344, 0xffd5d4d4, 0xffffffff, 0xffffffff, 0xffebeaea, 0xff5b5858, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff454343, 0xffd9d8d9, 0xffffffff, 0xffffffff, 0xffefefef, 0xff828081, 0xff2a2627, 0xff1c1819, 0xff201c1d, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff201c1d, 0xff1e191b, 0xff1c1819, 0xff272424, 0xff524f50, 0xff4b4848, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf4231f20, 0x49231f20, 0x01231f20, + 0x00000000, 0x1c231f20, 0xc3231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff757273, 0xffffffff, 0xffffffff, 0xffffffff, 0xff969495, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff393537, 0xffc7c6c6, 0xffffffff, 0xffffffff, 0xffececec, 0xff4c4949, 0xff1c1819, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff302c2d, 0xffb4b3b3, 0xffffffff, 0xffffffff, 0xffeeedee, 0xfff3f3f3, 0xffffffff, 0xffffffff, 0xff918f90, 0xff262223, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2d292a, 0xffafadae, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xffacabab, 0xff434041, 0xff211d1e, 0xff1d191a, 0xff1e1a1b, 0xff262223, 0xff504d4e, 0xffadabab, 0xfff7f7f7, 0xfffbfbfb, 0xfff2f2f2, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff555252, 0xffeaeae9, 0xffffffff, 0xffffffff, 0xffe3e2e2, 0xff726f70, 0xff2b2828, 0xff1e1a1b, 0xff1d191a, 0xff201b1c, 0xff332f30, 0xff777475, 0xffd8d7d7, 0xffffffff, 0xfff3f2f2, 0xfffdfdfd, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xfffafafa, 0xfff7f8f8, 0xfffbfbfb, 0xffafaeae, 0xff4c494a, 0xff242021, 0xff1d191a, 0xff1e1a1b, 0xff282425, 0xff615e5f, 0xffcbcacb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffa3a1a2, 0xff2c2829, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c3c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff726f70, 0xfff5f5f5, 0xffffffff, 0xffffffff, 0xfff0efef, 0xffa9a7a7, 0xff5a5757, 0xff2d2a2b, 0xff211c1d, 0xff1e1a1b, 0xff1f1b1c, 0xff211d1e, 0xff2a2627, 0xff434041, 0xff706d6e, 0xffaba9aa, 0xffe1e1e1, 0xff868384, 0xff252122, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xc3231f20, 0x1c231f20, 0x00000000, + 0x00000000, 0x06231f20, 0x7c231f20, 0xfc231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff262223, 0xffc0bebf, 0xffffffff, 0xffffffff, 0xfff5f4f4, 0xff575455, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff949293, 0xfffafafa, 0xffffffff, 0xfffbfbfb, 0xff908e8e, 0xff1c1819, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff7a7879, 0xfff6f5f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe9e9e9, 0xff595657, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201b1c, 0xff605e5e, 0xffebeaea, 0xffffffff, 0xffffffff, 0xfffbfbfb, 0xffdfdedf, 0xffb4b3b3, 0xff9b9999, 0xffa1a0a0, 0xffc0bfbf, 0xffe6e6e6, 0xfffdfdfd, 0xffffffff, 0xffa5a2a3, 0xffc8c6c7, 0xffffffff, 0xffffffff, 0xffbcbbbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b999a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f5f, 0xffedecec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2d292a, 0xffa3a1a1, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff1f1f1, 0xffcbcbcb, 0xffa4a3a3, 0xff9a9999, 0xffadabac, 0xffd4d3d3, 0xfff3f3f3, 0xffffffff, 0xffdfdfdf, 0xff9e9c9d, 0xfff6f6f6, 0xffffffff, 0xfff4f4f4, 0xff777576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe6e5e5, 0xffa09f9e, 0xfffcfcfc, 0xfffdfdfd, 0xffe4e4e4, 0xffbbbaba, 0xff9f9d9d, 0xffa3a0a2, 0xffc5c4c4, 0xffececec, 0xfffefefe, 0xffffffff, 0xffffffff, 0xffcccbcb, 0xff433f40, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242022, 0xffc4c2c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff333031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff242021, 0xff807e7e, 0xfff9f8f8, 0xffffffff, 0xffffffff, 0xfffcfcfc, 0xffececec, 0xffd0d0d0, 0xffb2b0b0, 0xffa4a1a2, 0xffa6a3a5, 0xffb5b3b4, 0xffcdcccd, 0xffe2e1e1, 0xfff3f3f3, 0xfffdfdfd, 0xffffffff, 0xff8b8a8a, 0xff242121, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfc231f20, 0x7c231f20, 0x06231f20, 0x00000000, + 0x00000000, 0x00000000, 0x32231f20, 0xd8231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e191a, 0xff514e4f, 0xfff7f7f7, 0xffffffff, 0xffffffff, 0xffc5c4c5, 0xff332f30, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff5c5a5a, 0xffebebeb, 0xffffffff, 0xffffffff, 0xffcdcdcd, 0xff343032, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff484546, 0xffdbdada, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbdbcbd, 0xff343132, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff807e7f, 0xfff2f2f2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffdfdfd, 0xffabaaaa, 0xff363233, 0xffc1bfc0, 0xffffffff, 0xffffffff, 0xffbcbcbb, 0xff343031, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff454142, 0xffe5e5e5, 0xffffffff, 0xfffbfbfb, 0xff9b9a9a, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1d191a, 0xff615f60, 0xffededec, 0xffffffff, 0xfffafafa, 0xff797778, 0xff1a1617, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff3e3b3c, 0xffc0bebf, 0xfffdfdfe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffe7e6e6, 0xff666364, 0xff656263, 0xfff5f5f5, 0xffffffff, 0xfff4f4f4, 0xff787576, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff232021, 0xffa5a3a3, 0xfffefefe, 0xffffffff, 0xffe2e2e2, 0xff464244, 0xff9e9d9d, 0xfffbfbfb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffd1cfcf, 0xff4b4849, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242122, 0xffc5c4c4, 0xffffffff, 0xffffffff, 0xffc0bfbf, 0xff343031, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff696767, 0xffdddddd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff9f9f9, 0xff7d7b7c, 0xff252122, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xd8231f20, 0x32231f20, 0x00000000, 0x00000000, + 0x00231f20, 0x00000000, 0x05231f20, 0x8e231f20, 0xfa231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1c1819, 0xff7f7d7d, 0xffe1e0e1, 0xffdbdbdb, 0xffdedede, 0xff7f7d7e, 0xff252122, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff302d2e, 0xffb2b1b1, 0xffdcdbdb, 0xffdbdbdb, 0xffcccccc, 0xff555253, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff282425, 0xff9b9999, 0xffdbdbdb, 0xffdbdbdb, 0xffdbdbdb, 0xffd9d8d9, 0xff7b797a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff656263, 0xffc7c6c7, 0xffeeeeee, 0xfff7f6f6, 0xfff8f8f8, 0xfff8f8f8, 0xfff6f6f6, 0xfff0f0f0, 0xffd4d3d3, 0xff838181, 0xff2b2728, 0xff211d1e, 0xffa7a5a6, 0xffe0dfdf, 0xffdedddd, 0xffa3a2a2, 0xff312d2e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff3f3b3c, 0xffc5c4c4, 0xffdcdcdc, 0xffd9d9d9, 0xff888586, 0xff211d1e, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff575455, 0xffcfcdcf, 0xffdbdbdb, 0xffdbd9da, 0xff6b696a, 0xff1b1718, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff363333, 0xff999797, 0xffe0dfe0, 0xfff5f4f4, 0xfff8f7f7, 0xfff9f8f8, 0xfff8f8f8, 0xfff5f5f5, 0xffe7e6e7, 0xffb6b5b5, 0xff535052, 0xff181415, 0xff595556, 0xffd4d3d3, 0xffdbdbdb, 0xffd4d4d4, 0xff696668, 0xff1e1a1b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff231f20, 0xff8f8d8e, 0xffdcdbdb, 0xffdddcdc, 0xffc3c2c2, 0xff322f30, 0xff272425, 0xff828080, 0xffd6d6d6, 0xfff1f1f1, 0xfff7f7f7, 0xfff9f9f9, 0xfff8f8f8, 0xfff5f5f5, 0xffe3e3e3, 0xff9e9d9d, 0xff393536, 0xff1d191a, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff242021, 0xffaaa8a9, 0xffdedede, 0xffdddcdc, 0xffa6a4a5, 0xff312d2e, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1e1a1b, 0xff3c3839, 0xff918f8f, 0xffd3d2d3, 0xffededed, 0xfff5f5f5, 0xfff8f8f8, 0xfff9f9f9, 0xfff8f8f8, 0xfff7f6f6, 0xfff4f3f3, 0xffe8e8e8, 0xffd0cece, 0xff9e9c9c, 0xff5c5959, 0xff2b2829, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfa231f20, 0x8e231f20, 0x05231f20, 0x00000000, 0x00231f20, + 0x00000000, 0x00231f20, 0x00000000, 0x30231f20, 0xd8231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff322e2f, 0xff373435, 0xff363334, 0xff363334, 0xff292526, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff302b2c, 0xff373334, 0xff363334, 0xff363334, 0xff2c2829, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff2c2829, 0xff363334, 0xff363334, 0xff363334, 0xff353233, 0xff282526, 0xff231e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1c1819, 0xff2b2829, 0xff585556, 0xff7b7878, 0xff8a8888, 0xff8b8989, 0xff7b797a, 0xff5e5b5b, 0xff343132, 0xff1d191a, 0xff201c1d, 0xff231f20, 0xff302d2e, 0xff373335, 0xff373334, 0xff312d2e, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff262223, 0xff343132, 0xff373334, 0xff363334, 0xff2d2a2b, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff292526, 0xff353132, 0xff363334, 0xff363334, 0xff2a2728, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1f1a1b, 0xff403d3e, 0xff6b6869, 0xff848283, 0xff8c8a8a, 0xff868384, 0xff6f6d6d, 0xff4b4748, 0xff242122, 0xff1d191a, 0xff221e1f, 0xff292526, 0xff363233, 0xff373334, 0xff363233, 0xff2a2728, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff2e2a2b, 0xff373334, 0xff373334, 0xff343131, 0xff252122, 0xff201c1d, 0xff1d191a, 0xff373334, 0xff625f60, 0xff807e7e, 0xff8c8a8b, 0xff888686, 0xff6f6c6d, 0xff433f40, 0xff1f1b1c, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff312e2f, 0xff373334, 0xff373334, 0xff312d2e, 0xff242021, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1d191a, 0xff322e2f, 0xff565354, 0xff737172, 0xff878485, 0xff8d8b8b, 0xff888687, 0xff7d7b7c, 0xff676464, 0xff4d494a, 0xff2d292a, 0xff1d191a, 0xff1c1819, 0xff221d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xd8231f20, 0x30231f20, 0x00000000, 0x00231f20, 0x00000000, + 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x73231f20, 0xfb231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1c1819, 0xff1c1819, 0xff1e1a1b, 0xff1e1a1b, 0xff1d181a, 0xff1b1718, 0xff1f1b1c, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1f1b1c, 0xff1f1b1c, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff221d1e, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1e1a1b, 0xff1b1718, 0xff1d191a, 0xff1e1a1b, 0xff1d191a, 0xff1b1718, 0xff1d191a, 0xff211d1e, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff1f1b1c, 0xff1f1b1c, 0xff1f1b1c, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff211d1e, 0xff1f1b1c, 0xff1f1b1c, 0xff201b1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1b1718, 0xff1d191a, 0xff1e1a1b, 0xff1e1a1b, 0xff1c1718, 0xff1d191a, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff201c1d, 0xff1f1b1c, 0xff1f1b1c, 0xff201c1d, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff1f1b1c, 0xff1c1718, 0xff1c1819, 0xff1e191a, 0xff1f1a1b, 0xff1e1a1b, 0xff1d1819, 0xff1b1718, 0xff1d191a, 0xff201c1d, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfb231f20, 0x73231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, + 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x0e231f20, 0xb5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff221e1f, 0xff221e1f, 0xff221e1f, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xb5231f20, 0x0e231f20, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2a231f20, 0xde231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xde231f20, 0x2a231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x51231f20, 0xf4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf4231f20, 0x51231f20, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x03231f20, 0x74231f20, 0xf5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf5231f20, 0x74231f20, 0x03231f20, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x0a231f20, 0x84231f20, 0xf5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf5231f20, 0x84231f20, 0x0a231f20, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10231f20, 0x84231f20, 0xf5231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf5231f20, 0x84231f20, 0x10231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x0a231f20, 0x72231f20, 0xf4231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf4231f20, 0x72231f20, 0x0a231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x03231f20, 0x4f231f20, 0xdd231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xdd231f20, 0x4f231f20, 0x03231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00231f20, 0x00000000, 0x29231f20, 0xb6231f20, 0xfb231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfb231f20, 0xb6231f20, 0x29231f20, 0x00000000, 0x00231f20, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x0e231f20, 0x73231f20, 0xd8231f20, 0xfb231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfb231f20, 0xd8231f20, 0x73231f20, 0x0e231f20, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x30231f20, 0x8e231f20, 0xd6231f20, 0xfd231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfd231f20, 0xd6231f20, 0x8e231f20, 0x30231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x04231f20, 0x32231f20, 0x7b231f20, 0xc2231f20, 0xf3231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf3231f20, 0xc2231f20, 0x7b231f20, 0x32231f20, 0x04231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00231f20, 0x00000000, 0x00000000, 0x06231f20, 0x1c231f20, 0x49231f20, 0x8c231f20, 0xc4231f20, 0xf0231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xf0231f20, 0xc4231f20, 0x8c231f20, 0x49231f20, 0x1c231f20, 0x06231f20, 0x00000000, 0x00000000, 0x00231f20, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x01231f20, 0x04231f20, 0x11231f20, 0x2e231f20, 0x62231f20, 0x8e231f20, 0xb4231f20, 0xd4231f20, 0xe5231f20, 0xf2231f20, 0xfd231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xff231f20, 0xfd231f20, 0xf2231f20, 0xe5231f20, 0xd4231f20, 0xb4231f20, 0x8e231f20, 0x62231f20, 0x2e231f20, 0x11231f20, 0x04231f20, 0x01231f20, 0x00000000, 0x00000000, 0x00000000, 0x00231f20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +} // anonymous namespace +} // namespace ui + +#endif // MAME_FRONTEND_UI_DEFIMG_IPP diff --git a/src/icludes/frontend/mame/ui/devctrl.h b/src/icludes/frontend/mame/ui/devctrl.h new file mode 100644 index 0000000..ec9dc0d --- /dev/null +++ b/src/icludes/frontend/mame/ui/devctrl.h @@ -0,0 +1,159 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/devctrl.h + + Device specific control menu + This source provides a base class for any device which need a specific + submenu and which can occur multiple times in the same driver (at the + moment, cassette tapes and barcode readers, in future possibly other like + printers) + The base class contains calls to get the total number of devices of + the same kind connected to the driver, and shortcuts to switch current + device to next one or previous one attached. This allows, for instance, + users to pass from a device to another one by simply pressing left/right + and the menu is rebuilt accordingly, without the need of a preliminary + submenu listing available devices of the same kind. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_DEVCTRL_H +#define MAME_FRONTEND_UI_DEVCTRL_H + +#pragma once + +#include "ui/menu.h" + +namespace ui { + +template +class menu_device_control : public menu +{ +public: + menu_device_control(mame_ui_manager &mui, render_container &container, DeviceType *device); + +protected: + DeviceType *current_device() { return m_device; } + int count() { return m_count; } + + int current_index(); + void previous(); + void next(); + std::string current_display_name(); + uint32_t current_display_flags(); + +private: + // device enumerator + typedef device_type_enumerator enumerator; + + DeviceType * m_device; + int m_count; +}; + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +template +menu_device_control::menu_device_control(mame_ui_manager &mui, render_container &container, DeviceType *device) + : menu(mui, container) +{ + enumerator iter(mui.machine().root_device()); + m_count = iter.count(); + m_device = device ? device : iter.first(); +} + + +//------------------------------------------------- +// current_index +//------------------------------------------------- + +template +int menu_device_control::current_index() +{ + enumerator iter(machine().root_device()); + return iter.indexof(*m_device); +} + + +//------------------------------------------------- +// previous +//------------------------------------------------- + +template +void menu_device_control::previous() +{ + // left arrow - rotate left through devices + if (m_device && (1 < m_count)) + { + enumerator iter(machine().root_device()); + int index = iter.indexof(*m_device); + if (index > 0) + index--; + else + index = m_count - 1; + m_device = iter.byindex(index); + reset(reset_options::REMEMBER_POSITION); + } +} + + +//------------------------------------------------- +// next +//------------------------------------------------- + +template +void menu_device_control::next() +{ + // right arrow - rotate right through cassette devices + if (m_device && (1 < m_count)) + { + enumerator iter(machine().root_device()); + int index = iter.indexof(*m_device); + if (index < m_count - 1) + index++; + else + index = 0; + m_device = iter.byindex(index); + reset(reset_options::REMEMBER_POSITION); + } +} + + +//------------------------------------------------- +// current_display_name +//------------------------------------------------- + +template +std::string menu_device_control::current_display_name() +{ + std::string display_name; + display_name.assign(current_device()->name()); + if (count() > 1) + display_name.append(string_format(" %d", current_index() + 1)); + return display_name; +} + + +//------------------------------------------------- +// current_display_flags +//------------------------------------------------- + +template +uint32_t menu_device_control::current_display_flags() +{ + uint32_t flags = 0; + if (count() > 1) + { + if (current_index() > 0) + flags |= FLAG_LEFT_ARROW; + if (current_index() < count() - 1) + flags |= FLAG_RIGHT_ARROW; + } + return flags; +} + +} // namespace ui + +#endif // MAME_FRONTEND_UI_DEVCTRL_H diff --git a/src/icludes/frontend/mame/ui/devopt.cpp b/src/icludes/frontend/mame/ui/devopt.cpp new file mode 100644 index 0000000..c9a2c7f --- /dev/null +++ b/src/icludes/frontend/mame/ui/devopt.cpp @@ -0,0 +1,365 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + ui/devopt.cpp + + Internal menu for the device configuration. + +*********************************************************************/ + +#include "emu.h" +#include "ui/devopt.h" + +#include "ui/ui.h" +#include "romload.h" + + +namespace ui { + +/*------------------------------------------------- + device_config - handle the game information + menu + -------------------------------------------------*/ + +menu_device_config::menu_device_config( + mame_ui_manager &mui, + render_container &container, + device_slot_interface *slot, + device_slot_interface::slot_option const *option) + : menu_textbox(mui, container) + , m_option(option) + , m_mounted(machine().root_device().subdevice(slot->device().subtag(option->name())) != nullptr) +{ + set_process_flags(PROCESS_CUSTOM_NAV); +} + +menu_device_config::~menu_device_config() +{ +} + +void menu_device_config::populate_text(std::optional &layout, float &width, int &lines) +{ + if (!layout || (layout->width() != width)) + { + rgb_t const color = ui().colors().text_color(); + layout.emplace(ui().create_layout(container(), width)); + + machine_config &mconfig(const_cast(machine().config())); + machine_config::token const tok(mconfig.begin_configuration(mconfig.root_device())); + device_t *const dev = mconfig.device_add(m_option->name(), m_option->devtype(), 0); + for (device_t &d : device_enumerator(*dev)) + if (!d.configured()) + d.config_complete(); + + layout->add_text( + util::string_format( + m_mounted + ? _("[This option is currently mounted in the running system]\n\nOption: %1$s\nDevice: %2$s\n\nThe selected option enables the following items:\n") + : _("[This option is NOT currently mounted in the running system]\n\nOption: %1$s\nDevice: %2$s\n\nIf you select this option, the following items will be enabled:\n"), + m_option->name(), + dev->name()), + color); + + // loop over all CPUs + execute_interface_enumerator execiter(*dev); + if (execiter.count() > 0) + { + layout->add_text(_("* CPU:\n"), color); + std::unordered_set exectags; + for (device_execute_interface &exec : execiter) + { + if (!exectags.insert(exec.device().tag()).second) + continue; + + // get cpu specific clock that takes internal multiplier/dividers into account + u32 clock = exec.device().clock(); + + // count how many identical CPUs we have + int count = 1; + const char *name = exec.device().name(); + for (device_execute_interface &scan : execiter) + { + if (exec.device().type() == scan.device().type() && strcmp(name, scan.device().name()) == 0 && exec.device().clock() == scan.device().clock()) + if (exectags.insert(scan.device().tag()).second) + count++; + } + + std::string hz(std::to_string(clock)); + int d = (clock >= 1'000'000'000) ? 9 : (clock >= 1'000'000) ? 6 : (clock >= 1000) ? 3 : 0; + if (d > 0) + { + size_t dpos = hz.length() - d; + hz.insert(dpos, "."); + size_t last = hz.find_last_not_of('0'); + hz = hz.substr(0, last + (last != dpos ? 1 : 0)); + } + + // if more than one, prepend a #x in front of the CPU name and display clock + layout->add_text( + util::string_format( + (count > 1) + ? ((clock != 0) ? " %1$d" UTF8_MULTIPLY "%2$s %3$s" UTF8_NBSP "%4$s\n" : " %1$d" UTF8_MULTIPLY "%2$s\n") + : ((clock != 0) ? " %2$s %3$s" UTF8_NBSP "%4$s\n" : " %2$s\n"), + count, name, hz, + (d == 9) ? _("GHz") : (d == 6) ? _("MHz") : (d == 3) ? _("kHz") : _("Hz")), + color); + } + } + + // display screen information + screen_device_enumerator scriter(*dev); + if (scriter.count() > 0) + { + layout->add_text(_("* Video:\n"), color); + for (screen_device &screen : scriter) + { + if (screen.screen_type() == SCREEN_TYPE_VECTOR) + { + layout->add_text(util::string_format(_(" Screen '%1$s': Vector\n"), screen.tag()), color); + } + else + { + const u32 rate = u32(screen.frame_period().as_hz() * 1'000'000 + 0.5); + const bool valid = rate >= 1'000'000; + std::string hz(valid ? std::to_string(rate) : "?"); + if (valid) + { + size_t dpos = hz.length() - 6; + hz.insert(dpos, "."); + size_t last = hz.find_last_not_of('0'); + hz = hz.substr(0, last + (last != dpos ? 1 : 0)); + } + + const rectangle &visarea = screen.visible_area(); + layout->add_text( + util::string_format( + (screen.orientation() & ORIENTATION_SWAP_XY) + ? _(" Screen '%1$s': %2$d \xC3\x97 %3$d (V) %4$s\xC2\xA0Hz\n") + : _(" Screen '%1$s': %2$d \xC3\x97 %3$d (H) %4$s\xC2\xA0Hz\n"), + screen.tag(), + visarea.width(), + visarea.height(), + hz), + color); + } + } + } + + // loop over all sound chips + sound_interface_enumerator snditer(*dev); + if (snditer.count() > 0) + { + layout->add_text(_("* Sound:\n"), color); + std::unordered_set soundtags; + for (device_sound_interface &sound : snditer) + { + if (!sound.issound() || !soundtags.insert(sound.device().tag()).second) + continue; + + // count how many identical sound chips we have + int count = 1; + for (device_sound_interface &scan : snditer) + { + if (sound.device().type() == scan.device().type() && sound.device().clock() == scan.device().clock()) + if (soundtags.insert(scan.device().tag()).second) + count++; + } + + const u32 clock = sound.device().clock(); + std::string hz(std::to_string(clock)); + int d = (clock >= 1'000'000'000) ? 9 : (clock >= 1'000'000) ? 6 : (clock >= 1000) ? 3 : 0; + if (d > 0) + { + size_t dpos = hz.length() - d; + hz.insert(dpos, "."); + size_t last = hz.find_last_not_of('0'); + hz = hz.substr(0, last + (last != dpos ? 1 : 0)); + } + + // if more than one, prepend a #x in front of the name and display clock + layout->add_text( + util::string_format( + (count > 1) + ? ((clock != 0) ? " %1$d" UTF8_MULTIPLY "%2$s %3$s" UTF8_NBSP "%4$s\n" : " %1$d" UTF8_MULTIPLY "%2$s\n") + : ((clock != 0) ? " %2$s %3$s" UTF8_NBSP "%4$s\n" : " %2$s\n"), + count, sound.device().name(), hz, + (d == 9) ? _("GHz") : (d == 6) ? _("MHz") : (d == 3) ? _("kHz") : _("Hz")), + color); + } + } + + // scan for BIOS settings + int bios = 0; + if (dev->rom_region()) + { + // first loop through roms in search of default bios (shortname) + char const *bios_str(nullptr); + for (const tiny_rom_entry *rom = dev->rom_region(); !ROMENTRY_ISEND(rom); ++rom) + { + if (ROMENTRY_ISDEFAULT_BIOS(rom)) + bios_str = rom->name; + } + + // then loop again to count bios options and to get the default bios complete name + char const *bios_desc(nullptr); + for (romload::system_bios const &rom : romload::entries(dev->rom_region()).get_system_bioses()) + { + bios++; + if (bios_str && !std::strcmp(bios_str, rom.get_name())) + bios_desc = rom.get_description(); + } + + if (bios) + { + layout->add_text( + util::string_format( + _("* BIOS settings:\n %1$d options [default: %2$s]\n"), + bios, + bios_desc ? bios_desc : bios_str ? bios_str : ""), + color); + } + } + + int input = 0, input_mj = 0, input_hana = 0, input_gamble = 0, input_analog = 0, input_adjust = 0; + int input_keypad = 0, input_keyboard = 0, dips = 0, confs = 0; + std::string errors; + std::ostringstream dips_opt, confs_opt; + ioport_list portlist; + for (device_t &iptdev : device_enumerator(*dev)) + portlist.append(iptdev, errors); + + // check if the device adds inputs to the system + for (auto &port : portlist) + for (ioport_field &field : port.second->fields()) + { + if (field.type() >= IPT_MAHJONG_FIRST && field.type() < IPT_MAHJONG_LAST) + input_mj++; + else if (field.type() >= IPT_HANAFUDA_FIRST && field.type() < IPT_HANAFUDA_LAST) + input_hana++; + else if (field.type() >= IPT_GAMBLING_FIRST && field.type() < IPT_GAMBLING_LAST) + input_gamble++; + else if (field.type() >= IPT_ANALOG_FIRST && field.type() < IPT_ANALOG_LAST) + input_analog++; + else if (field.type() == IPT_ADJUSTER) + input_adjust++; + else if (field.type() == IPT_KEYPAD) + input_keypad++; + else if (field.type() == IPT_KEYBOARD) + input_keyboard++; + else if (field.type() >= IPT_START1 && field.type() < IPT_UI_FIRST) + input++; + else if (field.type() == IPT_DIPSWITCH) + { + dips++; + bool def(false); + for (ioport_setting const &setting : field.settings()) + { + if (setting.value() == field.defvalue()) + { + def = true; + util::stream_format(dips_opt, _(" %1$s [default: %2$s]\n"), field.name(), setting.name()); + break; + } + } + if (!def) + util::stream_format(dips_opt, _(" %1$s\n"), field.name()); + } + else if (field.type() == IPT_CONFIG) + { + confs++; + bool def(false); + for (ioport_setting const &setting : field.settings()) + { + if (setting.value() == field.defvalue()) + { + def = true; + util::stream_format(confs_opt, _(" %1$s [default: %2$s]\n"), field.name(), setting.name()); + break; + } + } + if (!def) + util::stream_format(confs_opt, _(" %1$s\n"), field.name()); + } + } + + if (dips) + { + layout->add_text(_("* DIP switch settings:\n"), color); + layout->add_text(std::move(dips_opt).str(), color); + } + if (confs) + { + layout->add_text(_("* Configuration settings:\n"), color); + layout->add_text(std::move(confs_opt).str(), color); + } + if (input || input_mj || input_hana || input_gamble || input_analog || input_adjust || input_keypad || input_keyboard) + layout->add_text(_("* Input device(s):\n"), color); + if (input) + layout->add_text(util::string_format(_(" User inputs [%1$d inputs]\n"), input), color); + if (input_mj) + layout->add_text(util::string_format(_(" Mahjong inputs [%1$d inputs]\n"), input_mj), color); + if (input_hana) + layout->add_text(util::string_format(_(" Hanafuda inputs [%1$d inputs]\n"), input_hana), color); + if (input_gamble) + layout->add_text(util::string_format(_(" Gambling inputs [%1$d inputs]\n"), input_gamble), color); + if (input_analog) + layout->add_text(util::string_format(_(" Analog inputs [%1$d inputs]\n"), input_analog), color); + if (input_adjust) + layout->add_text(util::string_format(_(" Adjuster inputs [%1$d inputs]\n"), input_adjust), color); + if (input_keypad) + layout->add_text(util::string_format(_(" Keypad inputs [%1$d inputs]\n"), input_keypad), color); + if (input_keyboard) + layout->add_text(util::string_format(_(" Keyboard inputs [%1$d inputs]\n"), input_keyboard), color); + + image_interface_enumerator imgiter(*dev); + if (imgiter.count() > 0) + { + layout->add_text(_("* Media Options:\n"), color); + for (const device_image_interface &imagedev : imgiter) + { + layout->add_text( + util::string_format( + _(" %1$s [tag: %2$s]\n"), + imagedev.image_type_name(), + imagedev.device().tag()), + color); + } + } + + slot_interface_enumerator slotiter(*dev); + if (slotiter.count() > 0) + { + layout->add_text(_("* Slot Options:\n"), color); + for (const device_slot_interface &slot : slotiter) + { + layout->add_text( + util::string_format( + _(" %1$s [default: %2$s]\n"), + slot.device().tag(), + slot.default_option() ? slot.default_option() : "----"), + color); + } + } + + if ((execiter.count() + scriter.count() + snditer.count() + imgiter.count() + slotiter.count() + bios + dips + confs + + input + input_mj + input_hana + input_gamble + input_analog + input_adjust + input_keypad + input_keyboard) == 0) + layout->add_text(_("[None]\n"), color); + + mconfig.device_remove(m_option->name()); + lines = layout->lines(); + } + width = layout->actual_width(); +} + +void menu_device_config::populate(float &customtop, float &custombottom) +{ +} + +void menu_device_config::handle(event const *ev) +{ + if (ev) + handle_key(ev->iptkey); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/devopt.h b/src/icludes/frontend/mame/ui/devopt.h new file mode 100644 index 0000000..74c81a7 --- /dev/null +++ b/src/icludes/frontend/mame/ui/devopt.h @@ -0,0 +1,40 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/devopt.h + + Internal menu for the device configuration. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_DEVOPT_H +#define MAME_FRONTEND_UI_DEVOPT_H + +#pragma once + +#include "ui/textbox.h" + + +namespace ui { + +class menu_device_config : public menu_textbox +{ +public: + menu_device_config(mame_ui_manager &mui, render_container &container, device_slot_interface *slot, device_slot_interface::slot_option const *option); + virtual ~menu_device_config() override; + +protected: + virtual void populate_text(std::optional &layout, float &width, int &lines) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + device_slot_interface::slot_option const *const m_option; + bool const m_mounted; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_DEVOPT_H diff --git a/src/icludes/frontend/mame/ui/dirmenu.cpp b/src/icludes/frontend/mame/ui/dirmenu.cpp new file mode 100644 index 0000000..3b92204 --- /dev/null +++ b/src/icludes/frontend/mame/ui/dirmenu.cpp @@ -0,0 +1,519 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/********************************************************************* + + ui/dirmenu.cpp + + Internal UI user interface. + +*********************************************************************/ + +#include "emu.h" + +#include "ui/ui.h" +#include "ui/dirmenu.h" +#include "ui/utils.h" +#include "ui/optsmenu.h" + +#include "emuopts.h" +#include "mame.h" + +#include "corestr.h" + + +namespace ui { + +static int ADDING = 1; +static int CHANGE = 2; + +struct folders_entry +{ + const char *name; + const char *option; + const int action; +}; + +static const folders_entry s_folders[] = +{ + { N_p("path-option", "ROMs"), OPTION_MEDIAPATH, ADDING }, + { N_p("path-option", "Software Media"), OPTION_SWPATH, CHANGE }, + { N_p("path-option", "Sound Samples"), OPTION_SAMPLEPATH, ADDING }, + { N_p("path-option", "Artwork"), OPTION_ARTPATH, ADDING }, + { N_p("path-option", "Crosshairs"), OPTION_CROSSHAIRPATH, ADDING }, + { N_p("path-option", "Cheat Files"), OPTION_CHEATPATH, ADDING }, + { N_p("path-option", "Plugins"), OPTION_PLUGINSPATH, ADDING }, + { N_p("path-option", "UI Translations"), OPTION_LANGUAGEPATH, CHANGE }, + { N_p("path-option", "INIs"), OPTION_INIPATH, ADDING }, + { N_p("path-option", "UI Settings"), OPTION_UI_PATH, CHANGE }, + { N_p("path-option", "Plugin Data"), OPTION_PLUGINDATAPATH, CHANGE }, + { N_p("path-option", "DATs"), OPTION_HISTORY_PATH, ADDING }, + { N_p("path-option", "Category INIs"), OPTION_CATEGORYINI_PATH, CHANGE }, + { N_p("path-option", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY, ADDING }, + { N_p("path-option", "Icons"), OPTION_ICONS_PATH, ADDING }, + { N_p("path-option", "Control Panels"), OPTION_CPANELS_PATH, ADDING }, + { N_p("path-option", "Cabinets"), OPTION_CABINETS_PATH, ADDING }, + { N_p("path-option", "Marquees"), OPTION_MARQUEES_PATH, ADDING }, + { N_p("path-option", "PCBs"), OPTION_PCBS_PATH, ADDING }, + { N_p("path-option", "Flyers"), OPTION_FLYERS_PATH, ADDING }, + { N_p("path-option", "Title Screens"), OPTION_TITLES_PATH, ADDING }, + { N_p("path-option", "Game Endings"), OPTION_ENDS_PATH, ADDING }, + { N_p("path-option", "Bosses"), OPTION_BOSSES_PATH, ADDING }, + { N_p("path-option", "Artwork Previews"), OPTION_ARTPREV_PATH, ADDING }, + { N_p("path-option", "Select"), OPTION_SELECT_PATH, ADDING }, + { N_p("path-option", "Game Over Screens"), OPTION_GAMEOVER_PATH, ADDING }, + { N_p("path-option", "HowTo"), OPTION_HOWTO_PATH, ADDING }, + { N_p("path-option", "Logos"), OPTION_LOGOS_PATH, ADDING }, + { N_p("path-option", "Scores"), OPTION_SCORES_PATH, ADDING }, + { N_p("path-option", "Versus"), OPTION_VERSUS_PATH, ADDING }, + { N_p("path-option", "Covers"), OPTION_COVER_PATH, ADDING } +}; + + +/************************************************** + MENU DIRECTORY +**************************************************/ +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_directory::menu_directory(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ +} + +menu_directory::~menu_directory() +{ + ui().save_ui_options(); + ui_globals::reset = true; +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_directory::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), selected_index()); +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_directory::populate(float &customtop, float &custombottom) +{ + for (auto & elem : s_folders) + item_append(_("path-option", elem.name), 0, (void *)(uintptr_t)elem.action); + + item_append(menu_item_type::SEPARATOR); + customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_directory::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + char const *const toptext[] = { _("Folders Setup") }; + draw_text_box( + std::begin(toptext), std::end(toptext), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + +/************************************************** + MENU DISPLAY PATH +**************************************************/ +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_display_actual::menu_display_actual(mame_ui_manager &mui, render_container &container, int ref) + : menu(mui, container), m_ref(ref) +{ +} + +menu_display_actual::~menu_display_actual() +{ +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_display_actual::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT) + switch ((uintptr_t)ev->itemref) + { + case REMOVE: + menu::stack_push(ui(), container(), m_ref); + break; + + case ADD_CHANGE: + menu::stack_push(ui(), container(), m_ref); + break; + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_display_actual::populate(float &customtop, float &custombottom) +{ + m_heading[0] = string_format(_("Current %1$s Folders"), _("path-option", s_folders[m_ref].name)); + if (ui().options().exists(s_folders[m_ref].option)) + m_searchpath.assign(ui().options().value(s_folders[m_ref].option)); + else + m_searchpath.assign(machine().options().value(s_folders[m_ref].option)); + + path_iterator path(m_searchpath); + std::string curpath; + m_folders.clear(); + while (path.next(curpath, nullptr)) + m_folders.push_back(curpath); + + item_append((s_folders[m_ref].action == CHANGE) ? _("Change Folder") : _("Add Folder"), 0, (void *)ADD_CHANGE); + + if (m_folders.size() > 1) + item_append(_("Remove Folder"), 0, (void *)REMOVE); + + item_append(menu_item_type::SEPARATOR); + customtop = (m_folders.size() + 1) * ui().get_line_height() + 6.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_display_actual::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + float const lineheight(ui().get_line_height()); + float const maxwidth(draw_text_box( + std::begin(m_folders), std::end(m_folders), + origx1, origx2, origy1 - (3.0f * ui().box_tb_border()) - (m_folders.size() * lineheight), origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f)); + draw_text_box( + std::begin(m_heading), std::end(m_heading), + 0.5f * (1.0f - maxwidth), 0.5f * (1.0f + maxwidth), origy1 - top, origy1 - top + lineheight + (2.0f * ui().box_tb_border()), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + +/************************************************** +MENU ADD FOLDER +**************************************************/ +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_add_change_folder::menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref) : menu(mui, container) +{ + m_ref = ref; + m_change = (s_folders[ref].action == CHANGE); + m_search.clear(); + + // configure the starting path + osd_get_full_path(m_current_path, "."); + + std::string searchpath; + if (mui.options().exists(s_folders[m_ref].option)) + searchpath = mui.options().value(s_folders[m_ref].option); + else + searchpath = mui.machine().options().value(s_folders[m_ref].option); + + path_iterator path(searchpath); + std::string curpath; + while (path.next(curpath, nullptr)) + m_folders.push_back(curpath); +} + +menu_add_change_folder::~menu_add_change_folder() +{ +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_add_change_folder::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + if (ev->iptkey == IPT_UI_SELECT) + { + int index = (uintptr_t)ev->itemref - 1; + const menu_item &pitem = item(index); + + // go up to the parent path + if (pitem.text() == "..") + { + size_t first_sep = m_current_path.find_first_of(PATH_SEPARATOR[0]); + size_t last_sep = m_current_path.find_last_of(PATH_SEPARATOR[0]); + if (first_sep != last_sep) + m_current_path.erase(++last_sep); + } + else + { + // if isn't a drive, appends the directory + if (pitem.subtext() != "[DRIVE]") + { + if (m_current_path[m_current_path.length() - 1] == PATH_SEPARATOR[0]) + m_current_path.append(pitem.text()); + else + m_current_path.append(PATH_SEPARATOR).append(pitem.text()); + } + else + m_current_path = pitem.text(); + } + + // reset the char buffer also in this case + m_search.clear(); + reset(reset_options::SELECT_FIRST); + } + else if (ev->iptkey == IPT_SPECIAL) + { + bool update_selected = false; + + if (ev->unichar == 0x09) + { + // Tab key, save current path + std::string error_string; + if (m_change) + { + if (ui().options().exists(s_folders[m_ref].option)) + ui().options().set_value(s_folders[m_ref].option, m_current_path, OPTION_PRIORITY_CMDLINE); + else if (machine().options().value(s_folders[m_ref].option) != m_current_path) + { + machine().options().set_value(s_folders[m_ref].option, m_current_path, OPTION_PRIORITY_CMDLINE); + } + } + else + { + m_folders.push_back(m_current_path); + std::string tmppath; + for (int x = 0; x < m_folders.size(); ++x) + { + tmppath.append(m_folders[x]); + if (x != m_folders.size() - 1) + tmppath.append(";"); + } + + if (ui().options().exists(s_folders[m_ref].option)) + ui().options().set_value(s_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE); + else if (machine().options().value(s_folders[m_ref].option) != tmppath) + { + machine().options().set_value(s_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE); + } + } + + reset_parent(reset_options::SELECT_FIRST); + stack_pop(); + } + else + { + // if it's any other key and we're not maxed out, update + update_selected = input_character(m_search, ev->unichar, uchar_is_printable); + } + + // check for entries which matches our search buffer + if (update_selected) + { + const int cur_selected = selected_index(); + int entry, bestmatch = 0; + + // from current item to the end + for (entry = cur_selected; entry < item_count(); entry++) + if (item(entry).ref() && !m_search.empty()) + { + int match = 0; + for (int i = 0; i < m_search.size() + 1; i++) + { + if (core_strnicmp(item(entry).text().c_str(), m_search.data(), i) == 0) + match = i; + } + + if (match > bestmatch) + { + bestmatch = match; + set_selected_index(entry); + } + } + + // and from the first item to current one + for (entry = 0; entry < cur_selected; entry++) + { + if (item(entry).ref() && !m_search.empty()) + { + int match = 0; + for (int i = 0; i < m_search.size() + 1; i++) + { + if (core_strnicmp(item(entry).text().c_str(), m_search.data(), i) == 0) + match = i; + } + + if (match > bestmatch) + { + bestmatch = match; + set_selected_index(entry); + } + } + } + centre_selection(); + } + } + else if (ev->iptkey == IPT_UI_CANCEL) + { + // reset the char buffer also in this case + m_search.clear(); + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_add_change_folder::populate(float &customtop, float &custombottom) +{ + // open a path + file_enumerator path(m_current_path.c_str()); + const osd::directory::entry *dirent; + int folders_count = 0; + + // add the drives + for (std::string const &volume_name : osd_get_volume_names()) + item_append(volume_name, "[DRIVE]", 0, (void *)(uintptr_t)++folders_count); + + // add the directories + while ((dirent = path.next()) != nullptr) + { + if (dirent->type == osd::directory::entry::entry_type::DIR && strcmp(dirent->name, ".") != 0) + item_append(dirent->name, "[DIR]", 0, (void *)(uintptr_t)++folders_count); + } + + item_append(menu_item_type::SEPARATOR); + + // configure the custom rendering + customtop = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border(); + custombottom = 1.0f * ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_add_change_folder::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + std::string const toptext[] = { + util::string_format( + m_change ? _("Change %1$s Folder - Search: %2$s_") : _("Add %1$s Folder - Search: %2$s_"), + _("path-option", s_folders[m_ref].name), + m_search), + m_current_path }; + draw_text_box( + std::begin(toptext), std::end(toptext), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); + + // bottom text + char const *const bottomtext[] = { _("Press TAB to set") }; + draw_text_box( + std::begin(bottomtext), std::end(bottomtext), + origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); +} + +/************************************************** + MENU REMOVE FOLDER +**************************************************/ +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_remove_folder::menu_remove_folder(mame_ui_manager &mui, render_container &container, int ref) : menu(mui, container) +{ + m_ref = ref; + if (mui.options().exists(s_folders[m_ref].option)) + m_searchpath.assign(mui.options().value(s_folders[m_ref].option)); + else + m_searchpath.assign(mui.machine().options().value(s_folders[m_ref].option)); + + path_iterator path(m_searchpath); + std::string curpath; + while (path.next(curpath, nullptr)) + m_folders.push_back(curpath); +} + +menu_remove_folder::~menu_remove_folder() +{ +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_remove_folder::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT) + { + std::string tmppath, error_string; + m_folders.erase(m_folders.begin() + selected_index()); + for (int x = 0; x < m_folders.size(); ++x) + { + tmppath.append(m_folders[x]); + if (x < m_folders.size() - 1) + tmppath.append(";"); + } + + if (ui().options().exists(s_folders[m_ref].option)) + ui().options().set_value(s_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE); + else if (machine().options().value(s_folders[m_ref].option) != tmppath) + { + machine().options().set_value(s_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE); + } + + reset_parent(reset_options::REMEMBER_REF); + stack_pop(); + } +} + +//------------------------------------------------- +// populate menu +//------------------------------------------------- + +void menu_remove_folder::populate(float &customtop, float &custombottom) +{ + int folders_count = 0; + for (auto & elem : m_folders) + item_append(elem, 0, (void *)(uintptr_t)++folders_count); + + item_append(menu_item_type::SEPARATOR); + customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_remove_folder::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + std::string const toptext[] = {string_format(_("Remove %1$s Folder"), _("path-option", s_folders[m_ref].name)) }; + draw_text_box( + std::begin(toptext), std::end(toptext), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/dirmenu.h b/src/icludes/frontend/mame/ui/dirmenu.h new file mode 100644 index 0000000..5564274 --- /dev/null +++ b/src/icludes/frontend/mame/ui/dirmenu.h @@ -0,0 +1,119 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/*************************************************************************** + + ui/dirmenu.h + + Internal UI user interface. + +***************************************************************************/ + +#pragma once + +#ifndef MAME_FRONTEND_UI_DIRMENU_H +#define MAME_FRONTEND_UI_DIRMENU_H + +#include "ui/menu.h" + +#include +#include + +namespace ui { + +//------------------------------------------------- +// class directory menu +//------------------------------------------------- + +class menu_directory : public menu +{ +public: + menu_directory(mame_ui_manager &mui, render_container &container); + virtual ~menu_directory() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + +//------------------------------------------------- +// class directory specific menu +//------------------------------------------------- + +class menu_display_actual : public menu +{ +public: + menu_display_actual(mame_ui_manager &mui, render_container &container, int selectedref); + virtual ~menu_display_actual() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + enum + { + ADD_CHANGE = 1, + REMOVE, + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + std::string m_heading[1], m_searchpath; + std::vector m_folders; + int m_ref; +}; + +//------------------------------------------------- +// class remove folder menu +//------------------------------------------------- + +class menu_remove_folder : public menu +{ +public: + menu_remove_folder(mame_ui_manager &mui, render_container &container, int ref); + virtual ~menu_remove_folder() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + std::string m_searchpath; + int m_ref; + std::vector m_folders; +}; + +//------------------------------------------------- +// class add / change folder menu +//------------------------------------------------- + +class menu_add_change_folder : public menu +{ +public: + menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref); + virtual ~menu_add_change_folder() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + + virtual bool custom_ui_cancel() override { return !m_search.empty(); } + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + int m_ref; + std::string m_current_path; + std::string m_search; + bool m_change; + std::vector m_folders; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_DIRMENU_H diff --git a/src/icludes/frontend/mame/ui/filecreate.cpp b/src/icludes/frontend/mame/ui/filecreate.cpp new file mode 100644 index 0000000..875fe1c --- /dev/null +++ b/src/icludes/frontend/mame/ui/filecreate.cpp @@ -0,0 +1,339 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/filecreate.cpp + + MAME's clunky built-in file manager + + TODO + - Support image creation arguments + +***************************************************************************/ + +#include "emu.h" +#include "ui/filecreate.h" + +#include "ui/ui.h" +#include "ui/utils.h" + +#include "zippath.h" + +#include + + +namespace ui { + +/*************************************************************************** +CONSTANTS +***************************************************************************/ + +// conditional compilation to enable chosing of image formats - this is not +// yet fully implemented +#define ENABLE_FORMATS 0 + +// itemrefs for key menu items +#define ITEMREF_NEW_IMAGE_NAME ((void *) 0x0001) +#define ITEMREF_CREATE ((void *) 0x0002) +#define ITEMREF_FORMAT ((void *) 0x0003) +#define ITEMREF_NO ((void *) 0x0004) +#define ITEMREF_YES ((void *) 0x0005) + + +/*************************************************************************** +MENU HELPERS +***************************************************************************/ + +/*************************************************************************** +CONFIRM SAVE AS MENU +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_confirm_save_as::menu_confirm_save_as(mame_ui_manager &mui, render_container &container, bool *yes) + : menu(mui, container) +{ + m_yes = yes; + *m_yes = false; +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_confirm_save_as::~menu_confirm_save_as() +{ +} + + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_confirm_save_as::populate(float &customtop, float &custombottom) +{ + item_append(_("File Already Exists - Override?"), FLAG_DISABLE, nullptr); + item_append(menu_item_type::SEPARATOR); + item_append(_("No"), 0, ITEMREF_NO); + item_append(_("Yes"), 0, ITEMREF_YES); +} + +//------------------------------------------------- +// handle - confirm save as menu +//------------------------------------------------- + +void menu_confirm_save_as::handle(event const *ev) +{ + // process the event + if (ev && (ev->iptkey == IPT_UI_SELECT)) + { + if (ev->itemref == ITEMREF_YES) + *m_yes = true; + + // no matter what, pop out + stack_pop(); + } +} + + + +/*************************************************************************** +FILE CREATE MENU +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_file_create::menu_file_create(mame_ui_manager &mui, render_container &container, device_image_interface *image, std::string ¤t_directory, std::string ¤t_file, bool &ok) + : menu(mui, container) + , m_ok(ok) + , m_current_directory(current_directory) + , m_current_file(current_file) + , m_current_format(nullptr) +{ + m_image = image; + m_ok = true; + + m_filename.reserve(1024); + m_filename = core_filename_extract_base(current_file); +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_file_create::~menu_file_create() +{ +} + + +//------------------------------------------------- +// custom_render - perform our special rendering +//------------------------------------------------- + +void menu_file_create::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + extra_text_render(top, bottom, origx1, origy1, origx2, origy2, + m_current_directory, + std::string_view()); +} + + +//------------------------------------------------- +// populate - populates the file creator menu +//------------------------------------------------- + +void menu_file_create::populate(float &customtop, float &custombottom) +{ + std::string buffer; + const image_device_format *format; + const std::string *new_image_name; + + // append the "New Image Name" item + if (get_selection_ref() == ITEMREF_NEW_IMAGE_NAME) + { + buffer = m_filename + "_"; + new_image_name = &buffer; + } + else + { + new_image_name = &m_filename; + } + item_append(_("New Image Name:"), *new_image_name, 0, ITEMREF_NEW_IMAGE_NAME); + + // do we support multiple formats? + if (ENABLE_FORMATS) format = m_image->formatlist().front().get(); + if (ENABLE_FORMATS && (format != nullptr)) + { + item_append(_("Image Format:"), m_current_format->description(), 0, ITEMREF_FORMAT); + m_current_format = format; + } + + // finish up the menu + item_append(menu_item_type::SEPARATOR); + item_append(_("Create"), 0, ITEMREF_CREATE); + + customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + + +//------------------------------------------------- +// handle - file creator menu +//------------------------------------------------- + +void menu_file_create::handle(event const *ev) +{ + // process the event + if (ev) + { + // handle selections + switch (ev->iptkey) + { + case IPT_UI_SELECT: + if ((ev->itemref == ITEMREF_CREATE) || (ev->itemref == ITEMREF_NEW_IMAGE_NAME)) + { + std::string tmp_file(m_filename); + if (tmp_file.find('.') != -1 && tmp_file.find('.') < tmp_file.length() - 1) + { + m_current_file = m_filename; + stack_pop(); + } + else + ui().popup_time(1, "%s", _("Please enter a file extension too")); + } + break; + + case IPT_SPECIAL: + if (get_selection_ref() == ITEMREF_NEW_IMAGE_NAME) + { + input_character(m_filename, ev->unichar, &osd_is_valid_filename_char); + reset(reset_options::REMEMBER_POSITION); + } + break; + case IPT_UI_CANCEL: + m_ok = false; + break; + } + } +} + + +/*************************************************************************** +SELECT FORMAT MENU +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_select_format::menu_select_format(mame_ui_manager &mui, render_container &container, const std::vector &formats, int ext_match, floppy_image_format_t **result) + : menu(mui, container) +{ + m_formats = formats; + m_ext_match = ext_match; + m_result = result; +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_select_format::~menu_select_format() +{ +} + + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_select_format::populate(float &customtop, float &custombottom) +{ + item_append(_("Select image format"), FLAG_DISABLE, nullptr); + for (unsigned int i = 0; i != m_formats.size(); i++) + { + floppy_image_format_t *fmt = m_formats[i]; + + if (i && i == m_ext_match) + item_append(menu_item_type::SEPARATOR); + item_append(fmt->description(), fmt->name(), 0, fmt); + } +} + + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_select_format::handle(event const *ev) +{ + // process the menu + if (ev && ev->iptkey == IPT_UI_SELECT) + { + *m_result = (floppy_image_format_t *)ev->itemref; + stack_pop(); + } +} + + +/*************************************************************************** +SELECT FORMAT MENU +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_select_floppy_init::menu_select_floppy_init(mame_ui_manager &mui, render_container &container, const std::vector &fs, int *result) + : menu(mui, container), + m_fs(fs), + m_result(result) + +{ +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_select_floppy_init::~menu_select_floppy_init() +{ +} + + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_select_floppy_init::populate(float &customtop, float &custombottom) +{ + item_append(_("Select initial contents"), FLAG_DISABLE, nullptr); + int id = 0; + for (const auto &fmt : m_fs) + item_append(fmt.m_description, fmt.m_name, 0, (void *)(uintptr_t)(id++)); +} + + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_select_floppy_init::handle(event const *ev) +{ + // process the menu + if (ev && ev->iptkey == IPT_UI_SELECT) + { + *m_result = int(uintptr_t(ev->itemref)); + stack_pop(); + } +} + + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/filecreate.h b/src/icludes/frontend/mame/ui/filecreate.h new file mode 100644 index 0000000..2debc89 --- /dev/null +++ b/src/icludes/frontend/mame/ui/filecreate.h @@ -0,0 +1,104 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/filecreate.h + + MESS's clunky built-in file manager + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_FILECREATE_H +#define MAME_FRONTEND_UI_FILECREATE_H + +#pragma once + +#include "ui/menu.h" + +#include "imagedev/floppy.h" + + +class floppy_image_format_t; + +namespace ui { + +// ======================> menu_confirm_save_as + +class menu_confirm_save_as : public menu +{ +public: + menu_confirm_save_as(mame_ui_manager &mui, render_container &container, bool *yes); + virtual ~menu_confirm_save_as() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + bool *m_yes; +}; + + +// ======================> menu_file_create + +class menu_file_create : public menu +{ +public: + menu_file_create(mame_ui_manager &mui, render_container &container, device_image_interface *image, std::string ¤t_directory, std::string ¤t_file, bool &ok); + virtual ~menu_file_create() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + bool & m_ok; + device_image_interface * m_image; + std::string & m_current_directory; + std::string & m_current_file; + const image_device_format * m_current_format; + std::string m_filename; +}; + +// ======================> menu_select_format + +class menu_select_format : public menu +{ +public: + menu_select_format(mame_ui_manager &mui, render_container &container, + const std::vector &formats, int ext_match, floppy_image_format_t **result); + virtual ~menu_select_format() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + // internal state + std::vector m_formats; + int m_ext_match; + floppy_image_format_t * *m_result; +}; + +// ======================> menu_select_floppy_init + +class menu_select_floppy_init : public menu +{ +public: + menu_select_floppy_init(mame_ui_manager &mui, render_container &container, + const std::vector &fs, int *result); + virtual ~menu_select_floppy_init() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + // internal state + const std::vector &m_fs; + int * m_result; +}; + + +} // namespace ui + +#endif // MAME_FRONTEND_UI_FILECREATE_H diff --git a/src/icludes/frontend/mame/ui/filemngr.cpp b/src/icludes/frontend/mame/ui/filemngr.cpp new file mode 100644 index 0000000..06c2d82 --- /dev/null +++ b/src/icludes/frontend/mame/ui/filemngr.cpp @@ -0,0 +1,202 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + ui/filemngr.cpp + + MESS's clunky built-in file manager + + TODO + - Restrict directory listing by file extension + +*********************************************************************/ + +#include "emu.h" +#include "ui/filemngr.h" + +#include "ui/filesel.h" +#include "ui/floppycntrl.h" +#include "ui/imgcntrl.h" +#include "ui/miscmenu.h" +#include "ui/ui.h" + +#include "softlist.h" + + +namespace ui { + +/*************************************************************************** + FILE MANAGER +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_file_manager::menu_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings) + : menu(mui, container) + , selected_device(nullptr) + , m_warnings(warnings ? warnings : "") +{ + // The warning string is used when accessing from the force_file_manager call, i.e. + // when the file manager is loaded top front in the case of mandatory image devices +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_file_manager::~menu_file_manager() +{ +} + + +//------------------------------------------------- +// custom_render - perform our special rendering +//------------------------------------------------- + +void menu_file_manager::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + // access the path + std::string_view path = selected_device && selected_device->exists() ? selected_device->filename() : std::string_view(); + extra_text_render(top, bottom, origx1, origy1, origx2, origy2, std::string_view(), path); +} + + +void menu_file_manager::fill_image_line(device_image_interface *img, std::string &instance, std::string &filename) +{ + // get the image type/id + instance = string_format("%s (%s)", img->instance_name(), img->brief_instance_name()); + + // get the base name + if (img->basename() != nullptr) + { + filename.assign(img->basename()); + + // if the image has been loaded through softlist, also show the loaded part + if (img->loaded_through_softlist()) + { + const software_part *tmp = img->part_entry(); + if (!tmp->name().empty()) + { + filename.append(" ("); + filename.append(tmp->name()); + // also check if this part has a specific part_id (e.g. "Map Disc", "Bonus Disc", etc.), and in case display it + if (img->get_feature("part_id") != nullptr) + { + filename.append(": "); + filename.append(img->get_feature("part_id")); + } + filename.append(")"); + } + } + } + else + filename.assign("---"); +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_file_manager::populate(float &customtop, float &custombottom) +{ + std::string tmp_inst, tmp_name; + + if (!m_warnings.empty()) + item_append(m_warnings, FLAG_DISABLE, nullptr); + + // cycle through all devices for this system + bool missing_mandatory = false; + std::unordered_set devtags; + for (device_t &dev : device_enumerator(machine().root_device())) + { + bool tag_appended = false; + if (!devtags.insert(dev.tag()).second) + continue; + + // check whether it owns an image interface + image_interface_enumerator subiter(dev); + if (subiter.first() != nullptr) + { + // if so, cycle through all its image interfaces + for (device_image_interface &scan : subiter) + { + if (!scan.user_loadable()) + continue; + + // if it is a child device, and not something further down the device tree, we want it in the menu! + if (strcmp(scan.device().owner()->tag(), dev.tag()) == 0) + if (devtags.insert(scan.device().tag()).second) + { + if (!scan.basename() && scan.must_be_loaded()) + missing_mandatory = true; + + // check whether we already had some devices with the same owner: if not, output the owner tag! + if (!tag_appended) + { + item_append(string_format(_("[root%1$s]"), dev.tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + tag_appended = true; + } + + // finally, append the image interface to the menu + fill_image_line(&scan, tmp_inst, tmp_name); + item_append(tmp_inst, tmp_name, 0, (void *)&scan); + } + } + } + } + item_append(menu_item_type::SEPARATOR); + + if (m_warnings.empty() || !missing_mandatory) + item_append(m_warnings.empty() ? _("Reset Machine") : _("Start Machine"), 0, (void *)1); + + custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_file_manager::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref && (ev->iptkey == IPT_UI_SELECT)) + { + if ((uintptr_t)ev->itemref == 1) + { + machine().schedule_hard_reset(); + } + else + { + selected_device = (device_image_interface *) ev->itemref; + if (selected_device) + { + floppy_image_device *floppy_device = dynamic_cast(selected_device); + if (floppy_device) + menu::stack_push(ui(), container(), *floppy_device); + else + menu::stack_push(ui(), container(), *selected_device); + + // reset the existing menu + reset(reset_options::REMEMBER_POSITION); + } + } + } +} + +// force file manager menu +void menu_file_manager::force_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings) +{ + // drop any existing menus and start the file manager + menu::stack_reset(mui); + menu::stack_push_special_main(mui, container, warnings); + mui.show_menu(); + + // make sure MAME is paused + mui.machine().pause(); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/filemngr.h b/src/icludes/frontend/mame/ui/filemngr.h new file mode 100644 index 0000000..379912c --- /dev/null +++ b/src/icludes/frontend/mame/ui/filemngr.h @@ -0,0 +1,46 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/filemngr.h + + MESS's clunky built-in file manager + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_FILEMNGR_H +#define MAME_FRONTEND_UI_FILEMNGR_H + +#pragma once + +#include "ui/menu.h" + + +namespace ui { + +class menu_file_manager : public menu +{ +public: + std::string current_directory; + std::string current_file; + device_image_interface *selected_device; + + static void force_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings); + + menu_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings); + virtual ~menu_file_manager(); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void fill_image_line(device_image_interface *img, std::string &instance, std::string &filename); + + std::string const m_warnings; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_FILEMNGR_H diff --git a/src/icludes/frontend/mame/ui/filesel.cpp b/src/icludes/frontend/mame/ui/filesel.cpp new file mode 100644 index 0000000..3da27ae --- /dev/null +++ b/src/icludes/frontend/mame/ui/filesel.cpp @@ -0,0 +1,590 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/filesel.cpp + + MAME's clunky built-in file manager + + TODO + - Restrict empty slot if image required + +***************************************************************************/ + +#include "emu.h" +#include "ui/filesel.h" + +#include "ui/ui.h" +#include "ui/utils.h" + +#include "imagedev/floppy.h" + +#include "corestr.h" +#include "zippath.h" + +#include +#include + + +namespace ui { + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +// conditional compilation to enable chosing of image formats - this is not +// yet fully implemented +#define ENABLE_FORMATS 0 + +// time (in seconds) to display errors +#define ERROR_MESSAGE_TIME 5 + + +/*************************************************************************** + FILE SELECTOR MENU +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_file_selector::menu_file_selector(mame_ui_manager &mui, render_container &container, device_image_interface *image, std::string ¤t_directory, std::string ¤t_file, bool has_empty, bool has_softlist, bool has_create, menu_file_selector::result &result) + : menu(mui, container) + , m_image(image) + , m_current_directory(current_directory) + , m_current_file(current_file) + , m_has_empty(has_empty) + , m_has_softlist(has_softlist) + , m_has_create(has_create) + , m_result(result) +{ + (void)m_image; + set_process_flags(PROCESS_IGNOREPAUSE); +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_file_selector::~menu_file_selector() +{ +} + + +//------------------------------------------------- +// custom_render - perform our special rendering +//------------------------------------------------- + +void menu_file_selector::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + // lay out extra text + auto layout = ui().create_layout(container()); + layout.add_text(m_current_directory); + + // position this extra text + float x1, y1, x2, y2; + extra_text_position(origx1, origx2, origy1, top, layout, -1, x1, y1, x2, y2); + + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color()); + + // take off the borders + x1 += ui().box_lr_border() * machine().render().ui_aspect(&container()); + y1 += ui().box_tb_border(); + + size_t hit_start = 0, hit_span = 0; + if (is_mouse_hit() + && layout.hit_test(get_mouse_x() - x1, get_mouse_y() - y1, hit_start, hit_span) + && m_current_directory.substr(hit_start, hit_span) != PATH_SEPARATOR) + { + // we're hovering over a directory! highlight it + auto target_dir_start = m_current_directory.rfind(PATH_SEPARATOR, hit_start) + 1; + auto target_dir_end = m_current_directory.find(PATH_SEPARATOR, hit_start + hit_span); + m_hover_directory = m_current_directory.substr(0, target_dir_end + strlen(PATH_SEPARATOR)); + + // highlight the text in question + rgb_t fgcolor = ui().colors().mouseover_color(); + rgb_t bgcolor = ui().colors().mouseover_bg_color(); + layout.restyle(target_dir_start, target_dir_end - target_dir_start, &fgcolor, &bgcolor); + } + else + { + // we are not hovering over anything + m_hover_directory.clear(); + } + + // draw the text within it + layout.emit(container(), x1, y1); +} + + +//------------------------------------------------- +// custom_mouse_down - perform our special mouse down +//------------------------------------------------- + +bool menu_file_selector::custom_mouse_down() +{ + if (m_hover_directory.length() > 0) + { + m_current_directory = m_hover_directory; + reset(reset_options::SELECT_FIRST); + return true; + } + + return false; +} + + +//------------------------------------------------- +// compare_file_selector_entries - sorting proc +// for file selector entries +//------------------------------------------------- + +int menu_file_selector::compare_entries(const file_selector_entry *e1, const file_selector_entry *e2) +{ + int result; + const char *e1_basename = e1->basename.c_str(); + const char *e2_basename = e2->basename.c_str(); + + if (e1->type < e2->type) + { + result = -1; + } + else if (e1->type > e2->type) + { + result = 1; + } + else + { + result = core_stricmp(e1_basename, e2_basename); + if (result == 0) + { + result = strcmp(e1_basename, e2_basename); + if (result == 0) + { + if (e1 < e2) + result = -1; + else if (e1 > e2) + result = 1; + } + } + } + + return result; +} + + +//------------------------------------------------- +// append_entry - appends a new +// file selector entry to an entry list +//------------------------------------------------- + +menu_file_selector::file_selector_entry &menu_file_selector::append_entry( + file_selector_entry_type entry_type, + const std::string &entry_basename, + const std::string &entry_fullpath) +{ + return append_entry(entry_type, std::string(entry_basename), std::string(entry_fullpath)); +} + + +//------------------------------------------------- +// append_entry - appends a new +// file selector entry to an entry list +//------------------------------------------------- + +menu_file_selector::file_selector_entry &menu_file_selector::append_entry( + file_selector_entry_type entry_type, + std::string &&entry_basename, + std::string &&entry_fullpath) +{ + // allocate a new entry + file_selector_entry entry; + entry.type = entry_type; + entry.basename = std::move(entry_basename); + entry.fullpath = std::move(entry_fullpath); + + // find the end of the list + return m_entrylist.emplace_back(std::move(entry)); +} + + +//------------------------------------------------- +// append_dirent_entry - appends +// a menu item for a file selector entry +//------------------------------------------------- + +menu_file_selector::file_selector_entry *menu_file_selector::append_dirent_entry(const osd::directory::entry *dirent) +{ + file_selector_entry_type entry_type; + switch (dirent->type) + { + case osd::directory::entry::entry_type::FILE: + entry_type = SELECTOR_ENTRY_TYPE_FILE; + break; + + case osd::directory::entry::entry_type::DIR: + entry_type = SELECTOR_ENTRY_TYPE_DIRECTORY; + break; + + default: + // exceptional case; do not add a menu item + return nullptr; + } + + // determine the full path + std::string buffer = util::zippath_combine(m_current_directory, dirent->name); + + // create the file selector entry + return &append_entry( + entry_type, + dirent->name, + std::move(buffer)); +} + + +//------------------------------------------------- +// append_entry_menu_item - appends +// a menu item for a file selector entry +//------------------------------------------------- + +void menu_file_selector::append_entry_menu_item(const file_selector_entry *entry) +{ + std::string text; + std::string subtext; + + switch(entry->type) + { + case SELECTOR_ENTRY_TYPE_EMPTY: + text = _("[empty slot]"); + break; + + case SELECTOR_ENTRY_TYPE_CREATE: + text = _("[create]"); + break; + + case SELECTOR_ENTRY_TYPE_SOFTWARE_LIST: + text = _("[software list]"); + break; + + case SELECTOR_ENTRY_TYPE_DRIVE: + text = entry->basename; + subtext = "[DRIVE]"; + break; + + case SELECTOR_ENTRY_TYPE_DIRECTORY: + text = entry->basename; + subtext = "[DIR]"; + break; + + case SELECTOR_ENTRY_TYPE_FILE: + text = entry->basename; + subtext = "[FILE]"; + break; + } + item_append(std::move(text), std::move(subtext), 0, (void *) entry); +} + + +//------------------------------------------------- +// select_item +//------------------------------------------------- + +void menu_file_selector::select_item(const file_selector_entry &entry) +{ + switch (entry.type) + { + case SELECTOR_ENTRY_TYPE_EMPTY: + // empty slot - unload + m_result = result::EMPTY; + stack_pop(); + break; + + case SELECTOR_ENTRY_TYPE_CREATE: + // create + m_result = result::CREATE; + stack_pop(); + break; + + case SELECTOR_ENTRY_TYPE_SOFTWARE_LIST: + m_result = result::SOFTLIST; + stack_pop(); + break; + + case SELECTOR_ENTRY_TYPE_DRIVE: + case SELECTOR_ENTRY_TYPE_DIRECTORY: + { + // drive/directory - first check the path + util::zippath_directory::ptr dir; + std::error_condition const err = util::zippath_directory::open(entry.fullpath, dir); + if (err) + { + // this path is problematic; present the user with an error and bail + ui().popup_time(1, _("Error accessing %s"), entry.fullpath); + break; + } + } + m_current_directory.assign(entry.fullpath); + reset(reset_options::SELECT_FIRST); + break; + + case SELECTOR_ENTRY_TYPE_FILE: + // file + m_current_file.assign(entry.fullpath); + m_result = result::FILE; + stack_pop(); + break; + } +} + + +//------------------------------------------------- +// type_search_char +//------------------------------------------------- + +void menu_file_selector::type_search_char(char32_t ch) +{ + std::string const current(m_filename); + if (input_character(m_filename, ch, uchar_is_printable)) + { + ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename); + + file_selector_entry const *const cur_selected(reinterpret_cast(get_selection_ref())); + + // if it's a perfect match for the current selection, don't move it + if (!cur_selected || core_strnicmp(cur_selected->basename.c_str(), m_filename.c_str(), m_filename.size())) + { + std::string::size_type bestmatch(0); + file_selector_entry const *selected_entry(cur_selected); + for (auto &entry : m_entrylist) + { + // TODO: more efficient "common prefix" code + std::string::size_type match(0); + for (std::string::size_type i = 1; m_filename.size() >= i; ++i) + { + if (!core_strnicmp(entry.basename.c_str(), m_filename.c_str(), i)) + match = i; + else + break; + } + + if (match > bestmatch) + { + bestmatch = match; + selected_entry = &entry; + } + } + + if (selected_entry && (selected_entry != cur_selected)) + { + set_selection((void *)selected_entry); + centre_selection(); + } + } + } +} + + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_file_selector::populate(float &customtop, float &custombottom) +{ + const file_selector_entry *selected_entry = nullptr; + + // clear out the menu entries + m_entrylist.clear(); + + // open the directory + util::zippath_directory::ptr directory; + std::error_condition const err = util::zippath_directory::open(m_current_directory, directory); + + // add the "[empty slot]" entry if available + if (m_has_empty) + append_entry(SELECTOR_ENTRY_TYPE_EMPTY, "", ""); + + // add the "[create]" entry + if (m_has_create && directory && !directory->is_archive()) + append_entry(SELECTOR_ENTRY_TYPE_CREATE, "", ""); + + // add and select the "[software list]" entry if available + if (m_has_softlist) + selected_entry = &append_entry(SELECTOR_ENTRY_TYPE_SOFTWARE_LIST, "", ""); + + // add the drives + for (std::string const &volume_name : osd_get_volume_names()) + append_entry(SELECTOR_ENTRY_TYPE_DRIVE, volume_name, volume_name); + + // mark first filename entry + std::size_t const first = m_entrylist.size() + 1; + + // build the menu for each item + if (err) + { + osd_printf_verbose( + "menu_file_selector::populate: error opening directory '%s' (%s:%d %s)\n", + m_current_directory, err.category().name(), err.value(), err.message()); + } + else + { + for (osd::directory::entry const *dirent = directory->readdir(); dirent; dirent = directory->readdir()) + { + // append a dirent entry + file_selector_entry const *entry = append_dirent_entry(dirent); + if (entry) + { + // set the selected item to be the first non-parent directory or file + if (!selected_entry && strcmp(dirent->name, "..")) + selected_entry = entry; + + // do we have to select this file? + if (!core_stricmp(m_current_file.c_str(), dirent->name)) + selected_entry = entry; + } + } + } + directory.reset(); + + if (m_entrylist.size() > first) + { + // sort the menu entries + const std::collate &coll = std::use_facet>(std::locale()); + std::sort( + m_entrylist.begin() + first, + m_entrylist.end(), + [&coll] (file_selector_entry const &x, file_selector_entry const &y) + { + std::wstring const xstr = wstring_from_utf8(x.basename); + std::wstring const ystr = wstring_from_utf8(y.basename); + return coll.compare(xstr.data(), xstr.data()+xstr.size(), ystr.data(), ystr.data()+ystr.size()) < 0; + }); + } + + // append all of the menu entries + for (file_selector_entry const &entry : m_entrylist) + append_entry_menu_item(&entry); + + // set the selection (if we have one) + if (selected_entry) + set_selection((void *)selected_entry); + + // set up custom render proc + customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_file_selector::handle(event const *ev) +{ + // process the menu + if (ev) + { + if (ev->iptkey == IPT_SPECIAL) + { + // if it's any other key and we're not maxed out, update + type_search_char(ev->unichar); + } + else if (ev->iptkey == IPT_UI_CANCEL) + { + // reset the char buffer also in this case + if (!m_filename.empty()) + { + m_filename.clear(); + ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename); + } + } + else if (ev->itemref && (ev->iptkey == IPT_UI_SELECT)) + { + // handle selections + select_item(*reinterpret_cast(ev->itemref)); + + // reset the char buffer when pressing IPT_UI_SELECT + m_filename.clear(); + } + } +} + + + +/*************************************************************************** + SELECT RW +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_select_rw::menu_select_rw(mame_ui_manager &mui, render_container &container, + bool can_in_place, result &result) + : menu(mui, container), + m_can_in_place(can_in_place), + m_result(result) +{ +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_select_rw::~menu_select_rw() +{ +} + + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_select_rw::populate(float &customtop, float &custombottom) +{ + item_append(_("Select access mode"), FLAG_DISABLE, nullptr); + item_append(_("Read-only"), 0, itemref_from_result(result::READONLY)); + if (m_can_in_place) + item_append(_("Read-write"), 0, itemref_from_result(result::READWRITE)); + item_append(_("Read this image, write to another image"), 0, itemref_from_result(result::WRITE_OTHER)); + item_append(_("Read this image, write to diff"), 0, itemref_from_result(result::WRITE_DIFF)); +} + + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_select_rw::handle(event const *ev) +{ + // process the menu + if (ev && ev->iptkey == IPT_UI_SELECT) + { + m_result = result_from_itemref(ev->itemref); + stack_pop(); + } +} + + +//------------------------------------------------- +// itemref_from_result +//------------------------------------------------- + +void *menu_select_rw::itemref_from_result(menu_select_rw::result result) +{ + return (void *)(uintptr_t)(unsigned int)result; +} + + +//------------------------------------------------- +// result_from_itemref +//------------------------------------------------- + +menu_select_rw::result menu_select_rw::result_from_itemref(void *itemref) +{ + return (menu_select_rw::result) (unsigned int) (uintptr_t)itemref; +} + + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/filesel.h b/src/icludes/frontend/mame/ui/filesel.h new file mode 100644 index 0000000..11e5643 --- /dev/null +++ b/src/icludes/frontend/mame/ui/filesel.h @@ -0,0 +1,133 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/filesel.h + + MESS's clunky built-in file manager + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_FILESEL_H +#define MAME_FRONTEND_UI_FILESEL_H + +#pragma once + +#include "ui/menu.h" + +namespace ui { + +// ======================> menu_file_selector + +class menu_file_selector : public menu +{ +public: + enum class result + { + INVALID = -1, + EMPTY = 0x1000, + SOFTLIST, + CREATE, + FILE + }; + + menu_file_selector( + mame_ui_manager &mui, + render_container &container, + device_image_interface *image, + std::string ¤t_directory, + std::string ¤t_file, + bool has_empty, + bool has_softlist, + bool has_create, + result &result); + virtual ~menu_file_selector() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual bool custom_ui_cancel() override { return !m_filename.empty(); } + virtual bool custom_mouse_down() override; + +private: + enum file_selector_entry_type + { + SELECTOR_ENTRY_TYPE_EMPTY, + SELECTOR_ENTRY_TYPE_CREATE, + SELECTOR_ENTRY_TYPE_SOFTWARE_LIST, + SELECTOR_ENTRY_TYPE_DRIVE, + SELECTOR_ENTRY_TYPE_DIRECTORY, + SELECTOR_ENTRY_TYPE_FILE + }; + + struct file_selector_entry + { + file_selector_entry() = default; + file_selector_entry(file_selector_entry &&) = default; + file_selector_entry &operator=(file_selector_entry &&) = default; + + file_selector_entry_type type = SELECTOR_ENTRY_TYPE_EMPTY; + std::string basename; + std::string fullpath; + }; + + // internal state + device_image_interface *const m_image; + std::string & m_current_directory; + std::string & m_current_file; + bool const m_has_empty; + bool const m_has_softlist; + bool const m_has_create; + result & m_result; + std::vector m_entrylist; + std::string m_hover_directory; + std::string m_filename; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + // methods + int compare_entries(const file_selector_entry *e1, const file_selector_entry *e2); + file_selector_entry &append_entry(file_selector_entry_type entry_type, const std::string &entry_basename, const std::string &entry_fullpath); + file_selector_entry &append_entry(file_selector_entry_type entry_type, std::string &&entry_basename, std::string &&entry_fullpath); + file_selector_entry *append_dirent_entry(const osd::directory::entry *dirent); + void append_entry_menu_item(const file_selector_entry *entry); + void select_item(const file_selector_entry &entry); + void type_search_char(char32_t ch); +}; + + +// ======================> menu_select_rw + +class menu_select_rw : public menu +{ +public: + enum class result + { + INVALID = -1, + READONLY = 0x3000, + READWRITE, + WRITE_OTHER, + WRITE_DIFF + }; + menu_select_rw( + mame_ui_manager &mui, + render_container &container, + bool can_in_place, + result &result); + virtual ~menu_select_rw() override; + + static void *itemref_from_result(result result); + static result result_from_itemref(void *itemref); + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + // internal state + bool m_can_in_place; + result & m_result; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_FILESEL_H diff --git a/src/icludes/frontend/mame/ui/floppycntrl.cpp b/src/icludes/frontend/mame/ui/floppycntrl.cpp new file mode 100644 index 0000000..ee7740c --- /dev/null +++ b/src/icludes/frontend/mame/ui/floppycntrl.cpp @@ -0,0 +1,188 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + ui/floppycntrl.cpp + +***************************************************************************/ + +#include "emu.h" + +#include "ui/filesel.h" +#include "ui/filecreate.h" +#include "ui/floppycntrl.h" + +#include "zippath.h" + + +namespace ui { + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +menu_control_floppy_image::menu_control_floppy_image(mame_ui_manager &mui, render_container &container, device_image_interface &image) : + menu_control_device_image(mui, container, image), + fd(dynamic_cast(image)), + input_format(nullptr), + output_format(nullptr), + create_fs(nullptr), + input_filename(), + output_filename() +{ +} + +menu_control_floppy_image::~menu_control_floppy_image() +{ +} + +void menu_control_floppy_image::do_load_create() +{ + if(input_filename.compare("")==0) { + image_init_result err = fd.create(output_filename, nullptr, nullptr); + if (err != image_init_result::PASS) { + machine().popmessage("Error: %s", fd.error()); + return; + } + if (create_fs) { + // HACK: ensure the floppy_image structure is created since device_image_interface may not otherwise do so during "init phase" + err = fd.finish_load(); + if (err == image_init_result::PASS) { + fs_meta_data meta; + fd.init_fs(create_fs, meta); + } + } + } else { + image_init_result err = fd.load(input_filename); + if ((err == image_init_result::PASS) && (output_filename.compare("") != 0)) + err = fd.reopen_for_write(output_filename) ? image_init_result::FAIL : image_init_result::PASS; + if (err != image_init_result::PASS) { + machine().popmessage("Error: %s", fd.error()); + return; + } + } + if(output_format) + fd.setup_write(output_format); +} + +void menu_control_floppy_image::hook_load(const std::string &filename) +{ + input_filename = filename; + input_format = static_cast(m_image).identify(filename); + + if (!input_format) + { + machine().popmessage("Error: %s\n", m_image.error()); + stack_pop(); + } + else + { + bool can_in_place = input_format->supports_save(); + if(can_in_place) { + std::string tmp_path; + util::core_file::ptr tmp_file; + // attempt to open the file for writing but *without* create + std::error_condition const filerr = util::zippath_fopen(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE, tmp_file, tmp_path); + if(!filerr) + tmp_file.reset(); + else + can_in_place = false; + } + m_submenu_result.rw = menu_select_rw::result::INVALID; + menu::stack_push(ui(), container(), can_in_place, m_submenu_result.rw); + m_state = SELECT_RW; + } +} + +void menu_control_floppy_image::menu_activated() +{ + switch (m_state) { + case DO_CREATE: { + std::vector format_array; + for(floppy_image_format_t *i : fd.get_formats()) { + if(!i->supports_save()) + continue; + if (i->extension_matches(m_current_file.c_str())) + format_array.push_back(i); + } + int ext_match = format_array.size(); + for(floppy_image_format_t *i : fd.get_formats()) { + if(!i->supports_save()) + continue; + if (!i->extension_matches(m_current_file.c_str())) + format_array.push_back(i); + } + output_format = nullptr; + menu::stack_push(ui(), container(), format_array, ext_match, &output_format); + + m_state = SELECT_FORMAT; + break; + } + + case SELECT_FORMAT: + if(!output_format) { + m_state = START_FILE; + menu_activated(); + } else { + const auto &fs = fd.get_create_fs(); + output_filename = util::zippath_combine(m_current_directory, m_current_file); + if(fs.size() == 1) { + create_fs = &fs[0]; + do_load_create(); + stack_pop(); + } else { + m_submenu_result.i = -1; + menu::stack_push(ui(), container(), fs, &m_submenu_result.i); + m_state = SELECT_INIT; + } + } + break; + + case SELECT_INIT: + if(m_submenu_result.i == -1) { + m_state = START_FILE; + menu_activated(); + } else { + create_fs = &fd.get_create_fs()[m_submenu_result.i]; + do_load_create(); + stack_pop(); + } + break; + + case SELECT_RW: + switch(m_submenu_result.rw) { + case menu_select_rw::result::READONLY: + do_load_create(); + fd.setup_write(nullptr); + stack_pop(); + break; + + case menu_select_rw::result::READWRITE: + output_format = input_format; + do_load_create(); + stack_pop(); + break; + + case menu_select_rw::result::WRITE_DIFF: + machine().popmessage("Sorry, diffs are not supported yet\n"); + stack_pop(); + break; + + case menu_select_rw::result::WRITE_OTHER: + menu::stack_push(ui(), container(), &m_image, m_current_directory, m_current_file, m_create_ok); + m_state = CHECK_CREATE; + break; + + case menu_select_rw::result::INVALID: + m_state = START_FILE; + menu_activated(); + break; + } + break; + + default: + menu_control_device_image::menu_activated(); + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/floppycntrl.h b/src/icludes/frontend/mame/ui/floppycntrl.h new file mode 100644 index 0000000..bdc1104 --- /dev/null +++ b/src/icludes/frontend/mame/ui/floppycntrl.h @@ -0,0 +1,46 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + ui/floppycntrl.h + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_FLOPPYCNTRL_H +#define MAME_FRONTEND_UI_FLOPPYCNTRL_H + +#pragma once + +#include "ui/imgcntrl.h" + +#include "imagedev/floppy.h" +#include "formats/flopimg.h" + +#include + + +namespace ui { + +class menu_control_floppy_image : public menu_control_device_image +{ +public: + menu_control_floppy_image(mame_ui_manager &ui, render_container &container, device_image_interface &image); + virtual ~menu_control_floppy_image() override; + +protected: + virtual void menu_activated() override; + +private: + enum { SELECT_FORMAT = LAST_ID, SELECT_MEDIA, SELECT_INIT, SELECT_RW }; + + floppy_image_device &fd; + floppy_image_format_t *input_format, *output_format; + const floppy_image_device::fs_info *create_fs; + std::string input_filename, output_filename; + + void do_load_create(); + virtual void hook_load(const std::string &filename) override; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_FLOPPYCNTRL_H diff --git a/src/icludes/frontend/mame/ui/icorender.cpp b/src/icludes/frontend/mame/ui/icorender.cpp new file mode 100644 index 0000000..363e64b --- /dev/null +++ b/src/icludes/frontend/mame/ui/icorender.cpp @@ -0,0 +1,333 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Victor Laskin +/*************************************************************************** + + ui/icorender.h + + Windows icon file parser. + + Previously based on code by Victor Laskin (victor.laskin@gmail.com) + http://vitiy.info/Code/ico.cpp + + TODO: + * Add variant that loads all images from the file + * Allow size hint for choosing best candidate + * Allow selecting amongst candidates based on colour depth + +***************************************************************************/ + +#include "emu.h" +#include "icorender.h" + +#include "util/msdib.h" +#include "util/png.h" + +#include +#include +#include +#include + +// need to set LOG_OUTPUT_FUNC or LOG_OUTPUT_STREAM because there's no logerror outside devices +#define LOG_OUTPUT_FUNC osd_printf_verbose + +#define LOG_GENERAL (1U << 0) + +//#define VERBOSE (LOG_GENERAL | LOG_DIB) + +#include "logmacro.h" + + +namespace ui { + +namespace { + +// ICO file header +struct icon_dir_t +{ + uint16_t reserved; // must be 0 + uint16_t type; // 1 for icon or 2 for cursor + uint16_t count; // number of images in the file +}; + +// ICO file directory entry +struct icon_dir_entry_t +{ + constexpr unsigned get_width() const { return width ? width : 256U; } + constexpr unsigned get_height() const { return height ? height : 256U; } + + void byteswap() + { + planes = little_endianize_int16(planes); + bpp = little_endianize_int16(bpp); + size = little_endianize_int32(size); + offset = little_endianize_int32(offset); + } + + uint8_t width; // 0 means 256 + uint8_t height; // 0 means 256 + uint8_t colors; // for indexed colour, or 0 for direct colour + uint8_t reserved; // documentation says this should be 0 but .NET writes 255 + uint16_t planes; // or hotspot X for cursor + uint16_t bpp; // 0 to infer from image data, or hotspot Y for cursor + uint32_t size; // image data size in bytes + uint32_t offset; // offset to image data from start of file +}; + + +bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap) +{ + // skip out if the data isn't a reasonable size - PNG magic alone is eight bytes + if (9U >= dir.size) + return false; + fp.seek(dir.offset, SEEK_SET); + std::error_condition const err(util::png_read_bitmap(fp, bitmap)); + if (!err) + { + // found valid PNG image + assert(bitmap.valid()); + if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height()))) + { + LOG("Loaded %d*%d pixel PNG image from ICO file\n", bitmap.width(), bitmap.height()); + } + else + { + LOG( + "Loaded %d*%d pixel PNG image from ICO file (directory indicated %u*%u)\n", + bitmap.width(), + bitmap.height(), + dir.get_width(), + dir.get_height()); + } + return true; + } + else if (util::png_error::BAD_SIGNATURE == err) + { + // doesn't look like PNG data - just fall back to DIB without the file header + return false; + } + else + { + // invalid PNG data or I/O error + LOG( + "Error %s:%d %s reading PNG image data from ICO file at offset %u (directory size %u)\n", + err.category().name(), + err.value(), + err.message(), + dir.offset, + dir.size); + return false; + } +} + + +bool load_ico_dib(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap) +{ + fp.seek(dir.offset, SEEK_SET); + util::msdib_error const err(util::msdib_read_bitmap_data(fp, bitmap, dir.size, dir.get_height())); + switch (err) + { + case util::msdib_error::NONE: + // found valid DIB image + assert(bitmap.valid()); + if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height()))) + { + LOG("Loaded %d*%d pixel DIB image from ICO file\n", bitmap.width(), bitmap.height()); + } + else + { + LOG( + "Loaded %d*%d pixel DIB image from ICO file (directory indicated %u*%u)\n", + bitmap.width(), + bitmap.height(), + dir.get_width(), + dir.get_height()); + } + return true; + + default: + // invalid DIB data or I/O error + LOG( + "Error %u reading DIB image data from ICO file at offset %u (directory size %u)\n", + unsigned(err), + dir.offset, + dir.size); + return false; + } +} + + +bool load_ico_image(util::core_file &fp, unsigned index, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap) +{ + // try loading PNG image data (contains PNG file magic if used), and then fall back + if (load_ico_png(fp, dir, bitmap)) + { + LOG("Successfully loaded PNG image from ICO file entry %u\n", index); + return true; + } + else if (load_ico_dib(fp, dir, bitmap)) + { + LOG("Successfully loaded DIB image from ICO file entry %u\n", index); + return true; + } + + // no luck + return false; +} + + +bool load_ico_image(util::core_file &fp, unsigned count, unsigned index, bitmap_argb32 &bitmap) +{ + // read the directory entry + std::error_condition err; + size_t actual; + icon_dir_entry_t dir; + err = fp.seek(sizeof(icon_dir_t) + (sizeof(icon_dir_entry_t) * index), SEEK_SET); + if (!err) + err = fp.read(&dir, sizeof(dir), actual); + if (err || (sizeof(dir) != actual)) + { + LOG("Failed to read ICO file directory entry %u\n", index); + return false; + } + dir.byteswap(); + if ((sizeof(icon_dir_t) + (sizeof(icon_dir_entry_t) * count)) > dir.offset) + { + LOG( + "ICO file image %u data starting at %u overlaps %u bytes of file header and directory\n", + index, + dir.offset, + sizeof(icon_dir_t) + (sizeof(icon_dir_entry_t) * count)); + return false; + } + else + { + return load_ico_image(fp, index, dir, bitmap); + } +} + +} // anonymous namespace + + +int images_in_ico(util::core_file &fp) +{ + // read and check the icon file header + std::error_condition err; + size_t actual; + icon_dir_t header; + err = fp.seek(0, SEEK_SET); + if (!err) + err = fp.read(&header, sizeof(header), actual); + if (err || (sizeof(header) != actual)) + { + LOG("Failed to read ICO file header\n"); + return -1; + } + header.reserved = little_endianize_int16(header.reserved); + header.type = little_endianize_int16(header.type); + header.count = little_endianize_int16(header.count); + if (0U != header.reserved) + { + LOG("Invalid ICO file header reserved field %u (expected 0)\n", header.reserved); + return -1; + } + if ((1U != header.type) && (2U != header.type)) + { + LOG("Invalid ICO file header type field %u (expected 1 or 2)\n", header.type); + return -1; + } + return int(unsigned(little_endianize_int16(header.count))); +} + + +void render_load_ico(util::core_file &fp, unsigned index, bitmap_argb32 &bitmap) +{ + // check that these things haven't been padded somehow + static_assert(sizeof(icon_dir_t) == 6U, "compiler has applied padding to icon_dir_t"); + static_assert(sizeof(icon_dir_entry_t) == 16U, "compiler has applied padding to icon_dir_entry_t"); + + // read and check the icon file header, then try to load the specified image + int const count(images_in_ico(fp)); + if (0 > count) + { + // images_in_ico already logged an error + } + else if (index >= count) + { + osd_printf_verbose("Requested image %u from ICO file containing %d images\n", index, count); + } + else if (load_ico_image(fp, count, index, bitmap)) + { + return; + } + bitmap.reset(); +} + + +void render_load_ico_first(util::core_file &fp, bitmap_argb32 &bitmap) +{ + int const count(images_in_ico(fp)); + for (int i = 0; count > i; ++i) + { + if (load_ico_image(fp, count, i, bitmap)) + return; + } + bitmap.reset(); +} + + +void render_load_ico_highest_detail(util::core_file &fp, bitmap_argb32 &bitmap) +{ + // read and check the icon file header - logs a message on error + int const count(images_in_ico(fp)); + if (0 <= count) + { + // now load all the directory entries + size_t const dir_bytes(sizeof(icon_dir_entry_t) * count); + std::unique_ptr dir(new (std::nothrow) icon_dir_entry_t [count]); + std::unique_ptr index(new (std::nothrow) unsigned [count]); + size_t actual; + if (count && (!dir || !index || fp.read(dir.get(), dir_bytes, actual) || (dir_bytes != actual))) + { + LOG("Failed to read ICO file directory entries\n"); + } + else + { + // byteswap and sort by (pixels, depth) + for (int i = 0; count > i; ++i) + { + dir[i].byteswap(); + index[i] = i; + } + std::stable_sort( + index.get(), + index.get() + count, + [&dir] (unsigned x, unsigned y) + { + unsigned const x_pixels(dir[x].get_width() * dir[x].get_height()); + unsigned const y_pixels(dir[y].get_width() * dir[y].get_height()); + if (x_pixels > y_pixels) + return true; + else if (x_pixels < y_pixels) + return false; + else + return dir[x].bpp > dir[y].bpp; + }); + + // walk down until something works + for (int i = 0; count > i; ++i) + { + LOG( + "Try loading ICO file entry %u: %u*%u, %u bits per pixel\n", + index[i], + dir[index[i]].get_width(), + dir[index[i]].get_height(), + dir[index[i]].bpp); + if (load_ico_image(fp, index[i], dir[index[i]], bitmap)) + return; + } + } + } + bitmap.reset(); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/icorender.h b/src/icludes/frontend/mame/ui/icorender.h new file mode 100644 index 0000000..bb3f1a0 --- /dev/null +++ b/src/icludes/frontend/mame/ui/icorender.h @@ -0,0 +1,34 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ui/icorender.h + + Windows icon file parser. + + File handles passed to these functions must support read and seek + operations. + +***************************************************************************/ +#ifndef MAME_FRONTEND_MAME_UI_ICORENDER_H +#define MAME_FRONTEND_MAME_UI_ICORENDER_H + +#pragma once + +namespace ui { + +// get number of images in icon file (-1 on error) +int images_in_ico(util::core_file &fp); + +// load specified icon from file (zero-based) +void render_load_ico(util::core_file &fp, unsigned index, bitmap_argb32 &bitmap); + +// load first supported icon from file +void render_load_ico_first(util::core_file &fp, bitmap_argb32 &bitmap); + +// load highest detail supported icon from file +void render_load_ico_highest_detail(util::core_file &fp, bitmap_argb32 &bitmap); + +} // namespace ui + +#endif // MAME_FRONTEND_MAME_UI_ICORENDER_H diff --git a/src/icludes/frontend/mame/ui/imgcntrl.cpp b/src/icludes/frontend/mame/ui/imgcntrl.cpp new file mode 100644 index 0000000..5708a4a --- /dev/null +++ b/src/icludes/frontend/mame/ui/imgcntrl.cpp @@ -0,0 +1,380 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/imgcntrl.cpp + + MAME's clunky built-in file manager + +***************************************************************************/ + +#include "emu.h" + +#include "ui/imgcntrl.h" + +#include "ui/ui.h" +#include "ui/filesel.h" +#include "ui/filecreate.h" +#include "ui/swlist.h" + +#include "audit.h" +#include "drivenum.h" +#include "emuopts.h" +#include "image.h" +#include "softlist_dev.h" +#include "zippath.h" + + +namespace ui { +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_control_device_image::menu_control_device_image(mame_ui_manager &mui, render_container &container, device_image_interface &image) + : menu(mui, container) + , m_image(image) + , m_create_ok(false) + , m_create_confirmed(false) +{ + m_submenu_result.i = -1; + + if (m_image.software_list_name()) + m_sld = software_list_device::find_by_name(mui.machine().config(), m_image.software_list_name()); + else + m_sld = nullptr; + m_swi = m_image.software_entry(); + m_swp = m_image.part_entry(); + + if (m_swi != nullptr) + { + m_state = START_OTHER_PART; + m_current_directory = m_image.working_directory(); + + // check to see if we've never initialized the working directory + if (m_current_directory.empty()) + { + m_current_directory = machine().image().setup_working_directory(); + m_image.set_working_directory(m_current_directory); + } + } + else + { + m_state = START_FILE; + + // if the image exists, set the working directory to the parent directory + if (m_image.exists()) + { + m_current_file.assign(m_image.filename()); + m_current_directory = util::zippath_parent(m_current_file); + } + else + { + m_current_directory = m_image.working_directory(); + + // check to see if we've never initialized the working directory + if (m_current_directory.empty()) + { + m_current_directory = machine().image().setup_working_directory(); + m_image.set_working_directory(m_current_directory); + } + } + + // check to see if the path exists; if not then set to current directory + util::zippath_directory::ptr dir; + if (util::zippath_directory::open(m_current_directory, dir)) + osd_get_full_path(m_current_directory, "."); + } +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_control_device_image::~menu_control_device_image() +{ +} + + +//------------------------------------------------- +// test_create - creates a new disk image +//------------------------------------------------- + +void menu_control_device_image::test_create(bool &can_create, bool &need_confirm) +{ + // assemble the full path + auto path = util::zippath_combine(m_current_directory, m_current_file); + + // does a file or a directory exist at the path + auto entry = osd_stat(path); + auto file_type = (entry != nullptr) ? entry->type : osd::directory::entry::entry_type::NONE; + + switch(file_type) + { + case osd::directory::entry::entry_type::NONE: + // no file/dir here - always create + can_create = true; + need_confirm = false; + break; + + case osd::directory::entry::entry_type::FILE: + // a file exists here - ask for permission from the user + can_create = true; + need_confirm = true; + break; + + case osd::directory::entry::entry_type::DIR: + // a directory exists here - we can't save over it + ui().popup_time(5, "%s", _("Cannot save over directory")); + can_create = false; + need_confirm = false; + break; + + default: + can_create = false; + need_confirm = false; + fatalerror("Unexpected\n"); + } +} + + +//------------------------------------------------- +// load_software_part +//------------------------------------------------- + +void menu_control_device_image::load_software_part() +{ + std::string temp_name = string_format("%s:%s:%s", m_sld->list_name(), m_swi->shortname(), m_swp->name()); + + driver_enumerator drivlist(machine().options(), machine().options().system_name()); + drivlist.next(); + media_auditor auditor(drivlist); + media_auditor::summary summary = auditor.audit_software(*m_sld, *m_swi, AUDIT_VALIDATE_FAST); + // if everything looks good, load software + if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED) + { + m_image.load_software(temp_name); + stack_pop(); + } + else + { + machine().popmessage(_("The software selected is missing one or more required ROM or CHD images.\nPlease acquire the correct files or select a different one.")); + m_state = SELECT_SOFTLIST; + menu_activated(); + } +} + + +//------------------------------------------------- +// hook_load +//------------------------------------------------- + +void menu_control_device_image::hook_load(const std::string &name) +{ + m_image.load(name); + stack_pop(); +} + + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_control_device_image::populate(float &customtop, float &custombottom) +{ + throw emu_fatalerror("menu_control_device_image::populate: Shouldn't get here!"); +} + + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_control_device_image::handle(event const *ev) +{ + throw emu_fatalerror("menu_control_device_image::handle: Shouldn't get here!"); +} + + +//------------------------------------------------- +// menu_activated +//------------------------------------------------- + +void menu_control_device_image::menu_activated() +{ + switch(m_state) + { + case START_FILE: + m_submenu_result.filesel = menu_file_selector::result::INVALID; + menu::stack_push(ui(), container(), &m_image, m_current_directory, m_current_file, true, m_image.image_interface()!=nullptr, m_image.is_creatable(), m_submenu_result.filesel); + m_state = SELECT_FILE; + break; + + case START_SOFTLIST: + m_sld = nullptr; + menu::stack_push(ui(), container(), m_image.image_interface(), &m_sld); + m_state = SELECT_SOFTLIST; + break; + + case START_OTHER_PART: + m_submenu_result.swparts = menu_software_parts::result::INVALID; + menu::stack_push(ui(), container(), m_swi, m_swp->interface().c_str(), &m_swp, true, m_submenu_result.swparts); + m_state = SELECT_OTHER_PART; + break; + + case SELECT_SOFTLIST: + if (!m_sld) + { + stack_pop(); + } + else + { + m_software_info_name.clear(); + menu::stack_push(ui(), container(), m_sld, m_image.image_interface(), m_software_info_name); + m_state = SELECT_PARTLIST; + } + break; + + case SELECT_PARTLIST: + m_swi = m_sld->find(m_software_info_name); + if (!m_swi) + { + m_state = START_SOFTLIST; + menu_activated(); + } + else if (m_swi->has_multiple_parts(m_image.image_interface())) + { + m_submenu_result.swparts = menu_software_parts::result::INVALID; + m_swp = nullptr; + menu::stack_push(ui(), container(), m_swi, m_image.image_interface(), &m_swp, false, m_submenu_result.swparts); + m_state = SELECT_ONE_PART; + } + else + { + m_swp = m_swi->find_part("", m_image.image_interface()); + load_software_part(); + } + break; + + case SELECT_ONE_PART: + switch (m_submenu_result.swparts) + { + case menu_software_parts::result::ENTRY: + load_software_part(); + break; + + default: // return to list + m_state = SELECT_SOFTLIST; + menu_activated(); + break; + } + break; + + case SELECT_OTHER_PART: + switch (m_submenu_result.swparts) + { + case menu_software_parts::result::ENTRY: + load_software_part(); + break; + + case menu_software_parts::result::FMGR: + m_state = START_FILE; + menu_activated(); + break; + + case menu_software_parts::result::EMPTY: + m_image.unload(); + stack_pop(); + break; + + case menu_software_parts::result::SWLIST: + m_state = START_SOFTLIST; + menu_activated(); + break; + + case menu_software_parts::result::INVALID: // return to system + stack_pop(); + break; + } + break; + + case SELECT_FILE: + switch (m_submenu_result.filesel) + { + case menu_file_selector::result::EMPTY: + m_image.unload(); + stack_pop(); + break; + + case menu_file_selector::result::FILE: + hook_load(m_current_file); + break; + + case menu_file_selector::result::CREATE: + menu::stack_push(ui(), container(), &m_image, m_current_directory, m_current_file, m_create_ok); + m_state = CHECK_CREATE; + break; + + case menu_file_selector::result::SOFTLIST: + m_state = START_SOFTLIST; + menu_activated(); + break; + + default: // return to system + stack_pop(); + break; + } + break; + + case CREATE_FILE: + { + bool can_create, need_confirm; + test_create(can_create, need_confirm); + if (can_create) + { + if (need_confirm) + { + menu::stack_push(ui(), container(), &m_create_confirmed); + m_state = CREATE_CONFIRM; + } + else + { + m_state = DO_CREATE; + menu_activated(); + } + } + else + { + m_state = START_FILE; + menu_activated(); + } + } + break; + + case CREATE_CONFIRM: + m_state = m_create_confirmed ? DO_CREATE : START_FILE; + menu_activated(); + break; + + case CHECK_CREATE: + m_state = m_create_ok ? CREATE_FILE : START_FILE; + menu_activated(); + break; + + case DO_CREATE: + { + auto path = util::zippath_combine(m_current_directory, m_current_file); + image_init_result err = m_image.create(path, nullptr, nullptr); + if (err != image_init_result::PASS) + machine().popmessage("Error: %s", m_image.error()); + stack_pop(); + } + break; + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/imgcntrl.h b/src/icludes/frontend/mame/ui/imgcntrl.h new file mode 100644 index 0000000..66a5ee5 --- /dev/null +++ b/src/icludes/frontend/mame/ui/imgcntrl.h @@ -0,0 +1,78 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/imgcntrl.h + + MESS's clunky built-in file manager + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_IMAGECNTRL_H +#define MAME_FRONTEND_UI_IMAGECNTRL_H + +#pragma once + +#include "ui/filesel.h" +#include "ui/menu.h" +#include "ui/swlist.h" + + +namespace ui { + +// ======================> menu_control_device_image + +class menu_control_device_image : public menu +{ +public: + menu_control_device_image(mame_ui_manager &mui, render_container &container, device_image_interface &image); + virtual ~menu_control_device_image() override; + +protected: + enum + { + START_FILE, START_OTHER_PART, START_SOFTLIST, + SELECT_PARTLIST, SELECT_ONE_PART, SELECT_OTHER_PART, + SELECT_FILE, CREATE_FILE, CREATE_CONFIRM, CHECK_CREATE, DO_CREATE, SELECT_SOFTLIST, + LAST_ID + }; + + // this is a single union that contains all of the different types of + // results we could get from child menus + union + { + menu_file_selector::result filesel; + menu_software_parts::result swparts; + menu_select_rw::result rw; + int i; + } m_submenu_result; + + // instance variables - made protected so they can be shared with floppycntrl.cpp + int m_state; + device_image_interface & m_image; + std::string m_current_directory; + std::string m_current_file; + bool m_create_ok; + + // methods + virtual void menu_activated() override; + virtual void handle(event const *ev) override; + virtual void hook_load(const std::string &filename); + +private: + // instance variables + bool m_create_confirmed; + const software_info * m_swi; + const software_part * m_swp; + class software_list_device * m_sld; + std::string m_software_info_name; + + // methods + virtual void populate(float &customtop, float &custombottom) override; + void test_create(bool &can_create, bool &need_confirm); + void load_software_part(); +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_IMAGECNTRL_H diff --git a/src/icludes/frontend/mame/ui/info.cpp b/src/icludes/frontend/mame/ui/info.cpp new file mode 100644 index 0000000..d7ed210 --- /dev/null +++ b/src/icludes/frontend/mame/ui/info.cpp @@ -0,0 +1,685 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/info.cpp + + System and image info screens + +***************************************************************************/ + +#include "emu.h" +#include "ui/info.h" + +#include "ui/systemlist.h" +#include "ui/ui.h" + +#include "drivenum.h" +#include "romload.h" +#include "softlist.h" +#include "emuopts.h" + +#include +#include +#include +#include + + +namespace ui { + +namespace { + +constexpr machine_flags::type MACHINE_ERRORS = machine_flags::NOT_WORKING | machine_flags::MECHANICAL; +constexpr machine_flags::type MACHINE_WARNINGS = machine_flags::NO_COCKTAIL | machine_flags::REQUIRES_ARTWORK; +constexpr machine_flags::type MACHINE_BTANB = machine_flags::NO_SOUND_HW | machine_flags::IS_INCOMPLETE; + +constexpr std::pair FEATURE_NAMES[] = { + { device_t::feature::PROTECTION, N_p("emulation-feature", "protection") }, + { device_t::feature::TIMING, N_p("emulation-feature", "timing") }, + { device_t::feature::GRAPHICS, N_p("emulation-feature", "graphics") }, + { device_t::feature::PALETTE, N_p("emulation-feature", "color palette") }, + { device_t::feature::SOUND, N_p("emulation-feature", "sound") }, + { device_t::feature::CAPTURE, N_p("emulation-feature", "capture hardware") }, + { device_t::feature::CAMERA, N_p("emulation-feature", "camera") }, + { device_t::feature::MICROPHONE, N_p("emulation-feature", "microphone") }, + { device_t::feature::CONTROLS, N_p("emulation-feature", "controls") }, + { device_t::feature::KEYBOARD, N_p("emulation-feature", "keyboard") }, + { device_t::feature::MOUSE, N_p("emulation-feature", "mouse") }, + { device_t::feature::MEDIA, N_p("emulation-feature", "media") }, + { device_t::feature::DISK, N_p("emulation-feature", "disk") }, + { device_t::feature::PRINTER, N_p("emulation-feature", "printer") }, + { device_t::feature::TAPE, N_p("emulation-feature", "magnetic tape") }, + { device_t::feature::PUNCH, N_p("emulation-feature", "punch tape") }, + { device_t::feature::DRUM, N_p("emulation-feature", "magnetic drum") }, + { device_t::feature::ROM, N_p("emulation-feature", "solid state storage") }, + { device_t::feature::COMMS, N_p("emulation-feature", "communications") }, + { device_t::feature::LAN, N_p("emulation-feature", "LAN") }, + { device_t::feature::WAN, N_p("emulation-feature", "WAN") } }; + +void get_general_warnings(std::ostream &buf, running_machine &machine, machine_flags::type flags, device_t::feature_type unemulated, device_t::feature_type imperfect) +{ + // add a warning if any ROMs were loaded with warnings + bool bad_roms(false); + if (machine.rom_load().warnings() > 0) + { + bad_roms = true; + buf << _("One or more ROMs/CHDs for this machine are incorrect. The machine may not run correctly.\n"); + } + if (!machine.rom_load().software_load_warnings_message().empty()) + { + bad_roms = true; + buf << machine.rom_load().software_load_warnings_message(); + } + + // if we have at least one warning flag, print the general header + if ((machine.rom_load().knownbad() > 0) || (flags & (MACHINE_ERRORS | MACHINE_WARNINGS | MACHINE_BTANB)) || unemulated || imperfect) + { + if (bad_roms) + buf << '\n'; + buf << _("There are known problems with this machine\n\n"); + } + + // add a warning if any ROMs are flagged BAD_DUMP/NO_DUMP + if (machine.rom_load().knownbad() > 0) + buf << _("One or more ROMs/CHDs for this machine have not been correctly dumped.\n"); +} + +void get_device_warnings(std::ostream &buf, device_t::feature_type unemulated, device_t::feature_type imperfect) +{ + // add line for unemulated features + if (unemulated) + { + buf << _("Completely unemulated features: "); + bool first = true; + for (auto const &feature : FEATURE_NAMES) + { + if (unemulated & feature.first) + { + util::stream_format(buf, first ? _("%s") : _(", %s"), _("emulation-feature", feature.second)); + first = false; + } + } + buf << '\n'; + } + + // add line for imperfect features + if (imperfect) + { + buf << _("Imperfectly emulated features: "); + bool first = true; + for (auto const &feature : FEATURE_NAMES) + { + if (imperfect & feature.first) + { + util::stream_format(buf, first ? _("%s") : _(", %s"), _("emulation-feature", feature.second)); + first = false; + } + } + buf << '\n'; + } +} + +void get_system_warnings(std::ostream &buf, running_machine &machine, machine_flags::type flags, device_t::feature_type unemulated, device_t::feature_type imperfect) +{ + // start with the unemulated/imperfect features + get_device_warnings(buf, unemulated, imperfect); + + // add one line per machine warning flag + if (flags & ::machine_flags::NO_COCKTAIL) + buf << _("Screen flipping in cocktail mode is not supported.\n"); + if (flags & ::machine_flags::REQUIRES_ARTWORK) + buf << _("This machine requires external artwork files.\n"); + if (flags & ::machine_flags::IS_INCOMPLETE) + buf << _("This machine was never completed. It may exhibit strange behavior or missing elements that are not bugs in the emulation.\n"); + if (flags & ::machine_flags::NO_SOUND_HW) + buf << _("This machine has no sound hardware, MAME will produce no sounds, this is expected behaviour.\n"); + + // these are more severe warnings + if (flags & ::machine_flags::NOT_WORKING) + buf << _("\nTHIS MACHINE DOESN'T WORK. The emulation for this machine is not yet complete. There is nothing you can do to fix this problem except wait for the developers to improve the emulation.\n"); + if (flags & ::machine_flags::MECHANICAL) + buf << _("\nElements of this machine cannot be emulated as they require physical interaction or consist of mechanical devices. It is not possible to fully experience this machine.\n"); + + if ((flags & MACHINE_ERRORS) || ((machine.system().type.unemulated_features() | machine.system().type.imperfect_features()) & device_t::feature::PROTECTION)) + { + // find the parent of this driver + driver_enumerator drivlist(machine.options()); + int maindrv = drivlist.find(machine.system()); + int clone_of = drivlist.non_bios_clone(maindrv); + if (clone_of != -1) + maindrv = clone_of; + + // scan the driver list for any working clones and add them + bool foundworking = false; + while (drivlist.next()) + { + if (drivlist.current() == maindrv || drivlist.clone() == maindrv) + { + game_driver const &driver(drivlist.driver()); + if (!(driver.flags & MACHINE_ERRORS) && !((driver.type.unemulated_features() | driver.type.imperfect_features()) & device_t::feature::PROTECTION)) + { + // this one works, add a header and display the name of the clone + if (!foundworking) + util::stream_format(buf, _("\n\nThere are working clones of this machine: %s"), driver.name); + else + util::stream_format(buf, _(", %s"), driver.name); + foundworking = true; + } + } + } + if (foundworking) + buf << '\n'; + } +} + +} // anonymous namespace + + + +//------------------------------------------------- +// machine_static_info - constructors +//------------------------------------------------- + +machine_static_info::machine_static_info(const ui_options &options, machine_config const &config) + : machine_static_info(options, config, nullptr) +{ +} + +machine_static_info::machine_static_info(const ui_options &options, machine_config const &config, ioport_list const &ports) + : machine_static_info(options, config, &ports) +{ +} + +machine_static_info::machine_static_info(const ui_options &options, machine_config const &config, ioport_list const *ports) + : m_options(options) + , m_flags(config.gamedrv().flags) + , m_unemulated_features(config.gamedrv().type.unemulated_features()) + , m_imperfect_features(config.gamedrv().type.imperfect_features()) + , m_has_bioses(false) + , m_has_dips(false) + , m_has_configs(false) + , m_has_keyboard(false) + , m_has_test_switch(false) + , m_has_analog(false) +{ + ioport_list local_ports; + std::string sink; + for (device_t &device : device_enumerator(config.root_device())) + { + // the "no sound hardware" warning doesn't make sense when you plug in a sound card + if (dynamic_cast(&device)) + m_flags &= ~::machine_flags::NO_SOUND_HW; + + // build overall emulation status + m_unemulated_features |= device.type().unemulated_features(); + m_imperfect_features |= device.type().imperfect_features(); + + // look for BIOS options + device_t const *const parent(device.owner()); + device_slot_interface const *const slot(dynamic_cast(parent)); + if (!parent || (slot && (slot->get_card_device() == &device))) + { + for (tiny_rom_entry const *rom = device.rom_region(); !m_has_bioses && rom && !ROMENTRY_ISEND(rom); ++rom) + { + if (ROMENTRY_ISSYSTEM_BIOS(rom)) + m_has_bioses = true; + } + } + + // if we don't have ports passed in, build here + if (!ports) + local_ports.append(device, sink); + } + + // suppress "requires external artwork" warning when external artwork was loaded + if (config.root_device().has_running_machine()) + { + for (render_target *target = config.root_device().machine().render().first_target(); target != nullptr; target = target->next()) + if (!target->hidden() && target->external_artwork()) + { + m_flags &= ~::machine_flags::REQUIRES_ARTWORK; + break; + } + } + + // unemulated trumps imperfect when aggregating (always be pessimistic) + m_imperfect_features &= ~m_unemulated_features; + + // scan the input port array to see what options we need to enable + for (ioport_list::value_type const &port : (ports ? *ports : local_ports)) + { + for (ioport_field const &field : port.second->fields()) + { + switch (field.type()) + { + case IPT_DIPSWITCH: m_has_dips = true; break; + case IPT_CONFIG: m_has_configs = true; break; + case IPT_KEYBOARD: m_has_keyboard = true; break; + case IPT_SERVICE: m_has_test_switch = true; break; + default: break; + } + if (field.is_analog()) + m_has_analog = true; + } + } +} + + +//------------------------------------------------- +// has_warnings - returns true if the system has +// issues that warrant a yellow/red message +//------------------------------------------------- + +bool machine_static_info::has_warnings() const +{ + return (machine_flags() & (MACHINE_ERRORS | MACHINE_WARNINGS)) || unemulated_features() || imperfect_features(); +} + + +//------------------------------------------------- +// has_severe_warnings - returns true if the +// system has issues that warrant a red message +//------------------------------------------------- + +bool machine_static_info::has_severe_warnings() const +{ + return + (machine_flags() & MACHINE_ERRORS) || + (unemulated_features() & (device_t::feature::PROTECTION | device_t::feature::GRAPHICS | device_t::feature::SOUND)) || + (imperfect_features() & device_t::feature::PROTECTION); +} + + +//------------------------------------------------- +// status_color - returns suitable colour for +// driver status box +//------------------------------------------------- + +rgb_t machine_static_info::status_color() const +{ + if (has_severe_warnings()) + return UI_RED_COLOR; + else if ((machine_flags() & MACHINE_WARNINGS & ~::machine_flags::REQUIRES_ARTWORK) || unemulated_features() || imperfect_features()) + return UI_YELLOW_COLOR; + else + return UI_GREEN_COLOR; +} + + +//------------------------------------------------- +// warnings_color - returns suitable colour for +// warning message based on severity +//------------------------------------------------- + +rgb_t machine_static_info::warnings_color() const +{ + if (has_severe_warnings()) + return UI_RED_COLOR; + else if ((machine_flags() & MACHINE_WARNINGS) || unemulated_features() || imperfect_features()) + return UI_YELLOW_COLOR; + else + return m_options.background_color(); +} + + + +//------------------------------------------------- +// machine_info - constructor +//------------------------------------------------- + +machine_info::machine_info(running_machine &machine) + : machine_static_info(dynamic_cast(&machine.ui())->options(), machine.config(), machine.ioport().ports()) + , m_machine(machine) +{ +} + + +/*************************************************************************** + TEXT GENERATORS +***************************************************************************/ + +//------------------------------------------------- +// warnings_string - print the warning flags +// text to the given buffer +//------------------------------------------------- + +std::string machine_info::warnings_string() const +{ + std::ostringstream buf; + get_general_warnings(buf, m_machine, machine_flags(), unemulated_features(), imperfect_features()); + get_system_warnings(buf, m_machine, machine_flags(), unemulated_features(), imperfect_features()); + return buf.str(); +} + + +//------------------------------------------------- +// game_info_string - return the game info text +//------------------------------------------------- + +std::string machine_info::game_info_string() const +{ + std::ostringstream buf; + + // print description, manufacturer, and CPU: + util::stream_format(buf, _("%1$s\n%2$s %3$s\nDriver: %4$s\n\nCPU:\n"), + system_list::instance().systems()[driver_list::find(m_machine.system().name)].description, + m_machine.system().year, + m_machine.system().manufacturer, + core_filename_extract_base(m_machine.system().type.source())); + + // loop over all CPUs + execute_interface_enumerator execiter(m_machine.root_device()); + std::unordered_set exectags; + for (device_execute_interface &exec : execiter) + { + if (!exectags.insert(exec.device().tag()).second) + continue; + // get cpu specific clock that takes internal multiplier/dividers into account + u32 clock = exec.device().clock(); + + // count how many identical CPUs we have + int count = 1; + const char *name = exec.device().name(); + for (device_execute_interface &scan : execiter) + { + if (exec.device().type() == scan.device().type() && strcmp(name, scan.device().name()) == 0 && exec.device().clock() == scan.device().clock()) + if (exectags.insert(scan.device().tag()).second) + count++; + } + + std::string hz(std::to_string(clock)); + int d = (clock >= 1'000'000'000) ? 9 : (clock >= 1'000'000) ? 6 : (clock >= 1000) ? 3 : 0; + if (d > 0) + { + size_t dpos = hz.length() - d; + hz.insert(dpos, "."); + size_t last = hz.find_last_not_of('0'); + hz = hz.substr(0, last + (last != dpos ? 1 : 0)); + } + + // if more than one, prepend a #x in front of the CPU name and display clock + util::stream_format(buf, + (count > 1) + ? ((clock != 0) ? "%1$d" UTF8_MULTIPLY "%2$s %3$s" UTF8_NBSP "%4$s\n" : "%1$d" UTF8_MULTIPLY "%2$s\n") + : ((clock != 0) ? "%2$s %3$s" UTF8_NBSP "%4$s\n" : "%2$s\n"), + count, name, hz, + (d == 9) ? _("GHz") : (d == 6) ? _("MHz") : (d == 3) ? _("kHz") : _("Hz")); + } + + // loop over all sound chips + sound_interface_enumerator snditer(m_machine.root_device()); + std::unordered_set soundtags; + bool found_sound = false; + for (device_sound_interface &sound : snditer) + { + if (!sound.issound() || !soundtags.insert(sound.device().tag()).second) + continue; + + // append the Sound: string + if (!found_sound) + buf << _("\nSound:\n"); + found_sound = true; + + // count how many identical sound chips we have + int count = 1; + for (device_sound_interface &scan : snditer) + { + if (sound.device().type() == scan.device().type() && sound.device().clock() == scan.device().clock()) + if (soundtags.insert(scan.device().tag()).second) + count++; + } + + const u32 clock = sound.device().clock(); + std::string hz(std::to_string(clock)); + int d = (clock >= 1'000'000'000) ? 9 : (clock >= 1'000'000) ? 6 : (clock >= 1000) ? 3 : 0; + if (d > 0) + { + size_t dpos = hz.length() - d; + hz.insert(dpos, "."); + size_t last = hz.find_last_not_of('0'); + hz = hz.substr(0, last + (last != dpos ? 1 : 0)); + } + + // if more than one, prepend a #x in front of the soundchip name and display clock + util::stream_format(buf, + (count > 1) + ? ((clock != 0) ? "%1$d" UTF8_MULTIPLY "%2$s %3$s" UTF8_NBSP "%4$s\n" : "%1$d" UTF8_MULTIPLY "%2$s\n") + : ((clock != 0) ? "%2$s %3$s" UTF8_NBSP "%4$s\n" : "%2$s\n"), + count, sound.device().name(), hz, + (d == 9) ? _("GHz") : (d == 6) ? _("MHz") : (d == 3) ? _("kHz") : _("Hz")); + } + + // display screen information + buf << _("\nVideo:\n"); + screen_device_enumerator scriter(m_machine.root_device()); + int scrcount = scriter.count(); + if (scrcount == 0) + buf << _("None\n"); + else + { + for (screen_device &screen : scriter) + { + std::string detail; + if (screen.screen_type() == SCREEN_TYPE_VECTOR) + detail = _("Vector"); + else + { + const u32 rate = u32(screen.frame_period().as_hz() * 1'000'000 + 0.5); + const bool valid = rate >= 1'000'000; + std::string hz(valid ? std::to_string(rate) : "?"); + if (valid) + { + size_t dpos = hz.length() - 6; + hz.insert(dpos, "."); + size_t last = hz.find_last_not_of('0'); + hz = hz.substr(0, last + (last != dpos ? 1 : 0)); + } + + const rectangle &visarea = screen.visible_area(); + detail = string_format("%d " UTF8_MULTIPLY " %d (%s) %s" UTF8_NBSP "Hz", + visarea.width(), visarea.height(), + (screen.orientation() & ORIENTATION_SWAP_XY) ? "V" : "H", + hz); + } + + util::stream_format(buf, + (scrcount > 1) ? _("%1$s: %2$s\n") : _("%2$s\n"), + get_screen_desc(screen), detail); + } + } + + return buf.str(); +} + + +//------------------------------------------------- +// get_screen_desc - returns the description for +// a given screen +//------------------------------------------------- + +std::string machine_info::get_screen_desc(screen_device &screen) const +{ + if (screen_device_enumerator(m_machine.root_device()).count() > 1) + return string_format(_("Screen '%1$s'"), screen.tag()); + else + return _("Screen"); +} + + + +/*------------------------------------------------- + menu_game_info - handle the game information menu +-------------------------------------------------*/ + +menu_game_info::menu_game_info(mame_ui_manager &mui, render_container &container) : menu_textbox(mui, container) +{ + set_process_flags(PROCESS_CUSTOM_NAV); +} + +menu_game_info::~menu_game_info() +{ +} + +void menu_game_info::populate_text(std::optional &layout, float &width, int &lines) +{ + if (!layout || (layout->width() != width)) + { + rgb_t const color = ui().colors().text_color(); + layout.emplace(ui().create_layout(container(), width)); + layout->add_text(ui().machine_info().game_info_string(), color); + lines = layout->lines(); + } + width = layout->actual_width(); +} + +void menu_game_info::populate(float &customtop, float &custombottom) +{ +} + +void menu_game_info::handle(event const *ev) +{ + if (ev) + handle_key(ev->iptkey); +} + + +/*------------------------------------------------- + menu_warn_info - handle the emulation warnings menu +-------------------------------------------------*/ + +menu_warn_info::menu_warn_info(mame_ui_manager &mui, render_container &container) : menu_textbox(mui, container) +{ + set_process_flags(PROCESS_CUSTOM_NAV); +} + +menu_warn_info::~menu_warn_info() +{ +} + +void menu_warn_info::populate_text(std::optional &layout, float &width, int &lines) +{ + if (!layout || (layout->width() != width)) + { + std::ostringstream buf; + std::set > seen; + bool first(!machine().rom_load().knownbad()); + + machine_info const &info(ui().machine_info()); + device_t &root(machine().root_device()); + get_general_warnings(buf, machine(), info.machine_flags(), info.unemulated_features(), info.imperfect_features()); + if ((info.machine_flags() & (MACHINE_ERRORS | MACHINE_WARNINGS | MACHINE_BTANB)) || root.type().unemulated_features() || root.type().imperfect_features()) + { + seen.insert(&root.type()); + if (!first) + buf << '\n'; + first = false; + util::stream_format(buf, _("%1$s:\n"), root.name()); + get_system_warnings(buf, machine(), info.machine_flags(), root.type().unemulated_features(), root.type().imperfect_features()); + } + + for (device_t const &device : device_enumerator(root)) + { + if ((device.type().unemulated_features() || device.type().imperfect_features()) && seen.insert(&device.type()).second) + { + if (!first) + buf << '\n'; + first = false; + util::stream_format(buf, _("%1$s:\n"), device.name()); + get_device_warnings(buf, device.type().unemulated_features(), device.type().imperfect_features()); + } + } + + rgb_t const color(ui().colors().text_color()); + layout.emplace(ui().create_layout(container(), width)); + layout->add_text(std::move(buf).str(), color); + lines = layout->lines(); + } + width = layout->actual_width(); +} + +void menu_warn_info::populate(float &customtop, float &custombottom) +{ +} + +void menu_warn_info::handle(event const *ev) +{ + if (ev) + handle_key(ev->iptkey); +} + + +/*------------------------------------------------- + menu_image_info - handle the image information menu +-------------------------------------------------*/ + +menu_image_info::menu_image_info(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ +} + +menu_image_info::~menu_image_info() +{ +} + +void menu_image_info::menu_activated() +{ + reset(reset_options::REMEMBER_POSITION); +} + +void menu_image_info::populate(float &customtop, float &custombottom) +{ + ui_system_info const &system(system_list::instance().systems()[driver_list::find(machine().system().name)]); + item_append(system.description, FLAG_DISABLE, nullptr); + item_append(std::string(), FLAG_DISABLE, nullptr); + + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) + image_info(&image); +} + +void menu_image_info::handle(event const *ev) +{ +} + + +/*------------------------------------------------- + image_info - display image info for a specific + image interface device +-------------------------------------------------*/ + +void menu_image_info::image_info(device_image_interface *image) +{ + if (image->exists()) + { + // display device type and filename + item_append(image->brief_instance_name(), image->basename(), 0, nullptr); + + // if image has been loaded through softlist, let's add some more info + if (image->loaded_through_softlist()) + { + software_info const &swinfo(*image->software_entry()); + + // display full name, publisher and year + item_append(swinfo.longname(), FLAG_DISABLE, nullptr); + item_append(string_format("%1$s, %2$s", swinfo.publisher(), swinfo.year()), FLAG_DISABLE, nullptr); + + // display supported information, if available + switch (swinfo.supported()) + { + case software_support::UNSUPPORTED: + item_append(_("Not supported"), FLAG_DISABLE, nullptr); + break; + case software_support::PARTIALLY_SUPPORTED: + item_append(_("Partially supported"), FLAG_DISABLE, nullptr); + break; + case software_support::SUPPORTED: + break; + } + } + } + else + { + item_append(image->brief_instance_name(), _("[empty]"), 0, nullptr); + } + item_append(std::string(), FLAG_DISABLE, nullptr); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/info.h b/src/icludes/frontend/mame/ui/info.h new file mode 100644 index 0000000..b05a1d6 --- /dev/null +++ b/src/icludes/frontend/mame/ui/info.h @@ -0,0 +1,139 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/info.h + + System and image info screens + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_INFO_H +#define MAME_FRONTEND_UI_INFO_H + +#pragma once + +#include "ui/textbox.h" + +#include + + +namespace ui { + +class machine_static_info +{ +public: + // construction + machine_static_info(const ui_options &options, machine_config const &config); + + // overall emulation status + ::machine_flags::type machine_flags() const { return m_flags; } + device_t::feature_type unemulated_features() const { return m_unemulated_features; } + device_t::feature_type imperfect_features() const { return m_imperfect_features; } + + // has... getters + bool has_bioses() const { return m_has_bioses; } + + // has input types getters + bool has_dips() const { return m_has_dips; } + bool has_configs() const { return m_has_configs; } + bool has_keyboard() const { return m_has_keyboard; } + bool has_test_switch() const { return m_has_test_switch; } + bool has_analog() const { return m_has_analog; } + + // warning severity indications + bool has_warnings() const; + bool has_severe_warnings() const; + rgb_t status_color() const; + rgb_t warnings_color() const; + +protected: + machine_static_info(const ui_options &options, machine_config const &config, ioport_list const &ports); + +private: + machine_static_info(const ui_options &options, machine_config const &config, ioport_list const *ports); + + const ui_options & m_options; + + // overall feature status + ::machine_flags::type m_flags; + device_t::feature_type m_unemulated_features; + device_t::feature_type m_imperfect_features; + + // has... + bool m_has_bioses; + + // has input types + bool m_has_dips; + bool m_has_configs; + bool m_has_keyboard; + bool m_has_test_switch; + bool m_has_analog; +}; + + +class machine_info : public machine_static_info +{ +public: + // construction + machine_info(running_machine &machine); + + // text generators + std::string warnings_string() const; + std::string game_info_string() const; + std::string get_screen_desc(screen_device &screen) const; + +private: + // reference to machine + running_machine & m_machine; +}; + + +class menu_game_info : public menu_textbox +{ +public: + menu_game_info(mame_ui_manager &mui, render_container &container); + virtual ~menu_game_info() override; + +protected: + virtual void populate_text(std::optional &layout, float &width, int &lines) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + + +class menu_warn_info : public menu_textbox +{ +public: + menu_warn_info(mame_ui_manager &mui, render_container &container); + virtual ~menu_warn_info() override; + +protected: + virtual void populate_text(std::optional &layout, float &width, int &lines) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + + +class menu_image_info : public menu +{ +public: + menu_image_info(mame_ui_manager &mui, render_container &container); + virtual ~menu_image_info() override; + +protected: + virtual void menu_activated() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + void image_info(device_image_interface *image); +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_INFO_H diff --git a/src/icludes/frontend/mame/ui/info_pty.cpp b/src/icludes/frontend/mame/ui/info_pty.cpp new file mode 100644 index 0000000..4e541a7 --- /dev/null +++ b/src/icludes/frontend/mame/ui/info_pty.cpp @@ -0,0 +1,48 @@ +// license:BSD-3-Clause +// copyright-holders:F.Ulivi +/*************************************************************************** + + ui/info_pty.cpp + + Information screen on pseudo terminals + +***************************************************************************/ + +#include "emu.h" +#include "ui/info_pty.h" + +#include "dipty.h" + + +namespace ui { + +menu_pty_info::menu_pty_info(mame_ui_manager &mui, render_container &container) : + menu(mui, container) +{ +} + +menu_pty_info::~menu_pty_info() +{ +} + +void menu_pty_info::populate(float &customtop, float &custombottom) +{ + item_append(_("Pseudo terminals"), FLAG_DISABLE, nullptr); + item_append(std::string(), FLAG_DISABLE, nullptr); + + for (device_pty_interface &pty : pty_interface_enumerator(machine().root_device())) + { + const char *port_name = pty.device().owner()->tag() + 1; + if (pty.is_open()) + item_append(port_name, pty.slave_name(), FLAG_DISABLE, nullptr); + else + item_append(port_name, _("[failed]"), FLAG_DISABLE, nullptr); + item_append(std::string(), FLAG_DISABLE, nullptr); + } +} + +void menu_pty_info::handle(event const *ev) +{ +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/info_pty.h b/src/icludes/frontend/mame/ui/info_pty.h new file mode 100644 index 0000000..16f6543 --- /dev/null +++ b/src/icludes/frontend/mame/ui/info_pty.h @@ -0,0 +1,33 @@ +// license:BSD-3-Clause +// copyright-holders:F.Ulivi +/*************************************************************************** + + ui/info_pty.h + + Information screen on pseudo terminals + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_INFO_PTY_H +#define MAME_FRONTEND_UI_INFO_PTY_H + +#pragma once + +#include "ui/menu.h" + +namespace ui { + +class menu_pty_info : public menu +{ +public: + menu_pty_info(mame_ui_manager &mui, render_container &container); + virtual ~menu_pty_info() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_INFO_PTY_H diff --git a/src/icludes/frontend/mame/ui/inifile.cpp b/src/icludes/frontend/mame/ui/inifile.cpp new file mode 100644 index 0000000..87cdd93 --- /dev/null +++ b/src/icludes/frontend/mame/ui/inifile.cpp @@ -0,0 +1,575 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/*************************************************************************** + + ui/inifile.cpp + + UI INIs file manager. + +***************************************************************************/ + +#include "emu.h" +#include "ui/inifile.h" + +#include "ui/moptions.h" + +#include "language.h" + +#include "drivenum.h" +#include "softlist_dev.h" + +#include "corestr.h" + +#include +#include +#include + + +namespace { + +char const FAVORITE_FILENAME[] = "favorites.ini"; + +} // anonymous namespace + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +inifile_manager::inifile_manager(ui_options &options) + : m_options(options) + , m_ini_index() +{ + // scan directories and create index + file_enumerator path(m_options.categoryini_path()); + for (osd::directory::entry const *dir = path.next(); dir; dir = path.next()) + { + std::string name(dir->name); + if (core_filename_ends_with(name, ".ini")) + { + emu_file file(m_options.categoryini_path(), OPEN_FLAG_READ); + if (!file.open(name)) + { + init_category(std::move(name), file); + file.close(); + } + } + } + std::stable_sort(m_ini_index.begin(), m_ini_index.end(), [] (auto const &x, auto const &y) { return 0 > core_stricmp(x.first.c_str(), y.first.c_str()); }); +} + +//------------------------------------------------- +// load and indexing ini files +//------------------------------------------------- + +void inifile_manager::load_ini_category(size_t file, size_t category, std::unordered_set &result) const +{ + std::string const &filename(m_ini_index[file].first); + emu_file fp(m_options.categoryini_path(), OPEN_FLAG_READ); + if (fp.open(filename)) + { + osd_printf_error("Failed to open category file %s for reading\n", filename); + return; + } + + int64_t const offset(m_ini_index[file].second[category].second); + if (fp.seek(offset, SEEK_SET) || (fp.tell() != offset)) + { + fp.close(); + osd_printf_error("Failed to seek to category offset in file %s\n", filename); + return; + } + + char rbuf[MAX_CHAR_INFO]; + while (fp.gets(rbuf, MAX_CHAR_INFO) && rbuf[0] && ('[' != rbuf[0])) + { + auto const tail(std::find_if(std::begin(rbuf), std::prev(std::end(rbuf)), [] (char ch) { return !ch || ('\r' == ch) || ('\n' == ch); })); + *tail = '\0'; + int const dfind(driver_list::find(rbuf)); + if (0 <= dfind) + result.emplace(&driver_list::driver(dfind)); + } + + fp.close(); +} + +//------------------------------------------------- +// initialize category +//------------------------------------------------- + +void inifile_manager::init_category(std::string &&filename, emu_file &file) +{ + categoryindex index; + char rbuf[MAX_CHAR_INFO]; + std::string name; + while (file.gets(rbuf, std::size(rbuf))) + { + if ('[' == rbuf[0]) + { + auto const head(std::next(std::begin(rbuf))); + auto const tail(std::find_if(head, std::end(rbuf), [] (char ch) { return !ch || (']' == ch); })); + name.assign(head, tail); + if ("FOLDER_SETTINGS" != name) + index.emplace_back(std::move(name), file.tell()); + } + } + std::stable_sort(index.begin(), index.end(), [] (auto const &x, auto const &y) { return 0 > core_stricmp(x.first.c_str(), y.first.c_str()); }); + if (!index.empty()) + m_ini_index.emplace_back(std::move(filename), std::move(index)); +} + + +/************************************************************************** + FAVORITE MANAGER +**************************************************************************/ + +bool favorite_manager::favorite_compare::operator()(ui_software_info const &lhs, ui_software_info const &rhs) const +{ + assert(lhs.driver); + assert(rhs.driver); + + if (!lhs.startempty) + { + if (rhs.startempty) + return false; + else if (lhs.listname < rhs.listname) + return true; + else if (lhs.listname > rhs.listname) + return false; + else if (lhs.shortname < rhs.shortname) + return true; + else if (lhs.shortname > rhs.shortname) + return false; + } + else if (!rhs.startempty) + { + return true; + } + + return 0 > std::strncmp(lhs.driver->name, rhs.driver->name, std::size(lhs.driver->name)); +} + +bool favorite_manager::favorite_compare::operator()(ui_software_info const &lhs, game_driver const &rhs) const +{ + assert(lhs.driver); + + if (!lhs.startempty) + return false; + else + return 0 > std::strncmp(lhs.driver->name, rhs.name, std::size(rhs.name)); +} + +bool favorite_manager::favorite_compare::operator()(game_driver const &lhs, ui_software_info const &rhs) const +{ + assert(rhs.driver); + + if (!rhs.startempty) + return true; + else + return 0 > std::strncmp(lhs.name, rhs.driver->name, std::size(lhs.name)); +} + +bool favorite_manager::favorite_compare::operator()(ui_software_info const &lhs, running_software_key const &rhs) const +{ + assert(lhs.driver); + assert(std::get<1>(rhs)); + + if (lhs.startempty) + return true; + else if (lhs.listname < std::get<1>(rhs)) + return true; + else if (lhs.listname > std::get<1>(rhs)) + return false; + else if (lhs.shortname < std::get<2>(rhs)) + return true; + else if (lhs.shortname > std::get<2>(rhs)) + return false; + else + return 0 > std::strncmp(lhs.driver->name, std::get<0>(rhs).name, std::size(lhs.driver->name)); +} + +bool favorite_manager::favorite_compare::operator()(running_software_key const &lhs, ui_software_info const &rhs) const +{ + assert(std::get<1>(lhs)); + assert(rhs.driver); + + if (rhs.startempty) + return false; + else if (std::get<1>(lhs) < rhs.listname) + return true; + else if (std::get<1>(lhs) > rhs.listname) + return false; + else if (std::get<2>(lhs) < rhs.shortname) + return true; + else if (std::get<2>(lhs) > rhs.shortname) + return false; + else + return 0 > std::strncmp(std::get<0>(lhs).name, rhs.driver->name, std::size(rhs.driver->name)); +} + + +//------------------------------------------------- +// construction/destruction +//------------------------------------------------- + +favorite_manager::favorite_manager(ui_options &options) + : m_options(options) + , m_favorites() + , m_sorted() + , m_need_sort(true) +{ + emu_file file(m_options.ui_path(), OPEN_FLAG_READ); + if (!file.open(FAVORITE_FILENAME)) + { + char readbuf[1024]; + file.gets(readbuf, std::size(readbuf)); + + while (readbuf[0] == '[') + file.gets(readbuf, std::size(readbuf)); + + while (file.gets(readbuf, std::size(readbuf))) + { + ui_software_info tmpmatches; + tmpmatches.shortname = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.longname = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.parentname = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.year = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.publisher = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.supported = software_support(atoi(readbuf)); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.part = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + chartrimcarriage(readbuf); + auto dx = driver_list::find(readbuf); + if (0 > dx) + continue; + tmpmatches.driver = &driver_list::driver(dx); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.listname = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.interface = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.instance = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.startempty = atoi(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.parentlongname = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + //tmpmatches.usage = chartrimcarriage(readbuf); TODO: recover multi-line info + file.gets(readbuf, std::size(readbuf)); + tmpmatches.devicetype = chartrimcarriage(readbuf); + file.gets(readbuf, std::size(readbuf)); + tmpmatches.available = atoi(readbuf); + + // need to populate this, it isn't displayed anywhere else + tmpmatches.infotext.append(tmpmatches.longname); + tmpmatches.infotext.append(1, '\n'); + tmpmatches.infotext.append(_("swlist-info", "Software list/item")); + tmpmatches.infotext.append(1, '\n'); + tmpmatches.infotext.append(tmpmatches.listname); + tmpmatches.infotext.append(1, ':'); + tmpmatches.infotext.append(tmpmatches.shortname); + + m_favorites.emplace(std::move(tmpmatches)); + } + file.close(); + } +} + + +//------------------------------------------------- +// add +//------------------------------------------------- + +void favorite_manager::add_favorite_system(game_driver const &driver) +{ + add_impl(driver); +} + +void favorite_manager::add_favorite_software(ui_software_info const &swinfo) +{ + add_impl(swinfo); +} + +void favorite_manager::add_favorite(running_machine &machine) +{ + apply_running_machine( + machine, + [this, &machine] (game_driver const &driver, device_image_interface *imagedev, software_info const *software, bool &done) + { + if (imagedev) + { + // creating this is fairly expensive, but we'll assume this usually succeeds + software_part const *const part(imagedev->part_entry()); + assert(software); + assert(part); + ui_software_info info( + *software, + *part, + driver, + imagedev->software_list_name(), + imagedev->instance_name(), + strensure(imagedev->image_type_name())); + + // assume it's available if it's mounted + info.available = true; + + // look up the parent in the list if necessary (eugh, O(n) walk) + if (!info.parentname.empty()) + { + auto const listdev = software_list_device::find_by_name(machine.config(), info.listname); + assert(listdev); + for (software_info const &other : listdev->get_info()) + { + if (other.shortname() == info.parentname) + { + info.parentlongname = other.longname(); + break; + } + } + } + + // hooray for move semantics! + add_impl(std::move(info)); + } + else + { + add_impl(driver); + } + }); +} + +template void favorite_manager::add_impl(T &&key) +{ + auto const ins(m_favorites.emplace(std::forward(key))); + if (ins.second) + { + if (!m_sorted.empty()) + m_sorted.emplace_back(std::ref(*ins.first)); + m_need_sort = true; + save_favorites(); + } +} + + +//------------------------------------------------- +// check +//------------------------------------------------- + +bool favorite_manager::is_favorite_system(game_driver const &driver) const +{ + return check_impl(driver); +} + +bool favorite_manager::is_favorite_software(ui_software_info const &swinfo) const +{ + auto found(m_favorites.lower_bound(swinfo)); + if ((m_favorites.end() != found) && (found->listname == swinfo.listname) && (found->shortname == swinfo.shortname)) + return true; + else if (m_favorites.begin() == found) + return false; + + // need to back up and check for matching software with lexically earlier driver + --found; + return (found->listname == swinfo.listname) && (found->shortname == swinfo.shortname); +} + +bool favorite_manager::is_favorite(running_machine &machine) const +{ + bool result(false); + apply_running_machine( + machine, + [this, &result] (game_driver const &driver, device_image_interface *imagedev, software_info const *software, bool &done) + { + assert(!result); + result = imagedev + ? check_impl(running_software_key(driver, imagedev->software_list_name(), software->shortname())) + : check_impl(driver); + done = done || result; + }); + return result; +} + +bool favorite_manager::is_favorite_system_software(ui_software_info const &swinfo) const +{ + return check_impl(swinfo); +} + +template bool favorite_manager::check_impl(T const &key) const +{ + return m_favorites.find(key) != m_favorites.end(); +} + + +//------------------------------------------------- +// remove +//------------------------------------------------- + +void favorite_manager::remove_favorite_system(game_driver const &driver) +{ + remove_impl(driver); +} + +void favorite_manager::remove_favorite_software(ui_software_info const &swinfo) +{ + remove_impl(swinfo); +} + +void favorite_manager::remove_favorite(running_machine &machine) +{ + apply_running_machine( + machine, + [this] (game_driver const &driver, device_image_interface *imagedev, software_info const *software, bool &done) + { + done = imagedev + ? remove_impl(running_software_key(driver, imagedev->software_list_name(), software->shortname())) + : remove_impl(driver); + }); +} + +template bool favorite_manager::remove_impl(T const &key) +{ + auto const found(m_favorites.find(key)); + if (m_favorites.end() != found) + { + m_favorites.erase(found); + m_sorted.clear(); + m_need_sort = true; + save_favorites(); + return true; + } + else + { + return false; + } +} + + +//------------------------------------------------- +// implementation +//------------------------------------------------- + +template +void favorite_manager::apply_running_machine(running_machine &machine, T &&action) +{ + bool done(false); + + // TODO: this should be changed - it interacts poorly with cartslots on arcade systems + if ((machine.system().flags & machine_flags::MASK_TYPE) == machine_flags::TYPE_ARCADE) + { + action(machine.system(), nullptr, nullptr, done); + } + else + { + bool have_software(false); + for (device_image_interface &image_dev : image_interface_enumerator(machine.root_device())) + { + software_info const *const sw(image_dev.software_entry()); + if (image_dev.exists() && image_dev.loaded_through_softlist() && sw) + { + assert(image_dev.software_list_name()); + + have_software = true; + action(machine.system(), &image_dev, sw, done); + if (done) + return; + } + } + + if (!have_software) + action(machine.system(), nullptr, nullptr, done); + } +} + +void favorite_manager::update_sorted() +{ + if (m_need_sort) + { + if (m_sorted.empty()) + std::copy(m_favorites.begin(), m_favorites.end(), std::back_inserter(m_sorted)); + + assert(m_favorites.size() == m_sorted.size()); + std::stable_sort( + m_sorted.begin(), + m_sorted.end(), + [] (ui_software_info const &lhs, ui_software_info const &rhs) -> bool + { + assert(lhs.driver); + assert(rhs.driver); + + int cmp; + + cmp = core_stricmp(lhs.longname.c_str(), rhs.longname.c_str()); + if (0 > cmp) + return true; + else if (0 < cmp) + return false; + + cmp = core_stricmp(lhs.driver->type.fullname(), rhs.driver->type.fullname()); + if (0 > cmp) + return true; + else if (0 < cmp) + return false; + + cmp = std::strcmp(lhs.listname.c_str(), rhs.listname.c_str()); + if (0 > cmp) + return true; + else if (0 < cmp) + return false; + + return false; + }); + + m_need_sort = false; + } +} + +void favorite_manager::save_favorites() +{ + // attempt to open the output file + emu_file file(m_options.ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(FAVORITE_FILENAME)) + { + if (m_favorites.empty()) + { + // delete it if there are no favorites + file.remove_on_close(); + } + else + { + // generate the favorite INI + file.puts("[ROOT_FOLDER]\n[Favorite]\n\n"); + util::ovectorstream buf; + for (ui_software_info const &info : m_favorites) + { + buf.clear(); + buf.rdbuf()->clear(); + + buf << info.shortname << '\n'; + buf << info.longname << '\n'; + buf << info.parentname << '\n'; + buf << info.year << '\n'; + buf << info.publisher << '\n'; + util::stream_format(buf, "%d\n", int(info.supported)); + buf << info.part << '\n'; + util::stream_format(buf, "%s\n", info.driver->name); + buf << info.listname << '\n'; + buf << info.interface << '\n'; + buf << info.instance << '\n'; + util::stream_format(buf, "%d\n", info.startempty); + buf << info.parentlongname << '\n'; + buf << '\n'; //buf << info.usage << '\n'; TODO: store multi-line info in a recoverable format + buf << info.devicetype << '\n'; + util::stream_format(buf, "%d\n", info.available); + + file.puts(util::buf_to_string_view(buf)); + } + } + file.close(); + } +} diff --git a/src/icludes/frontend/mame/ui/inifile.h b/src/icludes/frontend/mame/ui/inifile.h new file mode 100644 index 0000000..68adca3 --- /dev/null +++ b/src/icludes/frontend/mame/ui/inifile.h @@ -0,0 +1,128 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/*************************************************************************** + + ui/inifile.h + + UI INIs file manager. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_INIFILE_H +#define MAME_FRONTEND_UI_INIFILE_H + +#pragma once + +#include "ui/utils.h" + +#include +#include +#include +#include +#include + + +//------------------------------------------------- +// INIFILE MANAGER +//------------------------------------------------- + +class ui_options; + +class inifile_manager +{ +public: + // construction/destruction + inifile_manager(ui_options &options); + + // load systems from category + void load_ini_category(size_t file, size_t category, std::unordered_set &result) const; + + // getters + size_t get_file_count() const { return m_ini_index.size(); } + std::string const &get_file_name(size_t file) const { return m_ini_index[file].first; } + size_t get_category_count(size_t file) const { return m_ini_index[file].second.size(); } + std::string const &get_category_name(size_t file, size_t category) const { return m_ini_index[file].second[category].first; } + +private: + // ini file structure + using categoryindex = std::vector>; + + void init_category(std::string &&filename, emu_file &file); + + // internal state + ui_options &m_options; + std::vector > m_ini_index; + +}; + +//------------------------------------------------- +// FAVORITE MANAGER +//------------------------------------------------- + +class favorite_manager +{ +public: + // construction/destruction + favorite_manager(ui_options &options); + + // add + void add_favorite_system(game_driver const &driver); + void add_favorite_software(ui_software_info const &swinfo); + void add_favorite(running_machine &machine); + + // check + bool is_favorite_system(game_driver const &driver) const; + bool is_favorite_software(ui_software_info const &swinfo) const; + bool is_favorite_system_software(ui_software_info const &swinfo) const; + bool is_favorite(running_machine &machine) const; + + // remove + void remove_favorite_system(game_driver const &driver); + void remove_favorite_software(ui_software_info const &swinfo); + void remove_favorite(running_machine &machine); + + // walk + template void apply(T &&action) + { + for (auto const &item : m_favorites) + action(item); + } + template void apply_sorted(T &&action) + { + update_sorted(); + for (auto const &item : m_sorted) + action(item.get()); + } + +private: + using running_software_key = std::tuple; + + struct favorite_compare + { + using is_transparent = std::true_type; + + bool operator()(ui_software_info const &lhs, ui_software_info const &rhs) const; + bool operator()(ui_software_info const &lhs, game_driver const &rhs) const; + bool operator()(game_driver const &lhs, ui_software_info const &rhs) const; + bool operator()(ui_software_info const &lhs, running_software_key const &rhs) const; + bool operator()(running_software_key const &lhs, ui_software_info const &rhs) const; + }; + + using favorites_set = std::set; + using sorted_favorites = std::vector >; + + // implementation + template static void apply_running_machine(running_machine &machine, T &&action); + template void add_impl(T &&key); + template bool check_impl(T const &key) const; + template bool remove_impl(T const &key); + void update_sorted(); + void save_favorites(); + + // internal state + ui_options &m_options; + favorites_set m_favorites; + sorted_favorites m_sorted; + bool m_need_sort; +}; + +#endif // MAME_FRONTEND_UI_INIFILE_H diff --git a/src/icludes/frontend/mame/ui/inputmap.cpp b/src/icludes/frontend/mame/ui/inputmap.cpp new file mode 100644 index 0000000..38dd731 --- /dev/null +++ b/src/icludes/frontend/mame/ui/inputmap.cpp @@ -0,0 +1,599 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/********************************************************************* + + ui/inputmap.cpp + + Internal menus for input mappings. + +*********************************************************************/ + +#include "emu.h" +#include "ui/inputmap.h" + +#include "uiinput.h" +#include "ui/ui.h" + +#include + + +namespace ui { + +/*------------------------------------------------- + menu_input_groups - handle the input groups + menu +-------------------------------------------------*/ + +menu_input_groups::menu_input_groups(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ +} + +menu_input_groups::~menu_input_groups() +{ +} + +void menu_input_groups::populate(float &customtop, float &custombottom) +{ + // build up the menu + item_append(_("User Interface"), 0, (void *)uintptr_t(IPG_UI + 1)); + for (int player = 0; player < MAX_PLAYERS; player++) + { + auto s = string_format(_("Player %1$d Controls"), player + 1); + item_append(s, 0, (void *)uintptr_t(IPG_PLAYER1 + player + 1)); + } + item_append(_("Other Controls"), 0, (void *)uintptr_t(IPG_OTHER + 1)); + item_append(menu_item_type::SEPARATOR); +} + +void menu_input_groups::handle(event const *ev) +{ + // process the menu + if (ev && (ev->iptkey == IPT_UI_SELECT)) + menu::stack_push(ui(), container(), int(uintptr_t(ev->itemref) - 1)); +} + + +/*------------------------------------------------- + menu_input_general - handle the general + input menu +-------------------------------------------------*/ + +menu_input_general::menu_input_general(mame_ui_manager &mui, render_container &container, int _group) + : menu_input(mui, container) + , group(_group) +{ +} + +menu_input_general::~menu_input_general() +{ +} + +void menu_input_general::populate(float &customtop, float &custombottom) +{ + if (data.empty()) + { + assert(!pollingitem); + + // iterate over the input ports and add menu items + for (const input_type_entry &entry : machine().ioport().types()) + { + // add if we match the group and we have a valid name + if (entry.group() == group) + { + std::string name = entry.name(); + if (!name.empty()) + { + // loop over all sequence types + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) + { + // build an entry for the standard sequence + input_item_data &item(data.emplace_back()); + item.ref = &entry; + item.seqtype = seqtype; + item.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqtype); + item.defseq = &entry.defseq(seqtype); + item.group = entry.group(); + item.type = ioport_manager::type_is_analog(entry.type()) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL; + item.is_optional = false; + item.name = name; + item.owner = nullptr; + + // stop after one, unless we're analog + if (item.type == INPUT_TYPE_DIGITAL) + break; + } + } + } + } + } + else + { + for (input_item_data &item : data) + { + const input_type_entry &entry(*reinterpret_cast(item.ref)); + item.seq = machine().ioport().type_seq(entry.type(), entry.player(), item.seqtype); + } + } + + // populate the menu in a standard fashion + populate_sorted(customtop, custombottom); + item_append(menu_item_type::SEPARATOR); +} + +void menu_input_general::update_input(input_item_data &seqchangeditem) +{ + const input_type_entry &entry = *reinterpret_cast(seqchangeditem.ref); + machine().ioport().set_type_seq(entry.type(), entry.player(), seqchangeditem.seqtype, seqchangeditem.seq); + seqchangeditem.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqchangeditem.seqtype); +} + + +/*------------------------------------------------- + menu_input_specific - handle the game-specific + input menu +-------------------------------------------------*/ + +menu_input_specific::menu_input_specific(mame_ui_manager &mui, render_container &container) : menu_input(mui, container) +{ +} + +menu_input_specific::~menu_input_specific() +{ +} + +void menu_input_specific::populate(float &customtop, float &custombottom) +{ + if (data.empty()) + { + assert(!pollingitem); + + // iterate over the input ports and add menu items + for (auto &port : machine().ioport().ports()) + { + for (ioport_field &field : port.second->fields()) + { + const ioport_type_class type_class = field.type_class(); + + // add if it's enabled and it's a system-specific class + if (field.enabled() && (type_class == INPUT_CLASS_CONTROLLER || type_class == INPUT_CLASS_MISC || type_class == INPUT_CLASS_KEYBOARD)) + { + // loop over all sequence types + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) + { + // build an entry for the standard sequence + input_item_data &item(data.emplace_back()); + item.ref = &field; + item.seqtype = seqtype; + item.seq = field.seq(seqtype); + item.defseq = &field.defseq(seqtype); + item.group = machine().ioport().type_group(field.type(), field.player()); + item.type = field.is_analog() ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL; + item.is_optional = field.optional(); + item.name = field.name(); + item.owner = &field.device(); + + // stop after one, unless we're analog + if (item.type == INPUT_TYPE_DIGITAL) + break; + } + } + } + } + + // sort it + std::sort( + data.begin(), + data.end(), + [] (const input_item_data &i1, const input_item_data &i2) + { + int cmp = strcmp(i1.owner->tag(), i2.owner->tag()); + if (cmp < 0) + return true; + if (cmp > 0) + return false; + if (i1.group < i2.group) + return true; + if (i1.group > i2.group) + return false; + const ioport_field &field1 = *reinterpret_cast(i1.ref); + const ioport_field &field2 = *reinterpret_cast(i2.ref); + if (field1.type() < field2.type()) + return true; + if (field1.type() > field2.type()) + return false; + std::vector codes1 = field1.keyboard_codes(0); + std::vector codes2 = field2.keyboard_codes(0); + if (!codes1.empty() && (codes2.empty() || codes1[0] < codes2[0])) + return true; + if (!codes2.empty() && (codes1.empty() || codes1[0] > codes2[0])) + return false; + cmp = i1.name.compare(i2.name); + if (cmp < 0) + return true; + if (cmp > 0) + return false; + return i1.type < i2.type; + }); + } + else + { + for (input_item_data &item : data) + { + const ioport_field &field(*reinterpret_cast(item.ref)); + item.seq = field.seq(item.seqtype); + } + } + + // populate the menu in a standard fashion + if (!data.empty()) + populate_sorted(customtop, custombottom); + else + item_append(_("This machine has no configurable inputs."), FLAG_DISABLE, nullptr); + + item_append(menu_item_type::SEPARATOR); +} + +void menu_input_specific::update_input(input_item_data &seqchangeditem) +{ + ioport_field::user_settings settings; + + // yeah, the const_cast is naughty, but we know we stored a non-const reference in it + ioport_field const &field(*reinterpret_cast(seqchangeditem.ref)); + field.get_user_settings(settings); + settings.seq[seqchangeditem.seqtype] = seqchangeditem.seq; + if (seqchangeditem.seq.is_default()) + settings.cfg[seqchangeditem.seqtype].clear(); + else if (!seqchangeditem.seq.length()) + settings.cfg[seqchangeditem.seqtype] = "NONE"; + else + settings.cfg[seqchangeditem.seqtype] = machine().input().seq_to_tokens(seqchangeditem.seq); + const_cast(field).set_user_settings(settings); + seqchangeditem.seq = field.seq(seqchangeditem.seqtype); +} + + +/*------------------------------------------------- + menu_input - display a menu for inputs +-------------------------------------------------*/ + +menu_input::menu_input(mame_ui_manager &mui, render_container &container) + : menu(mui, container) + , data() + , pollingitem(nullptr) + , seq_poll() + , errormsg() + , erroritem(nullptr) + , lastitem(nullptr) + , record_next(false) + , modified_ticks(0) +{ + set_process_flags(PROCESS_LR_ALWAYS); +} + +menu_input::~menu_input() +{ +} + +void menu_input::menu_activated() +{ + // scripts can change settings out from under us + reset(reset_options::REMEMBER_POSITION); +} + + +/*------------------------------------------------- + toggle_none_default - toggle between "NONE" + and the default item +-------------------------------------------------*/ + +void menu_input::toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq) +{ + if (original_seq.empty()) // if we used to be "none", toggle to the default value + selected_seq.set_default(); + else // otherwise, toggle to "none" + selected_seq.reset(); +} + +void menu_input::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) +{ + if (pollingitem) + { + const std::string seqname = machine().input().seq_name(seq_poll->sequence()); + char const *const text[] = { seqname.c_str() }; + draw_text_box( + std::begin(text), std::end(text), + x1, x2, y2 + ui().box_tb_border(), y2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); + } + else + { + if (erroritem && (selectedref != erroritem)) + { + errormsg.clear(); + erroritem = nullptr; + } + + if (erroritem) + { + char const *const text[] = { errormsg.c_str() }; + draw_text_box( + std::begin(text), std::end(text), + x1, x2, y2 + ui().box_tb_border(), y2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), UI_RED_COLOR, 1.0f); + } + else if (selectedref) + { + const input_item_data &item = *reinterpret_cast(selectedref); + if ((INPUT_TYPE_ANALOG != item.type) && machine().input().seq_pressed(item.seq)) + { + char const *const text[] = { _("Pressed") }; + draw_text_box( + std::begin(text), std::end(text), + x1, x2, y2 + ui().box_tb_border(), y2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); + } + else + { + char const *const text[] = { + record_next ? appendprompt.c_str() : assignprompt.c_str(), + (!item.seq.empty() || item.defseq->empty()) ? clearprompt.c_str() : defaultprompt.c_str() }; + draw_text_box( + std::begin(text), std::end(text), + x1, x2, y2 + ui().box_tb_border(), y2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); + } + } + } +} + +void menu_input::handle(event const *ev) +{ + input_item_data *seqchangeditem = nullptr; + bool invalidate = false; + + // process the menu + if (pollingitem) + { + // if we are polling, handle as a special case + input_item_data *const item = pollingitem; + + // prevent race condition between ui_input().pressed() and poll() + if (modified_ticks == 0 && seq_poll->modified()) + modified_ticks = osd_ticks(); + + if (machine().ui_input().pressed(IPT_UI_CANCEL)) + { + // if UI_CANCEL is pressed, abort + pollingitem = nullptr; + set_process_flags(PROCESS_LR_ALWAYS); + if (!seq_poll->modified() || modified_ticks == osd_ticks()) + { + // cancelled immediately - toggle between default and none + record_next = false; + toggle_none_default(item->seq, starting_seq, *item->defseq); + seqchangeditem = item; + } + else + { + // entered something before cancelling - abandon change + invalidate = true; + } + seq_poll.reset(); + } + else if (seq_poll->poll()) // poll again; if finished, update the sequence + { + pollingitem = nullptr; + set_process_flags(PROCESS_LR_ALWAYS); + if (seq_poll->valid()) + { + record_next = true; + item->seq = seq_poll->sequence(); + seqchangeditem = item; + } + else + { + // entered invalid sequence - abandon change + invalidate = true; + errormsg = _("Invalid sequence entered"); + erroritem = item; + } + seq_poll.reset(); + } + } + else if (ev && ev->itemref) + { + // otherwise, handle the events + input_item_data &item = *reinterpret_cast(ev->itemref); + input_item_data *newsel = &item; + switch (ev->iptkey) + { + case IPT_UI_SELECT: // an item was selected: begin polling + set_process_flags(PROCESS_NOKEYS); + errormsg.clear(); + erroritem = nullptr; + modified_ticks = 0; + pollingitem = &item; + lastitem = &item; + starting_seq = item.seq; + if (INPUT_TYPE_ANALOG == item.type) + seq_poll.reset(new axis_sequence_poller(machine().input())); + else + seq_poll.reset(new switch_sequence_poller(machine().input())); + if (record_next) + seq_poll->start(item.seq); + else + seq_poll->start(); + invalidate = true; + break; + + case IPT_UI_CLEAR: // if the clear key was pressed, reset the selected item + errormsg.clear(); + erroritem = nullptr; + toggle_none_default(item.seq, item.seq, *item.defseq); + record_next = false; + seqchangeditem = &item; + break; + + case IPT_UI_PREV_GROUP: + { + auto current = std::distance(data.data(), &item); + bool found_break = false; + while (0 < current) + { + if (!found_break) + { + if (data[--current].owner != item.owner) + found_break = true; + } + else if (data[current].owner != data[current - 1].owner) + { + newsel = &data[current]; + set_selection(newsel); + set_top_line(selected_index() - 1); + break; + } + else + { + --current; + } + if (found_break && !current) + { + newsel = &data[current]; + set_selection(newsel); + set_top_line(selected_index() - 1); + break; + } + } + } + break; + + case IPT_UI_NEXT_GROUP: + { + auto current = std::distance(data.data(), &item); + while (data.size() > ++current) + { + if (data[current].owner != item.owner) + { + newsel = &data[current]; + set_selection(newsel); + set_top_line(selected_index() - 1); + break; + } + } + } + break; + } + + // if the selection changed, reset the "record next" flag + if (newsel != lastitem) + { + if (erroritem) + { + errormsg.clear(); + erroritem = nullptr; + } + record_next = false; + lastitem = &item; + } + + // flip between set and append + // not very discoverable, but with the prompt it isn't completely opaque + if ((IPT_UI_LEFT == ev->iptkey) || (IPT_UI_RIGHT == ev->iptkey)) + { + if (erroritem) + { + errormsg.clear(); + erroritem = nullptr; + } + else if (record_next || !item.seq.empty()) + { + record_next = !record_next; + } + } + } + + // if the sequence changed, update it + if (seqchangeditem) + { + update_input(*seqchangeditem); + + // invalidate the menu to force an update + invalidate = true; + } + + // if the menu is invalidated, clear it now + if (invalidate) + reset(reset_options::REMEMBER_POSITION); +} + + +//------------------------------------------------- +// populate_sorted - take a sorted list of +// input_item_data objects and build up the +// menu from them +//------------------------------------------------- + +void menu_input::populate_sorted(float &customtop, float &custombottom) +{ + const char *nameformat[INPUT_TYPE_TOTAL] = { nullptr }; + + // create a mini lookup table for name format based on type + nameformat[INPUT_TYPE_DIGITAL] = "%1$s"; + nameformat[INPUT_TYPE_ANALOG] = _("input-name", "%1$s Analog"); + nameformat[INPUT_TYPE_ANALOG_INC] = _("input-name", "%1$s Analog Inc"); + nameformat[INPUT_TYPE_ANALOG_DEC] = _("input-name", "%1$s Analog Dec"); + + // build the menu + std::string text, subtext; + const device_t *prev_owner = nullptr; + for (input_item_data &item : data) + { + // generate the name of the item itself, based off the base name and the type + assert(nameformat[item.type] != nullptr); + + if (item.owner && (item.owner != prev_owner)) + { + if (item.owner->owner()) + item_append(string_format(_("%1$s [root%2$s]"), item.owner->type().fullname(), item.owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + else + item_append(string_format(_("[root%1$s]"), item.owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + prev_owner = item.owner; + } + + text = string_format(nameformat[item.type], item.name); + if (item.is_optional) + text = "(" + text + ")"; + + uint32_t flags = 0; + if (&item == pollingitem) + { + // if we're polling this item, use some spaces with left/right arrows + subtext = " "; + flags |= FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW; + } + else + { + // otherwise, generate the sequence name and invert it if different from the default + subtext = machine().input().seq_name(item.seq); + flags |= (item.seq != *item.defseq) ? FLAG_INVERT : 0; + } + + // add the item + item_append(std::move(text), std::move(subtext), flags, &item); + } + + // pre-format messages + assignprompt = util::string_format(_("Press %1$s to set\n"), ui().get_general_input_setting(IPT_UI_SELECT)); + appendprompt = util::string_format(_("Press %1$s to append\n"), ui().get_general_input_setting(IPT_UI_SELECT)); + clearprompt = util::string_format(_("Press %1$s to clear\n"), ui().get_general_input_setting(IPT_UI_CLEAR)); + defaultprompt = util::string_format(_("Press %1$s to restore default\n"), ui().get_general_input_setting(IPT_UI_CLEAR)); + + // leave space for showing the input sequence below the menu + custombottom = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/inputmap.h b/src/icludes/frontend/mame/ui/inputmap.h new file mode 100644 index 0000000..a43a682 --- /dev/null +++ b/src/icludes/frontend/mame/ui/inputmap.h @@ -0,0 +1,119 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/inputmap.h + + Internal menus for input mappings. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_INPUTMAP_H +#define MAME_FRONTEND_UI_INPUTMAP_H + +#pragma once + +#include "ui/menu.h" +#include "iptseqpoll.h" + +#include +#include + + +namespace ui { + +class menu_input_groups : public menu +{ +public: + menu_input_groups(mame_ui_manager &mui, render_container &container); + virtual ~menu_input_groups() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + + +class menu_input : public menu +{ +public: + virtual ~menu_input() override; + +protected: + enum { + INPUT_TYPE_DIGITAL = 0, + INPUT_TYPE_ANALOG = 1, + INPUT_TYPE_ANALOG_DEC = INPUT_TYPE_ANALOG + SEQ_TYPE_DECREMENT, + INPUT_TYPE_ANALOG_INC = INPUT_TYPE_ANALOG + SEQ_TYPE_INCREMENT, + INPUT_TYPE_TOTAL = INPUT_TYPE_ANALOG + SEQ_TYPE_TOTAL + }; + + // internal input menu item data + struct input_item_data + { + const void * ref = nullptr; // reference to type description for global inputs or field for game inputs + input_seq_type seqtype = SEQ_TYPE_INVALID; // sequence type + input_seq seq; // copy of the live sequence + const input_seq * defseq = nullptr; // pointer to the default sequence + std::string name; // base name of the item + const device_t * owner = nullptr; // pointer to the owner of the item + ioport_group group = IPG_INVALID; // group type + uint8_t type = 0U; // type of port + bool is_optional = false; // true if this input is considered optional + }; + using data_vector = std::vector; + + menu_input(mame_ui_manager &mui, render_container &container); + + virtual void menu_activated() override; + + void populate_sorted(float &customtop, float &custombottom); + void toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq); + + data_vector data; + input_item_data *pollingitem; + +private: + std::unique_ptr seq_poll; + std::string assignprompt, appendprompt; + std::string clearprompt, defaultprompt; + std::string errormsg; + input_item_data *erroritem; + input_item_data *lastitem; + bool record_next; + osd_ticks_t modified_ticks; + input_seq starting_seq; + + virtual void custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) override; + virtual void handle(event const *ev) override; + virtual void update_input(input_item_data &seqchangeditem) = 0; +}; + + +class menu_input_general : public menu_input +{ +public: + menu_input_general(mame_ui_manager &mui, render_container &container, int group); + virtual ~menu_input_general() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void update_input(input_item_data &seqchangeditem) override; + + const int group; +}; + + +class menu_input_specific : public menu_input +{ +public: + menu_input_specific(mame_ui_manager &mui, render_container &container); + virtual ~menu_input_specific() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void update_input(input_item_data &seqchangeditem) override; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_INPUTMAP_H diff --git a/src/icludes/frontend/mame/ui/keyboard.cpp b/src/icludes/frontend/mame/ui/keyboard.cpp new file mode 100644 index 0000000..cbdc332 --- /dev/null +++ b/src/icludes/frontend/mame/ui/keyboard.cpp @@ -0,0 +1,116 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ui/keyboard.cpp + + Keyboard mode menu. + +***************************************************************************/ + +#include "emu.h" +#include "ui/keyboard.h" + +#include "natkeyboard.h" + + +namespace ui { + +namespace { + +constexpr uintptr_t ITEM_KBMODE = 0x00000100; +constexpr uintptr_t ITEM_KBDEV_FIRST = 0x00000200; + +} // anonymous namespace + + +menu_keyboard_mode::menu_keyboard_mode(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ +} + +void menu_keyboard_mode::menu_activated() +{ + // scripts could have changed something behind our back + reset(reset_options::REMEMBER_POSITION); +} + +void menu_keyboard_mode::populate(float &customtop, float &custombottom) +{ + natural_keyboard &natkbd(machine().natkeyboard()); + + if (natkbd.can_post()) + { + bool const natmode(natkbd.in_use()); + item_append( + _("Keyboard Mode"), + natmode ? _("Natural") : _("Emulated"), + natmode ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW, + reinterpret_cast(ITEM_KBMODE)); + item_append(menu_item_type::SEPARATOR); + } + + uintptr_t ref(ITEM_KBDEV_FIRST); + for (size_t i = 0; natkbd.keyboard_count() > i; ++i, ++ref) + { + device_t &kbddev(natkbd.keyboard_device(i)); + bool const enabled(natkbd.keyboard_enabled(i)); + item_append( + util::string_format( + kbddev.owner() ? _("%1$s [root%2$s]") : _("[root%2$s]"), + kbddev.type().fullname(), + kbddev.tag()), + enabled ? _("Enabled") : _("Disabled"), + enabled ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW, + reinterpret_cast(ref)); + } + item_append(menu_item_type::SEPARATOR); +} + +menu_keyboard_mode::~menu_keyboard_mode() +{ +} + +void menu_keyboard_mode::handle(event const *ev) +{ + if (ev && uintptr_t(ev->itemref)) + { + natural_keyboard &natkbd(machine().natkeyboard()); + uintptr_t const ref(uintptr_t(ev->itemref)); + bool left(IPT_UI_LEFT == ev->iptkey); + bool right(IPT_UI_RIGHT == ev->iptkey); + if (ITEM_KBMODE == ref) + { + if (IPT_UI_SELECT == ev->iptkey) + { + left = natkbd.in_use(); + right = !left; + } + if ((left || right) && (natkbd.in_use() != right)) + { + natkbd.set_in_use(right); + ev->item->set_subtext(right ? _("Natural") : _("Emulated")); + ev->item->set_flags(right ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW); + } + } + else if (ITEM_KBDEV_FIRST <= ref) + { + auto const kbdno(ref - ITEM_KBDEV_FIRST); + if (IPT_UI_SELECT == ev->iptkey) + { + left = natkbd.keyboard_enabled(kbdno); + right = !left; + } + if ((left || right) && (natkbd.keyboard_enabled(kbdno) != right)) + { + if (right) + natkbd.enable_keyboard(kbdno); + else + natkbd.disable_keyboard(kbdno); + ev->item->set_subtext(right ? _("Enabled") : _("Disabled")); + ev->item->set_flags(right ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW); + } + } + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/keyboard.h b/src/icludes/frontend/mame/ui/keyboard.h new file mode 100644 index 0000000..36371f5 --- /dev/null +++ b/src/icludes/frontend/mame/ui/keyboard.h @@ -0,0 +1,36 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ui/keyboard.h + + Keyboard mode menu. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_KEYBOARD_H +#define MAME_FRONTEND_UI_KEYBOARD_H + +#pragma once + +#include "ui/menu.h" + + +namespace ui { + +class menu_keyboard_mode : public menu +{ +public: + menu_keyboard_mode(mame_ui_manager &mui, render_container &container); + virtual ~menu_keyboard_mode(); + +protected: + virtual void menu_activated() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_KEYBOARD_H diff --git a/src/icludes/frontend/mame/ui/mainmenu.cpp b/src/icludes/frontend/mame/ui/mainmenu.cpp new file mode 100644 index 0000000..74f0060 --- /dev/null +++ b/src/icludes/frontend/mame/ui/mainmenu.cpp @@ -0,0 +1,361 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/********************************************************************* + + ui/mainmenu.cpp + + Internal MAME menus for the user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/mainmenu.h" + +#include "ui/about.h" +#include "ui/analogipt.h" +#include "ui/barcode.h" +#include "ui/cheatopt.h" +#include "ui/confswitch.h" +#include "ui/datmenu.h" +#include "ui/filemngr.h" +#include "ui/info.h" +#include "ui/info_pty.h" +#include "ui/inifile.h" +#include "ui/inputmap.h" +#include "ui/keyboard.h" +#include "ui/miscmenu.h" +#include "ui/pluginopt.h" +#include "ui/selgame.h" +#include "ui/simpleselgame.h" +#include "ui/sliders.h" +#include "ui/slotopt.h" +#include "ui/tapectrl.h" +#include "ui/videoopt.h" + +#include "mame.h" +#include "luaengine.h" + +#include "machine/bcreader.h" +#include "imagedev/cassette.h" + +#include "crsshair.h" +#include "dipty.h" +#include "emuopts.h" +#include "natkeyboard.h" + + +namespace ui { + +enum : unsigned { + INPUT_GROUPS, + INPUT_SPECIFIC, + SETTINGS_DIP_SWITCHES, + SETTINGS_DRIVER_CONFIG, + ANALOG, + BOOKKEEPING, + GAME_INFO, + WARN_INFO, + IMAGE_MENU_IMAGE_INFO, + IMAGE_MENU_FILE_MANAGER, + TAPE_CONTROL, + SLOT_DEVICES, + NETWORK_DEVICES, + KEYBOARD_MODE, + SLIDERS, + VIDEO_TARGETS, + VIDEO_OPTIONS, + CROSSHAIR, + CHEAT, + PLUGINS, + BIOS_SELECTION, + BARCODE_READ, + PTY_INFO, + EXTERNAL_DATS, + ADD_FAVORITE, + REMOVE_FAVORITE, + ABOUT, + QUIT_GAME, + DISMISS, + SELECT_GAME +}; + +/*************************************************************************** + MENU HANDLERS +***************************************************************************/ + +/*------------------------------------------------- + menu_main constructor/destructor +-------------------------------------------------*/ + +menu_main::menu_main(mame_ui_manager &mui, render_container &container) + : menu(mui, container) + , m_phase(machine_phase::PREINIT) +{ + set_needs_prev_menu_item(false); +} + +menu_main::~menu_main() +{ +} + + +/*------------------------------------------------- + menu_activated - handle coming to foreground +-------------------------------------------------*/ + +void menu_main::menu_activated() +{ + if (machine().phase() != m_phase) + reset(reset_options::REMEMBER_REF); +} + + +/*------------------------------------------------- + populate - populate main menu items +-------------------------------------------------*/ + +void menu_main::populate(float &customtop, float &custombottom) +{ + m_phase = machine().phase(); + + item_append(_("Input (general)"), 0, (void *)INPUT_GROUPS); + + item_append(_("Input (this machine)"), 0, (void *)INPUT_SPECIFIC); + + if (ui().machine_info().has_analog()) + item_append(_("Analog Controls"), 0, (void *)ANALOG); + if (ui().machine_info().has_dips()) + item_append(_("DIP Switches"), 0, (void *)SETTINGS_DIP_SWITCHES); + if (ui().machine_info().has_configs()) + item_append(_("Machine Configuration"), 0, (void *)SETTINGS_DRIVER_CONFIG); + + item_append(_("Bookkeeping Info"), 0, (void *)BOOKKEEPING); + + item_append(_("Machine Information"), 0, (void *)GAME_INFO); + + if (ui().found_machine_warnings()) + item_append(_("Warning Information"), 0, (void *)WARN_INFO); + + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) + { + if (image.user_loadable()) + { + item_append(_("Image Information"), 0, (void *)IMAGE_MENU_IMAGE_INFO); + + item_append(_("File Manager"), 0, (void *)IMAGE_MENU_FILE_MANAGER); + + break; + } + } + + if (cassette_device_enumerator(machine().root_device()).first() != nullptr) + item_append(_("Tape Control"), 0, (void *)TAPE_CONTROL); + + if (pty_interface_enumerator(machine().root_device()).first() != nullptr) + item_append(_("Pseudo Terminals"), 0, (void *)PTY_INFO); + + if (ui().machine_info().has_bioses()) + item_append(_("BIOS Selection"), 0, (void *)BIOS_SELECTION); + + if (slot_interface_enumerator(machine().root_device()).first() != nullptr) + item_append(_("Slot Devices"), 0, (void *)SLOT_DEVICES); + + if (barcode_reader_device_enumerator(machine().root_device()).first() != nullptr) + item_append(_("Barcode Reader"), 0, (void *)BARCODE_READ); + + if (network_interface_enumerator(machine().root_device()).first() != nullptr) + item_append(_("Network Devices"), 0, (void*)NETWORK_DEVICES); + + if (machine().natkeyboard().keyboard_count()) + item_append(_("Keyboard Mode"), 0, (void *)KEYBOARD_MODE); + + item_append(_("Slider Controls"), 0, (void *)SLIDERS); + + item_append(_("Video Options"), 0, (void *)VIDEO_TARGETS); + + if (machine().crosshair().get_usage()) + item_append(_("Crosshair Options"), 0, (void *)CROSSHAIR); + + if (machine().options().cheat()) + item_append(_("Cheat"), 0, (void *)CHEAT); + + if (machine_phase::RESET <= m_phase) + { + if (machine().options().plugins() && !mame_machine_manager::instance()->lua()->get_menu().empty()) + item_append(_("Plugin Options"), 0, (void *)PLUGINS); + + if (mame_machine_manager::instance()->lua()->call_plugin_check("data_list", "", true)) + item_append(_("External DAT View"), 0, (void *)EXTERNAL_DATS); + } + + item_append(menu_item_type::SEPARATOR); + + if (!mame_machine_manager::instance()->favorite().is_favorite(machine())) + item_append(_("Add To Favorites"), 0, (void *)ADD_FAVORITE); + else + item_append(_("Remove From Favorites"), 0, (void *)REMOVE_FAVORITE); + + item_append(menu_item_type::SEPARATOR); + + item_append(string_format(_("About %1$s"), emulator_info::get_appname()), 0, (void *)ABOUT); + + item_append(menu_item_type::SEPARATOR); + +// item_append(_("Quit from Machine"), 0, (void *)QUIT_GAME); + + if (machine_phase::INIT == m_phase) + { + item_append(_("Start Machine"), 0, (void *)DISMISS); + } + else + { + item_append(_("Select New Machine"), 0, (void *)SELECT_GAME); + item_append(_("Return to Machine"), 0, (void *)DISMISS); + } +} + + +/*------------------------------------------------- + handle - handle main menu events +-------------------------------------------------*/ + +void menu_main::handle(event const *ev) +{ + // process the menu + if (ev && (ev->iptkey == IPT_UI_SELECT)) + { + switch (uintptr_t(ev->itemref)) + { + case INPUT_GROUPS: + menu::stack_push(ui(), container()); + break; + + case INPUT_SPECIFIC: + menu::stack_push(ui(), container()); + break; + + case SETTINGS_DIP_SWITCHES: + menu::stack_push(ui(), container()); + break; + + case SETTINGS_DRIVER_CONFIG: + menu::stack_push(ui(), container()); + break; + + case ANALOG: + menu::stack_push(ui(), container()); + break; + + case BOOKKEEPING: + menu::stack_push(ui(), container()); + break; + + case GAME_INFO: + menu::stack_push(ui(), container()); + break; + + case WARN_INFO: + menu::stack_push(ui(), container()); + break; + + case IMAGE_MENU_IMAGE_INFO: + menu::stack_push(ui(), container()); + break; + + case IMAGE_MENU_FILE_MANAGER: + menu::stack_push(ui(), container(), nullptr); + break; + + case TAPE_CONTROL: + menu::stack_push(ui(), container(), nullptr); + break; + + case PTY_INFO: + menu::stack_push(ui(), container()); + break; + + case SLOT_DEVICES: + menu::stack_push(ui(), container()); + break; + + case NETWORK_DEVICES: + menu::stack_push(ui(), container()); + break; + + case KEYBOARD_MODE: + menu::stack_push(ui(), container()); + break; + + case SLIDERS: + menu::stack_push(ui(), container(), false); + break; + + case VIDEO_TARGETS: + menu::stack_push(ui(), container()); + break; + + case VIDEO_OPTIONS: + menu::stack_push(ui(), container(), *machine().render().first_target(), false); + break; + + case CROSSHAIR: + menu::stack_push(ui(), container()); + break; + + case CHEAT: + menu::stack_push(ui(), container()); + break; + + case PLUGINS: + menu::stack_push(ui(), container()); + break; + + case SELECT_GAME: + if (machine().options().ui() == emu_options::UI_SIMPLE) + menu::stack_push(ui(), container(), nullptr); + else + menu::stack_push(ui(), container(), nullptr); + break; + + case ABOUT: + menu::stack_push(ui(), container()); + break; + + case BIOS_SELECTION: + menu::stack_push(ui(), container()); + break; + + case BARCODE_READ: + menu::stack_push(ui(), container(), nullptr); + break; + + case EXTERNAL_DATS: + menu::stack_push(ui(), container()); + break; + + case ADD_FAVORITE: + mame_machine_manager::instance()->favorite().add_favorite(machine()); + reset(reset_options::REMEMBER_REF); + break; + + case REMOVE_FAVORITE: + mame_machine_manager::instance()->favorite().remove_favorite(machine()); + reset(reset_options::REMEMBER_REF); + break; + + case QUIT_GAME: + stack_pop(); + ui().request_quit(); + break; + + case DISMISS: + stack_pop(); + return; + + default: + fatalerror("ui::menu_main::handle - unknown reference\n"); + } + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/mainmenu.h b/src/icludes/frontend/mame/ui/mainmenu.h new file mode 100644 index 0000000..6671dd9 --- /dev/null +++ b/src/icludes/frontend/mame/ui/mainmenu.h @@ -0,0 +1,39 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/mainmenu.h + + Internal MAME menus for the user interface. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_MAINMENU_H +#define MAME_FRONTEND_UI_MAINMENU_H + +#pragma once + +#include "ui/menu.h" + + +namespace ui { + +class menu_main : public menu +{ +public: + menu_main(mame_ui_manager &mui, render_container &container); + virtual ~menu_main(); + +protected: + virtual void menu_activated() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + machine_phase m_phase; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_MAINMENU_H diff --git a/src/icludes/frontend/mame/ui/menu.cpp b/src/icludes/frontend/mame/ui/menu.cpp new file mode 100644 index 0000000..1b8290f --- /dev/null +++ b/src/icludes/frontend/mame/ui/menu.cpp @@ -0,0 +1,1315 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Maurizio Petrarota +/********************************************************************* + + ui/menu.cpp + + Internal MAME menus for the user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/menu.h" + +#include "ui/ui.h" +#include "ui/mainmenu.h" +#include "ui/utils.h" +#include "ui/miscmenu.h" + +#include "cheat.h" +#include "mame.h" + +#include "corestr.h" +#include "drivenum.h" +#include "rendutil.h" +#include "uiinput.h" + +#include +#include +#include +#include + + +namespace ui { + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +menu::global_state &menu::get_global_state(mame_ui_manager &ui) +{ + return ui.get_session_data(ui); +} + +//------------------------------------------------- +// exclusive_input_pressed - return true if the +// given key is pressed and we haven't already +// reported a key +//------------------------------------------------- + +bool menu::exclusive_input_pressed(int &iptkey, int key, int repeat) +{ + if ((iptkey == IPT_INVALID) && machine().ui_input().pressed_repeat(key, repeat)) + { + iptkey = key; + return true; + } + else + { + return false; + } +} + + + +/*************************************************************************** + CORE SYSTEM MANAGEMENT +***************************************************************************/ + +menu::global_state::global_state(mame_ui_manager &ui) + : widgets_manager(ui.machine()) + , m_ui(ui) + , m_bgrnd_bitmap() + , m_bgrnd_texture(nullptr, ui.machine().render()) + , m_stack() + , m_free() + , m_hide(false) +{ + render_manager &render(ui.machine().render()); + + // create a texture for main menu background + m_bgrnd_texture.reset(render.texture_alloc(render_texture::hq_scale)); + if (ui.options().use_background_image() && (&ui.machine().system() == &GAME_NAME(___empty))) + { + m_bgrnd_bitmap = std::make_unique(0, 0); + emu_file backgroundfile(".", OPEN_FLAG_READ); + if (!backgroundfile.open("background.jpg")) + { + render_load_jpeg(*m_bgrnd_bitmap, backgroundfile); + backgroundfile.close(); + } + + if (!m_bgrnd_bitmap->valid() && !backgroundfile.open("background.png")) + { + render_load_png(*m_bgrnd_bitmap, backgroundfile); + backgroundfile.close(); + } + + if (m_bgrnd_bitmap->valid()) + m_bgrnd_texture->set_bitmap(*m_bgrnd_bitmap, m_bgrnd_bitmap->cliprect(), TEXFORMAT_ARGB32); + else + m_bgrnd_bitmap->reset(); + } +} + + +menu::global_state::~global_state() +{ + stack_reset(); + clear_free_list(); +} + + +void menu::global_state::stack_push(std::unique_ptr &&menu) +{ + if (m_stack && m_stack->is_active()) + { + m_stack->m_active = false; + m_stack->menu_deactivated(); + } + menu->m_parent = std::move(m_stack); + m_stack = std::move(menu); + m_stack->machine().ui_input().reset(); +} + + +void menu::global_state::stack_pop() +{ + if (m_stack) + { + if (m_stack->is_one_shot()) + m_hide = true; + if (m_stack->is_active()) + { + m_stack->m_active = false; + m_stack->menu_deactivated(); + } + m_stack->menu_dismissed(); + std::unique_ptr menu(std::move(m_stack)); + m_stack = std::move(menu->m_parent); + menu->m_parent = std::move(m_free); + m_free = std::move(menu); + m_ui.machine().ui_input().reset(); + } +} + + +void menu::global_state::stack_reset() +{ + while (m_stack) + stack_pop(); +} + + +void menu::global_state::clear_free_list() +{ + // free stack is in reverse order - unwind it properly + std::unique_ptr reversed; + while (m_free) + { + std::unique_ptr menu(std::move(m_free)); + m_free = std::move(menu->m_parent); + menu->m_parent = std::move(reversed); + reversed = std::move(menu); + } + while (reversed) + reversed = std::move(reversed->m_parent); +} + + +bool menu::global_state::stack_has_special_main_menu() const +{ + for (auto menu = m_stack.get(); menu != nullptr; menu = menu->m_parent.get()) + { + if (menu->is_special_main_menu()) + return true; + } + return false; +} + + +uint32_t menu::global_state::ui_handler(render_container &container) +{ + // if we have no menus stacked up, start with the main menu + if (!m_stack) + stack_push(std::make_unique(m_ui, container)); + + // ensure topmost menu is active - need a loop because it could push another menu + while (m_stack && !m_stack->is_active()) + { + m_stack->m_active = true; + m_stack->menu_activated(); + } + + // update the menu state + m_hide = false; + if (m_stack) + m_stack->do_handle(); + + // clear up anything pending being released + clear_free_list(); + + // if the menus are to be hidden, return a cancel here + if (m_ui.is_menu_active() && (m_hide || !m_stack)) + { + if (m_stack) + { + if (m_stack->is_one_shot()) + { + stack_pop(); + } + else if (m_stack->is_active()) + { + m_stack->m_active = false; + m_stack->menu_deactivated(); + } + } + return UI_HANDLER_CANCEL; + } + + return 0; +} + + + +/*************************************************************************** + CORE MENU MANAGEMENT +***************************************************************************/ + +//------------------------------------------------- +// menu - menu constructor +//------------------------------------------------- + +menu::menu(mame_ui_manager &mui, render_container &container) + : m_global_state(get_global_state(mui)) + , m_ui(mui) + , m_container(container) + , m_parent() + , m_items() + , m_process_flags(0) + , m_selected(0) + , m_hover(1) + , m_special_main_menu(false) + , m_one_shot(false) + , m_needs_prev_menu_item(true) + , m_active(false) + , m_event() + , m_customtop(0.0f) + , m_custombottom(0.0f) + , m_resetpos(0) + , m_resetref(nullptr) + , m_mouse_hit(false) + , m_mouse_button(false) + , m_mouse_x(-1.0f) + , m_mouse_y(-1.0f) +{ + reset(reset_options::SELECT_FIRST); + + top_line = 0; + m_visible_lines = 0; + m_visible_items = 0; +} + + +//------------------------------------------------- +// ~menu - menu destructor +//------------------------------------------------- + +menu::~menu() +{ +} + + +//------------------------------------------------- +// reset - free all items in the menu +//------------------------------------------------- + +void menu::reset(reset_options options) +{ + // based on the reset option, set the reset info + m_resetpos = 0; + m_resetref = nullptr; + if (options == reset_options::REMEMBER_POSITION) + m_resetpos = m_selected; + else if (options == reset_options::REMEMBER_REF) + m_resetref = get_selection_ref(); + + // reset the item count back to 0 + m_items.clear(); + m_visible_items = 0; + m_selected = 0; +} + + +//------------------------------------------------- +// set_special_main_menu - set whether the +// menu has special needs +//------------------------------------------------- + +void menu::set_special_main_menu(bool special) +{ + m_special_main_menu = special; +} + + +//------------------------------------------------- +// item_append - append a new item to the +// end of the menu +//------------------------------------------------- + +void menu::item_append(menu_item_type type, uint32_t flags) +{ + if (type == menu_item_type::SEPARATOR) + item_append(MENU_SEPARATOR_ITEM, flags, nullptr, menu_item_type::SEPARATOR); +} + +//------------------------------------------------- +// item_append - append a new item to the +// end of the menu +//------------------------------------------------- + +void menu::item_append(std::string &&text, std::string &&subtext, uint32_t flags, void *ref, menu_item_type type) +{ + // allocate a new item and populate it + menu_item pitem(type, ref, flags); + pitem.set_text(std::move(text)); + pitem.set_subtext(std::move(subtext)); + + // append to array + auto index = m_items.size(); + if (!m_items.empty() && m_needs_prev_menu_item) + { + m_items.emplace(m_items.end() - 1, std::move(pitem)); + --index; + } + else + { + m_items.emplace_back(std::move(pitem)); + } + + // update the selection if we need to + if ((m_resetpos == index) || (m_resetref && (m_resetref == ref))) + m_selected = index; + if (m_resetpos == (m_items.size() - 1)) + m_selected = m_items.size() - 1; +} + + +//------------------------------------------------- +// item_append_on_off - append a new "On"/"Off" +// item to the end of the menu +//------------------------------------------------- + +void menu::item_append_on_off(const std::string &text, bool state, uint32_t flags, void *ref, menu_item_type type) +{ + if (flags & FLAG_DISABLE) + ref = nullptr; + else + flags |= state ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW; + + item_append(std::string(text), state ? _("On") : _("Off"), flags, ref, type); +} + + +//------------------------------------------------- +// process - process a menu, drawing it +// and returning any interesting events +//------------------------------------------------- + +const menu::event *menu::process() +{ + // reset the event + m_event.iptkey = IPT_INVALID; + + // first make sure our selection is valid + validate_selection(1); + + // if we're not running the emulation, draw parent menus in the background + auto const draw_parent = + [] (auto &self, menu *parent) -> bool + { + if (!parent || !(parent->is_special_main_menu() || self(self, parent->m_parent.get()))) + return false; + else + parent->draw(PROCESS_NOINPUT); + return true; + }; + if (draw_parent(draw_parent, m_parent.get())) + container().add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(114, 0, 0, 0), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + + // draw the menu proper + draw(m_process_flags); + + // process input + if (!(m_process_flags & (PROCESS_NOKEYS | PROCESS_NOINPUT))) + { + // read events + handle_events(m_process_flags, m_event); + + // handle the keys if we don't already have an event + if (m_event.iptkey == IPT_INVALID) + handle_keys(m_process_flags, m_event.iptkey); + } + + // update the selected item in the event + if ((m_event.iptkey != IPT_INVALID) && selection_valid()) + { + m_event.itemref = get_selection_ref(); + m_event.item = &m_items[m_selected]; + return &m_event; + } + else + { + return nullptr; + } +} + + +//------------------------------------------------- +// set_selection - changes the index +// of the currently selected menu item +//------------------------------------------------- + +void menu::set_selection(void *selected_itemref) +{ + m_selected = -1; + for (int itemnum = 0; itemnum < m_items.size(); itemnum++) + { + if (m_items[itemnum].ref() == selected_itemref) + { + m_selected = itemnum; + break; + } + } +} + + + +/*************************************************************************** + INTERNAL MENU PROCESSING +***************************************************************************/ + +//------------------------------------------------- +// draw - draw a menu +//------------------------------------------------- + +void menu::draw(uint32_t flags) +{ + // first draw the FPS counter + if (ui().show_fps_counter()) + { + ui().draw_text_full( + container(), + machine().video().speed_text(), + 0.0f, 0.0f, 1.0f, + text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD, + mame_ui_manager::OPAQUE_, rgb_t::white(), rgb_t::black(), + nullptr, nullptr); + } + + bool const customonly = (flags & PROCESS_CUSTOM_ONLY); + bool const noinput = (flags & PROCESS_NOINPUT); + float const aspect = machine().render().ui_aspect(&container()); + float const line_height = ui().get_line_height(); + float const lr_arrow_width = 0.4f * line_height * aspect; + float const ud_arrow_width = line_height * aspect; + float const gutter_width = lr_arrow_width * 1.3f; + float const lr_border = ui().box_lr_border() * aspect; + + if (is_special_main_menu()) + draw_background(); + + // compute the width and height of the full menu + float visible_width = 0; + float visible_main_menu_height = 0; + for (auto const &pitem : m_items) + { + // compute width of left hand side + float total_width = gutter_width + ui().get_string_width(pitem.text()) + gutter_width; + + // add in width of right hand side + if (!pitem.subtext().empty()) + total_width += 2.0f * gutter_width + ui().get_string_width(pitem.subtext()); + else if (pitem.flags() & FLAG_UI_HEADING) + total_width += 4.0f * ud_arrow_width; + + // track the maximum + if (total_width > visible_width) + visible_width = total_width; + + // track the height as well + visible_main_menu_height += line_height; + } + + // account for extra space at the top and bottom + float const visible_extra_menu_height = m_customtop + m_custombottom; + + // add a little bit of slop for rounding + visible_width += 0.01f; + visible_main_menu_height += 0.01f; + + // if we are too wide or too tall, clamp it down + visible_width = std::min(visible_width, 1.0f - ((lr_border + (aspect * UI_LINE_WIDTH)) * 2.0f)); + + // if the menu and extra menu won't fit, take away part of the regular menu, it will scroll + if (visible_main_menu_height + visible_extra_menu_height + 2.0f * ui().box_tb_border() > 1.0f) + visible_main_menu_height = 1.0f - 2.0f * ui().box_tb_border() - visible_extra_menu_height; + + m_visible_lines = std::min(int(std::floor(visible_main_menu_height / line_height)), int(unsigned(m_items.size()))); + visible_main_menu_height = float(m_visible_lines) * line_height; + + // compute top/left of inner menu area by centering + float const visible_left = (1.0f - visible_width) * 0.5f; + float const visible_top = ((1.0f - visible_main_menu_height - visible_extra_menu_height) * 0.5f) + m_customtop; + + // first add us a box + float const x1 = visible_left - lr_border; + float const y1 = visible_top - ui().box_tb_border(); + float const x2 = visible_left + visible_width + lr_border; + float const y2 = visible_top + visible_main_menu_height + ui().box_tb_border(); + if (!customonly) + ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color()); + + if ((m_selected >= (top_line + m_visible_lines)) || (m_selected < (top_line + 1))) + top_line = m_selected - (m_visible_lines / 2); + if (top_line < 0 || is_first_selected()) + top_line = 0; + else if ((top_line > (m_items.size() - m_visible_lines)) || is_last_selected()) + top_line = m_items.size() - m_visible_lines; + else if (m_selected >= (top_line + m_visible_lines - 2)) + top_line = m_selected - m_visible_lines + ((m_selected == (m_items.size() - 1)) ? 1: 2); + + // if scrolling, show arrows + bool const show_top_arrow((m_items.size() > m_visible_lines) && !first_item_visible()); + bool const show_bottom_arrow((m_items.size() > m_visible_lines) && !last_item_visible()); + + // set the number of visible lines, minus 1 for top arrow and 1 for bottom arrow + m_visible_items = m_visible_lines - (show_top_arrow ? 1 : 0) - (show_bottom_arrow ? 1 : 0); + + // determine effective positions taking into account the hilighting arrows + float const effective_width = visible_width - 2.0f * gutter_width; + float const effective_left = visible_left + gutter_width; + + // locate mouse + if (!customonly && !noinput) + map_mouse(); + else + ignore_mouse(); + + // loop over visible lines + m_hover = m_items.size() + 1; + bool selected_subitem_too_big = false; + float const line_x0 = x1 + 0.5f * UI_LINE_WIDTH; + float const line_x1 = x2 - 0.5f * UI_LINE_WIDTH; + if (!customonly) + { + for (int linenum = 0; linenum < m_visible_lines; linenum++) + { + auto const itemnum = top_line + linenum; + menu_item const &pitem = m_items[itemnum]; + std::string_view const itemtext = pitem.text(); + rgb_t fgcolor = ui().colors().text_color(); + rgb_t bgcolor = ui().colors().text_bg_color(); + rgb_t fgcolor2 = ui().colors().subitem_color(); + rgb_t fgcolor3 = ui().colors().clone_color(); + float const line_y0 = visible_top + (float)linenum * line_height; + float const line_y1 = line_y0 + line_height; + + // work out what we're dealing with + bool const uparrow = !linenum && show_top_arrow; + bool const downarrow = (linenum == (m_visible_lines - 1)) && show_bottom_arrow; + + // set the hover if this is our item + bool const hovered = mouse_in_rect(line_x0, line_y0, line_x1, line_y1); + if (hovered) + { + if (uparrow) + m_hover = HOVER_ARROW_UP; + else if (downarrow) + m_hover = HOVER_ARROW_DOWN; + else if (is_selectable(pitem)) + m_hover = itemnum; + } + + if (is_selected(itemnum)) + { + // if we're selected, draw with a different background + fgcolor = fgcolor2 = fgcolor3 = ui().colors().selected_color(); + bgcolor = ui().colors().selected_bg_color(); + } + else if (hovered && (uparrow || downarrow || is_selectable(pitem))) + { + // else if the mouse is over this item, draw with a different background + fgcolor = fgcolor2 = fgcolor3 = ui().colors().mouseover_color(); + bgcolor = ui().colors().mouseover_bg_color(); + } + + // if we have some background hilighting to do, add a quad behind everything else + if (bgcolor != ui().colors().text_bg_color()) + highlight(line_x0, line_y0, line_x1, line_y1, bgcolor); + + if (uparrow) + { + // if we're on the top line, display the up arrow + draw_arrow( + 0.5f * (x1 + x2) - 0.5f * ud_arrow_width, + line_y0 + 0.25f * line_height, + 0.5f * (x1 + x2) + 0.5f * ud_arrow_width, + line_y0 + 0.75f * line_height, + fgcolor, + ROT0); + } + else if (downarrow) + { + // if we're on the bottom line, display the down arrow + draw_arrow( + 0.5f * (x1 + x2) - 0.5f * ud_arrow_width, + line_y0 + 0.25f * line_height, + 0.5f * (x1 + x2) + 0.5f * ud_arrow_width, + line_y0 + 0.75f * line_height, + fgcolor, + ROT0 ^ ORIENTATION_FLIP_Y); + } + else if (pitem.type() == menu_item_type::SEPARATOR) + { + // if we're just a divider, draw a line + container().add_line(visible_left, line_y0 + 0.5f * line_height, visible_left + visible_width, line_y0 + 0.5f * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } + else if (pitem.subtext().empty()) + { + // if we don't have a subitem, just draw the string centered + if (pitem.flags() & FLAG_UI_HEADING) + { + float heading_width = ui().get_string_width(itemtext); + container().add_line(visible_left, line_y0 + 0.5f * line_height, visible_left + ((visible_width - heading_width) / 2) - lr_border, line_y0 + 0.5f * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_line(visible_left + visible_width - ((visible_width - heading_width) / 2) + lr_border, line_y0 + 0.5f * line_height, visible_left + visible_width, line_y0 + 0.5f * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } + ui().draw_text_full( + container(), + itemtext, + effective_left, line_y0, effective_width, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NORMAL, fgcolor, bgcolor, + nullptr, nullptr); + } + else + { + // otherwise, draw the item on the left and the subitem text on the right + bool const subitem_invert(pitem.flags() & FLAG_INVERT); + float item_width, subitem_width; + + // draw the left-side text + ui().draw_text_full( + container(), + itemtext, + effective_left, line_y0, effective_width, + text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NORMAL, fgcolor, bgcolor, + &item_width, nullptr); + + if (pitem.flags() & FLAG_COLOR_BOX) + { + rgb_t color = rgb_t((uint32_t)strtoul(pitem.subtext().c_str(), nullptr, 16)); + + // give 2 spaces worth of padding + subitem_width = ui().get_string_width("FF00FF00"); + + ui().draw_outlined_box( + container(), + effective_left + effective_width - subitem_width, line_y0 + (UI_LINE_WIDTH * 2.0f), + effective_left + effective_width, line_y1 - (UI_LINE_WIDTH * 2.0f), + color); + } + else + { + std::string_view subitem_text(pitem.subtext()); + + // give 2 spaces worth of padding + item_width += 2.0f * gutter_width; + + // if the subitem doesn't fit here, display dots + if (ui().get_string_width(subitem_text) > effective_width - item_width) + { + subitem_text = "..."; + if (is_selected(itemnum)) + selected_subitem_too_big = true; + } + + // customize subitem text color + if (!core_stricmp(pitem.subtext().c_str(), _("On"))) + fgcolor2 = rgb_t(0x00,0xff,0x00); + + if (!core_stricmp(pitem.subtext().c_str(), _("Off"))) + fgcolor2 = rgb_t(0xff,0x00,0x00); + + if (!core_stricmp(pitem.subtext().c_str(), _("Auto"))) + fgcolor2 = rgb_t(0xff,0xff,0x00); + + // draw the subitem right-justified + ui().draw_text_full( + container(), + subitem_text, + effective_left + item_width, line_y0, effective_width - item_width, + text_layout::text_justify::RIGHT, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NORMAL, subitem_invert ? fgcolor3 : fgcolor2, bgcolor, + &subitem_width, nullptr); + } + + // apply arrows + if (is_selected(itemnum) && (pitem.flags() & FLAG_LEFT_ARROW)) + { + float const l = effective_left + effective_width - subitem_width - gutter_width; + float const r = l + lr_arrow_width; + draw_arrow( + l, line_y0 + 0.1f * line_height, r, line_y0 + 0.9f * line_height, + fgcolor, + ROT90 ^ ORIENTATION_FLIP_X); + if (mouse_in_rect(l, line_y0 + 0.1f * line_height, r, line_y0 + 0.9f * line_height)) + m_hover = HOVER_UI_LEFT; + } + if (is_selected(itemnum) && (pitem.flags() & FLAG_RIGHT_ARROW)) + { + float const r = effective_left + effective_width + gutter_width; + float const l = r - lr_arrow_width; + draw_arrow( + l, line_y0 + 0.1f * line_height, r, line_y0 + 0.9f * line_height, + fgcolor, + ROT90); + if (mouse_in_rect(l, line_y0 + 0.1f * line_height, r, line_y0 + 0.9f * line_height)) + m_hover = HOVER_UI_RIGHT; + } + } + } + } + + // if the selected subitem is too big, display it in a separate offset box + if (selected_subitem_too_big) + { + menu_item const &pitem = selected_item(); + bool const subitem_invert(pitem.flags() & FLAG_INVERT); + auto const linenum = m_selected - top_line; + float const line_y = visible_top + (float)linenum * line_height; + float target_width, target_height; + + // compute the multi-line target width/height + ui().draw_text_full( + container(), + pitem.subtext(), + 0, 0, visible_width * 0.75f, + text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), + &target_width, &target_height); + + // determine the target location + float const target_x = visible_left + visible_width - target_width - lr_border; + float target_y = line_y + line_height + ui().box_tb_border(); + if (target_y + target_height + ui().box_tb_border() > visible_main_menu_height) + target_y = line_y - target_height - ui().box_tb_border(); + + // add a box around that + ui().draw_outlined_box( + container(), + target_x - lr_border, target_y - ui().box_tb_border(), + target_x + target_width + lr_border, target_y + target_height + ui().box_tb_border(), + subitem_invert ? ui().colors().selected_bg_color() : ui().colors().background_color()); + + ui().draw_text_full( + container(), + pitem.subtext(), + target_x, target_y, target_width, + text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD, + mame_ui_manager::NORMAL, ui().colors().selected_color(), ui().colors().selected_bg_color(), + nullptr, nullptr); + } + + // if there is something special to add, do it by calling the virtual method + custom_render(get_selection_ref(), m_customtop, m_custombottom, x1, y1, x2, y2); +} + +void menu::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) +{ +} + + +//------------------------------------------------- +// map_mouse - map mouse pointer location to menu +// coordinates +//------------------------------------------------- + +void menu::map_mouse() +{ + ignore_mouse(); + int32_t mouse_target_x, mouse_target_y; + render_target *const mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &m_mouse_button); + if (mouse_target) + { + if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, container(), m_mouse_x, m_mouse_y)) + m_mouse_hit = true; + } +} + + +//------------------------------------------------- +// ignore_mouse - set members to ignore mouse +// input +//------------------------------------------------- + +void menu::ignore_mouse() +{ + m_mouse_hit = false; + m_mouse_button = false; + m_mouse_x = -1.0f; + m_mouse_y = -1.0f; +} + + +//------------------------------------------------- +// handle_events - generically handle +// input events for a menu +//------------------------------------------------- + +void menu::handle_events(uint32_t flags, event &ev) +{ + bool stop = false; + ui_event local_menu_event; + + // loop while we have interesting events + while (!stop && machine().ui_input().pop_event(&local_menu_event)) + { + switch (local_menu_event.event_type) + { + // if we are hovering over a valid item, select it with a single click + case ui_event::type::MOUSE_DOWN: + if (custom_mouse_down()) + return; + + if (!(flags & PROCESS_ONLYCHAR)) + { + if (m_hover >= 0 && m_hover < m_items.size()) + { + m_selected = m_hover; + } + else if (m_hover == HOVER_ARROW_UP) + { + if (flags & PROCESS_CUSTOM_NAV) + { + ev.iptkey = IPT_UI_PAGE_UP; + stop = true; + } + else + { + m_selected -= m_visible_items; + if (m_selected < 0) + m_selected = 0; + top_line -= m_visible_items - (last_item_visible() ? 1 : 0); + } + } + else if (m_hover == HOVER_ARROW_DOWN) + { + if (flags & PROCESS_CUSTOM_NAV) + { + ev.iptkey = IPT_UI_PAGE_DOWN; + stop = true; + } + else + { + m_selected += m_visible_lines - 2 + is_first_selected(); + if (m_selected > m_items.size() - 1) + m_selected = m_items.size() - 1; + top_line += m_visible_lines - 2; + } + } + else if (m_hover == HOVER_UI_LEFT) + { + ev.iptkey = IPT_UI_LEFT; + stop = true; + } + else if (m_hover == HOVER_UI_RIGHT) + { + ev.iptkey = IPT_UI_RIGHT; + stop = true; + } + } + break; + + // if we are hovering over a valid item, fake a UI_SELECT with a double-click + case ui_event::type::MOUSE_DOUBLE_CLICK: + if (!(flags & PROCESS_ONLYCHAR) && m_hover >= 0 && m_hover < m_items.size()) + { + m_selected = m_hover; + ev.iptkey = IPT_UI_SELECT; + if (is_last_selected() && m_needs_prev_menu_item) + { + ev.iptkey = IPT_UI_CANCEL; + stack_pop(); + if (is_special_main_menu()) + machine().schedule_exit(); + } + stop = true; + } + break; + + // caught scroll event + case ui_event::type::MOUSE_WHEEL: + if (!custom_mouse_scroll((0 < local_menu_event.zdelta) ? -local_menu_event.num_lines : local_menu_event.num_lines) && !(flags & (PROCESS_ONLYCHAR | PROCESS_CUSTOM_NAV))) + { + if (local_menu_event.zdelta > 0) + { + if (is_first_selected()) + { + select_last_item(); + } + else + { + m_selected -= local_menu_event.num_lines; + validate_selection(-1); + } + top_line -= (m_selected <= top_line && top_line != 0); + if (m_selected <= top_line && m_visible_items != m_visible_lines) + top_line -= local_menu_event.num_lines; + } + else + { + if (is_last_selected()) + { + select_first_item(); + } + else + { + m_selected += local_menu_event.num_lines; + validate_selection(1); + } + top_line += (m_selected >= top_line + m_visible_items + (top_line != 0)); + if (m_selected >= (top_line + m_visible_items + (top_line != 0))) + top_line += local_menu_event.num_lines; + } + } + break; + + // translate CHAR events into specials + case ui_event::type::IME_CHAR: + ev.iptkey = IPT_SPECIAL; + ev.unichar = local_menu_event.ch; + stop = true; + break; + + // ignore everything else + default: + break; + } + } +} + + +//------------------------------------------------- +// handle_keys - generically handle +// keys for a menu +//------------------------------------------------- + +void menu::handle_keys(uint32_t flags, int &iptkey) +{ + bool const ignorepause = (flags & PROCESS_IGNOREPAUSE) || stack_has_special_main_menu(); + + // bail if no items + if (m_items.empty()) + return; + + // if we hit select, return true or pop the stack, depending on the item + if (exclusive_input_pressed(iptkey, IPT_UI_SELECT, 0)) + { + if (is_last_selected() && m_needs_prev_menu_item) + { + iptkey = IPT_UI_CANCEL; + stack_pop(); + if (is_special_main_menu()) + machine().schedule_exit(); + } + return; + } + + // UI configure hides the menus + if (!(flags & PROCESS_NOKEYS) && exclusive_input_pressed(iptkey, IPT_UI_CONFIGURE, 0) && !m_global_state.stack_has_special_main_menu()) + { + if (is_one_shot()) + stack_pop(); + else + m_global_state.hide_menu(); + return; + } + + // bail out + if (flags & PROCESS_ONLYCHAR) + return; + + // hitting cancel also pops the stack + if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0)) + { + if (!custom_ui_cancel()) + { + stack_pop(); + if (is_special_main_menu()) + machine().schedule_exit(); + } + return; + } + + // validate the current selection + validate_selection(1); + + // swallow left/right keys if they are not appropriate + bool const ignoreleft = !(flags & PROCESS_LR_ALWAYS) && !(selected_item().flags() & FLAG_LEFT_ARROW); + bool const ignoreright = !(flags & PROCESS_LR_ALWAYS) && !(selected_item().flags() & FLAG_RIGHT_ARROW); + + // accept left/right/prev/next keys as-is with repeat if appropriate + if (!ignoreleft && exclusive_input_pressed(iptkey, IPT_UI_LEFT, (flags & PROCESS_LR_REPEAT) ? 6 : 0)) + return; + if (!ignoreright && exclusive_input_pressed(iptkey, IPT_UI_RIGHT, (flags & PROCESS_LR_REPEAT) ? 6 : 0)) + return; + + // up backs up by one item + if (exclusive_input_pressed(iptkey, IPT_UI_UP, 6)) + { + if (flags & PROCESS_CUSTOM_NAV) + { + return; + } + else if (is_first_selected()) + { + select_last_item(); + } + else + { + --m_selected; + validate_selection(-1); + } + top_line -= (m_selected <= top_line && top_line != 0); + if (m_selected <= top_line && m_visible_items != m_visible_lines) + top_line--; + } + + // down advances by one item + if (exclusive_input_pressed(iptkey, IPT_UI_DOWN, 6)) + { + if (flags & PROCESS_CUSTOM_NAV) + { + return; + } + else if (is_last_selected()) + { + select_first_item(); + } + else + { + ++m_selected; + validate_selection(1); + } + top_line += (m_selected >= top_line + m_visible_items + (top_line != 0)); + if (m_selected >= (top_line + m_visible_items + (top_line != 0))) + top_line++; + } + + // page up backs up by m_visible_items + if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_UP, 6)) + { + if (flags & PROCESS_CUSTOM_NAV) + return; + m_selected -= m_visible_items; + top_line -= m_visible_items - (last_item_visible() ? 1 : 0); + if (m_selected < 0) + m_selected = 0; + validate_selection(1); + } + + // page down advances by m_visible_items + if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_DOWN, 6)) + { + if (flags & PROCESS_CUSTOM_NAV) + return; + m_selected += m_visible_lines - 2 + is_first_selected(); + top_line += m_visible_lines - 2; + + if (m_selected > m_items.size() - 1) + m_selected = m_items.size() - 1; + validate_selection(-1); + } + + // home goes to the start + if (exclusive_input_pressed(iptkey, IPT_UI_HOME, 0)) + { + if (flags & PROCESS_CUSTOM_NAV) + return; + select_first_item(); + } + + // end goes to the last + if (exclusive_input_pressed(iptkey, IPT_UI_END, 0)) + { + if (flags & PROCESS_CUSTOM_NAV) + return; + select_last_item(); + } + + // pause enables/disables pause + if (!ignorepause && exclusive_input_pressed(iptkey, IPT_UI_PAUSE, 0)) + { + if (machine().paused()) + machine().resume(); + else + machine().pause(); + } + + // handle a toggle cheats request + if (machine().ui_input().pressed_repeat(IPT_UI_TOGGLE_CHEAT, 0)) + mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled()); + + // see if any other UI keys are pressed + if (iptkey == IPT_INVALID) + { + for (int code = IPT_UI_FIRST + 1; code < IPT_UI_LAST; code++) + { + switch (code) + { + case IPT_UI_LEFT: + if (ignoreleft) + continue; + break; + case IPT_UI_RIGHT: + if (ignoreright) + continue; + break; + case IPT_UI_PAUSE: + if (ignorepause) + continue; + break; + } + if (exclusive_input_pressed(iptkey, code, 0)) + break; + } + } +} + + +//------------------------------------------------- +// select_first_item - select the first item in +// the menu +//------------------------------------------------- + +void menu::select_first_item() +{ + m_selected = top_line = 0; + validate_selection(1); +} + + +//------------------------------------------------- +// select_last_item - select the last item in the +// menu +//------------------------------------------------- + +void menu::select_last_item() +{ + m_selected = top_line = m_items.size() - 1; + validate_selection(-1); +} + + +//------------------------------------------------- +// validate_selection - validate the +// current selection and ensure it is on a +// correct item +//------------------------------------------------- + +void menu::validate_selection(int scandir) +{ + // clamp to be in range + if (m_selected < 0) + m_selected = 0; + else if (m_selected >= m_items.size()) + m_selected = m_items.size() - 1; + + // skip past unselectable items + while (!is_selectable(m_items[m_selected])) + m_selected = (m_selected + m_items.size() + scandir) % m_items.size(); +} + + + +/*************************************************************************** + MENU STACK MANAGEMENT +***************************************************************************/ + +void menu::do_handle() +{ + if (m_items.empty()) + { + // add an item to return - this is a really hacky way of doing this + if (m_needs_prev_menu_item) + item_append(_("Return to Previous Menu"), 0, nullptr); + + // let implementation add other items + populate(m_customtop, m_custombottom); + } + handle(process()); +} + + +/*************************************************************************** + UI SYSTEM INTERACTION +***************************************************************************/ + +//------------------------------------------------- +// ui_menu_ui_handler - displays the current menu +// and calls the menu handler +//------------------------------------------------- + +delegate menu::get_ui_handler(mame_ui_manager &mui) +{ + global_state &state(get_global_state(mui)); + return delegate(&global_state::ui_handler, &state); +} + +/*************************************************************************** + MENU HELPERS +***************************************************************************/ + +//------------------------------------------------- +// highlight +//------------------------------------------------- + +void menu::highlight(float x0, float y0, float x1, float y1, rgb_t bgcolor) +{ + container().add_quad(x0, y0, x1, y1, bgcolor, m_global_state.hilight_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1) | PRIMFLAG_PACKABLE); +} + + +//------------------------------------------------- +// draw_arrow +//------------------------------------------------- + +void menu::draw_arrow(float x0, float y0, float x1, float y1, rgb_t fgcolor, uint32_t orientation) +{ + container().add_quad(x0, y0, x1, y1, fgcolor, m_global_state.arrow_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(orientation) | PRIMFLAG_PACKABLE); +} + + +//------------------------------------------------- +// extra_text_draw_box - generically adds header +// or footer text +//------------------------------------------------- + +void menu::extra_text_draw_box(float origx1, float origx2, float origy, float yspan, std::string_view text, int direction) +{ + // get the size of the text + auto layout = ui().create_layout(container()); + layout.add_text(text); + + // position this extra text + float x1, y1, x2, y2; + extra_text_position(origx1, origx2, origy, yspan, layout, direction, x1, y1, x2, y2); + + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color()); + + // take off the borders + x1 += ui().box_lr_border() * machine().render().ui_aspect(&container()); + y1 += ui().box_tb_border(); + + // draw the text within it + layout.emit(container(), x1, y1); +} + + +void menu::draw_background() +{ + // draw background image if available + if (ui().options().use_background_image() && m_global_state.bgrnd_bitmap() && m_global_state.bgrnd_bitmap()->valid()) + container().add_quad(0.0f, 0.0f, 1.0f, 1.0f, rgb_t::white(), m_global_state.bgrnd_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); +} + + +//------------------------------------------------- +// extra_text_position - given extra text that has +// been put into a layout, position it +//------------------------------------------------- + +void menu::extra_text_position(float origx1, float origx2, float origy, float yspan, text_layout &layout, + int direction, float &x1, float &y1, float &x2, float &y2) +{ + float width = layout.actual_width() + (2 * ui().box_lr_border() * machine().render().ui_aspect(&container())); + float maxwidth = std::max(width, origx2 - origx1); + + // compute our bounds + x1 = 0.5f - 0.5f * maxwidth; + x2 = x1 + maxwidth; + y1 = origy + (yspan * direction); + y2 = origy + (ui().box_tb_border() * direction); + + if (y1 > y2) + std::swap(y1, y2); +} + + +//------------------------------------------------- +// extra_text_render - generically adds header +// and footer text +//------------------------------------------------- + +void menu::extra_text_render(float top, float bottom, float origx1, float origy1, float origx2, float origy2, std::string_view header, std::string_view footer) +{ + if (!header.empty()) + extra_text_draw_box(origx1, origx2, origy1, top, header, -1); + if (!footer.empty()) + extra_text_draw_box(origx1, origx2, origy2, bottom, footer, +1); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/menu.h b/src/icludes/frontend/mame/ui/menu.h new file mode 100644 index 0000000..6b137a9 --- /dev/null +++ b/src/icludes/frontend/mame/ui/menu.h @@ -0,0 +1,429 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/menu.h + + Internal MAME menus for the user interface. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_MENU_H +#define MAME_FRONTEND_UI_MENU_H + +#pragma once + +#include "ui/ui.h" +#include "ui/menuitem.h" +#include "ui/widgets.h" + +#include "language.h" +#include "render.h" + +#include +#include +#include +#include +#include +#include + + +namespace ui { + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +class menu +{ +public: + // flags for menu items + enum : uint32_t + { + FLAG_LEFT_ARROW = 1U << 0, + FLAG_RIGHT_ARROW = 1U << 1, + FLAG_INVERT = 1U << 2, + FLAG_DISABLE = 1U << 4, + FLAG_UI_HEADING = 1U << 5, + FLAG_COLOR_BOX = 1U << 6 + }; + + virtual ~menu(); + + // append a new item to the end of the menu + void item_append(const std::string &text, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { item_append(std::string(text), std::string(), flags, ref, type); } + void item_append(const std::string &text, const std::string &subtext, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { item_append(std::string(text), std::string(subtext), flags, ref, type); } + void item_append(std::string &&text, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { item_append(text, std::string(), flags, ref, type); } + void item_append(std::string &&text, std::string &&subtext, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN); + void item_append(menu_item item) { item_append(item.text(), item.subtext(), item.flags(), item.ref(), item.type()); } + void item_append(menu_item_type type, uint32_t flags = 0); + void item_append_on_off(const std::string &text, bool state, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN); + + // reset the menus, clearing everything + static void stack_reset(mame_ui_manager &ui) { get_global_state(ui).stack_reset(); } + + // push a new menu onto the stack + template + static void stack_push(Params &&... args) + { + stack_push(std::make_unique(std::forward(args)...)); + } + template + static void stack_push_special_main(Params &&... args) + { + std::unique_ptr ptr(std::make_unique(std::forward(args)...)); + ptr->set_special_main_menu(true); + stack_push(std::move(ptr)); + } + + // pop a menu from the stack + static void stack_pop(mame_ui_manager &ui) { get_global_state(ui).stack_pop(); } + + // test if one of the menus in the stack requires hide disable + static bool stack_has_special_main_menu(mame_ui_manager &ui) { return get_global_state(ui).stack_has_special_main_menu(); } + + // master handler + static delegate get_ui_handler(mame_ui_manager &mui); + + // Used by sliders + void validate_selection(int scandir); + + void do_handle(); + +protected: + using bitmap_ptr = widgets_manager::bitmap_ptr; + using texture_ptr = widgets_manager::texture_ptr; + + // flags to pass to set_process_flags + enum + { + PROCESS_NOKEYS = 1 << 0, + PROCESS_LR_ALWAYS = 1 << 1, + PROCESS_LR_REPEAT = 1 << 2, + PROCESS_CUSTOM_NAV = 1 << 3, + PROCESS_CUSTOM_ONLY = 1 << 4, + PROCESS_ONLYCHAR = 1 << 5, + PROCESS_NOINPUT = 1 << 6, + PROCESS_IGNOREPAUSE = 1 << 7 + }; + + // options for reset + enum class reset_options + { + SELECT_FIRST, + REMEMBER_POSITION, + REMEMBER_REF + }; + + // menu-related events + struct event + { + void *itemref; // reference for the selected item or nullptr + menu_item *item; // selected item or nullptr + int iptkey; // one of the IPT_* values from inptport.h + char32_t unichar; // unicode character if iptkey == IPT_SPECIAL + render_bounds mouse; // mouse position if iptkey == IPT_CUSTOM + }; + + menu(mame_ui_manager &mui, render_container &container); + + mame_ui_manager &ui() const { return m_ui; } + running_machine &machine() const { return m_ui.machine(); } + render_container &container() const { return m_container; } + + bool is_special_main_menu() const { return m_special_main_menu; } + bool is_one_shot() const { return m_one_shot; } + bool is_active() const { return m_active; } + + void set_one_shot(bool oneshot) { m_one_shot = oneshot; } + void set_needs_prev_menu_item(bool needs) { m_needs_prev_menu_item = needs; } + void reset(reset_options options); + void reset_parent(reset_options options) { m_parent->reset(options); } + + template T *topmost_menu() const { return m_global_state.topmost_menu(); } + template static T *topmost_menu(mame_ui_manager &ui) { return get_global_state(ui).topmost_menu(); } + void stack_pop() { m_global_state.stack_pop(); } + void stack_reset() { m_global_state.stack_reset(); } + bool stack_has_special_main_menu() const { return m_global_state.stack_has_special_main_menu(); } + + menu_item &item(int index) { return m_items[index]; } + menu_item const &item(int index) const { return m_items[index]; } + int item_count() const { return m_items.size(); } + + // retrieves the ref of the currently selected menu item or nullptr + void *get_selection_ref() const { return selection_valid() ? m_items[m_selected].ref() : nullptr; } + + menu_item &selected_item() { return m_items[m_selected]; } + menu_item const &selected_item() const { return m_items[m_selected]; } + int selected_index() const { return m_selected; } + bool selection_valid() const { return (0 <= m_selected) && (m_items.size() > m_selected); } + bool is_selected(int index) const { return selection_valid() && (m_selected == index); } + bool is_first_selected() const { return 0 == m_selected; } + bool is_last_selected() const { return (m_items.size() - 1) == m_selected; } + + // changes the index of the currently selected menu item + void set_selection(void *selected_itemref); + void set_selected_index(int index) { m_selected = index; } + void select_first_item(); + void select_last_item(); + + int hover() const { return m_hover; } + void set_hover(int index) { m_hover = index; } + void clear_hover() { m_hover = m_items.size() + 1; } + + // scroll position control + void set_top_line(int index) { top_line = (0 < index) ? (index - 1) : index; } + void centre_selection() { top_line = m_selected - (m_visible_lines / 2); } + + // test if the given key is pressed and we haven't already reported a key + bool exclusive_input_pressed(int &iptkey, int key, int repeat); + + // layout + float get_customtop() const { return m_customtop; } + float get_custombottom() const { return m_custombottom; } + + // highlight + void highlight(float x0, float y0, float x1, float y1, rgb_t bgcolor); + render_texture *hilight_main_texture() { return m_global_state.hilight_main_texture(); } + + // draw arrow + void draw_arrow(float x0, float y0, float x1, float y1, rgb_t fgcolor, uint32_t orientation); + + // draw header and footer text + void extra_text_render(float top, float bottom, float origx1, float origy1, float origx2, float origy2, std::string_view header, std::string_view footer); + void extra_text_position(float origx1, float origx2, float origy, float yspan, text_layout &layout, + int direction, float &x1, float &y1, float &x2, float &y2); + + // draw a box of text - used for the custom boxes above/below menus + template + float draw_text_box( + Iter begin, Iter end, + float origx1, float origx2, float y1, float y2, + ui::text_layout::text_justify justify, ui::text_layout::word_wrapping wrap, bool scale, + rgb_t fgcolor, rgb_t bgcolor, float text_size) + { + // size up the text + float const lrborder(ui().box_lr_border() * machine().render().ui_aspect(&container())); + float const origwidth(origx2 - origx1 - (2.0f * lrborder)); + float maxwidth(origwidth); + for (Iter it = begin; it != end; ++it) + { + std::string_view const &line(*it); + if (!line.empty()) + { + auto layout = ui().create_layout(container(), 1.0f, justify, wrap); + layout.add_text(std::string_view(*it), rgb_t::white(), rgb_t::black(), text_size); + maxwidth = (std::max)(layout.actual_width(), maxwidth); + } + } + if (scale && (origwidth < maxwidth)) + { + text_size *= origwidth / maxwidth; + maxwidth = origwidth; + } + + // draw containing box + float const boxleft(0.5f - (maxwidth * 0.5f) - lrborder); + float boxright(0.5f + (maxwidth * 0.5f) + lrborder); + ui().draw_outlined_box(container(), boxleft, y1, boxright, y2, bgcolor); + + // inset box and draw content + float const textleft(0.5f - (maxwidth * 0.5f)); + y1 += ui().box_tb_border(); + for (Iter it = begin; it != end; ++it) + { + ui().draw_text_full( + container(), std::string_view(*it), + textleft, y1, maxwidth, justify, wrap, + mame_ui_manager::NORMAL, fgcolor, ui().colors().text_bg_color(), + nullptr, nullptr, text_size); + y1 += ui().get_line_height(); + } + + // in case you want another box of similar width + return maxwidth; + } + + void draw_background(); + + // draw additional menu content + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2); + + // map mouse to menu coordinates + void map_mouse(); + + // clear the mouse position + void ignore_mouse(); + + bool is_mouse_hit() const { return m_mouse_hit; } // is mouse pointer inside menu's render container? + float get_mouse_x() const { return m_mouse_x; } // mouse x location in menu coordinates + float get_mouse_y() const { return m_mouse_y; } // mouse y location in menu coordinates + + // mouse hit test - checks whether mouse_x is in [x0, x1) and mouse_y is in [y0, y1) + bool mouse_in_rect(float x0, float y0, float x1, float y1) const + { + return m_mouse_hit && (m_mouse_x >= x0) && (m_mouse_x < x1) && (m_mouse_y >= y0) && (m_mouse_y < y1); + } + + // overridable event handling + void set_process_flags(uint32_t flags) { m_process_flags = flags; } + virtual void handle_events(uint32_t flags, event &ev); + virtual void handle_keys(uint32_t flags, int &iptkey); + virtual bool custom_ui_cancel() { return false; } + virtual bool custom_mouse_down() { return false; } + virtual bool custom_mouse_scroll(int lines) { return false; } + + // event notifications + virtual void menu_activated() { } + virtual void menu_deactivated() { } + virtual void menu_dismissed() { } + + static bool is_selectable(menu_item const &item) + { + return (!(item.flags() & menu::FLAG_DISABLE) && (item.type() != menu_item_type::SEPARATOR)); + } + + // get arrows status + template + static uint32_t get_arrow_flags(T min, T max, T actual) + { + return ((actual > min) ? FLAG_LEFT_ARROW : 0) | ((actual < max) ? FLAG_RIGHT_ARROW : 0); + } + +private: + class global_state : public widgets_manager + { + public: + global_state(mame_ui_manager &ui); + global_state(global_state const &) = delete; + global_state(global_state &&) = delete; + ~global_state(); + + bitmap_argb32 *bgrnd_bitmap() { return m_bgrnd_bitmap.get(); } + render_texture *bgrnd_texture() { return m_bgrnd_texture.get(); } + + template + T *topmost_menu() const { return dynamic_cast(m_stack.get()); } + + void stack_push(std::unique_ptr &&menu); + void stack_pop(); + void stack_reset(); + void clear_free_list(); + bool stack_has_special_main_menu() const; + + void hide_menu() { m_hide = true; } + + uint32_t ui_handler(render_container &container); + + protected: + mame_ui_manager &m_ui; + + private: + bitmap_ptr m_bgrnd_bitmap; + texture_ptr m_bgrnd_texture; + + std::unique_ptr m_stack; + std::unique_ptr m_free; + + bool m_hide; + }; + + // this is to satisfy the std::any requirement that objects be copyable + class global_state_wrapper : public global_state + { + public: + global_state_wrapper(mame_ui_manager &ui) : global_state(ui) { } + global_state_wrapper(global_state_wrapper const &that) : global_state(that.m_ui) { } + }; + + // process a menu, drawing it and returning any interesting events + const event *process(); + virtual void draw(uint32_t flags); + + // request the specific handling of the game selection main menu + void set_special_main_menu(bool disable); + + // to be implemented in derived classes + virtual void populate(float &customtop, float &custombottom) = 0; + + // to be implemented in derived classes + virtual void handle(event const *ev) = 0; + + // push a new menu onto the stack + static void stack_push(std::unique_ptr &&menu) { menu->m_global_state.stack_push(std::move(menu)); } + + void extra_text_draw_box(float origx1, float origx2, float origy, float yspan, std::string_view text, int direction); + + bool first_item_visible() const { return top_line <= 0; } + bool last_item_visible() const { return (top_line + m_visible_lines) >= m_items.size(); } + + static global_state &get_global_state(mame_ui_manager &ui); + +protected: // TODO: remove need to expose these - only used here and in selmenu.cpp + int top_line; // main box top line + int m_visible_lines; // main box visible lines + int m_visible_items; // number of visible items + +private: + global_state &m_global_state; // reference to global state for session + mame_ui_manager &m_ui; // UI we are attached to + render_container &m_container; // render_container we render to + std::unique_ptr m_parent; // pointer to parent menu in the stack + + std::vector m_items; // array of items + + uint32_t m_process_flags; // event processing options + int m_selected; // which item is selected + int m_hover; // which item is being hovered over + bool m_special_main_menu; // true if no real emulation running under the menu + bool m_one_shot; // true for menus outside the normal stack + bool m_needs_prev_menu_item; // true to automatically create item to dismiss menu + bool m_active; // whether the menu is currently visible and topmost + + event m_event; // the UI event that occurred + + float m_customtop; // amount of extra height to add at the top + float m_custombottom; // amount of extra height to add at the bottom + + int m_resetpos; // item index to select after repopulating + void *m_resetref; // item reference value to select after repopulating + + bool m_mouse_hit; + bool m_mouse_button; + float m_mouse_x; + float m_mouse_y; +}; + + +template +class autopause_menu : public Base +{ +protected: + using Base::Base; + + virtual void menu_activated() override + { + m_was_paused = this->machine().paused(); + if (m_was_paused) + m_unpaused = false; + else if (!m_unpaused) + this->machine().pause(); + Base::menu_activated(); + } + + virtual void menu_deactivated() override + { + m_unpaused = !this->machine().paused(); + if (!m_was_paused && !m_unpaused) + this->machine().resume(); + Base::menu_deactivated(); + } + +private: + bool m_was_paused = false; + bool m_unpaused = false; +}; + + +} // namespace ui + +#endif // MAME_FRONTEND_UI_MENU_H diff --git a/src/icludes/frontend/mame/ui/menuitem.h b/src/icludes/frontend/mame/ui/menuitem.h new file mode 100644 index 0000000..d92e415 --- /dev/null +++ b/src/icludes/frontend/mame/ui/menuitem.h @@ -0,0 +1,69 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods + +/*************************************************************************** + + ui/menuitem.h + + Internal data representation for a UI menu item. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_MENUITEM_H +#define MAME_FRONTEND_UI_MENUITEM_H + +#pragma once + + +#include +#include +#include + + +namespace ui { + +// special menu item for separators +#define MENU_SEPARATOR_ITEM "---" + +// types of menu items (TODO: please expand) +enum class menu_item_type +{ + UNKNOWN, + SLIDER, + SEPARATOR +}; + +class menu_item +{ +public: + menu_item(menu_item const &) = default; + menu_item(menu_item &&) = default; + menu_item &operator=(menu_item const &) = default; + menu_item &operator=(menu_item &&) = default; + + menu_item(menu_item_type t = menu_item_type::UNKNOWN, void *r = nullptr, uint32_t f = 0) : m_ref(r), m_flags(f), m_type(t) + { } + + std::string const &text() const noexcept { return m_text; } + std::string const &subtext() const noexcept { return m_subtext; } + void *ref() const noexcept { return m_ref; } + uint32_t flags() const noexcept { return m_flags; } + unsigned generation() const noexcept { return m_generation; } + menu_item_type type() const noexcept { return m_type; } + + template void set_text(T &&... args) { m_text.assign(std::forward(args)...); ++m_generation; } + template void set_subtext(T &&... args) { m_subtext.assign(std::forward(args)...); ++m_generation; } + void set_flags(uint32_t f) noexcept { m_flags = f; ++m_generation; } + +private: + std::string m_text; + std::string m_subtext; + void *m_ref; + uint32_t m_flags; + unsigned m_generation = 0; + menu_item_type m_type; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_MENUITEM_H diff --git a/src/icludes/frontend/mame/ui/miscmenu.cpp b/src/icludes/frontend/mame/ui/miscmenu.cpp new file mode 100644 index 0000000..0d05256 --- /dev/null +++ b/src/icludes/frontend/mame/ui/miscmenu.cpp @@ -0,0 +1,953 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Maurizio Petrarota +/********************************************************************* + + ui/miscmenu.cpp + + Internal MAME menus for the user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/miscmenu.h" + +#include "ui/inifile.h" +#include "ui/selector.h" +#include "ui/submenu.h" +#include "ui/ui.h" +#include "ui/utils.h" + +#include "infoxml.h" +#include "mame.h" + +#include "osdnet.h" +#include "mameopts.h" +#include "pluginopts.h" +#include "drivenum.h" +#include "romload.h" +#include "uiinput.h" + +#include "corestr.h" + +#include +#include +#include +#include + + +namespace ui { + +/*************************************************************************** + MENU HANDLERS +***************************************************************************/ + +/*------------------------------------------------- + menu_bios_selection - populates the main + bios selection menu +-------------------------------------------------*/ + +menu_bios_selection::menu_bios_selection(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ +} + +void menu_bios_selection::populate(float &customtop, float &custombottom) +{ + // cycle through all devices for this system + for (device_t &device : device_enumerator(machine().root_device())) + { + device_t const *const parent(device.owner()); + device_slot_interface const *const slot(dynamic_cast(parent)); + if (!parent || (slot && (slot->get_card_device() == &device))) + { + tiny_rom_entry const *rom(device.rom_region()); + if (rom && !ROMENTRY_ISEND(rom)) + { + char const *val = nullptr; + for ( ; !ROMENTRY_ISEND(rom) && !val; rom++) + { + if (ROMENTRY_ISSYSTEM_BIOS(rom) && ROM_GETBIOSFLAGS(rom) == device.system_bios()) + val = rom->hashdata; + } + if (val) + item_append(!parent ? "driver" : (device.tag() + 1), val, FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)&device); + } + } + } + + item_append(menu_item_type::SEPARATOR); + item_append(_("Reset"), 0, (void *)1); +} + +menu_bios_selection::~menu_bios_selection() +{ +} + +/*------------------------------------------------- + menu_bios_selection - menu that +-------------------------------------------------*/ + +void menu_bios_selection::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + if ((uintptr_t)ev->itemref == 1 && ev->iptkey == IPT_UI_SELECT) + machine().schedule_hard_reset(); + else + { + device_t *dev = (device_t *)ev->itemref; + int bios_val = 0; + + switch (ev->iptkey) + { + // reset to default + case IPT_UI_SELECT: + bios_val = dev->default_bios(); + break; + + // previous/next bios setting + case IPT_UI_LEFT: case IPT_UI_RIGHT: + { + int const cnt = ([bioses = romload::entries(dev->rom_region()).get_system_bioses()] () { return std::distance(bioses.begin(), bioses.end()); })(); + bios_val = dev->system_bios() + ((ev->iptkey == IPT_UI_LEFT) ? -1 : +1); + + // wrap + if (bios_val < 1) + bios_val = cnt; + if (bios_val > cnt) + bios_val = 1; + + break; + } + + default: + break; + } + + if (bios_val > 0) + { + dev->set_system_bios(bios_val); + if (strcmp(dev->tag(),":")==0) { + machine().options().set_value("bios", bios_val-1, OPTION_PRIORITY_CMDLINE); + } else { + const char *slot_option_name = dev->owner()->tag() + 1; + machine().options().slot_option(slot_option_name).set_bios(string_format("%d", bios_val - 1)); + } + reset(reset_options::REMEMBER_REF); + } + } + } +} + + + +menu_network_devices::menu_network_devices(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ +} + +menu_network_devices::~menu_network_devices() +{ +} + +/*------------------------------------------------- + menu_network_devices_populate - populates the main + network device menu +-------------------------------------------------*/ + +void menu_network_devices::populate(float &customtop, float &custombottom) +{ + /* cycle through all devices for this system */ + for (device_network_interface &network : network_interface_enumerator(machine().root_device())) + { + int curr = network.get_interface(); + const char *title = nullptr; + for(auto &entry : get_netdev_list()) + { + if(entry->id==curr) { + title = entry->description; + break; + } + } + + item_append(network.device().tag(), (title) ? title : "------", FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)&network); + } + + item_append(menu_item_type::SEPARATOR); +} + +/*------------------------------------------------- + menu_network_devices - menu that +-------------------------------------------------*/ + +void menu_network_devices::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT) + { + device_network_interface *network = (device_network_interface *)ev->itemref; + int curr = network->get_interface(); + if (ev->iptkey == IPT_UI_LEFT) + curr--; + else + curr++; + if (curr == -2) + curr = netdev_count() - 1; + network->set_interface(curr); + reset(reset_options::REMEMBER_REF); + } + } +} + + +/*------------------------------------------------- + menu_bookkeeping - handle the bookkeeping + information menu +-------------------------------------------------*/ + +menu_bookkeeping::menu_bookkeeping(mame_ui_manager &mui, render_container &container) : menu_textbox(mui, container) +{ + set_process_flags(PROCESS_CUSTOM_NAV); +} + +menu_bookkeeping::~menu_bookkeeping() +{ +} + +void menu_bookkeeping::menu_activated() +{ + // stuff can change while the menu is hidden + reset_layout(); +} + +void menu_bookkeeping::populate_text(std::optional &layout, float &width, int &lines) +{ + if (!layout || (layout->width() != width)) + { + rgb_t const color = ui().colors().text_color(); + layout.emplace(ui().create_layout(container(), width)); + + // show total time first + prevtime = machine().time(); + if (prevtime.seconds() >= (60 * 60)) + layout->add_text(util::string_format(_("Uptime: %1$d:%2$02d:%3$02d\n\n"), prevtime.seconds() / (60 * 60), (prevtime.seconds() / 60) % 60, prevtime.seconds() % 60), color); + else + layout->add_text(util::string_format(_("Uptime: %1$d:%2$02d\n\n"), (prevtime.seconds() / 60) % 60, prevtime.seconds() % 60), color); + + // show tickets at the top + int const tickets = machine().bookkeeping().get_dispensed_tickets(); + if (tickets > 0) + layout->add_text(util::string_format(_("Tickets dispensed: %1$d\n\n"), tickets), color); + + // loop over coin counters + for (int ctrnum = 0; ctrnum < bookkeeping_manager::COIN_COUNTERS; ctrnum++) + { + int const count = machine().bookkeeping().coin_counter_get_count(ctrnum); + bool const locked = machine().bookkeeping().coin_lockout_get_state(ctrnum); + + // display the coin counter number + // display how many coins + // display whether or not we are locked out + layout->add_text( + util::string_format( + (count == 0) ? _("Coin %1$c: NA%3$s\n") : _("Coin %1$c: %2$d%3$s\n"), + ctrnum + 'A', + count, + locked ? _(" (locked)") : ""), + color); + } + + lines = layout->lines(); + } + width = layout->actual_width(); +} + +void menu_bookkeeping::populate(float &customtop, float &custombottom) +{ +} + +void menu_bookkeeping::handle(event const *ev) +{ + // if the time has rolled over another second, regenerate + // TODO: what about other bookkeeping events happening with the menu open? + attotime const curtime = machine().time(); + if (curtime.seconds() != prevtime.seconds()) + reset_layout(); + + if (ev) + handle_key(ev->iptkey); +} + + +/*------------------------------------------------- + menu_crosshair - handle the crosshair settings + menu +-------------------------------------------------*/ + +void menu_crosshair::handle(event const *ev) +{ + // handle events + if (ev && ev->itemref) + { + crosshair_item_data &data(*reinterpret_cast(ev->itemref)); + bool changed(false); + int newval(data.cur); + + switch (ev->iptkey) + { + // if selected, reset to default value + case IPT_UI_SELECT: + newval = data.defvalue; + break; + + // left decrements + case IPT_UI_LEFT: + newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; + break; + + // right increments + case IPT_UI_RIGHT: + newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; + break; + } + + // clamp to range + if (newval < data.min) + newval = data.min; + if (newval > data.max) + newval = data.max; + + // if things changed, update + if (newval != data.cur) + { + switch (data.type) + { + // visibility state + case CROSSHAIR_ITEM_VIS: + data.crosshair->set_mode(newval); + // set visibility as specified by mode - auto mode starts with visibility off + data.crosshair->set_visible(newval == CROSSHAIR_VISIBILITY_ON); + changed = true; + break; + + // auto time + case CROSSHAIR_ITEM_AUTO_TIME: + machine().crosshair().set_auto_time(newval); + changed = true; + break; + } + } + + // crosshair graphic name + if (data.type == CROSSHAIR_ITEM_PIC) + { + switch (ev->iptkey) + { + case IPT_UI_SELECT: + { + std::vector sel; + sel.reserve(m_pics.size() + 1); + sel.push_back("DEFAULT"); + std::copy(m_pics.begin(), m_pics.end(), std::back_inserter(sel)); + menu::stack_push( + ui(), container(), std::move(sel), data.cur, + [this, &data] (int selection) + { + if (!selection) + data.crosshair->set_default_bitmap(); + else + data.crosshair->set_bitmap_name(m_pics[selection - 1].c_str()); + reset(reset_options::REMEMBER_REF); + }); + } + break; + + case IPT_UI_LEFT: + data.crosshair->set_bitmap_name(data.last_name.c_str()); + changed = true; + break; + + case IPT_UI_RIGHT: + data.crosshair->set_bitmap_name(data.next_name.c_str()); + changed = true; + break; + } + } + + if (changed) + reset(reset_options::REMEMBER_REF); // rebuild the menu + } +} + + +/*------------------------------------------------- + menu_crosshair_populate - populate the + crosshair settings menu +-------------------------------------------------*/ + +menu_crosshair::menu_crosshair(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ + set_process_flags(PROCESS_LR_REPEAT); +} + +void menu_crosshair::populate(float &customtop, float &custombottom) +{ + if (m_data.empty()) + { + // loop over player and add the manual items + for (int player = 0; player < MAX_PLAYERS; player++) + { + // get the user settings + render_crosshair &crosshair(machine().crosshair().get_crosshair(player)); + + // add menu items for usable crosshairs + if (crosshair.is_used()) + { + // CROSSHAIR_ITEM_VIS - allocate a data item and fill it + crosshair_item_data &visdata(m_data.emplace_back()); + visdata.crosshair = &crosshair; + visdata.type = CROSSHAIR_ITEM_VIS; + visdata.player = player; + visdata.min = CROSSHAIR_VISIBILITY_OFF; + visdata.max = CROSSHAIR_VISIBILITY_AUTO; + visdata.defvalue = CROSSHAIR_VISIBILITY_DEFAULT; + + // CROSSHAIR_ITEM_PIC - allocate a data item and fill it + crosshair_item_data &picdata(m_data.emplace_back()); + picdata.crosshair = &crosshair; + picdata.type = CROSSHAIR_ITEM_PIC; + picdata.player = player; + // other data item not used by this menu + } + } + + // CROSSHAIR_ITEM_AUTO_TIME - allocate a data item and fill it + crosshair_item_data &timedata(m_data.emplace_back()); + timedata.type = CROSSHAIR_ITEM_AUTO_TIME; + timedata.min = CROSSHAIR_VISIBILITY_AUTOTIME_MIN; + timedata.max = CROSSHAIR_VISIBILITY_AUTOTIME_MAX; + timedata.defvalue = CROSSHAIR_VISIBILITY_AUTOTIME_DEFAULT; + } + + if (m_pics.empty()) + { + // open a path to the crosshairs + file_enumerator path(machine().options().crosshair_path()); + for (osd::directory::entry const *dir = path.next(); dir; dir = path.next()) + { + // look for files ending in .png + size_t const length(std::strlen(dir->name)); + if ((length > 4) && core_filename_ends_with(dir->name, ".png")) + m_pics.emplace_back(dir->name, length - 4); + } + std::stable_sort( + m_pics.begin(), + m_pics.end(), + [] (std::string const &a, std::string const &b) { return 0 > core_stricmp(a.c_str(), b.c_str()); }); + } + + // Make sure to keep these matched to the CROSSHAIR_VISIBILITY_xxx types + static char const *const vis_text[] = { "Off", "On", "Auto" }; + + bool use_auto = false; + for (crosshair_item_data &data : m_data) + { + switch (data.type) + { + case CROSSHAIR_ITEM_VIS: + { + // track if we need the auto time menu + if (data.crosshair->mode() == CROSSHAIR_VISIBILITY_AUTO) + use_auto = true; + + data.cur = data.crosshair->mode(); + + // put on arrows + uint32_t flags(0U); + if (data.cur > data.min) + flags |= FLAG_LEFT_ARROW; + if (data.cur < data.max) + flags |= FLAG_RIGHT_ARROW; + + // add CROSSHAIR_ITEM_VIS menu */ + item_append(util::string_format(_("P%d Visibility"), data.player + 1), vis_text[data.crosshair->mode()], flags, &data); + } + break; + + case CROSSHAIR_ITEM_PIC: + // search for crosshair graphics + { + // reset search flags + bool const using_default(*data.crosshair->bitmap_name() == '\0'); + bool finished(false); + bool found(false); + data.cur = using_default ? 0U : 1U; + data.last_name.clear(); + data.next_name.clear(); + + // look for the current name, then remember the name before and find the next name + for (auto it = m_pics.begin(); it != m_pics.end() && !finished; ++it) + { + // if we are using the default, then we just need to find the first in the list + if (found || using_default) + { + // get the next name + data.next_name = *it; + finished = true; + } + else if (data.crosshair->bitmap_name() == *it) + { + // we found the current name so loop once more to find the next name + found = true; + } + else + { + // remember last name - we will do it here in case files get added to the directory + ++data.cur; + data.last_name = *it; + } + } + + // if name not found then next item is DEFAULT + if (!found && !using_default) + { + data.cur = 0U; + data.next_name.clear(); + finished = true; + } + + // set up the selection flags + uint32_t flags(0U); + if (finished) + flags |= FLAG_RIGHT_ARROW; + if (found) + flags |= FLAG_LEFT_ARROW; + + // add CROSSHAIR_ITEM_PIC menu + item_append(util::string_format(_("P%d Crosshair"), data.player + 1), using_default ? "DEFAULT" : data.crosshair->bitmap_name(), flags, &data); + } + break; + + case CROSSHAIR_ITEM_AUTO_TIME: + if (use_auto) + { + data.cur = machine().crosshair().auto_time(); + + // put on arrows in visible menu + uint32_t flags(0U); + if (data.cur > data.min) + flags |= FLAG_LEFT_ARROW; + if (data.cur < data.max) + flags |= FLAG_RIGHT_ARROW; + + // add CROSSHAIR_ITEM_AUTO_TIME menu + item_append(_("Visible Delay"), util::string_format("%d", data.cur), flags, &data); + } + else + { + // leave a blank filler line when not in auto time so size does not rescale + //item_append("", "", nullptr, nullptr); + } + break; + } + } + + item_append(menu_item_type::SEPARATOR); +} + +menu_crosshair::~menu_crosshair() +{ +} + +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_export::menu_export(mame_ui_manager &mui, render_container &container, std::vector &&drvlist) + : menu(mui, container), m_list(std::move(drvlist)) +{ +} + +menu_export::~menu_export() +{ +} + +//------------------------------------------------- +// handle the export menu +//------------------------------------------------- + +void menu_export::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + switch (uintptr_t(ev->itemref)) + { + case 1: + case 3: + if (ev->iptkey == IPT_UI_SELECT) + { + std::string filename("exported"); + emu_file infile(ui().options().ui_path(), OPEN_FLAG_READ); + if (!infile.open(filename + ".xml")) + for (int seq = 0; ; ++seq) + { + const std::string seqtext = string_format("%s_%04d", filename, seq); + if (infile.open(seqtext + ".xml")) + { + filename = seqtext; + break; + } + } + + // attempt to open the output file + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(filename + ".xml")) + { + const std::string fullpath(file.fullpath()); + file.close(); + std::ofstream pfile(fullpath); + + // prepare a filter for the drivers we want to show + std::unordered_set driver_list(m_list.begin(), m_list.end()); + auto filter = [&driver_list](const char *shortname, bool &) + { + auto iter = std::find_if( + driver_list.begin(), + driver_list.end(), + [shortname](const game_driver *driver) { return !strcmp(shortname, driver->name); }); + return iter != driver_list.end(); + }; + + // do we want to show devices? + bool include_devices = uintptr_t(ev->itemref) == 1; + + // and do the dirty work + info_xml_creator creator(machine().options()); + creator.output(pfile, filter, include_devices); + machine().popmessage(_("%s.xml saved in UI settings folder."), filename); + } + } + break; + case 2: + if (ev->iptkey == IPT_UI_SELECT) + { + std::string filename("exported"); + emu_file infile(ui().options().ui_path(), OPEN_FLAG_READ); + if (!infile.open(filename + ".txt")) + for (int seq = 0; ; ++seq) + { + const std::string seqtext = string_format("%s_%04d", filename, seq); + if (infile.open(seqtext + ".txt")) + { + filename = seqtext; + break; + } + } + + // attempt to open the output file + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(filename + ".txt")) + { + // print the header + std::ostringstream buffer; + buffer << _("Name: Description:\n"); + driver_enumerator drvlist(machine().options()); + drvlist.exclude_all(); + for (auto & elem : m_list) + drvlist.include(driver_list::find(*elem)); + + // iterate through drivers and output the info + while (drvlist.next()) + util::stream_format(buffer, "%-18s\"%s\"\n", drvlist.driver().name, drvlist.driver().type.fullname()); + file.puts(buffer.str()); + file.close(); + machine().popmessage(_("%s.txt saved in UI settings folder."), filename); + } + } + break; + default: + break; + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_export::populate(float &customtop, float &custombottom) +{ + // add options items + item_append(_("Export list in XML format (like -listxml)"), 0, (void *)(uintptr_t)1); + item_append(_("Export list in XML format (like -listxml, but exclude devices)"), 0, (void *)(uintptr_t)3); + item_append(_("Export list in TXT format (like -listfull)"), 0, (void *)(uintptr_t)2); + item_append(menu_item_type::SEPARATOR); +} + +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_machine_configure::menu_machine_configure( + mame_ui_manager &mui, + render_container &container, + ui_system_info const &info, + std::function &&handler) + : menu(mui, container) + , m_handler(std::move(handler)) + , m_sys(info) + , m_curbios(0) + , m_was_favorite(mame_machine_manager::instance()->favorite().is_favorite_system(*info.driver)) + , m_want_favorite(m_was_favorite) +{ + // parse the INI file + std::ostringstream error; + osd_setup_osd_specific_emu_options(m_opts); + mame_options::parse_standard_inis(m_opts, error, m_sys.driver); + setup_bios(); +} + +menu_machine_configure::~menu_machine_configure() +{ + if (m_was_favorite != m_want_favorite) + { + if (m_want_favorite) + mame_machine_manager::instance()->favorite().add_favorite_system(*m_sys.driver); + else + mame_machine_manager::instance()->favorite().remove_favorite_system(*m_sys.driver); + } + + if (m_handler) + m_handler(m_want_favorite, m_was_favorite != m_want_favorite); +} + +//------------------------------------------------- +// handle the machine options menu +//------------------------------------------------- + +void menu_machine_configure::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + if (ev->iptkey == IPT_UI_SELECT) + { + switch ((uintptr_t)ev->itemref) + { + case SAVE: + { + const std::string filename(m_sys.driver->name); + emu_file file(machine().options().ini_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); + std::error_condition const filerr = file.open(filename + ".ini"); + if (!filerr) + { + std::string inistring = m_opts.output_ini(); + file.puts(inistring); + ui().popup_time(2, "%s", _("\n Configuration saved \n\n")); + } + } + break; + case ADDFAV: + m_want_favorite = true; + reset(reset_options::REMEMBER_POSITION); + break; + case DELFAV: + m_want_favorite = false; + reset(reset_options::REMEMBER_POSITION); + break; + case CONTROLLER: + if (ev->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), submenu::control_options(), m_sys.driver, &m_opts); + break; + case VIDEO: + if (ev->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), submenu::video_options(), m_sys.driver, &m_opts); + break; + case ADVANCED: + if (ev->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), submenu::advanced_options(), m_sys.driver, &m_opts); + break; + default: + break; + } + } + else if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT) + { + (ev->iptkey == IPT_UI_LEFT) ? --m_curbios : ++m_curbios; + m_opts.set_value(OPTION_BIOS, m_bios[m_curbios].second, OPTION_PRIORITY_CMDLINE); + reset(reset_options::REMEMBER_POSITION); + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_machine_configure::populate(float &customtop, float &custombottom) +{ + // add options items + item_append(_("BIOS"), FLAG_DISABLE | FLAG_UI_HEADING, nullptr); + if (!m_bios.empty()) + { + uint32_t arrows = get_arrow_flags(std::size_t(0), m_bios.size() - 1, m_curbios); + item_append(_("Driver"), m_bios[m_curbios].first, arrows, (void *)(uintptr_t)BIOS); + } + else + item_append(_("This machine has no BIOS."), FLAG_DISABLE, nullptr); + + item_append(menu_item_type::SEPARATOR); + item_append(_(submenu::advanced_options()[0].description), 0, (void *)(uintptr_t)ADVANCED); + item_append(_(submenu::video_options()[0].description), 0, (void *)(uintptr_t)VIDEO); + item_append(_(submenu::control_options()[0].description), 0, (void *)(uintptr_t)CONTROLLER); + item_append(menu_item_type::SEPARATOR); + + if (!m_want_favorite) + item_append(_("Add To Favorites"), 0, (void *)ADDFAV); + else + item_append(_("Remove From Favorites"), 0, (void *)DELFAV); + + item_append(menu_item_type::SEPARATOR); + item_append(_("Save Machine Configuration"), 0, (void *)(uintptr_t)SAVE); + + customtop = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_machine_configure::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + char const *const text[] = { _("Configure Machine:"), m_sys.description.c_str() }; + draw_text_box( + std::begin(text), std::end(text), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + +void menu_machine_configure::setup_bios() +{ + if (!m_sys.driver->rom) + return; + + std::string specbios(m_opts.bios()); + char const *default_name(nullptr); + for (tiny_rom_entry const *rom = m_sys.driver->rom; !ROMENTRY_ISEND(rom); ++rom) + { + if (ROMENTRY_ISDEFAULT_BIOS(rom)) + default_name = rom->name; + } + + std::size_t bios_count = 0; + for (romload::system_bios const &bios : romload::entries(m_sys.driver->rom).get_system_bioses()) + { + std::string name(bios.get_description()); + u32 const bios_flags(bios.get_value()); + std::string const bios_number(std::to_string(bios_flags - 1)); + + // check biosnumber and name + if ((bios_number == specbios) || (specbios == bios.get_name())) + m_curbios = bios_count; + + if (default_name && !std::strcmp(bios.get_name(), default_name)) + { + name.append(_(" (default)")); + if (specbios == "default") + m_curbios = bios_count; + } + + m_bios.emplace_back(std::move(name), bios_flags - 1); + bios_count++; + } +} + +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_plugins_configure::menu_plugins_configure(mame_ui_manager &mui, render_container &container) + : menu(mui, container) +{ +} + +menu_plugins_configure::~menu_plugins_configure() +{ + emu_file file_plugin(machine().options().ini_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (file_plugin.open("plugin.ini")) + // Can't throw in a destructor, so let's ignore silently for + // now. We shouldn't write files in a destructor in any case. + // + // throw emu_fatalerror("Unable to create file plugin.ini\n"); + return; + // generate the updated INI + file_plugin.puts(mame_machine_manager::instance()->plugins().output_ini()); +} + +//------------------------------------------------- +// handle the plugins menu +//------------------------------------------------- + +void menu_plugins_configure::handle(event const *ev) +{ + // process the menu + bool changed = false; + plugin_options &plugins = mame_machine_manager::instance()->plugins(); + if (ev && ev->itemref) + { + if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT || ev->iptkey == IPT_UI_SELECT) + { + plugin_options::plugin *p = plugins.find((const char*)ev->itemref); + if (p) + { + p->m_start = !p->m_start; + changed = true; + } + } + } + if (changed) + reset(reset_options::REMEMBER_REF); +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_plugins_configure::populate(float &customtop, float &custombottom) +{ + plugin_options const &plugins = mame_machine_manager::instance()->plugins(); + + bool first(true); + for (auto const &curentry : plugins.plugins()) + { + if ("library" != curentry.m_type) + { + first = false; + bool const enabled = curentry.m_start; + item_append_on_off(curentry.m_description, enabled, 0, (void *)(uintptr_t)curentry.m_name.c_str()); + } + } + if (first) + item_append(_("No plugins found"), 0, nullptr); + item_append(menu_item_type::SEPARATOR); + customtop = ui().get_line_height() + (3.0f * ui().box_tb_border()); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_plugins_configure::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + char const *const toptext[] = { _("Plugins") }; + draw_text_box( + std::begin(toptext), std::end(toptext), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/miscmenu.h b/src/icludes/frontend/mame/ui/miscmenu.h new file mode 100644 index 0000000..b17e671 --- /dev/null +++ b/src/icludes/frontend/mame/ui/miscmenu.h @@ -0,0 +1,188 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Maurizio Petrarota +/*************************************************************************** + + ui/miscmenu.h + + Internal MAME menus for the user interface. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_MISCMENU_H +#define MAME_FRONTEND_UI_MISCMENU_H + +#pragma once + +#include "ui/textbox.h" + +#include "crsshair.h" +#include "emuopts.h" + +#include +#include + + +struct ui_system_info; + + +namespace ui { + +class menu_network_devices : public menu +{ +public: + menu_network_devices(mame_ui_manager &mui, render_container &container); + virtual ~menu_network_devices(); + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + +class menu_bookkeeping : public menu_textbox +{ +public: + menu_bookkeeping(mame_ui_manager &mui, render_container &container); + virtual ~menu_bookkeeping(); + +protected: + virtual void menu_activated() override; + virtual void populate_text(std::optional &layout, float &width, int &lines) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + attotime prevtime; +}; + + +class menu_crosshair : public menu +{ +public: + menu_crosshair(mame_ui_manager &mui, render_container &container); + virtual ~menu_crosshair(); + +private: + enum { + CROSSHAIR_ITEM_VIS = 0, + CROSSHAIR_ITEM_PIC, + CROSSHAIR_ITEM_AUTO_TIME + }; + + /* internal crosshair menu item data */ + struct crosshair_item_data + { + render_crosshair *crosshair = nullptr; + uint8_t type = 0U; + uint8_t player = 0U; + uint8_t min = 0U, max = 0U; + uint32_t cur = 0U; + uint8_t defvalue = 0U; + std::string last_name; + std::string next_name; + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + std::vector m_data; + std::vector m_pics; +}; + + +class menu_bios_selection : public menu +{ +public: + menu_bios_selection(mame_ui_manager &mui, render_container &container); + virtual ~menu_bios_selection(); + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + + +//------------------------------------------------- +// export menu +//------------------------------------------------- + +class menu_export : public menu +{ +public: + menu_export(mame_ui_manager &mui, render_container &container, std::vector &&list); + virtual ~menu_export(); + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + std::vector m_list; +}; + + +//------------------------------------------------- +// machine configure menu +//------------------------------------------------- + +class menu_machine_configure : public menu +{ +public: + menu_machine_configure( + mame_ui_manager &mui, + render_container &container, + ui_system_info const &info, + std::function &&handler = nullptr); + virtual ~menu_machine_configure(); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + using s_bios = std::vector>; + + enum + { + ADDFAV = 1, + DELFAV, + SAVE, + CONTROLLER, + VIDEO, + BIOS, + ADVANCED, + LAST = ADVANCED + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void setup_bios(); + + std::function const m_handler; + ui_system_info const &m_sys; + emu_options m_opts; + s_bios m_bios; + std::size_t m_curbios; + bool const m_was_favorite; + bool m_want_favorite; +}; + + +//------------------------------------------------- +// plugins configure menu +//------------------------------------------------- + +class menu_plugins_configure : public menu +{ +public: + menu_plugins_configure(mame_ui_manager &mui, render_container &container); + virtual ~menu_plugins_configure(); + +protected: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_MISCMENU_H diff --git a/src/icludes/frontend/mame/ui/moptions.cpp b/src/icludes/frontend/mame/ui/moptions.cpp new file mode 100644 index 0000000..e02d453 --- /dev/null +++ b/src/icludes/frontend/mame/ui/moptions.cpp @@ -0,0 +1,112 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/*************************************************************************** + + ui/moptions.cpp + + UI main options manager. + +***************************************************************************/ + +#include "emu.h" +#include "options.h" +#include "ui/moptions.h" + + +//************************************************************************** +// UI EXTRA OPTIONS +//************************************************************************** + +const options_entry ui_options::s_option_entries[] = +{ + // search path options + { nullptr, nullptr, OPTION_HEADER, "UI SEARCH PATH OPTIONS" }, + { OPTION_HISTORY_PATH, "history;dats;.", OPTION_STRING, "path to system/software info files" }, + { OPTION_CATEGORYINI_PATH, "folders", OPTION_STRING, "path to category ini files" }, + { OPTION_CABINETS_PATH, "cabinets;cabdevs", OPTION_STRING, "path to cabinets / devices image" }, + { OPTION_CPANELS_PATH, "cpanel", OPTION_STRING, "path to control panel image" }, + { OPTION_PCBS_PATH, "pcb", OPTION_STRING, "path to pcbs image" }, + { OPTION_FLYERS_PATH, "flyers", OPTION_STRING, "path to flyers image" }, + { OPTION_TITLES_PATH, "titles", OPTION_STRING, "path to titles image" }, + { OPTION_ENDS_PATH, "ends", OPTION_STRING, "path to ends image" }, + { OPTION_MARQUEES_PATH, "marquees", OPTION_STRING, "path to marquees image" }, + { OPTION_ARTPREV_PATH, "artwork preview;artpreview", OPTION_STRING, "path to artwork preview image" }, + { OPTION_BOSSES_PATH, "bosses", OPTION_STRING, "path to bosses image" }, + { OPTION_LOGOS_PATH, "logo", OPTION_STRING, "path to logos image" }, + { OPTION_SCORES_PATH, "scores", OPTION_STRING, "path to scores image" }, + { OPTION_VERSUS_PATH, "versus", OPTION_STRING, "path to versus image" }, + { OPTION_GAMEOVER_PATH, "gameover", OPTION_STRING, "path to gameover image" }, + { OPTION_HOWTO_PATH, "howto", OPTION_STRING, "path to howto image" }, + { OPTION_SELECT_PATH, "select", OPTION_STRING, "path to select image" }, + { OPTION_ICONS_PATH, "icons", OPTION_STRING, "path to ICOns image" }, + { OPTION_COVER_PATH, "covers", OPTION_STRING, "path to software cover image" }, + { OPTION_UI_PATH, "ui", OPTION_STRING, "path to UI files" }, + + // misc options + { nullptr, nullptr, OPTION_HEADER, "UI MISC OPTIONS" }, + { OPTION_SYSTEM_NAMES, "", OPTION_STRING, "translated system names file" }, + { OPTION_SKIP_WARNINGS, "0", OPTION_BOOLEAN, "display fewer repeated warnings about imperfect emulation" }, + { OPTION_REMEMBER_LAST, "1", OPTION_BOOLEAN, "initially select last used system in main menu" }, + { OPTION_ENLARGE_SNAPS, "1", OPTION_BOOLEAN, "enlarge artwork (snapshot, title, etc.) in right panel (keeping aspect ratio)" }, + { OPTION_FORCED4X3, "1", OPTION_BOOLEAN, "force the appearance of the snapshot in the list software to 4:3" }, + { OPTION_USE_BACKGROUND, "1", OPTION_BOOLEAN, "enable background image in main view" }, + { OPTION_SKIP_BIOS_MENU, "0", OPTION_BOOLEAN, "skip bios submenu, start with configured or default" }, + { OPTION_SKIP_PARTS_MENU, "0", OPTION_BOOLEAN, "skip parts submenu, start with first part" }, + { OPTION_LAST_USED_FILTER, "", OPTION_STRING, "latest used filter" }, + { OPTION_LAST_RIGHT_PANEL "(0-1)", "0", OPTION_INTEGER, "latest right panel focus" }, + { OPTION_LAST_USED_MACHINE, "", OPTION_STRING, "latest used machine" }, + { OPTION_INFO_AUTO_AUDIT, "0", OPTION_BOOLEAN, "enable auto audit in the general info panel" }, + { OPTION_HIDE_ROMLESS, "1", OPTION_BOOLEAN, "hide romless machine from available list" }, + { OPTION_UNTHROTTLE_MUTE ";utm", "0", OPTION_BOOLEAN, "mute audio when running unthrottled" }, + + // UI options + { nullptr, nullptr, OPTION_HEADER, "UI OPTIONS" }, + { OPTION_INFOS_SIZE "(0.20-1.00)", "0.75", OPTION_FLOAT, "UI right panel infos text size (0.20 - 1.00)" }, + { OPTION_FONT_ROWS "(25-40)", "30", OPTION_INTEGER, "UI font lines per screen (25 - 40)" }, + { OPTION_HIDE_PANELS "(0-3)", "0", OPTION_INTEGER, "UI hide left/right panel in main view (0 = Show all, 1 = hide left, 2 = hide right, 3 = hide both" }, + { OPTION_UI_BORDER_COLOR, "ffffffff", OPTION_STRING, "UI border color (ARGB)" }, + { OPTION_UI_BACKGROUND_COLOR, "ef101030", OPTION_STRING, "UI background color (ARGB)" }, + { OPTION_UI_CLONE_COLOR, "ff808080", OPTION_STRING, "UI clone color (ARGB)" }, + { OPTION_UI_DIPSW_COLOR, "ffffff00", OPTION_STRING, "UI dipswitch color (ARGB)" }, + { OPTION_UI_GFXVIEWER_BG_COLOR, "ef101030", OPTION_STRING, "UI gfx viewer color (ARGB)" }, + { OPTION_UI_MOUSEDOWN_BG_COLOR, "b0606000", OPTION_STRING, "UI mouse down bg color (ARGB)" }, + { OPTION_UI_MOUSEDOWN_COLOR, "ffffff80", OPTION_STRING, "UI mouse down color (ARGB)" }, + { OPTION_UI_MOUSEOVER_BG_COLOR, "70404000", OPTION_STRING, "UI mouse over bg color (ARGB)" }, + { OPTION_UI_MOUSEOVER_COLOR, "ffffff80", OPTION_STRING, "UI mouse over color (ARGB)" }, + { OPTION_UI_SELECTED_BG_COLOR, "ef808000", OPTION_STRING, "UI selected bg color (ARGB)" }, + { OPTION_UI_SELECTED_COLOR, "ffffff00", OPTION_STRING, "UI selected color (ARGB)" }, + { OPTION_UI_SLIDER_COLOR, "ffffffff", OPTION_STRING, "UI slider color (ARGB)" }, + { OPTION_UI_SUBITEM_COLOR, "ffffffff", OPTION_STRING, "UI subitem color (ARGB)" }, + { OPTION_UI_TEXT_BG_COLOR, "ef000000", OPTION_STRING, "UI text bg color (ARGB)" }, + { OPTION_UI_TEXT_COLOR, "ffffffff", OPTION_STRING, "UI text color (ARGB)" }, + { OPTION_UI_UNAVAILABLE_COLOR, "ff404040", OPTION_STRING, "UI unavailable color (ARGB)" }, + { nullptr } +}; + +//------------------------------------------------- +// ui_options - constructor +//------------------------------------------------- + +ui_options::ui_options() : core_options() +{ + add_entries(ui_options::s_option_entries); +} + +//------------------------------------------------- +// rgb_value - decode an RGB option +//------------------------------------------------- + +rgb_t ui_options::rgb_value(const char *option) const +{ + // find the entry + core_options::entry::shared_const_ptr entry = get_entry(option); + + // look up the value, and sanity check the result + const char *value = entry->value(); + int len = strlen(value); + if (len != 8) + value = entry->default_value().c_str(); + + // convert to an rgb_t + return rgb_t((uint32_t)strtoul(value, nullptr, 16)); +} diff --git a/src/icludes/frontend/mame/ui/moptions.h b/src/icludes/frontend/mame/ui/moptions.h new file mode 100644 index 0000000..4b228aa --- /dev/null +++ b/src/icludes/frontend/mame/ui/moptions.h @@ -0,0 +1,151 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/*************************************************************************** + + ui/moptions.h + + UI main options manager. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_MOPTIONS_H +#define MAME_FRONTEND_UI_MOPTIONS_H + +#pragma once + +#include "options.h" + +// core directory options +#define OPTION_HISTORY_PATH "historypath" +#define OPTION_CATEGORYINI_PATH "categorypath" +#define OPTION_CABINETS_PATH "cabinets_directory" +#define OPTION_CPANELS_PATH "cpanels_directory" +#define OPTION_PCBS_PATH "pcbs_directory" +#define OPTION_FLYERS_PATH "flyers_directory" +#define OPTION_TITLES_PATH "titles_directory" +#define OPTION_ENDS_PATH "ends_directory" +#define OPTION_MARQUEES_PATH "marquees_directory" +#define OPTION_ARTPREV_PATH "artwork_preview_directory" +#define OPTION_BOSSES_PATH "bosses_directory" +#define OPTION_LOGOS_PATH "logos_directory" +#define OPTION_SCORES_PATH "scores_directory" +#define OPTION_VERSUS_PATH "versus_directory" +#define OPTION_GAMEOVER_PATH "gameover_directory" +#define OPTION_HOWTO_PATH "howto_directory" +#define OPTION_SELECT_PATH "select_directory" +#define OPTION_ICONS_PATH "icons_directory" +#define OPTION_COVER_PATH "covers_directory" +#define OPTION_UI_PATH "ui_path" + +// core misc options +#define OPTION_SYSTEM_NAMES "system_names" +#define OPTION_SKIP_WARNINGS "skip_warnings" +#define OPTION_REMEMBER_LAST "remember_last" +#define OPTION_ENLARGE_SNAPS "enlarge_snaps" +#define OPTION_FORCED4X3 "forced4x3" +#define OPTION_USE_BACKGROUND "use_background" +#define OPTION_SKIP_BIOS_MENU "skip_biosmenu" +#define OPTION_SKIP_PARTS_MENU "skip_partsmenu" +#define OPTION_LAST_USED_FILTER "last_used_filter" +#define OPTION_LAST_RIGHT_PANEL "last_right_panel" +#define OPTION_LAST_USED_MACHINE "last_used_machine" +#define OPTION_INFO_AUTO_AUDIT "info_audit_enabled" +#define OPTION_HIDE_ROMLESS "hide_romless" +#define OPTION_UNTHROTTLE_MUTE "unthrottle_mute" + + +// core UI options +#define OPTION_INFOS_SIZE "infos_text_size" +#define OPTION_FONT_ROWS "font_rows" +#define OPTION_HIDE_PANELS "hide_main_panel" + +#define OPTION_UI_BORDER_COLOR "ui_border_color" +#define OPTION_UI_BACKGROUND_COLOR "ui_bg_color" +#define OPTION_UI_GFXVIEWER_BG_COLOR "ui_gfxviewer_color" +#define OPTION_UI_UNAVAILABLE_COLOR "ui_unavail_color" +#define OPTION_UI_TEXT_COLOR "ui_text_color" +#define OPTION_UI_TEXT_BG_COLOR "ui_text_bg_color" +#define OPTION_UI_SUBITEM_COLOR "ui_subitem_color" +#define OPTION_UI_CLONE_COLOR "ui_clone_color" +#define OPTION_UI_SELECTED_COLOR "ui_selected_color" +#define OPTION_UI_SELECTED_BG_COLOR "ui_selected_bg_color" +#define OPTION_UI_MOUSEOVER_COLOR "ui_mouseover_color" +#define OPTION_UI_MOUSEOVER_BG_COLOR "ui_mouseover_bg_color" +#define OPTION_UI_MOUSEDOWN_COLOR "ui_mousedown_color" +#define OPTION_UI_MOUSEDOWN_BG_COLOR "ui_mousedown_bg_color" +#define OPTION_UI_DIPSW_COLOR "ui_dipsw_color" +#define OPTION_UI_SLIDER_COLOR "ui_slider_color" + +class ui_options : public core_options +{ +public: + // construction/destruction + ui_options(); + + // Search path options + const char *history_path() const { return value(OPTION_HISTORY_PATH); } + const char *categoryini_path() const { return value(OPTION_CATEGORYINI_PATH); } + const char *cabinets_directory() const { return value(OPTION_CABINETS_PATH); } + const char *cpanels_directory() const { return value(OPTION_CPANELS_PATH); } + const char *pcbs_directory() const { return value(OPTION_PCBS_PATH); } + const char *flyers_directory() const { return value(OPTION_FLYERS_PATH); } + const char *titles_directory() const { return value(OPTION_TITLES_PATH); } + const char *ends_directory() const { return value(OPTION_ENDS_PATH); } + const char *marquees_directory() const { return value(OPTION_MARQUEES_PATH); } + const char *artprev_directory() const { return value(OPTION_ARTPREV_PATH); } + const char *bosses_directory() const { return value(OPTION_BOSSES_PATH); } + const char *logos_directory() const { return value(OPTION_LOGOS_PATH); } + const char *scores_directory() const { return value(OPTION_SCORES_PATH); } + const char *versus_directory() const { return value(OPTION_VERSUS_PATH); } + const char *gameover_directory() const { return value(OPTION_GAMEOVER_PATH); } + const char *howto_directory() const { return value(OPTION_HOWTO_PATH); } + const char *select_directory() const { return value(OPTION_SELECT_PATH); } + const char *icons_directory() const { return value(OPTION_ICONS_PATH); } + const char *covers_directory() const { return value(OPTION_COVER_PATH); } + const char *ui_path() const { return value(OPTION_UI_PATH); } + + // Misc options + const char *system_names() const { return value(OPTION_SYSTEM_NAMES); } + bool skip_warnings() const { return bool_value(OPTION_SKIP_WARNINGS); } + bool remember_last() const { return bool_value(OPTION_REMEMBER_LAST); } + bool enlarge_snaps() const { return bool_value(OPTION_ENLARGE_SNAPS); } + bool forced_4x3_snapshot() const { return bool_value(OPTION_FORCED4X3); } + bool use_background_image() const { return bool_value(OPTION_USE_BACKGROUND); } + bool skip_bios_menu() const { return bool_value(OPTION_SKIP_BIOS_MENU); } + bool skip_parts_menu() const { return bool_value(OPTION_SKIP_PARTS_MENU); } + const char *last_used_machine() const { return value(OPTION_LAST_USED_MACHINE); } + const char *last_used_filter() const { return value(OPTION_LAST_USED_FILTER); } + int last_right_panel() const { return int_value(OPTION_LAST_RIGHT_PANEL); } + bool info_audit() const { return bool_value(OPTION_INFO_AUTO_AUDIT); } + bool hide_romless() const { return bool_value(OPTION_HIDE_ROMLESS); } + bool unthrottle_mute() const { return bool_value(OPTION_UNTHROTTLE_MUTE); } + + // UI options + float infos_size() const { return float_value(OPTION_INFOS_SIZE); } + int font_rows() const { return int_value(OPTION_FONT_ROWS); } + int hide_panels() const { return int_value(OPTION_HIDE_PANELS); } + + rgb_t border_color() const { return rgb_value(OPTION_UI_BORDER_COLOR); } + rgb_t background_color() const { return rgb_value(OPTION_UI_BACKGROUND_COLOR); } + rgb_t gfxviewer_bg_color() const { return rgb_value(OPTION_UI_GFXVIEWER_BG_COLOR); } + rgb_t unavailable_color() const { return rgb_value(OPTION_UI_UNAVAILABLE_COLOR); } + rgb_t text_color() const { return rgb_value(OPTION_UI_TEXT_COLOR); } + rgb_t text_bg_color() const { return rgb_value(OPTION_UI_TEXT_BG_COLOR); } + rgb_t subitem_color() const { return rgb_value(OPTION_UI_SUBITEM_COLOR); } + rgb_t clone_color() const { return rgb_value(OPTION_UI_CLONE_COLOR); } + rgb_t selected_color() const { return rgb_value(OPTION_UI_SELECTED_COLOR); } + rgb_t selected_bg_color() const { return rgb_value(OPTION_UI_SELECTED_BG_COLOR); } + rgb_t mouseover_color() const { return rgb_value(OPTION_UI_MOUSEOVER_COLOR); } + rgb_t mouseover_bg_color() const { return rgb_value(OPTION_UI_MOUSEOVER_BG_COLOR); } + rgb_t mousedown_color() const { return rgb_value(OPTION_UI_MOUSEDOWN_COLOR); } + rgb_t mousedown_bg_color() const { return rgb_value(OPTION_UI_MOUSEDOWN_BG_COLOR); } + rgb_t dipsw_color() const { return rgb_value(OPTION_UI_DIPSW_COLOR); } + rgb_t slider_color() const { return rgb_value(OPTION_UI_SLIDER_COLOR); } + + rgb_t rgb_value(const char *option) const; + +private: + static const options_entry s_option_entries[]; +}; + +#endif // MAME_FRONTEND_UI_MOPTIONS_H diff --git a/src/icludes/frontend/mame/ui/optsmenu.cpp b/src/icludes/frontend/mame/ui/optsmenu.cpp new file mode 100644 index 0000000..d21f069 --- /dev/null +++ b/src/icludes/frontend/mame/ui/optsmenu.cpp @@ -0,0 +1,301 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/********************************************************************* + + ui/optsmenu.cpp + + UI main options menu manager. + +*********************************************************************/ + +#include "emu.h" +#include "ui/optsmenu.h" + +#include "ui/custui.h" +#include "ui/dirmenu.h" +#include "ui/inputmap.h" +#include "ui/miscmenu.h" +#include "ui/selector.h" +#include "ui/sndmenu.h" +#include "ui/submenu.h" +#include "ui/ui.h" + +#include "mame.h" +#include "mameopts.h" +#include "rendfont.h" + + +namespace ui { + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_simple_game_options::menu_simple_game_options( + mame_ui_manager &mui, + render_container &container, + std::function &&handler) + : menu(mui, container) + , m_handler(std::move(handler)) +{ + set_process_flags(PROCESS_LR_REPEAT); +} + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_simple_game_options::~menu_simple_game_options() +{ + ui().save_ui_options(); + if (m_handler) + m_handler(); +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_simple_game_options::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + handle_item_event(*ev); +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_simple_game_options::populate(float &customtop, float &custombottom) +{ + item_append(_(submenu::video_options()[0].description), 0, (void *)(uintptr_t)DISPLAY_MENU); + item_append(_("Sound Options"), 0, (void *)(uintptr_t)SOUND_MENU); + item_append(_(submenu::misc_options()[0].description), 0, (void *)(uintptr_t)MISC_MENU); + item_append(_(submenu::control_options()[0].description), 0, (void *)(uintptr_t)CONTROLLER_MENU); + item_append(_("General Inputs"), 0, (void *)(uintptr_t)CGI_MENU); + item_append(_(submenu::advanced_options()[0].description), 0, (void *)(uintptr_t)ADVANCED_MENU); + if (machine().options().plugins()) + item_append(_("Plugins"), 0, (void *)(uintptr_t)PLUGINS_MENU); + item_append(menu_item_type::SEPARATOR); + item_append(_("Save Configuration"), 0, (void *)(uintptr_t)SAVE_CONFIG); + + custombottom = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border(); + customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + +//------------------------------------------------- +// handle item +//------------------------------------------------- + +void menu_simple_game_options::handle_item_event(event const &menu_event) +{ + switch ((uintptr_t)menu_event.itemref) + { + case MISC_MENU: + if (menu_event.iptkey == IPT_UI_SELECT) + { + menu::stack_push(ui(), container(), submenu::misc_options()); + ui_globals::reset = true; + } + break; + case SOUND_MENU: + if (menu_event.iptkey == IPT_UI_SELECT) + { + menu::stack_push(ui(), container()); + ui_globals::reset = true; + } + break; + case DISPLAY_MENU: + if (menu_event.iptkey == IPT_UI_SELECT) + { + menu::stack_push(ui(), container(), submenu::video_options()); + ui_globals::reset = true; + } + break; + case CONTROLLER_MENU: + if (menu_event.iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), submenu::control_options()); + break; + case CGI_MENU: + if (menu_event.iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container()); + break; + case ADVANCED_MENU: + if (menu_event.iptkey == IPT_UI_SELECT) + { + menu::stack_push(ui(), container(), submenu::advanced_options()); + ui_globals::reset = true; + } + break; + case PLUGINS_MENU: + if (menu_event.iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container()); + break; + case SAVE_CONFIG: + if (menu_event.iptkey == IPT_UI_SELECT) + ui().save_main_option(); + break; + } +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_simple_game_options::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + char const *const toptext[] = { _("Settings") }; + draw_text_box( + std::begin(toptext), std::end(toptext), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_game_options::menu_game_options( + mame_ui_manager &mui, + render_container &container, + machine_filter_data &filter_data, + std::function &&handler) + : menu_simple_game_options(mui, container, std::move(handler)) + , m_filter_data(filter_data) + , m_main_filter(filter_data.get_current_filter_type()) +{ + set_process_flags(PROCESS_LR_REPEAT); +} + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_game_options::~menu_game_options() +{ + m_filter_data.set_current_filter_type(m_main_filter); +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_game_options::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + handle_item_event(*ev); +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_game_options::populate(float &customtop, float &custombottom) +{ + // set filter arrow + std::string fbuff; + + // add filter item + uint32_t arrow_flags = get_arrow_flags(machine_filter::FIRST, machine_filter::LAST, m_main_filter); + machine_filter &active_filter(m_filter_data.get_filter(m_main_filter)); + item_append(_("Filter"), active_filter.display_name(), arrow_flags, (void *)(uintptr_t)FILTER_MENU); + + // add subitem if the filter wants it + if (active_filter.wants_adjuster()) + { + std::string name(convert_command_glyph("^!")); + item_append(name, active_filter.adjust_text(), active_filter.arrow_flags(), (void *)(FILTER_ADJUST)); + } + + item_append(menu_item_type::SEPARATOR); + + // add options items + item_append(_("Customize UI"), 0, (void *)(uintptr_t)CUSTOM_MENU); + item_append(_("Configure Directories"), 0, (void *)(uintptr_t)CONF_DIR); + + // add the options that don't relate to the UI + menu_simple_game_options::populate(customtop, custombottom); +} + +//------------------------------------------------- +// handle item +//------------------------------------------------- + +void menu_game_options::handle_item_event(event const &menu_event) +{ + bool changed = false; + + switch ((uintptr_t)menu_event.itemref) + { + case FILTER_MENU: + if (menu_event.iptkey == IPT_UI_LEFT || menu_event.iptkey == IPT_UI_RIGHT) + { + (menu_event.iptkey == IPT_UI_RIGHT) ? ++m_main_filter : --m_main_filter; + changed = true; + } + else if (menu_event.iptkey == IPT_UI_SELECT) + { + std::vector s_sel(machine_filter::COUNT); + for (unsigned index = 0; index < s_sel.size(); ++index) + s_sel[index] = machine_filter::display_name(machine_filter::type(index)); + + menu::stack_push( + ui(), container(), std::move(s_sel), m_main_filter, + [this] (int selection) + { + m_main_filter = machine_filter::type(selection); + reset(reset_options::REMEMBER_REF); + }); + } + break; + case FILTER_ADJUST: + if (menu_event.iptkey == IPT_UI_LEFT) + { + changed = m_filter_data.get_filter(m_main_filter).adjust_left(); + } + else if (menu_event.iptkey == IPT_UI_RIGHT) + { + changed = m_filter_data.get_filter(m_main_filter).adjust_right(); + } + else if (menu_event.iptkey == IPT_UI_SELECT) + { + m_filter_data.get_filter(m_main_filter).show_ui( + ui(), + container(), + [this] (machine_filter &filter) + { + if (machine_filter::CUSTOM == filter.get_type()) + { + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(util::string_format("custom_%s_filter.ini", emulator_info::get_configname()))) + { + filter.save_ini(file, 0); + file.close(); + } + } + reset(reset_options::REMEMBER_REF); + }); + } + break; + case CONF_DIR: + if (menu_event.iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container()); + break; + case CUSTOM_MENU: + if (menu_event.iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), [this] () { reset(reset_options::REMEMBER_REF); }); + break; + default: + menu_simple_game_options::handle_item_event(menu_event); + return; + } + + if (changed) + reset(reset_options::REMEMBER_REF); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/optsmenu.h b/src/icludes/frontend/mame/ui/optsmenu.h new file mode 100644 index 0000000..297ec7a --- /dev/null +++ b/src/icludes/frontend/mame/ui/optsmenu.h @@ -0,0 +1,85 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/*************************************************************************** + + ui/optsmenu.h + + UI main options menu manager. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_OPTSMENU_H +#define MAME_FRONTEND_UI_OPTSMENU_H + +#pragma once + +#include "ui/menu.h" +#include "ui/utils.h" + + +namespace ui { + +class menu_simple_game_options : public menu +{ +public: + menu_simple_game_options( + mame_ui_manager &mui, + render_container &container, + std::function &&handler); + virtual ~menu_simple_game_options() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void handle(event const *ev) override; + virtual void populate(float &customtop, float &custombottom) override; + + void handle_item_event(event const &menu_event); + +private: + enum + { + DISPLAY_MENU = 1001, + SOUND_MENU, + MISC_MENU, + CONTROLLER_MENU, + CGI_MENU, + ADVANCED_MENU, + PLUGINS_MENU, + SAVE_CONFIG + }; + + std::function const m_handler; +}; + + +class menu_game_options : public menu_simple_game_options +{ +public: + menu_game_options( + mame_ui_manager &mui, + render_container &container, + machine_filter_data &filter_data, + std::function &&handler); + virtual ~menu_game_options() override; + +protected: + virtual void handle(event const *ev) override; + virtual void populate(float &customtop, float &custombottom) override; + + void handle_item_event(event const &menu_event); + +private: + enum + { + FILTER_MENU = 2001, + FILTER_ADJUST, + CONF_DIR, + CUSTOM_MENU + }; + + machine_filter_data &m_filter_data; + machine_filter::type m_main_filter; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_OPTSMENU_H diff --git a/src/icludes/frontend/mame/ui/pluginopt.cpp b/src/icludes/frontend/mame/ui/pluginopt.cpp new file mode 100644 index 0000000..6f6a980 --- /dev/null +++ b/src/icludes/frontend/mame/ui/pluginopt.cpp @@ -0,0 +1,219 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/********************************************************************* + + ui/pluginopt.cpp + + Internal menu for the plugin interface. + +*********************************************************************/ + +#include "emu.h" +#include "pluginopt.h" + +#include "ui/utils.h" + +#include "luaengine.h" +#include "mame.h" + + +namespace ui { + +void menu_plugin::handle(event const *ev) +{ + if (ev && ev->itemref) + { + if (ev->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), (char *)ev->itemref); + } +} + +menu_plugin::menu_plugin(mame_ui_manager &mui, render_container &container) : + menu(mui, container), + m_plugins(mame_machine_manager::instance()->lua()->get_menu()) +{ +} + +void menu_plugin::populate(float &customtop, float &custombottom) +{ + for (auto &curplugin : m_plugins) + item_append(curplugin, 0, (void *)curplugin.c_str()); + item_append(menu_item_type::SEPARATOR); +} + +void menu_plugin::show_menu(mame_ui_manager &mui, render_container &container, char *menu) +{ + // reset the menu stack + menu::stack_reset(mui); + + // add the plugin menu entry + menu::stack_push(mui, container, menu); + + // force the menus on + mui.show_menu(); + + // make sure MAME is paused + mui.machine().pause(); +} + +menu_plugin::~menu_plugin() +{ +} + +menu_plugin_opt::menu_plugin_opt(mame_ui_manager &mui, render_container &container, std::string_view menu) : + ui::menu(mui, container), + m_menu(menu), + m_need_idle(false) +{ +} + +void menu_plugin_opt::handle(event const *ev) +{ + void *const itemref = ev ? ev->itemref : get_selection_ref(); + std::string key; + if (ev) + { + switch (ev->iptkey) + { + case IPT_UI_UP: + key = "up"; + break; + case IPT_UI_DOWN: + key = "down"; + break; + case IPT_UI_LEFT: + key = "left"; + break; + case IPT_UI_RIGHT: + key = "right"; + break; + case IPT_UI_PREV_GROUP: + key = "prevgroup"; + break; + case IPT_UI_NEXT_GROUP: + key = "nextgroup"; + break; + case IPT_UI_SELECT: + key = "select"; + break; + case IPT_UI_DISPLAY_COMMENT: + key = "comment"; + break; + case IPT_UI_CLEAR: + key = "clear"; + break; + case IPT_UI_CANCEL: + key = "cancel"; + break; + case IPT_SPECIAL: + key = std::to_string((u32)ev->unichar); + break; + default: + break; + } + } + if (!key.empty() || m_need_idle) + { + auto const result = mame_machine_manager::instance()->lua()->menu_callback(m_menu, uintptr_t(itemref), key); + if (result.second) + set_selection(reinterpret_cast(uintptr_t(*result.second))); + if (result.first) + reset(reset_options::REMEMBER_REF); + else if (ev && (ev->iptkey == IPT_UI_CANCEL)) + stack_pop(); + } +} + +void menu_plugin_opt::populate(float &customtop, float &custombottom) +{ + std::vector> menu_list; + std::string flags; + auto const sel = mame_machine_manager::instance()->lua()->menu_populate(m_menu, menu_list, flags); + + uintptr_t i = 1; + for (auto &item : menu_list) + { + std::string &text = std::get<0>(item); + std::string &subtext = std::get<1>(item); + std::string_view tflags = std::get<2>(item); + + uint32_t item_flags_or = uint32_t(0); + uint32_t item_flags_and = ~uint32_t(0); + auto flag_start = tflags.find_first_not_of(' '); + while (std::string_view::npos != flag_start) + { + tflags.remove_prefix(flag_start); + auto const flag_end = tflags.find(' '); + auto const flag = tflags.substr(0, flag_end); + tflags.remove_prefix(flag.length()); + flag_start = tflags.find_first_not_of(' '); + + if (flag == "off") + item_flags_or |= FLAG_DISABLE; + else if (flag == "on") + item_flags_and &= ~FLAG_DISABLE; + else if (flag == "l") + item_flags_or |= FLAG_LEFT_ARROW; + else if (flag == "r") + item_flags_or |= FLAG_RIGHT_ARROW; + else if (flag == "lr") + item_flags_or |= FLAG_RIGHT_ARROW | FLAG_LEFT_ARROW; + else if (flag == "invert") + item_flags_or |= FLAG_INVERT; + else if (flag == "heading") + item_flags_or |= FLAG_DISABLE | FLAG_UI_HEADING; + else + osd_printf_info("menu_plugin_opt: unknown flag '%s' for item %d (%s)\n", flag, i, text); + } + + if (text == "---") + item_append(menu_item_type::SEPARATOR); + else + item_append(std::move(text), std::move(subtext), item_flags_or & item_flags_and, reinterpret_cast(i)); + ++i; + } + item_append(menu_item_type::SEPARATOR); + + if (sel) + set_selection(reinterpret_cast(uintptr_t(*sel))); + + uint32_t process_flags = 0U; + m_need_idle = false; + if (!flags.empty()) + { + std::string_view mflags = flags; + auto flag_start = mflags.find_first_not_of(' '); + while (std::string_view::npos != flag_start) + { + mflags.remove_prefix(flag_start); + auto const flag_end = mflags.find(' '); + auto const flag = mflags.substr(0, flag_end); + mflags.remove_prefix(flag.length()); + flag_start = mflags.find_first_not_of(' '); + + if (flag == "nokeys") + process_flags |= PROCESS_NOKEYS; + else if (flag == "lralways") + process_flags |= PROCESS_LR_ALWAYS; + else if (flag == "lrrepeat") + process_flags |= PROCESS_LR_REPEAT; + else if (flag == "customnav") + process_flags |= PROCESS_CUSTOM_NAV; + else if (flag == "ignorepause") + process_flags |= PROCESS_IGNOREPAUSE; + else if (flag == "idle") + m_need_idle = true; + else + osd_printf_info("menu_plugin_opt: unknown processing flag '%s'\n", flag); + } + if (process_flags & PROCESS_NOKEYS) + m_need_idle = true; + } + set_process_flags(process_flags); +} + +menu_plugin_opt::~menu_plugin_opt() +{ +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/pluginopt.h b/src/icludes/frontend/mame/ui/pluginopt.h new file mode 100644 index 0000000..15196c1 --- /dev/null +++ b/src/icludes/frontend/mame/ui/pluginopt.h @@ -0,0 +1,60 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Carl +/*************************************************************************** + + ui/pluginopt.h + + Internal menu for the plugin interface. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_PLUGINOPT_H +#define MAME_FRONTEND_UI_PLUGINOPT_H + +#pragma once + +#include "ui/menu.h" +#include "ui/ui.h" + +#include +#include +#include + + +namespace ui { + +class menu_plugin : public menu +{ +public: + menu_plugin(mame_ui_manager &mui, render_container &container); + + static void show_menu(mame_ui_manager &mui, render_container &container, char *menu); + + virtual ~menu_plugin(); + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + std::vector &m_plugins; +}; + +class menu_plugin_opt : public menu +{ +public: + menu_plugin_opt(mame_ui_manager &mui, render_container &container, std::string_view menu); + virtual ~menu_plugin_opt(); + +protected: + virtual bool custom_ui_cancel() override { return true; } + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + std::string const m_menu; + bool m_need_idle; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_PLUGINOPT_H diff --git a/src/icludes/frontend/mame/ui/quitmenu.cpp b/src/icludes/frontend/mame/ui/quitmenu.cpp new file mode 100644 index 0000000..bb26f7e --- /dev/null +++ b/src/icludes/frontend/mame/ui/quitmenu.cpp @@ -0,0 +1,61 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ui/quitmenu.h + + Menus involved in quitting MAME. + +***************************************************************************/ + +#include "emu.h" +#include "quitmenu.h" + +#include "uiinput.h" + + +namespace ui { + +menu_confirm_quit::menu_confirm_quit(mame_ui_manager &mui, render_container &container) + : autopause_menu<>(mui, container) +{ + set_one_shot(true); + set_process_flags(PROCESS_CUSTOM_ONLY | PROCESS_NOINPUT); +} + + +menu_confirm_quit::~menu_confirm_quit() +{ +} + + +void menu_confirm_quit::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) +{ + ui().draw_text_box( + container(), + util::string_format( + _("Are you sure you want to quit?\n\n" + "Press %1$s to quit\n" + "Press %2$s to return to emulation"), + ui().get_general_input_setting(IPT_UI_SELECT), + ui().get_general_input_setting(IPT_UI_CANCEL)), + text_layout::text_justify::CENTER, + 0.5f, 0.5f, + UI_RED_COLOR); +} + + +void menu_confirm_quit::populate(float &customtop, float &custombottom) +{ +} + + +void menu_confirm_quit::handle(event const *ev) +{ + if (machine().ui_input().pressed(IPT_UI_SELECT)) + machine().schedule_exit(); + else if (machine().ui_input().pressed(IPT_UI_CANCEL)) + stack_pop(); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/quitmenu.h b/src/icludes/frontend/mame/ui/quitmenu.h new file mode 100644 index 0000000..81ca61e --- /dev/null +++ b/src/icludes/frontend/mame/ui/quitmenu.h @@ -0,0 +1,36 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ui/quitmenu.h + + Menus involved in quitting MAME. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_QUITMENU_H +#define MAME_FRONTEND_UI_QUITMENU_H + +#pragma once + +#include "ui/menu.h" + + +namespace ui { + +class menu_confirm_quit : public autopause_menu<> +{ +public: + menu_confirm_quit(mame_ui_manager &mui, render_container &container); + virtual ~menu_confirm_quit(); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_QUITMENU_H diff --git a/src/icludes/frontend/mame/ui/selector.cpp b/src/icludes/frontend/mame/ui/selector.cpp new file mode 100644 index 0000000..6a58334 --- /dev/null +++ b/src/icludes/frontend/mame/ui/selector.cpp @@ -0,0 +1,172 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/********************************************************************* + + ui/selector.cpp + + Internal UI user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/selector.h" + +#include "ui/ui.h" +#include "ui/utils.h" + +#include "corestr.h" +#include "unicode.h" + + +namespace ui { + +//------------------------------------------------- +// ctor / dtor +//------------------------------------------------- + +menu_selector::menu_selector( + mame_ui_manager &mui, + render_container &container, + std::vector &&sel, + int initial, + std::function &&handler) + : menu(mui, container) + , m_search() + , m_str_items(std::move(sel)) + , m_handler(std::move(handler)) + , m_initial(initial) +{ + m_searchlist[0] = nullptr; +} + +menu_selector::~menu_selector() +{ +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_selector::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref) + { + if (ev->iptkey == IPT_UI_SELECT) + { + int selection(-1); + for (size_t idx = 0; (m_str_items.size() > idx) && (0 > selection); ++idx) + if ((void*)&m_str_items[idx] == ev->itemref) + selection = int(unsigned(idx)); + + m_handler(selection); + + stack_pop(); + } + else if (ev->iptkey == IPT_SPECIAL) + { + if (input_character(m_search, ev->unichar, uchar_is_printable)) + reset(reset_options::SELECT_FIRST); + } + + // escape pressed with non-empty text clears the text + else if (ev->iptkey == IPT_UI_CANCEL && !m_search.empty()) + { + m_search.clear(); + reset(reset_options::SELECT_FIRST); + } + } +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_selector::populate(float &customtop, float &custombottom) +{ + if (!m_search.empty()) + { + find_matches(m_search.c_str()); + + for (int curitem = 0; m_searchlist[curitem]; ++curitem) + item_append(*m_searchlist[curitem], 0, (void *)m_searchlist[curitem]); + } + else + { + for (size_t index = 0; index < m_str_items.size(); ++index) + { + if ((0 <= m_initial) && (unsigned(m_initial) == index)) + set_selected_index(index); + + item_append(m_str_items[index], 0, (void *)&m_str_items[index]); + } + } + + item_append(menu_item_type::SEPARATOR); + customtop = custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border(); + m_initial = -1; +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_selector::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + std::string tempbuf[1] = { std::string(_("Selection List - Search: ")).append(m_search).append("_") }; + draw_text_box( + std::begin(tempbuf), std::end(tempbuf), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); + + // get the text for 'UI Select' + tempbuf[0] = string_format(_("Double-click or press %1$s to select"), ui().get_general_input_setting(IPT_UI_SELECT)); + draw_text_box( + std::begin(tempbuf), std::end(tempbuf), + origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); +} + +//------------------------------------------------- +// find approximate matches +//------------------------------------------------- + +void menu_selector::find_matches(const char *str) +{ + // allocate memory to track the penalty value + m_ucs_items.reserve(m_str_items.size()); + std::vector penalty(VISIBLE_GAMES_IN_SEARCH, 1.0); + std::u32string const search(ustr_from_utf8(normalize_unicode(str, unicode_normalization_form::D, true))); + + int index = 0; + for ( ; index < m_str_items.size(); ++index) + { + assert(m_ucs_items.size() >= index); + if (m_ucs_items.size() == index) + m_ucs_items.emplace_back(ustr_from_utf8(normalize_unicode(m_str_items[index], unicode_normalization_form::D, true))); + double const curpenalty(util::edit_distance(search, m_ucs_items[index])); + + // insert into the sorted table of matches + for (int matchnum = VISIBLE_GAMES_IN_SEARCH - 1; matchnum >= 0; --matchnum) + { + // stop if we're worse than the current entry + if (curpenalty >= penalty[matchnum]) + break; + + // as long as this isn't the last entry, bump this one down + if (matchnum < VISIBLE_GAMES_IN_SEARCH - 1) + { + penalty[matchnum + 1] = penalty[matchnum]; + m_searchlist[matchnum + 1] = m_searchlist[matchnum]; + } + + m_searchlist[matchnum] = &m_str_items[index]; + penalty[matchnum] = curpenalty; + } + } + (index < VISIBLE_GAMES_IN_SEARCH) ? m_searchlist[index] = nullptr : m_searchlist[VISIBLE_GAMES_IN_SEARCH] = nullptr; +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/selector.h b/src/icludes/frontend/mame/ui/selector.h new file mode 100644 index 0000000..d8b0abc --- /dev/null +++ b/src/icludes/frontend/mame/ui/selector.h @@ -0,0 +1,58 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/*************************************************************************** + + ui/selector.h + + Internal UI user interface. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_SELECTOR_H +#define MAME_FRONTEND_UI_SELECTOR_H + +#pragma once + + +#include "ui/menu.h" + + +namespace ui { + +//------------------------------------------------- +// class selector menu +//------------------------------------------------- + +class menu_selector : public menu +{ +public: + menu_selector( + mame_ui_manager &mui, + render_container &container, + std::vector &&sel, + int initial, + std::function &&handler); + virtual ~menu_selector() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual bool custom_ui_cancel() override { return !m_search.empty(); } + +private: + enum { VISIBLE_GAMES_IN_SEARCH = 200 }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void find_matches(const char *str); + + std::string m_search; + std::vector m_str_items; + std::function m_handler; + std::vector m_ucs_items; + int m_initial; + std::string *m_searchlist[VISIBLE_GAMES_IN_SEARCH + 1]; +}; + +} // namespace ui + +#endif /* MAME_FRONTEND_UI_SELECTOR_H */ diff --git a/src/icludes/frontend/mame/ui/selgame.cpp b/src/icludes/frontend/mame/ui/selgame.cpp new file mode 100644 index 0000000..d52274a --- /dev/null +++ b/src/icludes/frontend/mame/ui/selgame.cpp @@ -0,0 +1,1118 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/********************************************************************* + + ui/selgame.cpp + + Main UI menu. + +*********************************************************************/ + +#include "emu.h" +#include "ui/selgame.h" + +#include "ui/auditmenu.h" +#include "ui/icorender.h" +#include "ui/inifile.h" +#include "ui/miscmenu.h" +#include "ui/optsmenu.h" +#include "ui/selector.h" +#include "ui/selsoft.h" +#include "ui/systemlist.h" +#include "ui/ui.h" + +#include "infoxml.h" +#include "luaengine.h" +#include "mame.h" + +#include "corestr.h" +#include "drivenum.h" +#include "emuopts.h" +#include "rendutil.h" +#include "romload.h" +#include "softlist_dev.h" +#include "uiinput.h" +#include "unicode.h" + +#include +#include +#include + + +extern const char UI_VERSION_TAG[]; + +namespace ui { + +bool menu_select_game::s_first_start = true; + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &container, const char *gamename) + : menu_select_launch(mui, container, false) + , m_persistent_data(system_list::instance()) + , m_icons(MAX_ICONS_RENDER) + , m_icon_paths() + , m_displaylist() + , m_searchlist() + , m_searched_fields(system_list::AVAIL_NONE) + , m_populated_favorites(false) +{ + std::string error_string, last_filter, sub_filter; + ui_options &moptions = mui.options(); + + // load drivers cache + m_persistent_data.cache_data(mui.options()); + + // check if there are available system icons + check_for_icons(nullptr); + + // build drivers list + if (!load_available_machines()) + build_available_list(); + + if (s_first_start) + { + //s_first_start = false; TODO: why wasn't it ever clearing the first start flag? + reselect_last::set_driver(moptions.last_used_machine()); + ui_globals::rpanel = std::min(std::max(moptions.last_right_panel(), RP_FIRST), RP_LAST); + + std::string tmp(moptions.last_used_filter()); + std::size_t const found = tmp.find_first_of(','); + std::string fake_ini; + if (found == std::string::npos) + { + fake_ini = util::string_format(u8"\uFEFF%s = 1\n", tmp); + } + else + { + std::string const sub_filter(tmp.substr(found + 1)); + tmp.resize(found); + fake_ini = util::string_format(u8"\uFEFF%s = %s\n", tmp, sub_filter); + } + + emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); + if (!file.open_ram(fake_ini.c_str(), fake_ini.size())) + { + m_persistent_data.filter_data().load_ini(file); + file.close(); + } + } + + // do this after processing the last used filter setting so it overwrites the placeholder + load_custom_filters(); + m_filter_highlight = m_persistent_data.filter_data().get_current_filter_type(); + + if (!moptions.remember_last()) + reselect_last::reset(); + + mui.machine().options().set_value(OPTION_SNAPNAME, "%g/%i", OPTION_PRIORITY_CMDLINE); + + ui_globals::curdats_view = 0; + ui_globals::panels_status = moptions.hide_panels(); + ui_globals::curdats_total = 1; +} + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_select_game::~menu_select_game() +{ + std::string error_string, last_driver; + ui_system_info const *system; + ui_software_info const *swinfo; + get_selection(swinfo, system); + if (swinfo) + last_driver = swinfo->shortname; + else if (system) + last_driver = system->driver->name; + + std::string const filter(m_persistent_data.filter_data().get_config_string()); + + ui_options &mopt = ui().options(); + mopt.set_value(OPTION_LAST_RIGHT_PANEL, ui_globals::rpanel, OPTION_PRIORITY_CMDLINE); + mopt.set_value(OPTION_LAST_USED_FILTER, filter, OPTION_PRIORITY_CMDLINE); + mopt.set_value(OPTION_LAST_USED_MACHINE, last_driver, OPTION_PRIORITY_CMDLINE); + mopt.set_value(OPTION_HIDE_PANELS, ui_globals::panels_status, OPTION_PRIORITY_CMDLINE); + ui().save_ui_options(); +} + +//------------------------------------------------- +// menu_activated +//------------------------------------------------- + +void menu_select_game::menu_activated() +{ + // if I have to load datfile, perform a hard reset + if (ui_globals::reset) + { + // dumb workaround for not being able to add an exit notifier + struct cache_reset { ~cache_reset() { system_list::instance().reset_cache(); } }; + ui().get_session_data(); + + ui_globals::reset = false; + machine().schedule_hard_reset(); + stack_reset(); + return; + } +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_select_game::handle(event const *ev) +{ + if (!m_prev_selected) + m_prev_selected = item(0).ref(); + + // if I have to select software, force software list submenu + if (reselect_last::get()) + { + // FIXME: this is never hit, need a better way to return to software selection if necessary + const ui_system_info *system; + const ui_software_info *software; + get_selection(software, system); + menu::stack_push(ui(), container(), *system); + return; + } + + // FIXME: everything above here used to run before events were processed + + // process the menu + if (ev) + { + if (dismiss_error()) + { + // reset the error on any subsequent menu event + } + else switch (ev->iptkey) + { + case IPT_UI_UP: + if ((get_focus() == focused_menu::LEFT) && (machine_filter::FIRST < m_filter_highlight)) + --m_filter_highlight; + break; + + case IPT_UI_DOWN: + if ((get_focus() == focused_menu::LEFT) && (machine_filter::LAST > m_filter_highlight)) + m_filter_highlight++; + break; + + case IPT_UI_HOME: + if (get_focus() == focused_menu::LEFT) + m_filter_highlight = machine_filter::FIRST; + break; + + case IPT_UI_END: + if (get_focus() == focused_menu::LEFT) + m_filter_highlight = machine_filter::LAST; + break; + + case IPT_UI_EXPORT: + inkey_export(); + break; + + case IPT_UI_DATS: + inkey_dats(); + break; + + default: + if (ev->itemref) + { + switch (ev->iptkey) + { + case IPT_UI_SELECT: + if (get_focus() == focused_menu::MAIN) + { + if (m_populated_favorites) + inkey_select_favorite(ev); + else + inkey_select(ev); + } + break; + + case IPT_CUSTOM: + // handle IPT_CUSTOM (mouse right click) + if (!m_populated_favorites) + { + menu::stack_push( + ui(), + container(), + *reinterpret_cast(m_prev_selected), + nullptr); + } + else + { + ui_software_info *sw = reinterpret_cast(m_prev_selected); + ui_system_info const &sys = m_persistent_data.systems()[driver_list::find(sw->driver->name)]; + menu::stack_push( + ui(), + container(), + sys, + [this, empty = sw->startempty] (bool fav, bool changed) + { + if (changed) + reset(empty ? reset_options::SELECT_FIRST : reset_options::REMEMBER_REF); + }); + } + break; + + case IPT_UI_LEFT: + if (ui_globals::rpanel == RP_IMAGES) + { + // Images + previous_image_view(); + } + else if (ui_globals::rpanel == RP_INFOS) + { + // Infos + change_info_pane(-1); + } + break; + + case IPT_UI_RIGHT: + if (ui_globals::rpanel == RP_IMAGES) + { + // Images + next_image_view(); + } + else if (ui_globals::rpanel == RP_INFOS) + { + // Infos + change_info_pane(1); + } + break; + + case IPT_UI_FAVORITES: + if (uintptr_t(ev->itemref) > skip_main_items) + { + favorite_manager &mfav(mame_machine_manager::instance()->favorite()); + if (!m_populated_favorites) + { + auto const &info(*reinterpret_cast(ev->itemref)); + auto const &driver(*info.driver); + if (!mfav.is_favorite_system(driver)) + { + mfav.add_favorite_system(driver); + machine().popmessage(_("%s\n added to favorites list."), info.description); + } + else + { + mfav.remove_favorite_system(driver); + machine().popmessage(_("%s\n removed from favorites list."), info.description); + } + } + else + { + ui_software_info const *const swinfo(reinterpret_cast(ev->itemref)); + machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname); + mfav.remove_favorite_software(*swinfo); + reset(reset_options::SELECT_FIRST); + } + } + break; + + case IPT_UI_AUDIT: + menu::stack_push(ui(), container()); + break; + } + } + } + } + + // if we're in an error state, overlay an error message + draw_error_text(); +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_select_game::populate(float &customtop, float &custombottom) +{ + for (auto &icon : m_icons) // TODO: why is this here? maybe better on resize or setting change? + icon.second.texture.reset(); + + set_switch_image(); + + bool have_prev_selected = false; + int old_item_selected = -1; + if (!isfavorite()) + { + if (m_populated_favorites) + m_prev_selected = nullptr; + m_populated_favorites = false; + m_displaylist.clear(); + machine_filter const *const flt(m_persistent_data.filter_data().get_current_filter()); + + // if search is not empty, find approximate matches + if (!m_search.empty()) + { + populate_search(); + if (flt) + { + for (auto it = m_searchlist.begin(); (m_searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it) + { + if (flt->apply(it->second)) + m_displaylist.emplace_back(it->second); + } + } + else + { + std::transform( + m_searchlist.begin(), + std::next(m_searchlist.begin(), (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH)), + std::back_inserter(m_displaylist), + [] (auto const &entry) { return entry.second; }); + } + } + else + { + // if filter is set on category, build category list + auto const &sorted(m_persistent_data.sorted_list()); + if (!flt) + { + for (ui_system_info const &sysinfo : sorted) + m_displaylist.emplace_back(sysinfo); + } + else + { + for (ui_system_info const &sysinfo : sorted) + { + if (flt->apply(sysinfo)) + m_displaylist.emplace_back(sysinfo); + } + } + } + + // iterate over entries + int curitem = 0; + for (ui_system_info const &elem : m_displaylist) + { + have_prev_selected = have_prev_selected || (&elem == m_prev_selected); + if ((old_item_selected == -1) && (elem.driver->name == reselect_last::driver())) + old_item_selected = curitem; + + item_append(elem.description, elem.is_clone ? FLAG_INVERT : 0, (void *)&elem); + curitem++; + } + } + else + { + // populate favorites list + if (!m_populated_favorites) + m_prev_selected = nullptr; + m_populated_favorites = true; + m_search.clear(); + mame_machine_manager::instance()->favorite().apply_sorted( + [this, &have_prev_selected, &old_item_selected, curitem = 0] (ui_software_info const &info) mutable + { + have_prev_selected = have_prev_selected || (&info == m_prev_selected); + if (info.startempty) + { + if (old_item_selected == -1 && info.shortname == reselect_last::driver()) + old_item_selected = curitem; + + bool cloneof = strcmp(info.driver->parent, "0"); + if (cloneof) + { + int const cx = driver_list::find(info.driver->parent); + if ((0 <= cx) && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) + cloneof = false; + } + + item_append(info.longname, cloneof ? FLAG_INVERT : 0, (void *)&info); + } + else + { + if (old_item_selected == -1 && info.shortname == reselect_last::driver()) + old_item_selected = curitem; + item_append(info.longname, info.devicetype, info.parentname.empty() ? 0 : FLAG_INVERT, (void *)&info); + } + curitem++; + }); + } + + // add special items + if (stack_has_special_main_menu()) + { + item_append(menu_item_type::SEPARATOR, 0); + item_append(_("Configure Options"), 0, (void *)(uintptr_t)CONF_OPTS); + item_append(_("Configure Machine"), 0, (void *)(uintptr_t)CONF_MACHINE); + skip_main_items = 3; + + if (m_prev_selected && !have_prev_selected) + m_prev_selected = item(0).ref(); + } + else + { + skip_main_items = 0; + } + + // configure the custom rendering + customtop = 3.0f * ui().get_line_height() + 5.0f * ui().box_tb_border(); + custombottom = 4.0f * ui().get_line_height() + 3.0f * ui().box_tb_border(); + + // reselect prior game launched, if any + if (old_item_selected != -1) + { + set_selected_index(old_item_selected); + if (ui_globals::visible_main_lines == 0) + top_line = (selected_index() != 0) ? selected_index() - 1 : 0; + else + top_line = selected_index() - (ui_globals::visible_main_lines / 2); + + if (reselect_last::software().empty()) + reselect_last::reset(); + } + else + { + reselect_last::reset(); + } +} + +//------------------------------------------------- +// build a list of available drivers +//------------------------------------------------- + +void menu_select_game::build_available_list() +{ + std::size_t const total = driver_list::total(); + std::vector included(total, false); + + // iterate over ROM directories and look for potential ROMs + file_enumerator path(machine().options().media_path()); + for (osd::directory::entry const *dir = path.next(); dir; dir = path.next()) + { + char drivername[50]; + char *dst = drivername; + char const *src; + + // build a name for it + for (src = dir->name; *src != 0 && *src != '.' && dst < &drivername[std::size(drivername) - 1]; ++src) + *dst++ = tolower(uint8_t(*src)); + + *dst = 0; + int const drivnum = driver_list::find(drivername); + if (0 <= drivnum) + included[drivnum] = true; + } + + // now check and include NONE_NEEDED + if (!ui().options().hide_romless()) + { + // FIXME: can't use the convenience macros tiny ROM entries + auto const is_required_rom = + [] (tiny_rom_entry const &rom) { return ROMENTRY_ISFILE(rom) && !ROM_ISOPTIONAL(rom) && !std::strchr(rom.hashdata, '!'); }; + for (std::size_t x = 0; total > x; ++x) + { + game_driver const &driver(driver_list::driver(x)); + if (!included[x] && (&GAME_NAME(___empty) != &driver)) + { + bool noroms(true); + tiny_rom_entry const *rom; + for (rom = driver.rom; !ROMENTRY_ISEND(rom); ++rom) + { + // check optional and NO_DUMP + if (is_required_rom(*rom)) + { + noroms = false; + break; // break before incrementing, or it will subtly break the check for all ROMs belonging to parent + } + } + + if (!noroms) + { + // check if clone == parent + auto const cx(driver_list::clone(driver)); + if ((0 <= cx) && included[cx]) + { + game_driver const &parent(driver_list::driver(cx)); + if (driver.rom == parent.rom) + { + noroms = true; + } + else + { + // check if clone < parent + noroms = true; + for ( ; noroms && !ROMENTRY_ISEND(rom); ++rom) + { + if (is_required_rom(*rom)) + { + util::hash_collection const hashes(rom->hashdata); + + bool found(false); + for (tiny_rom_entry const *parentrom = parent.rom; !found && !ROMENTRY_ISEND(parentrom); ++parentrom) + { + if (is_required_rom(*parentrom) && (rom->length == parentrom->length)) + { + util::hash_collection const parenthashes(parentrom->hashdata); + if (hashes == parenthashes) + found = true; + } + } + noroms = found; + } + } + } + } + } + + if (noroms) + included[x] = true; + } + } + } + + // copy into the persistent sorted list + for (ui_system_info &info : m_persistent_data.sorted_list()) + info.available = included[info.index]; +} + + +//------------------------------------------------- +// force the game select menu to be visible +// and inescapable +//------------------------------------------------- + +void menu_select_game::force_game_select(mame_ui_manager &mui, render_container &container) +{ + // drop any existing menus and start the system selection menu + menu::stack_reset(mui); + menu::stack_push_special_main(mui, container, nullptr); + mui.show_menu(); + + // make sure MAME is paused + mui.machine().pause(); +} + +//------------------------------------------------- +// handle select key event +//------------------------------------------------- + +void menu_select_game::inkey_select(const event *menu_event) +{ + auto const system = reinterpret_cast(menu_event->itemref); + + if (uintptr_t(system) == CONF_OPTS) + { + // special case for configure options + menu::stack_push( + ui(), + container(), + m_persistent_data.filter_data(), + [this] () { reset(reset_options::SELECT_FIRST); }); + } + else if (uintptr_t(system) == CONF_MACHINE) + { + // special case for configure machine + if (m_prev_selected) + menu::stack_push(ui(), container(), *reinterpret_cast(m_prev_selected)); + return; + } + else + { + // anything else is a driver + driver_enumerator enumerator(machine().options(), *system->driver); + enumerator.next(); + + // if there are software entries, show a software selection menu + for (software_list_device &swlistdev : software_list_device_enumerator(enumerator.config()->root_device())) + { + if (!swlistdev.get_info().empty()) + { + menu::stack_push(ui(), container(), *system); + return; + } + } + + // audit the system ROMs first to see if we're going to work + media_auditor auditor(enumerator); + media_auditor::summary const summary = auditor.audit_media(AUDIT_VALIDATE_FAST); + + // if everything looks good, schedule the new driver + if (audit_passed(summary)) + { + if (!select_bios(*system->driver, false)) + launch_system(*system->driver); + } + else + { + // otherwise, display an error + set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, summary)); + } + } +} + +//------------------------------------------------- +// handle select key event for favorites menu +//------------------------------------------------- + +void menu_select_game::inkey_select_favorite(const event *menu_event) +{ + ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref; + + if ((uintptr_t)ui_swinfo == CONF_OPTS) + { + // special case for configure options + menu::stack_push( + ui(), + container(), + m_persistent_data.filter_data(), + [this] () { reset(reset_options::SELECT_FIRST); }); + } + else if ((uintptr_t)ui_swinfo == CONF_MACHINE) + { + // special case for configure machine + if (m_prev_selected) + { + ui_software_info *swinfo = reinterpret_cast(m_prev_selected); + ui_system_info const &sysinfo = m_persistent_data.systems()[driver_list::find(swinfo->driver->name)]; + menu::stack_push( + ui(), + container(), + sysinfo, + [this, empty = swinfo->startempty] (bool fav, bool changed) + { + if (changed) + reset(empty ? reset_options::SELECT_FIRST : reset_options::REMEMBER_REF); + }); + } + return; + } + else if (ui_swinfo->startempty == 1) + { + driver_enumerator enumerator(machine().options(), *ui_swinfo->driver); + enumerator.next(); + + // if there are software entries, show a software selection menu + for (software_list_device &swlistdev : software_list_device_enumerator(enumerator.config()->root_device())) + { + if (!swlistdev.get_info().empty()) + { + ui_system_info const &system(m_persistent_data.systems()[driver_list::find(ui_swinfo->driver->name)]); + menu::stack_push(ui(), container(), system); + return; + } + } + + // audit the system ROMs first to see if we're going to work + media_auditor auditor(enumerator); + media_auditor::summary const summary = auditor.audit_media(AUDIT_VALIDATE_FAST); + + if (audit_passed(summary)) + { + // if everything looks good, schedule the new driver + if (!select_bios(*ui_swinfo->driver, false)) + { + reselect_last::reselect(true); + launch_system(*ui_swinfo->driver); + } + } + else + { + // otherwise, display an error + set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, summary)); + } + } + else + { + // first audit the system ROMs + driver_enumerator drv(machine().options(), *ui_swinfo->driver); + media_auditor auditor(drv); + drv.next(); + media_auditor::summary const sysaudit = auditor.audit_media(AUDIT_VALIDATE_FAST); + if (!audit_passed(sysaudit)) + { + set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, sysaudit)); + } + else + { + // now audit the software + software_list_device *swlist = software_list_device::find_by_name(*drv.config(), ui_swinfo->listname); + const software_info *swinfo = swlist->find(ui_swinfo->shortname); + + media_auditor::summary const swaudit = auditor.audit_software(*swlist, *swinfo, AUDIT_VALIDATE_FAST); + + if (audit_passed(swaudit)) + { + reselect_last::reselect(true); + if (!select_bios(*ui_swinfo, false) && !select_part(*swinfo, *ui_swinfo)) + launch_system(drv.driver(), *ui_swinfo, ui_swinfo->part); + } + else + { + // otherwise, display an error + set_error(reset_options::REMEMBER_REF, make_software_audit_fail_text(auditor, swaudit)); + } + } + } +} + +//------------------------------------------------- +// returns if the search can be activated +//------------------------------------------------- + +bool menu_select_game::isfavorite() const +{ + return machine_filter::FAVORITE == m_persistent_data.filter_data().get_current_filter_type(); +} + + +//------------------------------------------------- +// change what's displayed in the info box +//------------------------------------------------- + +void menu_select_game::change_info_pane(int delta) +{ + auto const cap_delta = [this, &delta] (uint8_t ¤t, uint8_t &total) + { + if ((0 > delta) && (-delta > current)) + delta = -int(unsigned(current)); + else if ((0 < delta) && ((current + unsigned(delta)) >= total)) + delta = int(unsigned(total - current - 1)); + if (delta) + { + current += delta; + m_topline_datsview = 0; + } + }; + ui_system_info const *sys; + ui_software_info const *soft; + get_selection(soft, sys); + if (!m_populated_favorites) + { + if (uintptr_t(sys) > skip_main_items) + cap_delta(ui_globals::curdats_view, ui_globals::curdats_total); + } + else if (uintptr_t(soft) > skip_main_items) + { + if (soft->startempty) + cap_delta(ui_globals::curdats_view, ui_globals::curdats_total); + else + cap_delta(ui_globals::cur_sw_dats_view, ui_globals::cur_sw_dats_total); + } +} + +//------------------------------------------------- +// populate search list +//------------------------------------------------- + +void menu_select_game::populate_search() +{ + // ensure search list is populated + if (m_searchlist.empty()) + { + auto const &sorted(m_persistent_data.sorted_list()); + m_searchlist.reserve(sorted.size()); + for (ui_system_info const &info : sorted) + m_searchlist.emplace_back(1.0, std::ref(info)); + } + + // keep track of what we matched against + const std::u32string ucs_search(ustr_from_utf8(normalize_unicode(m_search, unicode_normalization_form::D, true))); + + // check available search data + if (m_persistent_data.is_available(system_list::AVAIL_UCS_SHORTNAME)) + m_searched_fields |= system_list::AVAIL_UCS_SHORTNAME; + if (m_persistent_data.is_available(system_list::AVAIL_UCS_DESCRIPTION)) + m_searched_fields |= system_list::AVAIL_UCS_DESCRIPTION; + if (m_persistent_data.is_available(system_list::AVAIL_UCS_MANUF_DESC)) + m_searched_fields |= system_list::AVAIL_UCS_MANUF_DESC; + if (m_persistent_data.is_available(system_list::AVAIL_UCS_DFLT_DESC)) + m_searched_fields |= system_list::AVAIL_UCS_DFLT_DESC; + if (m_persistent_data.is_available(system_list::AVAIL_UCS_MANUF_DFLT_DESC)) + m_searched_fields |= system_list::AVAIL_UCS_MANUF_DFLT_DESC; + + for (std::pair > &info : m_searchlist) + { + info.first = 1.0; + ui_system_info const &sys(info.second); + + // match shortnames + if (m_searched_fields & system_list::AVAIL_UCS_SHORTNAME) + info.first = util::edit_distance(ucs_search, sys.ucs_shortname); + + // match reading + if (info.first && !sys.ucs_reading_description.empty()) + { + info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_reading_description), info.first); + + // match " " + if (info.first) + info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_manufacturer_reading_description), info.first); + } + + // match descriptions + if (info.first && (m_searched_fields & system_list::AVAIL_UCS_DESCRIPTION)) + info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_description), info.first); + + // match " " + if (info.first && (m_searched_fields & system_list::AVAIL_UCS_MANUF_DESC)) + info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_manufacturer_description), info.first); + + // match default description + if (info.first && (m_searched_fields & system_list::AVAIL_UCS_DFLT_DESC) && !sys.ucs_default_description.empty()) + { + info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_default_description), info.first); + + // match " " + if (info.first && (m_searched_fields & system_list::AVAIL_UCS_MANUF_DFLT_DESC)) + info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_manufacturer_default_description), info.first); + } + } + + // sort according to edit distance + std::stable_sort( + m_searchlist.begin(), + m_searchlist.end(), + [] (auto const &lhs, auto const &rhs) { return lhs.first < rhs.first; }); +} + + +//------------------------------------------------- +// get (possibly cached) icon texture +//------------------------------------------------- + +render_texture *menu_select_game::get_icon_texture(int linenum, void *selectedref) +{ + game_driver const *const driver(m_populated_favorites + ? reinterpret_cast(selectedref)->driver + : reinterpret_cast(selectedref)->driver); + assert(driver); + + icon_cache::iterator icon(m_icons.find(driver)); + if ((m_icons.end() == icon) || !icon->second.texture) + { + if (m_icon_paths.empty()) + m_icon_paths = make_icon_paths(nullptr); + + // allocate an entry or allocate a texture on forced redraw + if (m_icons.end() == icon) + { + icon = m_icons.emplace(driver, texture_ptr(machine().render().texture_alloc(), machine().render())).first; + } + else + { + assert(!icon->second.texture); + icon->second.texture.reset(machine().render().texture_alloc()); + } + + // set clone status + bool cloneof = strcmp(driver->parent, "0"); + if (cloneof) + { + auto cx = driver_list::find(driver->parent); + if ((cx >= 0) && (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) + cloneof = false; + } + + bitmap_argb32 tmp; + emu_file snapfile(std::string(m_icon_paths), OPEN_FLAG_READ); + if (!snapfile.open(std::string(driver->name) + ".ico")) + { + render_load_ico_highest_detail(snapfile, tmp); + snapfile.close(); + } + if (!tmp.valid() && cloneof && !snapfile.open(std::string(driver->parent) + ".ico")) + { + render_load_ico_highest_detail(snapfile, tmp); + snapfile.close(); + } + + scale_icon(std::move(tmp), icon->second); + } + + return icon->second.bitmap.valid() ? icon->second.texture.get() : nullptr; +} + + +void menu_select_game::inkey_export() +{ + std::vector list; + if (m_populated_favorites) + { + // iterate over favorites + mame_machine_manager::instance()->favorite().apply( + [&list] (ui_software_info const &info) + { + assert(info.driver); + if (info.startempty) + list.push_back(info.driver); + }); + } + else + { + list.reserve(m_displaylist.size()); + for (ui_system_info const &info : m_displaylist) + list.emplace_back(info.driver); + } + + menu::stack_push(ui(), container(), std::move(list)); +} + +//------------------------------------------------- +// load drivers infos from file +//------------------------------------------------- + +bool menu_select_game::load_available_machines() +{ + // try to load available drivers from file + emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); + if (file.open(std::string(emulator_info::get_configname()) + "_avail.ini")) + return false; + + char rbuf[MAX_CHAR_INFO]; + std::string readbuf; + file.gets(rbuf, MAX_CHAR_INFO); + file.gets(rbuf, MAX_CHAR_INFO); + readbuf = chartrimcarriage(rbuf); + std::string a_rev = string_format("%s%s", UI_VERSION_TAG, bare_build_version); + + // version not matching ? exit + if (a_rev != readbuf) + { + file.close(); + return false; + } + + // load available list + std::unordered_set available; + while (file.gets(rbuf, MAX_CHAR_INFO)) + { + readbuf = strtrimspace(rbuf); + + if (readbuf.empty() || ('#' == readbuf[0])) // ignore empty lines and line comments + ; + else if ('[' == readbuf[0]) // throw out the rest of the file if we find a section heading + break; + else + available.emplace(std::move(readbuf)); + } + file.close(); + + // turn it into the sorted system list we all love + for (ui_system_info &info : m_persistent_data.sorted_list()) + { + std::unordered_set::iterator const it(available.find(&info.driver->name[0])); + bool const found(available.end() != it); + info.available = found; + if (found) + available.erase(it); + } + + return true; +} + +//------------------------------------------------- +// load custom filters info from file +//------------------------------------------------- + +void menu_select_game::load_custom_filters() +{ + emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); + if (!file.open(util::string_format("custom_%s_filter.ini", emulator_info::get_configname()))) + { + machine_filter::ptr flt(machine_filter::create(file, m_persistent_data.filter_data())); + if (flt) + m_persistent_data.filter_data().set_filter(std::move(flt)); // not emplace/insert - could replace bogus filter from ui.ini line + file.close(); + } + +} + + +//------------------------------------------------- +// draw left box +//------------------------------------------------- + +float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2) +{ + machine_filter_data &filter_data(m_persistent_data.filter_data()); + return menu_select_launch::draw_left_panel(filter_data.get_current_filter_type(), filter_data.get_filters(), x1, y1, x2, y2); +} + + +//------------------------------------------------- +// get selected software and/or driver +//------------------------------------------------- + +void menu_select_game::get_selection(ui_software_info const *&software, ui_system_info const *&system) const +{ + if (m_populated_favorites) + { + software = reinterpret_cast(get_selection_ptr()); + system = software ? &m_persistent_data.systems()[driver_list::find(software->driver->name)] : nullptr; + } + else + { + software = nullptr; + system = reinterpret_cast(get_selection_ptr()); + } +} + +void menu_select_game::make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const +{ + line0 = string_format(_("%1$s %2$s ( %3$d / %4$d machines (%5$d BIOS) )"), + emulator_info::get_appname(), + bare_build_version, + m_available_items, + (driver_list::total() - 1), + m_persistent_data.bios_count()); + + if (m_populated_favorites) + { + line1.clear(); + } + else + { + machine_filter const *const it(m_persistent_data.filter_data().get_current_filter()); + char const *const filter(it ? it->filter_text() : nullptr); + if (filter) + line1 = string_format(_("%1$s: %2$s - Search: %3$s_"), it->display_name(), filter, m_search); + else + line1 = string_format(_("Search: %1$s_"), m_search); + } + + line2.clear(); +} + + +std::string menu_select_game::make_software_description(ui_software_info const &software, ui_system_info const *system) const +{ + // first line is system + return string_format(_("System: %1$-.100s"), system->description); +} + + +void menu_select_game::filter_selected() +{ + if ((machine_filter::FIRST <= m_filter_highlight) && (machine_filter::LAST >= m_filter_highlight)) + { + m_persistent_data.filter_data().get_filter(machine_filter::type(m_filter_highlight)).show_ui( + ui(), + container(), + [this] (machine_filter &filter) + { + set_switch_image(); + machine_filter::type const new_type(filter.get_type()); + if (machine_filter::CUSTOM == new_type) + { + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(util::string_format("custom_%s_filter.ini", emulator_info::get_configname()))) + { + filter.save_ini(file, 0); + file.close(); + } + } + m_persistent_data.filter_data().set_current_filter_type(new_type); + reset(reset_options::REMEMBER_REF); + }); + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/selgame.h b/src/icludes/frontend/mame/ui/selgame.h new file mode 100644 index 0000000..4099e24 --- /dev/null +++ b/src/icludes/frontend/mame/ui/selgame.h @@ -0,0 +1,96 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/*************************************************************************** + + ui/selgame.h + + Main UI menu. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_SELGAME_H +#define MAME_FRONTEND_UI_SELGAME_H + +#pragma once + +#include "ui/selmenu.h" +#include "ui/utils.h" + +#include + + +namespace ui { + +class system_list; + + +class menu_select_game : public menu_select_launch +{ +public: + menu_select_game(mame_ui_manager &mui, render_container &container, const char *gamename); + virtual ~menu_select_game(); + + // force game select menu + static void force_game_select(mame_ui_manager &mui, render_container &container); + +protected: + void menu_activated() override; + +private: + enum + { + CONF_OPTS = 1, + CONF_MACHINE, + }; + + using icon_cache = texture_lru; + + system_list &m_persistent_data; + icon_cache m_icons; + std::string m_icon_paths; + std::vector > m_displaylist; + + std::vector > > m_searchlist; + unsigned m_searched_fields; + bool m_populated_favorites; + + static bool s_first_start; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + // drawing + virtual float draw_left_panel(float x1, float y1, float x2, float y2) override; + virtual render_texture *get_icon_texture(int linenum, void *selectedref) override; + + // get selected software and/or driver + virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const override; + virtual bool accept_search() const override { return !isfavorite(); } + + // text for main top/bottom panels + virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const override; + virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const override; + + // filter navigation + virtual void filter_selected() override; + + // toolbar + virtual void inkey_export() override; + + // internal methods + void change_info_pane(int delta); + + void build_available_list(); + + bool isfavorite() const; + void populate_search(); + bool load_available_machines(); + void load_custom_filters(); + + // handlers + void inkey_select(const event *menu_event); + void inkey_select_favorite(const event *menu_event); +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_SELGAME_H diff --git a/src/icludes/frontend/mame/ui/selmenu.cpp b/src/icludes/frontend/mame/ui/selmenu.cpp new file mode 100644 index 0000000..3c16f83 --- /dev/null +++ b/src/icludes/frontend/mame/ui/selmenu.cpp @@ -0,0 +1,3079 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Vas Crabb +/*************************************************************************** + + ui/selmenu.cpp + + MAME system/software selection menu. + +***************************************************************************/ + +#include "emu.h" +#include "ui/selmenu.h" + +#include "ui/datmenu.h" +#include "ui/info.h" +#include "ui/inifile.h" + +// these hold static bitmap images +#include "ui/defimg.ipp" +#include "ui/toolbar.ipp" + +#include "audit.h" +#include "cheat.h" +#include "mame.h" +#include "mameopts.h" + +#include "corestr.h" +#include "drivenum.h" +#include "emuopts.h" +#include "rendfont.h" +#include "rendutil.h" +#include "romload.h" +#include "softlist.h" +#include "softlist_dev.h" +#include "uiinput.h" +#include "luaengine.h" + +#include "util/nanosvg.h" +#include "util/path.h" + +#include +#include +#include +#include + + +namespace ui { + +namespace { + +enum +{ + FIRST_VIEW = 0, + SNAPSHOT_VIEW = FIRST_VIEW, + CABINETS_VIEW, + CPANELS_VIEW, + PCBS_VIEW, + FLYERS_VIEW, + TITLES_VIEW, + ENDS_VIEW, + ARTPREV_VIEW, + BOSSES_VIEW, + LOGOS_VIEW, + VERSUS_VIEW, + GAMEOVER_VIEW, + HOWTO_VIEW, + SCORES_VIEW, + SELECT_VIEW, + MARQUEES_VIEW, + COVERS_VIEW, + LAST_VIEW = COVERS_VIEW +}; + +std::pair const arts_info[] = +{ + { N_p("selmenu-artwork", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY }, + { N_p("selmenu-artwork", "Cabinet"), OPTION_CABINETS_PATH }, + { N_p("selmenu-artwork", "Control Panel"), OPTION_CPANELS_PATH }, + { N_p("selmenu-artwork", "PCB"), OPTION_PCBS_PATH }, + { N_p("selmenu-artwork", "Flyer"), OPTION_FLYERS_PATH }, + { N_p("selmenu-artwork", "Title Screen"), OPTION_TITLES_PATH }, + { N_p("selmenu-artwork", "Ending"), OPTION_ENDS_PATH }, + { N_p("selmenu-artwork", "Artwork Preview"), OPTION_ARTPREV_PATH }, + { N_p("selmenu-artwork", "Bosses"), OPTION_BOSSES_PATH }, + { N_p("selmenu-artwork", "Logo"), OPTION_LOGOS_PATH }, + { N_p("selmenu-artwork", "Versus"), OPTION_VERSUS_PATH }, + { N_p("selmenu-artwork", "Game Over"), OPTION_GAMEOVER_PATH }, + { N_p("selmenu-artwork", "HowTo"), OPTION_HOWTO_PATH }, + { N_p("selmenu-artwork", "Scores"), OPTION_SCORES_PATH }, + { N_p("selmenu-artwork", "Select"), OPTION_SELECT_PATH }, + { N_p("selmenu-artwork", "Marquee"), OPTION_MARQUEES_PATH }, + { N_p("selmenu-artwork", "Covers"), OPTION_COVER_PATH }, +}; + +char const *const hover_msg[] = { + N_("Add or remove favorite"), + N_("Export displayed list to file"), + N_("Audit media"), + N_("Show DATs view"), +}; + + +void load_image(bitmap_argb32 &bitmap, emu_file &file, std::string const &base) +{ + if (!file.open(base + ".png")) + { + render_load_png(bitmap, file); + file.close(); + } + + if (!bitmap.valid() && !file.open(base + ".jpg")) + { + render_load_jpeg(bitmap, file); + file.close(); + } + + if (!bitmap.valid() && !file.open(base + ".bmp")) + { + render_load_msdib(bitmap, file); + file.close(); + } +} + + +void load_driver_image(bitmap_argb32 &bitmap, emu_file &file, game_driver const &driver) +{ + // try to load snapshot first from saved "0000.png" file + std::string fullname = driver.name; + load_image(bitmap, file, util::path_concat(fullname, "0000")); + + // if fail, attempt to load from standard file + if (!bitmap.valid()) + load_image(bitmap, file, fullname); + + // if fail again, attempt to load from parent file + if (!bitmap.valid()) + { + // ignore BIOS sets + bool isclone = strcmp(driver.parent, "0") != 0; + if (isclone) + { + int const cx = driver_list::find(driver.parent); + if ((0 <= cx) && (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) + isclone = false; + } + + if (isclone) + { + fullname = driver.parent; + load_image(bitmap, file, util::path_concat(fullname, "0000")); + + if (!bitmap.valid()) + load_image(bitmap, file, fullname); + } + } +} + +} // anonymous namespace + +constexpr std::size_t menu_select_launch::MAX_VISIBLE_SEARCH; // stupid non-inline semantics + + +class menu_select_launch::software_parts : public menu +{ +public: + software_parts(mame_ui_manager &mui, render_container &container, s_parts &&parts, ui_software_info const &ui_info); + virtual ~software_parts() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + ui_software_info const &m_uiinfo; + s_parts const m_parts; +}; + +class menu_select_launch::bios_selection : public menu +{ +public: + bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, game_driver const &driver, bool inlist); + bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, ui_software_info const &swinfo, bool inlist); + virtual ~bios_selection() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + +private: + bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, void const *driver, bool software, bool inlist); + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + void const *m_driver; + bool m_software, m_inlist; + s_bios m_bios; +}; + +std::string menu_select_launch::reselect_last::s_driver; +std::string menu_select_launch::reselect_last::s_software; +std::string menu_select_launch::reselect_last::s_swlist; +bool menu_select_launch::reselect_last::s_reselect = false; + +// instantiate possible variants of these so derived classes don't get link errors +template bool menu_select_launch::select_bios(game_driver const &, bool); +template bool menu_select_launch::select_bios(ui_software_info const &, bool); +template float menu_select_launch::draw_left_panel(machine_filter::type current, std::map const &filters, float x1, float y1, float x2, float y2); +template float menu_select_launch::draw_left_panel(software_filter::type current, std::map const &filters, float x1, float y1, float x2, float y2); + + +menu_select_launch::system_flags::system_flags(machine_static_info const &info) + : m_machine_flags(info.machine_flags()) + , m_unemulated_features(info.unemulated_features()) + , m_imperfect_features(info.imperfect_features()) + , m_has_keyboard(info.has_keyboard()) + , m_has_analog(info.has_analog()) + , m_status_color(info.status_color()) +{ +} + + +void menu_select_launch::reselect_last::reset() +{ + s_driver.clear(); + s_software.clear(); + s_swlist.clear(); + reselect(false); +} + +void menu_select_launch::reselect_last::set_driver(std::string const &name) +{ + s_driver = name; + s_software.clear(); + s_swlist.clear(); +} + +void menu_select_launch::reselect_last::set_software(game_driver const &driver, ui_software_info const &swinfo) +{ + s_driver = driver.name; + if (swinfo.startempty) + { + // FIXME: magic strings are bad... + s_software = "[Start empty]"; + s_swlist.clear(); + } + else + { + s_software = swinfo.shortname; + s_swlist = swinfo.listname; + } +} + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_select_launch::software_parts::software_parts(mame_ui_manager &mui, render_container &container, s_parts &&parts, ui_software_info const &ui_info) + : menu(mui, container) + , m_uiinfo(ui_info) + , m_parts(std::move(parts)) +{ +} + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_select_launch::software_parts::~software_parts() +{ +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_select_launch::software_parts::populate(float &customtop, float &custombottom) +{ + std::vector parts; + parts.reserve(m_parts.size()); + for (s_parts::const_iterator it = m_parts.begin(); m_parts.end() != it; ++it) + parts.push_back(it); + std::sort(parts.begin(), parts.end(), [] (auto const &left, auto const &right) { return 0 > core_stricmp(left->first.c_str(), right->first.c_str()); }); + for (auto const &elem : parts) + item_append(elem->first, elem->second, 0, (void *)&*elem); + + item_append(menu_item_type::SEPARATOR); + customtop = ui().get_line_height() + (3.0f * ui().box_tb_border()); +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_select_launch::software_parts::handle(event const *ev) +{ + // process the menu + if (ev && (ev->iptkey == IPT_UI_SELECT) && ev->itemref) + { + for (auto const &elem : m_parts) + { + if ((void*)&elem == ev->itemref) + { + launch_system(ui(), *m_uiinfo.driver, &m_uiinfo, &elem.first, nullptr); + break; + } + } + } +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_select_launch::software_parts::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + char const *const text[] = { _("Software part selection:") }; + draw_text_box( + std::begin(text), std::end(text), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_select_launch::bios_selection::bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, game_driver const &driver, bool inlist) + : bios_selection(mui, container, std::move(biosname), reinterpret_cast(&driver), false, inlist) +{ +} + +menu_select_launch::bios_selection::bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, ui_software_info const &swinfo, bool inlist) + : bios_selection(mui, container, std::move(biosname), reinterpret_cast(&swinfo), true, inlist) +{ +} + +menu_select_launch::bios_selection::bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, void const *driver, bool software, bool inlist) + : menu(mui, container) + , m_driver(driver) + , m_software(software) + , m_inlist(inlist) + , m_bios(std::move(biosname)) +{ +} + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_select_launch::bios_selection::~bios_selection() +{ +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_select_launch::bios_selection::populate(float &customtop, float &custombottom) +{ + for (auto & elem : m_bios) + item_append(elem.first, 0, (void *)&elem.first); + + item_append(menu_item_type::SEPARATOR); + customtop = ui().get_line_height() + (3.0f * ui().box_tb_border()); +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_select_launch::bios_selection::handle(event const *ev) +{ + // process the menu + if (ev && (ev->iptkey == IPT_UI_SELECT) && ev->itemref) + { + for (auto & elem : m_bios) + { + if ((void*)&elem.first == ev->itemref) + { + if (!m_software) + { + const game_driver *s_driver = (const game_driver *)m_driver; + if (m_inlist) + { + ui_software_info empty(*s_driver); + launch_system(ui(), *s_driver, &empty, nullptr, &elem.second); + } + else + { + reselect_last::reselect(true); + launch_system(ui(), *s_driver, nullptr, nullptr, &elem.second); + } + } + else + { + ui_software_info *ui_swinfo = (ui_software_info *)m_driver; + machine().options().set_value(OPTION_BIOS, elem.second, OPTION_PRIORITY_CMDLINE); // oh dear, relying on this persisting through the part selection menu + driver_enumerator drivlist(machine().options(), *ui_swinfo->driver); + drivlist.next(); + software_list_device *swlist = software_list_device::find_by_name(*drivlist.config(), ui_swinfo->listname); + const software_info *swinfo = swlist->find(ui_swinfo->shortname); + if (!select_part(ui(), container(), *swinfo, *ui_swinfo)) + { + reselect_last::reselect(true); + launch_system(ui(), drivlist.driver(), ui_swinfo, nullptr, &elem.second); + } + } + } + } + } +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_select_launch::bios_selection::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + char const *const text[] = { _("BIOS selection:") }; + draw_text_box( + std::begin(text), std::end(text), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + + +menu_select_launch::cache::cache(running_machine &machine) + : m_snapx_bitmap(std::make_unique(0, 0)) + , m_snapx_texture(nullptr, machine.render()) + , m_snapx_driver(nullptr) + , m_snapx_software(nullptr) + , m_no_avail_bitmap(256, 256) + , m_toolbar_bitmaps() + , m_toolbar_textures() +{ + render_manager &render(machine.render()); + + // create a texture for snapshot + m_snapx_texture.reset(render.texture_alloc(render_texture::hq_scale)); + + std::memcpy(&m_no_avail_bitmap.pix(0), no_avail_bmp, 256 * 256 * sizeof(uint32_t)); + + m_toolbar_bitmaps.resize(UI_TOOLBAR_BUTTONS); + m_toolbar_textures.reserve(UI_TOOLBAR_BUTTONS); +} + + +menu_select_launch::cache::~cache() +{ +} + + +void menu_select_launch::cache::cache_toolbar(running_machine &machine, float width, float height) +{ + // not bothering to transform for non-square pixels greatly simplifies this + render_manager &render(machine.render()); + render_target const &target(render.ui_target()); + int32_t const pix_size(std::ceil(std::max(width * target.width(), height * target.height()))); + if (m_toolbar_textures.empty() || (m_toolbar_bitmaps[0].width() != pix_size) || (m_toolbar_bitmaps[0].height() != pix_size)) + { + m_toolbar_textures.clear(); + util::nsvg_rasterizer_ptr rasterizer(nsvgCreateRasterizer()); + std::string xml; + for (unsigned i = 0; UI_TOOLBAR_BUTTONS > i; ++i) + { + // parse SVG and calculate scale + xml = toolbar_icons_svg[i]; + util::nsvg_image_ptr svg(nsvgParse(xml.data(), "px", 72)); + float const xscale(float(pix_size) / svg->width); + float const yscale(float(pix_size) / svg->height); + float const drawscale((std::max)(xscale, yscale)); + + // rasterise the SVG and clear it out of memory + bitmap_argb32 &bitmap(m_toolbar_bitmaps[i]); + bitmap.resize(pix_size, pix_size); + nsvgRasterize( + rasterizer.get(), + svg.get(), + 0, 0, drawscale, + reinterpret_cast(&bitmap.pix(0)), + pix_size, pix_size, + bitmap.rowbytes()); + svg.reset(); + + // correct colour format + for (int32_t y = 0; bitmap.height() > y; ++y) + { + uint32_t *dst(&bitmap.pix(y)); + for (int32_t x = 0; bitmap.width() > x; ++x, ++dst) + { + u8 const *const src(reinterpret_cast(dst)); + rgb_t const d(src[3], src[0], src[1], src[2]); + *dst = d; + } + } + + // make a texture + render_texture &texture(*m_toolbar_textures.emplace_back(render.texture_alloc(), render)); + texture.set_bitmap(bitmap, bitmap.cliprect(), TEXFORMAT_ARGB32); + } + } +} + + +menu_select_launch::~menu_select_launch() +{ +} + + +menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &container, bool is_swlist) + : menu(mui, container) + , m_prev_selected(nullptr) + , m_total_lines(0) + , m_topline_datsview(0) + , m_filter_highlight(0) + , m_ui_error(false) + , m_info_driver(nullptr) + , m_info_software(nullptr) + , m_info_view(-1) + , m_items_list() + , m_info_buffer() + , m_info_layout() + , m_cache(mui.get_session_data(machine())) + , m_is_swlist(is_swlist) + , m_focus(focused_menu::MAIN) + , m_pressed(false) + , m_repeat(0) + , m_right_visible_lines(0) + , m_has_icons(false) + , m_switch_image(false) + , m_default_image(true) + , m_image_view(FIRST_VIEW) + , m_flags(256) +{ + set_needs_prev_menu_item(false); + set_process_flags(PROCESS_LR_REPEAT); +} + + +void menu_select_launch::next_image_view() +{ + if (LAST_VIEW > m_image_view) + { + ++m_image_view; + set_switch_image(); + m_default_image = false; + } +} + + +void menu_select_launch::previous_image_view() +{ + if (FIRST_VIEW < m_image_view) + { + --m_image_view; + set_switch_image(); + m_default_image = false; + } +} + + +bool menu_select_launch::dismiss_error() +{ + bool const result = m_ui_error; + if (result) + { + m_ui_error = false; + m_error_text.clear(); + machine().ui_input().reset(); + } + return result; +} + +void menu_select_launch::set_error(reset_options ropt, std::string &&message) +{ + reset(ropt); + m_ui_error = true; + m_error_text = std::move(message); +} + + +//------------------------------------------------- +// get overall emulation status for a system +//------------------------------------------------- + +menu_select_launch::system_flags const &menu_select_launch::get_system_flags(game_driver const &driver) +{ + // try the cache + flags_cache::const_iterator const found(m_flags.find(&driver)); + if (m_flags.end() != found) + return found->second; + + // aggregate flags + emu_options clean_options; + machine_config const mconfig(driver, clean_options); + return m_flags.emplace(&driver, machine_static_info(ui().options(), mconfig)).first->second; +} + + +//------------------------------------------------- +// actually start an emulation session +//------------------------------------------------- + +void menu_select_launch::launch_system(mame_ui_manager &mui, game_driver const &driver, ui_software_info const *swinfo, std::string const *part, int const *bios) +{ + emu_options &moptions(mui.machine().options()); + moptions.set_system_name(driver.name); + + if (swinfo) + { + if (!swinfo->startempty) + { + if (part) + moptions.set_value(swinfo->instance, util::string_format("%s:%s:%s", swinfo->listname, swinfo->shortname, *part), OPTION_PRIORITY_CMDLINE); + else + moptions.set_value(OPTION_SOFTWARENAME, util::string_format("%s:%s", swinfo->listname, swinfo->shortname), OPTION_PRIORITY_CMDLINE); + + moptions.set_value(OPTION_SNAPNAME, util::path_concat(swinfo->listname, swinfo->shortname), OPTION_PRIORITY_CMDLINE); + } + reselect_last::set_software(driver, *swinfo); + } + else + { + reselect_last::set_driver(driver); + } + + if (bios) + moptions.set_value(OPTION_BIOS, *bios, OPTION_PRIORITY_CMDLINE); + + mame_machine_manager::instance()->schedule_new_driver(driver); + mui.machine().schedule_hard_reset(); + stack_reset(mui); +} + + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_select_launch::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + std::string tempbuf[4]; + + // determine the text for the header + make_topbox_text(tempbuf[0], tempbuf[1], tempbuf[2]); + float const y1 = origy1 - 3.0f * ui().box_tb_border() - ui().get_line_height(); + draw_text_box( + tempbuf, tempbuf + 3, + origx1, origx2, origy1 - top, y1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, true, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); + + // draw toolbar + draw_toolbar(origx1, y1, origx2, origy1 - ui().box_tb_border()); + + // determine the text to render below + ui_software_info const *swinfo; + ui_system_info const *system; + get_selection(swinfo, system); + + bool isstar = false; + rgb_t color = ui().colors().background_color(); + if (swinfo && !swinfo->startempty) + { + isstar = mame_machine_manager::instance()->favorite().is_favorite_system_software(*swinfo); + + // first line is long name or system + tempbuf[0] = make_software_description(*swinfo, system); + + // next line is year, publisher + tempbuf[1] = string_format(_("%1$s, %2$s"), swinfo->year, swinfo->publisher); + + // next line is parent/clone + if (!swinfo->parentname.empty()) + tempbuf[2] = string_format(_("Software is clone of: %1$s"), !swinfo->parentlongname.empty() ? swinfo->parentlongname : swinfo->parentname); + else + tempbuf[2] = _("Software is parent"); + + // next line is supported status + if (swinfo->supported == software_support::UNSUPPORTED) + { + tempbuf[3] = _("Supported: No"); + color = UI_RED_COLOR; + } + else if (swinfo->supported == software_support::PARTIALLY_SUPPORTED) + { + tempbuf[3] = _("Supported: Partial"); + color = UI_YELLOW_COLOR; + } + else + { + tempbuf[3] = _("Supported: Yes"); + color = UI_GREEN_COLOR; + } + } + else if (system || (swinfo && swinfo->driver)) + { + game_driver const &driver(system ? *system->driver : *swinfo->driver); + isstar = mame_machine_manager::instance()->favorite().is_favorite_system(driver); + + // first line is year, manufacturer + tempbuf[0] = string_format(_("%1$s, %2$s"), driver.year, driver.manufacturer); + + // next line is clone/parent status + int cloneof = driver_list::non_bios_clone(driver); + + if (0 > cloneof) + tempbuf[1] = _("Driver is parent"); + else if (system) + tempbuf[1] = string_format(_("Driver is clone of: %1$s"), system->parent); + else + tempbuf[1] = string_format(_("Driver is clone of: %1$s"), driver_list::driver(cloneof).type.fullname()); + + // next line is overall driver status + system_flags const &flags(get_system_flags(driver)); + if (flags.machine_flags() & machine_flags::NOT_WORKING) + tempbuf[2] = _("Overall: NOT WORKING"); + else if ((flags.unemulated_features() | flags.imperfect_features()) & device_t::feature::PROTECTION) + tempbuf[2] = _("Overall: Unemulated Protection"); + else + tempbuf[2] = _("Overall: Working"); + + // next line is graphics, sound status + if (flags.unemulated_features() & device_t::feature::GRAPHICS) + tempbuf[3] = _("Graphics: Unimplemented, "); + else if ((flags.unemulated_features() | flags.imperfect_features()) & (device_t::feature::GRAPHICS | device_t::feature::PALETTE)) + tempbuf[3] = _("Graphics: Imperfect, "); + else + tempbuf[3] = _("Graphics: OK, "); + + if (driver.flags & machine_flags::NO_SOUND_HW) + tempbuf[3].append(_("Sound: None")); + else if (flags.unemulated_features() & device_t::feature::SOUND) + tempbuf[3].append(_("Sound: Unimplemented")); + else if (flags.imperfect_features() & device_t::feature::SOUND) + tempbuf[3].append(_("Sound: Imperfect")); + else + tempbuf[3].append(_("Sound: OK")); + + color = flags.status_color(); + } + else + { + std::string copyright(emulator_info::get_copyright()); + size_t found1 = copyright.find_first_of('\n'); + size_t found2 = copyright.find_last_of('\n'); + + tempbuf[0] = string_format(_("%1$s %2$s"), emulator_info::get_appname(), build_version); + tempbuf[1] = copyright.substr(0, found1); + tempbuf[2] = copyright.substr(found1 + 1, found2 - (found1 + 1)); + tempbuf[3] = copyright.substr(found2 + 1); + } + + // draw the footer + draw_text_box( + std::begin(tempbuf), std::end(tempbuf), + origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, true, + ui().colors().text_color(), color, 1.0f); + + // is favorite? draw the star + if (isstar) + draw_star(origx1 + ui().box_lr_border() * machine().render().ui_aspect(&container()), origy2 + (2.0f * ui().box_tb_border())); +} + + +void menu_select_launch::rotate_focus(int dir) +{ + switch (get_focus()) + { + case focused_menu::MAIN: + if (selected_index() <= m_available_items) + { + if (skip_main_items || (ui_globals::panels_status != HIDE_BOTH)) + { + m_prev_selected = get_selection_ref(); + if ((0 < dir) || (ui_globals::panels_status == HIDE_BOTH)) + set_selected_index(m_available_items + 1); + else if (ui_globals::panels_status == HIDE_RIGHT_PANEL) + set_focus(focused_menu::LEFT); + else + set_focus(focused_menu::RIGHTBOTTOM); + } + } + else + { + if ((0 > dir) || (ui_globals::panels_status == HIDE_BOTH)) + select_prev(); + else if (ui_globals::panels_status == HIDE_LEFT_PANEL) + set_focus(focused_menu::RIGHTTOP); + else + set_focus(focused_menu::LEFT); + } + break; + + case focused_menu::LEFT: + if (0 > dir) + { + set_focus(focused_menu::MAIN); + if (skip_main_items) + set_selected_index(m_available_items + 1); + else + select_prev(); + } + else if (ui_globals::panels_status != HIDE_RIGHT_PANEL) + { + set_focus(focused_menu::RIGHTTOP); + } + else + { + set_focus(focused_menu::MAIN); + select_prev(); + } + break; + + case focused_menu::RIGHTTOP: + if (0 < dir) + { + set_focus(focused_menu::RIGHTBOTTOM); + } + else if (ui_globals::panels_status != HIDE_LEFT_PANEL) + { + set_focus(focused_menu::LEFT); + } + else + { + set_focus(focused_menu::MAIN); + set_selected_index(m_available_items + 1); + } + break; + + case focused_menu::RIGHTBOTTOM: + if (0 > dir) + { + set_focus(focused_menu::RIGHTTOP); + } + else + { + set_focus(focused_menu::MAIN); + select_prev(); + } + break; + } +} + + +void menu_select_launch::inkey_dats() +{ + ui_software_info const *software; + ui_system_info const *system; + get_selection(software, system); + if (software && !software->startempty) + menu::stack_push(ui(), container(), *software); + else if (system) + menu::stack_push(ui(), container(), system); +} + + +//------------------------------------------------- +// draw common arrows +//------------------------------------------------- + +void menu_select_launch::draw_common_arrow(float origx1, float origy1, float origx2, float origy2, int current, int dmin, int dmax, float title_size) +{ + auto line_height = ui().get_line_height(); + auto lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect(&container()); + auto gutter_width = lr_arrow_width * 1.3f; + + // set left-right arrows dimension + float const ar_x0 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width - lr_arrow_width; + float const ar_y0 = origy1 + 0.1f * line_height; + float const ar_x1 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width; + float const ar_y1 = origy1 + 0.9f * line_height; + + float const al_x0 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width; + float const al_y0 = origy1 + 0.1f * line_height; + float const al_x1 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width + lr_arrow_width; + float const al_y1 = origy1 + 0.9f * line_height; + + rgb_t fgcolor_right, fgcolor_left; + fgcolor_right = fgcolor_left = ui().colors().text_color(); + + // set hover + if (mouse_in_rect(ar_x0, ar_y0, ar_x1, ar_y1) && current != dmax) + { + ui().draw_textured_box(container(), ar_x0 + 0.01f, ar_y0, ar_x1 - 0.01f, ar_y1, ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + set_hover(HOVER_UI_RIGHT); + fgcolor_right = ui().colors().mouseover_color(); + } + else if (mouse_in_rect(al_x0, al_y0, al_x1, al_y1) && current != dmin) + { + ui().draw_textured_box(container(), al_x0 + 0.01f, al_y0, al_x1 - 0.01f, al_y1, ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + set_hover(HOVER_UI_LEFT); + fgcolor_left = ui().colors().mouseover_color(); + } + + // apply arrow + if (dmax == dmin) + return; + else if (current == dmin) + draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, ROT90); + else if (current == dmax) + draw_arrow(al_x0, al_y0, al_x1, al_y1, fgcolor_left, ROT90 ^ ORIENTATION_FLIP_X); + else + { + draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, ROT90); + draw_arrow(al_x0, al_y0, al_x1, al_y1, fgcolor_left, ROT90 ^ ORIENTATION_FLIP_X); + } +} + + +//------------------------------------------------- +// draw info arrow +//------------------------------------------------- + +void menu_select_launch::draw_info_arrow(int ub, float origx1, float origx2, float oy1, float line_height, float text_size, float ud_arrow_width) +{ + rgb_t fgcolor = ui().colors().text_color(); + uint32_t orientation = (!ub) ? ROT0 : ROT0 ^ ORIENTATION_FLIP_Y; + + if (mouse_in_rect(origx1, oy1, origx2, oy1 + (line_height * text_size))) + { + ui().draw_textured_box(container(), origx1 + 0.01f, oy1, origx2 - 0.01f, oy1 + (line_height * text_size), ui().colors().mouseover_bg_color(), + rgb_t(43, 43, 43), hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + set_hover((!ub) ? HOVER_DAT_UP : HOVER_DAT_DOWN); + fgcolor = ui().colors().mouseover_color(); + } + + draw_arrow(0.5f * (origx1 + origx2) - 0.5f * (ud_arrow_width * text_size), oy1 + 0.25f * (line_height * text_size), + 0.5f * (origx1 + origx2) + 0.5f * (ud_arrow_width * text_size), oy1 + 0.75f * (line_height * text_size), fgcolor, orientation); +} + +bool menu_select_launch::draw_error_text() +{ + if (m_ui_error) + ui().draw_text_box(container(), m_error_text, text_layout::text_justify::CENTER, 0.5f, 0.5f, UI_RED_COLOR); + + return m_ui_error; +} + + +template +float menu_select_launch::draw_left_panel( + typename Filter::type current, + std::map const &filters, + float x1, float y1, float x2, float y2) +{ + if ((ui_globals::panels_status != SHOW_PANELS) && (ui_globals::panels_status != HIDE_RIGHT_PANEL)) + return draw_collapsed_left_panel(x1, y1, x2, y2); + + // calculate line height + float const line_height(ui().get_line_height()); + float const text_size(ui().options().infos_size()); + float const sc(y2 - y1 - (2.0f * ui().box_tb_border())); + float line_height_max(line_height * text_size); + if ((Filter::COUNT * line_height_max) > sc) + { + float const lm(sc / Filter::COUNT); + line_height_max = line_height * (lm / line_height); + } + + // calculate horizontal offset for unadorned names + std::string tmp(convert_command_glyph("_# ")); + float const text_sign = ui().get_string_width(tmp, text_size); + + // get the maximum width of a filter name + float left_width(0.0f); + for (typename Filter::type x = Filter::FIRST; Filter::COUNT > x; ++x) + left_width = std::max(ui().get_string_width(Filter::display_name(x), text_size) + text_sign, left_width); + + // outline the box and inset by the border width + float const origy1(y1); + float const origy2(y2); + float const aspect(machine().render().ui_aspect(&container())); + float const lr_border(ui().box_lr_border() * aspect); + x2 = x1 + left_width + 2.0f * lr_border; + ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color()); + x1 += lr_border; + x2 -= lr_border; + y1 += ui().box_tb_border(); + y2 -= ui().box_tb_border(); + + // now draw the rows + auto const active_filter(filters.find(current)); + for (typename Filter::type filter = Filter::FIRST; Filter::COUNT > filter; ++filter) + { + std::string str; + if (filters.end() != active_filter) + { + str = active_filter->second->adorned_display_name(filter); + } + else + { + if (current == filter) + str = convert_command_glyph("_> "); + str.append(Filter::display_name(filter)); + } + + // handle mouse hover in passing + rgb_t bgcolor = ui().colors().text_bg_color(); + rgb_t fgcolor = ui().colors().text_color(); + if (mouse_in_rect(x1, y1, x2, y1 + line_height_max)) + { + bgcolor = ui().colors().mouseover_bg_color(); + fgcolor = ui().colors().mouseover_color(); + set_hover(HOVER_FILTER_FIRST + filter); + highlight(x1, y1, x2, y1 + line_height_max, bgcolor); + } + + // draw primary highlight if keyboard focus is here + if ((m_filter_highlight == filter) && (get_focus() == focused_menu::LEFT)) + { + fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00); + bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff); + ui().draw_textured_box( + container(), + x1, y1, x2, y1 + line_height_max, + bgcolor, rgb_t(255, 43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + + // finally draw the text itself and move to the next line + float const x1t = x1 + ((str == Filter::display_name(filter)) ? text_sign : 0.0f); + ui().draw_text_full( + container(), str, + x1t, y1, x2 - x1, + text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, fgcolor, bgcolor, + nullptr, nullptr, text_size); + y1 += line_height_max; + } + + x1 = x2 + lr_border; + x2 = x1 + 2.0f * lr_border; + y1 = origy1; + y2 = origy2; + float const space = x2 - x1; + float const lr_arrow_width = 0.4f * space * aspect; + + // set left-right arrows dimension + float const ar_x0 = 0.5f * (x2 + x1) - 0.5f * lr_arrow_width; + float const ar_y0 = 0.5f * (y2 + y1) + 0.1f * space; + float const ar_x1 = ar_x0 + lr_arrow_width; + float const ar_y1 = 0.5f * (y2 + y1) + 0.9f * space; + + ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xef, 0x12, 0x47, 0x7b)); + + rgb_t fgcolor = ui().colors().text_color(); + if (mouse_in_rect(x1, y1, x2, y2)) + { + fgcolor = ui().colors().mouseover_color(); + set_hover(HOVER_LPANEL_ARROW); + } + + draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X); + return x2 + lr_border; +} + + +//------------------------------------------------- +// icon helpers +//------------------------------------------------- + +void menu_select_launch::check_for_icons(char const *listname) +{ + // only ever set the flag, never clear it + if (m_has_icons) + return; + + // iterate over configured icon paths + path_iterator paths(ui().options().icons_directory()); + std::string current; + while (paths.next(current)) + { + // if we're doing a software list, append it to the configured path + if (listname) + util::path_append(current, listname); + osd_printf_verbose("Checking for icons in directory %s\n", current); + + // open and walk the directory + osd::directory::ptr const dir(osd::directory::open(current)); + if (dir) + { + // this could be improved in many ways - it's just a rough go/no-go + osd::directory::entry const *entry; + while ((entry = dir->read()) != nullptr) + { + current = entry->name; + std::string::size_type const found(current.rfind(".ico")); + if ((std::string::npos != found) && ((current.length() - 4) == found)) + { + osd_printf_verbose("Entry %s is a candidate icon file\n", entry->name); + m_has_icons = true; + return; + } + else if (("icons" == current) || (current.find("icons.") == 0U)) + { + osd_printf_verbose("Entry %s is a candidate icon collection\n", entry->name); + m_has_icons = true; + return; + } + } + } + } + + // nothing promising + osd_printf_verbose( + "No candidate icons found for %s%s\n", + listname ? "software list " : "", + listname ? listname : "machines"); +} + +std::string menu_select_launch::make_icon_paths(char const *listname) const +{ + // iterate over configured icon paths + path_iterator paths(ui().options().icons_directory()); + std::string current, result; + while (paths.next(current)) + { + // if we're doing a software list, append it to the configured path + if (listname) + util::path_append(current, listname); + + // append the configured path + if (!result.empty()) + result.append(1, ';'); // FIXME: should be a macro + result.append(current); + + // append with "icons" appended so it'll search icons.zip or icons.7z in the directory + if (!current.empty()) + { + result.append(1, ';'); // FIXME: should be a macro + result.append(current); + if (!util::is_directory_separator(result.back())) + result.append(PATH_SEPARATOR); + } + result.append("icons"); + } + + // log the result for debugging + osd_printf_verbose( + "Icon path for %s%s set to %s\n", + listname ? "software list " : "", + listname ? listname : "machines", + result.c_str()); + return result; +} + +bool menu_select_launch::scale_icon(bitmap_argb32 &&src, texture_and_bitmap &dst) const +{ + assert(dst.texture); + if (src.valid()) + { + // calculate available space for the icon in pixels + float const height(ui().get_line_height()); + float const width(height * container().manager().ui_aspect(&container())); + render_target const &target(machine().render().ui_target()); + uint32_t const dst_height(target.height()); + uint32_t const dst_width(target.width()); + bool const rotated((target.orientation() & ORIENTATION_SWAP_XY) != 0); + int const max_height(int((rotated ? dst_width : dst_height) * height)); + int const max_width(int((rotated ? dst_height : dst_width) * width)); + + // reduce the source bitmap if it's too big + bitmap_argb32 tmp; + float const ratio((std::min)({ float(max_height) / src.height(), float(max_width) / src.width(), 1.0F })); + if (1.0F > ratio) + { + float const pix_height(std::ceil(src.height() * ratio)); + float const pix_width(std::ceil(src.width() * ratio)); + tmp.allocate(int32_t(pix_width), int32_t(pix_height)); + render_resample_argb_bitmap_hq(tmp, src, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true); + } + else + { + tmp = std::move(src); + } + + // copy into the destination + dst.bitmap.allocate(max_width, max_height); + for (int y = 0; tmp.height() > y; ++y) + for (int x = 0; tmp.width() > x; ++x) + dst.bitmap.pix(y, x) = tmp.pix(y, x); + dst.texture->set_bitmap(dst.bitmap, dst.bitmap.cliprect(), TEXFORMAT_ARGB32); + return true; + } + else + { + // couldn't load icon + dst.bitmap.reset(); + return false; + } +} + + +template bool menu_select_launch::select_bios(T const &driver, bool inlist) +{ + s_bios biosname; + if (ui().options().skip_bios_menu() || !has_multiple_bios(driver, biosname)) + return false; + + menu::stack_push(ui(), container(), std::move(biosname), driver, inlist); + return true; +} + +bool menu_select_launch::select_part(software_info const &info, ui_software_info const &ui_info) +{ + return select_part(ui(), container(), info, ui_info); +} + +bool menu_select_launch::select_part(mame_ui_manager &mui, render_container &container, software_info const &info, ui_software_info const &ui_info) +{ + if (mui.options().skip_parts_menu() || !info.has_multiple_parts(ui_info.interface.c_str())) + return false; + + s_parts parts; + for (software_part const &part : info.parts()) + { + if (part.matches_interface(ui_info.interface.c_str())) + { + std::string menu_part_name(part.name()); + if (part.feature("part_id")) + menu_part_name.assign("(").append(part.feature("part_id")).append(")"); + parts.emplace(part.name(), std::move(menu_part_name)); + } + } + menu::stack_push(mui, container, std::move(parts), ui_info); + return true; +} + + +//------------------------------------------------- +// draw toolbar +//------------------------------------------------- + +void menu_select_launch::draw_toolbar(float x1, float y1, float x2, float y2) +{ + // draw a box + ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xef, 0x12, 0x47, 0x7b)); + + // take off the borders + float const aspect(machine().render().ui_aspect(&container())); + float const lr_border(ui().box_lr_border() * aspect); + x1 += lr_border; + x2 -= lr_border; + y1 += ui().box_tb_border(); + y2 -= ui().box_tb_border(); + + // work out which buttons we're going to draw + constexpr unsigned SYS_TOOLBAR_BITMAPS[] = { TOOLBAR_BITMAP_FAVORITE, TOOLBAR_BITMAP_SAVE, TOOLBAR_BITMAP_AUDIT, TOOLBAR_BITMAP_INFO }; + constexpr unsigned SW_TOOLBAR_BITMAPS[] = { TOOLBAR_BITMAP_FAVORITE, TOOLBAR_BITMAP_INFO }; + bool const have_parent = m_is_swlist || !stack_has_special_main_menu(); + unsigned const *const toolbar_bitmaps = m_is_swlist ? SW_TOOLBAR_BITMAPS : SYS_TOOLBAR_BITMAPS; + unsigned const toolbar_count = m_is_swlist ? std::size(SW_TOOLBAR_BITMAPS) : std::size(SYS_TOOLBAR_BITMAPS); + + // calculate metrics + float const x_size = (y2 - y1) * aspect; + float const x_spacing = x_size * 1.5f; + float const backtrack_pos = x2 - x_size; + float const total_width = (float(toolbar_count) + (float(toolbar_count - 1) * 0.5f)) * x_size; + m_cache.cache_toolbar(machine(), x_size, y2 - y1); + + // add backtrack button + rgb_t color(0xffcccccc); + if (mouse_in_rect(backtrack_pos, y1, x2, y2)) + { + set_hover(HOVER_BACKTRACK); + color = rgb_t::white(); + float const ypos = y2 + ui().get_line_height() + 2.0f * ui().box_tb_border(); + ui().draw_text_box( + container(), + have_parent ? _("Return to Previous Menu") : _("Exit"), + text_layout::text_justify::RIGHT, 1.0f - lr_border, ypos, + ui().colors().background_color()); + } + container().add_quad( + backtrack_pos, y1, x2, y2, + color, + m_cache.toolbar_textures()[have_parent ? TOOLBAR_BITMAP_PREVMENU : TOOLBAR_BITMAP_EXIT].get(), + PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + + // now add the other buttons + x1 = (std::min)(backtrack_pos - (float(toolbar_count) * x_spacing), x1 + ((x2 - x1 - total_width) * 0.5f)); + for (int z = 0; toolbar_count > z; ++z, x1 += x_spacing) + { + auto const bitmap = toolbar_bitmaps[z]; + x2 = x1 + x_size; + color = rgb_t (0xffcccccc); + if (mouse_in_rect(x1, y1, x2, y2)) + { + bool const need_selection = (TOOLBAR_BITMAP_FAVORITE == bitmap) || (TOOLBAR_BITMAP_INFO == bitmap); + if (!need_selection || get_selection_ptr()) + { + set_hover(HOVER_B_FAV + bitmap); + color = rgb_t::white(); + } + float ypos = y2 + ui().get_line_height() + 2.0f * ui().box_tb_border(); + ui().draw_text_box( + container(), + _(hover_msg[bitmap]), + text_layout::text_justify::CENTER, (x1 + x2) * 0.5f, ypos, + ui().colors().background_color()); + } + container().add_quad( + x1, y1, x2, y2, + color, + m_cache.toolbar_textures()[bitmap].get(), + PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } +} + + +//------------------------------------------------- +// draw favorites star +//------------------------------------------------- + +void menu_select_launch::draw_star(float x0, float y0) +{ + if (TOOLBAR_BITMAP_FAVORITE < m_cache.toolbar_textures().size()) + { + float const y1 = y0 + ui().get_line_height(); + float const x1 = x0 + ui().get_line_height() * container().manager().ui_aspect(&container()); + container().add_quad( + x0, y0, x1, y1, + rgb_t::white(), + m_cache.toolbar_textures()[TOOLBAR_BITMAP_FAVORITE].get(), + PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_PACKABLE); + } +} + + +void menu_select_launch::set_pressed() +{ + (m_repeat == 0) ? m_repeat = osd_ticks() + osd_ticks_per_second() / 2 : m_repeat = osd_ticks() + osd_ticks_per_second() / 4; + m_pressed = true; +} + + +//------------------------------------------------- +// draw icons +//------------------------------------------------- + +void menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, float y0) +{ + render_texture *const icon(get_icon_texture(linenum, selectedref)); + if (icon) + { + float const ud_arrow_width = ui().get_line_height() * container().manager().ui_aspect(&container()); + float const x1 = x0 + ud_arrow_width; + float const y1 = y0 + ui().get_line_height(); + container().add_quad(x0, y0, x1, y1, rgb_t::white(), icon, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + }; +} + + +//------------------------------------------------- +// get title and search path for right panel +//------------------------------------------------- + +void menu_select_launch::get_title_search(std::string &snaptext, std::string &searchstr) +{ + // get arts title text + snaptext.assign(_("selmenu-artwork", arts_info[m_image_view].first)); + + // get search path + std::string addpath; + if (m_image_view == SNAPSHOT_VIEW) + { + emu_options moptions; + searchstr = machine().options().value(arts_info[m_image_view].second); + addpath = moptions.value(arts_info[m_image_view].second); + } + else + { + ui_options moptions; + searchstr = ui().options().value(arts_info[m_image_view].second); + addpath = moptions.value(arts_info[m_image_view].second); + } + + std::string tmp(searchstr); + path_iterator path(tmp); + path_iterator path_iter(addpath); + std::string c_path, curpath; + + // iterate over path and add path for zipped formats + while (path.next(curpath)) + { + path_iter.reset(); + while (path_iter.next(c_path)) + searchstr.append(";").append(curpath).append(PATH_SEPARATOR).append(c_path); + } +} + + +//------------------------------------------------- +// handle keys for main menu +//------------------------------------------------- + +void menu_select_launch::handle_keys(uint32_t flags, int &iptkey) +{ + // bail if no items + if (item_count() == 0) + return; + + // if we hit select, return true or pop the stack, depending on the item + if (exclusive_input_pressed(iptkey, IPT_UI_SELECT, 0)) + { + if (m_ui_error) + { + // dismiss error + } + else if (m_focus == focused_menu::LEFT) + { + filter_selected(); + } + return; + } + + if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0)) + { + if (m_ui_error) + { + // dismiss error + } + else if (!m_search.empty()) + { + // escape pressed with non-empty search text clears it + m_search.clear(); + reset(reset_options::REMEMBER_REF); + } + else + { + // otherwise pop the stack + stack_pop(); + if (is_special_main_menu()) + machine().schedule_exit(); + } + return; + } + + // validate the current selection + validate_selection(1); + + // swallow left/right keys if they are not appropriate + bool const leftclose = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_LEFT_PANEL); + bool const rightclose = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_RIGHT_PANEL); + + // accept left/right keys as-is with repeat + if (exclusive_input_pressed(iptkey, IPT_UI_LEFT, (flags & PROCESS_LR_REPEAT) ? 6 : 0)) + { + if (m_focus == focused_menu::RIGHTTOP) + { + // Swap the right panel and swallow it + ui_globals::rpanel = RP_IMAGES; + iptkey = IPT_INVALID; + } + else + { + return; + } + } + + if (exclusive_input_pressed(iptkey, IPT_UI_RIGHT, (flags & PROCESS_LR_REPEAT) ? 6 : 0)) + { + if (m_focus == focused_menu::RIGHTTOP) + { + // Swap the right panel and swallow it + ui_globals::rpanel = RP_INFOS; + iptkey = IPT_INVALID; + } + else + { + return; + } + } + + // up backs up by one item + if (exclusive_input_pressed(iptkey, IPT_UI_UP, 6)) + { + if (!leftclose && m_focus == focused_menu::LEFT) + { + return; + } + else if (!rightclose && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))) + { + m_topline_datsview--; + return; + } + else if (selected_index() == m_available_items + 1 || is_first_selected() || m_ui_error) + { + return; + } + + set_selected_index(selected_index() - 1); + + if (selected_index() == top_line && top_line != 0) + top_line--; + } + + // down advances by one item + if (exclusive_input_pressed(iptkey, IPT_UI_DOWN, 6)) + { + if (!leftclose && m_focus == focused_menu::LEFT) + { + return; + } + else if (!rightclose && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))) + { + m_topline_datsview++; + return; + } + else if (is_last_selected() || selected_index() == m_available_items - 1 || m_ui_error) + { + return; + } + + set_selected_index(selected_index() + 1); + if (selected_index() == top_line + m_visible_items + (top_line != 0)) + top_line++; + } + + // page up backs up by m_visible_items + if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_UP, 6)) + { + // Infos + if (!rightclose && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))) + { + m_topline_datsview -= m_right_visible_lines - 3; + return; + } + + if (selected_index() < m_available_items && !m_ui_error) + { + set_selected_index(std::max(selected_index() - m_visible_items, 0)); + + top_line -= m_visible_items - (top_line + m_visible_lines == m_available_items); + } + } + + // page down advances by m_visible_items + if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_DOWN, 6)) + { + // Infos + if (!rightclose && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))) + { + m_topline_datsview += m_right_visible_lines - 3; + return; + } + + if (selected_index() < m_available_items && !m_ui_error) + { + set_selected_index(std::min(selected_index() + m_visible_lines - 2 + (selected_index() == 0), m_available_items - 1)); + + top_line += m_visible_lines - 2; + } + } + + // home goes to the start + if (exclusive_input_pressed(iptkey, IPT_UI_HOME, 0)) + { + if (!leftclose && m_focus == focused_menu::LEFT) + { + return; + } + else if (!rightclose && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))) + { + m_topline_datsview = 0; + return; + } + + if (selected_index() < m_available_items && !m_ui_error) + select_first_item(); + } + + // end goes to the last + if (exclusive_input_pressed(iptkey, IPT_UI_END, 0)) + { + if (!leftclose && m_focus == focused_menu::LEFT) + { + return; + } + else if (!rightclose && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))) + { + m_topline_datsview = m_total_lines; + return; + } + + if (selected_index() < m_available_items && !m_ui_error) + set_selected_index(top_line = m_available_items - 1); + } + + // focus next rotates throw targets forward + if (exclusive_input_pressed(iptkey, IPT_UI_FOCUS_NEXT, 12)) + { + if (!m_ui_error) + rotate_focus(1); + } + + // focus next rotates throw targets forward + if (exclusive_input_pressed(iptkey, IPT_UI_FOCUS_PREV, 12)) + { + if (!m_ui_error) + rotate_focus(-1); + } + + // handle a toggle cheats request + if (!m_ui_error && machine().ui_input().pressed_repeat(IPT_UI_TOGGLE_CHEAT, 0)) + mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled()); + + // see if any other UI keys are pressed + if (iptkey == IPT_INVALID) + { + for (int code = IPT_UI_FIRST + 1; code < IPT_UI_LAST; code++) + { + if (m_ui_error) + continue; + + switch (code) + { + case IPT_UI_FOCUS_NEXT: + case IPT_UI_FOCUS_PREV: + case IPT_UI_PAUSE: + continue; + } + + if (exclusive_input_pressed(iptkey, code, 0)) + break; + } + } +} + + +//------------------------------------------------- +// handle input events for main menu +//------------------------------------------------- + +void menu_select_launch::handle_events(uint32_t flags, event &ev) +{ + if (m_pressed) + { + bool const pressed = mouse_pressed(); + int32_t target_x, target_y; + bool button; + render_target *const mouse_target = machine().ui_input().find_mouse(&target_x, &target_y, &button); + if (mouse_target && button && (hover() == HOVER_ARROW_DOWN || hover() == HOVER_ARROW_UP)) + { + if (pressed) + machine().ui_input().push_mouse_down_event(mouse_target, target_x, target_y); + } + else + { + reset_pressed(); + } + } + + // loop while we have interesting events + bool stop(false), search_changed(false); + ui_event local_menu_event; + while (!stop && machine().ui_input().pop_event(&local_menu_event)) + { + switch (local_menu_event.event_type) + { + // if we are hovering over a valid item, select it with a single click + case ui_event::type::MOUSE_DOWN: + if (m_ui_error) + { + ev.iptkey = IPT_OTHER; + stop = true; + } + else + { + if (hover() >= 0 && hover() < item_count()) + { + if (hover() >= m_available_items - 1 && selected_index() < m_available_items) + m_prev_selected = get_selection_ref(); + set_selected_index(hover()); + m_focus = focused_menu::MAIN; + } + else if (hover() == HOVER_ARROW_UP) + { + set_selected_index(std::max(selected_index() - m_visible_items, 0)); + top_line -= m_visible_items - (top_line + m_visible_lines == m_available_items); + set_pressed(); + } + else if (hover() == HOVER_ARROW_DOWN) + { + set_selected_index(std::min(selected_index() + m_visible_lines - 2 + (selected_index() == 0), m_available_items - 1)); + top_line += m_visible_lines - 2; + set_pressed(); + } + else if (hover() == HOVER_UI_RIGHT) + ev.iptkey = IPT_UI_RIGHT; + else if (hover() == HOVER_UI_LEFT) + ev.iptkey = IPT_UI_LEFT; + else if (hover() == HOVER_DAT_DOWN) + m_topline_datsview += m_right_visible_lines - 3; + else if (hover() == HOVER_DAT_UP) + m_topline_datsview -= m_right_visible_lines - 3; + else if (hover() == HOVER_LPANEL_ARROW) + { + if (get_focus() == focused_menu::LEFT) + { + set_focus(focused_menu::MAIN); + select_prev(); + } + + if (ui_globals::panels_status == HIDE_LEFT_PANEL) + ui_globals::panels_status = SHOW_PANELS; + else if (ui_globals::panels_status == HIDE_BOTH) + ui_globals::panels_status = HIDE_RIGHT_PANEL; + else if (ui_globals::panels_status == SHOW_PANELS) + ui_globals::panels_status = HIDE_LEFT_PANEL; + else if (ui_globals::panels_status == HIDE_RIGHT_PANEL) + ui_globals::panels_status = HIDE_BOTH; + } + else if (hover() == HOVER_RPANEL_ARROW) + { + if ((get_focus() == focused_menu::RIGHTTOP) || (get_focus() == focused_menu::RIGHTBOTTOM)) + { + set_focus(focused_menu::MAIN); + select_prev(); + } + + if (ui_globals::panels_status == HIDE_RIGHT_PANEL) + ui_globals::panels_status = SHOW_PANELS; + else if (ui_globals::panels_status == HIDE_BOTH) + ui_globals::panels_status = HIDE_LEFT_PANEL; + else if (ui_globals::panels_status == SHOW_PANELS) + ui_globals::panels_status = HIDE_RIGHT_PANEL; + else if (ui_globals::panels_status == HIDE_LEFT_PANEL) + ui_globals::panels_status = HIDE_BOTH; + } + else if (hover() == HOVER_B_FAV) + { + ev.iptkey = IPT_UI_FAVORITES; + stop = true; + } + else if (hover() == HOVER_B_EXPORT) + { + inkey_export(); + stop = true; + } + else if (hover() == HOVER_B_AUDIT) + { + ev.iptkey = IPT_UI_AUDIT; + stop = true; + } + else if (hover() == HOVER_B_DATS) + { + inkey_dats(); + stop = true; + } + else if (hover() == HOVER_BACKTRACK) + { + ev.iptkey = IPT_UI_CANCEL; + stack_pop(); + if (is_special_main_menu()) + machine().schedule_exit(); + stop = true; + } + else if (hover() >= HOVER_RP_FIRST && hover() <= HOVER_RP_LAST) + { + ui_globals::rpanel = (HOVER_RP_FIRST - hover()) * (-1); + stop = true; + } + else if (hover() >= HOVER_FILTER_FIRST && hover() <= HOVER_FILTER_LAST) + { + m_filter_highlight = hover() - HOVER_FILTER_FIRST; + filter_selected(); + stop = true; + } + } + break; + + // if we are hovering over a valid item, fake a UI_SELECT with a double-click + case ui_event::type::MOUSE_DOUBLE_CLICK: + if (hover() >= 0 && hover() < item_count()) + { + set_selected_index(hover()); + ev.iptkey = IPT_UI_SELECT; + } + stop = true; + break; + + // caught scroll event + case ui_event::type::MOUSE_WHEEL: + if (hover() >= 0 && hover() < item_count() - skip_main_items) + { + if (local_menu_event.zdelta > 0) + { + if (selected_index() >= m_available_items || is_first_selected() || m_ui_error) + break; + set_selected_index(selected_index() - local_menu_event.num_lines); + if (selected_index() < top_line + (top_line != 0)) + top_line -= local_menu_event.num_lines; + } + else + { + if (selected_index() >= m_available_items - 1 || m_ui_error) + break; + set_selected_index(std::min(selected_index() + local_menu_event.num_lines, m_available_items - 1)); + if (selected_index() >= top_line + m_visible_items + (top_line != 0)) + top_line += local_menu_event.num_lines; + } + } + else if (hover() == HOVER_INFO_TEXT) + { + if (local_menu_event.zdelta > 0) + m_topline_datsview -= local_menu_event.num_lines; + else + m_topline_datsview += local_menu_event.num_lines; + } + break; + + // translate CHAR events into specials + case ui_event::type::IME_CHAR: + if (exclusive_input_pressed(ev.iptkey, IPT_UI_FOCUS_NEXT, 0) || exclusive_input_pressed(ev.iptkey, IPT_UI_FOCUS_PREV, 0)) + { + stop = true; + } + else if (m_ui_error) + { + ev.iptkey = IPT_SPECIAL; + stop = true; + } + else if (accept_search()) + { + if (input_character(m_search, local_menu_event.ch, uchar_is_printable)) + search_changed = true; + } + break; + + case ui_event::type::MOUSE_RDOWN: + if (hover() >= 0 && hover() < item_count() - skip_main_items) + { + set_selected_index(hover()); + m_prev_selected = get_selection_ref(); + m_focus = focused_menu::MAIN; + ev.iptkey = IPT_CUSTOM; + ev.mouse.x0 = local_menu_event.mouse_x; + ev.mouse.y0 = local_menu_event.mouse_y; + stop = true; + } + break; + + // ignore everything else + default: + break; + } + + // need to update search before processing certain kinds of events, but others don't matter + if (search_changed) + { + switch (machine().ui_input().peek_event_type()) + { + case ui_event::type::MOUSE_DOWN: + case ui_event::type::MOUSE_RDOWN: + case ui_event::type::MOUSE_DOUBLE_CLICK: + case ui_event::type::MOUSE_WHEEL: + stop = true; + break; + case ui_event::type::NONE: + case ui_event::type::WINDOW_FOCUS: + case ui_event::type::WINDOW_DEFOCUS: + case ui_event::type::MOUSE_MOVE: + case ui_event::type::MOUSE_LEAVE: + case ui_event::type::MOUSE_UP: + case ui_event::type::MOUSE_RUP: + case ui_event::type::IME_CHAR: + break; + } + } + } + if (search_changed) + reset(reset_options::SELECT_FIRST); +} + + +//------------------------------------------------- +// draw main menu +//------------------------------------------------- + +void menu_select_launch::draw(uint32_t flags) +{ + bool noinput = (flags & PROCESS_NOINPUT); + float const aspect = machine().render().ui_aspect(&container()); + float const lr_border = ui().box_lr_border() * aspect; + float line_height = ui().get_line_height(); + float const ud_arrow_width = line_height * aspect; + float const gutter_width = 0.52f * ud_arrow_width; + float const icon_offset = m_has_icons ? (1.5f * ud_arrow_width) : 0.0f; + float right_panel_size = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_RIGHT_PANEL) ? 2.0f * lr_border : 0.3f; + float visible_width = 1.0f - 4.0f * lr_border; + float primary_left = (1.0f - visible_width) * 0.5f; + float primary_width = visible_width; + + draw_background(); + + clear_hover(); + m_available_items = item_count() - skip_main_items; + float extra_height = skip_main_items * line_height; + float visible_extra_menu_height = get_customtop() + get_custombottom() + extra_height; + + // locate mouse + if (noinput) + ignore_mouse(); + else + map_mouse(); + + // account for extra space at the top and bottom + float visible_main_menu_height = 1.0f - 2.0f * ui().box_tb_border() - visible_extra_menu_height; + m_visible_lines = int(std::trunc(visible_main_menu_height / line_height)); + visible_main_menu_height = float(m_visible_lines) * line_height; + + if (!m_is_swlist) + ui_globals::visible_main_lines = m_visible_lines; + else + ui_globals::visible_sw_lines = m_visible_lines; + + // compute top/left of inner menu area by centering + float visible_left = primary_left; + float visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f; + + // if the menu is at the bottom of the extra, adjust + visible_top += get_customtop(); + + // compute left box size + float x1 = visible_left - lr_border; + float y1 = visible_top - ui().box_tb_border(); + float x2 = x1 + 2.0f * lr_border; + float y2 = visible_top + visible_main_menu_height + ui().box_tb_border() + extra_height; + + // add left box + visible_left = draw_left_panel(x1, y1, x2, y2); + visible_width -= right_panel_size + visible_left - 2.0f * lr_border; + + // compute and add main box + x1 = visible_left - lr_border; + x2 = visible_left + visible_width + lr_border; + float line = visible_top + (float(m_visible_lines) * line_height); + ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color()); + + // make sure the selection + if (m_available_items < m_visible_lines) + m_visible_lines = m_available_items; + int selection; + if (selected_index() < m_available_items) + { + selection = selected_index(); + } + else + { + selection = 0; + while ((m_available_items > selection) && (item(selection).ref() != m_prev_selected)) + ++selection; + } + if (top_line < 0 || !selection) + { + top_line = 0; + } + else if (selection < m_available_items) + { + if ((selection >= (top_line + m_visible_lines)) || (selection <= top_line)) + top_line = (std::max)(selection - (m_visible_lines / 2), 0); + if ((top_line + m_visible_lines) >= m_available_items) + top_line = m_available_items - m_visible_lines; + else if (selection >= (top_line + m_visible_lines - 2)) + top_line = selection - m_visible_lines + ((selection == (m_available_items - 1)) ? 1: 2); + } + + // determine effective positions taking into account the hilighting arrows + float effective_width = visible_width - 2.0f * gutter_width; + float effective_left = visible_left + gutter_width; + + if ((m_focus == focused_menu::MAIN) && (selected_index() < m_available_items)) + m_prev_selected = nullptr; + + int const n_loop = (std::min)(m_visible_lines, m_available_items); + for (int linenum = 0; linenum < n_loop; linenum++) + { + float line_y = visible_top + (float(linenum) * line_height); + int itemnum = top_line + linenum; + const menu_item &pitem = item(itemnum); + const std::string_view itemtext = pitem.text(); + rgb_t fgcolor = ui().colors().text_color(); + rgb_t bgcolor = ui().colors().text_bg_color(); + rgb_t fgcolor3 = ui().colors().clone_color(); + float line_x0 = x1 + 0.5f * UI_LINE_WIDTH; + float line_y0 = line_y; + float line_x1 = x2 - 0.5f * UI_LINE_WIDTH; + float line_y1 = line_y + line_height; + + // set the hover if this is our item + if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem)) + set_hover(itemnum); + + if (is_selected(itemnum) && m_focus == focused_menu::MAIN) + { + // if we're selected, draw with a different background + fgcolor = rgb_t(0xff, 0xff, 0x00); + bgcolor = rgb_t(0xff, 0xff, 0xff); + fgcolor3 = rgb_t(0xcc, 0xcc, 0x00); + ui().draw_textured_box( + container(), + line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, + bgcolor, rgb_t(43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + else if (itemnum == hover()) + { + // else if the mouse is over this item, draw with a different background + fgcolor = fgcolor3 = ui().options().mouseover_color(); + bgcolor = ui().colors().mouseover_bg_color(); + highlight(line_x0, line_y0, line_x1, line_y1, bgcolor); + } + else if (pitem.ref() == m_prev_selected) + { + fgcolor = fgcolor3 = ui().options().mouseover_color(); + bgcolor = ui().colors().mouseover_bg_color(); + ui().draw_textured_box(container(), line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + + if (linenum == 0 && top_line != 0) + { + // if we're on the top line, display the up arrow + draw_arrow(0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height, + 0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0); + + if (hover() == itemnum) + set_hover(HOVER_ARROW_UP); + } + else if (linenum == m_visible_lines - 1 && itemnum != m_available_items - 1) + { + // if we're on the bottom line, display the down arrow + draw_arrow(0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height, + 0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0 ^ ORIENTATION_FLIP_Y); + + if (hover() == itemnum) + set_hover(HOVER_ARROW_DOWN); + } + else if (pitem.type() == menu_item_type::SEPARATOR) + { + // if we're just a divider, draw a line + container().add_line(visible_left, line_y + 0.5f * line_height, visible_left + visible_width, line_y + 0.5f * line_height, + UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } + else if (pitem.subtext().empty()) + { + // draw the item centered + int const item_invert = pitem.flags() & FLAG_INVERT; + if (m_has_icons) + draw_icon(linenum, item(itemnum).ref(), effective_left, line_y); + ui().draw_text_full( + container(), + itemtext, + effective_left + icon_offset, line_y, effective_width - icon_offset, + text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor, + nullptr, nullptr); + } + else + { + int const item_invert = pitem.flags() & FLAG_INVERT; + std::string_view const subitem_text = pitem.subtext(); + float item_width, subitem_width; + + // compute right space for subitem + ui().draw_text_full( + container(), + subitem_text, + effective_left + icon_offset, line_y, ui().get_string_width(pitem.subtext()), + text_layout::text_justify::RIGHT, text_layout::word_wrapping::NEVER, + mame_ui_manager::NONE, item_invert ? fgcolor3 : fgcolor, bgcolor, + &subitem_width, nullptr); + subitem_width += gutter_width; + + // draw the item left-justified + if (m_has_icons) + draw_icon(linenum, item(itemnum).ref(), effective_left, line_y); + ui().draw_text_full( + container(), + itemtext, + effective_left + icon_offset, line_y, effective_width - icon_offset - subitem_width, + text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor, + &item_width, nullptr); + + // draw the subitem right-justified + ui().draw_text_full( + container(), + subitem_text, + effective_left + icon_offset + item_width, line_y, effective_width - icon_offset - item_width, + text_layout::text_justify::RIGHT, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor, + nullptr, nullptr); + } + } + + for (size_t count = m_available_items; count < item_count(); count++) + { + const menu_item &pitem = item(count); + const std::string_view itemtext = pitem.text(); + float line_x0 = x1 + 0.5f * UI_LINE_WIDTH; + float line_y0 = line; + float line_x1 = x2 - 0.5f * UI_LINE_WIDTH; + float line_y1 = line + line_height; + rgb_t fgcolor = ui().colors().text_color(); + rgb_t bgcolor = ui().colors().text_bg_color(); + + if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem)) + set_hover(count); + + // if we're selected, draw with a different background + if (is_selected(count) && m_focus == focused_menu::MAIN) + { + fgcolor = rgb_t(0xff, 0xff, 0x00); + bgcolor = rgb_t(0xff, 0xff, 0xff); + ui().draw_textured_box(container(), line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + // else if the mouse is over this item, draw with a different background + else if (count == hover()) + { + fgcolor = ui().options().mouseover_color(); + bgcolor = ui().colors().mouseover_bg_color(); + highlight(line_x0, line_y0, line_x1, line_y1, bgcolor); + } + + if (pitem.type() == menu_item_type::SEPARATOR) + { + container().add_line( + visible_left, line + 0.5f * line_height, + visible_left + visible_width, line + 0.5f * line_height, + UI_LINE_WIDTH, + ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } + else + { + ui().draw_text_full( + container(), + itemtext, + effective_left, line, effective_width, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NORMAL, fgcolor, bgcolor, + nullptr, nullptr); + } + line += line_height; + } + + x1 = x2; + x2 += right_panel_size; + + draw_right_panel(x1, y1, x2, y2); + + x1 = primary_left - lr_border; + x2 = primary_left + primary_width + lr_border; + + // if there is something special to add, do it by calling the virtual method + custom_render(get_selection_ref(), get_customtop(), get_custombottom(), x1, y1, x2, y2); + + // return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow + m_visible_items = m_visible_lines - (top_line != 0) - (top_line + m_visible_lines != m_available_items); + + // noinput + if (noinput) + { + int alpha = (1.0f - machine().options().pause_brightness()) * 255.0f; + if (alpha > 255) + alpha = 255; + if (alpha >= 0) + container().add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(alpha, 0x00, 0x00, 0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } +} + + +//------------------------------------------------- +// draw right panel +//------------------------------------------------- + +void menu_select_launch::draw_right_panel(float origx1, float origy1, float origx2, float origy2) +{ + float const aspect(machine().render().ui_aspect(&container())); + bool const hide((ui_globals::panels_status == HIDE_RIGHT_PANEL) || (ui_globals::panels_status == HIDE_BOTH)); + float const x2(hide ? origx2 : (origx1 + 2.0f * ui().box_lr_border() * aspect)); + float const space(x2 - origx1); + float const lr_arrow_width(0.4f * space * aspect); + + // set left-right arrows dimension + float const ar_x0(0.5f * (x2 + origx1) - 0.5f * lr_arrow_width); + float const ar_y0(0.5f * (origy2 + origy1) + 0.1f * space); + float const ar_x1(ar_x0 + lr_arrow_width); + float const ar_y1(0.5f * (origy2 + origy1) + 0.9f * space); + + ui().draw_outlined_box(container(), origx1, origy1, origx2, origy2, rgb_t(0xEF, 0x12, 0x47, 0x7B)); + + rgb_t fgcolor(ui().colors().text_color()); + if (mouse_in_rect(origx1, origy1, x2, origy2)) + { + fgcolor = ui().options().mouseover_color(); + set_hover(HOVER_RPANEL_ARROW); + } + + if (hide) + { + draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X); + return; + } + + draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90); + origy1 = draw_right_box_title(x2, origy1, origx2, origy2); + + if (ui_globals::rpanel == RP_IMAGES) + arts_render(x2, origy1, origx2, origy2); + else + infos_render(x2, origy1, origx2, origy2); +} + + +//------------------------------------------------- +// draw right box title +//------------------------------------------------- + +float menu_select_launch::draw_right_box_title(float x1, float y1, float x2, float y2) +{ + auto line_height = ui().get_line_height(); + float const midl = (x2 - x1) * 0.5f; + + // add outlined box for options + ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color()); + + // add separator line + container().add_line(x1 + midl, y1, x1 + midl, y1 + line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + + std::string buffer[RP_LAST + 1]; + buffer[RP_IMAGES] = _("Images"); + buffer[RP_INFOS] = _("Infos"); + + // check size + float text_size = 1.0f; + for (auto & elem : buffer) + { + auto textlen = ui().get_string_width(elem) + 0.01f; + float tmp_size = (textlen > midl) ? (midl / textlen) : 1.0f; + text_size = std::min(text_size, tmp_size); + } + + for (int cells = RP_FIRST; cells <= RP_LAST; ++cells) + { + rgb_t bgcolor = ui().colors().text_bg_color(); + rgb_t fgcolor = ui().colors().text_color(); + + if (mouse_in_rect(x1, y1, x1 + midl, y1 + line_height)) + { + if (ui_globals::rpanel != cells) + { + bgcolor = ui().colors().mouseover_bg_color(); + fgcolor = ui().options().mouseover_color(); + set_hover(HOVER_RP_FIRST + cells); + } + } + + if (ui_globals::rpanel != cells) + { + container().add_line(x1, y1 + line_height, x1 + midl, y1 + line_height, UI_LINE_WIDTH, + ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + if (fgcolor != ui().colors().mouseover_color()) + fgcolor = ui().colors().clone_color(); + } + + if (m_focus == focused_menu::RIGHTTOP && ui_globals::rpanel == cells) + { + fgcolor = rgb_t(0xff, 0xff, 0x00); + bgcolor = rgb_t(0xff, 0xff, 0xff); + ui().draw_textured_box( + container(), + x1 + UI_LINE_WIDTH, y1 + UI_LINE_WIDTH, x1 + midl - UI_LINE_WIDTH, y1 + line_height, + bgcolor, rgb_t(43, 43, 43), hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + else if (bgcolor == ui().colors().mouseover_bg_color()) + { + container().add_rect(x1 + UI_LINE_WIDTH, y1 + UI_LINE_WIDTH, x1 + midl - UI_LINE_WIDTH, y1 + line_height, + bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + + ui().draw_text_full( + container(), + buffer[cells], + x1 + UI_LINE_WIDTH, y1, midl - UI_LINE_WIDTH, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr, text_size); + x1 += midl; + } + + return (y1 + line_height + UI_LINE_WIDTH); +} + + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_select_launch::arts_render(float origx1, float origy1, float origx2, float origy2) +{ + ui_software_info const *software; + ui_system_info const *system; + get_selection(software, system); + + if (software && (!software->startempty || !system)) + { + m_cache.set_snapx_driver(nullptr); + + if (m_default_image) + m_image_view = (software->startempty == 0) ? SNAPSHOT_VIEW : CABINETS_VIEW; + + // arts title and searchpath + std::string const searchstr = arts_render_common(origx1, origy1, origx2, origy2); + + // loads the image if necessary + if (!m_cache.snapx_software_is(software) || !snapx_valid() || m_switch_image) + { + emu_file snapfile(searchstr.c_str(), OPEN_FLAG_READ); + bitmap_argb32 tmp_bitmap; + + if (software->startempty == 1) + { + // Load driver snapshot + load_driver_image(tmp_bitmap, snapfile, *software->driver); + } + else + { + // First attempt from name list + load_image(tmp_bitmap, snapfile, util::path_concat(software->listname, software->shortname)); + + // Second attempt from driver name + part name + if (!tmp_bitmap.valid()) + load_image(tmp_bitmap, snapfile, util::path_concat(software->driver->name + software->part, software->shortname)); + } + + m_cache.set_snapx_software(software); + m_switch_image = false; + arts_render_images(std::move(tmp_bitmap), origx1, origy1, origx2, origy2); + } + + // if the image is available, loaded and valid, display it + draw_snapx(origx1, origy1, origx2, origy2); + } + else if (system) + { + m_cache.set_snapx_software(nullptr); + + if (m_default_image) + m_image_view = ((system->driver->flags & machine_flags::MASK_TYPE) != machine_flags::TYPE_ARCADE) ? CABINETS_VIEW : SNAPSHOT_VIEW; + + std::string const searchstr = arts_render_common(origx1, origy1, origx2, origy2); + + // loads the image if necessary + if (!m_cache.snapx_driver_is(system->driver) || !snapx_valid() || m_switch_image) + { + emu_file snapfile(searchstr, OPEN_FLAG_READ); + bitmap_argb32 tmp_bitmap; + load_driver_image(tmp_bitmap, snapfile, *system->driver); + + m_cache.set_snapx_driver(system->driver); + m_switch_image = false; + arts_render_images(std::move(tmp_bitmap), origx1, origy1, origx2, origy2); + } + + // if the image is available, loaded and valid, display it + draw_snapx(origx1, origy1, origx2, origy2); + } +} + + +//------------------------------------------------- +// common function for images render +//------------------------------------------------- + +std::string menu_select_launch::arts_render_common(float origx1, float origy1, float origx2, float origy2) +{ + float const line_height = ui().get_line_height(); + float const gutter_width = 0.4f * line_height * machine().render().ui_aspect(&container()) * 1.3f; + + std::string snaptext, searchstr; + get_title_search(snaptext, searchstr); + + // apply title to right panel + float title_size = 0.0f; + for (int x = FIRST_VIEW; x < LAST_VIEW; x++) + { + float text_length; + ui().draw_text_full(container(), + _("selmenu-artwork", arts_info[x].first), origx1, origy1, origx2 - origx1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), + &text_length, nullptr); + title_size = (std::max)(text_length + 0.01f, title_size); + } + + rgb_t const fgcolor = (m_focus == focused_menu::RIGHTBOTTOM) ? rgb_t(0xff, 0xff, 0x00) : ui().colors().text_color(); + rgb_t const bgcolor = (m_focus == focused_menu::RIGHTBOTTOM) ? rgb_t(0xff, 0xff, 0xff) : ui().colors().text_bg_color(); + float const middle = origx2 - origx1; + + // check size + float const sc = title_size + 2.0f * gutter_width; + float const tmp_size = (sc > middle) ? ((middle - 2.0f * gutter_width) / sc) : 1.0f; + title_size *= tmp_size; + + if (bgcolor != ui().colors().text_bg_color()) + { + ui().draw_textured_box( + container(), + origx1 + ((middle - title_size) * 0.5f), origy1 + ui().box_tb_border(), + origx1 + ((middle + title_size) * 0.5f), origy1 + ui().box_tb_border() + line_height, + bgcolor, rgb_t(43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + + ui().draw_text_full(container(), + snaptext, origx1, origy1 + ui().box_tb_border(), origx2 - origx1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor, + nullptr, nullptr, tmp_size); + + draw_common_arrow(origx1, origy1 + ui().box_tb_border(), origx2, origy2, m_image_view, FIRST_VIEW, LAST_VIEW, title_size); + + return searchstr; +} + + +//------------------------------------------------- +// perform rendering of image +//------------------------------------------------- + +void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap, float origx1, float origy1, float origx2, float origy2) +{ + bool no_available = false; + float line_height = ui().get_line_height(); + + // if it fails, use the default image + if (!tmp_bitmap.valid()) + { + tmp_bitmap.allocate(256, 256); + const bitmap_argb32 &src(m_cache.no_avail_bitmap()); + for (int x = 0; x < 256; x++) + { + for (int y = 0; y < 256; y++) + tmp_bitmap.pix(y, x) = src.pix(y, x); + } + no_available = true; + } + + bitmap_argb32 &snapx_bitmap(m_cache.snapx_bitmap()); + if (tmp_bitmap.valid()) + { + float panel_width = origx2 - origx1 - 0.02f; + float panel_height = origy2 - origy1 - 0.02f - (3.0f * ui().box_tb_border()) - (2.0f * line_height); + int screen_width = machine().render().ui_target().width(); + int screen_height = machine().render().ui_target().height(); + + if (machine().render().ui_target().orientation() & ORIENTATION_SWAP_XY) + std::swap(screen_height, screen_width); + + int panel_width_pixel = panel_width * screen_width; + int panel_height_pixel = panel_height * screen_height; + + // Calculate resize ratios for resizing + auto ratioW = (float)panel_width_pixel / tmp_bitmap.width(); + auto ratioH = (float)panel_height_pixel / tmp_bitmap.height(); + auto ratioI = (float)tmp_bitmap.height() / tmp_bitmap.width(); + auto dest_xPixel = tmp_bitmap.width(); + auto dest_yPixel = tmp_bitmap.height(); + + // force 4:3 ratio min + if (ui().options().forced_4x3_snapshot() && ratioI < 0.75f && m_image_view == SNAPSHOT_VIEW) + { + // smaller ratio will ensure that the image fits in the view + dest_yPixel = tmp_bitmap.width() * 0.75f; + ratioH = (float)panel_height_pixel / dest_yPixel; + float ratio = std::min(ratioW, ratioH); + dest_xPixel = tmp_bitmap.width() * ratio; + dest_yPixel *= ratio; + } + // resize the bitmap if necessary + else if (ratioW < 1 || ratioH < 1 || (ui().options().enlarge_snaps() && !no_available)) + { + // smaller ratio will ensure that the image fits in the view + float ratio = std::min(ratioW, ratioH); + dest_xPixel = tmp_bitmap.width() * ratio; + dest_yPixel = tmp_bitmap.height() * ratio; + } + + bitmap_argb32 dest_bitmap; + + // resample if necessary + if (dest_xPixel != tmp_bitmap.width() || dest_yPixel != tmp_bitmap.height()) + { + dest_bitmap.allocate(dest_xPixel, dest_yPixel); + render_color color = { 1.0f, 1.0f, 1.0f, 1.0f }; + render_resample_argb_bitmap_hq(dest_bitmap, tmp_bitmap, color, true); + } + else + dest_bitmap = std::move(tmp_bitmap); + + snapx_bitmap.allocate(panel_width_pixel, panel_height_pixel); + int x1 = (0.5f * panel_width_pixel) - (0.5f * dest_xPixel); + int y1 = (0.5f * panel_height_pixel) - (0.5f * dest_yPixel); + + for (int x = 0; x < dest_xPixel; x++) + for (int y = 0; y < dest_yPixel; y++) + snapx_bitmap.pix(y + y1, x + x1) = dest_bitmap.pix(y, x); + + // apply bitmap + m_cache.snapx_texture()->set_bitmap(snapx_bitmap, snapx_bitmap.cliprect(), TEXFORMAT_ARGB32); + } + else + { + snapx_bitmap.reset(); + } +} + + +//------------------------------------------------- +// draw snapshot +//------------------------------------------------- + +void menu_select_launch::draw_snapx(float origx1, float origy1, float origx2, float origy2) +{ + // if the image is available, loaded and valid, display it + if (snapx_valid()) + { + float const line_height = ui().get_line_height(); + float const x1 = origx1 + 0.01f; + float const x2 = origx2 - 0.01f; + float const y1 = origy1 + (2.0f * ui().box_tb_border()) + line_height; + float const y2 = origy2 - ui().box_tb_border() - line_height; + + // apply texture + container().add_quad(x1, y1, x2, y2, rgb_t::white(), m_cache.snapx_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + } +} + + +std::string menu_select_launch::make_system_audit_fail_text(media_auditor const &auditor, media_auditor::summary summary) +{ + std::ostringstream str; + if (!auditor.records().empty()) + { + str << "System media audit failed:\n"; + auditor.summarize(nullptr, &str); + osd_printf_info(str.str()); + str.str(""); + } + str << _("Required ROM/disk images for the selected system are missing or incorrect. Please acquire the correct files or select a different system.\n\n"); + make_audit_fail_text(str, auditor, summary); + return str.str(); +} + + +std::string menu_select_launch::make_software_audit_fail_text(media_auditor const &auditor, media_auditor::summary summary) +{ + std::ostringstream str; + if (!auditor.records().empty()) + { + str << "System media audit failed:\n"; + auditor.summarize(nullptr, &str); + osd_printf_info(str.str()); + str.str(""); + } + str << _("Required ROM/disk images for the selected software are missing or incorrect. Please acquire the correct files or select a different software item.\n\n"); + make_audit_fail_text(str, auditor, summary); + return str.str(); +} + + +void menu_select_launch::make_audit_fail_text(std::ostream &str, media_auditor const &auditor, media_auditor::summary summary) +{ + if ((media_auditor::NOTFOUND != summary) && !auditor.records().empty()) + { + char const *message = nullptr; + for (media_auditor::audit_record const &record : auditor.records()) + { + switch (record.substatus()) + { + case media_auditor::audit_substatus::FOUND_BAD_CHECKSUM: + message = _("incorrect checksum"); + break; + case media_auditor::audit_substatus::FOUND_WRONG_LENGTH: + message = _("incorrect length"); + break; + case media_auditor::audit_substatus::NOT_FOUND: + message = _("not found"); + break; + case media_auditor::audit_substatus::GOOD: + case media_auditor::audit_substatus::GOOD_NEEDS_REDUMP: + case media_auditor::audit_substatus::FOUND_NODUMP: + case media_auditor::audit_substatus::NOT_FOUND_NODUMP: + case media_auditor::audit_substatus::NOT_FOUND_OPTIONAL: + case media_auditor::audit_substatus::UNVERIFIED: + continue; + } + if (record.shared_device()) + util::stream_format(str, _("%1$s (%2$s) - %3$s\n"), record.name(), record.shared_device()->shortname(), message); + else + util::stream_format(str, _("%1$s - %2$s\n"), record.name(), message); + } + str << '\n'; + } + str << _("Press any key to continue."); +} + + +//------------------------------------------------- +// get bios count +//------------------------------------------------- + +bool menu_select_launch::has_multiple_bios(ui_software_info const &swinfo, s_bios &biosname) +{ + return has_multiple_bios(*swinfo.driver, biosname); +} + +bool menu_select_launch::has_multiple_bios(game_driver const &driver, s_bios &biosname) +{ + if (!driver.rom) + return false; + + char const *default_name(nullptr); + for (tiny_rom_entry const *rom = driver.rom; !ROMENTRY_ISEND(rom); ++rom) + { + if (ROMENTRY_ISDEFAULT_BIOS(rom)) + default_name = rom->name; + } + + for (romload::system_bios const &bios : romload::entries(driver.rom).get_system_bioses()) + { + std::string name(bios.get_description()); + uint32_t const bios_flags(bios.get_value()); + + if (default_name && !std::strcmp(bios.get_name(), default_name)) + { + name.append(_(" (default)")); + biosname.emplace(biosname.begin(), std::move(name), bios_flags - 1); + } + else + { + biosname.emplace_back(std::move(name), bios_flags - 1); + } + } + return biosname.size() > 1U; +} + + +//------------------------------------------------- +// draw collapsed left panel +//------------------------------------------------- + +float menu_select_launch::draw_collapsed_left_panel(float x1, float y1, float x2, float y2) +{ + float const aspect = machine().render().ui_aspect(&container()); + float const space = x2 - x1; + float const lr_arrow_width = 0.4f * space * aspect; + + // set left-right arrows dimension + float const ar_x0 = 0.5f * (x2 + x1) - (0.5f * lr_arrow_width); + float const ar_y0 = 0.5f * (y2 + y1) + (0.1f * space); + float const ar_x1 = ar_x0 + lr_arrow_width; + float const ar_y1 = 0.5f * (y2 + y1) + (0.9f * space); + + ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xef, 0x12, 0x47, 0x7b)); // FIXME: magic numbers in colour? + + rgb_t fgcolor = ui().colors().text_color(); + if (mouse_in_rect(x1, y1, x2, y2)) + { + fgcolor = ui().options().mouseover_color(); + set_hover(HOVER_LPANEL_ARROW); + } + + draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90); + + return x2 + ui().box_lr_border() * aspect; +} + + +//------------------------------------------------- +// draw infos +//------------------------------------------------- + +void menu_select_launch::infos_render(float origx1, float origy1, float origx2, float origy2) +{ + float const line_height = ui().get_line_height(); + float text_size = ui().options().infos_size(); + std::string_view first; + ui_software_info const *software; + ui_system_info const *system; + int total; + get_selection(software, system); + + if (software && !software->startempty) + { + m_info_driver = nullptr; + first = _("Software List Info"); + + if ((m_info_software != software) || (m_info_view != ui_globals::cur_sw_dats_view)) + { + m_info_buffer.clear(); + m_info_layout = std::nullopt; + if (software == m_info_software) + { + m_info_view = ui_globals::cur_sw_dats_view; + } + else + { + m_info_view = 0; + m_info_software = software; + ui_globals::cur_sw_dats_view = 0; + + m_items_list.clear(); + mame_machine_manager::instance()->lua()->call_plugin("data_list", std::string(software->shortname).append(1, ',').append(software->listname).c_str(), m_items_list); + ui_globals::cur_sw_dats_total = m_items_list.size() + 1; + } + + if (m_info_view == 0) + { + m_info_buffer = software->infotext; + } + else + { + m_info_buffer.clear(); + mame_machine_manager::instance()->lua()->call_plugin("data", m_info_view - 1, m_info_buffer); + } + } + total = ui_globals::cur_sw_dats_total; + } + else if (system || (software && software->driver)) + { + game_driver const &driver(system ? *system->driver : *software->driver); + m_info_software = nullptr; + first = _("General Info"); + + if (&driver != m_info_driver || ui_globals::curdats_view != m_info_view) + { + m_info_buffer.clear(); + m_info_layout = std::nullopt; + if (&driver == m_info_driver) + { + m_info_view = ui_globals::curdats_view; + } + else + { + m_info_driver = &driver; + m_info_view = 0; + ui_globals::curdats_view = 0; + + m_items_list.clear(); + mame_machine_manager::instance()->lua()->call_plugin("data_list", driver.name, m_items_list); + ui_globals::curdats_total = m_items_list.size() + 1; + } + + if (m_info_view == 0) + { + general_info(system, driver, m_info_buffer); + } + else + { + m_info_buffer.clear(); + mame_machine_manager::instance()->lua()->call_plugin("data", m_info_view - 1, m_info_buffer); + } + } + total = ui_globals::curdats_total; + } + else + { + return; + } + + origy1 += ui().box_tb_border(); + float const aspect(machine().render().ui_aspect(&container())); + float const gutter_width = 0.4f * line_height * aspect * 1.3f; + float const ud_arrow_width = line_height * aspect; + float oy1 = origy1 + line_height; + + std::string_view const snaptext(m_info_view ? std::string_view(m_items_list[m_info_view - 1]) : first); + + // get width of widest title + float title_size(0.0f); + for (std::size_t x = 0; total > x; ++x) + { + std::string_view const name(x ? std::string_view(m_items_list[x - 1]) : first); + float txt_length(0.0f); + ui().draw_text_full( + container(), name, + origx1, origy1, origx2 - origx1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NONE, ui().colors().text_color(), ui().colors().text_bg_color(), + &txt_length, nullptr); + txt_length += 0.01f; + title_size = (std::max)(txt_length, title_size); + } + + rgb_t fgcolor = ui().colors().text_color(); + rgb_t bgcolor = ui().colors().text_bg_color(); + if (get_focus() == focused_menu::RIGHTBOTTOM) + { + fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00); + bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff); + } + + float middle = origx2 - origx1; + + // check size + float sc = title_size + 2.0f * gutter_width; + float tmp_size = (sc > middle) ? ((middle - 2.0f * gutter_width) / sc) : 1.0f; + title_size *= tmp_size; + + if (bgcolor != ui().colors().text_bg_color()) + { + ui().draw_textured_box( + container(), + origx1 + ((middle - title_size) * 0.5f), origy1, origx1 + ((middle + title_size) * 0.5f), + origy1 + line_height, bgcolor, rgb_t(255, 43, 43, 43), + hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); + } + + ui().draw_text_full( + container(), + snaptext, + origx1, origy1, origx2 - origx1, + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, + mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr, tmp_size); + + sc = origx2 - origx1 - (2.0f * gutter_width); + if (!m_info_layout || (m_info_layout->width() != sc)) + { + m_info_layout.emplace( + ui().create_layout( + container(), + sc, + text_layout::text_justify::LEFT, text_layout::word_wrapping::WORD)); + menu_dats_view::add_info_text(*m_info_layout, m_info_buffer, ui().colors().text_color(), text_size); + m_total_lines = m_info_layout->lines(); + } + + draw_common_arrow(origx1, origy1, origx2, origy2, m_info_view, 0, total - 1, title_size); + + m_right_visible_lines = floor((origy2 - oy1) / (line_height * text_size)); + if (m_total_lines < m_right_visible_lines) + m_right_visible_lines = m_total_lines; + if (m_topline_datsview < 0) + m_topline_datsview = 0; + if ((m_topline_datsview + m_right_visible_lines) >= m_total_lines) + m_topline_datsview = m_total_lines - m_right_visible_lines; + + // get the number of visible lines, minus 1 for top arrow and 1 for bottom arrow + bool const up_arrow = m_topline_datsview > 0; + bool const down_arrow = (m_topline_datsview + m_right_visible_lines) < m_total_lines; + int const r_visible_lines = m_right_visible_lines - (up_arrow ? 1 : 0) - (down_arrow ? 1 : 0); + + if (mouse_in_rect(origx1 + gutter_width, oy1, origx2 - gutter_width, origy2)) + set_hover(HOVER_INFO_TEXT); + + if (up_arrow) + draw_info_arrow(0, origx1, origx2, oy1, line_height, text_size, ud_arrow_width); + if (down_arrow) + draw_info_arrow(1, origx1, origx2, oy1 + (float(m_right_visible_lines - 1) * line_height * text_size), line_height, text_size, ud_arrow_width); + + m_info_layout->emit( + container(), + m_topline_datsview ? (m_topline_datsview + 1) : 0, r_visible_lines, + origx1 + gutter_width, oy1 + (m_topline_datsview ? (line_height * text_size) : 0.0f)); +} + + +//------------------------------------------------- +// generate general info +//------------------------------------------------- + +void menu_select_launch::general_info(ui_system_info const *system, game_driver const &driver, std::string &buffer) +{ + system_flags const &flags(get_system_flags(driver)); + std::ostringstream str; + + str << "#j2\n"; + + if (system) + str << system->description; + else + str << driver.type.fullname(); + str << "\t\n\n"; + + util::stream_format(str, _("Romset\t%1$s\n"), driver.name); + util::stream_format(str, _("Year\t%1$s\n"), driver.year); + util::stream_format(str, _("Manufacturer\t%1$s\n"), driver.manufacturer); + + int cloneof = driver_list::non_bios_clone(driver); + if (0 <= cloneof) + { + util::stream_format( + str, + _("Driver is Clone of\t%1$s\n"), + system ? std::string_view(system->parent) : std::string_view(driver_list::driver(cloneof).type.fullname())); + } + else + { + str << _("Driver is Parent\t\n"); + } + + if (flags.has_analog()) + str << _("Analog Controls\tYes\n"); + if (flags.has_keyboard()) + str << _("Keyboard Inputs\tYes\n"); + + if (flags.machine_flags() & machine_flags::NOT_WORKING) + str << _("Overall\tNOT WORKING\n"); + else if ((flags.unemulated_features() | flags.imperfect_features()) & device_t::feature::PROTECTION) + str << _("Overall\tUnemulated Protection\n"); + else + str << _("Overall\tWorking\n"); + + if (flags.unemulated_features() & device_t::feature::GRAPHICS) + str << _("Graphics\tUnimplemented\n"); + else if (flags.unemulated_features() & device_t::feature::PALETTE) + str << _("Graphics\tWrong Colors\n"); + else if (flags.imperfect_features() & device_t::feature::PALETTE) + str << _("Graphics\tImperfect Colors\n"); + else if (flags.imperfect_features() & device_t::feature::GRAPHICS) + str << _("Graphics\tImperfect\n"); + else + str << _("Graphics\tOK\n"); + + if (flags.machine_flags() & machine_flags::NO_SOUND_HW) + str << _("Sound\tNone\n"); + else if (flags.unemulated_features() & device_t::feature::SOUND) + str << _("Sound\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::SOUND) + str << _("Sound\tImperfect\n"); + else + str << _("Sound\tOK\n"); + + if (flags.unemulated_features() & device_t::feature::CAPTURE) + str << _("Capture\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::CAPTURE) + str << _("Capture\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::CAMERA) + str << _("Camera\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::CAMERA) + str << _("Camera\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::MICROPHONE) + str << _("Microphone\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::MICROPHONE) + str << _("Microphone\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::CONTROLS) + str << _("Controls\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::CONTROLS) + str << _("Controls\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::KEYBOARD) + str << _("Keyboard\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::KEYBOARD) + str << _("Keyboard\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::MOUSE) + str << _("Mouse\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::MOUSE) + str << _("Mouse\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::MEDIA) + str << _("Media\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::MEDIA) + str << _("Media\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::DISK) + str << _("Disk\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::DISK) + str << _("Disk\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::PRINTER) + str << _("Printer\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::PRINTER) + str << _("Printer\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::TAPE) + str << _("Mag. Tape\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::TAPE) + str << _("Mag. Tape\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::PUNCH) + str << _("Punch Tape\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::PUNCH) + str << _("Punch Tape\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::DRUM) + str << _("Mag. Drum\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::DRUM) + str << _("Mag. Drum\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::ROM) + str << _("(EP)ROM\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::ROM) + str << _("(EP)ROM\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::COMMS) + str << _("Communications\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::COMMS) + str << _("Communications\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::LAN) + str << _("LAN\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::LAN) + str << _("LAN\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::WAN) + str << _("WAN\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::WAN) + str << _("WAN\tImperfect\n"); + + if (flags.unemulated_features() & device_t::feature::TIMING) + str << _("Timing\tUnimplemented\n"); + else if (flags.imperfect_features() & device_t::feature::TIMING) + str << _("Timing\tImperfect\n"); + + str << ((flags.machine_flags() & machine_flags::MECHANICAL) ? _("Mechanical Machine\tYes\n") : _("Mechanical Machine\tNo\n")); + str << ((flags.machine_flags() & machine_flags::REQUIRES_ARTWORK) ? _("Requires Artwork\tYes\n") : _("Requires Artwork\tNo\n")); + str << ((flags.machine_flags() & machine_flags::CLICKABLE_ARTWORK) ? _("Requires Clickable Artwork\tYes\n") : _("Requires Clickable Artwork\tNo\n")); + if (flags.machine_flags() & machine_flags::NO_COCKTAIL) + str << _("Support Cocktail\tNo\n"); + str << ((flags.machine_flags() & machine_flags::IS_BIOS_ROOT) ? _("Driver is BIOS\tYes\n") : _("Driver is BIOS\tNo\n")); + str << ((flags.machine_flags() & machine_flags::SUPPORTS_SAVE) ? _("Support Save\tYes\n") : _("Support Save\tNo\n")); + str << ((flags.machine_flags() & ORIENTATION_SWAP_XY) ? _("Screen Orientation\tVertical\n") : _("Screen Orientation\tHorizontal\n")); + bool found = false; + for (romload::region const ®ion : romload::entries(driver.rom).get_regions()) + { + if (region.is_diskdata()) + { + found = true; + break; + } + } + str << (found ? _("Requires CHD\tYes\n") : _("Requires CHD\tNo\n")); + + // audit the game first to see if we're going to work + if (ui().options().info_audit()) + { + driver_enumerator enumerator(machine().options(), driver); + enumerator.next(); + media_auditor auditor(enumerator); + media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST); + media_auditor::summary summary_samples = auditor.audit_samples(); + + // if everything looks good, schedule the new driver + if (audit_passed(summary)) + str << _("Media Audit Result\tOK\n"); + else + str << _("Media Audit Result\tBAD\n"); + + if (summary_samples == media_auditor::NONE_NEEDED) + str << _("Samples Audit Result\tNone Needed\n"); + else if (audit_passed(summary_samples)) + str << _("Samples Audit Result\tOK\n"); + else + str << _("Samples Audit Result\tBAD\n"); + } + else + { + str << _("Media Audit\tDisabled\nSamples Audit\tDisabled\n"); + } + + buffer = std::move(str).str(); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/selmenu.h b/src/icludes/frontend/mame/ui/selmenu.h new file mode 100644 index 0000000..6568ca1 --- /dev/null +++ b/src/icludes/frontend/mame/ui/selmenu.h @@ -0,0 +1,334 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Vas Crabb +/*************************************************************************** + + ui/selmenu.h + + MAME system/software selection menu. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_SELMENU_H +#define MAME_FRONTEND_UI_SELMENU_H + +#pragma once + +#include "ui/menu.h" + +#include "audit.h" + +#include "lrucache.h" + +#include +#include +#include +#include + + +struct ui_system_info; +struct ui_software_info; + + +namespace ui { + +class machine_static_info; + +class menu_select_launch : public menu +{ +public: + + virtual ~menu_select_launch() override; + +protected: + static constexpr std::size_t MAX_ICONS_RENDER = 128; + static constexpr std::size_t MAX_VISIBLE_SEARCH = 200; + + // tab navigation + enum class focused_menu + { + MAIN, + LEFT, + RIGHTTOP, + RIGHTBOTTOM + }; + + struct texture_and_bitmap + { + template + texture_and_bitmap(T &&tex) : texture(std::forward(tex)) { } + texture_and_bitmap(texture_and_bitmap &&that) = default; + texture_and_bitmap &operator=(texture_and_bitmap &&that) = default; + + texture_ptr texture; + bitmap_argb32 bitmap; + }; + + template > + using texture_lru = util::lru_cache_map; + + class system_flags + { + public: + system_flags(machine_static_info const &info); + system_flags(system_flags const &) = default; + system_flags(system_flags &&) = default; + system_flags &operator=(system_flags const &) = default; + system_flags &operator=(system_flags &&) = default; + + ::machine_flags::type machine_flags() const { return m_machine_flags; } + device_t::feature_type unemulated_features() const { return m_unemulated_features; } + device_t::feature_type imperfect_features() const { return m_imperfect_features; } + bool has_keyboard() const { return m_has_keyboard; } + bool has_analog() const { return m_has_analog; } + rgb_t status_color() const { return m_status_color; } + + private: + ::machine_flags::type m_machine_flags; + device_t::feature_type m_unemulated_features; + device_t::feature_type m_imperfect_features; + bool m_has_keyboard; + bool m_has_analog; + rgb_t m_status_color; + }; + + class reselect_last + { + public: + static std::string const &driver() { return s_driver; } + static std::string const &software() { return s_software; } + static std::string const &swlist() { return s_swlist; } + + static void reselect(bool value) { s_reselect = value; } + static bool get() { return s_reselect; } + static void reset(); + + static void set_driver(std::string const &name); + static void set_driver(game_driver const &driver) { set_driver(driver.name); } + static void set_software(game_driver const &driver, ui_software_info const &swinfo); + + private: + static std::string s_driver, s_software, s_swlist; + static bool s_reselect; + }; + + menu_select_launch(mame_ui_manager &mui, render_container &container, bool is_swlist); + + focused_menu get_focus() const { return m_focus; } + void set_focus(focused_menu focus) { m_focus = focus; } + void next_image_view(); + void previous_image_view(); + + bool dismiss_error(); + void set_error(reset_options ropt, std::string &&message); + + system_flags const &get_system_flags(game_driver const &driver); + + void launch_system(game_driver const &driver) { launch_system(ui(), driver, nullptr, nullptr, nullptr); } + void launch_system(game_driver const &driver, ui_software_info const &swinfo) { launch_system(ui(), driver, &swinfo, nullptr, nullptr); } + void launch_system(game_driver const &driver, ui_software_info const &swinfo, std::string const &part) { launch_system(ui(), driver, &swinfo, &part, nullptr); } + + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + + // handlers + virtual void inkey_export() = 0; + void inkey_dats(); + + // draw arrow + void draw_common_arrow(float origx1, float origy1, float origx2, float origy2, int current, int dmin, int dmax, float title); + void draw_info_arrow(int ub, float origx1, float origx2, float oy1, float line_height, float text_size, float ud_arrow_width); + + bool draw_error_text(); + + template + float draw_left_panel( + typename Filter::type current, + std::map const &filters, + float x1, float y1, float x2, float y2); + + // icon helpers + void check_for_icons(char const *listname); + std::string make_icon_paths(char const *listname) const; + bool scale_icon(bitmap_argb32 &&src, texture_and_bitmap &dst) const; + + // forcing refresh + void set_switch_image() { m_switch_image = true; } + + template bool select_bios(T const &driver, bool inlist); + bool select_part(software_info const &info, ui_software_info const &ui_info); + + void *get_selection_ptr() const + { + void *const selected_ref(get_selection_ref()); + return (uintptr_t(selected_ref) > skip_main_items) ? selected_ref : m_prev_selected; + } + + static std::string make_system_audit_fail_text(media_auditor const &auditor, media_auditor::summary summary); + static std::string make_software_audit_fail_text(media_auditor const &auditor, media_auditor::summary summary); + static constexpr bool audit_passed(media_auditor::summary summary) + { + return (media_auditor::CORRECT == summary) || (media_auditor::BEST_AVAILABLE == summary) || (media_auditor::NONE_NEEDED == summary); + } + + int m_available_items; + int skip_main_items; + void *m_prev_selected; + int m_total_lines; + int m_topline_datsview; + int m_filter_highlight; + std::string m_search; + +private: + using bitmap_vector = std::vector; + using texture_ptr_vector = std::vector; + + using s_parts = std::unordered_map; + using s_bios = std::vector>; + + class software_parts; + class bios_selection; + + class cache + { + public: + cache(running_machine &machine); + ~cache(); + + bitmap_argb32 &snapx_bitmap() { return *m_snapx_bitmap; } + render_texture *snapx_texture() { return m_snapx_texture.get(); } + bool snapx_driver_is(game_driver const *value) const { return m_snapx_driver == value; } + bool snapx_software_is(ui_software_info const *software) const { return m_snapx_software == software; } + void set_snapx_driver(game_driver const *value) { m_snapx_driver = value; } + void set_snapx_software(ui_software_info const *software) { m_snapx_software = software; } + + bitmap_argb32 &no_avail_bitmap() { return m_no_avail_bitmap; } + + bitmap_vector const &toolbar_bitmaps() { return m_toolbar_bitmaps; } + texture_ptr_vector const &toolbar_textures() { return m_toolbar_textures; } + + void cache_toolbar(running_machine &machine, float width, float height); + + private: + bitmap_ptr m_snapx_bitmap; + texture_ptr m_snapx_texture; + game_driver const *m_snapx_driver; + ui_software_info const *m_snapx_software; + + bitmap_argb32 m_no_avail_bitmap; + + bitmap_vector m_toolbar_bitmaps; + texture_ptr_vector m_toolbar_textures; + }; + + // this is to satisfy the std::any requirement that objects be copyable + class cache_wrapper : public cache + { + public: + cache_wrapper(running_machine &machine) : cache(machine), m_machine(machine) { } + cache_wrapper(cache_wrapper const &that) : cache(that.m_machine), m_machine(that.m_machine) { } + private: + running_machine &m_machine; + }; + + using flags_cache = util::lru_cache_map; + + void reset_pressed() { m_pressed = false; m_repeat = 0; } + bool mouse_pressed() const { return (osd_ticks() >= m_repeat); } + void set_pressed(); + + bool snapx_valid() const { return m_cache.snapx_bitmap().valid(); } + + // draw left panel + virtual float draw_left_panel(float x1, float y1, float x2, float y2) = 0; + float draw_collapsed_left_panel(float x1, float y1, float x2, float y2); + + // draw infos + void infos_render(float x1, float y1, float x2, float y2); + void general_info(ui_system_info const *system, game_driver const &driver, std::string &buffer); + + // get selected software and/or driver + virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const = 0; + virtual bool accept_search() const { return true; } + void select_prev() + { + if (!m_prev_selected) + { + set_selected_index(0); + } + else + { + for (int x = 0; x < item_count(); ++x) + { + if (item(x).ref() == m_prev_selected) + { + set_selected_index(x); + break; + } + } + } + } + void rotate_focus(int dir); + + void draw_toolbar(float x1, float y1, float x2, float y2); + void draw_star(float x0, float y0); + void draw_icon(int linenum, void *selectedref, float x1, float y1); + virtual render_texture *get_icon_texture(int linenum, void *selectedref) = 0; + + void get_title_search(std::string &title, std::string &search); + + // event handling + virtual void handle_keys(uint32_t flags, int &iptkey) override; + virtual void handle_events(uint32_t flags, event &ev) override; + + // draw game list + virtual void draw(uint32_t flags) override; + + // draw right panel + void draw_right_panel(float origx1, float origy1, float origx2, float origy2); + float draw_right_box_title(float x1, float y1, float x2, float y2); + + // images render + void arts_render(float origx1, float origy1, float origx2, float origy2); + std::string arts_render_common(float origx1, float origy1, float origx2, float origy2); + void arts_render_images(bitmap_argb32 &&bitmap, float origx1, float origy1, float origx2, float origy2); + void draw_snapx(float origx1, float origy1, float origx2, float origy2); + + // text for main top/bottom panels + virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const = 0; + virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const = 0; + + // filter navigation + virtual void filter_selected() = 0; + + static void make_audit_fail_text(std::ostream &str, media_auditor const &auditor, media_auditor::summary summary); + static void launch_system(mame_ui_manager &mui, game_driver const &driver, ui_software_info const *swinfo, std::string const *part, int const *bios); + static bool select_part(mame_ui_manager &mui, render_container &container, software_info const &info, ui_software_info const &ui_info); + static bool has_multiple_bios(ui_software_info const &swinfo, s_bios &biosname); + static bool has_multiple_bios(game_driver const &driver, s_bios &biosname); + + bool m_ui_error; + std::string m_error_text; + + game_driver const *m_info_driver; + ui_software_info const *m_info_software; + int m_info_view; + std::vector m_items_list; + std::string m_info_buffer; + std::optional m_info_layout; + + cache &m_cache; + bool m_is_swlist; + focused_menu m_focus; + bool m_pressed; // mouse button held down + osd_ticks_t m_repeat; + + int m_right_visible_lines; // right box lines + + bool m_has_icons; + bool m_switch_image; + bool m_default_image; + uint8_t m_image_view; + flags_cache m_flags; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_SELMENU_H diff --git a/src/icludes/frontend/mame/ui/selsoft.cpp b/src/icludes/frontend/mame/ui/selsoft.cpp new file mode 100644 index 0000000..c5c3230 --- /dev/null +++ b/src/icludes/frontend/mame/ui/selsoft.cpp @@ -0,0 +1,749 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/*************************************************************************** + + ui/selsoft.cpp + + UI software menu. + +***************************************************************************/ + +#include "emu.h" +#include "ui/selsoft.h" + +#include "ui/ui.h" +#include "ui/icorender.h" +#include "ui/inifile.h" +#include "ui/selector.h" + +#include "corestr.h" +#include "drivenum.h" +#include "emuopts.h" +#include "mame.h" +#include "rendutil.h" +#include "softlist_dev.h" +#include "uiinput.h" +#include "luaengine.h" +#include "unicode.h" + +#include +#include +#include +#include +#include + + +namespace ui { + +struct menu_select_software::search_item +{ + search_item(search_item const &) = default; + search_item(search_item &&) = default; + search_item &operator=(search_item const &) = default; + search_item &operator=(search_item &&) = default; + + search_item(ui_software_info const &s) + : software(s) + , ucs_shortname(ustr_from_utf8(normalize_unicode(s.shortname, unicode_normalization_form::D, true))) + , ucs_longname(ustr_from_utf8(normalize_unicode(s.longname, unicode_normalization_form::D, true))) + , ucs_alttitles() + , penalty(1.0) + { + ucs_alttitles.reserve(s.alttitles.size()); + for (std::string const &alttitle : s.alttitles) + ucs_alttitles.emplace_back(ustr_from_utf8(normalize_unicode(alttitle, unicode_normalization_form::D, true))); + } + + void set_penalty(std::u32string const &search) + { + penalty = util::edit_distance(search, ucs_shortname); + if (penalty) + penalty = (std::min)(penalty, util::edit_distance(search, ucs_longname)); + auto it(ucs_alttitles.begin()); + while (penalty && (ucs_alttitles.end() != it)) + penalty = (std::min)(penalty, util::edit_distance(search, *it++)); + } + + std::reference_wrapper software; + std::u32string ucs_shortname; + std::u32string ucs_longname; + std::vector ucs_alttitles; + double penalty; +}; + + + +class menu_select_software::machine_data +{ +public: + machine_data(menu_select_software &menu) + : m_icons(MAX_ICONS_RENDER) + , m_has_empty_start(false) + , m_filter_data() + , m_filters() + , m_filter_type(software_filter::ALL) + , m_swinfo() + , m_searchlist() + { + // add start empty item + m_swinfo.emplace_back(*menu.m_system.driver); + + machine_config config(*menu.m_system.driver, menu.machine().options()); + + // see if any media devices require an image to be loaded + m_has_empty_start = true; + for (device_image_interface &image : image_interface_enumerator(config.root_device())) + { + if (!image.filename() && image.must_be_loaded()) + { + m_has_empty_start = false; + break; + } + } + + // iterate through all software lists + std::vector orphans; + struct orphan_less + { + std::vector &swinfo; + bool operator()(std::string const &a, std::string const &b) const { return a < b; }; + bool operator()(std::string const &a, std::size_t b) const { return a < swinfo[b].parentname; }; + bool operator()(std::size_t a, std::string const &b) const { return swinfo[a].parentname < b; }; + bool operator()(std::size_t a, std::size_t b) const { return swinfo[a].parentname < swinfo[b].parentname; }; + }; + orphan_less const orphan_cmp{ m_swinfo }; + for (software_list_device &swlist : software_list_device_enumerator(config.root_device())) + { + m_filter_data.add_list(swlist.list_name(), swlist.description()); + menu.check_for_icons(swlist.list_name().c_str()); + orphans.clear(); + std::map parentnames; + std::map::const_iterator prevparent(parentnames.end()); + for (const software_info &swinfo : swlist.get_info()) + { + // check for previously-encountered clones + if (swinfo.parentname().empty()) + { + if (parentnames.emplace(swinfo.shortname(), swinfo.longname()).second) + { + auto const clones(std::equal_range(orphans.begin(), orphans.end(), swinfo.shortname(), orphan_cmp)); + for (auto it = clones.first; clones.second != it; ++it) + m_swinfo[*it].parentlongname = swinfo.longname(); + orphans.erase(clones.first, clones.second); + } + else + { + assert([] (auto const x) { return x.first == x.second; } (std::equal_range(orphans.begin(), orphans.end(), swinfo.shortname(), orphan_cmp))); + } + } + + const software_part &part = swinfo.parts().front(); + if (swlist.is_compatible(part) == SOFTWARE_IS_COMPATIBLE) + { + char const *instance_name(nullptr); + char const *type_name(nullptr); + for (device_image_interface &image : image_interface_enumerator(config.root_device())) + { + char const *const interface = image.image_interface(); + if (interface && part.matches_interface(interface)) + { + instance_name = image.instance_name().c_str(); + type_name = image.image_type_name(); + break; + } + } + + if (instance_name && type_name) + { + // add to collection and try to resolve parent if applicable + auto const ins(m_swinfo.emplace(m_swinfo.end(), swinfo, part, *menu.m_system.driver, swlist.list_name(), instance_name, type_name)); + if (!swinfo.parentname().empty()) + { + if ((parentnames.end() == prevparent) || (swinfo.parentname() != prevparent->first)) + prevparent = parentnames.find(swinfo.parentname()); + + if (parentnames.end() != prevparent) + { + ins->parentlongname = prevparent->second; + } + else + { + orphans.emplace( + std::upper_bound(orphans.begin(), orphans.end(), swinfo.parentname(), orphan_cmp), + std::distance(m_swinfo.begin(), ins)); + } + } + + // populate filter choices + m_filter_data.add_region(ins->longname); + m_filter_data.add_publisher(ins->publisher); + m_filter_data.add_year(ins->year); + for (software_info_item const &i : ins->info) + m_filter_data.add_info(i); + m_filter_data.add_device_type(ins->devicetype); + } + } + } + } + + std::string searchstr, curpath; + for (auto &elem : m_filter_data.list_names()) + { + path_iterator path(menu.machine().options().media_path()); + while (path.next(curpath)) + { + searchstr.assign(curpath).append(PATH_SEPARATOR).append(elem).append(";"); + file_enumerator fpath(searchstr.c_str()); + + // iterate while we get new objects + osd::directory::entry const *dir; + while ((dir = fpath.next()) != nullptr) + { + std::string name; + if (dir->type == osd::directory::entry::entry_type::FILE) + name = strmakelower(core_filename_extract_base(dir->name, true)); + else if (dir->type == osd::directory::entry::entry_type::DIR && strcmp(dir->name, ".") != 0) + name = strmakelower(dir->name); + else + continue; + + for (auto & yelem : m_swinfo) + if (yelem.shortname == name && yelem.listname == elem) + { + yelem.available = true; + break; + } + } + } + } + + // sort array + std::collate const &coll = std::use_facet >(std::locale()); + auto const compare_names = + [&coll] (std::string const &x, std::string const &y) -> bool + { + std::wstring const wx = wstring_from_utf8(x); + std::wstring const wy = wstring_from_utf8(y); + return 0 > coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size()); + }; + std::stable_sort( + m_swinfo.begin() + 1, + m_swinfo.end(), + [&compare_names] (ui_software_info const &a, ui_software_info const &b) -> bool + { + bool const clonex = !a.parentname.empty() && !a.parentlongname.empty(); + bool const cloney = !b.parentname.empty() && !b.parentlongname.empty(); + + if (!clonex && !cloney) + { + return compare_names(a.longname, b.longname); + } + else if (!clonex && cloney) + { + if ((a.shortname == b.parentname) && (a.instance == b.instance)) + return true; + else + return compare_names(a.longname, b.parentlongname); + } + else if (clonex && !cloney) + { + if ((a.parentname == b.shortname) && (a.instance == b.instance)) + return false; + else + return compare_names(a.parentlongname, b.longname); + } + else if ((a.parentname == b.parentname) && (a.instance == b.instance)) + { + return compare_names(a.longname, b.longname); + } + else + { + return compare_names(a.parentlongname, b.parentlongname); + } + }); + + // start populating search info in background + m_search_thread = std::make_unique( + [this] () + { + m_searchlist.reserve(m_swinfo.size()); + for (ui_software_info const &sw : m_swinfo) + { + if (!sw.startempty) + m_searchlist.emplace_back(sw); + } + }); + + // build derivative filter data + m_filter_data.finalise(); + + // load custom filters info from file + emu_file file(menu.ui().options().ui_path(), OPEN_FLAG_READ); + if (!file.open(util::string_format("custom_%s_filter.ini", menu.m_system.driver->name))) + { + software_filter::ptr flt(software_filter::create(file, m_filter_data)); + if (flt) + m_filters.emplace(flt->get_type(), std::move(flt)); + file.close(); + } + } + + ~machine_data() + { + if (m_search_thread) + m_search_thread->join(); + } + + icon_cache &icons() { return m_icons; } + + bool has_empty_start() const noexcept { return m_has_empty_start; } + + filter_map const &filters() const noexcept { return m_filters; } + + software_filter::type filter_type() const noexcept { return m_filter_type; } + void set_filter_type(software_filter::type type) noexcept { m_filter_type = type; } + + software_filter const *current_filter() const noexcept + { + auto const found(m_filters.find(m_filter_type)); + return (m_filters.end() != found) ? found->second.get() : nullptr; + } + + software_filter &get_filter(software_filter::type type) + { + filter_map::const_iterator it(m_filters.find(type)); + if (m_filters.end() != it) + return *it->second; + else + return *m_filters.emplace(type, software_filter::create(type, m_filter_data)).first->second; + } + + std::vector const &swinfo() const noexcept { return m_swinfo; } + + std::vector const &find_matches(std::string const &search) + { + // ensure search list is populated + if (m_search_thread) + { + m_search_thread->join(); + m_search_thread.reset(); + } + + // update search + const std::u32string ucs_search(ustr_from_utf8(normalize_unicode(search, unicode_normalization_form::D, true))); + for (search_item &entry : m_searchlist) + entry.set_penalty(ucs_search); + + // sort according to edit distance + std::stable_sort( + m_searchlist.begin(), + m_searchlist.end(), + [] (search_item const &lhs, search_item const &rhs) { return lhs.penalty < rhs.penalty; }); + + // return reference to search results + return m_searchlist; + } + +private: + icon_cache m_icons; + bool m_has_empty_start; + software_filter_data m_filter_data; + filter_map m_filters; + software_filter::type m_filter_type; + std::vector m_swinfo; + std::vector m_searchlist; + + std::unique_ptr m_search_thread; +}; + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_select_software::menu_select_software(mame_ui_manager &mui, render_container &container, ui_system_info const &system) + : menu_select_launch(mui, container, true) + , m_icon_paths() + , m_system(system) + , m_displaylist() +{ + reselect_last::reselect(false); + + using machine_data_cache = util::lru_cache_map >; + auto &cached(mui.get_session_data(8)[system.driver]); + if (!cached) + cached = std::make_shared(*this); + m_data = cached; + + m_filter_highlight = m_data->filter_type(); + + set_switch_image(); + ui_globals::cur_sw_dats_view = 0; + ui_globals::cur_sw_dats_total = 1; +} + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_select_software::~menu_select_software() +{ +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_select_software::handle(event const *ev) +{ + if (m_prev_selected == nullptr) + m_prev_selected = item(0).ref(); + + // FIXME: everything above here used run before events were processed + + // process the menu + if (ev) + { + if (dismiss_error()) + { + // reset the error on any subsequent menu event + } + else switch (ev->iptkey) + { + case IPT_UI_SELECT: + if ((get_focus() == focused_menu::MAIN) && ev->itemref) + inkey_select(ev); + break; + + case IPT_UI_LEFT: + if (ui_globals::rpanel == RP_IMAGES) + { + // Images + previous_image_view(); + } + else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view > 0) + { + // Infos + ui_globals::cur_sw_dats_view--; + m_topline_datsview = 0; + } + break; + + case IPT_UI_RIGHT: + if (ui_globals::rpanel == RP_IMAGES) + { + // Images + next_image_view(); + } + else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view < (ui_globals::cur_sw_dats_total - 1)) + { + // Infos + ui_globals::cur_sw_dats_view++; + m_topline_datsview = 0; + } + break; + + case IPT_UI_UP: + if ((get_focus() == focused_menu::LEFT) && (software_filter::FIRST < m_filter_highlight)) + --m_filter_highlight; + break; + + case IPT_UI_DOWN: + if ((get_focus() == focused_menu::LEFT) && (software_filter::LAST > m_filter_highlight)) + ++m_filter_highlight; + break; + + case IPT_UI_HOME: + if (get_focus() == focused_menu::LEFT) + m_filter_highlight = software_filter::FIRST; + break; + + case IPT_UI_END: + if (get_focus() == focused_menu::LEFT) + m_filter_highlight = software_filter::LAST; + break; + + case IPT_UI_DATS: + inkey_dats(); + break; + + default: + if (ev->itemref) + { + if (ev->iptkey == IPT_UI_FAVORITES) + { + // handle UI_FAVORITES + ui_software_info *swinfo = (ui_software_info *)ev->itemref; + + if ((uintptr_t)swinfo > 2) + { + favorite_manager &mfav = mame_machine_manager::instance()->favorite(); + if (!mfav.is_favorite_system_software(*swinfo)) + { + mfav.add_favorite_software(*swinfo); + machine().popmessage(_("%s\n added to favorites list."), swinfo->longname); + } + + else + { + machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname); + mfav.remove_favorite_software(*swinfo); + } + } + } + } + } + } + + // if we're in an error state, overlay an error message + draw_error_text(); +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_select_software::populate(float &customtop, float &custombottom) +{ + for (auto &icon : m_data->icons()) // TODO: why is this here? maybe better on resize or setting change? + icon.second.texture.reset(); + + int old_software = -1; + + // start with an empty list + m_displaylist.clear(); + software_filter const *const flt(m_data->current_filter()); + + // no active search + if (m_search.empty()) + { + // add an item to start empty or let the user use the file manager + item_append( + m_data->has_empty_start() ? _("[Start empty]") : _("[Use file manager]"), + 0, + (void *)&m_data->swinfo()[0]); + + if (!flt) + std::copy(std::next(m_data->swinfo().begin()), m_data->swinfo().end(), std::back_inserter(m_displaylist)); + else + flt->apply(std::next(m_data->swinfo().begin()), m_data->swinfo().end(), std::back_inserter(m_displaylist)); + } + else + { + std::vector const &searchlist = m_data->find_matches(m_search); + + if (!flt) + { + std::transform( + searchlist.begin(), + std::next(searchlist.begin(), (std::min)(searchlist.size(), MAX_VISIBLE_SEARCH)), + std::back_inserter(m_displaylist), + [] (search_item const &entry) { return entry.software; }); + } + else + { + for (auto it = searchlist.begin(); (searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it) + { + if (flt->apply(it->software)) + m_displaylist.emplace_back(it->software); + } + } + } + + // iterate over entries + for (size_t curitem = 0; curitem < m_displaylist.size(); ++curitem) + { + if (reselect_last::software() == "[Start empty]" && !reselect_last::driver().empty()) + old_software = 0; + else if (m_displaylist[curitem].get().shortname == reselect_last::software() && m_displaylist[curitem].get().listname == reselect_last::swlist()) + old_software = curitem + 1; + + item_append( + m_displaylist[curitem].get().longname, m_displaylist[curitem].get().devicetype, + m_displaylist[curitem].get().parentname.empty() ? 0 : FLAG_INVERT, (void *)&m_displaylist[curitem].get()); + } + + // configure the custom rendering + skip_main_items = 0; + customtop = 4.0f * ui().get_line_height() + 5.0f * ui().box_tb_border(); + custombottom = 4.0f * ui().get_line_height() + 4.0f * ui().box_tb_border(); + + if (old_software != -1) + { + set_selected_index(old_software); + top_line = selected_index() - (ui_globals::visible_sw_lines / 2); + } + + reselect_last::reset(); +} + + +//------------------------------------------------- +// handle select key event +//------------------------------------------------- + +void menu_select_software::inkey_select(const event *menu_event) +{ + ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref; + driver_enumerator drivlist(machine().options(), *ui_swinfo->driver); + media_auditor auditor(drivlist); + drivlist.next(); + + // audit the system ROMs first to see if we're going to work + media_auditor::summary const sysaudit = auditor.audit_media(AUDIT_VALIDATE_FAST); + if (!audit_passed(sysaudit)) + { + set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, sysaudit)); + } + else if (ui_swinfo->startempty == 1) + { + if (!select_bios(*ui_swinfo->driver, true)) + { + reselect_last::reselect(true); + launch_system(*ui_swinfo->driver, *ui_swinfo); + } + } + else + { + // now audit the software + software_list_device *swlist = software_list_device::find_by_name(*drivlist.config(), ui_swinfo->listname); + const software_info *swinfo = swlist->find(ui_swinfo->shortname); + media_auditor::summary const swaudit = auditor.audit_software(*swlist, *swinfo, AUDIT_VALIDATE_FAST); + + if (audit_passed(swaudit)) + { + if (!select_bios(*ui_swinfo, false) && !select_part(*swinfo, *ui_swinfo)) + { + reselect_last::reselect(true); + launch_system(drivlist.driver(), *ui_swinfo); + } + } + else + { + // otherwise, display an error + set_error(reset_options::REMEMBER_REF, make_software_audit_fail_text(auditor, swaudit)); + } + } +} + + +//------------------------------------------------- +// draw left box +//------------------------------------------------- + +float menu_select_software::draw_left_panel(float x1, float y1, float x2, float y2) +{ + return menu_select_launch::draw_left_panel(m_data->filter_type(), m_data->filters(), x1, y1, x2, y2); +} + + +//------------------------------------------------- +// get (possibly cached) icon texture +//------------------------------------------------- + +render_texture *menu_select_software::get_icon_texture(int linenum, void *selectedref) +{ + ui_software_info const *const swinfo(reinterpret_cast(selectedref)); + assert(swinfo); + + if (swinfo->startempty) + return nullptr; + + icon_cache::iterator icon(m_data->icons().find(swinfo)); + if ((m_data->icons().end() == icon) || !icon->second.texture) + { + std::map::iterator paths(m_icon_paths.find(swinfo->listname)); + if (m_icon_paths.end() == paths) + paths = m_icon_paths.emplace(swinfo->listname, make_icon_paths(swinfo->listname.c_str())).first; + + // allocate an entry or allocate a texture on forced redraw + if (m_data->icons().end() == icon) + { + icon = m_data->icons().emplace(swinfo, texture_ptr(machine().render().texture_alloc(), machine().render())).first; + } + else + { + assert(!icon->second.texture); + icon->second.texture.reset(machine().render().texture_alloc()); + } + + bitmap_argb32 tmp; + emu_file snapfile(std::string(paths->second), OPEN_FLAG_READ); + if (!snapfile.open(std::string(swinfo->shortname) + ".ico")) + { + render_load_ico_highest_detail(snapfile, tmp); + snapfile.close(); + } + if (!tmp.valid() && !swinfo->parentname.empty() && !snapfile.open(std::string(swinfo->parentname) + ".ico")) + { + render_load_ico_highest_detail(snapfile, tmp); + snapfile.close(); + } + + scale_icon(std::move(tmp), icon->second); + } + + return icon->second.bitmap.valid() ? icon->second.texture.get() : nullptr; +} + + +//------------------------------------------------- +// get selected software and/or driver +//------------------------------------------------- + +void menu_select_software::get_selection(ui_software_info const *&software, ui_system_info const *&system) const +{ + software = reinterpret_cast(get_selection_ptr()); + system = &m_system; +} + + +void menu_select_software::make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const +{ + // determine the text for the header + int vis_item = !m_search.empty() ? m_available_items : (m_available_items - 1); + line0 = string_format(_("%1$s %2$s ( %3$d / %4$d software packages )"), emulator_info::get_appname(), bare_build_version, vis_item, m_data->swinfo().size() - 1); + line1 = string_format(_("Driver: \"%1$s\" software list "), m_system.description); + + software_filter const *const it(m_data->current_filter()); + char const *const filter(it ? it->filter_text() : nullptr); + if (filter) + line2 = string_format(_("%1$s: %2$s - Search: %3$s_"), it->display_name(), filter, m_search); + else + line2 = string_format(_("Search: %1$s_"), m_search); +} + + +std::string menu_select_software::make_software_description(ui_software_info const &software, ui_system_info const *system) const +{ + // show list/item to make it less confusing when there are multiple lists mixed + return string_format(_("Software list/item: %1$s:%2$s"), software.listname, software.shortname); +} + + +void menu_select_software::filter_selected() +{ + if ((software_filter::FIRST <= m_filter_highlight) && (software_filter::LAST >= m_filter_highlight)) + { + m_data->get_filter(software_filter::type(m_filter_highlight)).show_ui( + ui(), + container(), + [this] (software_filter &filter) + { + software_filter::type const new_type(filter.get_type()); + if (software_filter::CUSTOM == new_type) + { + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(util::string_format("custom_%s_filter.ini", m_system.driver->name))) + { + filter.save_ini(file, 0); + file.close(); + } + } + m_data->set_filter_type(new_type); + reset(reset_options::REMEMBER_REF); + }); + } +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/selsoft.h b/src/icludes/frontend/mame/ui/selsoft.h new file mode 100644 index 0000000..c91495d --- /dev/null +++ b/src/icludes/frontend/mame/ui/selsoft.h @@ -0,0 +1,74 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota, Vas Crabb +/*************************************************************************** + + ui/selsoft.h + + UI software menu. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_SELSOFT_H +#define MAME_FRONTEND_UI_SELSOFT_H + +#pragma once + +#include "ui/selmenu.h" +#include "ui/utils.h" + +#include "lrucache.h" + +#include +#include +#include +#include + + +namespace ui { + +// Menu Class +class menu_select_software : public menu_select_launch +{ +public: + menu_select_software(mame_ui_manager &mui, render_container &container, ui_system_info const &system); + virtual ~menu_select_software() override; + +private: + using filter_map = std::map; + using icon_cache = texture_lru; + + struct search_item; + class machine_data; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + // drawing + virtual float draw_left_panel(float x1, float y1, float x2, float y2) override; + virtual render_texture *get_icon_texture(int linenum, void *selectedref) override; + + // get selected software and/or driver + virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const override; + + // text for main top/bottom panels + virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const override; + virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const override; + + // filter navigation + virtual void filter_selected() override; + + // toolbar + virtual void inkey_export() override { throw false; } + + // handlers + void inkey_select(const event *menu_event); + + std::map m_icon_paths; + ui_system_info const &m_system; + std::shared_ptr m_data; + + std::vector > m_displaylist; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_SELSOFT_H diff --git a/src/icludes/frontend/mame/ui/simpleselgame.cpp b/src/icludes/frontend/mame/ui/simpleselgame.cpp new file mode 100644 index 0000000..7cb346e --- /dev/null +++ b/src/icludes/frontend/mame/ui/simpleselgame.cpp @@ -0,0 +1,430 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/simpleselgame.cpp + + Game selector + +***************************************************************************/ + +#include "emu.h" + +#include "ui/simpleselgame.h" + +#include "ui/info.h" +#include "ui/optsmenu.h" +#include "ui/ui.h" +#include "ui/utils.h" + +#include "audit.h" +#include "drivenum.h" +#include "emuopts.h" +#include "mame.h" +#include "uiinput.h" + +#include + + +namespace ui { + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +simple_menu_select_game::simple_menu_select_game(mame_ui_manager &mui, render_container &container, const char *gamename) + : menu(mui, container) + , m_nomatch(false), m_error(false), m_rerandomize(false) + , m_search() + , m_driverlist(driver_list::total() + 1) + , m_drivlist() + , m_cached_driver(nullptr) + , m_cached_flags(machine_flags::NOT_WORKING) + , m_cached_unemulated(device_t::feature::NONE), m_cached_imperfect(device_t::feature::NONE) + , m_cached_color(ui().colors().background_color()) +{ + set_process_flags(PROCESS_IGNOREPAUSE); + set_needs_prev_menu_item(false); + build_driver_list(); + if (gamename) + m_search.assign(gamename); + m_matchlist[0] = -1; +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +simple_menu_select_game::~simple_menu_select_game() +{ +} + + + +//------------------------------------------------- +// build_driver_list - build a list of available +// drivers +//------------------------------------------------- + +void simple_menu_select_game::build_driver_list() +{ + // start with an empty list + m_drivlist = std::make_unique(machine().options()); + m_drivlist->exclude_all(); + + // open a path to the ROMs and find them in the array + file_enumerator path(machine().options().media_path()); + + // iterate while we get new objects + for (const osd::directory::entry *dir = path.next(); dir; dir = path.next()) + { + char drivername[50]; + char *dst = drivername; + const char *src; + + // build a name for it + for (src = dir->name; *src != 0 && *src != '.' && dst < &drivername[std::size(drivername) - 1]; src++) + *dst++ = tolower((uint8_t)*src); + *dst = 0; + + int drivnum = m_drivlist->find(drivername); + if (drivnum != -1) + m_drivlist->include(drivnum); + } + + // now build the final list + m_drivlist->reset(); + int listnum = 0; + while (m_drivlist->next()) + m_driverlist[listnum++] = &m_drivlist->driver(); + + // NULL-terminate + m_driverlist[listnum] = nullptr; +} + + + +//------------------------------------------------- +// handle - handle the game select menu +//------------------------------------------------- + +void simple_menu_select_game::handle(event const *ev) +{ + // process the menu + if (ev) + { + if (m_error) + { + // reset the error on any subsequent menu event + m_error = false; + machine().ui_input().reset(); + } + else + { + // handle selections + switch(ev->iptkey) + { + case IPT_UI_SELECT: + inkey_select(*ev); + break; + case IPT_UI_CANCEL: + inkey_cancel(); + break; + case IPT_SPECIAL: + inkey_special(*ev); + break; + } + } + } + + // if we're in an error state, overlay an error message + if (m_error) + { + ui().draw_text_box( + container(), + _("The selected game is missing one or more required ROM or CHD images. " + "Please select a different game.\n\nPress any key to continue."), + text_layout::text_justify::CENTER, 0.5f, 0.5f, UI_RED_COLOR); + } +} + + +//------------------------------------------------- +// inkey_select +//------------------------------------------------- + +void simple_menu_select_game::inkey_select(const event &menu_event) +{ + const game_driver *driver = (const game_driver *)menu_event.itemref; + + if ((uintptr_t)driver == 1) // special case for configure inputs + { + menu::stack_push( + ui(), + container(), + [this] () { reset(reset_options::SELECT_FIRST); }); + } + else if (!driver) // special case for previous menu + { + stack_pop(); + } + else // anything else is a driver + { + // audit the game first to see if we're going to work + driver_enumerator enumerator(machine().options(), *driver); + enumerator.next(); + media_auditor auditor(enumerator); + media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST); + + if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED) + { + // if everything looks good, schedule the new driver + mame_machine_manager::instance()->schedule_new_driver(*driver); + machine().schedule_hard_reset(); + stack_reset(); + } + else + { + // otherwise, display an error + reset(reset_options::REMEMBER_REF); + m_error = true; + } + } +} + + +//------------------------------------------------- +// inkey_cancel +//------------------------------------------------- + +void simple_menu_select_game::inkey_cancel() +{ + // escape pressed with non-empty text clears the text + if (!m_search.empty()) + { + m_search.clear(); + m_rerandomize = true; + reset(reset_options::SELECT_FIRST); + } +} + + +//------------------------------------------------- +// inkey_special - typed characters append to the buffer +//------------------------------------------------- + +void simple_menu_select_game::inkey_special(const event &menu_event) +{ + // typed characters append to the buffer + size_t old_size = m_search.size(); + if (input_character(m_search, menu_event.unichar, uchar_is_printable)) + { + if (m_search.size() < old_size) + m_rerandomize = true; + reset(reset_options::SELECT_FIRST); + } +} + + +//------------------------------------------------- +// populate - populate the game select menu +//------------------------------------------------- + +void simple_menu_select_game::populate(float &customtop, float &custombottom) +{ + int matchcount; + int curitem; + + for (curitem = matchcount = 0; m_driverlist[curitem] != nullptr && matchcount < VISIBLE_GAMES_IN_LIST; curitem++) + matchcount++; + + // if nothing there, add a single multiline item and return + m_nomatch = !matchcount; + + // otherwise, rebuild the match list + if (matchcount) + { + assert(m_drivlist != nullptr); + if (!m_search.empty() || m_matchlist[0] == -1 || m_rerandomize) + m_drivlist->find_approximate_matches(m_search, matchcount, m_matchlist); + m_rerandomize = false; + + // iterate over entries + for (curitem = 0; curitem < matchcount; curitem++) + { + int curmatch = m_matchlist[curitem]; + if (curmatch != -1) + { + int cloneof = m_drivlist->non_bios_clone(curmatch); + item_append( + m_drivlist->driver(curmatch).type.fullname(), + m_drivlist->driver(curmatch).name, + (cloneof == -1) ? 0 : FLAG_INVERT, + (void *)&m_drivlist->driver(curmatch)); + } + } + item_append(menu_item_type::SEPARATOR); + } + + // if we're forced into this, allow general input configuration as well + if (stack_has_special_main_menu()) + { + item_append(_("Configure Options"), 0, (void *)1); + item_append(_("Exit"), 0, nullptr); + } + else + { + item_append(_("Return to Previous Menu"), 0, nullptr); + } + + // configure the custom rendering + customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); + custombottom = 4.0f * ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + + +//------------------------------------------------- +// custom_render - perform our special rendering +//------------------------------------------------- + +void simple_menu_select_game::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + // if no matches, display the error message + if (m_nomatch) + { + ui().draw_text_box( + container(), + string_format( + _("No machines found. Please check the rompath specified in the %1$s.ini file.\n\n" + "If this is your first time using %2$s, please see the config.txt file in " + "the docs directory for information on configuring %2$s."), + emulator_info::get_configname(), + emulator_info::get_appname()), + text_layout::text_justify::CENTER, + 0.5f, origy2 + ui().box_tb_border() + (0.5f * (bottom - ui().box_tb_border())), + UI_RED_COLOR); + return; + } + + const game_driver *driver; + std::string tempbuf[5]; + + // display the current typeahead + if (!m_search.empty()) + tempbuf[0] = string_format(_("Type name or select: %1$s_"), m_search); + else + tempbuf[0] = _("Type name or select: (random)"); + + // draw the top box + draw_text_box( + tempbuf, tempbuf + 1, + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); + + // determine the text to render below + driver = ((uintptr_t)selectedref > 1) ? (const game_driver *)selectedref : nullptr; + if (driver) + { + // first line is game name + tempbuf[0] = string_format(_("%1$-.100s"), driver->type.fullname()); + + // next line is year, manufacturer + tempbuf[1] = string_format(_("%1$s, %2$-.100s"), driver->year, driver->manufacturer); + + // next line source path + tempbuf[2] = string_format(_("Driver: %1$s"), core_filename_extract_base(driver->type.source())); + + // update cached values if selection changed + if (driver != m_cached_driver) + { + emu_options clean_options; + machine_static_info const info(ui().options(), machine_config(*driver, clean_options)); + m_cached_driver = driver; + m_cached_flags = info.machine_flags(); + m_cached_unemulated = info.unemulated_features(); + m_cached_imperfect = info.imperfect_features(); + m_cached_color = info.status_color(); + } + + // next line is overall driver status + if (m_cached_flags & machine_flags::NOT_WORKING) + tempbuf[3] = _("Overall: NOT WORKING"); + else if ((m_cached_unemulated | m_cached_imperfect) & device_t::feature::PROTECTION) + tempbuf[3] = _("Overall: Unemulated Protection"); + else + tempbuf[3] = _("Overall: Working"); + + // next line is graphics, sound status + if (m_cached_unemulated & device_t::feature::GRAPHICS) + tempbuf[4] = _("Graphics: Unimplemented, "); + else if ((m_cached_unemulated | m_cached_imperfect) & (device_t::feature::GRAPHICS | device_t::feature::PALETTE)) + tempbuf[4] = _("Graphics: Imperfect, "); + else + tempbuf[4] = _("Graphics: OK, "); + + if (m_cached_flags & machine_flags::NO_SOUND_HW) + tempbuf[4].append(_("Sound: None")); + else if (m_cached_unemulated & device_t::feature::SOUND) + tempbuf[4].append(_("Sound: Unimplemented")); + else if (m_cached_imperfect & device_t::feature::SOUND) + tempbuf[4].append(_("Sound: Imperfect")); + else + tempbuf[4].append(_("Sound: OK")); + } + else + { + const char *s = emulator_info::get_copyright(); + unsigned line = 0; + + // first line is version string + tempbuf[line++] = string_format("%s %s", emulator_info::get_appname(), build_version); + + // output message + while (line < std::size(tempbuf)) + { + if (!(*s == 0 || *s == '\n')) + tempbuf[line].push_back(*s); + + if (*s == '\n') + { + line++; + s++; + } else if (*s != 0) + s++; + else + line++; + } + } + + // draw the bottom box + draw_text_box( + tempbuf, tempbuf + 4, + origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, true, + ui().colors().text_color(), driver ? m_cached_color : ui().colors().background_color(), 1.0f); +} + + +//------------------------------------------------- +// force_game_select - force the game +// select menu to be visible and inescapable +//------------------------------------------------- + +void simple_menu_select_game::force_game_select(mame_ui_manager &mui, render_container &container) +{ + char *gamename = (char *)mui.machine().options().system_name(); + + // reset the menu stack + + // drop any existing menus and start the system selection menu + menu::stack_reset(mui); + menu::stack_push_special_main(mui, container, gamename); + mui.show_menu(); + + // make sure MAME is paused + mui.machine().pause(); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/simpleselgame.h b/src/icludes/frontend/mame/ui/simpleselgame.h new file mode 100644 index 0000000..e5ea310 --- /dev/null +++ b/src/icludes/frontend/mame/ui/simpleselgame.h @@ -0,0 +1,66 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/selgame.h + + Game selector + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_SIMPLESELGAME_H +#define MAME_FRONTEND_UI_SIMPLESELGAME_H + +#pragma once + +#include "menu.h" + +class driver_enumerator; + +namespace ui { + +class simple_menu_select_game : public menu +{ +public: + simple_menu_select_game(mame_ui_manager &mui, render_container &container, const char *gamename); + virtual ~simple_menu_select_game(); + + // force game select menu + static void force_game_select(mame_ui_manager &mui, render_container &container); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual bool custom_ui_cancel() override { return !m_search.empty(); } + +private: + enum { VISIBLE_GAMES_IN_LIST = 15 }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + // internal methods + void build_driver_list(); + void inkey_select(const event &menu_event); + void inkey_cancel(); + void inkey_special(const event &menu_event); + + // internal state + bool m_nomatch; + bool m_error; + bool m_rerandomize; + std::string m_search; + int m_matchlist[VISIBLE_GAMES_IN_LIST]; + std::vector m_driverlist; + std::unique_ptr m_drivlist; + + // cached driver flags + const game_driver * m_cached_driver; + machine_flags::type m_cached_flags; + device_t::feature_type m_cached_unemulated; + device_t::feature_type m_cached_imperfect; + rgb_t m_cached_color; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_SIMPLESELGAME_H diff --git a/src/icludes/frontend/mame/ui/slider.cpp b/src/icludes/frontend/mame/ui/slider.cpp new file mode 100644 index 0000000..f9eb861 --- /dev/null +++ b/src/icludes/frontend/mame/ui/slider.cpp @@ -0,0 +1,20 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz + +/*************************************************************************** + + ui/slider.h + + Internal data representation for an adjustment slider. + +***************************************************************************/ + +#include "slider.h" + +slider_state::slider_state() +{ +} + +slider_state::~slider_state() +{ +} diff --git a/src/icludes/frontend/mame/ui/slider.h b/src/icludes/frontend/mame/ui/slider.h new file mode 100644 index 0000000..f39b3dd --- /dev/null +++ b/src/icludes/frontend/mame/ui/slider.h @@ -0,0 +1,44 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods + +/*************************************************************************** + + ui/slider.h + + Internal data representation for an adjustment slider. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_MAME_UI_SLIDER_H +#define MAME_FRONTEND_MAME_UI_SLIDER_H + +#pragma once + +#include +#include + +#define SLIDER_NOCHANGE 0x12345678 + +typedef std::function slider_update; + +struct slider_state +{ + slider_state(const std::string &title, std::int32_t min, std::int32_t def, std::int32_t max, std::int32_t inc, slider_update func) + : update(func), minval(min), defval(def), maxval(max), incval(inc), description(title) + { + } + + slider_state(std::string &&title, std::int32_t min, std::int32_t def, std::int32_t max, std::int32_t inc, slider_update func) + : update(func), minval(min), defval(def), maxval(max), incval(inc), description(std::move(title)) + { + } + + slider_update update; // callback + std::int32_t minval; // minimum value + std::int32_t defval; // default value + std::int32_t maxval; // maximum value + std::int32_t incval; // increment value + std::string description; // textual description +}; + +#endif // MAME_FRONTEND_MAME_UI_SLIDER_H diff --git a/src/icludes/frontend/mame/ui/sliders.cpp b/src/icludes/frontend/mame/ui/sliders.cpp new file mode 100644 index 0000000..404e174 --- /dev/null +++ b/src/icludes/frontend/mame/ui/sliders.cpp @@ -0,0 +1,333 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/********************************************************************* + + ui/sliders.cpp + + Internal MAME menus for the user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/sliders.h" + +#include "ui/slider.h" +#include "ui/ui.h" + +#include "osdepend.h" + + +namespace ui { + +menu_sliders::menu_sliders(mame_ui_manager &mui, render_container &container, bool menuless_mode) + : menu(mui, container) + , m_menuless_mode(menuless_mode) + , m_hidden(menuless_mode) +{ + set_one_shot(menuless_mode); + set_needs_prev_menu_item(!menuless_mode); + set_process_flags(PROCESS_LR_REPEAT | (m_hidden ? PROCESS_CUSTOM_ONLY : 0)); +} + +menu_sliders::~menu_sliders() +{ +} + + +//------------------------------------------------- +// menu_sliders - handle the sliders menu +//------------------------------------------------- + +void menu_sliders::handle(event const *ev) +{ + // process the menu + if (ev) + { + if (ev->iptkey == IPT_UI_ON_SCREEN_DISPLAY) + { + // toggle visibility + if (m_menuless_mode) + { + stack_pop(); + } + else + { + m_hidden = !m_hidden; + set_process_flags(PROCESS_LR_REPEAT | (m_hidden ? PROCESS_CUSTOM_ONLY : 0)); + } + + } + else if (ev->itemref && (ev->item->type() == menu_item_type::SLIDER)) + { + // handle keys if there is a valid item selected + const slider_state *slider = (const slider_state *)ev->itemref; + int32_t curvalue = slider->update(nullptr, SLIDER_NOCHANGE); + int32_t increment = 0; + bool const alt_pressed = machine().input().code_pressed(KEYCODE_LALT) || machine().input().code_pressed(KEYCODE_RALT); + bool const ctrl_pressed = machine().input().code_pressed(KEYCODE_LCONTROL) || machine().input().code_pressed(KEYCODE_RCONTROL); + bool const shift_pressed = machine().input().code_pressed(KEYCODE_LSHIFT) || machine().input().code_pressed(KEYCODE_RSHIFT); + + switch (ev->iptkey) + { + // decrease value + case IPT_UI_LEFT: + if (alt_pressed && shift_pressed) + increment = -1; + else if (alt_pressed) + increment = -(curvalue - slider->minval); + else if (shift_pressed) + increment = (slider->incval > 10) ? -(slider->incval / 10) : -1; + else if (ctrl_pressed) + increment = -slider->incval * 10; + else + increment = -slider->incval; + break; + + // increase value + case IPT_UI_RIGHT: + if (alt_pressed && shift_pressed) + increment = 1; + else if (alt_pressed) + increment = slider->maxval - curvalue; + else if (shift_pressed) + increment = (slider->incval > 10) ? (slider->incval / 10) : 1; + else if (ctrl_pressed) + increment = slider->incval * 10; + else + increment = slider->incval; + break; + + // restore default + case IPT_UI_SELECT: + case IPT_UI_CLEAR: + increment = slider->defval - curvalue; + break; + } + + // handle any changes + if (increment != 0) + { + int32_t newvalue = curvalue + increment; + + // clamp within bounds + if (newvalue < slider->minval) + newvalue = slider->minval; + if (newvalue > slider->maxval) + newvalue = slider->maxval; + + // update the slider and recompute the menu + slider->update(nullptr, newvalue); + if (m_menuless_mode) + ui().get_session_data(nullptr) = ev->itemref; + reset(reset_options::REMEMBER_REF); + } + } + else if (m_hidden) + { + // if we are selecting an invalid item and we are hidden, skip to the next one + if (ev->iptkey == IPT_UI_UP || ev->iptkey == IPT_UI_PAGE_UP) + { + // if we got here via up or page up, select the previous item + if (is_first_selected()) + { + select_last_item(); + } + else + { + set_selected_index(selected_index() - 1); + validate_selection(-1); + } + } + else if (ev->iptkey == IPT_UI_DOWN || ev->iptkey == IPT_UI_PAGE_DOWN) + { + // otherwise select the next item + if (is_last_selected()) + select_first_item(); + else + { + set_selected_index(selected_index() + 1); + validate_selection(1); + } + } + } + } +} + + +//------------------------------------------------- +// menu_sliders_populate - populate the sliders +// menu +//------------------------------------------------- + +void menu_sliders::populate(float &customtop, float &custombottom) +{ + std::string tempstring; + + // add UI sliders + std::vector ui_sliders = ui().get_slider_list(); + for (const menu_item &item : ui_sliders) + { + if (item.type() == menu_item_type::SLIDER) + { + slider_state *const slider = reinterpret_cast(item.ref()); + bool display(true); +#if 0 + // FIXME: this test should be reimplemented in a dedicated menu + if (slider->id >= SLIDER_ID_ADJUSTER && slider->id <= SLIDER_ID_ADJUSTER_LAST) + display = reinterpret_cast(slider->arg)->enabled(); +#endif + if (display) + { + int32_t curval = slider->update(&tempstring, SLIDER_NOCHANGE); + uint32_t flags = 0; + if (curval > slider->minval) + flags |= FLAG_LEFT_ARROW; + if (curval < slider->maxval) + flags |= FLAG_RIGHT_ARROW; + item_append(slider->description, tempstring, flags, (void *)slider, menu_item_type::SLIDER); + } + } + else + { + item_append(item); + } + } + + item_append(menu_item_type::SEPARATOR); + + // add OSD options + std::vector osd_sliders = machine().osd().get_slider_list(); + for (const menu_item &item : osd_sliders) + { + if (item.type() == menu_item_type::SLIDER) + { + slider_state* slider = reinterpret_cast(item.ref()); + int32_t curval = slider->update(&tempstring, SLIDER_NOCHANGE); + uint32_t flags = 0; + if (curval > slider->minval) + flags |= FLAG_LEFT_ARROW; + if (curval < slider->maxval) + flags |= FLAG_RIGHT_ARROW; + item_append(slider->description, tempstring, flags, (void *)slider, menu_item_type::SLIDER); + } + else + { + item_append(item); + } + } + + // reselect last slider used in menuless mode + if (m_menuless_mode) + { + auto const ref = ui().get_session_data(nullptr); + if (ref) + set_selection(ref); + } + + custombottom = 2.0f * ui().get_line_height() + 2.0f * ui().box_tb_border(); +} + + +//------------------------------------------------- +// menu_sliders_custom_render - perform our special +// rendering +//------------------------------------------------- + +void menu_sliders::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) +{ + const slider_state *curslider = (const slider_state *)selectedref; + if (curslider != nullptr) + { + float bar_left, bar_area_top, bar_width, bar_area_height, bar_top, bar_bottom, default_x, current_x; + float line_height = ui().get_line_height(); + float percentage, default_percentage; + std::string tempstring; + float text_height; + int32_t curval; + + // determine the current value and text + curval = curslider->update(&tempstring, SLIDER_NOCHANGE); + + // compute the current and default percentages + percentage = (float)(curval - curslider->minval) / (float)(curslider->maxval - curslider->minval); + default_percentage = (float)(curslider->defval - curslider->minval) / (float)(curslider->maxval - curslider->minval); + + // assemble the text + tempstring.insert(0, " ").insert(0, curslider->description); + + // move us to the bottom of the screen, and expand to full width + const float lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container()); + y2 = 1.0f - ui().box_tb_border(); + y1 = y2 - bottom; + x1 = lr_border; + x2 = 1.0f - lr_border; + + // draw extra menu area + ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color()); + y1 += ui().box_tb_border(); + + // determine the text height + ui().draw_text_full( + container(), + tempstring, + 0, 0, x2 - x1 - 2.0f * lr_border, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, + mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), nullptr, &text_height); + + // draw the thermometer + bar_left = x1 + lr_border; + bar_area_top = y1; + bar_width = x2 - x1 - 2.0f * lr_border; + bar_area_height = line_height; + + // compute positions + bar_top = bar_area_top + 0.125f * bar_area_height; + bar_bottom = bar_area_top + 0.875f * bar_area_height; + default_x = bar_left + bar_width * default_percentage; + current_x = bar_left + bar_width * percentage; + + // fill in the percentage + container().add_rect(bar_left, bar_top, current_x, bar_bottom, ui().colors().slider_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + + // draw the top and bottom lines + container().add_line(bar_left, bar_top, bar_left + bar_width, bar_top, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_line(bar_left, bar_bottom, bar_left + bar_width, bar_bottom, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + + // draw default marker + container().add_line(default_x, bar_area_top, default_x, bar_top, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + container().add_line(default_x, bar_bottom, default_x, bar_area_top + bar_area_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + + // draw the actual text + ui().draw_text_full( + container(), + tempstring, + x1 + lr_border, y1 + line_height, x2 - x1 - 2.0f * lr_border, + text_layout::text_justify::CENTER, text_layout::word_wrapping::WORD, + mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(), nullptr, &text_height); + } +} + + +//------------------------------------------------- +// menu_activated - handle menu gaining focus +//------------------------------------------------- + +void menu_sliders::menu_activated() +{ + // scripts or the other form of the menu could have changed something in the mean time + reset(reset_options::REMEMBER_POSITION); +} + + +//------------------------------------------------- +// menu_deactivated - handle menu losing focus +//------------------------------------------------- + +void menu_sliders::menu_deactivated() +{ + // save active slider for next time in menuless mode + if (m_menuless_mode) + ui().get_session_data(nullptr) = get_selection_ref(); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/sliders.h b/src/icludes/frontend/mame/ui/sliders.h new file mode 100644 index 0000000..99a5c6e --- /dev/null +++ b/src/icludes/frontend/mame/ui/sliders.h @@ -0,0 +1,42 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/sliders.h + + Internal MAME menus for the user interface. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_SLIDERS_H +#define MAME_FRONTEND_UI_SLIDERS_H + +#pragma once + +#include "ui/menu.h" + + +namespace ui { + +class menu_sliders : public menu +{ +public: + menu_sliders(mame_ui_manager &mui, render_container &container, bool menuless_mode = false); + virtual ~menu_sliders() override; + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void menu_activated() override; + virtual void menu_deactivated() override; + +private: + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + bool const m_menuless_mode; + bool m_hidden; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_SLIDERS_H diff --git a/src/icludes/frontend/mame/ui/slotopt.cpp b/src/icludes/frontend/mame/ui/slotopt.cpp new file mode 100644 index 0000000..e27e626 --- /dev/null +++ b/src/icludes/frontend/mame/ui/slotopt.cpp @@ -0,0 +1,324 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/********************************************************************* + + ui/slotopt.cpp + + Internal menu for the slot options. + +*********************************************************************/ + +#include "emu.h" + +#include "ui/ui.h" +#include "ui/slotopt.h" +#include "ui/devopt.h" + +#include "emuopts.h" +#include "mameopts.h" + +#include + + +/*************************************************************************** + UTILITY +***************************************************************************/ + +namespace { + +// constants +void *const ITEMREF_RESET = ((void *)1); +constexpr char const DIVIDER[] = "------"; + +} // anonymous namespace + + +/*************************************************************************** + SLOT MENU +***************************************************************************/ + +namespace ui { + +//------------------------------------------------- +// menu_slot_devices constructor +//------------------------------------------------- + +menu_slot_devices::menu_slot_devices(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ +} + +//------------------------------------------------- +// menu_slot_devices destructor +//------------------------------------------------- + +menu_slot_devices::~menu_slot_devices() +{ +} + + +//------------------------------------------------- +// get_current_option - returns the current +// slot option +//------------------------------------------------- + +device_slot_interface::slot_option const *menu_slot_devices::get_current_option(device_slot_interface &slot) const +{ + std::string current; + + if (!slot.fixed()) + { + char const *const slot_option_name = slot.slot_name(); + current = machine().options().slot_option(slot_option_name).value(); + } + else + { + if (!slot.default_option()) + return nullptr; + current.assign(slot.default_option()); + } + + return slot.option(current.c_str()); +} + + +//------------------------------------------------- +// set_slot_device +//------------------------------------------------- + +void menu_slot_devices::set_slot_device(device_slot_interface &slot, std::string_view val) +{ + // we might change slot options; in the spirit of user friendliness, we should record all current + // options + record_current_options(); + + // find the slot option + slot_option &opt(machine().options().slot_option(slot.slot_name())); + + // specify it + opt.specify(val); + + // erase this from our recorded options list - this is the slot we're trying to change! + m_slot_options.erase(slot.slot_name()); + + // refresh any options that we might have annotated earlier + while (try_refresh_current_options()) + ; + + // changing the options may result in options changing; we need to reset + reset(reset_options::REMEMBER_POSITION); +} + + +//------------------------------------------------- +// record_current_options +//------------------------------------------------- + +void menu_slot_devices::record_current_options() +{ + for (device_slot_interface &slot : slot_interface_enumerator(m_config->root_device())) + { + // we're doing this out of a desire to honor user-selectable options; therefore it only + // makes sense to record values for selectable options + if (slot.has_selectable_options()) + { + // get the slot option + const slot_option &opt(machine().options().slot_option(slot.slot_name())); + + // and record the value in our local cache + m_slot_options[slot.slot_name()] = opt.specified_value(); + } + } +} + + +//------------------------------------------------- +// try_refresh_current_options +//------------------------------------------------- + +bool menu_slot_devices::try_refresh_current_options() +{ + // enumerate through all slot options that we've tracked + for (const auto &opt : m_slot_options) + { + // do we have a value different than what we're tracking? + slot_option *slotopt = machine().options().find_slot_option(opt.first); + if (slotopt && slotopt->specified_value() != opt.second) + { + // specify this option (but catch errors) + try + { + slotopt->specify(opt.second); + + // the option was successfully specified; it isn't safe to continue + // checking slots as the act of specifying the slot may have drastically + // changed the options list + return true; + } + catch (options_exception &) + { + // this threw an exception - that is fine; we can just proceed + } + } + } + + // we've went through all options without changing anything + return false; +} + + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_slot_devices::populate(float &customtop, float &custombottom) +{ + // we need to keep our own copy of the machine_config because we + // can change this out from under the caller + m_config = std::make_unique(machine().system(), machine().options()); + + // cycle through all devices for this system + for (device_slot_interface &slot : slot_interface_enumerator(m_config->root_device())) + { + // does this slot have any selectable options? + bool has_selectable_options = slot.has_selectable_options(); + + // name this option + std::string opt_name(DIVIDER); + device_slot_interface::slot_option const *option = get_current_option(slot); + if (option) + { + opt_name = has_selectable_options + ? option->name() + : string_format(_("%s [internal]"), option->name()); + } + + // choose item flags + uint32_t const item_flags = has_selectable_options + ? FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW + : FLAG_DISABLE; + + item_append(slot.slot_name(), opt_name, item_flags, (void *)&slot); + } + item_append(menu_item_type::SEPARATOR); + item_append(_("Reset Machine"), 0, ITEMREF_RESET); + + // leave space for the name of the current option at the bottom + custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border(); +} + + +//------------------------------------------------- +// custom_render - draw extra menu content +//------------------------------------------------- + +void menu_slot_devices::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + if (selectedref && (ITEMREF_RESET != selectedref)) + { + device_slot_interface *const slot(reinterpret_cast(selectedref)); + device_slot_interface::slot_option const *const option(get_current_option(*slot)); + char const *const text[] = { option ? option->devtype().fullname() : _("[empty slot]") }; + draw_text_box( + std::begin(text), std::end(text), + origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom, + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); + } +} + + +//------------------------------------------------- +// handle - process an input event +//------------------------------------------------- + +void menu_slot_devices::handle(event const *ev) +{ + // process the menu + if (ev && ev->itemref != nullptr) + { + if (ev->itemref == ITEMREF_RESET) + { + if (ev->iptkey == IPT_UI_SELECT) + machine().schedule_hard_reset(); + } + else if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT) + { + device_slot_interface *slot = (device_slot_interface *)ev->itemref; + rotate_slot_device(*slot, ev->iptkey == IPT_UI_LEFT ? step_t::PREVIOUS : step_t::NEXT); + } + else if (ev->iptkey == IPT_UI_SELECT) + { + device_slot_interface *slot = (device_slot_interface *)ev->itemref; + device_slot_interface::slot_option const *const option = get_current_option(*slot); + if (option) + menu::stack_push(ui(), container(), slot, option); + } + } +} + + +//------------------------------------------------- +// rotate_slot_device - rotates the specified slot +//------------------------------------------------- + +void menu_slot_devices::rotate_slot_device(device_slot_interface &slot, menu_slot_devices::step_t step) +{ + // first, we need to make sure our cache of options is up to date + if (m_current_option_list_slot_tag != slot.device().tag()) + { + device_slot_interface::slot_option const *current = get_current_option(slot); + + // build the option list, including the blank option + m_current_option_list.clear(); + m_current_option_list.emplace_back(""); + for (const auto &ent : slot.option_list()) + { + if (ent.second->selectable()) + m_current_option_list.emplace_back(ent.second->name()); + } + + // since the order is indeterminate, we need to sort the options + std::sort(m_current_option_list.begin(), m_current_option_list.end()); + + // find the current position + char const *const target = current ? current->name() : ""; + m_current_option_list_iter = std::find_if( + m_current_option_list.begin(), + m_current_option_list.end(), + [target] (const std::string &opt_value) + { + return opt_value == target; + }); + + // we expect the above search to succeed, because if an internal + // option was selected, the menu item should be disabled + assert(m_current_option_list_iter != m_current_option_list.end()); + + // we've succeeded; don't do this again + m_current_option_list_slot_tag.assign(slot.device().tag()); + } + + // At this point, the current option list and accompanying iterator should + // be good; perform the rotation + switch (step) + { + case step_t::NEXT: + m_current_option_list_iter++; + if (m_current_option_list_iter == m_current_option_list.end()) + m_current_option_list_iter = m_current_option_list.begin(); + break; + + case step_t::PREVIOUS: + if (m_current_option_list_iter == m_current_option_list.begin()) + m_current_option_list_iter = m_current_option_list.end(); + m_current_option_list_iter--; + break; + + default: + throw false; + } + + set_slot_device(slot, *m_current_option_list_iter); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/slotopt.h b/src/icludes/frontend/mame/ui/slotopt.h new file mode 100644 index 0000000..449d226 --- /dev/null +++ b/src/icludes/frontend/mame/ui/slotopt.h @@ -0,0 +1,55 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods +/*************************************************************************** + + ui/slotopt.h + + Internal menu for the slot options. + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_SLOTOPT_H +#define MAME_FRONTEND_UI_SLOTOPT_H + +#pragma once + +#include "ui/menu.h" + +#include + + +namespace ui { + +class menu_slot_devices : public menu +{ +public: + menu_slot_devices(mame_ui_manager &mui, render_container &container); + virtual ~menu_slot_devices() override; + +private: + enum class step_t + { + NEXT, + PREVIOUS + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) override; + virtual void handle(event const *ev) override; + + device_slot_interface::slot_option const *get_current_option(device_slot_interface &slot) const; + void set_slot_device(device_slot_interface &slot, std::string_view val); + void record_current_options(); + bool try_refresh_current_options(); + void rotate_slot_device(device_slot_interface &slot, step_t step); + + // variables + std::unique_ptr m_config; + std::unordered_map m_slot_options; + std::string m_current_option_list_slot_tag; + std::vector m_current_option_list; + std::vector::const_iterator m_current_option_list_iter; +}; + +} // namespace ui + +#endif /* MAME_FRONTEND_UI_SLOTOPT_H */ diff --git a/src/icludes/frontend/mame/ui/sndmenu.cpp b/src/icludes/frontend/mame/ui/sndmenu.cpp new file mode 100644 index 0000000..1ee1e82 --- /dev/null +++ b/src/icludes/frontend/mame/ui/sndmenu.cpp @@ -0,0 +1,169 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/********************************************************************* + + ui/sndmenu.cpp + + Internal UI user interface. + +*********************************************************************/ + +#include "emu.h" +#include "ui/sndmenu.h" + +#include "ui/selector.h" +#include "ui/ui.h" + +#include "../osd/modules/lib/osdobj_common.h" // TODO: remove + + +namespace ui { + +const int menu_sound_options::m_sound_rate[] = { 11025, 22050, 44100, 48000 }; + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_sound_options::menu_sound_options(mame_ui_manager &mui, render_container &container) : menu(mui, container) +{ + osd_options &options = downcast(mui.machine().options()); + + m_sample_rate = mui.machine().options().sample_rate(); + m_sound = (strcmp(options.sound(), OSDOPTVAL_NONE) && strcmp(options.sound(), "0")); + m_samples = mui.machine().options().samples(); + m_compressor = mui.machine().options().compressor(); + + int total = std::size(m_sound_rate); + + for (m_cur_rates = 0; m_cur_rates < total; m_cur_rates++) + if (m_sample_rate == m_sound_rate[m_cur_rates]) + break; + + if (m_cur_rates == total) + m_cur_rates = 2; +} + +//------------------------------------------------- +// menu_dismissed +//------------------------------------------------- + +void menu_sound_options::menu_dismissed() +{ + emu_options &moptions = machine().options(); + + if (strcmp(moptions.value(OSDOPTION_SOUND), m_sound ? OSDOPTVAL_AUTO : OSDOPTVAL_NONE)) + moptions.set_value(OSDOPTION_SOUND, m_sound ? OSDOPTVAL_AUTO : OSDOPTVAL_NONE, OPTION_PRIORITY_CMDLINE); + + if (moptions.bool_value(OPTION_COMPRESSOR) != m_compressor) + moptions.set_value(OPTION_COMPRESSOR, m_compressor, OPTION_PRIORITY_CMDLINE); + + if (moptions.int_value(OPTION_SAMPLERATE) != m_sound_rate[m_cur_rates]) + moptions.set_value(OPTION_SAMPLERATE, m_sound_rate[m_cur_rates], OPTION_PRIORITY_CMDLINE); + + if (moptions.bool_value(OPTION_SAMPLES) != m_samples) + moptions.set_value(OPTION_SAMPLES, m_samples, OPTION_PRIORITY_CMDLINE); + +} + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_sound_options::handle(event const *ev) +{ + bool changed = false; + + // process the menu + if (ev && ev->itemref) + { + switch ((uintptr_t)ev->itemref) + { + case ENABLE_SOUND: + if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT || ev->iptkey == IPT_UI_SELECT) + { + m_sound = !m_sound; + changed = true; + } + break; + + case ENABLE_COMPRESSOR: + if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT || ev->iptkey == IPT_UI_SELECT) + { + m_compressor = !m_compressor; + changed = true; + } + break; + + case SAMPLE_RATE: + if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT) + { + (ev->iptkey == IPT_UI_LEFT) ? m_cur_rates-- : m_cur_rates++; + changed = true; + } + else if (ev->iptkey == IPT_UI_SELECT) + { + int total = std::size(m_sound_rate); + std::vector s_sel(total); + for (int index = 0; index < total; index++) + s_sel[index] = std::to_string(m_sound_rate[index]); + + menu::stack_push( + ui(), container(), std::move(s_sel), m_cur_rates, + [this] (int selection) + { + m_cur_rates = selection; + reset(reset_options::REMEMBER_REF); + }); + } + break; + + case ENABLE_SAMPLES: + if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT || ev->iptkey == IPT_UI_SELECT) + { + m_samples = !m_samples; + changed = true; + } + break; + } + } + + if (changed) + reset(reset_options::REMEMBER_REF); + +} + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_sound_options::populate(float &customtop, float &custombottom) +{ + uint32_t arrow_flags = get_arrow_flags(uint16_t(0), uint16_t(std::size(m_sound_rate) - 1), m_cur_rates); + m_sample_rate = m_sound_rate[m_cur_rates]; + + // add options items + item_append_on_off(_("Sound"), m_sound, 0, (void *)(uintptr_t)ENABLE_SOUND); + item_append_on_off(_("Compressor"), m_compressor, 0, (void *)(uintptr_t)ENABLE_COMPRESSOR); + item_append(_("Sample Rate"), string_format("%d", m_sample_rate), arrow_flags, (void *)(uintptr_t)SAMPLE_RATE); + item_append_on_off(_("Use External Samples"), m_samples, 0, (void *)(uintptr_t)ENABLE_SAMPLES); + item_append(menu_item_type::SEPARATOR); + + customtop = ui().get_line_height() + (3.0f * ui().box_tb_border()); +} + +//------------------------------------------------- +// perform our special rendering +//------------------------------------------------- + +void menu_sound_options::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + char const *const toptext[] = { _("Sound Options") }; + draw_text_box( + std::begin(toptext), std::end(toptext), + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); +} + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/sndmenu.h b/src/icludes/frontend/mame/ui/sndmenu.h new file mode 100644 index 0000000..03f6820 --- /dev/null +++ b/src/icludes/frontend/mame/ui/sndmenu.h @@ -0,0 +1,53 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota +/*************************************************************************** + + ui/sndmenu.h + + Internal UI user interface. + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_SNDMENU_H +#define MAME_FRONTEND_UI_SNDMENU_H + +#pragma once + +#include "ui/menu.h" + +namespace ui { + +//------------------------------------------------- +// class sound options menu +//------------------------------------------------- + +class menu_sound_options : public menu +{ +public: + menu_sound_options(mame_ui_manager &mui, render_container &container); + +protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void menu_dismissed() override; + +private: + enum + { + ENABLE_SOUND = 1, + ENABLE_COMPRESSOR, + SAMPLE_RATE, + ENABLE_SAMPLES + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + uint16_t m_cur_rates; + static const int m_sound_rate[]; + int m_sample_rate; + bool m_samples, m_sound, m_compressor; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_SNDMENU_H diff --git a/src/icludes/frontend/mame/ui/state.cpp b/src/icludes/frontend/mame/ui/state.cpp new file mode 100644 index 0000000..4bda4d4 --- /dev/null +++ b/src/icludes/frontend/mame/ui/state.cpp @@ -0,0 +1,538 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/state.cpp + + Menus for saving and loading state + +***************************************************************************/ + +#include "emu.h" +#include "ui/state.h" + +#include "emuopts.h" +#include "inputdev.h" + + +namespace ui { + +/*************************************************************************** + ANONYMOUS NAMESPACE +***************************************************************************/ + +namespace { + +//------------------------------------------------- +// keyboard_input_item_name +//------------------------------------------------- + +std::string keyboard_input_item_name(input_item_id id) +{ + if (id >= ITEM_ID_A && id <= ITEM_ID_Z) + return std::string(1, char(id - ITEM_ID_A + 'a')); + if (id >= ITEM_ID_0 && id <= ITEM_ID_9) + return std::string(1, char(id - ITEM_ID_0 + '0')); + + // only supported for A-Z/0-9 + throw false; +} + + +//------------------------------------------------- +// code_item_pair +//------------------------------------------------- + +std::pair code_item_pair(const running_machine &machine, input_item_id id) +{ + // only supported for A-Z|0-9 + assert((id >= ITEM_ID_A && id <= ITEM_ID_Z) || (id >= ITEM_ID_0 && id <= ITEM_ID_9)); + input_code const code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id); + + return std::make_pair(keyboard_input_item_name(id), machine.input().code_name(code)); +} + +} // anonymous namespace + + +/*************************************************************************** + FILE ENTRY +***************************************************************************/ + +std::string menu_load_save_state_base::s_last_file_selected; + + +//------------------------------------------------- +// file_entry ctor +//------------------------------------------------- + +menu_load_save_state_base::file_entry::file_entry(std::string &&file_name, std::string &&visible_name, const std::chrono::system_clock::time_point &last_modified) + : m_file_name(std::move(file_name)) + , m_visible_name(std::move(visible_name)) + , m_last_modified(last_modified) +{ +} + + +/*************************************************************************** + BASE CLASS FOR LOAD AND SAVE +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_load_save_state_base::menu_load_save_state_base( + mame_ui_manager &mui, + render_container &container, + std::string_view header, + std::string_view footer, + bool must_exist, + bool one_shot) + : autopause_menu<>(mui, container) + , m_switch_poller(machine().input()) + , m_header(header) + , m_footer(footer) + , m_confirm_delete(nullptr) + , m_must_exist(must_exist) + , m_keys_released(false) +{ + set_one_shot(one_shot); + set_needs_prev_menu_item(!one_shot); +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_load_save_state_base::~menu_load_save_state_base() +{ +} + + +//------------------------------------------------- +// populate +//------------------------------------------------- + +void menu_load_save_state_base::populate(float &customtop, float &custombottom) +{ + // build the "filename to code" map, if we have not already (if it were not for the + // possibility that the system keyboard can be changed at runtime, I would put this + // into a static) + if (m_filename_to_code_map.empty()) + { + // loop through A-Z/0-9 + for (input_item_id id = ITEM_ID_A; id <= ITEM_ID_Z; id++) + m_filename_to_code_map.emplace(code_item_pair(machine(), id)); + for (input_item_id id = ITEM_ID_0; id <= ITEM_ID_9; id++) + m_filename_to_code_map.emplace(code_item_pair(machine(), id)); + + // do joysticks + input_class const &sticks = machine().input().device_class(DEVICE_CLASS_JOYSTICK); + if (sticks.enabled()) + { + for (int i = 0; sticks.maxindex() >= i; ++i) + { + input_device const *const stick = sticks.device(i); + if (stick) + { + for (input_item_id j = ITEM_ID_BUTTON1; (ITEM_ID_BUTTON32 >= j) && (stick->maxitem() >= j); ++j) + { + input_device_item const *const item = stick->item(j); + if (item && (item->itemclass() == ITEM_CLASS_SWITCH)) + { + m_filename_to_code_map.emplace( + util::string_format("joy%i-%i", i, j - ITEM_ID_BUTTON1 + 1), + machine().input().code_name(item->code())); + } + } + } + } + } + } + + // open the state directory + osd::directory::ptr dir = osd::directory::open(state_directory()); + + // create a separate vector, so we can add sorted entries to the menu + std::vector m_entries_vec; + + // populate all file entries + m_file_entries.clear(); + if (dir) + { + const osd::directory::entry *entry; + while ((entry = dir->read()) != nullptr) + { + if (core_filename_ends_with(entry->name, ".sta")) + { + // get the file name of the entry + std::string file_name(core_filename_extract_base(entry->name, true)); + + // try translating it + std::string visible_name = get_visible_name(file_name); + + // and proceed + file_entry fileent(std::string(file_name), std::move(visible_name), entry->last_modified); + auto iter = m_file_entries.emplace(std::make_pair(std::move(file_name), std::move(fileent))).first; + m_entries_vec.push_back(&iter->second); + } + } + } + + // sort the vector; put recently modified state files at the top + std::sort( + m_entries_vec.begin(), + m_entries_vec.end(), + [] (const file_entry *a, const file_entry *b) + { + return a->last_modified() > b->last_modified(); + }); + + // add the entries + for (const file_entry *entry : m_entries_vec) + { + // get the time as a local time string + char time_string[128]; + auto last_modified_time_t = std::chrono::system_clock::to_time_t(entry->last_modified()); + std::strftime(time_string, sizeof(time_string), "%c", std::localtime(&last_modified_time_t)); + + // format the text + std::string text = util::string_format("%s: %s", + entry->visible_name(), + time_string); + + // append the menu item + void *const itemref = itemref_from_file_entry(*entry); + item_append(std::move(text), 0, itemref); + + // is this item selected? + if (entry->file_name() == s_last_file_selected) + set_selection(itemref); + } + + if (m_entries_vec.empty()) + { + item_append(_("[no saved states found]"), FLAG_DISABLE, nullptr); + set_selection(nullptr); + } + item_append(menu_item_type::SEPARATOR); + if (is_one_shot()) + item_append(_("Cancel"), 0, nullptr); + + // set up custom render proc + customtop = ui().get_line_height() + (3.0f * ui().box_tb_border()); + custombottom = (2.0f * ui().get_line_height()) + (3.0f * ui().box_tb_border()); + + // get ready to poll inputs + m_switch_poller.reset(); + m_keys_released = false; +} + + +//------------------------------------------------- +// handle +//------------------------------------------------- + +void menu_load_save_state_base::handle(event const *ev) +{ + // process the event + if (ev && (ev->iptkey == IPT_UI_SELECT)) + { + if (ev->itemref) + { + // user selected one of the entries + file_entry const &entry = file_entry_from_itemref(ev->itemref); + slot_selected(std::string(entry.file_name())); + } + else + { + stack_pop(); + } + } + else if (ev && (ev->iptkey == IPT_UI_CLEAR)) + { + if (ev->itemref) + { + // prompt to confirm delete + m_confirm_delete = &file_entry_from_itemref(ev->itemref); + m_confirm_prompt = util::string_format( + _("Delete saved state %1$s?\nPress %2$s to delete\nPress %3$s to cancel"), + m_confirm_delete->visible_name(), + ui().get_general_input_setting(IPT_UI_SELECT), + ui().get_general_input_setting(IPT_UI_CANCEL)); + } + } + else if (!m_confirm_delete) + { + // poll inputs + std::string name = poll_inputs(); + if (!name.empty()) + try_select_slot(std::move(name)); + } +} + + +//------------------------------------------------- +// get_visible_name +//------------------------------------------------- + +std::string menu_load_save_state_base::get_visible_name(const std::string &file_name) +{ + auto const iter = m_filename_to_code_map.find(file_name); + if (iter != m_filename_to_code_map.end()) + return iter->second; + + // otherwise these are the same + return file_name; +} + + +//------------------------------------------------- +// poll_inputs +//------------------------------------------------- + +std::string menu_load_save_state_base::poll_inputs() +{ + input_code const code = m_switch_poller.poll(); + if (INPUT_CODE_INVALID == code) + { + m_keys_released = true; + } + else if (m_keys_released) + { + input_item_id const id = code.item_id(); + + // keyboard A-Z and 0-9 + if (((ITEM_ID_A <= id) && (ITEM_ID_Z >= id)) || ((ITEM_ID_0 <= id) && (ITEM_ID_9 >= id))) + return keyboard_input_item_name(id); + + // joystick buttons + if ((DEVICE_CLASS_JOYSTICK == code.device_class()) && (ITEM_CLASS_SWITCH == code.item_class()) && (ITEM_MODIFIER_NONE == code.item_modifier()) && (ITEM_ID_BUTTON1 <= id) && (ITEM_ID_BUTTON32 >= id)) + return util::string_format("joy%i-%i", code.device_index(), id - ITEM_ID_BUTTON1 + 1); + } + return ""; +} + + +//------------------------------------------------- +// try_select_slot +//------------------------------------------------- + +void menu_load_save_state_base::try_select_slot(std::string &&name) +{ + if (!m_must_exist || is_present(name)) + slot_selected(std::move(name)); +} + + +//------------------------------------------------- +// slot_selected +//------------------------------------------------- + +void menu_load_save_state_base::slot_selected(std::string &&name) +{ + // handle it + process_file(std::string(name)); + + // record the last slot touched + s_last_file_selected = std::move(name); + + // no matter what, pop out + menu::stack_pop(); +} + + +//------------------------------------------------- +// handle_keys - override key handling +//------------------------------------------------- + +void menu_load_save_state_base::handle_keys(uint32_t flags, int &iptkey) +{ + if (m_confirm_delete) + { + if (exclusive_input_pressed(iptkey, IPT_UI_SELECT, 0)) + { + // try to remove the file + std::string const filename(util::string_format( + "%2$s%1$s%3$s%1$s%4$s.sta", + PATH_SEPARATOR, + machine().options().state_directory(), + machine().get_statename(machine().options().state_name()), + m_confirm_delete->file_name())); + std::error_condition const err(osd_file::remove(filename)); + if (err) + { + osd_printf_error( + "Error removing file %s for state %s (%s:%d %s)\n", + filename, + m_confirm_delete->visible_name(), + err.category().name(), + err.value(), + err.message()); + machine().popmessage(_("Error removing saved state file %1$s"), filename); + } + + // repopulate the menu + // reset switch poller here to avoid bogus save/load if confirmed with joystick button + m_switch_poller.reset(); + m_confirm_prompt.clear(); + m_confirm_delete = nullptr; + m_keys_released = false; + reset(reset_options::REMEMBER_POSITION); + } + else if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0)) + { + // don't delete it - dismiss the prompt + m_switch_poller.reset(); + m_confirm_prompt.clear(); + m_confirm_delete = nullptr; + m_keys_released = false; + } + iptkey = IPT_INVALID; + } + else + { + menu::handle_keys(flags, iptkey); + } +} + + +//------------------------------------------------- +// custom_render - perform our special rendering +//------------------------------------------------- + +void menu_load_save_state_base::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +{ + // draw menu title + draw_text_box( + &m_header, &m_header + 1, + origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(), + text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false, + ui().colors().text_color(), UI_GREEN_COLOR, 1.0f); + + std::string_view text[2]; + unsigned count(0U); + + // add fixed footer if supplied + if (!m_footer.empty()) + text[count++] = m_footer; + + // provide a prompt to delete if a state is selected + if (selected_item().ref()) + { + if (m_delete_prompt.empty()) + m_delete_prompt = util::string_format(_("Press %1$s to delete"), machine().input().seq_name(machine().ioport().type_seq(IPT_UI_CLEAR))); + text[count++] = m_delete_prompt; + } + + // draw the footer box if necessary + if (count) + { + draw_text_box( + std::begin(text), std::next(std::begin(text), count), + origx1, origx2, origy2 + ui().box_tb_border(), origy2 + (count * ui().get_line_height()) + (3.0f * ui().box_tb_border()), + text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false, + ui().colors().text_color(), ui().colors().background_color(), 1.0f); + } + + // draw the confirmation prompt if necessary + if (!m_confirm_prompt.empty()) + ui().draw_text_box(container(), m_confirm_prompt, text_layout::text_justify::CENTER, 0.5f, 0.5f, ui().colors().background_color()); +} + + +//------------------------------------------------- +// itemref_from_file_entry +//------------------------------------------------- + +void *menu_load_save_state_base::itemref_from_file_entry(const menu_load_save_state_base::file_entry &entry) +{ + return (void *)&entry; +} + + +//------------------------------------------------- +// file_entry_from_itemref +//------------------------------------------------- + +const menu_load_save_state_base::file_entry &menu_load_save_state_base::file_entry_from_itemref(void *itemref) +{ + return *((const file_entry *)itemref); +} + + +//------------------------------------------------- +// state_name +//------------------------------------------------- + +std::string menu_load_save_state_base::state_directory() const +{ + const char *stateopt = machine().options().state_name(); + return util::string_format("%s%s%s", + machine().options().state_directory(), + PATH_SEPARATOR, + machine().get_statename(stateopt)); +} + + +//------------------------------------------------- +// is_present +//------------------------------------------------- + +bool menu_load_save_state_base::is_present(const std::string &name) const +{ + return m_file_entries.find(name) != m_file_entries.end(); +} + + +/*************************************************************************** + LOAD STATE +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_load_state::menu_load_state(mame_ui_manager &mui, render_container &container, bool one_shot) + : menu_load_save_state_base(mui, container, _("Load State"), _("Select state to load"), true, one_shot) +{ +} + + +//------------------------------------------------- +// process_file +//------------------------------------------------- + +void menu_load_state::process_file(std::string &&file_name) +{ + machine().schedule_load(std::move(file_name)); +} + + +/*************************************************************************** + SAVE STATE +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +menu_save_state::menu_save_state(mame_ui_manager &mui, render_container &container, bool one_shot) + : menu_load_save_state_base(mui, container, _("Save State"), _("Press a key or joystick button, or select state to overwrite"), false, one_shot) +{ +} + + +//------------------------------------------------- +// process_file +//------------------------------------------------- + +void menu_save_state::process_file(std::string &&file_name) +{ + machine().schedule_save(std::move(file_name)); +} + + +} // namespace ui diff --git a/src/icludes/frontend/mame/ui/state.h b/src/icludes/frontend/mame/ui/state.h new file mode 100644 index 0000000..af44b47 --- /dev/null +++ b/src/icludes/frontend/mame/ui/state.h @@ -0,0 +1,111 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + ui/state.h + + Menus for saving and loading state + +***************************************************************************/ +#ifndef MAME_FRONTEND_UI_STATE_H +#define MAME_FRONTEND_UI_STATE_H + +#pragma once + +#include "ui/menu.h" + +#include "iptseqpoll.h" + +#include +#include + + +namespace ui { + +class menu_load_save_state_base : public autopause_menu<> +{ +public: + virtual ~menu_load_save_state_base() override; + +protected: + menu_load_save_state_base( + mame_ui_manager &mui, + render_container &container, + std::string_view header, + std::string_view footer, + bool must_exist, + bool one_shot); + + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void handle_keys(uint32_t flags, int &iptkey) override; + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle(event const *ev) override; + + virtual void process_file(std::string &&file_name) = 0; + +private: + class file_entry + { + public: + file_entry() = delete; + file_entry(const file_entry &) = delete; + file_entry(file_entry &&) = default; + file_entry(std::string &&file_name, std::string &&visible_name, const std::chrono::system_clock::time_point &last_modified); + + const std::string &file_name() const { return m_file_name; } + const std::string &visible_name() const { return m_visible_name; } + const std::chrono::system_clock::time_point &last_modified() const { return m_last_modified; } + + private: + std::string m_file_name; // filename for the state itself + std::string m_visible_name; // how it appears in the dialog + std::chrono::system_clock::time_point m_last_modified; + }; + + static std::string s_last_file_selected; + + switch_code_poller m_switch_poller; + std::unordered_map m_file_entries; + std::unordered_map m_filename_to_code_map; + std::string_view const m_header; + std::string_view const m_footer; + std::string m_delete_prompt; + std::string m_confirm_prompt; + file_entry const * m_confirm_delete; + bool const m_must_exist; + bool m_keys_released; + + static void *itemref_from_file_entry(const file_entry &entry); + static const file_entry &file_entry_from_itemref(void *itemref); + + void try_select_slot(std::string &&name); + void slot_selected(std::string &&name); + std::string state_directory() const; + bool is_present(const std::string &name) const; + std::string poll_inputs(); + std::string get_visible_name(const std::string &file_name); +}; + + +class menu_load_state : public menu_load_save_state_base +{ +public: + menu_load_state(mame_ui_manager &mui, render_container &container, bool one_shot); + +protected: + virtual void process_file(std::string &&file_name) override; +}; + + +class menu_save_state : public menu_load_save_state_base +{ +public: + menu_save_state(mame_ui_manager &mui, render_container &container, bool one_shot); + +protected: + virtual void process_file(std::string &&file_name) override; +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_STATE_H diff --git a/src/icludes/frontend/mame/ui/submenu.cpp b/src/icludes/frontend/mame/ui/submenu.cpp new file mode 100644 index 0000000..496201c --- /dev/null +++ b/src/icludes/frontend/mame/ui/submenu.cpp @@ -0,0 +1,477 @@ +// license:BSD-3-Clause +// copyright-holders:Maurizio Petrarota,Jeffrey Clark +/*************************************************************************** + + ui/submenu.cpp + + UI options menu + +***************************************************************************/ + +#include "emu.h" +#include "ui/submenu.h" + +#include "ui/menuitem.h" +#include "ui/ui.h" +#include "ui/utils.h" + +#if defined(UI_WINDOWS) && !defined(UI_SDL) +#include "../osd/windows/winmain.h" +#else +#include "../osd/modules/lib/osdobj_common.h" +#endif +#include "../osd/modules/input/input_module.h" + +#include + + +namespace ui { + +std::vector submenu::misc_options() +{ + return std::vector