├── .gitignore ├── Ase_Loader ├── Ase_Loader.h └── decompressor.h ├── readme.md └── test ├── main.cpp └── tests ├── 1.1_no_slices.ase ├── 1_no_slices_blank.ase ├── 2.1_no_slices.ase ├── 2.2_no_slices_animated.ase ├── 3.0_one_slice.ase ├── 3.1_seven_slices_blank.ase ├── 3.2_animated_two_slices.ase ├── 4.0_slice_names_empty.ase └── 5.0_rgba_format.ase /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.bat 3 | *.dll 4 | *.pdb 5 | *.rdbg -------------------------------------------------------------------------------- /Ase_Loader/Ase_Loader.h: -------------------------------------------------------------------------------- 1 | /* 2 | Aseprite Loader 3 | Copyright © 2020 Stan O 4 | 5 | MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | Parses: 26 | - All header data 27 | - All frame data 28 | - All pixel data 29 | 30 | Chunks Supported: 31 | - CEL 32 | - No opacity support 33 | - PALETTE 0x2019 34 | - No name support 35 | - SLICE 0x2022 36 | - Does not support 9 patches or pivot flags 37 | - Loads only first slice key 38 | 39 | Types have Ase_ prefix if they're Ase specific. 40 | 41 | 42 | - Only supports zlib compressed pixel data 43 | 44 | - Does not support blend mode 45 | - Does not support layers 46 | 47 | Let me know if you want something added, 48 | ~ Stan 49 | 50 | */ 51 | 52 | #pragma once 53 | 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include "decompressor.h" 62 | 63 | typedef int64_t s64; 64 | typedef int32_t s32; 65 | typedef int16_t s16; 66 | typedef int8_t s8; 67 | typedef uint64_t u64; 68 | typedef uint32_t u32; 69 | typedef uint16_t u16; 70 | typedef uint8_t u8; 71 | 72 | inline u16 GetU16(char* memory) { 73 | u8* p = (u8*)(memory); 74 | return (((u16)p[1]) << 8) | 75 | (((u16)p[0])); 76 | } 77 | 78 | inline u32 GetU32(void* memory) { 79 | u8* p = (u8*)(memory); 80 | return (((u32)p[3]) << 24) | 81 | (((u32)p[2]) << 16) | 82 | (((u32)p[1]) << 8) | 83 | (((u32)p[0])); 84 | } 85 | 86 | #define bmalloc(t) (t*)(malloc(sizeof(t))) 87 | #define bmalloc_arr(t,n) (t*)(malloc(sizeof(t)*n)) 88 | #define bcalloc_arr(t,n) (t*)(calloc(n, sizeof(t))) 89 | 90 | #define HEADER_MN 0xA5E0 91 | #define FRAME_MN 0xF1FA 92 | 93 | #define HEADER_SIZE 128 94 | #define FRAME_SIZE 16 95 | 96 | #define OLD_PALETTE_1 0x0004 // depricated 97 | #define OLD_PALETTE_2 0x0011 // depricated 98 | #define LAYER 0x2004 99 | #define CEL 0x2005 100 | #define CEL_EXTRA 0x2006 101 | #define COLOR_PROFILE 0x2007 102 | #define MASK 0x2016 // depricated 103 | #define PATH 0x2017 // depricated 104 | #define TAGS 0x2018 105 | #define PALETTE 0x2019 106 | #define USER_DATA 0x2020 107 | #define SLICE 0x2022 108 | 109 | struct Ase_Header { 110 | u32 file_size; 111 | u16 magic_number; 112 | u16 num_frames; 113 | u16 width; 114 | u16 height; 115 | u16 color_depth; // 32 RGBA, 16 Grayscale, 8 Indexed 116 | u32 flags; // 1 = layer opacity valid 117 | u16 speed; // frame speed, depricated 118 | u8 palette_entry; // the one transparent colour for indexed sprites only 119 | u16 num_colors; 120 | 121 | // Pixel ratio. Default 1:1. 122 | u8 pixel_width; 123 | u8 pixel_height; 124 | 125 | 126 | // Rendered grid for aseprite, not for asset loading. 127 | s16 x_grid; 128 | s16 y_grid; 129 | u16 grid_width; 130 | u16 grid_height; 131 | }; 132 | 133 | struct Ase_Frame { 134 | u32 num_bytes; 135 | u16 magic_number; 136 | u16 old_num_chunks; // old specifier of number of chunks 137 | u16 frame_duration; 138 | u32 new_num_chunks; // number of chunks, if 0, use old field. 139 | }; 140 | 141 | struct Animation_Tag { 142 | char* name; 143 | u16 from; 144 | u16 to; 145 | }; 146 | 147 | // Delete and replace with SDL_Color if using SDL. 148 | struct Color { 149 | u8 r; 150 | u8 g; 151 | u8 b; 152 | u8 a; 153 | }; 154 | 155 | // Need to fix, for now assuming .ase are indexed sprites, 156 | // but will need to change in the future as there won't always be 256 entries. 157 | struct Palette_Chunk { 158 | u32 num_entries; 159 | u8 color_key; 160 | Color entries [256]; 161 | }; 162 | 163 | // Delete and replace with SDL_Rect if using SDL 164 | struct Rect { 165 | u32 x; 166 | u32 y; 167 | u32 w; 168 | u32 h; 169 | }; 170 | 171 | struct Slice { 172 | char* name; 173 | Rect quad; 174 | }; 175 | 176 | struct Ase_Output { 177 | u8* pixels; 178 | u8 bpp; // bytes per pixel 179 | u16 frame_width; 180 | u16 frame_height; 181 | 182 | // Junk values if indexed color mode is not used 183 | Palette_Chunk palette; 184 | 185 | Animation_Tag* tags; 186 | u16 num_tags; 187 | 188 | u16* frame_durations; 189 | u16 num_frames; 190 | 191 | Slice* slices; 192 | u32 num_slices; 193 | }; 194 | 195 | 196 | 197 | Ase_Output* Ase_Load(std::string path); 198 | void Ase_Destroy_Output(Ase_Output* output); 199 | void Ase_SetFlipVerticallyOnLoad(bool input_flag); 200 | 201 | 202 | 203 | 204 | 205 | #ifdef ASE_LOADER_IMPLEMENTATION 206 | 207 | 208 | 209 | 210 | 211 | static bool vertically_flip_on_load = false; 212 | 213 | void Ase_SetFlipVerticallyOnLoad(bool input_flag) { 214 | vertically_flip_on_load = input_flag; 215 | } 216 | 217 | 218 | Ase_Output* Ase_Load(std::string path) { 219 | 220 | std::ifstream file(path, std::ifstream::binary); 221 | 222 | if (file) { 223 | 224 | file.seekg(0, file.end); 225 | const int file_size = file.tellg(); 226 | 227 | char buffer [file_size]; 228 | char* buffer_p = & buffer[HEADER_SIZE]; 229 | 230 | // transfer data from file into buffer and close file 231 | file.seekg(0, std::ios::beg); 232 | file.read(buffer, file_size); 233 | file.close(); 234 | 235 | Ase_Header header = { 236 | GetU32(& buffer[0]), 237 | GetU16(& buffer[4]), 238 | GetU16(& buffer[6]), 239 | GetU16(& buffer[8]), 240 | GetU16(& buffer[10]), 241 | GetU16(& buffer[12]), 242 | GetU32(& buffer[14]), 243 | GetU16(& buffer[18]), 244 | (u8) buffer[28], 245 | GetU16(& buffer[32]), 246 | (u8) buffer[34], 247 | (u8) buffer[35], 248 | (s16) GetU16(& buffer[36]), 249 | (s16) GetU16(& buffer[38]), 250 | GetU16(& buffer[40]), 251 | GetU16(& buffer[42]) 252 | }; 253 | 254 | if (! (header.color_depth == 8 || header.color_depth == 32)) { 255 | printf("%s: Color depth %i not supported.\n", path.c_str(), header.color_depth); 256 | return NULL; 257 | } 258 | 259 | Ase_Output* output = bmalloc(Ase_Output); 260 | output->bpp = header.color_depth / 8; 261 | output->pixels = bcalloc_arr(u8, header.width * header.height * header.num_frames * output->bpp); // using calloc instead of malloc so that we avoid junk pixel data 262 | output->frame_width = header.width; 263 | output->frame_height = header.height; 264 | output->palette.color_key = header.palette_entry; 265 | 266 | output->frame_durations = bmalloc_arr(u16, header.num_frames); 267 | output->num_frames = header.num_frames; 268 | 269 | 270 | // Because we are using malloc, we cannot use default values in struct because 271 | // the memory that we are given has garbage values, so we have to manually set 272 | // the values here. 273 | output->tags = NULL; 274 | output->num_tags = 0; 275 | output->slices = NULL; 276 | output->num_slices = 0; 277 | 278 | // Aseprite doesn't tell us upfront how many slices we're given, 279 | // so there's no way really of creating the array of size X before 280 | // we receive all the slices. Vector is used temporarily, but then 281 | // converted into Slice* for output. 282 | std::vector temp_slices; 283 | 284 | // This helps us with formulating output but not all frame data is needed for output. 285 | Ase_Frame frames [header.num_frames]; 286 | 287 | // Indexed? fill the pixel indexes in the frame with transparent color index 288 | if (header.color_depth == 8) { 289 | for (int i = 0; i < header.width * header.height * header.num_frames; i++) { 290 | output->pixels[i] = header.palette_entry; 291 | } 292 | } 293 | 294 | // Each frame may have multiple chunks, so we first get frame data, then iterate over all the chunks that the frame has. 295 | for (u16 current_frame_index = 0; current_frame_index < header.num_frames; current_frame_index++) { 296 | 297 | frames[current_frame_index] = { 298 | GetU32(buffer_p), 299 | GetU16(buffer_p + 4), 300 | GetU16(buffer_p + 6), 301 | GetU16(buffer_p + 8), 302 | GetU32(buffer_p + 12) 303 | }; 304 | output->frame_durations[current_frame_index] = frames[current_frame_index].frame_duration; 305 | 306 | if (frames[current_frame_index].magic_number != FRAME_MN) { 307 | printf("%s: Frame %i magic number not correct, corrupt file?\n", path.c_str(), current_frame_index); 308 | Ase_Destroy_Output(output); 309 | return NULL; 310 | } 311 | 312 | buffer_p += FRAME_SIZE; 313 | 314 | for (u32 j = 0; j < frames[current_frame_index].new_num_chunks; j++) { 315 | 316 | u32 chunk_size = GetU32(buffer_p); 317 | u16 chunk_type = GetU16(buffer_p + 4); 318 | 319 | switch (chunk_type) { 320 | 321 | case PALETTE: { 322 | 323 | output->palette.num_entries = GetU32(buffer_p + 6); 324 | // specifies the range of unique colors in the palette 325 | // There may be many repeated colors, so range -> efficient. 326 | u32 first_to_change = GetU32(buffer_p + 10); 327 | u32 last_to_change = GetU32(buffer_p + 14); 328 | 329 | for (u32 k = first_to_change; k < last_to_change + 1; k++) { 330 | 331 | // We do not support color data with strings in it. Flag 1 means there's a name. 332 | if (GetU16(buffer_p + 26) == 1) { 333 | printf("%s: Name flag detected, cannot load! Color Index: %i.\n", path.c_str(), k); 334 | Ase_Destroy_Output(output); 335 | return NULL; 336 | } 337 | output->palette.entries[k] = {buffer_p[28 + k*6], buffer_p[29 + k*6], buffer_p[30 + k*6], buffer_p[31 + k*6]}; 338 | } 339 | break; 340 | } 341 | 342 | case CEL: { 343 | 344 | s16 x_offset = GetU16(buffer_p + 8); 345 | s16 y_offset = GetU16(buffer_p + 10); 346 | u16 cel_type = GetU16(buffer_p + 13); 347 | 348 | if (x_offset < 0 || y_offset < 0) { 349 | print("%s: Starting pixel coordinates out of bounds! Aseprite: Sprite -> Canvas Size -> Trim content outside of canvas [ON]", path.c_str()); 350 | return NULL; 351 | } 352 | 353 | 354 | if (cel_type != 2) { 355 | printf("%s: Only compressed images supported\n", path.c_str()); 356 | Ase_Destroy_Output(output); 357 | return NULL; 358 | } 359 | 360 | u16 width = GetU16(buffer_p + 22); 361 | u16 height = GetU16(buffer_p + 24); 362 | u8 pixels [width * height * output->bpp]; 363 | 364 | // have to use pixels instead of output->pixels because we need to convert the pixel position if there's more than one frame 365 | unsigned int data_size = Decompressor_Feed(buffer_p + 26, 26 - chunk_size, pixels, width * height * output->bpp, true); 366 | if (data_size == -1) { 367 | printf("%s: Pixel format not supported!\n", path.c_str()); 368 | Ase_Destroy_Output(output); 369 | return NULL; 370 | } 371 | 372 | // 373 | // transforming array of pixels onto larger array of pixels 374 | // 375 | 376 | // Our offset will be larger for a spritesheet because the total width of the image would have increased (when creating a spritesheet texture from .ase). 377 | // Same logic with offset_x. 378 | const int byte_offset_y = header.width * header.num_frames * y_offset * output->bpp; 379 | const int byte_offset_x = (current_frame_index * header.width + x_offset) * output->bpp; 380 | const int byte_offset = byte_offset_x + byte_offset_y; 381 | 382 | const int total_num_bytes = width * height * output->bpp; 383 | 384 | for (int k = 0; k < total_num_bytes; k ++) { 385 | int index = byte_offset + k % (width * output->bpp) + floor(k / width / output->bpp) * header.width * header.num_frames * output->bpp; 386 | output->pixels[index] = pixels[k]; 387 | } 388 | 389 | break; 390 | } 391 | 392 | case TAGS: { 393 | 394 | output->num_tags = GetU16(buffer_p + 6);; 395 | output->tags = bmalloc_arr(Animation_Tag, output->num_tags); 396 | 397 | // iterate over each tag and append data to output->tags 398 | int tag_buffer_offset = 0; 399 | for (u16 k = 0; k < output->num_tags; k ++) { 400 | 401 | output->tags[k].from = GetU16(buffer_p + tag_buffer_offset + 16); 402 | output->tags[k].to = GetU16(buffer_p + tag_buffer_offset + 18); 403 | 404 | // get string 405 | u16 slen = GetU16(buffer_p + tag_buffer_offset + 33); 406 | output->tags[k].name = (char*) malloc(sizeof(char) * (slen + 1)); // slen + 1 because we need to make it a null terminating string 407 | 408 | for (u16 a = 0; a < slen; a++) { 409 | output->tags[k].name[a] = *(buffer_p + tag_buffer_offset + a + 35); 410 | } 411 | output->tags[k].name[slen] = '\0'; 412 | 413 | tag_buffer_offset += 19 + slen; 414 | } 415 | break; 416 | } 417 | case SLICE: { 418 | 419 | u32 num_keys = GetU32(buffer_p + 6); 420 | u32 flag = GetU32(buffer_p + 10); 421 | if (flag != 0) { 422 | printf("%s: Flag %i not supported!\n", path.c_str(), flag); 423 | Ase_Destroy_Output(output); 424 | return NULL; 425 | } 426 | 427 | // get string 428 | u16 slen = GetU16(buffer_p + 18); 429 | char* name = bmalloc_arr(char, slen + 1); 430 | 431 | for (u16 a = 0; a < slen; a++) { 432 | name[a] = *(buffer_p + a + 20); 433 | } 434 | name[slen] = '\0'; 435 | 436 | // For now, we assume that the slice is the same 437 | // throughout all the frames, so we don't care about 438 | // the starting frame_number. 439 | // int frame_number = GetU32(buffer_p + 20 + slen); 440 | 441 | Rect quad = { 442 | (s32) GetU32(buffer_p + slen + 24), 443 | (s32) GetU32(buffer_p + slen + 28), 444 | GetU32(buffer_p + slen + 32), 445 | GetU32(buffer_p + slen + 36) 446 | }; 447 | 448 | temp_slices.push_back({name, quad}); 449 | 450 | break; 451 | } 452 | default: break; 453 | } 454 | buffer_p += chunk_size; 455 | } 456 | } 457 | 458 | // flip pixels if vertically_flip_on_load is true 459 | if (vertically_flip_on_load) { 460 | 461 | u8 temp; // temp variable for swapping 462 | int num_bytes_per_row = output->frame_width * output->num_frames * output->bpp; 463 | 464 | for (int i = 0; i < (int) output->frame_height / 2; i++) { 465 | 466 | // the pointers of the two rows we're swapping 467 | u8* swap_a = output->pixels + i * num_bytes_per_row; 468 | u8* swap_b = output->pixels + (output->frame_height - i - 1) * num_bytes_per_row; 469 | 470 | // Swapping two rows of pixels, pixel by pixel. 471 | // stb_image.h did it pixel by pixel instead of memcpy-ing the entire row at once 472 | // I'm going to trust that that's a wise move and do that as well. 473 | for (int j = 0; j < num_bytes_per_row; j++) { 474 | temp = *(swap_a + j); 475 | *(swap_a + j) = *(swap_b + j); 476 | *(swap_b + j) = temp; 477 | } 478 | 479 | } 480 | } 481 | 482 | // convert vector to array for output 483 | 484 | output->slices = bmalloc_arr(Slice, temp_slices.size()); 485 | 486 | // We do "int i" instead of u8 / u16 / u32... because we don't know how many slices there are upfront :( 487 | for (int i = 0; i < temp_slices.size(); i ++) { 488 | output->slices[i] = temp_slices[i]; 489 | } 490 | output->num_slices = temp_slices.size(); 491 | 492 | return output; 493 | 494 | 495 | } else { 496 | printf("%s: File could not be loaded.\n", path.c_str()); 497 | return NULL; 498 | } 499 | } 500 | 501 | void Ase_Destroy_Output(Ase_Output* output) { 502 | 503 | free(output->pixels); 504 | free(output->frame_durations); 505 | 506 | for (int i = 0; i < output->num_tags; i++) { 507 | free(output->tags[i].name); 508 | } 509 | for (int i = 0; i < output->num_slices; i++) { 510 | free(output->slices[i].name); 511 | } 512 | 513 | // There are cases where memory is never allocated for these fyi. 514 | free(output->tags); 515 | free(output->slices); 516 | 517 | free(output); 518 | } 519 | 520 | 521 | #endif -------------------------------------------------------------------------------- /Ase_Loader/decompressor.h: -------------------------------------------------------------------------------- 1 | /* 2 | By Mark Adler - https://github.com/madler/zlib/blob/master/adler32.c 3 | 4 | Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | Jean-loup Gailly Mark Adler 19 | jloup@gzip.org madler@alumni.caltech.edu 20 | The data format used by the zlib library is described by RFCs (Request for 21 | Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 22 | (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). 23 | */ 24 | 25 | /* 26 | MIT License 27 | 28 | Copyright (c) 2020 Artexety 29 | 30 | Permission is hereby granted, free of charge, to any person obtaining a copy 31 | of this software and associated documentation files (the "Software"), to deal 32 | in the Software without restriction, including without limitation the rights 33 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | copies of the Software, and to permit persons to whom the Software is 35 | furnished to do so, subject to the following conditions: 36 | 37 | The above copyright notice and this permission notice shall be included in all 38 | copies or substantial portions of the Software. 39 | 40 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 45 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 46 | SOFTWARE. 47 | */ 48 | 49 | #pragma once 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #define BASE 65521U 58 | #define NMAX 5552 59 | 60 | #define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} 61 | #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); 62 | #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); 63 | #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); 64 | #define DO16(buf) DO8(buf,0); DO8(buf,8); 65 | #define MOD(a) a %= BASE 66 | #define MOD28(a) a %= BASE 67 | #define MOD63(a) a %= BASE 68 | 69 | inline unsigned int adler32_z(unsigned int adler, const unsigned char *buf, unsigned int len) { 70 | 71 | unsigned long sum2; 72 | unsigned n; 73 | 74 | /* split Adler-32 into component sums */ 75 | sum2 = (adler >> 16) & 0xffff; 76 | adler &= 0xffff; 77 | 78 | /* in case user likes doing a byte at a time, keep it fast */ 79 | if (len == 1) { 80 | adler += buf[0]; 81 | if (adler >= BASE) 82 | adler -= BASE; 83 | sum2 += adler; 84 | if (sum2 >= BASE) 85 | sum2 -= BASE; 86 | return adler | (sum2 << 16); 87 | } 88 | 89 | /* initial Adler-32 value (deferred check for len == 1 speed) */ 90 | if (buf == NULL) 91 | return 1L; 92 | 93 | /* in case short lengths are provided, keep it somewhat fast */ 94 | if (len < 16) { 95 | while (len--) { 96 | adler += *buf++; 97 | sum2 += adler; 98 | } 99 | if (adler >= BASE) 100 | adler -= BASE; 101 | MOD28(sum2); /* only added so many BASE's */ 102 | return adler | (sum2 << 16); 103 | } 104 | 105 | /* do length NMAX blocks -- requires just one modulo operation */ 106 | while (len >= NMAX) { 107 | len -= NMAX; 108 | n = NMAX / 16; /* NMAX is divisible by 16 */ 109 | do { 110 | DO16(buf); /* 16 sums unrolled */ 111 | buf += 16; 112 | } while (--n); 113 | MOD(adler); 114 | MOD(sum2); 115 | } 116 | 117 | /* do remaining bytes (less than NMAX, still just one modulo) */ 118 | if (len) { /* avoid modulos if none remaining */ 119 | while (len >= 16) { 120 | len -= 16; 121 | DO16(buf); 122 | buf += 16; 123 | } 124 | while (len--) { 125 | adler += *buf++; 126 | sum2 += adler; 127 | } 128 | MOD(adler); 129 | MOD(sum2); 130 | } 131 | 132 | /* return recombined sums */ 133 | return adler | (sum2 << 16); 134 | } 135 | 136 | #if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) 137 | #define X64BIT_SHIFTER 138 | #endif /* defined(_M_X64) */ 139 | 140 | #ifdef X64BIT_SHIFTER 141 | typedef unsigned long long shifter_t; 142 | #else 143 | typedef unsigned int shifter_t; 144 | #endif /* X64BIT_SHIFTER */ 145 | 146 | struct BitReader { 147 | /** 148 | * Initialize bit reader 149 | * 150 | * @param in_block pointer to the start of the compressed block 151 | * @param in_blockend pointer to the end of the compressed block + 1 152 | */ 153 | void Init(unsigned char *in_block, unsigned char *in_blockend) { 154 | this->in_block = in_block; 155 | this->in_blockend = in_blockend; 156 | this->in_blockstart = in_block; 157 | } 158 | 159 | /** Refill 32 bits at a time if the architecture allows it, otherwise do nothing. */ 160 | void Refill32() { 161 | 162 | #ifdef X64BIT_SHIFTER 163 | if (this->shifter_bit_count <= 32 && (this->in_block + 4) <= this->in_blockend) 164 | { 165 | #ifdef defined(_M_X64) || defined(__x86_64__) 166 | this->shifter_data |= (((shifter_t)(*((unsigned int*)this->in_block))) << this->shifter_bit_count); 167 | this->shifter_bit_count += 32; 168 | this->in_block += 4; 169 | #else 170 | this->shifter_data |= (((shifter_t)(*this->in_block++)) << this->shifter_bit_count); 171 | this->shifter_bit_count += 8; 172 | this->shifter_data |= (((shifter_t)(*this->in_block++)) << this->shifter_bit_count); 173 | this->shifter_bit_count += 8; 174 | this->shifter_data |= (((shifter_t)(*this->in_block++)) << this->shifter_bit_count); 175 | this->shifter_bit_count += 8; 176 | this->shifter_data |= (((shifter_t)(*this->in_block++)) << this->shifter_bit_count); 177 | this->shifter_bit_count += 8; 178 | #endif 179 | } 180 | #endif /* X64BIT_SHIFTER */ 181 | } 182 | 183 | /** 184 | * Consume variable bit-length value, after reading it with PeekBits() 185 | * 186 | * @param n size of value to consume, in bits 187 | */ 188 | void ConsumeBits(const int n) { 189 | this->shifter_data >>= n; 190 | this->shifter_bit_count -= n; 191 | } 192 | 193 | /** 194 | * Read variable bit-length value 195 | * 196 | * @param n size of value in bits (number of bits to read), 0..16 197 | * 198 | * @return value, or -1 for failure 199 | */ 200 | unsigned int GetBits(const int n) { 201 | if (this->shifter_bit_count < n) { 202 | if (this->in_block < this->in_blockend) { 203 | this->shifter_data |= (((shifter_t)(*this->in_block++)) << this->shifter_bit_count); 204 | this->shifter_bit_count += 8; 205 | 206 | if (this->in_block < this->in_blockend) { 207 | this->shifter_data |= (((shifter_t)(*this->in_block++)) << this->shifter_bit_count); 208 | this->shifter_bit_count += 8; 209 | } 210 | } 211 | else return -1; 212 | } 213 | 214 | unsigned int value = this->shifter_data & ((1 << n) - 1); 215 | this->shifter_data >>= n; 216 | this->shifter_bit_count -= n; 217 | return value; 218 | } 219 | 220 | /** 221 | * Peek at a 16-bit value in the bitstream (lookahead) 222 | * 223 | * @return value 224 | */ 225 | unsigned int PeekBits() { 226 | if (this->shifter_bit_count < 16) { 227 | if (this->in_block < this->in_blockend) { 228 | 229 | this->shifter_data |= (((shifter_t)(*this->in_block++)) << this->shifter_bit_count); 230 | if (this->in_block < this->in_blockend) 231 | this->shifter_data |= (((shifter_t)(*this->in_block++)) << (this->shifter_bit_count + 8)); 232 | this->shifter_bit_count += 16; 233 | } 234 | } 235 | 236 | return this->shifter_data & 0xffff; 237 | } 238 | 239 | /** Re-align bitstream on a byte */ 240 | int ByteAllign() { 241 | 242 | while (this->shifter_bit_count >= 8) { 243 | this->shifter_bit_count -= 8; 244 | this->in_block--; 245 | if (this->in_block < this->in_blockstart) return -1; 246 | } 247 | 248 | this->shifter_bit_count = 0; 249 | this->shifter_data = 0; 250 | return 0; 251 | } 252 | 253 | void ModifyInBlock(const int v) { 254 | this->in_block += v; 255 | } 256 | 257 | 258 | int shifter_bit_count = 0; 259 | shifter_t shifter_data = 0; 260 | unsigned char *in_block = nullptr; 261 | unsigned char *in_blockend = nullptr; 262 | unsigned char *in_blockstart = nullptr; 263 | }; 264 | 265 | const int kMaxSymbols = 288; 266 | const int kCodeLenSyms = 19; 267 | const int kFastSymbolBits = 10; 268 | 269 | struct HuffmanDecoder { 270 | /** 271 | * Prepare huffman tables 272 | * 273 | * @param rev_symbol_table array of 2 * symbols entries for storing the reverse lookup table 274 | * @param code_length codeword lengths table 275 | * 276 | * @return 0 for success, -1 for failure 277 | */ 278 | int PrepareTable(unsigned int *rev_symbol_table, const int read_symbols, const int symbols, unsigned char *code_length) { 279 | 280 | int num_symbols_per_len[16]; 281 | int i; 282 | 283 | if (read_symbols < 0 || read_symbols > kMaxSymbols || symbols < 0 || symbols > kMaxSymbols || read_symbols > symbols) 284 | return -1; 285 | this->symbols_ = symbols; 286 | 287 | 288 | for (i = 0; i < 16; i++) 289 | num_symbols_per_len[i] = 0; 290 | 291 | for (i = 0; i < read_symbols; i++) { 292 | if (code_length[i] >= 16) return -1; 293 | num_symbols_per_len[code_length[i]]++; 294 | } 295 | 296 | this->starting_pos_[0] = 0; 297 | this->num_sorted_ = 0; 298 | for (i = 1; i < 16; i++) { 299 | this->starting_pos_[i] = this->num_sorted_; 300 | this->num_sorted_ += num_symbols_per_len[i]; 301 | } 302 | 303 | for (i = 0; i < symbols; i++) 304 | rev_symbol_table[i] = -1; 305 | 306 | for (i = 0; i < read_symbols; i++) { 307 | if (code_length[i]) 308 | rev_symbol_table[this->starting_pos_[code_length[i]]++] = i; 309 | } 310 | 311 | return 0; 312 | } 313 | 314 | /** 315 | * Finalize huffman codewords for decoding 316 | * 317 | * @param rev_symbol_table array of 2 * symbols entries that contains the reverse lookup table 318 | * 319 | * @return 0 for success, -1 for failure 320 | */ 321 | int FinalizeTable(unsigned int *rev_symbol_table) { 322 | const int symbols = this->symbols_; 323 | unsigned int canonical_code_word = 0; 324 | unsigned int *rev_code_length_table = rev_symbol_table + symbols; 325 | int canonical_length = 1; 326 | int i; 327 | 328 | for (i = 0; i < (1 << kFastSymbolBits); i++) this->fast_symbol_[i] = 0; 329 | for (i = 0; i < 16; i++) this->start_index_[i] = 0; 330 | 331 | i = 0; 332 | while (i < this->num_sorted_) { 333 | 334 | if (canonical_length >= 16) return -1; 335 | this->start_index_[canonical_length] = i - canonical_code_word; 336 | 337 | while (i < this->starting_pos_[canonical_length]) { 338 | 339 | if (i >= symbols) return -1; 340 | rev_code_length_table[i] = canonical_length; 341 | 342 | if (canonical_code_word >= (1U << canonical_length)) return -1; 343 | 344 | if (canonical_length <= kFastSymbolBits) { 345 | 346 | unsigned int rev_word; 347 | 348 | /* Get upside down codeword (branchless method by Eric Biggers) */ 349 | rev_word = ((canonical_code_word & 0x5555) << 1) | ((canonical_code_word & 0xaaaa) >> 1); 350 | rev_word = ((rev_word & 0x3333) << 2) | ((rev_word & 0xcccc) >> 2); 351 | rev_word = ((rev_word & 0x0f0f) << 4) | ((rev_word & 0xf0f0) >> 4); 352 | rev_word = ((rev_word & 0x00ff) << 8) | ((rev_word & 0xff00) >> 8); 353 | rev_word = rev_word >> (16 - canonical_length); 354 | 355 | int slots = 1 << (kFastSymbolBits - canonical_length); 356 | while (slots) { 357 | this->fast_symbol_[rev_word] = (rev_symbol_table[i] & 0xffffff) | (canonical_length << 24); 358 | rev_word += (1 << canonical_length); 359 | slots--; 360 | } 361 | } 362 | 363 | i++; 364 | canonical_code_word++; 365 | } 366 | canonical_length++; 367 | canonical_code_word <<= 1; 368 | } 369 | 370 | while (i < symbols) { 371 | rev_symbol_table[i] = -1; 372 | rev_code_length_table[i++] = 0; 373 | } 374 | 375 | return 0; 376 | } 377 | 378 | /** 379 | * Read fixed bit size code lengths 380 | * 381 | * @param len_bits number of bits per code length 382 | * @param read_symbols number of symbols actually read 383 | * @param symbols number of symbols to build codes for 384 | * @param code_length output code lengths table 385 | * @param bit_reader bit reader context 386 | * 387 | * @return 0 for success, -1 for failure 388 | */ 389 | static int ReadRawLengths(const int len_bits, const int read_symbols, const int symbols, unsigned char *code_length, BitReader *bit_reader) { 390 | 391 | const unsigned char code_len_syms[kCodeLenSyms] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; 392 | int i; 393 | 394 | if (read_symbols < 0 || read_symbols > kMaxSymbols || symbols < 0 || symbols > kMaxSymbols || read_symbols > symbols) 395 | return -1; 396 | 397 | i = 0; 398 | while (i < read_symbols) { 399 | unsigned int length = bit_reader->GetBits(len_bits); 400 | if (length == -1) return -1; 401 | code_length[code_len_syms[i++]] = length; 402 | } 403 | 404 | while (i < symbols) { 405 | code_length[code_len_syms[i++]] = 0; 406 | } 407 | 408 | return 0; 409 | } 410 | 411 | /** 412 | * Read huffman-encoded code lengths 413 | * 414 | * @param tables_rev_symbol_table reverse lookup table for code lengths 415 | * @param read_symbols number of symbols actually read 416 | * @param symbols number of symbols to build codes for 417 | * @param code_length output code lengths table 418 | * @param bit_reader bit reader context 419 | * 420 | * @return 0 for success, -1 for failure 421 | */ 422 | int ReadLength(const unsigned int *tables_rev_symbol_table, const int read_symbols, const int symbols, unsigned char *code_length, BitReader *bit_reader) { 423 | 424 | if (read_symbols < 0 || symbols < 0 || read_symbols > symbols) return -1; 425 | 426 | int i = 0; 427 | unsigned int previous_length = 0; 428 | 429 | while (i < read_symbols) { 430 | 431 | unsigned int length = this->ReadValue(tables_rev_symbol_table, bit_reader); 432 | if (length == -1) return -1; 433 | 434 | if (length < 16) { 435 | previous_length = length; 436 | code_length[i++] = previous_length; 437 | } 438 | else { 439 | unsigned int run_length = 0; 440 | 441 | if (length == 16) { 442 | int extra_run_length = bit_reader->GetBits(2); 443 | if (extra_run_length == -1) return -1; 444 | run_length = 3 + extra_run_length; 445 | } 446 | else if (length == 17) { 447 | int extra_run_length = bit_reader->GetBits(3); 448 | if (extra_run_length == -1) return -1; 449 | previous_length = 0; 450 | run_length = 3 + extra_run_length; 451 | } 452 | else if (length == 18) { 453 | int extra_run_length = bit_reader->GetBits(7); 454 | if (extra_run_length == -1) return -1; 455 | previous_length = 0; 456 | run_length = 11 + extra_run_length; 457 | } 458 | 459 | while (run_length && i < read_symbols) { 460 | code_length[i++] = previous_length; 461 | run_length--; 462 | } 463 | } 464 | } 465 | 466 | while (i < symbols) 467 | code_length[i++] = 0; 468 | 469 | return 0; 470 | } 471 | 472 | /** 473 | * Decode next symbol 474 | * 475 | * @param rev_symbol_table reverse lookup table 476 | * @param bit_reader bit reader context 477 | * 478 | * @return symbol, or -1 for error 479 | */ 480 | unsigned int ReadValue(const unsigned int *rev_symbol_table, BitReader *bit_reader) { 481 | 482 | unsigned int stream = bit_reader->PeekBits(); 483 | unsigned int fast_sym_bits = this->fast_symbol_[stream & ((1 << kFastSymbolBits) - 1)]; 484 | 485 | if (fast_sym_bits) { 486 | bit_reader->ConsumeBits(fast_sym_bits >> 24); 487 | return fast_sym_bits & 0xffffff; 488 | } 489 | 490 | const unsigned int *rev_code_length_table = rev_symbol_table + this->symbols_; 491 | unsigned int code_word = 0; 492 | int bits = 1; 493 | 494 | do { 495 | code_word |= (stream & 1); 496 | unsigned int table_index = this->start_index_[bits] + code_word; 497 | 498 | if (table_index < this->symbols_) { 499 | if (bits == rev_code_length_table[table_index]) { 500 | bit_reader->ConsumeBits(bits); 501 | return rev_symbol_table[table_index]; 502 | } 503 | } 504 | code_word <<= 1; 505 | stream >>= 1; 506 | bits++; 507 | } 508 | while (bits < 16); 509 | 510 | return -1; 511 | } 512 | 513 | unsigned int fast_symbol_[1 << kFastSymbolBits]; 514 | unsigned int start_index_[16]; 515 | unsigned int symbols_; 516 | int num_sorted_; 517 | int starting_pos_[16]; 518 | }; 519 | 520 | 521 | #define MATCHLEN_PAIR(__base,__dispbits) ((__base) | ((__dispbits) << 16) | 0x8000) 522 | #define OFFSET_PAIR(__base,__dispbits) ((__base) | ((__dispbits) << 16)) 523 | 524 | /*-- zlib static and dynamic blocks inflater --*/ 525 | const int kCodeLenBits = 3; 526 | const int kLiteralSyms = 288; 527 | const int kEODMarkerSym = 256; 528 | const int kMatchLenSymStart = 257; 529 | const int kMatchLenSyms = 29; 530 | const int kOffsetSyms = 32; 531 | const int kMinMatchSize = 3; 532 | 533 | constexpr unsigned int kMatchLenCode[kMatchLenSyms] = { 534 | MATCHLEN_PAIR(kMinMatchSize + 0, 0), MATCHLEN_PAIR(kMinMatchSize + 1, 0), MATCHLEN_PAIR(kMinMatchSize + 2, 0), MATCHLEN_PAIR(kMinMatchSize + 3, 0), MATCHLEN_PAIR(kMinMatchSize + 4, 0), 535 | MATCHLEN_PAIR(kMinMatchSize + 5, 0), MATCHLEN_PAIR(kMinMatchSize + 6, 0), MATCHLEN_PAIR(kMinMatchSize + 7, 0), MATCHLEN_PAIR(kMinMatchSize + 8, 1), MATCHLEN_PAIR(kMinMatchSize + 10, 1), 536 | MATCHLEN_PAIR(kMinMatchSize + 12, 1), MATCHLEN_PAIR(kMinMatchSize + 14, 1), MATCHLEN_PAIR(kMinMatchSize + 16, 2), MATCHLEN_PAIR(kMinMatchSize + 20, 2), MATCHLEN_PAIR(kMinMatchSize + 24, 2), 537 | MATCHLEN_PAIR(kMinMatchSize + 28, 2), MATCHLEN_PAIR(kMinMatchSize + 32, 3), MATCHLEN_PAIR(kMinMatchSize + 40, 3), MATCHLEN_PAIR(kMinMatchSize + 48, 3), MATCHLEN_PAIR(kMinMatchSize + 56, 3), 538 | MATCHLEN_PAIR(kMinMatchSize + 64, 4), MATCHLEN_PAIR(kMinMatchSize + 80, 4), MATCHLEN_PAIR(kMinMatchSize + 96, 4), MATCHLEN_PAIR(kMinMatchSize + 112, 4), MATCHLEN_PAIR(kMinMatchSize + 128, 5), 539 | MATCHLEN_PAIR(kMinMatchSize + 160, 5), MATCHLEN_PAIR(kMinMatchSize + 192, 5), MATCHLEN_PAIR(kMinMatchSize + 224, 5), MATCHLEN_PAIR(kMinMatchSize + 255, 0), 540 | }; 541 | 542 | constexpr unsigned int kOffsetCode[kOffsetSyms] = { 543 | OFFSET_PAIR(1, 0), OFFSET_PAIR(2, 0), OFFSET_PAIR(3, 0), OFFSET_PAIR(4, 0), OFFSET_PAIR(5, 1), OFFSET_PAIR(7, 1), OFFSET_PAIR(9, 2), OFFSET_PAIR(13, 2), OFFSET_PAIR(17, 3), OFFSET_PAIR(25, 3), 544 | OFFSET_PAIR(33, 4), OFFSET_PAIR(49, 4), OFFSET_PAIR(65, 5), OFFSET_PAIR(97, 5), OFFSET_PAIR(129, 6), OFFSET_PAIR(193, 6), OFFSET_PAIR(257, 7), OFFSET_PAIR(385, 7), OFFSET_PAIR(513, 8), OFFSET_PAIR(769, 8), 545 | OFFSET_PAIR(1025, 9), OFFSET_PAIR(1537, 9), OFFSET_PAIR(2049, 10), OFFSET_PAIR(3073, 10), OFFSET_PAIR(4097, 11), OFFSET_PAIR(6145, 11), OFFSET_PAIR(8193, 12), OFFSET_PAIR(12289, 12), OFFSET_PAIR(16385, 13), OFFSET_PAIR(24577, 13), 546 | }; 547 | 548 | inline unsigned int CopyStored(BitReader *bit_reader, unsigned char *out, unsigned int out_offset, unsigned int block_size_max) { 549 | 550 | if (bit_reader->ByteAllign() < 0 || bit_reader->in_block + 4 > bit_reader->in_blockend) 551 | return -1; 552 | 553 | unsigned short stored_length = ((unsigned short)bit_reader->in_block[0]) | (((unsigned short)bit_reader->in_block[0]) << 8); 554 | bit_reader->ModifyInBlock(2); 555 | 556 | unsigned short neg_stored_length = ((unsigned short)bit_reader->in_block[0]) | (((unsigned short)bit_reader->in_block[1]) << 8); 557 | bit_reader->ModifyInBlock(2); 558 | 559 | if (stored_length != ((~neg_stored_length) & 0xffff) || stored_length > block_size_max) 560 | return -1; 561 | 562 | std::memcpy(out + out_offset, bit_reader->in_block, stored_length); 563 | bit_reader->ModifyInBlock(stored_length); 564 | 565 | return (unsigned int)stored_length; 566 | } 567 | 568 | inline unsigned int DecompressBlock(BitReader *bit_reader, int dynamic_block, unsigned char *out, unsigned int out_offset, unsigned int block_size_max) { 569 | 570 | HuffmanDecoder literals_decoder; 571 | HuffmanDecoder offset_decoder; 572 | unsigned int literals_rev_sym_table[kLiteralSyms * 2]; 573 | unsigned int offset_rev_sym_table[kLiteralSyms * 2]; 574 | int i; 575 | 576 | if (dynamic_block) { 577 | 578 | HuffmanDecoder tables_decoder; 579 | unsigned char code_length[kLiteralSyms + kOffsetSyms]; 580 | unsigned int tables_rev_sym_table[kCodeLenSyms * 2]; 581 | 582 | unsigned int literal_syms = bit_reader->GetBits(5); 583 | if (literal_syms == -1) return -1; 584 | literal_syms += 257; 585 | if (literal_syms > kLiteralSyms) return -1; 586 | 587 | unsigned int offset_syms = bit_reader->GetBits(5); 588 | if (offset_syms == -1) return -1; 589 | offset_syms += 1; 590 | if (offset_syms > kOffsetSyms) return -1; 591 | 592 | unsigned int code_len_syms = bit_reader->GetBits(4); 593 | if (code_len_syms == -1) return -1; 594 | code_len_syms += 4; 595 | if (code_len_syms > kCodeLenSyms) return -1; 596 | 597 | if (HuffmanDecoder::ReadRawLengths(kCodeLenBits, code_len_syms, kCodeLenSyms, code_length, bit_reader) < 0 598 | || tables_decoder.PrepareTable(tables_rev_sym_table, kCodeLenSyms, kCodeLenSyms, code_length) < 0 599 | || tables_decoder.FinalizeTable(tables_rev_sym_table) < 0 600 | || tables_decoder.ReadLength(tables_rev_sym_table, literal_syms + offset_syms, kLiteralSyms + kOffsetSyms, code_length, bit_reader) < 0 601 | || literals_decoder.PrepareTable(literals_rev_sym_table, literal_syms, kLiteralSyms, code_length) < 0 602 | || offset_decoder.PrepareTable(offset_rev_sym_table, offset_syms, kOffsetSyms, code_length + literal_syms) < 0) 603 | return -1; 604 | } 605 | else { 606 | unsigned char fixed_literal_code_len[kLiteralSyms]; 607 | unsigned char fixed_offset_code_len[kOffsetSyms]; 608 | 609 | for (i = 0; i < 144; i++) fixed_literal_code_len[i] = 8; 610 | for (; i < 256; i++) fixed_literal_code_len[i] = 9; 611 | for (; i < 280; i++) fixed_literal_code_len[i] = 7; 612 | for (; i < kLiteralSyms; i++) fixed_literal_code_len[i] = 8; 613 | for (i = 0; i < kOffsetSyms; i++) fixed_offset_code_len[i] = 5; 614 | 615 | if (literals_decoder.PrepareTable(literals_rev_sym_table, kLiteralSyms, kLiteralSyms, fixed_literal_code_len) < 0 616 | || offset_decoder.PrepareTable(offset_rev_sym_table, kOffsetSyms, kOffsetSyms, fixed_offset_code_len) < 0) 617 | return -1; 618 | } 619 | 620 | for (i = 0; i < kOffsetSyms; i++) { 621 | unsigned int n = offset_rev_sym_table[i]; 622 | if (n < kOffsetSyms) { 623 | offset_rev_sym_table[i] = kOffsetCode[n]; 624 | } 625 | } 626 | 627 | for (i = 0; i < kLiteralSyms; i++) { 628 | unsigned int n = literals_rev_sym_table[i]; 629 | if (n >= kMatchLenSymStart && n < kLiteralSyms) { 630 | literals_rev_sym_table[i] = kMatchLenCode[n - kMatchLenSymStart]; 631 | } 632 | } 633 | 634 | if (literals_decoder.FinalizeTable(literals_rev_sym_table) < 0 635 | || offset_decoder.FinalizeTable(offset_rev_sym_table) < 0) 636 | return -1; 637 | 638 | unsigned char *current_out = out + out_offset; 639 | const unsigned char *out_end = current_out + block_size_max; 640 | const unsigned char *out_fast_end = out_end - 15; 641 | 642 | while (1) 643 | { 644 | bit_reader->Refill32(); 645 | 646 | unsigned int literals_code_word = literals_decoder.ReadValue(literals_rev_sym_table, bit_reader); 647 | if (literals_code_word < 256) { 648 | 649 | if (current_out < out_end) 650 | *current_out++ = literals_code_word; 651 | else 652 | return -1; 653 | } 654 | else { 655 | if (literals_code_word == kEODMarkerSym) break; 656 | if (literals_code_word == -1) return -1; 657 | 658 | unsigned int match_length = bit_reader->GetBits((literals_code_word >> 16) & 15); 659 | if (match_length == -1) return -1; 660 | 661 | match_length += (literals_code_word & 0x7fff); 662 | 663 | unsigned int offset_code_word = offset_decoder.ReadValue(offset_rev_sym_table, bit_reader); 664 | if (offset_code_word == -1) return -1; 665 | 666 | unsigned int match_offset = bit_reader->GetBits((offset_code_word >> 16) & 15); 667 | if (match_offset == -1) return -1; 668 | 669 | match_offset += (offset_code_word & 0x7fff); 670 | 671 | const unsigned char *src = current_out - match_offset; 672 | if (src >= out) { 673 | if (match_offset >= 16 && (current_out + match_length) <= out_fast_end) { 674 | const unsigned char *copy_src = src; 675 | unsigned char *copy_dst = current_out; 676 | const unsigned char *copy_end_dst = current_out + match_length; 677 | 678 | do { 679 | std::memcpy(copy_dst, copy_src, 16); 680 | copy_src += 16; 681 | copy_dst += 16; 682 | } 683 | while (copy_dst < copy_end_dst); 684 | 685 | current_out += match_length; 686 | } 687 | else { 688 | 689 | if ((current_out + match_length) > out_end) return -1; 690 | 691 | while 692 | (match_length--) { 693 | *current_out++ = *src++; 694 | } 695 | } 696 | } 697 | else return -1; 698 | } 699 | } 700 | 701 | return (unsigned int)(current_out - (out + out_offset)); 702 | } 703 | 704 | /** 705 | * Inflate zlib data 706 | * 707 | * @param compressed_data pointer to start of zlib data 708 | * @param compressed_data_size size of zlib data, in bytes 709 | * @param out pointer to start of decompression buffer 710 | * @param out_size_max maximum size of decompression buffer, in bytes 711 | * @param checksum defines if the decompressor should use a specific checksum 712 | * 713 | * @return number of bytes decompressed, or -1 in case of an error 714 | */ 715 | inline unsigned int Decompressor_Feed(const void *compressed_data, unsigned int compressed_data_size, unsigned char *out, unsigned int out_size_max, bool checksum) { 716 | 717 | unsigned char *current_compressed_data = (unsigned char *)compressed_data; 718 | unsigned char *end_compressed_data = current_compressed_data + compressed_data_size; 719 | unsigned int final_block; 720 | unsigned int current_out_offset; 721 | unsigned long check_sum = 0; 722 | 723 | BitReader bit_reader; 724 | 725 | if ((current_compressed_data + 2) > end_compressed_data) return -1; 726 | 727 | unsigned char CMF = current_compressed_data[0]; 728 | unsigned char FLG = current_compressed_data[1]; 729 | unsigned short check = FLG | (((unsigned short)CMF) << 8); 730 | 731 | if ((CMF >> 4) <= 7 && (check % 31) == 0) { 732 | current_compressed_data += 2; 733 | if (FLG & 0x20) { 734 | if ((current_compressed_data + 4) > end_compressed_data) return -1; 735 | current_compressed_data += 4; 736 | } 737 | } 738 | 739 | if (checksum) check_sum = adler32_z(0, nullptr, 0); 740 | 741 | bit_reader.Init(current_compressed_data, end_compressed_data); 742 | current_out_offset = 0; 743 | 744 | do { 745 | unsigned int block_type; 746 | unsigned int block_result; 747 | 748 | final_block = bit_reader.GetBits(1); 749 | block_type = bit_reader.GetBits(2); 750 | 751 | switch (block_type) { 752 | case 0: 753 | block_result = CopyStored(&bit_reader, out, current_out_offset, out_size_max - current_out_offset); 754 | break; 755 | 756 | case 1: 757 | block_result = DecompressBlock(&bit_reader, 0, out, current_out_offset, out_size_max - current_out_offset); 758 | break; 759 | 760 | case 2: 761 | block_result = DecompressBlock(&bit_reader, 1, out, current_out_offset, out_size_max - current_out_offset); 762 | break; 763 | 764 | case 3: 765 | return -1; 766 | } 767 | 768 | if (block_result == -1) return -1; 769 | 770 | if (checksum) { 771 | check_sum = adler32_z(check_sum, out + current_out_offset, block_result); 772 | } 773 | 774 | current_out_offset += block_result; 775 | } 776 | while (!final_block); 777 | 778 | bit_reader.ByteAllign(); 779 | current_compressed_data = bit_reader.in_block; 780 | 781 | if (checksum) { 782 | unsigned int stored_check_sum; 783 | 784 | if ((current_compressed_data + 4) > end_compressed_data) return -1; 785 | 786 | stored_check_sum = ((unsigned int)current_compressed_data[0]) << 24; 787 | stored_check_sum |= ((unsigned int)current_compressed_data[1]) << 16; 788 | stored_check_sum |= ((unsigned int)current_compressed_data[2]) << 8; 789 | stored_check_sum |= ((unsigned int)current_compressed_data[3]); 790 | 791 | if (stored_check_sum != check_sum) return -1; 792 | 793 | current_compressed_data += 4; 794 | } 795 | 796 | return current_out_offset; 797 | } 798 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Aseprite Loader 2 | 3 | Aseprite Loader is a C++11 library that loads .ase files, the file format used in the pixel editor [Aseprite](https://aseprite.org). 4 | 5 | ## Usage 6 | - Copy ase_loader.h and decompressor.h into your project folder (or copy the single file under releases) 7 | - Include ase_loader.h and define implementation: 8 | ```c++ 9 | #include "ase_loader.h" 10 | #define ASE_LOADER_IMPLEMENTATION // define only in one file 11 | ``` 12 | - Available functions: 13 | ```c++ 14 | Ase_Output* Ase_Load(std::string path); 15 | void Ase_Destroy_Output(Ase_Output* output); 16 | void Ase_SetFlipVerticallyOnLoad(bool input_flag); 17 | ``` 18 | 19 | ## Example 20 | 21 | ```c++ 22 | #include 23 | #include "ase_loader.h" 24 | 25 | int main() { 26 | 27 | Ase_Output* output = Ase_Load("example.ase"); 28 | if (output) { 29 | std::cout << "Success!\n"; 30 | } 31 | else { 32 | std::cout << "Failure.\n"; 33 | } 34 | Ase_Destroy_Output(output); 35 | 36 | return 0; 37 | } 38 | ``` 39 | 40 | ## Contributing 41 | Pull requests are welcome, but I do not plan or want this library to grow any bigger than it needs to be. 42 | ## License 43 | [MIT](https://choosealicense.com/licenses/mit/) -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #ifdef _WIN32 8 | #include 9 | #include 10 | #else 11 | #include 12 | #include 13 | #endif 14 | 15 | #define print SDL_Log 16 | // In order to view Ase_Loader.h logs 17 | #undef printf 18 | #define printf SDL_Log 19 | 20 | #define strequal(a,b) strcmp(a,b)==0 21 | 22 | #define ASE_LOADER_IMPLEMENTATION 23 | #include "../Ase_Loader/Ase_Loader.h" 24 | 25 | 26 | // For now using SDL_Delay instead of this, but will probably use this later. 27 | struct Clock { 28 | // dt 29 | float last_tick_time = 0; 30 | float dt = 0; 31 | 32 | void tick() { 33 | float tick_time = SDL_GetTicks(); 34 | dt = (tick_time - last_tick_time) / 1000; 35 | last_tick_time = tick_time; 36 | } 37 | }; 38 | 39 | struct Graphics { 40 | SDL_Window* window; 41 | SDL_Renderer* rdr; 42 | }; 43 | 44 | enum Test_Types { 45 | NULL_TEST, 46 | SLICES, 47 | SLICE_NAMES 48 | }; 49 | 50 | // Expected results 51 | struct Test { 52 | Test_Types type = NULL_TEST; 53 | char* file_path = NULL; 54 | void* expected; 55 | }; 56 | 57 | struct TestIter { 58 | Ase_Output* ase = NULL; 59 | int i = 0; // index 60 | }; 61 | 62 | 63 | void StartIthTest(TestIter* test_iter, Test* tests); 64 | void FinishIthTest(TestIter* test_iter); 65 | void GraphicsLaunch(Graphics* g); 66 | void GraphicsShutdown(Graphics* g); 67 | bool EventLoop(); 68 | 69 | 70 | int main(int argc, char* argv[]) { 71 | 72 | bool graphics_mode = false; 73 | if (argc > 1) { 74 | if (strcmp(argv[1], "graphic") == 0) { 75 | graphics_mode = true; 76 | } 77 | else { 78 | print("%s", argv[1]); 79 | print("Invalid arg. Available: graphic"); 80 | } 81 | } 82 | 83 | 84 | Test tests [] = { 85 | {SLICES, "tests/1_no_slices_blank.ase", 0}, 86 | {SLICES, "tests/1.1_no_slices.ase", 0}, 87 | {SLICES, "tests/2.1_no_slices.ase", 0}, 88 | {SLICES, "tests/2.2_no_slices_animated.ase", 0}, 89 | {SLICES, "tests/3.1_seven_slices_blank.ase", 7}, 90 | {SLICES, "tests/3.0_one_slice.ase", 1}, 91 | {SLICES, "tests/3.2_animated_two_slices.ase", 2}, 92 | {SLICE_NAMES, "tests/4.0_slice_names_empty.ase", "example_slice"}, 93 | {NULL_TEST, "tests/5.0_rgba_format.ase", "NULL"}, 94 | }; 95 | 96 | TestIter test_iter; 97 | int num_tests = sizeof(tests) / sizeof(tests[0]); 98 | 99 | //////// GRAPHICS MODE //////// 100 | if (graphics_mode) { 101 | 102 | Graphics g; 103 | GraphicsLaunch(& g); 104 | SDL_Texture* test_texture = NULL; 105 | 106 | for (; test_iter.i < num_tests; test_iter.i++) { 107 | 108 | StartIthTest(& test_iter, & tests[0]); 109 | 110 | SDL_PixelFormatEnum pixel_format; 111 | if (test_iter.ase->bpp == 1) { 112 | pixel_format = SDL_PIXELFORMAT_INDEX8; 113 | } 114 | else if (test_iter.ase->bpp == 4) { 115 | pixel_format = SDL_PIXELFORMAT_RGBA32; 116 | } 117 | else { 118 | print("Test %i | %i BPP not supported!", test_iter.i, test_iter.ase->bpp); 119 | } 120 | 121 | SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormatFrom(test_iter.ase->pixels, test_iter.ase->frame_width * test_iter.ase->num_frames, test_iter.ase->frame_height, test_iter.ase->bpp * 8, test_iter.ase->bpp * test_iter.ase->frame_width * test_iter.ase->num_frames, pixel_format); 122 | if (! surface) print("Surface could not be created!, %s\n", SDL_GetError()); 123 | SDL_SetPaletteColors(surface->format->palette, (SDL_Color*) & test_iter.ase->palette.entries, 0, test_iter.ase->palette.num_entries); 124 | SDL_SetColorKey(surface, SDL_TRUE, test_iter.ase->palette.color_key); 125 | 126 | test_texture = SDL_CreateTextureFromSurface(g.rdr, surface); 127 | 128 | FinishIthTest(& test_iter); 129 | 130 | // Draw the test texture if it actually exists 131 | // (checking != NULL on the off chance that a test failes miserably and the texture also fails to load) 132 | if (test_texture != NULL) { 133 | SDL_RenderClear(g.rdr); 134 | SDL_Rect drect = {0, 0, 0, 0}; 135 | SDL_QueryTexture(test_texture, NULL, NULL, & drect.w, & drect.h); 136 | SDL_RenderCopy(g.rdr, test_texture, NULL, & drect); 137 | SDL_RenderPresent(g.rdr); 138 | 139 | // If we're on the last test, then keep the texture up on the screen. 140 | if (test_iter.i < num_tests - 1) SDL_DestroyTexture(test_texture); 141 | } 142 | 143 | // If we get an input that we want to quit the program, we do as so. 144 | if (EventLoop()) { 145 | SDL_DestroyTexture(test_texture); 146 | GraphicsShutdown(& g); 147 | return 0; 148 | } 149 | 150 | SDL_Delay(300); 151 | } 152 | 153 | // This lets us have the window open even after we finish all the tests. 154 | // This makes it easier to create the ability to cycle back through or pick 155 | // specific tests in the future. 156 | while (true) { 157 | if (EventLoop()) { 158 | SDL_DestroyTexture(test_texture); 159 | GraphicsShutdown(& g); 160 | break; 161 | } 162 | } 163 | } 164 | 165 | //////// NON-GRAPHICS MODE //////// 166 | else { 167 | for (; test_iter.i < num_tests; test_iter.i++) { 168 | StartIthTest(& test_iter, & tests[0]); 169 | FinishIthTest(& test_iter); 170 | } 171 | } 172 | 173 | return 0; 174 | } 175 | 176 | // Tests the ith test. 177 | // The only thing it doesn't do is SDL_DestroyTexture, because when we are in graphics mode 178 | // we want to wait until we draw the texture to the screen before we destroy it. 179 | void StartIthTest(TestIter* test_iter, Test* tests) { 180 | 181 | test_iter->ase = Ase_Load(tests[test_iter->i].file_path); 182 | 183 | u32 expected = tests[test_iter->i].expected; 184 | u32 actual; 185 | bool success; 186 | 187 | if (tests[test_iter->i].type == SLICES) { 188 | actual = test_iter->ase->num_slices; 189 | success = expected == actual; 190 | } 191 | else if (tests[test_iter->i].type == SLICE_NAMES) { 192 | actual = test_iter->ase->slices[0].name; 193 | success = strequal(expected, actual); 194 | } 195 | else { 196 | print("Test %i has null test type!", test_iter->i); 197 | return; 198 | } 199 | 200 | 201 | if (success) { 202 | print("Test %i | %s | PASSED", test_iter->i, tests[test_iter->i].file_path); 203 | } 204 | else { 205 | print("Test %i | %s | FAILED | Expected %i, Got %i", test_iter->i, tests[test_iter->i].file_path, expected, actual); 206 | } 207 | 208 | } 209 | 210 | void FinishIthTest(TestIter* test_iter) { 211 | Ase_Destroy_Output(test_iter->ase); 212 | } 213 | 214 | void GraphicsLaunch(Graphics* g) { 215 | SDL_Init(SDL_INIT_EVERYTHING); 216 | IMG_Init(IMG_INIT_PNG); 217 | 218 | g->window = SDL_CreateWindow("Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1600, 800, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); 219 | g->rdr = SDL_CreateRenderer(g->window, -1, SDL_RENDERER_ACCELERATED); 220 | const int scale = 4; 221 | SDL_RenderSetScale(g->rdr, scale, scale); 222 | } 223 | 224 | void GraphicsShutdown(Graphics* g) { 225 | SDL_DestroyWindow(g->window); 226 | IMG_Quit(); 227 | SDL_Quit(); 228 | } 229 | 230 | // Returns true if quiting the program 231 | bool EventLoop() { 232 | 233 | bool quit = false; 234 | 235 | SDL_Event event; 236 | while (SDL_PollEvent(& event)) { 237 | 238 | switch (event.type) { 239 | case SDL_QUIT: 240 | quit = true; 241 | case SDL_KEYDOWN: 242 | switch (event.key.keysym.sym) { 243 | case SDLK_ESCAPE: 244 | quit = true; 245 | break; 246 | default: break; 247 | } 248 | default: break; 249 | } 250 | } 251 | 252 | return quit; 253 | } -------------------------------------------------------------------------------- /test/tests/1.1_no_slices.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/1.1_no_slices.ase -------------------------------------------------------------------------------- /test/tests/1_no_slices_blank.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/1_no_slices_blank.ase -------------------------------------------------------------------------------- /test/tests/2.1_no_slices.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/2.1_no_slices.ase -------------------------------------------------------------------------------- /test/tests/2.2_no_slices_animated.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/2.2_no_slices_animated.ase -------------------------------------------------------------------------------- /test/tests/3.0_one_slice.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/3.0_one_slice.ase -------------------------------------------------------------------------------- /test/tests/3.1_seven_slices_blank.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/3.1_seven_slices_blank.ase -------------------------------------------------------------------------------- /test/tests/3.2_animated_two_slices.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/3.2_animated_two_slices.ase -------------------------------------------------------------------------------- /test/tests/4.0_slice_names_empty.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/4.0_slice_names_empty.ase -------------------------------------------------------------------------------- /test/tests/5.0_rgba_format.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmoa/Aseprite_Loader/4ee224469aaf5fbaed5543a5613ab47ef9700924/test/tests/5.0_rgba_format.ase --------------------------------------------------------------------------------