this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 354 lines 13 kB view raw
1// MIT License 2 3// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com 4 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to deal 7// in the Software without restriction, including without limitation the rights 8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9// copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11 12// The above copyright notice and this permission notice shall be included in all 13// copies or substantial portions of the Software. 14 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21// SOFTWARE. 22 23#include "retro_endianness.h" 24#include "cart.h" 25 26#if defined(BUILD_DEPRECATED) 27#include "tools.h" 28#include "ext/gif.h" 29#endif 30 31#include <string.h> 32#include <stdlib.h> 33#include "tic_assert.h" 34#include "tools.h" 35 36typedef enum 37{ 38 CHUNK_DUMMY, // 0 39 CHUNK_TILES, // 1 40 CHUNK_SPRITES, // 2 41 CHUNK_COVER_DEP, // 3 - deprecated chunk 42 CHUNK_MAP, // 4 43 CHUNK_CODE, // 5 44 CHUNK_FLAGS, // 6 45 CHUNK_TEMP2, // 7 46 CHUNK_TEMP3, // 8 47 CHUNK_SAMPLES, // 9 48 CHUNK_WAVEFORM, // 10 49 CHUNK_TEMP4, // 11 50 CHUNK_PALETTE, // 12 51 CHUNK_PATTERNS_DEP, // 13 - deprecated chunk 52 CHUNK_MUSIC, // 14 53 CHUNK_PATTERNS, // 15 54 CHUNK_CODE_ZIP, // 16 55 CHUNK_DEFAULT, // 17 56 CHUNK_SCREEN, // 18 57 CHUNK_BINARY, // 19 58 CHUNK_LANG, // 20 59} ChunkType; 60 61typedef struct 62{ 63#if RETRO_IS_BIG_ENDIAN 64 u32 bank:TIC_BANK_BITS; 65 u32 type:5; // ChunkType 66#else 67 u32 type:5; // ChunkType 68 u32 bank:TIC_BANK_BITS; 69#endif 70 u32 size:TIC_BANKSIZE_BITS; // max chunk size is 64K 71 u32 temp:8; 72} Chunk; 73 74static_assert(sizeof(Chunk) == 4, "tic_chunk_size"); 75 76static const u8 Sweetie16[] = {0x1a, 0x1c, 0x2c, 0x5d, 0x27, 0x5d, 0xb1, 0x3e, 0x53, 0xef, 0x7d, 0x57, 0xff, 0xcd, 0x75, 0xa7, 0xf0, 0x70, 0x38, 0xb7, 0x64, 0x25, 0x71, 0x79, 0x29, 0x36, 0x6f, 0x3b, 0x5d, 0xc9, 0x41, 0xa6, 0xf6, 0x73, 0xef, 0xf7, 0xf4, 0xf4, 0xf4, 0x94, 0xb0, 0xc2, 0x56, 0x6c, 0x86, 0x33, 0x3c, 0x57}; 77static const u8 Waveforms[] = {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe}; 78 79static s32 chunkSize(const Chunk* chunk) 80{ 81 return chunk->size == 0 && (chunk->type == CHUNK_CODE || chunk->type == CHUNK_BINARY) ? TIC_BANK_SIZE : retro_le_to_cpu16(chunk->size); 82} 83 84void tic_cart_load(tic_cartridge* cart, const u8* buffer, s32 size) 85{ 86 memset(cart, 0, sizeof(tic_cartridge)); 87 const u8* end = buffer + size; 88 u8 *chunk_cart = NULL; 89 90 // check if this cartridge is in PNG format 91 if (!memcmp(buffer, "\x89PNG", 4)) 92 { 93 s32 siz; 94 const u8* ptr = buffer + 8; 95 // iterate on chunks until we find a cartridge 96 while (ptr < end) 97 { 98 siz = ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]); 99 if (!memcmp(ptr + 4, "caRt", 4) && siz > 0) 100 { 101 chunk_cart = malloc(sizeof(tic_cartridge)); 102 if (chunk_cart) 103 { 104 size = tic_tool_unzip(chunk_cart, sizeof(tic_cartridge), ptr + 8, siz); 105 buffer = chunk_cart; 106 end = buffer + size; 107 } 108 break; 109 } 110 ptr += siz + 12; 111 } 112 // error, no TIC-80 cartridge chunk in PNG??? 113 if (!chunk_cart) 114 return; 115 } 116 117#define LOAD_CHUNK(to) memcpy(&to, ptr, MIN(sizeof(to), chunk->size ? retro_le_to_cpu16(chunk->size) : TIC_BANK_SIZE)) 118 119 // load palette chunk first 120 { 121 const u8* ptr = buffer; 122 while (ptr < end) 123 { 124 const Chunk* chunk = (Chunk*)ptr; 125 ptr += sizeof(Chunk); 126 127 switch (chunk->type) 128 { 129 case CHUNK_PALETTE: 130 LOAD_CHUNK(cart->banks[chunk->bank].palette); 131 break; 132 case CHUNK_DEFAULT: 133 memcpy(&cart->banks[chunk->bank].palette, Sweetie16, sizeof Sweetie16); 134 memcpy(&cart->banks[chunk->bank].sfx.waveforms, Waveforms, sizeof Waveforms); 135 break; 136 default: break; 137 } 138 139 ptr += chunkSize(chunk); 140 } 141 142#if defined(BUILD_DEPRECATED) 143 // workaround to support ancient carts without palette 144 // load DB16 palette if it not exists 145 if (EMPTY(cart->bank0.palette.vbank0.data)) 146 { 147 static const u8 DB16[] = { 0x14, 0x0c, 0x1c, 0x44, 0x24, 0x34, 0x30, 0x34, 0x6d, 0x4e, 0x4a, 0x4e, 0x85, 0x4c, 0x30, 0x34, 0x65, 0x24, 0xd0, 0x46, 0x48, 0x75, 0x71, 0x61, 0x59, 0x7d, 0xce, 0xd2, 0x7d, 0x2c, 0x85, 0x95, 0xa1, 0x6d, 0xaa, 0x2c, 0xd2, 0xaa, 0x99, 0x6d, 0xc2, 0xca, 0xda, 0xd4, 0x5e, 0xde, 0xee, 0xd6 }; 148 memcpy(cart->bank0.palette.vbank0.data, DB16, sizeof DB16); 149 } 150#endif 151 } 152 153 struct CodeChunk {s32 size; const char* data;} code[TIC_BANKS] = {0}; 154 struct BinaryChunk {s32 size; const u8* data;} binary[TIC_BINARY_BANKS] = {0}; 155 156 { 157 const u8* ptr = buffer; 158 while(ptr < end) 159 { 160 const Chunk* chunk = (Chunk*)ptr; 161 ptr += sizeof(Chunk); 162 163 switch(chunk->type) 164 { 165 case CHUNK_TILES: LOAD_CHUNK(cart->banks[chunk->bank].tiles); break; 166 case CHUNK_SPRITES: LOAD_CHUNK(cart->banks[chunk->bank].sprites); break; 167 case CHUNK_MAP: LOAD_CHUNK(cart->banks[chunk->bank].map); break; 168 case CHUNK_SAMPLES: LOAD_CHUNK(cart->banks[chunk->bank].sfx.samples); break; 169 case CHUNK_WAVEFORM: LOAD_CHUNK(cart->banks[chunk->bank].sfx.waveforms); break; 170 case CHUNK_MUSIC: LOAD_CHUNK(cart->banks[chunk->bank].music.tracks); break; 171 case CHUNK_PATTERNS: LOAD_CHUNK(cart->banks[chunk->bank].music.patterns); break; 172 case CHUNK_FLAGS: LOAD_CHUNK(cart->banks[chunk->bank].flags); break; 173 case CHUNK_SCREEN: LOAD_CHUNK(cart->banks[chunk->bank].screen); break; 174 case CHUNK_LANG: LOAD_CHUNK(cart->lang); break; 175 case CHUNK_BINARY: 176 binary[chunk->bank] = (struct BinaryChunk){chunkSize(chunk), ptr}; 177 break; 178 case CHUNK_CODE: 179 code[chunk->bank] = (struct CodeChunk){chunkSize(chunk), (char*)ptr}; 180 break; 181#if defined(BUILD_DEPRECATED) 182 case CHUNK_CODE_ZIP: 183 tic_tool_unzip(cart->code.data, TIC_CODE_SIZE, ptr, retro_le_to_cpu16(chunk->size)); 184 break; 185 case CHUNK_COVER_DEP: 186 { 187 // workaround to load deprecated cover section 188 gif_image* image = gif_read_data(ptr, retro_le_to_cpu16(chunk->size)); 189 190 if (image) 191 { 192 if(image->width == TIC80_WIDTH && image->height == TIC80_HEIGHT) 193 for (s32 i = 0; i < TIC80_WIDTH * TIC80_HEIGHT; i++) 194 tic_tool_poke4(cart->bank0.screen.data, i, 195 tic_nearest_color(cart->bank0.palette.vbank0.colors, (const tic_rgb*)&image->palette[image->buffer[i]], TIC_PALETTE_SIZE)); 196 197 gif_close(image); 198 } 199 } 200 break; 201 case CHUNK_PATTERNS_DEP: 202 { 203 // workaround to load deprecated music patterns section 204 // and automatically convert volume value to a command 205 tic_patterns* ptrns = &cart->banks[chunk->bank].music.patterns; 206 LOAD_CHUNK(*ptrns); 207 for(s32 i = 0; i < MUSIC_PATTERNS; i++) 208 for(s32 r = 0; r < MUSIC_PATTERN_ROWS; r++) 209 { 210 tic_track_row* row = &ptrns->data[i].rows[r]; 211 if(row->note >= NoteStart && row->command == tic_music_cmd_empty) 212 { 213 row->command = tic_music_cmd_volume; 214 row->param2 = row->param1 = MAX_VOLUME - row->param1; 215 } 216 } 217 } 218 break; 219#endif 220 default: break; 221 } 222 223 ptr += chunkSize(chunk); 224 } 225#undef LOAD_CHUNK 226 227 { 228 u32 total_size = 0; 229 char* ptr = cart->binary.data; 230 RFOR(const struct BinaryChunk*, chunk, binary) 231 if (chunk->size) 232 { 233 memcpy(ptr, chunk->data, chunk->size); 234 ptr += chunk->size; 235 total_size += chunk->size; 236 } 237 cart->binary.size = total_size; 238 } 239 240 if (!*cart->code.data) 241 { 242 char* ptr = cart->code.data; 243 RFOR(const struct CodeChunk*, chunk, code) 244 if (chunk->data) 245 { 246 memcpy(ptr, chunk->data, chunk->size); 247 ptr += chunk->size; 248 } 249 } 250 } 251 // if we have allocated the buffer from a PNG chunk 252 if (chunk_cart) 253 free(chunk_cart); 254} 255 256 257static s32 calcBufferSize(const void* buffer, s32 size) 258{ 259 const u8* ptr = (u8*)buffer + size - 1; 260 const u8* end = (u8*)buffer; 261 262 while(ptr >= end) 263 { 264 if(*ptr) break; 265 266 ptr--; 267 size--; 268 } 269 270 return size; 271} 272 273static u8* saveFixedChunk(u8* buffer, ChunkType type, const void* from, s32 size, s32 bank) 274{ 275 if(size) 276 { 277 Chunk chunk = {.type = type, .bank = bank, .size = retro_le_to_cpu16(size), .temp = 0}; 278 memcpy(buffer, &chunk, sizeof(Chunk)); 279 buffer += sizeof(Chunk); 280 281 memcpy(buffer, from, size); 282 buffer += size; 283 } 284 285 return buffer; 286} 287 288static u8* saveChunk(u8* buffer, ChunkType type, const void* from, s32 size, s32 bank) 289{ 290 s32 chunkSize = calcBufferSize(from, size); 291 292 return saveFixedChunk(buffer, type, from, chunkSize, bank); 293} 294 295s32 tic_cart_save(const tic_cartridge* cart, u8* buffer) 296{ 297 u8* start = buffer; 298 299#define SAVE_CHUNK(ID, FROM, BANK) saveChunk(buffer, ID, &FROM, sizeof(FROM), BANK) 300 301 tic_waveforms defaultWaveforms = {0}; 302 tic_palettes defaultPalettes = {0}; 303 304 memcpy(&defaultWaveforms, Waveforms, sizeof Waveforms); 305 memcpy(&defaultPalettes, Sweetie16, sizeof Sweetie16); 306 307 for(s32 i = 0; i < TIC_BANKS; i++) 308 { 309 if(memcmp(&cart->banks[i].sfx.waveforms, &defaultWaveforms, sizeof defaultWaveforms) == 0 310 && memcmp(&cart->banks[i].palette, &defaultPalettes, sizeof defaultPalettes) == 0) 311 { 312 Chunk chunk = {.type = CHUNK_DEFAULT, .bank = i, .size = 0, .temp = 0}; 313 memcpy(buffer, &chunk, sizeof chunk); 314 buffer += sizeof chunk; 315 } 316 else 317 { 318 buffer = SAVE_CHUNK(CHUNK_PALETTE, cart->banks[i].palette, i); 319 buffer = SAVE_CHUNK(CHUNK_WAVEFORM, cart->banks[i].sfx.waveforms, i); 320 } 321 322 buffer = SAVE_CHUNK(CHUNK_TILES, cart->banks[i].tiles, i); 323 buffer = SAVE_CHUNK(CHUNK_SPRITES, cart->banks[i].sprites, i); 324 buffer = SAVE_CHUNK(CHUNK_MAP, cart->banks[i].map, i); 325 buffer = SAVE_CHUNK(CHUNK_SAMPLES, cart->banks[i].sfx.samples, i); 326 buffer = SAVE_CHUNK(CHUNK_PATTERNS, cart->banks[i].music.patterns, i); 327 buffer = SAVE_CHUNK(CHUNK_MUSIC, cart->banks[i].music.tracks, i); 328 buffer = SAVE_CHUNK(CHUNK_FLAGS, cart->banks[i].flags, i); 329 buffer = SAVE_CHUNK(CHUNK_SCREEN, cart->banks[i].screen, i); 330 } 331 332 const char* ptr; 333 if (cart->binary.size) 334 { 335 ptr = cart->binary.data; 336 s32 remaining = cart->binary.size; 337 for (s32 i = cart->binary.size / TIC_BANK_SIZE; i >= 0; --i, ptr += TIC_BANK_SIZE) 338 { 339 buffer = saveFixedChunk(buffer, CHUNK_BINARY, ptr, MIN(remaining, TIC_BANK_SIZE), i); 340 remaining -= TIC_BANK_SIZE; 341 } 342 } 343 344 ptr = cart->code.data; 345 for(s32 i = strlen(ptr) / TIC_BANK_SIZE; i >= 0; --i, ptr += TIC_BANK_SIZE) 346 buffer = saveFixedChunk(buffer, CHUNK_CODE, ptr, MIN(strlen(ptr), TIC_BANK_SIZE), i); 347 348 if(cart->lang) 349 SAVE_CHUNK(CHUNK_LANG, cart->lang, 0); 350 351#undef SAVE_CHUNK 352 353 return (s32)(buffer - start); 354}