////////////////////////////////////////////////////////////////////////////////
// Freezer.cpp -- this file is part of the Emulator Developers Kit(EDK)
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm
//
// Saves a memory snapshot when the 6510 executes a given address.
// Useful for re-cracking games with packed headers.

#include <EDK.h>
#include "resource.h"


// delete this when the C64 class has been included in the EDK
#define C64 CC64
class C64 : public Chip {
public:
  global void MapROM8000(byte* pbROM);
  global void UnmapROM8000();
  global void MapROMA000(byte* pbROM);
  global void UnmapROMA000();
  global byte* GetBasic();
  global byte* GetKernal();
  global byte* GetCharset();
  global byte* GetIO();
  global byte* GetRAM();
};


class Freezer : public Object {

  C64* pC64;
  CPU6510* pCPU;
  byte* pbRAM;
  int iAddress;
  char acFileName[_MAX_PATH];
  byte bTrap;

  byte SaveSnapshot();
  friend BOOL CALLBACK FreezerDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

public:

  // constructor
  Freezer() {
    pC64 = NULL;
    pCPU = NULL;
    pbRAM = NULL;
    iAddress = 0;
    memset(acFileName, 0, sizeof acFileName);
    bTrap = 0;
  }

  // destructor
  virtual ~Freezer() {
    if (bTrap != 0) {
      pCPU->ClearTrap(pbRAM + iAddress);
      pCPU->FreeTrap(bTrap);
      bTrap = 0;
    }
  }

  // initialisation
  void Init(HINSTANCE hinst);

};


// dialog function to read address and file name
BOOL CALLBACK FreezerDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  static Freezer* p;
  switch (uMsg) {
  case WM_INITDIALOG:
    p = (Freezer*)lParam;
    CenterWindow(hwnd);
    SetDlgItemHex(hwnd, IDC_Address, p->iAddress, 4);
    SetDlgItemText(hwnd, IDC_FileName, p->acFileName);
    return TRUE;
  case WM_COMMAND:
    switch (LOWORD(wParam)) {
    case IDOK:
      p->iAddress = GetDlgItemHex(hwnd, IDC_Address);
      GetDlgItemText(hwnd, IDC_FileName, p->acFileName, sizeof p->acFileName);
    case IDCANCEL:
      EndDialog(hwnd, LOWORD(wParam));
    }
    return TRUE;
  }
  return FALSE;
}


// initialisation
void Freezer::Init(HINSTANCE hinst) {
  pC64 = (C64*)gObjectRoot.FindChild("C64");
  pCPU = (CPU6510*)pC64->FindChild("CPU");
  pbRAM = pC64->GetRAM();
  iAddress = gconf.GetInt("Extensions\\Freezer", "Address", 0);
  gconf.GetString("Extensions\\Freezer", "FileName", acFileName, sizeof acFileName, GetProgramDirectory("Snapshot.mem"));
  if (DialogBoxParam(hinst, MAKEINTRESOURCE(IDD_Freezer), ghwnd, FreezerDlgProc, (LPARAM)this) == IDOK) {
    bTrap = pCPU->AllocCodeTrap(this, (bpfn)SaveSnapshot);
    pCPU->SetTrap(pbRAM + iAddress, bTrap);
  }
}


// dll entry
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID) {
  static Freezer* p;
  try {
    switch (dwReason) {
    case DLL_PROCESS_ATTACH:
      assert(p == NULL);
      p = new Freezer;
      p->Init(hinst);
      break;
    case DLL_PROCESS_DETACH:
      assert(p != NULL);
      delete p;
      p = NULL;
      break;
    }
    return TRUE;
  } catch (...) {
    report();
    return FALSE;
  }
}


// here is the action
byte Freezer::SaveSnapshot() {

  // write the memory snapshot
  trace("Freezer.dll is saving snapshot at %04X to %s", iAddress, acFileName);
  File out(acFileName, CREATE_ALWAYS);
  out.Write(pbRAM, 65536);

  // disable the trap
  pCPU->ClearTrap(pbRAM + iAddress);
  pCPU->FreeTrap(bTrap);
  bTrap = 0;

  // fetch the byte for the CPU
  return pbRAM[iAddress];

}
