#include <memory.h>
#include "MusicHook.h"
// Addresses of Outpost2 code and data
DWORD outpost2ModuleBase = 0x00400000;	// Expected module base. Used for recalculating addresses
CRITICAL_SECTION *lpTimerCriticalSection = (CRITICAL_SECTION*)0x00565420;  // Critical section controlling access to the timer for the music buffer filling
DWORD hookAddress = 0x00450ED9;
DWORD hookReturn = 0x00450F28;
DWORD clmLoadAddress = 0x00450340;
DWORD managePlayListAddress = 0x00450DC0;
DWORD firstPlayAddress = 0x00450E63;
DWORD memAllocAddr = 0x004C0F40;
DWORD musicManagerAddr = 0x00565390;
const unsigned char hookedCodeBytes[] = {
	0xA1, 0x68, 0x54, 0x56, 0x00,  // MOV EAX, MusicManager.currentSongFileIndex
	0x8B, 0x0D, 0xAC, 0x53, 0x56, 0x00,	// MOV ECX, MusicManager.headerData*
	0xC1, 0xE0, 0x04,    	// SHL EAX, 4
	0x8B, 0x15, 0x6C, 0x54, 0x56, 0x00,	// MOV EDX, MusicManager.currentSongPosition
};
const unsigned char newCodeBytes[] = {
	0x8B, 0x4C, 0x24, 0x10,    // MOV ECX, [ESP+0x10] (AudioPtr1)
	0x68, 0x00, 0x80, 0x00, 0x00,  // PUSH 0x8000
	0x51,        // PUSH ECX (AudioPtr1)
	0x68,        // PUSH returnAddress
  0x00, 0x00, 0x00, 0x00,  	//  (returnAddress is written here)
	0xE9,        // JMP musicBufferFillingFunction
  0x00, 0x00, 0x00, 0x00,  	//  (musicBufferFillingFunction relative address is written here)
};
const unsigned char managePlayListBytes[] = {
	0xA1, 0x6C, 0x54, 0x56, 0x00  // MOV EAX, MusicManager.currentSongPosition
};
const unsigned char newPlayListSkip[] = {
	0xE9, 0xB9, 0x00, 0x00, 0x00,  // JMP FillMusicBuffer
};
const unsigned char firstPlayBytes[] = {
	0x8B, 0x14, 0x81,    	// MOV EDX, [ECX + EAX*4]
};
const unsigned char newFirstPlay[] = {
	0x33, 0xD2,      	// XOR EDX, EDX
	0x90        // NOP
};
const unsigned char clmLoadBytes[] = {
	0x81, 0xEC, 0x58, 0x01, 0x00, 0x00,	// SUB ESP, 0x158
};
const unsigned char newClmLoad[] = {
	0xB8, 0x01, 0x00, 0x00, 0x00,  // MOV EAX, 1
	0xC3,        // RETN
};
// **TODO** VERIFY THIS WORKS!
// **TODO** Find out if there is even much point? The hooked code contains memory addresses
//	that are being verified against anyways. :(
// Used to update all expected memory addresses of things in Outpost2.exe
void RebaseCode(DWORD newBaseAddress)
{
	// Calculate the offset between new and old addresses
	DWORD offset = newBaseAddress - outpost2ModuleBase;
	// Update current load address
	outpost2ModuleBase += offset;
	// Update the addresses
	*(char**)&lpTimerCriticalSection += offset;
	hookAddress += offset;
	hookReturn += offset;
	clmLoadAddress += offset;
	managePlayListAddress += offset;
	firstPlayAddress += offset;
	memAllocAddr += offset;
	musicManagerAddr += offset;
}
bool InitializeClmLoad()
{
	int headerDataAddr;
	// Allocate space for header data
	_asm
	{
  PUSH 0x4C
  CALL [memAllocAddr]
  MOV headerDataAddr, EAX
	}
	if (headerDataAddr == NULL)
  return false;
	// Fill in a fake CLM header
	memcpy((void*)headerDataAddr, "OP2 Clump File Version 1.0\0x1A\0x0\0\0\0\0", 32);
	WAVEFORMATEX *waveFormat = (WAVEFORMATEX*)(headerDataAddr + 0x32);
	waveFormat->wFormatTag = 1;
	waveFormat->nChannels = 1;
	waveFormat->nSamplesPerSec = 22050;
	waveFormat->nAvgBytesPerSec = 22050*2;
	waveFormat->nBlockAlign = 2;
	waveFormat->wBitsPerSample = 16;
	waveFormat->cbSize = 0;
	*(short*)(headerDataAddr+0x32) = 0;
	*(int*)(headerDataAddr+0x34) = 0x10000;
	*(int*)(headerDataAddr+0x38) = 0;  	// numPackedFiles
	// Fill in a fake index entry in the CLM header
	memset((void*)(headerDataAddr + 0x3C), 0, 8);
	*(int*)(headerDataAddr+0x44) = 0;
	*(int*)(headerDataAddr+0x48) = 0;
	// Fill in needed MusicManager fields
	*(int*)(musicManagerAddr + 0x1C) = headerDataAddr;	// char *headerData
	*(int*)(musicManagerAddr + 0x20) = -1;    // HANDLE hClmFile
	*(int*)(musicManagerAddr + 0x24) = 0x4C;  	// int totalFileHeaderSize
	// Fill in the song index table (point to only dummy index entry)
	int i;
	int *addr = (int*)(musicManagerAddr + 0x28);
	for (i = 0; i < 26; i++)
  addr[i] = 0;
	return true;
}
int InstallMusicHook(MusicBufferFillingFunc *musicPump)
{
	// Make sure the hooking location contains the correct code
	if (memcmp((void*)hookAddress, hookedCodeBytes, sizeof(hookedCodeBytes)) != 0)
  return 1;	// Failed. Unexpected instructions at hook point.
	// Make sure the clm loading location contains the correct code
	if (memcmp((void*)clmLoadAddress, clmLoadBytes, sizeof(clmLoadBytes)) != 0)
  return 2;	// Failed. Unexpected instructions at hook point.
	if (memcmp((void*)managePlayListAddress, managePlayListBytes, sizeof(managePlayListBytes)) != 0)
  return 3;	// Failed. Unexpected instructions at hook point.
	if (memcmp((void*)firstPlayAddress, firstPlayBytes, sizeof(firstPlayBytes)) != 0)
  return 4;	// Failed. Unexpected instructions at hook point.
	// Try to unprotect the code section
	DWORD oldAttributes;
	if (VirtualProtect((LPVOID)hookAddress, sizeof(hookedCodeBytes), PAGE_EXECUTE_READWRITE, &oldAttributes) == 0)
  return 5;  	// Could not unprotect pages. Abort
	if (VirtualProtect((LPVOID)clmLoadAddress, sizeof(clmLoadBytes), PAGE_EXECUTE_READWRITE, &oldAttributes) == 0)
  return 6;  	// Could not unprotect pages. Abort
	if (VirtualProtect((LPVOID)managePlayListAddress, sizeof(managePlayListBytes), PAGE_EXECUTE_READWRITE, &oldAttributes) == 0)
  return 7;  	// Could not unprotect pages. Abort
	if (VirtualProtect((LPVOID)firstPlayAddress, sizeof(firstPlayBytes), PAGE_EXECUTE_READWRITE, &oldAttributes) == 0)
  return 8;  	// Could not unprotect pages. Abort
	// Enter (timer) Critical Section
	// Note: The code we are overwriting is inside a critical section. By entering
	//	the critical section ourselves, we ensure the code is not executed while we
	//	are in the process of overwriting it.
	EnterCriticalSection(lpTimerCriticalSection);
	// Insert the hook
	// ***************
	memcpy((void*)hookAddress, newCodeBytes, sizeof(newCodeBytes));
	// Make sure the correct return address and function address are used
	*(DWORD*)&(((char*)hookAddress)[11]) = hookReturn;
	*(DWORD*)&(((char*)hookAddress)[16]) = (DWORD)musicPump - hookAddress - sizeof(newCodeBytes);
	// Patch up the CLM load
	memcpy((void*)clmLoadAddress, newClmLoad, sizeof(newClmLoad));
	memcpy((void*)managePlayListAddress, newPlayListSkip, sizeof(newPlayListSkip));
	memcpy((void*)firstPlayAddress, newFirstPlay, sizeof(newFirstPlay));
	// Initialized CLM data for safety (since load code is now skipped)
	InitializeClmLoad();
	// Leave (timer) Critical Section
	LeaveCriticalSection(lpTimerCriticalSection);
	// Return success
	return 0;
}
int UninstallMusicHook()
{
	// Check for an installed hook
	// Meh. Doesn't matter. Just overwriting with original code anyways
	// Enter (timer) Critical Section
	// Note: The code we are overwriting is inside a critical section. By entering
	//	the critical section ourselves, we ensure the code is not executed while we
	//	are in the process of overwriting it.
	EnterCriticalSection(lpTimerCriticalSection);
	// Remove the hook
	memcpy((void*)hookAddress, hookedCodeBytes, sizeof(hookedCodeBytes));
	memcpy((void*)clmLoadAddress, clmLoadBytes, sizeof(clmLoadBytes));
	memcpy((void*)managePlayListAddress, managePlayListBytes, sizeof(managePlayListBytes));
	memcpy((void*)firstPlayAddress, firstPlayBytes, sizeof(firstPlayBytes));
	// Leave (timer) Critical Section
	LeaveCriticalSection(lpTimerCriticalSection);
	// Return success
	return 0;
}#include <dsound.h>
// Define a typedef for the callback function
typedef int __stdcall MusicBufferFillingFunc(char *musicBuffer, int bufferSize);
void RebaseCode(DWORD newBaseAddress);
int InstallMusicHook(MusicBufferFillingFunc *musicPump);
int UninstallMusicHook();BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
	if (ul_reason_for_call == DLL_PROCESS_ATTACH) 
	{
  DisableThreadLibraryCalls((HMODULE)hModule);
  InstallMusicHook(&FillMusicBuffer);
	}
	if (ul_reason_for_call == DLL_PROCESS_DETACH)
	{
  UninstallMusicHook();
	}
    return TRUE;
}#include "MusicHook.h"
int __stdcall FillMusicBuffer(char *musicBuffer, int bufferSize);#include "MusicHook.h"
#include "AudFileReader.h"
#include "AudDecoder.h"
// Note: This function fills the music buffer
//	with 0x8000 bytes worth of PCM data
// Note: Data format is 16 bit signed, mono, at 22050 Hz
// Return: Nonzero on success, zero on error. If an error occurs, 
//	the music buffer is cleared to zero.
int __stdcall FillMusicBuffer(char *musicBuffer, int bufferSize)
{
	DWORD numDecoded1;
	DWORD numAudBytesRead;
	static AudFileReader audReader;
	static AudDecoder audDecoder;
	static bool playNewAudFile = true;
	const DWORD audBufferSize = 0x2000;
	char audBuffer[audBufferSize];
	// Check if a new Aud file needs to be decoded
	if (playNewAudFile == true)
	{
  // **TODO** Make this work in the right way
  audReader.Open("ARAKATAK.AUD");
  audDecoder = AudDecoder();
  playNewAudFile = false;
	}
	// Read a chunk of Aud data
	numAudBytesRead = audReader.ReadAudData(audBuffer, audBufferSize);
	// Fill sound buffer
	// *****************
	numDecoded1 = audDecoder.Decode(musicBuffer, bufferSize, audBuffer, numAudBytesRead);
	memset(musicBuffer + numDecoded1, 0, bufferSize - numDecoded1);	// Zero fill if short on data
	// Return success
	return 1;
}#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <fstream.h>
class AudFileReader
{
	public:
  AudFileReader();
  ~AudFileReader();
  int Open(char *fileName);        // Opens file and reads in the header
  int ReadAudData(char *buffer, unsigned int bufferSize);	// Returns a chunk of AUD data
  // Wave format info
  int GetFrequency();  	
  int GetOutputSize();
  int GetCompressionType();
  int GetBitsPerSample();
  int GetNumChannels();
	private:
  // Private data types
  #pragma pack(push, 1)
  // AUD File header
  struct AudHeader
  {
  	short samplesPerSec;	// Frequency
  	int fileSize;  	// Size of file (without header)
  	int outputSize;  	// Size of output data
  	char flags;    // Bit 0 = Stereo, Bit 1 = 16 bit
  	char type;    // 1 = WW compressed, 99 = IMA ADPCM
  };
  // Chunk header
  struct ChunkHeader
  {
  	short size;
  	short outputSize;
  	int id;
  };
  #pragma pack(pop)
  enum AudFlags
  {
  	FlagStereo = 1,
  	Flag16Bit = 2,
  };
  // Private functions
  int ReadChunk(char *buffer, int bufferSize);
  // Class variables
  //ifstream audFile;
  HANDLE hFile;
  AudHeader audHeader;
  ChunkHeader currentChunkHeader;
  int currentChunkOffset;
};#include "AudFileReader.h"
AudFileReader::AudFileReader()
{
	hFile = INVALID_HANDLE_VALUE;
	currentChunkOffset = 0;
	memset(&audHeader, 0, sizeof(audHeader));
	memset(¤tChunkHeader, 0, sizeof(currentChunkHeader));
}
AudFileReader::~AudFileReader()
{
	// Close the file if it is open
	if (hFile != INVALID_HANDLE_VALUE)
  CloseHandle(hFile);
}
// Opens an AUD file and reads in the file header
// Return: 0 on success, nonzero on failure.
int AudFileReader::Open(char *fileName)
{
	DWORD numBytesRead;
	// Try to open the file
	hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	// Check for errors
	if (hFile == INVALID_HANDLE_VALUE)
	{
  // Error opening the file
  return 1;
	}
	// Read the file header
	ReadFile(hFile, &audHeader, sizeof(audHeader), &numBytesRead, NULL);
	// Check for errors
	if (numBytesRead != sizeof(audHeader))
	{
  // Error reading AUD header
  CloseHandle(hFile);
  return 2;
	}
	// Return success
	return 0;
}
// Fills the passed in buffer with AUD data, up to the maximum size of the buffer.
// Return: The number of bytes actually written to the buffer
int AudFileReader::ReadAudData(char *buffer, unsigned int bufferSize)
{
	DWORD numBytesRead;
	DWORD numBytesWritten = 0;
	DWORD readSize;
	// Try to fill the buffer with AUD data
	while(bufferSize > numBytesWritten)
	{
  // Check if the current chunk has no more data left
  if (currentChunkHeader.size <= currentChunkOffset)
  {
  	// We need to read in a new chunk
  	ReadFile(hFile, ¤tChunkHeader, sizeof(currentChunkHeader), &numBytesRead, NULL);
  	// Check for errors
  	if (numBytesRead != sizeof(currentChunkHeader))
  	{
    // Error reading new chunk header. Abort.
    return numBytesWritten;
  	}
  	// Reset current chunk offset
  	currentChunkOffset = 0;
  }
  // Determine how many bytes to read from the current chunk
  readSize = currentChunkHeader.size - currentChunkOffset;
  if (readSize > bufferSize - numBytesWritten)
  	readSize = bufferSize - numBytesWritten;
  // Read the chunk data
  ReadFile(hFile, &buffer[numBytesWritten], readSize, &numBytesRead, NULL);
  // Adjust the counters
  numBytesWritten += readSize;
  currentChunkOffset += readSize;
  // Check for errors
  if (numBytesRead != readSize)
  {
  	// Error reading chunk data. Abort.
  	return numBytesWritten;
  }
	}
	// Return the number of bytes written
	return numBytesWritten;
}
int AudFileReader::GetFrequency()
{
	return audHeader.samplesPerSec;
}
int AudFileReader::GetOutputSize()
{
	return audHeader.outputSize;
}
int AudFileReader::GetCompressionType()
{
	return audHeader.type;
}
int AudFileReader::GetBitsPerSample()
{
	if ((audHeader.flags & Flag16Bit) == Flag16Bit)
  return 16;
	else
  return 8;
}
int AudFileReader::GetNumChannels()
{
	if ((audHeader.flags & FlagStereo) == FlagStereo)
  return 2;
	else
  return 1;
}class AudDecoder
{
	public:
  AudDecoder();
  int Decode(void *pcmBuffer, int pcmBufferSize, char *audBuffer, int audBufferSize);
	private:
  // Audio decoding state
  int index;
  int curSample;
  // Private static constant tables used during decompression
  static const int indexAdjust[];
  static const int stepTable[];
};#include "AudDecoder.h"
// Define the static tables used during decompression
const int AudDecoder::indexAdjust[] = {-1,-1,-1,-1,2,4,6,8};
const int AudDecoder::stepTable[] = {
	7,     8,     9,     10,    11,    12,     13,    14,    16,
	17,    19,    21,    23,    25,    28,     31,    34,    37,
	41,    45,    50,    55,    60,    66,     73,    80,    88,
	97,    107,   118,   130,   143,   157,    173,   190,   209,
	230,   253,   279,   307,   337,   371,    408,   449,   494,
	544,   598,   658,   724,   796,   876,    963,   1060,  1166,
	1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,
	3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,
	7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,
	16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 
};
// Constructor
AudDecoder::AudDecoder()
{
	// Initialize the decoding state
	index = 0;
	curSample = 0;
}
// Decompresses the data from the audBuffer into the pcmBuffer
// Return value: The number of bytes written to the pcm buffer.
// Parameters:
//	pcmBuffer - a pointer to the buffer to fill with signed 16 bit PCM samples
//	pcmBufferSize - size of the pcmBuffer in bytes. (not the size in shorts)
//	audBuffer - a pointer to the input aud data (consisting of 4 bit samples)
//	audBufferSize - size of the audBuffer in bytes. (not the number of samples)
// Note: The aud data is in 4 bit samples, and the pcm data is 16 bit signed samples
// Note: Data is assumed to be mono
int AudDecoder::Decode(void *pcmBuffer, int pcmBufferSize, char *audBuffer, int audBufferSize)
{
	int numSamples;
	int i;
	int code;
	bool signBit;
	int delta;
	// Determine maximum amount of data that can be processed
	if (pcmBufferSize/2 >= audBufferSize*2)
  numSamples = audBufferSize*2;  	// Number of samples is implicity even
	else
  numSamples = (pcmBufferSize/2) & ~1;	// Round down to even number of samples
	// Decode the chunk of data
	for (i = 0; i < numSamples; i++)
	{
  // Get the current code (full byte)
  code = audBuffer[i/2];
  // Adjust for correct nibble
  if ((i & 1) == 1)
  	code >>= 4;
  // Get the sign of the nibble
  signBit = ((code & 8) == 8);
  // Truncate code to remaining bits
  code &= 7;
  // Calculate delta. Last part minimizes errors
  delta =(stepTable[index]*code)/4 + stepTable[index]/8;
  // Adjust for sign
  if (signBit)
  	delta = -delta;
  // Calculate the new sample
  curSample += delta;
  if (curSample < -32768) curSample = -32768;
  else if (curSample > 32767) curSample = 32767;
  // Write out the current sample
  ((short*)pcmBuffer)[i] = curSample;
  // Adjust the index
  index += indexAdjust[code];
  if (index < 0) index = 0;
  else if (index > 88) index = 88;
	}
	// Return the number of bytes written
	return numSamples*2;
}