summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/p_plats.c
blob: 4a5c28bb883df376255e92555c9eaf3cb4b11170 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 *
 *  PrBoom a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:
 *  Plats (i.e. elevator platforms) code, raising/lowering.
 *
 *-----------------------------------------------------------------------------*/

#include "doomstat.h"
#include "m_random.h"
#include "r_main.h"
#include "p_spec.h"
#include "p_tick.h"
#include "s_sound.h"
#include "sounds.h"
#include "rockmacros.h"
platlist_t *activeplats;       // killough 2/14/98: made global again

//
// T_PlatRaise()
//
// Action routine to move a plat up and down
//
// Passed a plat structure containing all pertinent information about the move
// No return
//
// jff 02/08/98 all cases with labels beginning with gen added to support
// generalized line type behaviors.

void T_PlatRaise(plat_t* plat)
{
   result_e      res;

   // handle plat moving, up, down, waiting, or in stasis,
   switch(plat->status)
   {
   case up: // plat moving up
      res = T_MovePlane(plat->sector,plat->speed,plat->high,plat->crush,0,1);

      // if a pure raise type, make the plat moving sound
      if (plat->type == raiseAndChange
            || plat->type == raiseToNearestAndChange)
      {
         if (!(leveltime&7))
            S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_stnmov);
      }

      // if encountered an obstacle, and not a crush type, reverse direction
      if (res == crushed && (!plat->crush))
      {
         plat->count = plat->wait;
         plat->status = down;
         S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstart);
      }
      else  // else handle reaching end of up stroke
      {
         if (res == pastdest) // end of stroke
         {
            // if not an instant toggle type, wait, make plat stop sound
            if (plat->type!=toggleUpDn)
            {
               plat->count = plat->wait;
               plat->status = waiting;
               S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstop);
            }
            else // else go into stasis awaiting next toggle activation
            {
               plat->oldstatus = plat->status;//jff 3/14/98 after action wait
               plat->status = in_stasis;      //for reactivation of toggle
            }

            // lift types and pure raise types are done at end of up stroke
            // only the perpetual type waits then goes back up
            switch(plat->type)
            {
            case blazeDWUS:
            case downWaitUpStay:
            case raiseAndChange:
            case raiseToNearestAndChange:
            case genLift:
               P_RemoveActivePlat(plat);     // killough
            default:
               break;
            }
         }
      }
      break;

   case down: // plat moving down
      res = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1);

      // handle reaching end of down stroke
      if (res == pastdest)
      {
         // if not an instant toggle, start waiting, make plat stop sound
         if (plat->type!=toggleUpDn) //jff 3/14/98 toggle up down
         {                           // is silent, instant, no waiting
            plat->count = plat->wait;
            plat->status = waiting;
            S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstop);
         }
         else // instant toggles go into stasis awaiting next activation
         {
            plat->oldstatus = plat->status;//jff 3/14/98 after action wait
            plat->status = in_stasis;      //for reactivation of toggle
         }

         //jff 1/26/98 remove the plat if it bounced so it can be tried again
         //only affects plats that raise and bounce
         //killough 1/31/98: relax compatibility to demo_compatibility

         // remove the plat if its a pure raise type
         if (!comp[comp_floors])
         {
            switch(plat->type)
            {
            case raiseAndChange:
            case raiseToNearestAndChange:
               P_RemoveActivePlat(plat);
            default:
               break;
            }
         }
      }
      break;

   case waiting: // plat is waiting
      if (!--plat->count)  // downcount and check for delay elapsed
      {
         if (plat->sector->floorheight == plat->low)
            plat->status = up;     // if at bottom, start up
         else
            plat->status = down;   // if at top, start down

         // make plat start sound
         S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstart);
      }
      break; //jff 1/27/98 don't pickup code added later to in_stasis

   case in_stasis: // do nothing if in stasis
      break;
   }
}


//
// EV_DoPlat
//
// Handle Plat linedef types
//
// Passed the linedef that activated the plat, the type of plat action,
// and for some plat types, an amount to raise
// Returns true if a thinker is started, or restarted from stasis
//
int EV_DoPlat
( line_t*       line,
  plattype_e    type,
  int           amount )
{
   plat_t* plat;
   int             secnum;
   int             rtn;
   sector_t*       sec;

   secnum = -1;
   rtn = 0;


   // Activate all <type> plats that are in_stasis
   switch(type)
   {
   case perpetualRaise:
      P_ActivateInStasis(line->tag);
      break;

   case toggleUpDn:
      P_ActivateInStasis(line->tag);
      rtn=1;
      break;

   default:
      break;
   }

   // act on all sectors tagged the same as the activating linedef
   while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
   {
      sec = &sectors[secnum];

      // don't start a second floor function if already moving
      if (P_SectorActive(floor_special,sec)) //jff 2/23/98 multiple thinkers
         continue;

      // Create a thinker
      rtn = 1;
      plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0);
      P_AddThinker(&plat->thinker);

      plat->type = type;
      plat->sector = sec;
      plat->sector->floordata = plat; //jff 2/23/98 multiple thinkers
      plat->thinker.function = T_PlatRaise;
      plat->crush = false;
      plat->tag = line->tag;

      //jff 1/26/98 Avoid raise plat bouncing a head off a ceiling and then
      //going down forever -- default low to plat height when triggered
      plat->low = sec->floorheight;

      // set up plat according to type
      switch(type)
      {
      case raiseToNearestAndChange:
         plat->speed = PLATSPEED/2;
         sec->floorpic = sides[line->sidenum[0]].sector->floorpic;
         plat->high = P_FindNextHighestFloor(sec,sec->floorheight);
         plat->wait = 0;
         plat->status = up;
         sec->special = 0;
         //jff 3/14/98 clear old field as well
         sec->oldspecial = 0;

         S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov);
         break;

      case raiseAndChange:
         plat->speed = PLATSPEED/2;
         sec->floorpic = sides[line->sidenum[0]].sector->floorpic;
         plat->high = sec->floorheight + amount*FRACUNIT;
         plat->wait = 0;
         plat->status = up;

         S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov);
         break;

      case downWaitUpStay:
         plat->speed = PLATSPEED * 4;
         plat->low = P_FindLowestFloorSurrounding(sec);

         if (plat->low > sec->floorheight)
            plat->low = sec->floorheight;

         plat->high = sec->floorheight;
         plat->wait = 35*PLATWAIT;
         plat->status = down;
         S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart);
         break;

      case blazeDWUS:
         plat->speed = PLATSPEED * 8;
         plat->low = P_FindLowestFloorSurrounding(sec);

         if (plat->low > sec->floorheight)
            plat->low = sec->floorheight;

         plat->high = sec->floorheight;
         plat->wait = 35*PLATWAIT;
         plat->status = down;
         S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart);
         break;

      case perpetualRaise:
         plat->speed = PLATSPEED;
         plat->low = P_FindLowestFloorSurrounding(sec);

         if (plat->low > sec->floorheight)
            plat->low = sec->floorheight;

         plat->high = P_FindHighestFloorSurrounding(sec);

         if (plat->high < sec->floorheight)
            plat->high = sec->floorheight;

         plat->wait = 35*PLATWAIT;
         plat->status = P_Random(pr_plats)&1;

         S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart);
         break;

      case toggleUpDn: //jff 3/14/98 add new type to support instant toggle
         plat->speed = PLATSPEED;  //not used
         plat->wait = 35*PLATWAIT; //not used
         plat->crush = true; //jff 3/14/98 crush anything in the way

         // set up toggling between ceiling, floor inclusive
         plat->low = sec->ceilingheight;
         plat->high = sec->floorheight;
         plat->status =  down;
         break;

      default:
         break;
      }
      P_AddActivePlat(plat);  // add plat to list of active plats
   }
   return rtn;
}

// The following were all rewritten by Lee Killough
// to use the new structure which places no limits
// on active plats. It also avoids spending as much
// time searching for active plats. Previously a
// fixed-size array was used, with NULL indicating
// empty entries, while now a doubly-linked list
// is used.

//
// P_ActivateInStasis()
//
// Activate a plat that has been put in stasis
// (stopped perpetual floor, instant floor/ceil toggle)
//
// Passed the tag of the plat that should be reactivated
// Returns nothing
//
void P_ActivateInStasis(int tag)
{
   platlist_t *pl;
   for (pl=activeplats; pl; pl=pl->next)   // search the active plats
   {
      plat_t *plat = pl->plat;              // for one in stasis with right tag
      if (plat->tag == tag && plat->status == in_stasis)
      {
         if (plat->type==toggleUpDn) //jff 3/14/98 reactivate toggle type
            plat->status = plat->oldstatus==up? down : up;
         else
            plat->status = plat->oldstatus;
         plat->thinker.function = T_PlatRaise;
      }
   }
}

//
// EV_StopPlat()
//
// Handler for "stop perpetual floor" linedef type
//
// Passed the linedef that stopped the plat
// Returns true if a plat was put in stasis
//
// jff 2/12/98 added int return value, fixed return
//
int EV_StopPlat(line_t* line)
{
   platlist_t *pl;
   for (pl=activeplats; pl; pl=pl->next)  // search the active plats
   {
      plat_t *plat = pl->plat;             // for one with the tag not in stasis
      if (plat->status != in_stasis && plat->tag == line->tag)
      {
         plat->oldstatus = plat->status;    // put it in stasis
         plat->status = in_stasis;
         plat->thinker.function = NULL;
      }
   }
   return 1;
}

//
// P_AddActivePlat()
//
// Add a plat to the head of the active plat list
//
// Passed a pointer to the plat to add
// Returns nothing
//
void P_AddActivePlat(plat_t* plat)
{
   platlist_t *list = malloc(sizeof *list);
   list->plat = plat;
   plat->list = list;
   if ((list->next = activeplats))
      list->next->prev = &list->next;
   list->prev = &activeplats;
   activeplats = list;
}

//
// P_RemoveActivePlat()
//
// Remove a plat from the active plat list
//
// Passed a pointer to the plat to remove
// Returns nothing
//
void P_RemoveActivePlat(plat_t* plat)
{
   platlist_t *list = plat->list;
   plat->sector->floordata = NULL; //jff 2/23/98 multiple thinkers
   P_RemoveThinker(&plat->thinker);
   if ((*list->prev = list->next))
      list->next->prev = list->prev;
   free(list);
}

//
// P_RemoveAllActivePlats()
//
// Remove all plats from the active plat list
//
// Passed nothing, returns nothing
//
void P_RemoveAllActivePlats(void)
{
   while (activeplats)
   {
      platlist_t *next = activeplats->next;
      free(activeplats);
      activeplats = next;
   }
}
="hl opt">( t < 3600000 ) { snprintf(buf, buf_size, "%d:%02d", (int) (t / 60000), (int) (t % 60000 / 1000)); } else { snprintf(buf, buf_size, "%d:%02d:%02d", (int) (t / 3600000), (int) (t % 3600000 / 60000), (int) (t % 60000 / 1000)); } } #if CONFIG_RTC /* Create a filename with a date+time part. It is allowed that buffer and path point to the same memory location, saving a strcpy(). Path must always be given without trailing slash. unique_time as true makes the function wait until the current time has changed. */ char *create_datetime_filename(char *buffer, const char *path, const char *prefix, const char *suffix, bool unique_time) { struct tm *tm = get_time(); static struct tm last_tm; int pathlen; while (unique_time && !memcmp(get_time(), &last_tm, sizeof (struct tm))) sleep(HZ/10); last_tm = *tm; if (buffer != path) strncpy(buffer, path, MAX_PATH); pathlen = strlen(buffer); snprintf(buffer + pathlen, MAX_PATH - pathlen, "/%s%02d%02d%02d-%02d%02d%02d%s", prefix, tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, suffix); return buffer; } #endif /* CONFIG_RTC */ /* Read (up to) a line of text from fd into buffer and return number of bytes * read (which may be larger than the number of bytes stored in buffer). If * an error occurs, -1 is returned (and buffer contains whatever could be * read). A line is terminated by a LF char. Neither LF nor CR chars are * stored in buffer. */ int read_line(int fd, char* buffer, int buffer_size) { int count = 0; int num_read = 0; errno = 0; while (count < buffer_size) { unsigned char c; if (1 != read(fd, &c, 1)) break; num_read++; if ( c == '\n' ) break; if ( c == '\r' ) continue; buffer[count++] = c; } buffer[MIN(count, buffer_size - 1)] = 0; return errno ? -1 : num_read; } /* Performance optimized version of the previous function. */ int fast_readline(int fd, char *buf, int buf_size, void *parameters, int (*callback)(int n, const char *buf, void *parameters)) { char *p, *next; int rc, pos = 0; int count = 0; while ( 1 ) { next = NULL; rc = read(fd, &buf[pos], buf_size - pos - 1); if (rc >= 0) buf[pos+rc] = '\0'; if ( (p = strchr(buf, '\r')) != NULL) { *p = '\0'; next = ++p; } else p = buf; if ( (p = strchr(p, '\n')) != NULL) { *p = '\0'; next = ++p; } rc = callback(count, buf, parameters); if (rc < 0) return rc; count++; if (next) { pos = buf_size - ((long)next - (long)buf) - 1; memmove(buf, next, pos); } else break ; } return 0; } #ifdef HAVE_LCD_BITMAP #if LCD_DEPTH == 16 #define BMP_COMPRESSION 3 /* BI_BITFIELDS */ #define BMP_NUMCOLORS 3 #else #define BMP_COMPRESSION 0 /* BI_RGB */ #if LCD_DEPTH <= 8 #define BMP_NUMCOLORS (1 << LCD_DEPTH) #else #define BMP_NUMCOLORS 0 #endif #endif #if LCD_DEPTH == 1 #define BMP_BPP 1 #define BMP_LINESIZE ((LCD_WIDTH/8 + 3) & ~3) #elif LCD_DEPTH <= 4 #define BMP_BPP 4 #define BMP_LINESIZE ((LCD_WIDTH/2 + 3) & ~3) #elif LCD_DEPTH <= 8 #define BMP_BPP 8 #define BMP_LINESIZE ((LCD_WIDTH + 3) & ~3) #elif LCD_DEPTH <= 16 #define BMP_BPP 16 #define BMP_LINESIZE ((LCD_WIDTH*2 + 3) & ~3) #else #define BMP_BPP 24 #define BMP_LINESIZE ((LCD_WIDTH*3 + 3) & ~3) #endif #define BMP_HEADERSIZE (54 + 4 * BMP_NUMCOLORS) #define BMP_DATASIZE (BMP_LINESIZE * LCD_HEIGHT) #define BMP_TOTALSIZE (BMP_HEADERSIZE + BMP_DATASIZE) #define LE16_CONST(x) (x)&0xff, ((x)>>8)&0xff #define LE32_CONST(x) (x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff static const unsigned char bmpheader[] = { 0x42, 0x4d, /* 'BM' */ LE32_CONST(BMP_TOTALSIZE), /* Total file size */ 0x00, 0x00, 0x00, 0x00, /* Reserved */ LE32_CONST(BMP_HEADERSIZE), /* Offset to start of pixel data */ 0x28, 0x00, 0x00, 0x00, /* Size of (2nd) header */ LE32_CONST(LCD_WIDTH), /* Width in pixels */ LE32_CONST(LCD_HEIGHT), /* Height in pixels */ 0x01, 0x00, /* Number of planes (always 1) */ LE16_CONST(BMP_BPP), /* Bits per pixel 1/4/8/16/24 */ LE32_CONST(BMP_COMPRESSION),/* Compression mode */ LE32_CONST(BMP_DATASIZE), /* Size of bitmap data */ 0xc4, 0x0e, 0x00, 0x00, /* Horizontal resolution (pixels/meter) */ 0xc4, 0x0e, 0x00, 0x00, /* Vertical resolution (pixels/meter) */ LE32_CONST(BMP_NUMCOLORS), /* Number of used colours */ LE32_CONST(BMP_NUMCOLORS), /* Number of important colours */ #if LCD_DEPTH == 1 0x90, 0xee, 0x90, 0x00, /* Colour #0 */ 0x00, 0x00, 0x00, 0x00 /* Colour #1 */ #elif LCD_DEPTH == 2 0xe6, 0xd8, 0xad, 0x00, /* Colour #0 */ 0x99, 0x90, 0x73, 0x00, /* Colour #1 */ 0x4c, 0x48, 0x39, 0x00, /* Colour #2 */ 0x00, 0x00, 0x00, 0x00 /* Colour #3 */ #elif LCD_DEPTH == 16 0x00, 0xf8, 0x00, 0x00, /* red bitfield mask */ 0xe0, 0x07, 0x00, 0x00, /* green bitfield mask */ 0x1f, 0x00, 0x00, 0x00 /* blue bitfield mask */ #endif }; static void (*screen_dump_hook)(int fh) = NULL; void screen_dump(void) { int fh; char filename[MAX_PATH]; int bx, by; #if LCD_DEPTH == 1 static unsigned char line_block[8][BMP_LINESIZE]; #elif LCD_DEPTH == 2 #if LCD_PIXELFORMAT == HORIZONTAL_PACKING static unsigned char line_block[BMP_LINESIZE]; #else static unsigned char line_block[4][BMP_LINESIZE]; #endif #elif LCD_DEPTH == 16 static unsigned short line_block[BMP_LINESIZE/2]; #endif #if CONFIG_RTC create_datetime_filename(filename, "", "dump ", ".bmp", false); #else create_numbered_filename(filename, "", "dump_", ".bmp", 4 IF_CNFN_NUM_(, NULL)); #endif fh = creat(filename); if (fh < 0) return; if (screen_dump_hook) { screen_dump_hook(fh); } else { write(fh, bmpheader, sizeof(bmpheader)); /* BMP image goes bottom up */ #if LCD_DEPTH == 1 for (by = LCD_FBHEIGHT - 1; by >= 0; by--) { unsigned char *src = &lcd_framebuffer[by][0]; unsigned char *dst = &line_block[0][0]; memset(line_block, 0, sizeof(line_block)); for (bx = LCD_WIDTH/8; bx > 0; bx--) { unsigned dst_mask = 0x80; int ix; for (ix = 8; ix > 0; ix--) { unsigned char *dst_blk = dst; unsigned src_byte = *src++; int iy; for (iy = 8; iy > 0; iy--) { if (src_byte & 0x80) *dst_blk |= dst_mask; src_byte <<= 1; dst_blk += BMP_LINESIZE; } dst_mask >>= 1; } dst++; } write(fh, line_block, sizeof(line_block)); } #elif LCD_DEPTH == 2 #if LCD_PIXELFORMAT == HORIZONTAL_PACKING for (by = LCD_FBHEIGHT - 1; by >= 0; by--) { unsigned char *src = &lcd_framebuffer[by][0]; unsigned char *dst = line_block; memset(line_block, 0, sizeof(line_block)); for (bx = LCD_FBWIDTH; bx > 0; bx--) { unsigned src_byte = *src++; *dst++ = ((src_byte >> 2) & 0x30) | ((src_byte >> 4) & 0x03); *dst++ = ((src_byte << 2) & 0x30) | (src_byte & 0x03); } write(fh, line_block, sizeof(line_block)); } #else /* VERTICAL_PACKING */ for (by = LCD_FBHEIGHT - 1; by >= 0; by--) { unsigned char *src = &lcd_framebuffer[by][0]; unsigned char *dst = &line_block[3][0]; memset(line_block, 0, sizeof(line_block)); for (bx = LCD_WIDTH/2; bx > 0; bx--) { unsigned char *dst_blk = dst++; unsigned src_byte0 = *src++; unsigned src_byte1 = *src++; int iy; for (iy = 4; iy > 0; iy--) { *dst_blk = ((src_byte0 & 3) << 4) | (src_byte1 & 3); src_byte0 >>= 2; src_byte1 >>= 2; dst_blk -= BMP_LINESIZE; } } write(fh, line_block, sizeof(line_block)); } #endif #elif LCD_DEPTH == 16 for (by = LCD_HEIGHT - 1; by >= 0; by--) { unsigned short *src = &lcd_framebuffer[by][0]; unsigned short *dst = line_block; memset(line_block, 0, sizeof(line_block)); for (bx = LCD_WIDTH; bx > 0; bx--) { #if (LCD_PIXELFORMAT == RGB565SWAPPED) /* iPod LCD data is big endian although the CPU is not */ *dst++ = htobe16(*src++); #else *dst++ = htole16(*src++); #endif } write(fh, line_block, sizeof(line_block)); } #endif /* LCD_DEPTH */ } close(fh); } void screen_dump_set_hook(void (*hook)(int fh)) { screen_dump_hook = hook; } #endif /* HAVE_LCD_BITMAP */ /* parse a line from a configuration file. the line format is: name: value Any whitespace before setting name or value (after ':') is ignored. A # as first non-whitespace character discards the whole line. Function sets pointers to null-terminated setting name and value. Returns false if no valid config entry was found. */ bool settings_parseline(char* line, char** name, char** value) { char* ptr; while ( isspace(*line) ) line++; if ( *line == '#' ) return false; ptr = strchr(line, ':'); if ( !ptr ) return false; *name = line; *ptr = 0; ptr++; while (isspace(*ptr)) ptr++; *value = ptr; return true; } static void system_flush(void) { tree_flush(); call_ata_idle_notifys(true); /*doesnt work on usb and shutdown from ata thread */ } static void system_restore(void) { tree_restore(); } static bool clean_shutdown(void (*callback)(void *), void *parameter) { #ifdef SIMULATOR (void)callback; (void)parameter; call_ata_idle_notifys(true); exit(0); #else int i; scrobbler_poweroff(); #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) if(!charger_inserted()) #endif { bool batt_safe = battery_level_safe(); int audio_stat = audio_status(); FOR_NB_SCREENS(i) screens[i].clear_display(); #ifdef X5_BACKLIGHT_SHUTDOWN x5_backlight_shutdown(); #endif if (batt_safe) { #ifdef HAVE_TAGCACHE if (!tagcache_prepare_shutdown()) { cancel_shutdown(); gui_syncsplash(HZ, str(LANG_TAGCACHE_BUSY)); return false; } #endif if (battery_level() > 10) gui_syncsplash(0, str(LANG_SHUTTINGDOWN)); else gui_syncsplash(0, "%s %s", str(LANG_WARNING_BATTERY_LOW), str(LANG_SHUTTINGDOWN)); } else { gui_syncsplash(0, "%s %s", str(LANG_WARNING_BATTERY_EMPTY), str(LANG_SHUTTINGDOWN)); } if (global_settings.fade_on_stop && (audio_stat & AUDIO_STATUS_PLAY)) { fade(0); } if (batt_safe) /* do not save on critical battery */ { #if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC if (audio_stat & AUDIO_STATUS_RECORD) { audio_stop_recording(); /* wait for stop to complete */ while (audio_status() & AUDIO_STATUS_RECORD) sleep(1); } #endif /* audio_stop_recording == audio_stop for HWCODEC */ audio_stop(); if (callback != NULL) callback(parameter); #if CONFIG_CODEC != SWCODEC /* wait for audio_stop or audio_stop_recording to complete */ while (audio_status()) sleep(1); #endif #if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC audio_close_recording(); #endif system_flush(); #ifdef HAVE_EEPROM_SETTINGS if (firmware_settings.initialized) { firmware_settings.disk_clean = true; firmware_settings.bl_version = 0; eeprom_settings_store(); } #endif } #ifdef HAVE_DIRCACHE else dircache_disable(); #endif shutdown_hw(); } #endif return false; } bool list_stop_handler(void) { bool ret = false; /* Stop the music if it is playing */ if(audio_status()) { if (!global_settings.party_mode) { if (global_settings.fade_on_stop) fade(0); bookmark_autobookmark(); audio_stop(); } } #if CONFIG_CHARGING #if (CONFIG_KEYPAD == RECORDER_PAD) && !defined(HAVE_SW_POWEROFF) else { if (charger_inserted()) charging_splash(); else shutdown_screen(); /* won't return if shutdown actually happens */ ret = true; /* screen is dirty, caller needs to refresh */ } #endif #ifndef HAVE_POWEROFF_WHILE_CHARGING { static long last_off = 0; if (TIME_BEFORE(current_tick, last_off + HZ/2)) { if (charger_inserted()) { charging_splash(); ret = true; /* screen is dirty, caller needs to refresh */ } } last_off = current_tick; } #endif #endif /* CONFIG_CHARGING */ return ret; } #if CONFIG_CHARGING static bool waiting_to_resume_play = false; static long play_resume_tick; static void car_adapter_mode_processing(bool inserted) { if (global_settings.car_adapter_mode) { if(inserted) { /* * Just got plugged in, delay & resume if we were playing */ if (audio_status() & AUDIO_STATUS_PAUSE) { /* delay resume a bit while the engine is cranking */ play_resume_tick = current_tick + HZ*5; waiting_to_resume_play = true; } } else { /* * Just got unplugged, pause if playing */ if ((audio_status() & AUDIO_STATUS_PLAY) && !(audio_status() & AUDIO_STATUS_PAUSE)) { if (global_settings.fade_on_stop) fade(0); else audio_pause(); } } } } static void car_adapter_tick(void) { if (waiting_to_resume_play) { if (TIME_AFTER(current_tick, play_resume_tick)) { if (audio_status() & AUDIO_STATUS_PAUSE) { audio_resume(); } waiting_to_resume_play = false; } } } void car_adapter_mode_init(void) { tick_add_task(car_adapter_tick); } #endif #ifdef HAVE_HEADPHONE_DETECTION static void unplug_change(bool inserted) { if (global_settings.unplug_mode) { if (inserted) { if ( global_settings.unplug_mode > 1 ) audio_resume(); backlight_on(); } else { audio_pause(); if (global_settings.unplug_rw) { if ( audio_current_track()->elapsed > (unsigned long)(global_settings.unplug_rw*1000)) audio_ff_rewind(audio_current_track()->elapsed - (global_settings.unplug_rw*1000)); else audio_ff_rewind(0); } } } } #endif long default_event_handler_ex(long event, void (*callback)(void *), void *parameter) { switch(event) { case SYS_USB_CONNECTED: if (callback != NULL) callback(parameter); #ifdef HAVE_MMC if (!mmc_touched() || (mmc_remove_request() == SYS_HOTSWAP_EXTRACTED)) #endif { scrobbler_flush_cache(); system_flush(); #ifdef BOOTFILE #ifndef USB_IPODSTYLE check_bootfile(false); /* gets initial size */ #endif #endif usb_screen(); #ifdef BOOTFILE #ifndef USB_IPODSTYLE check_bootfile(true); #endif #endif system_restore(); } return SYS_USB_CONNECTED; case SYS_POWEROFF: if (!clean_shutdown(callback, parameter)) return SYS_POWEROFF; break; #if CONFIG_CHARGING case SYS_CHARGER_CONNECTED: car_adapter_mode_processing(true); return SYS_CHARGER_CONNECTED; case SYS_CHARGER_DISCONNECTED: car_adapter_mode_processing(false); return SYS_CHARGER_DISCONNECTED; #endif #ifdef HAVE_HEADPHONE_DETECTION case SYS_PHONE_PLUGGED: unplug_change(true); return SYS_PHONE_PLUGGED; case SYS_PHONE_UNPLUGGED: unplug_change(false); return SYS_PHONE_UNPLUGGED; #endif } return 0; } long default_event_handler(long event) { return default_event_handler_ex(event, NULL, NULL); } int show_logo( void ) { #ifdef HAVE_LCD_BITMAP char version[32]; int font_h, font_w; snprintf(version, sizeof(version), "Ver. %s", appsversion); lcd_clear_display(); lcd_bitmap(rockboxlogo, 0, 10, BMPWIDTH_rockboxlogo, BMPHEIGHT_rockboxlogo); lcd_setfont(FONT_SYSFIXED); lcd_getstringsize((unsigned char *)"A", &font_w, &font_h); lcd_putsxy((LCD_WIDTH/2) - ((strlen(version)*font_w)/2), LCD_HEIGHT-font_h, (unsigned char *)version); lcd_setfont(FONT_UI); #else char *rockbox = " ROCKbox!"; lcd_clear_display(); lcd_double_height(true); lcd_puts(0, 0, rockbox); lcd_puts_scroll(0, 1, appsversion); #endif lcd_update(); #ifdef HAVE_REMOTE_LCD lcd_remote_clear_display(); lcd_remote_bitmap(remote_rockboxlogo, 0, 10, BMPWIDTH_remote_rockboxlogo, BMPHEIGHT_remote_rockboxlogo); lcd_remote_setfont(FONT_SYSFIXED); lcd_remote_getstringsize((unsigned char *)"A", &font_w, &font_h); lcd_remote_putsxy((LCD_REMOTE_WIDTH/2) - ((strlen(version)*font_w)/2), LCD_REMOTE_HEIGHT-font_h, (unsigned char *)version); lcd_remote_setfont(FONT_UI); lcd_remote_update(); #endif return 0; } #if CONFIG_CODEC == SWCODEC int get_replaygain_mode(bool have_track_gain, bool have_album_gain) { int type; bool track = ((global_settings.replaygain_type == REPLAYGAIN_TRACK) || ((global_settings.replaygain_type == REPLAYGAIN_SHUFFLE) && global_settings.playlist_shuffle)); type = (!track && have_album_gain) ? REPLAYGAIN_ALBUM : have_track_gain ? REPLAYGAIN_TRACK : -1; return type; } #endif #ifdef BOOTFILE #ifndef USB_IPODSTYLE /* memorize/compare details about the BOOTFILE we don't use dircache because it may not be up to date after USB disconnect (scanning in the background) */ void check_bootfile(bool do_rolo) { static unsigned short wrtdate = 0; static unsigned short wrttime = 0; DIR* dir = NULL; struct dirent* entry = NULL; /* 1. open BOOTDIR and find the BOOTFILE dir entry */ dir = opendir(BOOTDIR); if(!dir) return; /* do we want an error splash? */ /* loop all files in BOOTDIR */ while(0 != (entry = readdir(dir))) { if(!strcasecmp(entry->d_name, BOOTFILE)) { /* found the bootfile */ if(wrtdate && do_rolo) { if((entry->wrtdate != wrtdate) || (entry->wrttime != wrttime)) { char *lines[] = { str(LANG_BOOT_CHANGED), str(LANG_REBOOT_NOW) }; struct text_message message={ lines, 2 }; button_clear_queue(); /* Empty the keyboard buffer */ if(gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES) rolo_load(BOOTDIR "/" BOOTFILE); } } wrtdate = entry->wrtdate; wrttime = entry->wrttime; } } closedir(dir); } #endif #endif /* check range, set volume and save settings */ void setvol(void) { const int min_vol = sound_min(SOUND_VOLUME); const int max_vol = sound_max(SOUND_VOLUME); if (global_settings.volume < min_vol) global_settings.volume = min_vol; if (global_settings.volume > max_vol) global_settings.volume = max_vol; sound_set_volume(global_settings.volume); settings_save(); } #ifdef HAVE_LCD_COLOR /* * Helper function to convert a string of 6 hex digits to a native colour */ #define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \ (toupper(c)) - 'A' + 10) int hex_to_rgb(const char* hex) { int ok = 1; int i; int red, green, blue; if (strlen(hex) == 6) { for (i=0; i < 6; i++ ) { if (!isxdigit(hex[i])) { ok=0; break; } } if (ok) { red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]); green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]); blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]); return LCD_RGBPACK(red,green,blue); } } return 0; } #endif /* HAVE_LCD_COLOR */