#include "main.h"


static char *dumb_hex_dump(void *data, uint32_t len)
{
   static char buf[1024];
   int i;

   if(len > 500)
      len = 500;

   for(i=0; i<(int)len; i++)
      snprintf(buf + i * 2, sizeof(buf) - i * 2, "%02X", *((uint8_t *)data + i));

   return buf;
}


int patcher_install(struct patch_set *set)
{
   int i;

   /* previous initialization failed; always return error. */
   if(!set->initialized && set->failed)
      return 0;

   if(!set->initialized)
   {
      set->failed = 1;

      for(i=0; i<PATCHER_CHUNKS_MAX; i++)
      {
         if(set->chunk[i].ptr == NULL)
            break;

         if(set->chunk[i].data_cmp != NULL &&
            !memcmp_safe((uint8_t *)set->chunk[i].ptr, set->chunk[i].data_cmp, set->chunk[i].len))
         {
            void *mem = malloc(set->chunk[i].len);

            log_debug("Data mismatch for patch '%s'.", set->name);
            if(mem != NULL)
            {
               if(memcpy_safe(mem, set->chunk[i].ptr, set->chunk[i].len))
                  log_debug("Data @ 0x%p is: %s", set->chunk[i].ptr, dumb_hex_dump(mem, set->chunk[i].len));
               log_debug("Should be: %s", dumb_hex_dump(set->chunk[i].data_cmp, set->chunk[i].len));
               free(mem);
            }
            return 0;
         }

         if(set->chunk[i].data_orig == NULL)
            set->chunk[i].data_orig = (uint8_t *)malloc(set->chunk[i].len);
         if(set->chunk[i].data_orig == NULL)
            continue;

         if(!memcpy_safe(set->chunk[i].data_orig, set->chunk[i].ptr, set->chunk[i].len))
            log_debug("Failed to copy original data for patch '%s' chunk %d", set->name, i);
      }
      set->initialized = 1;
      set->failed = 0;
      log_debug("Initialized patch '%s'.", set->name);
   }

   if(!set->installed)
   {
      for(i=0; i<PATCHER_CHUNKS_MAX; i++)
      {
         if(set->chunk[i].ptr == NULL)
            break;

         if(set->chunk[i].data_rep == NULL)
         {
            if(!memset_safe((uint8_t *)set->chunk[i].ptr, 0x90, set->chunk[i].len))
               log_debug("Failed to install patch '%s' chunk %d", set->name, i);
         }
         else
         {
            if(!memcpy_safe((uint8_t *)set->chunk[i].ptr, set->chunk[i].data_rep, set->chunk[i].len))
               log_debug("Failed to install patch '%s' chunk %d", set->name, i);
         }
      }
      set->installed = 1;
      log_debug("Installed patch '%s'.", set->name);
   }

   return 1;
}


int patcher_remove(struct patch_set *set)
{
   int i;

   if(set->failed)
   {
      set->failed = 0;
      return 1;
   }

   if(set->installed)
   {
      for(i=0; i<PATCHER_CHUNKS_MAX; i++)
      {
         if(set->chunk[i].ptr == NULL)
            break;

         if(!memcpy_safe((uint8_t *)set->chunk[i].ptr, set->chunk[i].data_orig, set->chunk[i].len))
            log_debug("Failed to restore patch '%s' chunk %d", set->name, i);
      }
      set->installed = 0;
      log_debug("Removed patch '%s'.", set->name);
   }

   return 1;
}


void patcher_free(struct patch_set *set)
{
   int i;

   for(i=0; i<PATCHER_CHUNKS_MAX; i++)
   {
      if(set->chunk[i].ptr == NULL)
         break;

      if(set->chunk[i].data_orig != NULL)
         free(set->chunk[i].data_orig);
   }
}

