Atrinik Client  4.0
image.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <toolkit/packet.h>
32 #include <toolkit/string.h>
33 #include <toolkit/path.h>
34 
42 static bmap_t *image_bmaps = NULL;
46 static size_t image_bmaps_size = 0;
47 
51 static void
53 {
54  HARD_ASSERT(bmap != NULL);
55  efree(bmap->name);
56 }
57 
61 void
62 image_init (void)
63 {
64  FILE *fp = path_fopen(FILE_ATRINIK_P0, "rb");
65  if (fp == NULL) {
66  return;
67  }
68 
69  size_t tmp_buf_size = 24 * 1024;
70  char *tmp_buf = emalloc(tmp_buf_size);
71 
72  char buf[HUGE_BUF];
73  while (fgets(buf, sizeof(buf), fp) != NULL) {
74  if (strncmp(buf, "IMAGE ", 6)) {
75  LOG(ERROR, "The file %s is corrupted.", FILE_ATRINIK_P0);
76  exit(1);
77  }
78 
79  char *cp;
80  /* Skip across the image ID data. */
81  for (cp = buf + 6; *cp != ' '; cp++) {
82  }
83 
84  size_t len = atoi(cp);
85 
86  /* Skip across the length data. */
87  for (cp = cp + 1; *cp != ' '; cp++) {
88  }
89 
90  /* Adjust the buffer if necessary. */
91  if (len > tmp_buf_size) {
92  tmp_buf_size = len;
93  tmp_buf = erealloc(tmp_buf, tmp_buf_size);
94  }
95 
96  long pos = ftell(fp);
97  if (fread(tmp_buf, 1, len, fp) != len) {
98  break;
99  }
100 
101  string_strip_newline(cp);
102 
103  /* Trim left whitespace. */
104  while (*cp == ' ') {
105  cp++;
106  }
107 
108  bmap_hash_t *bmap = ecalloc(1, sizeof(*bmap));
109  bmap->bmap.name = estrdup(cp);
110  bmap->bmap.crc32 = crc32(1L, (const unsigned char FAR *) tmp_buf, len);
111  bmap->bmap.len = len;
112  bmap->bmap.pos = pos;
113  HASH_ADD_KEYPTR(hh,
114  image_bmap_packs,
115  bmap->bmap.name,
116  strlen(bmap->bmap.name),
117  bmap);
118  }
119 
120  efree(tmp_buf);
121  fclose(fp);
122 }
123 
124 /*
125  * Deinitialize the image packs.
126  */
127 void
128 image_deinit (void)
129 {
130  bmap_hash_t *curr, *tmp;
131 
132  HASH_ITER(hh, image_bmap_packs, curr, tmp) {
133  HASH_DEL(image_bmap_packs, curr);
134  bmap_free(&curr->bmap);
135  efree(curr);
136  }
137 }
138 
142 void
144 {
145  FILE *fp = server_file_open_name(SERVER_FILE_BMAPS);
146  if (fp == NULL) {
147  return;
148  }
149 
150  /* Free previously allocated bmaps. */
152 
153  char buf[HUGE_BUF];
154  while (fgets(buf, sizeof(buf), fp)) {
155  uint32_t len, crc;
156  char name[HUGE_BUF];
157  if (sscanf(buf, "%x %x %s", &len, &crc, name) != 3) {
158  LOG(BUG, "Syntax error in server bmaps file: %s", buf);
159  break;
160  }
161 
162  bmap_hash_t *bmap;
163  HASH_FIND_STR(image_bmap_packs, name, bmap);
164 
165  /* Expand the array. */
166  image_bmaps = erealloc(image_bmaps,
167  sizeof(*image_bmaps) * (image_bmaps_size + 1));
168 
169  /* Does it exist, and the lengths and checksums match? */
170  if (bmap != NULL && bmap->bmap.len == len && bmap->bmap.crc32 == crc) {
171  image_bmaps[image_bmaps_size].pos = bmap->bmap.pos;
172  } else {
173  /* It doesn't exist in the atrinik.p0 file. */
174  image_bmaps[image_bmaps_size].pos = -1;
175  }
176 
177  image_bmaps[image_bmaps_size].len = len;
178  image_bmaps[image_bmaps_size].crc32 = crc;
179  image_bmaps[image_bmaps_size].name = estrdup(name);
180 
182  }
183 
184  fclose(fp);
185 }
186 
190 void
192 {
193  if (image_bmaps != NULL) {
194  for (size_t i = 0; i < image_bmaps_size; i++) {
195  efree(image_bmaps[i].name);
196  }
197 
198  efree(image_bmaps);
199  image_bmaps = NULL;
200  image_bmaps_size = 0;
201  }
202 
203  for (size_t i = 0; i < MAX_FACE_TILES; i++) {
204  if (FaceList[i].name != NULL) {
205  efree(FaceList[i].name);
206  FaceList[i].name = NULL;
207  sprite_free_sprite(FaceList[i].sprite);
208  FaceList[i].sprite = NULL;
209  FaceList[i].checksum = 0;
210  FaceList[i].flags = 0;
211  }
212  }
213 
215 }
216 
227 void
228 finish_face_cmd (int facenum, uint32_t checksum, const char *face)
229 {
230  HARD_ASSERT(facenum >= 0);
231  HARD_ASSERT(face != NULL);
232 
233  /* Loaded or requested. */
234  if (FaceList[facenum].name != NULL) {
235  if (strcmp(face, FaceList[facenum].name) == 0 &&
236  checksum == FaceList[facenum].checksum &&
237  FaceList[facenum].sprite != NULL) {
238  return;
239  }
240 
241  /* Something is different. */
242  efree(FaceList[facenum].name);
243  FaceList[facenum].name = NULL;
244  sprite_free_sprite(FaceList[facenum].sprite);
245  FaceList[facenum].sprite = NULL;
246  }
247 
248  char buf[HUGE_BUF];
249  snprintf(VS(buf), "%s.png", face);
250  FaceList[facenum].name = estrdup(buf);
251  FaceList[facenum].checksum = checksum;
252 
253  /* Check private cache first */
254  snprintf(VS(buf), DIRECTORY_CACHE "/%s", FaceList[facenum].name);
255 
256  FILE *fp = path_fopen(buf, "rb");
257  if (fp != NULL) {
258  struct stat statbuf;
259  fstat(fileno(fp), &statbuf);
260  size_t len = statbuf.st_size;
261  unsigned char *data = emalloc(len);
262  len = fread(data, 1, len, fp);
263  fclose(fp);
264  uint32_t newsum = 0;
265 
266  /* Something is wrong... Unlink the file and let it reload. */
267  if (len == 0) {
268  unlink(buf);
269  checksum = 1;
270  } else {
271  /* Checksum check */
272  newsum = crc32(1L, data, len);
273  }
274 
275  efree(data);
276 
277  if (newsum == checksum) {
278  FaceList[facenum].sprite = sprite_tryload_file(buf, 0, NULL);
279  if (FaceList[facenum].sprite != NULL) {
280  return;
281  }
282  }
283  }
284 
285  packet_struct *packet = packet_new(SERVER_CMD_ASK_FACE, 16, 0);
286  packet_append_uint16(packet, facenum);
287  socket_send_packet(packet);
288 }
289 
296 static void
298 {
299  FILE *fp = path_fopen(FILE_ATRINIK_P0, "rb");
300  if (fp == NULL) {
301  LOG(ERROR, "Failed to open %s", FILE_ATRINIK_P0);
302  return;
303  }
304 
305  if (lseek(fileno(fp), image_bmaps[num].pos, SEEK_SET) == -1) {
306  LOG(ERROR, "Failed to seek to %ld: %s",
307  image_bmaps[num].pos, strerror(errno));
308  fclose(fp);
309  return;
310  }
311 
312  char *buf = emalloc(image_bmaps[num].len);
313  size_t num_read = fread(buf, 1, image_bmaps[num].len, fp);
314  if (num_read != image_bmaps[num].len) {
315  LOG(ERROR, "Expected %" PRIu64 " bytes but read %" PRIu64 " bytes",
316  (uint64_t) image_bmaps[num].len,
317  (uint64_t) num_read);
318  efree(buf);
319  fclose(fp);
320  return;
321  }
322 
323  fclose(fp);
324 
325  SDL_RWops *rwop = SDL_RWFromMem(buf, image_bmaps[num].len);
326  if (rwop == NULL) {
327  LOG(ERROR, "Failed to load image from pack using SDL_RWFromMem(): %s",
328  SDL_GetError());
329  } else {
330  FaceList[num].sprite = sprite_tryload_file(NULL, 0, rwop);
331  SDL_FreeRW(rwop);
332  }
333 
334  efree(buf);
335 }
336 
345 static bool
346 load_gfx_user_face (uint16_t num)
347 {
348  /* First check for this image in gfx_user directory. */
349  char buf[MAX_BUF];
350  snprintf(VS(buf), DIRECTORY_GFX_USER "/%s.png", image_bmaps[num].name);
351 
352  FILE *fp = path_fopen(buf, "rb");
353  if (fp == NULL) {
354  return false;
355  }
356 
357  struct stat statbuf;
358  fstat(fileno(fp), &statbuf);
359  size_t len = statbuf.st_size;
360  unsigned char *data = emalloc(len);
361  len = fread(data, 1, len, fp);
362 
363  bool ret = false;
364  if (len == 0) {
365  goto out;
366  }
367 
368  if (FaceList[num].sprite != NULL) {
369  sprite_free_sprite(FaceList[num].sprite);
370  }
371 
372  if (FaceList[num].name != NULL) {
373  efree(FaceList[num].name);
374  FaceList[num].name = NULL;
375  }
376 
377  /* Try to load it. */
378  FaceList[num].sprite = sprite_tryload_file(buf, 0, NULL);
379  if (FaceList[num].sprite == NULL) {
380  goto out;
381  }
382 
383  FaceList[num].name = estrdup(buf);
384  FaceList[num].checksum = crc32(1L, data, len);
385  ret = true;
386 
387 out:
388  efree(data);
389  fclose(fp);
390 
391  return ret;
392 }
393 
401 void
403 {
404  char buf[MAX_BUF];
405  uint16_t num = (uint16_t) (pnum &~0x8000);
406 
408  load_gfx_user_face(num)) {
409  return;
410  }
411 
412  /* Loaded or requested */
413  if (FaceList[num].name != NULL || FaceList[num].flags & FACE_REQUESTED) {
414  return;
415  }
416 
417  if (num >= image_bmaps_size) {
418  LOG(ERROR, "Server sent picture ID too loarge (%d, max: %" PRIu64 ")",
419  num, (uint64_t) image_bmaps_size);
420  return;
421  }
422 
423  if (load_gfx_user_face(num)) {
424  return;
425  }
426 
427  if (image_bmaps[num].pos != -1) {
428  snprintf(VS(buf), "%s.png", image_bmaps[num].name);
429  FaceList[num].name = estrdup(buf);
430  FaceList[num].checksum = image_bmaps[num].crc32;
432  } else {
433  FaceList[num].flags |= FACE_REQUESTED;
434  finish_face_cmd(num, image_bmaps[num].crc32, image_bmaps[num].name);
435  }
436 }
437 
447 int
448 image_get_id (const char *name)
449 {
450  int l = 0, r = image_bmaps_size - 1;
451 
452  /* All the faces in ::image_bmaps are already sorted, so we can use a
453  * binary search here. */
454  while (r >= l) {
455  int x = (l + r) / 2;
456  int cmp = strcmp(name, image_bmaps[x].name);
457  if (cmp < 0) {
458  r = x - 1;
459  } else if (cmp > 0) {
460  l = x + 1;
461  } else {
463  return x;
464  }
465  }
466 
467  return -1;
468 }
size_t len
Definition: image.h:45
static size_t image_bmaps_size
Definition: image.c:46
long pos
Definition: image.h:50
void finish_face_cmd(int facenum, uint32_t checksum, const char *face)
Definition: image.c:228
#define MAX_FACE_TILES
Definition: config.h:52
static void bmap_free(bmap_t *bmap)
Definition: image.c:52
FILE * server_file_open_name(const char *name)
Definition: server_files.c:437
void image_request_face(int pnum)
Definition: image.c:402
bmap_t bmap
Definition: image.h:65
_face_struct FaceList[MAX_FACE_TILES]
Definition: main.c:77
sprite_struct * sprite_tryload_file(char *fname, uint32_t flag, SDL_RWops *rwop)
Definition: sprite.c:110
int image_get_id(const char *name)
Definition: image.c:448
int64_t setting_get_int(int cat, int setting)
Definition: settings.c:414
static bool load_gfx_user_face(uint16_t num)
Definition: image.c:346
static bmap_t * image_bmaps
Definition: image.c:42
void image_bmaps_init(void)
Definition: image.c:143
Definition: image.h:36
static bmap_hash_t * image_bmap_packs
Definition: image.c:38
char * name
Definition: image.h:40
void sprite_cache_free_all(void)
Definition: sprite.c:262
static void load_picture_from_pack(int num)
Definition: image.c:297
void image_bmaps_deinit(void)
Definition: image.c:191
unsigned long crc32
Definition: image.h:55
void image_init(void)
Definition: image.c:62
void sprite_free_sprite(sprite_struct *sprite)
Definition: sprite.c:164