| /* | 
 |     genpeimg - Modify Portable Executable flags and properties. | 
 |     Copyright (C) 2009-2016  mingw-w64 project | 
 |  | 
 |     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 3 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, see <http://www.gnu.org/licenses/>. | 
 | */ | 
 | #include <inttypes.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include "img.h" | 
 |  | 
 | file_image * | 
 | fimg_create (void) | 
 | { | 
 |   file_image *r = (file_image *) malloc (sizeof (file_image)); | 
 |   if (r) | 
 |     memset (r, 0, sizeof (file_image)); | 
 |   return r; | 
 | } | 
 |  | 
 | static void | 
 | fimg_free_content (file_image *pimg) | 
 | { | 
 |   if (!pimg) | 
 |     return; | 
 |   if (pimg->data) | 
 |     free (pimg->data); | 
 |   if (pimg->filename) | 
 |     free (pimg->filename); | 
 |   memset (pimg, 0, sizeof (file_image)); | 
 | } | 
 |  | 
 | void | 
 | fimg_free (file_image *pimg) | 
 | { | 
 |   if (!pimg) | 
 |     return; | 
 |   if (pimg->want_save && pimg->is_modified) | 
 |     fimg_save_as (pimg, pimg->filename, pimg->data_len); | 
 |   fimg_free_content (pimg); | 
 |   free (pimg); | 
 | } | 
 |  | 
 | int fimg_load (file_image *pimg, const char *fname) | 
 | { | 
 |   FILE *fp; | 
 |   size_t r; | 
 |  | 
 |   fp = fopen (fname, "rb"); | 
 |   if (!pimg || !fp) | 
 |     return 0; | 
 |   fimg_free_content (pimg); | 
 |   pimg->filename = strdup (fname); | 
 |   if (pimg->filename == NULL) | 
 |     { | 
 |       fclose (fp); | 
 |       return 0; | 
 |     } | 
 |   fseek (fp, 0, SEEK_END); | 
 |   pimg->data_len = (size_t) ftell (fp); | 
 |   fseek (fp, 0, SEEK_SET); | 
 |   if (pimg->data_len == FIMG_POS_END) | 
 |     { | 
 |       fclose (fp); | 
 |       fimg_free_content (pimg); | 
 |       return 0; | 
 |     } | 
 |   if (pimg->data_len == 0) | 
 |     { | 
 |       fclose (fp); | 
 |       return 1; | 
 |     } | 
 |   pimg->data = (unsigned char *) malloc (pimg->data_len); | 
 |   if (pimg->data == NULL) | 
 |     { | 
 |       fclose (fp); | 
 |       fimg_free_content (pimg); | 
 |       return 0; | 
 |     } | 
 |   r = fread (pimg->data, 1, pimg->data_len, fp); | 
 |   fclose (fp); | 
 |   return r == pimg->data_len; | 
 | } | 
 |  | 
 | int | 
 | fimg_rename (file_image *pimg, const char *newfname) | 
 | { | 
 |   char *n = NULL; | 
 |   if (!pimg) | 
 |     return 0; | 
 |   if (newfname) | 
 |     { | 
 |       n = strdup (newfname); | 
 |       if (!n) | 
 |         return 0; | 
 |     } | 
 |   if (pimg->filename) | 
 |     free (pimg->filename); | 
 |   pimg->filename = n; | 
 |   return 1; | 
 | } | 
 |  | 
 | int | 
 | fimg_save_as (file_image *pimg, const char *fname, size_t length) | 
 | { | 
 |   FILE *fp; | 
 |  | 
 |   if (!fname || *fname == 0) | 
 |     return 0; | 
 |   fp = fopen (fname, "wb"); | 
 |   if (!fp) | 
 |     return 0; | 
 |   if (length != 0) | 
 |     fwrite (pimg->data, 1, length, fp); | 
 |   fclose (fp); | 
 |   pimg->is_modified = 0; | 
 |   return 1; | 
 | } | 
 |  | 
 | int fimg_save (file_image *pimg) | 
 | { | 
 |   return fimg_save_as (pimg, pimg->filename, pimg->data_len); | 
 | } | 
 |  | 
 | file_image * | 
 | fimg_clone (const file_image *pimg) | 
 | { | 
 |   file_image *r = fimg_create (); | 
 |  | 
 |   if (!r) | 
 |     return NULL; | 
 |   r->filename = (pimg->filename ? strdup (pimg->filename) : NULL); | 
 |   if (pimg->filename && r->filename == NULL) | 
 |     { | 
 |       fimg_free (r); | 
 |       return NULL; | 
 |     } | 
 |   r->data_len = pimg->data_len; | 
 |   if (r->data_len) | 
 |     { | 
 |       r->data = (unsigned char *) malloc (r->data_len); | 
 |       if (r->data == NULL) | 
 |         { | 
 | 	  fimg_free (r); | 
 | 	  return NULL; | 
 | 	} | 
 |       memcpy (r->data, pimg->data, r->data_len); | 
 |     } | 
 |   r->is_modified = 1; | 
 |   r->want_save = 0; | 
 |   return r; | 
 | } | 
 |  | 
 | int | 
 | fimg_resize (file_image *pimg, size_t new_size) | 
 | { | 
 |   unsigned char *h; | 
 |   if (!pimg) | 
 |     return 0; | 
 |   if (pimg->data_len >= new_size) | 
 |     { | 
 |       pimg->data_len = new_size; | 
 |       pimg->is_modified = 1; | 
 |       return 1; | 
 |     } | 
 |   h = (unsigned char *) realloc (pimg->data, new_size); | 
 |   if (!h) | 
 |     return 0; | 
 |   pimg->data = h; | 
 |   memset (h + pimg->data_len, 0, (new_size - pimg->data_len)); | 
 |   pimg->data_len = new_size; | 
 |   pimg->is_modified = 1; | 
 |   return 1; | 
 | } | 
 |    | 
 | int | 
 | fimg_replace_at (file_image *pimg, const void *dta, size_t pos, size_t length) | 
 | { | 
 |   size_t end = pos + length; | 
 |   if (end > pimg->data_len) | 
 |     { | 
 |       if (!fimg_resize (pimg, end)) | 
 |         return 0; | 
 |     } | 
 |   if (!length) | 
 |     return 1; | 
 |   if (!dta) | 
 |     memset (pimg->data + pos, 0, length); | 
 |   else | 
 |     memcpy (pimg->data + pos, dta, length); | 
 |   pimg->is_modified = 1; | 
 |   return 1; | 
 | } | 
 |  | 
 | int | 
 | fimg_insert_at (file_image *pimg, const void *dta, size_t pos, size_t length) | 
 | { | 
 |   size_t new_size, old_end, sv_len = length; | 
 |   if (!pimg) | 
 |     return 0; | 
 |   new_size = pimg->data_len + length; | 
 |   if (pos > pimg->data_len) | 
 |     length += pos - pimg->data_len; | 
 |   if (!length) | 
 |     return 1; | 
 |   old_end = pimg->data_len; | 
 |   if (!fimg_resize (pimg,new_size)) | 
 |     return 0; | 
 |   if (pos < old_end) | 
 |     memmove (pimg->data + pos + length, pimg->data + pos, (old_end - pos)); | 
 |   if (!dta && sv_len) | 
 |     memset (pimg->data + pos, 0, sv_len); | 
 |   else if (dta && sv_len) | 
 |     memcpy (pimg->data + pos, dta, sv_len); | 
 |   pimg->is_modified = 1; | 
 |   return 1; | 
 | } | 
 |  | 
 | int | 
 | fimg_remove_at (file_image *pimg, size_t pos, size_t length) | 
 | { | 
 |   size_t emax = pos + length; | 
 |   if (!pimg) | 
 |     return 0; | 
 |   if (pos > pimg->data_len) | 
 |     return 1; | 
 |   if (emax > pimg->data_len) | 
 |     length = pimg->data_len - pos; | 
 |   if (!length) | 
 |     return 1; | 
 |   if (pimg->data_len > (pos + length)) | 
 |     memmove (pimg->data + pos, pimg->data + pos + length, (pimg->data_len - (pos + length))); | 
 |   fimg_resize (pimg, pimg->data_len - length); | 
 |   return 1; | 
 | } | 
 |  | 
 | unsigned char | 
 | fimg_get_uchar_at (const file_image *pimg, size_t pos) | 
 | { | 
 |   if (!pimg || pos >= pimg->data_len) | 
 |     return 0; | 
 |   return pimg->data[pos]; | 
 | } | 
 |  | 
 | int | 
 | fimg_set_uchar_at (file_image *pimg, unsigned char val, size_t pos) | 
 | { | 
 |   return fimg_replace_at (pimg, &val, pos, 1); | 
 | } | 
 |  | 
 | unsigned short | 
 | fimg_get_ushort_at (const file_image *pimg, size_t pos, int big_endian) | 
 | { | 
 |   unsigned short r, r1; | 
 |   r = (unsigned short) fimg_get_uchar_at (pimg, pos); | 
 |   r1 = (unsigned short) fimg_get_uchar_at (pimg, pos + 1); | 
 |   if (big_endian) | 
 |     r <<= 8; | 
 |   else | 
 |     r1 <<= 8; | 
 |   return r | r1; | 
 | } | 
 |  | 
 | int | 
 | fimg_set_ushort_at (file_image *pimg, unsigned short val, size_t pos, int big_endian) | 
 | { | 
 |   int r = 1; | 
 |   if (big_endian) | 
 |     { | 
 |       r &= fimg_set_uchar_at (pimg, (unsigned char) ((val >> 8) & 0xff), pos); | 
 |       r &= fimg_set_uchar_at (pimg, (unsigned char) (val & 0xff), pos + 1); | 
 |     } | 
 |   else | 
 |     { | 
 |       r &= fimg_set_uchar_at (pimg, (unsigned char) ((val >> 8) & 0xff), pos + 1); | 
 |       r &= fimg_set_uchar_at (pimg, (unsigned char) (val & 0xff), pos); | 
 |     } | 
 |   return r; | 
 | } | 
 |  | 
 | unsigned int | 
 | fimg_get_uint_at (const file_image *pimg, size_t pos, int big_endian) | 
 | { | 
 |   unsigned int r1, r2; | 
 |   r1 = (unsigned int) fimg_get_ushort_at (pimg, pos, big_endian); | 
 |   r2 = (unsigned int) fimg_get_ushort_at (pimg, pos + 2, big_endian); | 
 |   if (big_endian) | 
 |     r1 <<= 16; | 
 |   else | 
 |     r2 <<= 16; | 
 |   return r1 | r2; | 
 | } | 
 |  | 
 | int | 
 | fimg_set_uint_at (file_image *pimg, unsigned int val, size_t pos, int big_endian) | 
 | { | 
 |   int r = 1; | 
 |   if (big_endian) | 
 |     { | 
 |       r &= fimg_set_ushort_at (pimg, (unsigned short) ((val >> 16) & 0xffff), pos, 1); | 
 |       r &= fimg_set_ushort_at (pimg, (unsigned short) (val & 0xffff), pos + 2, 1); | 
 |     } | 
 |   else | 
 |     { | 
 |       r &= fimg_set_ushort_at (pimg, (unsigned short) ((val >> 16) & 0xffff), pos + 2, 0); | 
 |       r &= fimg_set_ushort_at (pimg, (unsigned short) (val & 0xffff), pos, 0); | 
 |     } | 
 |   return r; | 
 | } | 
 |  | 
 | unsigned long long | 
 | fimg_get_uquad_at (const file_image *pimg, size_t pos, int big_endian) | 
 | { | 
 |   unsigned long long r1, r2; | 
 |  | 
 |   r1 = (unsigned long long) fimg_get_uint_at (pimg, pos, big_endian); | 
 |   r2 = (unsigned long long) fimg_get_uint_at (pimg, pos + 4, big_endian); | 
 |   if (big_endian) | 
 |     r1 <<= 32; | 
 |   else | 
 |     r2 <<= 32; | 
 |   return r1 | r2; | 
 | } | 
 |  | 
 | int fimg_set_uquad_at (file_image *pimg, unsigned long long val, size_t pos, int big_endian) | 
 | { | 
 |   int r = 1; | 
 |   if (big_endian) | 
 |     { | 
 |       r &= fimg_set_uint_at (pimg, (unsigned short) ((val >> 32) & 0xffffffffULL), pos, 1); | 
 |       r &= fimg_set_uint_at (pimg, (unsigned short) (val & 0xffffffffULL), pos + 4, 1); | 
 |     } | 
 |   else | 
 |     { | 
 |       r &= fimg_set_uint_at (pimg, (unsigned short) ((val >> 32) & 0xffffffffULL), pos + 4, 1); | 
 |       r &= fimg_set_uint_at (pimg, (unsigned short) (val & 0xffffffffULL), pos, 1); | 
 |     } | 
 |   return r; | 
 | } | 
 |  | 
 | void | 
 | fimg_show_stats (const file_image *pimg) | 
 | { | 
 |   fprintf (stderr, "fimg: %p", pimg); | 
 |   if (pimg) | 
 |     { | 
 |       fprintf (stderr, "{ data:%p, len:%#"PRIxPTR", name:\"%s\", mod:%s, want_save:%s }", | 
 |         pimg->data, pimg->data_len, (pimg->filename ? pimg->filename : "<unnamed>"), | 
 |         pimg->is_modified ? "yes" : "no", | 
 |         pimg->want_save ? "yes" : "no"); | 
 |     } | 
 |   fprintf (stderr, "\n"); | 
 | } | 
 |  | 
 | void | 
 | fimg_dump_mem (const file_image *pimg, size_t len, FILE *out) | 
 | { | 
 |   size_t off = 0, i; | 
 |   if (!pimg) | 
 |     return; | 
 |   while (off < len) | 
 |     { | 
 |       fprintf (out, "%#08"PRIXPTR":", off); | 
 |       for (i = 0; i < 16 && off < len; i++, off++) | 
 |         { | 
 | 	  fprintf (out," %02X", fimg_get_uchar_at (pimg, off)); | 
 | 	} | 
 |       fprintf (out,"\n"); | 
 |     } | 
 | } | 
 |  | 
 | #if 0 | 
 | int main (int argc,char **argv) | 
 | { | 
 |   file_image *p = fimg_create (); | 
 |   if (!fimg_load (p, argv[0])) | 
 |     { | 
 |       fprintf (stderr, "Failed to load %s\n", argv[0]); | 
 |       fimg_free (p); | 
 |       return 0; | 
 |     } | 
 |   fimg_show_stats (p); | 
 |   fimg_dump_mem (p, 32, stderr); | 
 |   fprintf (stderr, "Remove leading 4 bytes\n"); | 
 |   fimg_remove_at (p, 0, 4); | 
 |   fimg_show_stats (p); | 
 |   fimg_dump_mem (p, 32, stderr); | 
 |   fprintf (stderr, "Remove at pos 2 the next 10 bytes\n"); | 
 |   fimg_remove_at (p, 2, 10); | 
 |   fimg_show_stats (p); | 
 |   fimg_dump_mem (p, 32, stderr); | 
 |   fprintf (stderr, "Remove at end-pos the next 10 bytes\n"); | 
 |   fimg_remove_at (p, p->data_len, 10); | 
 |   fimg_show_stats (p); | 
 |   fimg_dump_mem (p, 32, stderr); | 
 |   fimg_free (p); | 
 |   return 1; | 
 | } | 
 | #endif |