/*
	Copyright (C) 2009-2021 DeSmuME team

	This file is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This file is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with the this software.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include "types.h"

#define CHEAT_VERSION_MAJOR			2
#define CHEAT_VERSION_MINOR			0
#define MAX_CHEAT_LIST				100
#define	MAX_XX_CODE					1024
#define CHEAT_FILE_MIN_FGETS_BUFFER	32768
#define CHEAT_DB_GAME_TITLE_SIZE	256

#define CHEAT_TYPE_EMPTY 0xFF
#define CHEAT_TYPE_INTERNAL 0
#define CHEAT_TYPE_AR 1
#define CHEAT_TYPE_CODEBREAKER 2

struct CHEATS_LIST
{
	CHEATS_LIST()
	{
		memset(this,0,sizeof(*this));
		type = 0xFF;
	}
	u8 type;
	BOOL	enabled;
	// TODO
	u8		freezeType;			// 0 - normal freeze
								// 1 - can decrease
								// 2 - can increase
	u32		code[MAX_XX_CODE][2];
	char	description[1024];
	u32		num;
	u8		size;
};

class CHEATS
{
private:
	std::vector<CHEATS_LIST> list;
	u8					filename[MAX_PATH];
	u32					currentGet;

	void	clear();
	void	ARparser(CHEATS_LIST& cheat);
	char	*clearCode(char *s);

public:
	CHEATS()
		: currentGet(0)
	{
		memset(filename, 0, sizeof(filename));
	}
	~CHEATS() {}

	void	init(char *path);
	BOOL	add(u8 size, u32 address, u32 val, char *description, BOOL enabled);
	BOOL	update(u8 size, u32 address, u32 val, char *description, BOOL enabled, u32 pos);
	BOOL	move(u32 srcPos, u32 dstPos);
	BOOL	add_AR(char *code, char *description, BOOL enabled);
	BOOL	update_AR(char *code, char *description, BOOL enabled, u32 pos);
	BOOL	add_AR_Direct(CHEATS_LIST cheat);
	BOOL	add_CB(char *code, char *description, BOOL enabled);
	BOOL	update_CB(char *code, char *description, BOOL enabled, u32 pos);
	BOOL	remove(u32 pos);
	void	getListReset();
	BOOL	getList(CHEATS_LIST *cheat);
	CHEATS_LIST*	getListPtr();
	BOOL	get(CHEATS_LIST *cheat, u32 pos);
	CHEATS_LIST*	getItemByIndex(const u32 pos);
	u32		getSize();
	size_t	getActiveCount();
	void	setDescription(const char *description, u32 pos);
	BOOL	save();
	BOOL	load();
	void	process(int targetType);
	void	getXXcodeString(CHEATS_LIST theList, char *res_buf);
	
	static BOOL XXCodeFromString(CHEATS_LIST *cheatItem, const std::string codeString);
	static BOOL XXCodeFromString(CHEATS_LIST *cheatItem, const char *codeString);
};

class CHEATSEARCH
{
private:
	u8	*statMem;
	u8	*mem;
	u32	amount;
	u32	lastRecord;

	u32	_type;
	u32	_size;
	u32	_sign;

public:
	CHEATSEARCH()
			: statMem(0), mem(0), amount(0), lastRecord(0), _type(0), _size(0), _sign(0) 
	{}
	~CHEATSEARCH() { close(); }
	BOOL start(u8 type, u8 size, u8 sign);
	BOOL close();
	u32 search(u32 val);
	u32 search(u8 comp);
	u32 getAmount();
	BOOL getList(u32 *address, u32 *curVal);
	void getListReset();
};

enum CHEATS_DB_TYPE
{
	CHEATS_DB_R4 = 0
};

#pragma pack(push)
#pragma pack(1)
typedef struct FAT_R4
{
	u8	serial[4];
	u32	CRC;
	u64 addr;
} FAT_R4;
#pragma pack(pop)

class CHEATSEXPORT
{
private:
	CHEATS_DB_TYPE		type;
	bool				encrypted;
	FILE				*fp;
	u32					fsize;
	u32					dataSize;
	u32					encOffset;
	FAT_R4				fat;
	bool				search();
	bool				getCodes();
	void				R4decrypt(u8 *buf, u32 len, u32 n);

	u32					numCheats;
	CHEATS_LIST			*cheats;

	u8					error;		//	0 - no errors
									//	1 - open failed/file not found
									//	2 - file format is wrong (no valid header ID)
									//	3 - cheat not found in database
									//	4 - export error from database

public:
	CHEATSEXPORT() :
			fp(NULL),
			fsize(0),
			dataSize(0),
			encOffset(0),
			type(CHEATS_DB_R4),
			encrypted(false),
			numCheats(0),
			cheats(0),
			CRC(0),
			error(0)
	{
		memset(date, 0, sizeof(date));
		gametitle = (u8 *)malloc(CHEAT_DB_GAME_TITLE_SIZE);
		memset(gametitle, 0, CHEAT_DB_GAME_TITLE_SIZE);
	}
	~CHEATSEXPORT()
	{
		free(gametitle);
		gametitle = NULL;
	}

	u8				*gametitle;
	u8				date[17];
	u32				CRC;
	bool			load(char *path);
	void			close();
	CHEATS_LIST		*getCheats();
	u32				getCheatsNum();
	u8				getErrorCode() { return error; }
};

extern CHEATS *cheats;
extern CHEATSEARCH *cheatSearch;


