/* * OpenTyrian Classic: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "file.h" #include "opentyr.h" #include "sprite.h" #include "video.h" #include Sprite_array sprite_table[SPRITE_TABLES_MAX]; Sprite2_array eShapes1, eShapes2, eShapes3, eShapes4, eShapes5, eShapes6; Sprite2_array shapesC1, shapes6, shapes9, shapesW2; void load_sprites_file( unsigned int table, const char *filename ) { free_sprites(table); FILE *f = dir_fopen_die(data_dir(), filename, "rb"); load_sprites(table, f); fclose(f); } void load_sprites( unsigned int table, FILE *f ) { free_sprites(table); Uint16 temp; efread(&temp, sizeof(Uint16), 1, f); sprite_table[table].count = temp; for (unsigned int i = 0; i < sprite_table[table].count; ++i) { Sprite * const cur_sprite = sprite(table, i); if (!getc(f)) // sprite is empty continue; efread(&cur_sprite->width, sizeof(Uint16), 1, f); efread(&cur_sprite->height, sizeof(Uint16), 1, f); efread(&cur_sprite->size, sizeof(Uint16), 1, f); cur_sprite->data = (Uint8 *)malloc(cur_sprite->size); efread(cur_sprite->data, sizeof(Uint8), cur_sprite->size, f); } } void free_sprites( unsigned int table ) { for (unsigned int i = 0; i < sprite_table[table].count; ++i) { Sprite * const cur_sprite = sprite(table, i); cur_sprite->width = 0; cur_sprite->height = 0; cur_sprite->size = 0; free(cur_sprite->data); cur_sprite->data = NULL; } sprite_table[table].count = 0; } // does not clip on left or right edges of surface void blit_sprite( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index ) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = *data; pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface void blit_sprite_blend( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index ) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = (*data & 0xf0) | (((*pixels & 0x0f) + (*data & 0x0f)) / 2); pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface // unsafe because it doesn't check that value won't overflow into hue // we can replace it when we know that we don't rely on that 'feature' void blit_sprite_hv_unsafe( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } hue <<= 4; const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = hue | ((*data & 0x0f) + value); pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface void blit_sprite_hv( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } hue <<= 4; const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) { Uint8 temp_value = (*data & 0x0f) + value; if (temp_value > 0xf) temp_value = (temp_value >= 0x1f) ? 0x0 : 0xf; *pixels = hue | temp_value; } pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface void blit_sprite_hv_blend( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } hue <<= 4; const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) { Uint8 temp_value = (*data & 0x0f) + value; if (temp_value > 0xf) temp_value = (temp_value >= 0x1f) ? 0x0 : 0xf; *pixels = hue | (((*pixels & 0x0f) + temp_value) / 2); } pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } // does not clip on left or right edges of surface void blit_sprite_dark( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, bool black ) { if (index >= sprite_table[table].count || !sprite_exists(table, index)) { assert(false); return; } const Sprite * const cur_sprite = sprite(table, index); const Uint8 *data = cur_sprite->data; const Uint8 * const data_ul = data + cur_sprite->size; const unsigned int width = cur_sprite->width; unsigned int x_offset = 0; assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit for (; data < data_ul; ++data) { switch (*data) { case 255: // transparent pixels data++; // next byte tells how many pixels += *data; x_offset += *data; break; case 254: // next pixel row pixels += width - x_offset; x_offset = width; break; case 253: // 1 transparent pixel pixels++; x_offset++; break; default: // set a pixel if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = black ? 0x00 : ((*pixels & 0xf0) | ((*pixels & 0x0f) / 2)); pixels++; x_offset++; break; } if (x_offset >= width) { pixels += surface->pitch - x_offset; x_offset = 0; } } } void JE_loadCompShapes( Sprite2_array *sprite2s, char s ) { char buffer[20]; snprintf(buffer, sizeof(buffer), "newsh%c.shp", tolower((unsigned char)s)); FILE *f = dir_fopen_die(data_dir(), buffer, "rb"); sprite2s->size = ftell_eof(f); JE_loadCompShapesB(sprite2s, f); fclose(f); } void JE_loadCompShapesB( Sprite2_array *sprite2s, FILE *f ) { free_sprite2s(sprite2s); sprite2s->data = (Uint8 *)malloc(sizeof(Uint8) * sprite2s->size); efread(sprite2s->data, sizeof(Uint8), sprite2s->size, f); } void free_sprite2s( Sprite2_array *sprite2s ) { free(sprite2s->data); sprite2s->data = NULL; } // does not clip on left or right edges of surface void blit_sprite2( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) { assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { pixels += *data & 0x0f; // second nibble: transparent pixel count unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count if (count == 0) // move to next pixel row { pixels += VGAScreen->pitch - 12; } else { while (count--) { ++data; if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = *data; ++pixels; } } } } // does not clip on left or right edges of surface void blit_sprite2_blend( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) { assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { pixels += *data & 0x0f; // second nibble: transparent pixel count unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count if (count == 0) // move to next pixel row { pixels += VGAScreen->pitch - 12; } else { while (count--) { ++data; if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = (((*data & 0x0f) + (*pixels & 0x0f)) / 2) | (*data & 0xf0); ++pixels; } } } } // does not clip on left or right edges of surface void blit_sprite2_darken( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) { assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { pixels += *data & 0x0f; // second nibble: transparent pixel count unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count if (count == 0) // move to next pixel row { pixels += VGAScreen->pitch - 12; } else { while (count--) { ++data; if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = ((*pixels & 0x0f) / 2) + (*pixels & 0xf0); ++pixels; } } } } // does not clip on left or right edges of surface void blit_sprite2_filter( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index, Uint8 filter ) { assert(surface->format->BitsPerPixel == 8); Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); for (; *data != 0x0f; ++data) { pixels += *data & 0x0f; // second nibble: transparent pixel count unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count if (count == 0) // move to next pixel row { pixels += VGAScreen->pitch - 12; } else { while (count--) { ++data; if (pixels >= pixels_ul) return; if (pixels >= pixels_ll) *pixels = filter | (*data & 0x0f); ++pixels; } } } } // does not clip on left or right edges of surface void blit_sprite2x2( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) { blit_sprite2(surface, x, y, sprite2s, index); blit_sprite2(surface, x + 12, y, sprite2s, index + 1); blit_sprite2(surface, x, y + 14, sprite2s, index + 19); blit_sprite2(surface, x + 12, y + 14, sprite2s, index + 20); } // does not clip on left or right edges of surface void blit_sprite2x2_blend( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) { blit_sprite2_blend(surface, x, y, sprite2s, index); blit_sprite2_blend(surface, x + 12, y, sprite2s, index + 1); blit_sprite2_blend(surface, x, y + 14, sprite2s, index + 19); blit_sprite2_blend(surface, x + 12, y + 14, sprite2s, index + 20); } // does not clip on left or right edges of surface void blit_sprite2x2_darken( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) { blit_sprite2_darken(surface, x, y, sprite2s, index); blit_sprite2_darken(surface, x + 12, y, sprite2s, index + 1); blit_sprite2_darken(surface, x, y + 14, sprite2s, index + 19); blit_sprite2_darken(surface, x + 12, y + 14, sprite2s, index + 20); } void JE_loadMainShapeTables( const char *shpfile ) { const int SHP_NUM = 12; FILE *f = dir_fopen_die(data_dir(), shpfile, "rb"); JE_word shpNumb; JE_longint shpPos[SHP_NUM + 1]; // +1 for storing file length efread(&shpNumb, sizeof(JE_word), 1, f); assert(shpNumb + 1u <= COUNTOF(shpPos)); for (int i = 0; i < shpNumb; i++) { efread(&shpPos[i], sizeof(JE_longint), 1, f); } fseek(f, 0, SEEK_END); shpPos[shpNumb] = ftell(f); int i; // fonts, interface, option sprites for (i = 0; i < 7; i++) { fseek(f, shpPos[i], SEEK_SET); load_sprites(i, f); } // player shot sprites shapesC1.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&shapesC1, f); i++; // player ship sprites shapes9.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&shapes9 , f); i++; // power-up sprites eShapes6.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&eShapes6, f); i++; // coins, datacubes, etc sprites eShapes5.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&eShapes5, f); i++; // more player shot sprites shapesW2.size = shpPos[i + 1] - shpPos[i]; JE_loadCompShapesB(&shapesW2, f); fclose(f); } void free_main_shape_tables( void ) { for (uint i = 0; i < COUNTOF(sprite_table); ++i) free_sprites(i); free_sprite2s(&shapesC1); free_sprite2s(&shapes9); free_sprite2s(&eShapes6); free_sprite2s(&eShapes5); free_sprite2s(&shapesW2); } // kate: tab-width 4; vim: set noet: