1 /*
2 	This file is part of cave9.
3 
4 	cave9 is free software: you can redistribute it and/or modify
5 	it under the terms of the GNU General Public License as published by
6 	the Free Software Foundation, either version 3 of the License, or
7 	(at your option) any later version.
8 
9 	cave9 is distributed in the hope that it will be useful,
10 	but WITHOUT ANY WARRANTY; without even the implied warranty of
11 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 	GNU General Public License for more details.
13 
14 	You should have received a copy of the GNU General Public License
15 	along with cave9.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifdef _WIN32
19 # undef __STRICT_ANSI__ // where did it come from? we're running C99 // for string.h: strdup
20 #endif
21 #include <string.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <assert.h>
27 #include "score.h"
28 #include "util.h"
29 
30 #ifdef _WIN32
31 #include <io.h> // mkdir
32 #endif
33 
34 #ifdef GLOBAL_SCORE
35 
score_net_finish(Score * score)36 static void score_net_finish (Score* score)
37 {
38 	if(score->udp_pkt != NULL) {
39 		SDLNet_FreePacket(score->udp_pkt);
40 		score->udp_pkt = NULL;
41 	}
42 
43 	if(score->udp_sock != NULL) {
44 		SDLNet_UDP_Close(score->udp_sock);
45 		score->udp_sock = NULL;
46 	}
47 }
48 
score_net_init(Score * score,const char * server,int port)49 static void score_net_init (Score* score, const char* server, int port)
50 {
51 	if (server == NULL  ||  server[0] == '\0')
52 		return;
53 
54 	if (SDLNet_Init() == -1) {
55 		fprintf (stderr, "SDLNet_Init(): %s\n", SDLNet_GetError());
56 		exit(1);
57 	}
58 	atexit (SDLNet_Quit);
59 
60 	IPaddress addr;
61 	score->udp_sock = NULL;
62 	score->udp_pkt = NULL;
63 	if (SDLNet_ResolveHost (&addr, server, port) == -1) {
64 		fprintf(stderr, "SDLNet_ResolveHost('%s',%d): %s\n",
65 				server, port, SDLNet_GetError());
66 	} else {
67 		score->udp_sock = SDLNet_UDP_Open(0);
68 		if(score->udp_sock == NULL) {
69 			fprintf(stderr, "SDLNet_UDP_Open(): %s\n", SDLNet_GetError());
70 			score_net_finish (score);
71 		} else {
72 			if(SDLNet_UDP_Bind(score->udp_sock, 0, &addr) == -1) {
73 				fprintf(stderr, "SDLNet_UDP_Bind(): %s\n", SDLNet_GetError());
74 				score_net_finish (score);
75 			} else {
76 				score->udp_pkt = SDLNet_AllocPacket (GLOBAL_SCORE_LEN);
77 				if(score->udp_pkt == NULL) {
78 					fprintf(stderr, "SDLNet_AllocPacket(%d): %s\n",
79 							GLOBAL_SCORE_LEN, SDLNet_GetError());
80 					score_net_finish (score);
81 				} else {
82 					printf("Score Global init UDP port %d\n", port);
83 				}
84 			}
85 		}
86 	}
87 }
88 
score_net_update(Score * score)89 static void score_net_update (Score* score)
90 {
91 	if (score->udp_sock == NULL) {
92 		return;
93 	}
94 
95 	printf("Score Global send %d\n", score->global);
96 	snprintf ((char*)score->udp_pkt->data,GLOBAL_SCORE_LEN, "%d\n", score->global);
97 	score->udp_pkt->len = GLOBAL_SCORE_LEN;
98 	if (SDLNet_UDP_Send (score->udp_sock, 0, score->udp_pkt) == 1)
99 	{
100 		SDL_Delay (GLOBAL_SCORE_WAIT); // XXX only wait GLOBAL_SCORE_WAIT for hiscores
101 		int n = SDLNet_UDP_Recv (score->udp_sock, score->udp_pkt);
102 		if (n == 1) {
103 			score->udp_pkt->data[GLOBAL_SCORE_LEN-1] = '\0'; // XXX safeguard
104 			sscanf ((char*)score->udp_pkt->data, "%d", &score->global);
105 			printf("Score Global recv %d\n", score->global);
106 		}
107 		else if (n < 0) {
108 			fprintf (stderr, "SDLNet_UDP_Recv(%s,%d): %s\n",
109 					GLOBAL_SCORE_HOST, GLOBAL_SCORE_PORT, SDLNet_GetError());
110 		}
111 	}
112 	else {
113 		fprintf (stderr, "SDLNet_UDP_Send(): %s\n", SDLNet_GetError());
114 	}
115 }
116 
117 #endif
118 
score_init(Score * score,Args * args,int caveseed,int monstal)119 void score_init (Score* score, Args* args, int caveseed, int monstal)
120 {
121 	if (args == NULL)
122 		return;
123 
124 	memset (score, 0, sizeof(Score));
125 
126 	char cave9_home[FILENAME_MAX];
127 	char* home = getenv("HOME");
128 	if (home != NULL) {
129 		sprintf (cave9_home, "%s/.cave9", home);
130 		mkdir (
131 			cave9_home
132 #ifndef _WIN32 // XXX no mode on win32
133 			, 0755
134 #endif
135 		);
136 	}
137 	else {
138 		fprintf (stderr,
139 			"HOME environment variable not set, using '%s' to save\n",
140 			bin_path);
141 
142 		strncpy(cave9_home, bin_path, FILENAME_MAX-1);
143 		cave9_home[FILENAME_MAX-1] = '\0';
144 	}
145 	size_t len = strlen(cave9_home) + strlen("/") + strlen(SCORE_FILE) + 1;
146 	score->filename = malloc (len);
147 	snprintf (score->filename, len, "%s/%s", cave9_home, SCORE_FILE);
148 	assert (score->filename != NULL);
149 
150 	const char* paths[] = { "", ".", "~/.cave9", NULL };
151 	const char* filename = find_file (SCORE_FILE, paths, false);
152 
153 	score->local = 0;
154 	score->caveseed = caveseed;
155 	score->monstal = monstal;
156 
157 	if(filename != NULL) {
158 		FILE* fp = fopen (filename, "r");
159 		if (fp != NULL) {
160 			int fseed = -1;
161 			int fmonstal = 0;
162 			int fscore = 0;
163 			while(!(fseed == score->caveseed && fmonstal == score->monstal) && !feof(fp))
164 			{
165 				fscanf (fp, "%11d:%2d:%11d ", &fseed, &fmonstal, &fscore);
166 			}
167 			if(fseed == caveseed && fmonstal == monstal)
168 				score->local = fscore;
169 			fclose (fp);
170 		}
171 	}
172 
173 #ifdef GLOBAL_SCORE
174 	score_net_init (score, args->server, args->port);
175 #endif
176 }
177 
score_finish(Score * score)178 void score_finish (Score* score)
179 {
180 #ifdef GLOBAL_SCORE
181 	score_net_finish (score);
182 #endif
183 	free (score->filename);
184 	score->filename = NULL;
185 }
186 
score_update(Score * score,int new_score,bool is_global)187 void score_update (Score* score, int new_score, bool is_global)
188 {
189 	if (new_score > score->session)
190 		score->session = new_score;
191 
192 	if (is_global) {
193 
194 		if (new_score > score->local) {
195 			score->local = new_score;
196 			FILE* fp = fopen (score->filename, "r+");
197 			bool readwrite = true;
198 			if (fp == NULL)
199 			{
200 				// file didn't exist yet, we need to create an empty one
201 				fp = fopen (score->filename, "w");
202 				readwrite = false;
203 			}
204 			if (fp == NULL) {
205 				perror ("failed to open score file");
206 			} else {
207 				int fseed = -1;
208 				int fmonstal = 0;
209 				int fscore = 0;
210 				bool found = false;
211 				if (readwrite)
212 				{
213 					while (!feof(fp) && !found)
214 					{
215 						fscanf (fp, "%11d:%2d:%11d", &fseed, &fmonstal, &fscore);
216 						if (fseed == score->caveseed && fmonstal == score->monstal)
217 						{
218 							if (fscore <= score->local)
219 							{
220 								fseek(fp, -11, SEEK_CUR);
221 								fprintf(fp, "%011d", score->local);
222 							}
223 							found = true;
224 						}
225 					}
226 				}
227 				if (!found || !readwrite)
228 				{
229 					fprintf(fp, "%011d:%02d:%011d\n", score->caveseed, score->monstal, score->local);
230 				}
231 				fclose (fp);
232 			}
233 		}
234 
235 		int new_global = MAX(new_score, score->local);
236 		if (new_global > score->global) {
237 			score->global = new_global;
238 #ifdef GLOBAL_SCORE
239 			score_net_update (score);
240 #endif
241 		}
242 	}
243 }
244 
245 // vim600:fdm=syntax:fdn=1:
246