////////////////////////////////////////////////////////////////////////////////
// Win32/Toolbox.h -- this file is part of the Emulator Developers Kit
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm
//
// The Toolbox contains classes and routines for use with Win32 projects.
//
// In order to use the toolbox with other projects:
// - add the directory of Toolbox.h to the compiler's include path
// - add the file Toolbox.cpp to your project
// - let every source file start with #include <Toolbox.h>
// - set the precompiled headers to Toolbox.h

#ifdef _MSC_VER // Visual C++
  #pragma warning(disable: 4156 4201 4284 4514 4702 4786)
#endif


////////////////////////////////////////////////////////////////////////////////
// common header files for Windows

// MSVC42 bug: you must delete /D "_MBCS" by hand after switching the project
// settings to "Not Using MFC"
#ifdef _MBCS
  #include <afxwin.h>
#else
  #define STRICT
  #define NOMINMAX
  #include <windows.h> 
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <io.h>
#include <conio.h>
#include <winioctl.h>
#include <typeinfo.h>
#include <limits.h>
#include <richedit.h>

// mmsystem.h requires to link with WinMM.lib
#include <mmsystem.h>
#ifdef _MSC_VER
  #pragma comment(lib, "WinMM.lib")
#endif

// STL
#if _MSC_VER >= 1100
  using namespace std;
#endif
#include <memory>
#include <string>


////////////////////////////////////////////////////////////////////////////////
// define global linkage for the toolbox components
// use /D "ExportToolbox" and /D "ImportToolbox" if the code resides in a DLL

#if defined(Toolbox_cpp) && defined(ExportToolbox)
  #define global __declspec(dllexport)
#elif defined(ImportToolbox)
  #define global __declspec(dllimport)
#else
  #define global
#endif


////////////////////////////////////////////////////////////////////////////////
// Hungarian notation for the basic data types:
//
// g  global variable
// a  array
// aa two dimensional array
// p  pointer
// pp pointer to pointer
// c  char, 8 bit signed
// s  short (int), 16 bit signed
// i  int, 16 or 32 bit signed
// l  long (int), 32 bit signed
// b  bool, 8 bit, true or false
// b  byte, 8 bit unsigned
// w  word, 16 bit unsigned
// u  unsigned (int), 16 or 32 bit unsigned
// dw dword, 32 bit unsigned
// qw qword, 64 bit unsigned
// f  float, 32 bit floating point (flag has been replaced by bool)
// d  double, 64 bit floating point
// ld long double, 80 bit floating point
// h  handle, 16 or 32 bit
//
// enums, strings and structures either don't have a prefix or they use the
// convention in the Windows help files (example: WIN32_FIND_DATA -> wfd)

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;
typedef unsigned __int64 qword;

// Visual C++ 4.x doesn't know the bool type
#if _MSC_VER < 1100
  #define bool byte
  #define false 0
  #define true 1
#endif


////////////////////////////////////////////////////////////////////////////////
// helper functions

inline int min(int i1, int i2) {
  if (i1 <= i2) {
    return i1;
  } else {
    return i2;
  }
}

inline int max(int i1, int i2) {
  if (i1 >= i2) {
    return i1;
  } else {
    return i2;
  }
}

inline int min(int i1, int i2, int i3) {
  return min(min(i1, i2), i3);
}

inline int max(int i1, int i2, int i3) {
  return max(max(i1, i2), i3);
}

inline int bound(int iValue, int iLower, int iUpper) {
  if (iValue < iLower) {
    return iLower;
  } else if (iValue > iUpper) {
    return iUpper;
  } else {
    return iValue;
  }
}

#define countof(x) (sizeof (x) / sizeof ((x)[0]))

inline char* strend(const char* pc) {
  return (char*)pc + strlen(pc);
}

inline void swap1(void* p1, void* p2) {
  byte bSaved1 = *(byte*)p1;
  *(byte*)p1 = *(byte*)p2;
  *(byte*)p2 = bSaved1;
}

inline void swap2(void* p1, void* p2) {
  word wSaved1 = *(word*)p1;
  *(word*)p1 = *(word*)p2;
  *(word*)p2 = wSaved1;
}

inline void swap4(void* p1, void* p2) {
  dword dwSaved1 = *(dword*)p1;
  *(dword*)p1 = *(dword*)p2;
  *(dword*)p2 = dwSaved1;
}

global void swap(void* p1, void* p2, int iSize);


////////////////////////////////////////////////////////////////////////////////
// cycle exact timing routines for the Pentium w/o MMX under Windows 95 and NT

#define _TimerCount 1000

#define rdtsc __asm __emit(0x0F) __asm __emit(0x31)

#define _B __asm { \
  __asm push EAX \
  __asm mov EAX,_TimerStart /* pre-fetch cache line */ \
  __asm push EDX \
  __asm rdtsc \
  __asm nop \
  __asm mov _TimerStart,EAX \
  __asm pop EDX \
  __asm pop EAX \
  __asm nop /* prevents AGI stall EAX/EDX/ESP */ \
  __asm nop };

#define _E __asm { \
  __asm nop /* prevents AGI stall ESP */ \
  __asm nop \
  __asm push EAX \
  __asm push EDX \
  __asm nop /* don't decode rdtsc 0F prefix in shadows */ \
  __asm nop \
  __asm rdtsc \
  __asm pushf \
  __asm mov EDX,_TimerIndex \
  __asm cmp EDX,_TimerCount /* stop counter after last index */ \
  __asm mov _TimerEnd[EDX*4],EAX \
  __asm mov EAX,_TimerStart \
  __asm adc _TimerIndex,0 \
  __asm mov _TimerBeg[EDX*4],EAX \
  __asm popf \
  __asm pop EDX \
  __asm pop EAX };

static int _TimerBeg[_TimerCount + 1];
static int _TimerEnd[_TimerCount + 1];
static int _TimerIndex;
static int _TimerStart;

static struct _TimerAtExit {
  ~_TimerAtExit() {
    char ac[1024];
    char* pc = ac;
    int iMin = 0x7FFFFFFF;
    double rSum = 0;
    double rUsed = 0;
    double rTotal = 0;
    for (int i = 0; i < _TimerCount; i++) {
      int iClocks = ((_TimerEnd[i] - _TimerBeg[i]) & 0x7FFFFFFF) - 19;
      if (iClocks < 0) {
        break;
      }
      if (pc < ac + sizeof ac - 16) {
        pc += wsprintf(pc, "  %d", iClocks);
      }
      if (iClocks < iMin) {
        iMin = iClocks;
      }
      rSum += iClocks;
      if (i > 0) {
        rUsed += iClocks;
        rTotal += ((_TimerBeg[i] - _TimerBeg[i - 1]) & 0x7FFFFFFF) - 59;
      }
    }
    if (i > 0) {
      strcpy(pc, "\n");
      OutputDebugString(ac);
      if (i > 1) {
        double rAvg = rSum / i;
        double rDev = 0;
        if (iMin != 0) {
          rDev = (rAvg - iMin) * 100.0 / iMin;
        }
        double rPercent = rUsed * 100.0 / rTotal;
        sprintf(ac, "  %.6g clocks avg (%d clocks minimum + %.3g%% deviation) consuming %.4g%% of CPU performance\n", rAvg, iMin, rDev, rPercent);
        OutputDebugString(ac);
      }
    }
  }
} _TimerAtExit;


////////////////////////////////////////////////////////////////////////////////
// error handling

#ifdef _DEBUG
  #define _DebugBreak \
    { \
      __asm int 3 \
    }
#else
  #define _DebugBreak
#endif

#define error \
    _DebugBreak \
    _Error

#ifdef _DEBUG
  #define assert(_Statement_) \
    if (!(_Statement_)) { \
      error("assert(%s) failed in %s at line %d", #_Statement_, GetFileName(__FILE__), __LINE__); \
    }
#else
  #define assert(_Statement_)
#endif

#define verify(_Statement_) \
  if (!(_Statement_)) { \
    error("verify(%s) failed in %s at line %d", #_Statement_, GetFileName(__FILE__), __LINE__); \
  }

#define win(_Statement_) \
  if ((int)(_Statement_) == 0) { \
    _DebugBreak \
    _WinError(#_Statement_, __FILE__, __LINE__); \
  }

#define reg(_Statement_) \
  { \
    int _iError_ = (int)(_Statement_); \
    if (_iError_ != 0) { \
      _DebugBreak \
      SetLastError(_iError_); \
      _WinError(#_Statement_, __FILE__, __LINE__); \
    } \
  }

#define wavein(_Statement_) \
  { \
    MMRESULT _mmrError_ = (_Statement_); \
    if (_mmrError_ != MMSYSERR_NOERROR) { \
      _DebugBreak \
      _WaveError(#_Statement_, __FILE__, __LINE__, _mmrError_, waveInGetErrorText); \
    } \
  }

#define waveout(_Statement_) \
  { \
    MMRESULT _mmrError_ = (_Statement_); \
    if (_mmrError_ != MMSYSERR_NOERROR) { \
      _DebugBreak \
      _WaveError(#_Statement_, __FILE__, __LINE__, _mmrError_, waveOutGetErrorText); \
    } \
  }

global HWND GetLogEditWindow();
global void __cdecl more(const char* pcFormat, ...);
global void __cdecl info(const char* pcFormat, ...);
global void __cdecl trace(const char* pcFormat, ...);
global void __cdecl user(const char* pcFormat, ...);
global void __cdecl warning(const char* pcFormat, ...);
global void __cdecl _Error(const char* pcFormat, ...);
global void report();
global char* WinErrorString(DWORD dwError);
global void _WinError(const char* pcStatement, const char* pcFile, int iLine);
global void _WaveError(const char* pcStatement, const char* pcFile, int iLine, MMRESULT mmrError, MMRESULT (WINAPI*)(MMRESULT, LPSTR, UINT));


////////////////////////////////////////////////////////////////////////////////
// check platform

#if defined(Toolbox_cpp)
  OSVERSIONINFO gosvi;
  #if defined(ExportToolbox)
    __declspec(dllexport) OSVERSIONINFO* gposvi = &gosvi;
  #endif
#elif defined(ImportToolbox)
  __declspec(dllimport) OSVERSIONINFO* gposvi;
  static OSVERSIONINFO& gosvi = *gposvi;
#else
  extern OSVERSIONINFO gosvi;
#endif

global void _InitGOSVI();

inline bool IsWin32s() {
  if (gosvi.dwPlatformId == 0) {
    _InitGOSVI();
  }
  return gosvi.dwPlatformId == VER_PLATFORM_WIN32s;
}

inline bool IsWindows95() {
  if (gosvi.dwPlatformId == 0) {
    _InitGOSVI();
  }
  return gosvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;
}

inline bool IsWindowsNT() {
  if (gosvi.dwPlatformId == 0) {
    _InitGOSVI();
  }
  return gosvi.dwPlatformId == VER_PLATFORM_WIN32_NT;
}


////////////////////////////////////////////////////////////////////////////////
// direct port access

global int GetLPTPort(int iLPT);
global int inp2(int iPort);
global void outp(int iPort, int iValue);


////////////////////////////////////////////////////////////////////////////////
// memory functions

global byte* MemAlloc(size_t Size);
global void MemFree(void* pMem);


////////////////////////////////////////////////////////////////////////////////
// path functions

global const char* GetProgramDirectory(const char* pcFileName = NULL);
global void AddPath(char* pcDest, const char* pcSrc);
global char* GetFileName(const char* pcPath);
inline unsigned GetDriveType(char cDrive) {
  char ac[4] = { cDrive, ':', '\\', 0 };
  return GetDriveType(ac);
}
global double GetDiskFreeSpace(char cDrive);


////////////////////////////////////////////////////////////////////////////////
// window functions

#if defined(Toolbox_cpp)
  HINSTANCE ghinst;
  #if defined(ExportToolbox)
    __declspec(dllexport) HINSTANCE* gphinst = &ghinst;
  #endif
#elif defined(ImportToolbox)
  __declspec(dllimport) HINSTANCE* gphinst;
  static HINSTANCE& ghinst = *gphinst;
#else
  extern HINSTANCE ghinst;
#endif

#if defined(Toolbox_cpp)
  HWND ghwnd;
  #if defined(ExportToolbox)
    __declspec(dllexport) HWND* gphwnd = &ghwnd;
  #endif
#elif defined(ImportToolbox)
  __declspec(dllimport) HWND* gphwnd;
  static HWND& ghwnd = *gphwnd;
#else
  extern HWND ghwnd;
#endif

global void CenterWindow(HWND hwnd);
global void SetDlgItemHex(HWND hwnd, int iItem, long lValue, int iLength);
global long GetDlgItemHex(HWND hwnd, int iItem);
global bool Input(const char* pcPrompt, char* pcBuffer, int iSize);


////////////////////////////////////////////////////////////////////////////////
// include the component headers which reside in separate files

#include "File.h"
#include "MappedFile.h"
#include "Config.h"


////////////////////////////////////////////////////////////////////////////////
// prevent unintentional use of the 'global' keyword in other files

#undef global
