#include <windows.h>
#include <stdio.h>

// shared memory block size
static int memSize = 65535;
// shared memory listeners offset
static int listenersOffset = 40976;
// pointer to the shared memory
byte* buffer;

// localconnection listener name (always localhost:<name>)
const char connectionName[256] = "localhost:";
const char protocolName[] = "localhost";
const char methodName[] = "testMethod";
const char dummyMessage[] = "Flash should receive this!";
// currently listening
int listening = 0;

/**
 * Check if recipient is listening
 * @return free offset to add listener, or -1 if already registered
 */
static int checkListener(void)
{
	int i = listenersOffset;
	int alreadyListening = 0;
	while ( (i<memSize) && buffer[i] ) {
		if (!alreadyListening && (strcmp(connectionName, (char*)&buffer[i]) == 0)) {
			alreadyListening = 1;
			printf("%4Xh: %s (myself)\n", i, (char*)&buffer[i]);
		}
		else printf("%4Xh: %s\n", i, (char*)&buffer[i]);
		i += strlen((char*)&buffer[i])+1;
	}
	return (alreadyListening) ? -1 : i;
}

/**
 * Shared memory hex view
 * @param offset	Display memory from this offset
 * @param size	Display <size> bytes of data
 */
static void dumpMemory(int offset, int size)
{
	int i = 0;
	int c = 0;
	byte b;
	while (i<size) {
		while ( (c < 16) && (i+c < size) ) {
			b = buffer[offset+i+c];
			printf("%X%X ", b/16, b & 0x0f );
			c++;
		}
		while (c++ < 16) printf("   ");
		c = 0;
		while ( (c < 16) && (i+c < size) ) {
			b = buffer[offset+i+c];
			if (b > 31) printf("%c", (char)b );
			else printf(".");
			c++;
		}
		i += 16;
		c = 0;
		printf("\n");
	}
}

/**
 * Write an AMF string in the buffer
 * @param str	text to write
 * @param pos	position in buffer
 * @return updated position in buffer
 */
static int writeAMFString(const char * str, int pos)
{
	int len = strlen(str);
	buffer[pos++] = 0x02;
	buffer[pos++] = 0; // string length is badly encoded here!
	buffer[pos++] = (byte)(len & 0xff);
	strcpy((char*)&buffer[pos], str);
	pos += len;
	return pos;
}

/**
 * Write message in memory file
 * @param msg	the text to send
 */
static int writeMessage(const char * msg)
{
	DWORD *timestamp = (DWORD*)&buffer[8];
	DWORD *size = (DWORD*)&buffer[12];
	int pos = 16;
	// check timestamp and size
	if (*size) return 0;
	*timestamp = GetTickCount();
	// write connection name
	pos = writeAMFString(connectionName, pos);
	// write protocol
	pos = writeAMFString(protocolName, pos);
	// write method name
	pos = writeAMFString(methodName, pos);
	// write message
	pos = writeAMFString(msg, pos);
	*size = pos-16;
	// report
	printf("Written: timestamp=%d, size=%d\n", *timestamp, *size);
	// dump memory
	dumpMemory(0, *size+16);
	return 1;
}

/**
 * check the memory file
 */
static int pollMemory(void)
{
	HANDLE hMutex;
	HANDLE hMapFile;
	LPVOID lpMapAddress;
	DWORD dwWaitResult;
	int result = 0;

	// file access mutex
	hMutex = OpenMutex(
		MUTEX_ALL_ACCESS, // request full access
		FALSE, // handle not inheritable
		"MacromediaMutexOmega"); // object name

	// wait for mutex
	dwWaitResult = WaitForSingleObject(
		hMutex, // handle to mutex
		5000L); // five-second time-out interval

	switch (dwWaitResult) {
		// The thread got mutex ownership.
		case WAIT_OBJECT_0:
			__try {
				// open file mapping
				hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, // read/write permission
					FALSE, // do not inherit the name
					"MacromediaFMOmega"); // mapping object name

				// get memory file pointer
				lpMapAddress = MapViewOfFile(hMapFile, // handle to mapping object
					FILE_MAP_ALL_ACCESS, // read/write permission
					0, // max. object size
					0, // size of hFile
					0); // map entire file

				// lpMapAddress points to a struct like this:
				// 01 00 00 00 01 00 00 00 EC 00 00 00 3E 00  ..............
				// 00 00 02 00 0e 6c 6f 63 61 6c 68 6f 73 74  .....localhost
				// 3a 74 65 73 74 02 00 09 6c 6f 63 61 6c 68  :test...localh
				// 6f 73 74 01 00 01 00 02 00 0f 6d 65 74 68  ost.......meth
				// 6f 64 54 6f 45 78 65 63 75 74 65 02 00 08  odToExecute...
				// 61 61 61 61 61 61 61 61 .. .. .. .. .. ..  aaaaaaaa......

				// read memory
				if (lpMapAddress != NULL) {
					buffer = (byte*)lpMapAddress;
					// check recipient
					if (checkListener() != -1) {
						printf("Recipient not connected\n");
						Sleep(500);
					}
					// no message waiting
					else if (writeMessage(dummyMessage)) {
						printf("Message sent\n");
						result = 999;
					}
					else printf("Message waiting\n");
				}
				else {
					printf("MapViewOfFile failed\n");
					result = -1;
				}

				// release file mapping
				if (!UnmapViewOfFile(lpMapAddress)) {
					printf("UnmapViewOfFile failed\n");
					result = -2;
				}
				if(!CloseHandle(hMapFile)) {
					printf("CloseHandle failed\n");
					result = -3;
				}
			}
			__finally {
				// Release ownership of the mutex object.
				if(!ReleaseMutex(hMutex)) {
					printf("ReleaseMutex failed\n");
					result = 1;
				}
				if(!CloseHandle(hMutex)) {
					printf("CloseHandle failed\n");
					result = 2;
				}
			}
			break;

		// Cannot get mutex ownership due to time-out.
		case WAIT_TIMEOUT:
			printf("Wait timeout\n");
			Sleep(500);
			break;
		// Got ownership of the abandoned mutex object.
		case WAIT_ABANDONED:
			printf("Wait abandoned\n");
			Sleep(500);
			break;
		default:
			printf("No LocalConnection open\n");
			Sleep(500);
			break;
	}
	//
	Sleep(10);
	return result;
}

/**
 * Main thread: poll shared memory
 */
void main(int argc, char *argv[])
{
	// check parameters
	if (argc < 2) {
		printf("Usage:\n   lcwrite <connection name>\n");
		return;
	}
	// connection name
	strcpy( (char*)connectionName+strlen(connectionName), argv[1] );
	printf("Write to LocalConnection %s\n", connectionName);

	// poll until Ctrl+C
	while (!pollMemory()) ;
}
