#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 *patch)
{
   int i;

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

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

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

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

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

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

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

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

         if(patch->installed && !patch->chunk[i].is_volatile)
            break;

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

   return 1;
}


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

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

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

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

   return 1;
}


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

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

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

