| /* |
| 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 |