/*
 * m1cartridge.cpp
 *
 *  Created on: Nov 4, 2018
 *      Author: flyinghead
 *  Plagiarized from mame (mame/src/mame/machine/naomim1.cpp)
 *  // license:BSD-3-Clause
 *  // copyright-holders:Olivier Galibert
 */
#include "m1cartridge.h"

M1Cartridge::M1Cartridge(u32 size) : NaomiCartridge(size)
{
	rom_cur_address = 0;
	buffer_actual_size = 0;
	has_history = false;
	stream_ended = false;
	memset(dict, 0, sizeof(dict));
	memset(hist, 0, sizeof(hist));
	avail_val = 0;
	avail_bits = 0;
	encryption = false;
}

void M1Cartridge::AdvancePtr(u32 size)
{
	if (encryption)
	{
		if (size < buffer_actual_size)
		{
			memmove(buffer, buffer + size, buffer_actual_size - size);
			buffer_actual_size -= size;
		}
		else
		{
			hist[0] = buffer[buffer_actual_size - 2];
			hist[1] = buffer[buffer_actual_size - 1];
			has_history = true;
			buffer_actual_size = 0;
		}
		enc_fill();
	}
	else
		NaomiCartridge::AdvancePtr(size);
}

void M1Cartridge::wb(u8 byte)
{
	if (dict[0] & 64)
		if (buffer_actual_size < 2)
			if (has_history)
				buffer[buffer_actual_size] = hist[buffer_actual_size] - byte;
			else
				buffer[buffer_actual_size] = byte;
		else
			buffer[buffer_actual_size] = buffer[buffer_actual_size - 2] - byte;
	else
		buffer[buffer_actual_size] = byte;

	buffer_actual_size++;
}

void M1Cartridge::enc_fill()
{
	while (buffer_actual_size < sizeof(buffer) && !stream_ended)
	{
		switch (lookb(3)) {
		// 00+2 - 0000+esc
		case 0:
		case 1: {
			skipb(2);
			int addr = getb(2);
			if (addr)
				wb (dict[addr]);
			else
				wb(getb(8));

			break;
		}
			// 010+2
		case 2:
			skipb(3);
			wb (dict[getb(2) + 4]);
			break;
			// 011+3
		case 3:
			skipb(3);
			wb(dict[getb(3) + 8]);
			break;
			// 10+5
		case 4:
		case 5:
			skipb(2);
			wb(dict[getb(5) + 16]);
			break;
			// 11+6
		case 6:
		case 7:
			{
				skipb(2);
				int addr = getb(6) + 48;
				if (addr == 111)
					stream_ended = true;
				else
					wb(dict[addr]);

				break;
			}
		}
	}
	while (buffer_actual_size < sizeof(buffer))
		buffer[buffer_actual_size++] = 0;
}

u32 M1Cartridge::get_decrypted_32b()
{
	u8* base = RomPtr + rom_cur_address;
	u8 a = base[0];
	u8 b = base[1];
	u8 c = base[2];
	u8 d = base[3];
	rom_cur_address += 4;

	u32 swapped_key = (key >> 24) | ((key >> 8) & 0xFF00)
			| ((key << 8) & 0xFF0000) | (key << 24);
	u32 res = swapped_key ^ (((b ^ d) << 24) | ((a ^ c) << 16) | (b << 8) | a);
	return res;
}

void M1Cartridge::Serialize(void** data, unsigned int* total_size) {
	LIBRETRO_S(buffer);
	LIBRETRO_S(dict);
	LIBRETRO_S(hist);
	LIBRETRO_S(avail_val);
	LIBRETRO_S(rom_cur_address);
	LIBRETRO_S(buffer_actual_size);
	LIBRETRO_S(avail_bits);
	LIBRETRO_S(stream_ended);
	LIBRETRO_S(has_history);
	LIBRETRO_S(encryption);

   NaomiCartridge::Serialize(data, total_size);
}

void M1Cartridge::Unserialize(void** data, unsigned int* total_size) {
	LIBRETRO_US(buffer);
	LIBRETRO_US(dict);
	LIBRETRO_US(hist);
	LIBRETRO_US(avail_val);
	LIBRETRO_US(rom_cur_address);
	LIBRETRO_US(buffer_actual_size);
	LIBRETRO_US(avail_bits);
	LIBRETRO_US(stream_ended);
	LIBRETRO_US(has_history);
	LIBRETRO_US(encryption);

   NaomiCartridge::Unserialize(data, total_size);
}
