aboutsummaryrefslogtreecommitdiff
path: root/source/nds/gcheat.c
blob: d0ada43ea9e36f2668541e8daa20cd00ca106b42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* gcheat.c
 *
 * Copyright (C) 2012 GBAtemp user Nebuleon.
 *
 * This program 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 program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "port.h"
#include "string.h"
#include "fs_api.h"
#include "ds2_malloc.h"
#include "gcheat.h"
#include "charsets.h"
#include "cheats.h"

extern struct SCheatData Cheat;

// Reads a cheat text file in BSNES's format.
int NDSSFCLoadCheatFile(const char* filename)
{
	FILE* fp = fopen(filename, "r");
	if (fp == NULL)
		return -1;

	S9xDeleteCheats();

	// The construction is "a","b","c" <newline>.
	// a is ignored. In BSNES, it decides whether the code is enabled.
	// b is a series of codes separated by +. Each of the codes is in the form
	//   accepted by the Game Genie, Pro Action Replay, or the GoldFinger.
	// c is the cheat's description.
	char line[256], code[24];
	char *description, *codes_ptr;
	uint32 address;
	uint8 byte;
	uint8 bytes [3];
	bool8 sram;
	uint8 num_bytes;

	while (fgets(line, sizeof(line), fp))
	{
		char* ptr = &line[0];
		// Ignore a.
		while (*ptr && *ptr != ',')
			ptr++;
		// If there was no comma, declare a bad file.
		if (*ptr == '\0') {
			fclose(fp);
			return -2;
		}
		*ptr++; // Past the comma

		if (*ptr && *ptr == '"')
			ptr++; // Starting quote of b.
		codes_ptr = ptr; // Save this for later.
		while (*ptr && *ptr != ',')
			ptr++;
		// If there was no comma, declare a bad file.
		if (*ptr == '\0') {
			fclose(fp);
			return -2;
		}
		*ptr = '\0'; // End the codes there
		*ptr++; // Past the comma

		uint32 i = 0;
		description = ptr; // Skip starting " in description
		while (*description && *description == '"')
			description++;
		ptr = description;
		while (*ptr && !(*ptr == '\r' || *ptr == '\n' || *ptr == '"') && i < MAX_SFCCHEAT_NAME - 1) {
			ptr++; // Remove trailing newline/quote in description
			i++; // Clip the cheat name to MAX_SFCCHEAT_NAME chars
		}
		*ptr = '\0';

		uint32 c;
		// n is the number of cheat codes. Beware of MAX_CHEATS_T.

		// List of cheat codes having the same description.
		ptr = codes_ptr;
		while (*ptr && !(*ptr == ',' || *ptr == '"')) {
			if (Cheat.num_cheats >= MAX_CHEATS_T) {
				fclose(fp);
				return 0;
			}
			i = 0;
			while (*ptr && !(*ptr == '+' || *ptr == ',' || *ptr == '"') && i < sizeof(code) - 1)
				code[i++] = *ptr++;
			if (*ptr)
				ptr++; // Go past the + , or "
			code[i] = '\0';
			if (!S9xGameGenieToRaw (code, &address, &byte)) {
				S9xAddCheat (FALSE, FALSE, address, byte);
				strncpy (Cheat.c[Cheat.num_cheats - 1].name, description, MAX_SFCCHEAT_NAME);
			}
			else if (!S9xProActionReplayToRaw (code, &address, &byte)) {
				S9xAddCheat (FALSE, FALSE, address, byte);
				strncpy (Cheat.c[Cheat.num_cheats - 1].name, description, MAX_SFCCHEAT_NAME);
			}
			else if (!S9xGoldFingerToRaw (code, &address, &sram, &num_bytes, bytes))
			{
				for (c = 0; c < num_bytes; c++) {
					S9xAddCheat (FALSE, FALSE, address + c, bytes[c]);
					strncpy (Cheat.c[Cheat.num_cheats - 1].name, description, MAX_SFCCHEAT_NAME);
				}
			}
			else {
				fclose(fp);
				return -3; // Bad cheat format
			}
		}
	}

	fclose(fp);
	return 0;
}