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 if (sprite[zoom_lvl].width > INT16_MAX) {
00239 WarnCorruptSprite(file_slot, file_pos, __LINE__);
00240 return 0;
00241 }
00242
00243
00244
00245 num = (type & 0x02) ? sprite[zoom_lvl].width * sprite[zoom_lvl].height : num - 8;
00246
00247 if (DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, num, type, zoom_lvl, SCC_PAL, 1)) return 1 << zoom_lvl;
00248
00249 return 0;
00250 }
00251
00252 uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
00253 {
00254 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};
00255
00256
00257 if (file_pos == SIZE_MAX) return 0;
00258
00259
00260 FioSeekToFile(file_slot, file_pos);
00261
00262 uint32 id = FioReadDword();
00263
00264 uint8 loaded_sprites = 0;
00265 do {
00266 int64 num = FioReadDword();
00267 size_t start_pos = FioGetPos();
00268 byte type = FioReadByte();
00269
00270
00271 if (type == 0xFF) return 0;
00272
00273 byte colour = type & SCC_MASK;
00274 byte zoom = FioReadByte();
00275
00276 if (colour != 0 && (load_32bpp ? colour != SCC_PAL : colour == SCC_PAL) && (sprite_type == ST_NORMAL ? zoom < lengthof(zoom_lvl_map) : zoom == 0)) {
00277 ZoomLevel zoom_lvl = (sprite_type == ST_NORMAL) ? zoom_lvl_map[zoom] : ZOOM_LVL_NORMAL;
00278
00279 if (HasBit(loaded_sprites, zoom_lvl)) {
00280
00281 DEBUG(sprite, 1, "Ignoring duplicate zoom level sprite %u from %s", id, FioGetFilename(file_slot));
00282 FioSkipBytes(num - 2);
00283 continue;
00284 }
00285
00286 sprite[zoom_lvl].height = FioReadWord();
00287 sprite[zoom_lvl].width = FioReadWord();
00288 sprite[zoom_lvl].x_offs = FioReadWord();
00289 sprite[zoom_lvl].y_offs = FioReadWord();
00290
00291 if (sprite[zoom_lvl].width > INT16_MAX || sprite[zoom_lvl].height > INT16_MAX) {
00292 WarnCorruptSprite(file_slot, file_pos, __LINE__);
00293 return 0;
00294 }
00295
00296
00297 type = type & ~SCC_MASK;
00298
00299
00300 int bpp = 0;
00301 if (colour & SCC_RGB) bpp += 3;
00302 if (colour & SCC_ALPHA) bpp++;
00303 if (colour & SCC_PAL) bpp++;
00304
00305
00306
00307 uint decomp_size = (type & 0x08) ? FioReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height * bpp;
00308
00309 bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2);
00310 if (FioGetPos() != start_pos + num) {
00311 WarnCorruptSprite(file_slot, file_pos, __LINE__);
00312 return 0;
00313 }
00314
00315 if (valid) SetBit(loaded_sprites, zoom_lvl);
00316 } else {
00317
00318 FioSkipBytes(num - 2);
00319 }
00320
00321 } while (FioReadDword() == id);
00322
00323 return loaded_sprites;
00324 }
00325
00326 uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
00327 {
00328 if (this->container_ver >= 2) {
00329 return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type, load_32bpp);
00330 } else {
00331 return LoadSpriteV1(sprite, file_slot, file_pos, sprite_type, load_32bpp);
00332 }
00333 }