1 /*
2 Copyright (C) 2003-2006 Andrey Nazarov
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program 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.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 #include "ui_local.h"
22
23 /*
24 =======================================================================
25
26 DEMOS MENU
27
28 =======================================================================
29 */
30
31 #define MAX_MENU_DEMOS 1024
32
33 #define FFILE_UP 1
34 #define FFILE_FOLDER 2
35 #define FFILE_DEMO 3
36
37 #define ID_LIST 105
38
39 typedef struct m_demos_s {
40 menuFrameWork_t menu;
41 menuList_t list;
42 menuList_t playerList;
43 menuStatic_t banner;
44
45 int count;
46 char *names[MAX_MENU_DEMOS];
47 int types[MAX_MENU_DEMOS];
48 char *playerNames[MAX_DEMOINFO_CLIENTS+1];
49
50 // Demo file info
51 demoInfo_t demo;
52 } m_demos_t;
53
54 static m_demos_t m_demos;
55 static char m_demos_browse[MAX_QPATH];
56 static int m_demos_selection;
57
Demos_FreeInfo(void)58 static void Demos_FreeInfo( void ) {
59 memset( &m_demos.demo, 0, sizeof( m_demos.demo ) );
60
61 m_demos.playerList.generic.flags |= QMF_HIDDEN;
62 }
63
Demos_LoadInfo(int index)64 static void Demos_LoadInfo( int index ) {
65 char buffer[MAX_QPATH];
66 int i, numNames, localPlayerNum;
67
68 if( m_demos.types[index] != FFILE_DEMO ) {
69 m_demos.playerList.generic.flags |= QMF_HIDDEN;
70 m_demos.menu.statusbar = NULL;
71 return;
72 }
73
74
75 Com_sprintf( buffer, sizeof( buffer ), "%s/%s", m_demos_browse + 1, m_demos.names[index] );
76
77 client.GetDemoInfo( buffer, &m_demos.demo );
78
79 if( !m_demos.demo.mapname[0] ) {
80 m_demos.menu.statusbar = "Couldn't read demo info";
81 m_demos.playerList.generic.flags |= QMF_HIDDEN;
82 return;
83 }
84
85
86 localPlayerNum = 0;
87
88 numNames = 0;
89 for( i = 0; i < MAX_DEMOINFO_CLIENTS; i++ ) {
90 if( !m_demos.demo.clients[i][0] ) {
91 continue;
92 }
93
94 if( i == m_demos.demo.clientNum ) {
95 localPlayerNum = numNames;
96 }
97
98 // ( m_demos.demo.mvd || i == m_demos.demo.clientNum ) ? colorYellow : NULL
99
100 m_demos.playerNames[numNames++] = m_demos.demo.clients[i];
101 }
102
103 if( numNames ) {
104 m_demos.playerList.generic.flags &= ~QMF_HIDDEN;
105 } else {
106 m_demos.playerList.generic.flags |= QMF_HIDDEN;
107 }
108
109 m_demos.playerNames[numNames] = NULL;
110
111 if( m_demos.demo.mvd ) {
112 m_demos.menu.statusbar = "Press Enter to play";
113 } else {
114 m_demos.menu.statusbar = "Press Enter to play, "
115 "hold Shift to play on server";
116 }
117
118 MenuList_Init( &m_demos.playerList );
119 MenuList_SetValue( &m_demos.playerList, localPlayerNum );
120 }
121
Demos_BuildName(const char * base,const char * extenstion,int fileSize,int fileType)122 static char *Demos_BuildName( const char *base, const char *extenstion, int fileSize, int fileType ) {
123 char *type, *sizeString, *s;
124
125 switch( fileType ) {
126 case FFILE_UP:
127 sizeString = "";
128 type = " UP";
129 break;
130 case FFILE_FOLDER:
131 sizeString = "";
132 type = "DIR";
133 break;
134 case FFILE_DEMO:
135 sizeString = va( "%4i", fileSize );
136 if( !strncmp( extenstion, ".mvd2", 5 ) ) {
137 type = "MVD";
138 } else {
139 type = "DM2";
140 }
141 break;
142 default:
143 type = "";
144 sizeString = "";
145 break;
146 }
147
148 s = UI_FormatColumns( 3, base, sizeString, type );
149
150 return s;
151
152 }
153
Demos_Scan(const char * path,const char * extenstion)154 static void Demos_Scan( const char *path, const char *extenstion ) {
155 int numFiles;
156 char **list;
157 int i;
158 fsFileInfo_t *info;
159
160 list = fs.ListFiles( path, extenstion, FS_PATH_GAME|FS_SEARCH_EXTRAINFO, &numFiles );
161 if( !list ) {
162 return;
163 }
164
165 for( i = 0; i < numFiles; i++ ) {
166 if( m_demos.count == MAX_MENU_DEMOS - 1 ) {
167 break;
168 }
169
170 info = ( fsFileInfo_t * )( list[i] + strlen( list[i] ) + 1 );
171
172 m_demos.names[m_demos.count] = Demos_BuildName( list[i], extenstion, info->fileSize >> 10, FFILE_DEMO );
173 m_demos.types[m_demos.count] = FFILE_DEMO;
174 m_demos.count++;
175 }
176
177 fs.FreeFileList( list );
178
179 }
180
Demos_BuildList(const char * path)181 static void Demos_BuildList( const char *path ) {
182 int numFiles;
183 char **list;
184 int pos;
185 int i;
186
187 if( *path == '/' ) {
188 path++;
189 }
190
191 if( *path ) {
192 m_demos.names[m_demos.count] = Demos_BuildName( "..", NULL, -1, FFILE_UP );
193 m_demos.types[m_demos.count] = FFILE_UP;
194 m_demos.count++;
195 }
196
197 // List directories first
198 list = fs.ListFiles( path, NULL, FS_PATH_GAME|FS_SEARCHDIRS_ONLY, &numFiles );
199 if( list ) {
200 for( i = 0; i < numFiles; i++ ) {
201 if( m_demos.count == MAX_MENU_DEMOS - 1 ) {
202 break;
203 }
204 m_demos.names[m_demos.count] = Demos_BuildName( list[i], NULL, -1, FFILE_FOLDER );
205 m_demos.types[m_demos.count] = FFILE_FOLDER;
206 m_demos.count++;
207 }
208
209 fs.FreeFileList( list );
210 }
211
212 pos = m_demos.count;
213
214 Demos_Scan( path, ".dm2" );
215 Demos_Scan( path, ".dm2.gz" );
216 Demos_Scan( path, ".dm_35" );
217 Demos_Scan( path, ".dm_35.gz" );
218 Demos_Scan( path, ".dm_36" );
219 Demos_Scan( path, ".dm_36.gz" );
220 Demos_Scan( path, ".mvd2" );
221 Demos_Scan( path, ".mvd2.gz" );
222
223 // Sort demos, not directories
224 if( m_demos.count - pos > 1 ) {
225 qsort( m_demos.names + pos, m_demos.count - pos, sizeof( m_demos.names[0] ), SortStrcmp );
226 }
227
228 m_demos.names[m_demos.count] = NULL;
229 }
230
Demos_Free(void)231 static void Demos_Free( void ) {
232 int i;
233
234 Demos_FreeInfo();
235
236 for( i = 0; i < m_demos.count; i++ ) {
237 com.Free( m_demos.names[i] );
238 m_demos.names[i] = NULL;
239 m_demos.types[i] = 0;
240 }
241
242 m_demos.count = 0;
243 m_demos.list.curvalue = 0;
244 }
245
Demos_LeaveDirectory(void)246 static void Demos_LeaveDirectory( void ) {
247 char buffer[MAX_QPATH];
248 char *s;
249 int i;
250
251 s = strrchr( m_demos_browse, '/' );
252 if( s ) {
253 *s = 0;
254 strcpy( buffer, s + 1 );
255 } else {
256 buffer[0] = 0;
257 }
258
259 // rebuild list
260 Demos_Free();
261 Demos_BuildList( m_demos_browse );
262 MenuList_Init( &m_demos.list );
263
264 if( s == m_demos_browse ) {
265 m_demos_browse[0] = '/';
266 m_demos_browse[1] = 0;
267 }
268
269 // move cursor to the previous directory
270 if( buffer[0] ) {
271 for( i = 0; i < m_demos.count; i++ ) {
272 if( !strcmp( m_demos.names[i], buffer ) ) {
273 MenuList_SetValue( &m_demos.list, i );
274 return;
275 }
276 }
277 }
278
279 MenuList_SetValue( &m_demos.list, 0 );
280
281 }
282
Demos_Action(void)283 static int Demos_Action( void ) {
284 char buffer[MAX_QPATH];
285 int length, baseLength;
286 char *c;
287
288 switch( m_demos.types[m_demos.list.curvalue] ) {
289 case FFILE_UP:
290 Demos_LeaveDirectory();
291 return QMS_OUT;
292
293 case FFILE_FOLDER:
294 baseLength = strlen( m_demos_browse );
295 length = strlen( m_demos.names[m_demos.list.curvalue] );
296 if( baseLength + length > sizeof( m_demos_browse ) - 2 ) {
297 return QMS_BEEP;
298 }
299 if( m_demos_browse[ baseLength - 1 ] != '/' ) {
300 m_demos_browse[ baseLength++ ] = '/';
301 }
302
303 strcpy( m_demos_browse + baseLength, m_demos.names[m_demos.list.curvalue] );
304
305 // rebuild list
306 Demos_Free();
307 Demos_BuildList( m_demos_browse );
308 MenuList_Init( &m_demos.list );
309 return QMS_IN;
310
311 case FFILE_DEMO:
312 if( !m_demos.demo.mapname[0] ) {
313 return QMS_BEEP;
314 }
315 c = m_demos.demo.mvd || keys.IsDown( K_SHIFT ) ? "mvdplay" : "demo";
316 Com_sprintf( buffer, sizeof( buffer ), "%s \"%s/%s\"\n", c,
317 m_demos_browse, m_demos.names[m_demos.list.curvalue] );
318 cmd.ExecuteText( EXEC_APPEND, buffer );
319 //UI_ForceMenuOff();
320 return QMS_SILENT;
321 }
322
323 return QMS_NOTHANDLED;
324 }
325
Demos_MenuCallback(int id,int msg,int param)326 static int Demos_MenuCallback( int id, int msg, int param ) {
327 switch( msg ) {
328 case QM_ACTIVATE:
329 switch( id ) {
330 case ID_LIST:
331 return Demos_Action();
332 }
333 return QMS_IN;
334
335 case QM_CHANGE:
336 switch( id ) {
337 case ID_LIST:
338 Demos_LoadInfo( m_demos.list.curvalue );
339 break;
340 default:
341 break;
342 }
343 return QMS_MOVE;
344
345 case QM_KEY:
346 switch( id ) {
347 case ID_LIST:
348 switch( param ) {
349 case K_BACKSPACE:
350 Demos_LeaveDirectory();
351 return QMS_OUT;
352 default:
353 break;
354 }
355 default:
356 break;
357 }
358 break;
359
360 case QM_DESTROY:
361 m_demos_selection = m_demos.list.curvalue; // save previous position
362 Demos_Free();
363 break;
364
365 default:
366 break;
367 }
368
369 return QMS_NOTHANDLED;
370 }
371
Demos_MenuInit(void)372 static void Demos_MenuInit( void ) {
373 int w1, w2;
374
375 memset( &m_demos, 0, sizeof( m_demos ) );
376
377 m_demos.menu.callback = Demos_MenuCallback;
378
379 // Point to a nice location at startup
380 if( !m_demos_browse[0] ) {
381 strcpy( m_demos_browse, "/demos" );
382 }
383
384 Demos_BuildList( m_demos_browse );
385
386 w1 = ( uis.glconfig.vidWidth - 30 ) * 0.8f;
387 w2 = ( uis.glconfig.vidWidth - 30 ) * 0.2f;
388
389 m_demos.list.generic.type = MTYPE_LIST;
390 m_demos.list.generic.id = ID_LIST;
391 m_demos.list.generic.flags = QMF_HASFOCUS;
392 m_demos.list.generic.x = 10;
393 m_demos.list.generic.y = 32;
394 m_demos.list.generic.width = 0;
395 m_demos.list.generic.height = uis.glconfig.vidHeight - 64;
396 m_demos.list.generic.name = NULL;
397 m_demos.list.itemnames = ( const char ** )m_demos.names;
398 m_demos.list.drawNames = qtrue;
399 m_demos.list.numcolumns = 3;
400
401 m_demos.list.columns[0].width = w1 - 90;
402 m_demos.list.columns[0].name = m_demos_browse;
403 m_demos.list.columns[0].uiFlags = UI_LEFT;
404
405 m_demos.list.columns[1].width = 40;
406 m_demos.list.columns[1].name = "KB";
407 m_demos.list.columns[1].uiFlags = UI_RIGHT;
408
409 m_demos.list.columns[2].width = 50;
410 m_demos.list.columns[2].name = "Type";
411 m_demos.list.columns[2].uiFlags = UI_CENTER;
412
413 m_demos.playerList.generic.type = MTYPE_LIST;
414 m_demos.playerList.generic.flags = QMF_HIDDEN|QMF_DISABLED;
415 m_demos.playerList.generic.x = w1 + 20;
416 m_demos.playerList.generic.y = 32;
417 m_demos.playerList.generic.height = uis.glconfig.vidHeight - 64;
418 m_demos.playerList.itemnames = ( const char ** )m_demos.playerNames;
419 m_demos.playerList.mlFlags = MLF_HIDE_SCROLLBAR_EMPTY;
420 m_demos.playerList.numcolumns = 1;
421 m_demos.playerList.drawNames = qtrue;
422
423 m_demos.playerList.columns[0].width = w2;
424 m_demos.playerList.columns[0].name = "Players";
425 m_demos.playerList.columns[0].uiFlags = UI_CENTER;
426
427 UI_SetupDefaultBanner( &m_demos.banner, "Demos" );
428
429 Menu_AddItem( &m_demos.menu, (void *)&m_demos.list );
430 Menu_AddItem( &m_demos.menu, (void *)&m_demos.playerList );
431 Menu_AddItem( &m_demos.menu, (void *)&m_demos.banner );
432
433 // move cursor to previous position
434 MenuList_SetValue( &m_demos.list, m_demos_selection );
435 }
436
M_Menu_Demos_f(void)437 void M_Menu_Demos_f( void ) {
438 Demos_MenuInit();
439 UI_PushMenu( &m_demos.menu );
440 }
441