00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../stdafx.h"
00013 #include "../gfx_func.h"
00014 #include "../fileio_func.h"
00015 #include "../debug.h"
00016 #include "../strings_func.h"
00017 #include "table/strings.h"
00018 #include "../error.h"
00019 #include "../core/math_func.hpp"
00020 #include "../core/alloc_type.hpp"
00021 #include "../core/bitmath_func.hpp"
00022 #include "grf.hpp"
00023
00024 extern const byte _palmap_w2d[];
00025
00027 enum SpriteColourComponent {
00028 SCC_RGB = 1 << 0,
00029 SCC_ALPHA = 1 << 1,
00030 SCC_PAL = 1 << 2,
00031 SCC_MASK = SCC_RGB | SCC_ALPHA | SCC_PAL,
00032 };
00033 DECLARE_ENUM_AS_BIT_SET(SpriteColourComponent)
00034
00035
00043 static bool WarnCorruptSprite(uint8 file_slot, size_t file_pos, int line)
00044 {
00045 static byte warning_level = 0;
00046 if (warning_level == 0) {
00047 SetDParamStr(0, FioGetFilename(file_slot));
00048 ShowErrorMessage(STR_NEWGRF_ERROR_CORRUPT_SPRITE, INVALID_STRING_ID, WL_ERROR);
00049 }
00050 DEBUG(sprite, warning_level, "[%i] Loading corrupted sprite from %s at position %i", line, FioGetFilename(file_slot), (int)file_pos);
00051 warning_level = 6;
00052 return false;
00053 }
00054
00068 bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, int64 num, byte type, ZoomLevel zoom_lvl, byte colour_fmt, byte container_format)
00069 {
00070 AutoFreePtr<byte> dest_orig(MallocT<byte>(num));
00071 byte *dest = dest_orig;
00072 const int64 dest_size = num;
00073
00074
00075 while (num > 0) {
00076 int8 code = FioReadByte();
00077
00078 if (code >= 0) {
00079
00080 int size = (code == 0) ? 0x80 : code;
00081 num -= size;
00082 if (num < 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
00083 for (; size > 0; size--) {
00084 *dest = FioReadByte();
00085 dest++;
00086 }
00087 } else {
00088
00089 const uint data_offset = ((code & 7) << 8) | FioReadByte();
00090 if (dest - data_offset < dest_orig) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
00091 int size = -(code >> 3);
00092 num -= size;
00093 if (num < 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
00094 for (; size > 0; size--) {
00095 *dest = *(dest - data_offset);
00096 dest++;
00097 }
00098 }
00099 }
00100
00101 if (num != 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
00102
00103 sprite->AllocateData(zoom_lvl, sprite->width * sprite->height);
00104
00105
00106 int bpp = 0;
00107 if (colour_fmt & SCC_RGB) bpp += 3;
00108 if (colour_fmt & SCC_ALPHA) bpp++;
00109 if (colour_fmt & SCC_PAL) bpp++;
00110
00111
00112 if (type & 0x08) {
00113 for (int y = 0; y < sprite->height; y++) {
00114 bool last_item = false;
00115
00116 int offset;
00117 if (container_format >= 2 && dest_size > UINT16_MAX) {
00118 offset = (dest_orig[y * 4 + 3] << 24) | (dest_orig[y * 4 + 2] << 16) | (dest_orig[y * 4 + 1] << 8) | dest_orig[y * 4];
00119 } else {
00120 offset = (dest_orig[y * 2 + 1] << 8) | dest_orig[y * 2];
00121 }
00122
00123
00124 dest = dest_orig + offset;
00125
00126 do {
00127 if (dest + (container_format >= 2 && sprite->width > 256 ? 4 : 2) > dest_orig + dest_size) {
00128 return WarnCorruptSprite(file_slot, file_pos, __LINE__);
00129 }
00130
00131 SpriteLoader::CommonPixel *data;
00132
00133 int length, skip;
00134 if (container_format >= 2 && sprite->width > 256) {
00135
00136
00137
00138 last_item = (dest[1] & 0x80) != 0;
00139 length = ((dest[1] & 0x7F) << 8) | dest[0];
00140 skip = (dest[3] << 8) | dest[2];
00141 dest += 4;
00142 } else {
00143
00144
00145
00146 last_item = ((*dest) & 0x80) != 0;
00147 length = (*dest++) & 0x7F;
00148 skip = *dest++;
00149 }
00150
00151 data = &sprite->data[y * sprite->width + skip];
00152
00153 if (skip + length > sprite->width || dest + length * bpp > dest_orig + dest_size) {
00154 return WarnCorruptSprite(file_slot, file_pos, __LINE__);
00155 }
00156
00157 for (int x = 0; x < length; x++) {
00158 if (colour_fmt & SCC_RGB) {
00159 data->r = *dest++;
00160 data->g = *dest++;
00161 data->b = *dest++;
00162 }
00163 data->a = (colour_fmt & SCC_ALPHA) ? *dest++ : 0xFF;
00164 if (colour_fmt & SCC_PAL) {
00165 switch (sprite_type) {
00166 case ST_NORMAL: data->m = _palette_remap_grf[file_slot] ? _palmap_w2d[*dest] : *dest; break;
00167 case ST_FONT: data->m = min(*dest, 2u); break;
00168 default: data->m = *dest; break;
00169 }
00170
00171 if (colour_fmt == SCC_PAL && *dest == 0) data->a = 0x00;
00172 dest++;
00173 }
00174 data++;
00175 }
00176 } while (!last_item);
00177 }
00178 } else {
00179 if (dest_size < sprite->width * sprite->height * bpp) {
00180 return WarnCorruptSprite(file_slot, file_pos, __LINE__);
00181 }
00182
00183 if (dest_size > sprite->width * sprite->height * bpp) {
00184 static byte warning_level = 0;
00185 DEBUG(sprite, warning_level, "Ignoring " OTTD_PRINTF64 " unused extra bytes from the sprite from %s at position %i", dest_size - sprite->width * sprite->height * bpp, FioGetFilename(file_slot), (int)file_pos);
00186 warning_level = 6;
00187 }
00188
00189 dest = dest_orig;
00190
00191 for (int i = 0; i < sprite->width * sprite->height; i++) {
00192 byte *pixel = &dest[i * bpp];
00193
00194 if (colour_fmt & SCC_RGB) {
00195 sprite->data[i].r = *pixel++;
00196 sprite->data[i].g = *pixel++;
00197 sprite->data[i].b = *pixel++;
00198 }
00199 sprite->data[i].a = (colour_fmt & SCC_ALPHA) ? *pixel++ : 0xFF;
00200 if (colour_fmt & SCC_PAL) {
00201 switch (sprite_type) {
00202 case ST_NORMAL: sprite->data[i].m = _palette_remap_grf[file_slot] ? _palmap_w2d[*pixel] : *pixel; break;
00203 case ST_FONT: sprite->data[i].m = min(*pixel, 2u); break;
00204 default: sprite->data[i].m = *pixel; break;
00205 }
00206
00207 if (colour_fmt == SCC_PAL && *pixel == 0) sprite->data[i].a = 0x00;
00208 pixel++;
00209 }
00210 }
00211 }
00212
00213 return true;
00214 }
00215
00216 uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
00217 {
00218
00219 if (load_32bpp) return 0;
00220
00221
00222 FioSeekToFile(file_slot, file_pos);
00223
00224
00225 int num = FioReadWord();
00226 byte type = FioReadByte();
00227
00228
00229 if (type == 0xFF) return 0;
00230
00231 ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL;
00232
00233 sprite[zoom_lvl].height = FioReadByte();
00234 sprite[zoom_lvl].width = FioReadWord();
00235 sprite[zoom_lvl].x_offs = FioReadWord();
00236 sprite[zoom_lvl].y_offs = FioReadWord();
00237
00238
00239
00240 num = (type & 0x02) ? sprite[zoom_lvl].width * sprite[zoom_lvl].height : num - 8;
00241
00242 if (DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, num, type, zoom_lvl, SCC_PAL, 1)) return 1 << zoom_lvl;
00243
00244 return 0;
00245 }
00246
00247 uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
00248 {
00249 static const ZoomLevel zoom_lvl_map[6] = {ZOOM_LVL_OUT_4X, ZOOM_LVL_NORMAL, ZOOM_LVL_OUT_2X, ZOOM_LVL_OUT_8X, ZOOM_LVL_OUT_16X, ZOOM_LVL_OUT_32X};
00250
00251
00252 if (file_pos == SIZE_MAX) return 0;
00253
00254
00255 FioSeekToFile(file_slot, file_pos);
00256
00257 uint32 id = FioReadDword();
00258
00259 uint8 loaded_sprites = 0;
00260 do {
00261 int64 num = FioReadDword();
00262 size_t start_pos = FioGetPos();
00263 byte type = FioReadByte();
00264
00265
00266 if (type == 0xFF) return 0;
00267
00268 byte colour = type & SCC_MASK;
00269 byte zoom = FioReadByte();
00270
00271 if (colour != 0 && (load_32bpp ? colour != SCC_PAL : colour == SCC_PAL) && (sprite_type == ST_NORMAL ? zoom < lengthof(zoom_lvl_map) : zoom == 0)) {
00272 ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? zoom_lvl_map[zoom] : ZOOM_LVL_NORMAL;
00273
00274 if (HasBit(loaded_sprites, zoom_lvl)) {
00275
00276 DEBUG(sprite, 1, "Ignoring duplicate zoom level sprite %u from %s", id, FioGetFilename(file_slot));
00277 FioSkipBytes(num - 2);
00278 continue;
00279 }
00280
00281 sprite[zoom_lvl].height = FioReadWord();
00282 sprite[zoom_lvl].width = FioReadWord();
00283 sprite[zoom_lvl].x_offs = FioReadWord();
00284 sprite[zoom_lvl].y_offs = FioReadWord();
00285
00286
00287 type = type & ~SCC_MASK;
00288
00289
00290 int bpp = 0;
00291 if (colour & SCC_RGB) bpp += 3;
00292 if (colour & SCC_ALPHA) bpp++;
00293 if (colour & SCC_PAL) bpp++;
00294
00295
00296
00297 uint decomp_size = (type & 0x08) ? FioReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height * bpp;
00298
00299 bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2);
00300 if (FioGetPos() != start_pos + num) {
00301 WarnCorruptSprite(file_slot, file_pos, __LINE__);
00302 return 0;
00303 }
00304
00305 if (valid) SetBit(loaded_sprites, zoom_lvl);
00306 } else {
00307
00308 FioSkipBytes(num - 2);
00309 }
00310
00311 } while (FioReadDword() == id);
00312
00313 return loaded_sprites;
00314 }
00315
00316 uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
00317 {
00318 if (this->container_ver >= 2) {
00319 return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type, load_32bpp);
00320 } else {
00321 return LoadSpriteV1(sprite, file_slot, file_pos, sprite_type, load_32bpp);
00322 }
00323 }