Don't objcopy simulator plugins.
[kugel-rb.git] / apps / plugins / iriver_flash.c
blob9f26f82d13f6452d6d8ce5c7093f2c064ffc85b2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SURE WHAT YOU DO !!!
12 * Copyright (C) 2006 by Miika Pekkarinen
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "plugin.h"
25 /* All CFI flash routines are copied and ported from firmware_flash.c */
27 #ifndef SIMULATOR /* only for target */
29 unsigned char *audiobuf;
30 ssize_t audiobuf_size;
32 #ifdef IRIVER_H100_SERIES
33 #define PLATFORM_ID ID_IRIVER_H100
34 #else
35 #undef PLATFORM_ID /* this platform is not (yet) flashable */
36 #endif
38 #ifdef PLATFORM_ID
40 PLUGIN_HEADER
42 #if CONFIG_KEYPAD == IRIVER_H100_PAD
43 #define KEY1 BUTTON_OFF
44 #define KEY2 BUTTON_ON
45 #define KEY3 BUTTON_SELECT
46 #define KEYNAME1 "[Stop]"
47 #define KEYNAME2 "[On]"
48 #define KEYNAME3 "[Select]"
49 #endif
51 struct flash_info
53 uint8_t manufacturer;
54 uint8_t id;
55 int size;
56 char name[32];
59 static const struct plugin_api* rb; /* here is a global api struct pointer */
61 #ifdef IRIVER_H100_SERIES
62 #define SEC_SIZE 4096
63 #define BOOTLOADER_ERASEGUARD (BOOTLOADER_ENTRYPOINT / SEC_SIZE)
64 enum sections {
65 SECT_RAMIMAGE = 1,
66 SECT_ROMIMAGE = 2,
69 static volatile uint16_t* FB = (uint16_t*)0x00000000; /* Flash base address */
70 #endif
72 /* read the manufacturer and device ID */
73 bool cfi_read_id(volatile uint16_t* pBase, uint8_t* pManufacturerID, uint8_t* pDeviceID)
75 uint8_t not_manu, not_id; /* read values before switching to ID mode */
76 uint8_t manu, id; /* read values when in ID mode */
78 pBase = (uint16_t*)((uint32_t)pBase & 0xFFF80000); /* down to 512k align */
80 /* read the normal content */
81 not_manu = pBase[0]; /* should be 'A' (0x41) and 'R' (0x52) */
82 not_id = pBase[1]; /* from the "ARCH" marker */
84 pBase[0x5555] = 0xAA; /* enter command mode */
85 pBase[0x2AAA] = 0x55;
86 pBase[0x5555] = 0x90; /* ID command */
87 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
89 manu = pBase[0];
90 id = pBase[1];
92 pBase[0] = 0xF0; /* reset flash (back to normal read mode) */
93 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
95 /* I assume success if the obtained values are different from
96 the normal flash content. This is not perfectly bulletproof, they
97 could theoretically be the same by chance, causing us to fail. */
98 if (not_manu != manu || not_id != id) /* a value has changed */
100 *pManufacturerID = manu; /* return the results */
101 *pDeviceID = id;
102 return true; /* success */
104 return false; /* fail */
108 /* erase the sector which contains the given address */
109 bool cfi_erase_sector(volatile uint16_t* pAddr)
111 unsigned timeout = 430000; /* the timeout loop should be no less than 25ms */
113 FB[0x5555] = 0xAA; /* enter command mode */
114 FB[0x2AAA] = 0x55;
115 FB[0x5555] = 0x80; /* erase command */
116 FB[0x5555] = 0xAA; /* enter command mode */
117 FB[0x2AAA] = 0x55;
118 *pAddr = 0x30; /* erase the sector */
120 /* I counted 7 instructions for this loop -> min. 0.58 us per round */
121 /* Plus memory waitstates it will be much more, gives margin */
122 while (*pAddr != 0xFFFF && --timeout); /* poll for erased */
124 return (timeout != 0);
128 /* address must be in an erased location */
129 inline bool cfi_program_word(volatile uint16_t* pAddr, uint16_t data)
131 unsigned timeout = 85; /* the timeout loop should be no less than 20us */
133 if (~*pAddr & data) /* just a safety feature, not really necessary */
134 return false; /* can't set any bit from 0 to 1 */
136 FB[0x5555] = 0xAA; /* enter command mode */
137 FB[0x2AAA] = 0x55;
138 FB[0x5555] = 0xA0; /* byte program command */
140 *pAddr = data;
142 /* I counted 7 instructions for this loop -> min. 0.58 us per round */
143 /* Plus memory waitstates it will be much more, gives margin */
144 while (*pAddr != data && --timeout); /* poll for programmed */
146 return (timeout != 0);
150 /* this returns true if supported and fills the info struct */
151 bool cfi_get_flash_info(struct flash_info* pInfo)
153 rb->memset(pInfo, 0, sizeof(struct flash_info));
155 if (!cfi_read_id(FB, &pInfo->manufacturer, &pInfo->id))
156 return false;
158 if (pInfo->manufacturer == 0xBF) /* SST */
160 if (pInfo->id == 0xD6)
162 pInfo->size = 256* 1024; /* 256k */
163 rb->strcpy(pInfo->name, "SST39VF020");
164 return true;
166 else if (pInfo->id == 0xD7)
168 pInfo->size = 512* 1024; /* 512k */
169 rb->strcpy(pInfo->name, "SST39VF040");
170 return true;
172 else if (pInfo->id == 0x82)
174 pInfo->size = 2048* 1024; /* 2 MiB */
175 rb->strcpy(pInfo->name, "SST39VF160");
176 return true;
178 else
179 return false;
181 return false;
185 /*********** Utility Functions ************/
188 /* Tool function to calculate a CRC32 across some buffer */
189 /* third argument is either 0xFFFFFFFF to start or value from last piece */
190 unsigned crc_32(const unsigned char* buf, unsigned len, unsigned crc32)
192 /* CCITT standard polynomial 0x04C11DB7 */
193 static const unsigned crc32_lookup[16] =
194 { /* lookup table for 4 bits at a time is affordable */
195 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
196 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
197 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
198 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD
201 unsigned char byte;
202 unsigned t;
204 while (len--)
206 byte = *buf++; /* get one byte of data */
208 /* upper nibble of our data */
209 t = crc32 >> 28; /* extract the 4 most significant bits */
210 t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */
211 crc32 <<= 4; /* shift the CRC register left 4 bits */
212 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
214 /* lower nibble of our data */
215 t = crc32 >> 28; /* extract the 4 most significant bits */
216 t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */
217 crc32 <<= 4; /* shift the CRC register left 4 bits */
218 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
221 return crc32;
225 /***************** User Interface Functions *****************/
226 int wait_for_button(void)
228 int button;
232 button = rb->button_get(true);
233 } while (button & BUTTON_REL);
235 return button;
238 /* helper for DoUserDialog() */
239 void ShowFlashInfo(struct flash_info* pInfo)
241 char buf[32];
243 if (!pInfo->manufacturer)
245 rb->lcd_puts(0, 0, "Flash: M=?? D=??");
246 rb->lcd_puts(0, 1, "Impossible to program");
248 else
250 rb->snprintf(buf, sizeof(buf), "Flash: M=%02x D=%02x",
251 pInfo->manufacturer, pInfo->id);
252 rb->lcd_puts(0, 0, buf);
255 if (pInfo->size)
257 rb->lcd_puts(0, 1, pInfo->name);
258 rb->snprintf(buf, sizeof(buf), "Size: %d KB", pInfo->size / 1024);
259 rb->lcd_puts(0, 2, buf);
261 else
263 rb->lcd_puts(0, 1, "Unsupported chip");
268 rb->lcd_update();
271 bool show_info(void)
273 struct flash_info fi;
275 rb->lcd_clear_display();
276 cfi_get_flash_info(&fi);
277 ShowFlashInfo(&fi);
278 if (fi.size == 0) /* no valid chip */
280 rb->splash(HZ*3, "Sorry!");
281 return false; /* exit */
284 return true;
287 bool confirm(const char *msg)
289 char buf[128];
290 bool ret;
292 rb->snprintf(buf, sizeof buf, "%s ([PLAY] to CONFIRM)", msg);
293 rb->splash(0, buf);
295 ret = (wait_for_button() == BUTTON_ON);
296 show_info();
298 return ret;
301 int load_firmware_file(const char *filename, uint32_t *checksum)
303 int fd;
304 int len, rc;
305 int i;
306 uint32_t sum;
308 fd = rb->open(filename, O_RDONLY);
309 if (fd < 0)
310 return -1;
312 len = rb->filesize(fd);
314 if (audiobuf_size < len)
316 rb->splash(HZ*3, "Aborting: Out of memory!");
317 rb->close(fd);
318 return -2;
321 rb->read(fd, checksum, 4);
322 rb->lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
323 len -= FIRMWARE_OFFSET_FILE_DATA;
325 rc = rb->read(fd, audiobuf, len);
326 rb->close(fd);
327 if (rc != len)
329 rb->splash(HZ*3, "Aborting: Read failure");
330 return -3;
333 /* Verify the checksum */
334 sum = MODEL_NUMBER;
335 for (i = 0; i < len; i++)
336 sum += audiobuf[i];
338 if (sum != *checksum)
340 rb->splash(HZ*3, "Aborting: Checksums mismatch!");
341 return -4;
344 return len;
347 unsigned long valid_bootloaders[][2] = {
348 /* Size-8 CRC32 */
349 #ifdef IRIVER_H120 /* Iriver H120/H140 checksums */
350 { 63788, 0x08ff01a9 }, /* 7-pre3, improved failsafe functions */
351 { 48764, 0xc674323e }, /* 7-pre4. Fixed audio thump & remote bootup */
352 #endif
353 #ifdef IRIVER_H100
354 { 48760, 0x2efc3323 }, /* 7-pre4 */
355 #endif
356 { 0, 0 }
360 bool detect_valid_bootloader(const unsigned char *addr, int len)
362 int i;
363 unsigned long crc32;
365 /* Try to scan through all valid bootloaders. */
366 for (i = 0; valid_bootloaders[i][0]; i++)
368 if (len > 0 && len != (long)valid_bootloaders[i][0])
369 continue;
371 crc32 = crc_32(addr, valid_bootloaders[i][0], 0xffffffff);
372 if (crc32 == valid_bootloaders[i][1])
373 return true;
376 return false;
379 static int get_section_address(int section)
381 if (section == SECT_RAMIMAGE)
382 return FLASH_RAMIMAGE_ENTRY;
383 else if (section == SECT_ROMIMAGE)
384 return FLASH_ROMIMAGE_ENTRY;
385 else
386 return -1;
389 int flash_rockbox(const char *filename, int section)
391 struct flash_header hdr;
392 char buf[64];
393 int pos, i, len, rc;
394 unsigned long checksum, sum;
395 unsigned char *p8;
396 uint16_t *p16;
398 if (get_section_address(section) < 0)
399 return -1;
401 p8 = (char *)BOOTLOADER_ENTRYPOINT;
402 if (!detect_valid_bootloader(p8, 0))
404 rb->splash(HZ*3, "Incompatible bootloader");
405 return -1;
408 if (!rb->detect_original_firmware())
410 if (!confirm("Update Rockbox flash image?"))
411 return -2;
413 else
415 if (!confirm("Erase original firmware?"))
416 return -3;
419 len = load_firmware_file(filename, &checksum);
420 if (len <= 0)
421 return len * 10;
423 pos = get_section_address(section);
425 /* Check if image relocation seems to be sane. */
426 if (section == SECT_ROMIMAGE)
428 uint32_t *p32 = (uint32_t *)audiobuf;
430 if (pos+sizeof(struct flash_header) != *p32)
432 rb->snprintf(buf, sizeof(buf), "Incorrect relocation: 0x%08lx/0x%08lx",
433 *p32, pos+sizeof(struct flash_header));
434 rb->splash(HZ*10, buf);
435 return -1;
440 /* Erase the program flash. */
441 for (i = 0; i + pos < BOOTLOADER_ENTRYPOINT && i < len + 32; i += SEC_SIZE)
443 /* Additional safety check. */
444 if (i + pos < SEC_SIZE)
445 return -1;
447 rb->snprintf(buf, sizeof(buf), "Erasing... %d%%",
448 (i+SEC_SIZE)*100/len);
449 rb->lcd_puts(0, 3, buf);
450 rb->lcd_update();
452 rc = cfi_erase_sector(FB + (i + pos)/2);
455 /* Write the magic and size. */
456 rb->memset(&hdr, 0, sizeof(struct flash_header));
457 hdr.magic = FLASH_MAGIC;
458 hdr.length = len;
459 // rb->strncpy(hdr.version, APPSVERSION, sizeof(hdr.version)-1);
460 p16 = (uint16_t *)&hdr;
462 rb->snprintf(buf, sizeof(buf), "Programming...");
463 rb->lcd_puts(0, 4, buf);
464 rb->lcd_update();
466 pos = get_section_address(section)/2;
467 for (i = 0; i < (long)sizeof(struct flash_header)/2; i++)
469 cfi_program_word(FB + pos, p16[i]);
470 pos++;
473 p16 = (uint16_t *)audiobuf;
474 for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++)
476 if (i % SEC_SIZE == 0)
478 rb->snprintf(buf, sizeof(buf), "Programming... %d%%",
479 (i+1)*100/(len/2));
480 rb->lcd_puts(0, 4, buf);
481 rb->lcd_update();
484 cfi_program_word(FB + pos + i, p16[i]);
487 /* Verify */
488 rb->snprintf(buf, sizeof(buf), "Verifying");
489 rb->lcd_puts(0, 5, buf);
490 rb->lcd_update();
492 p8 = (char *)get_section_address(section);
493 p8 += sizeof(struct flash_header);
494 sum = MODEL_NUMBER;
495 for (i = 0; i < len; i++)
496 sum += p8[i];
498 if (sum != checksum)
500 rb->splash(HZ*3, "Verify failed!");
501 /* Erase the magic sector so bootloader does not try to load
502 * rockbox from flash and crash. */
503 if (section == SECT_RAMIMAGE)
504 cfi_erase_sector(FB + FLASH_RAMIMAGE_ENTRY/2);
505 else
506 cfi_erase_sector(FB + FLASH_ROMIMAGE_ENTRY/2);
507 return -5;
510 rb->splash(HZ*2, "Success");
512 return 0;
515 void show_fatal_error(void)
517 rb->splash(HZ*30, "Disable idle poweroff, connect AC power and DON'T TURN PLAYER OFF!!");
518 rb->splash(HZ*30, "Contact Rockbox developers as soon as possible!");
519 rb->splash(HZ*30, "Your device won't be bricked unless you turn off the power");
520 rb->splash(HZ*30, "Don't use the device before further instructions from Rockbox developers");
523 int flash_bootloader(const char *filename)
525 char *bootsector;
526 int pos, i, len, rc;
527 unsigned long checksum, sum;
528 unsigned char *p8;
529 uint16_t *p16;
531 bootsector = audiobuf;
532 audiobuf += SEC_SIZE;
533 audiobuf_size -= SEC_SIZE;
535 if (!confirm("Update bootloader?"))
536 return -2;
538 len = load_firmware_file(filename, &checksum);
539 if (len <= 0)
540 return len * 10;
542 if (len > 0xFFFF)
544 rb->splash(HZ*3, "Too big bootloader");
545 return -1;
548 /* Verify the crc32 checksum also. */
549 if (!detect_valid_bootloader(audiobuf, len))
551 rb->splash(HZ*3, "Incompatible/Untested bootloader");
552 return -1;
555 rb->lcd_puts(0, 3, "Flashing...");
556 rb->lcd_update();
558 /* Backup the bootloader sector first. */
559 p8 = (char *)FB;
560 rb->memcpy(bootsector, p8, SEC_SIZE);
562 /* Erase the boot sector and write a proper reset vector. */
563 cfi_erase_sector(FB);
564 p16 = (uint16_t *)audiobuf;
565 for (i = 0; i < 8/2; i++)
566 cfi_program_word(FB + i, p16[i]);
568 /* And restore original content for original FW to function. */
569 p16 = (uint16_t *)bootsector;
570 for (i = 8/2; i < SEC_SIZE/2; i++)
571 cfi_program_word(FB + i, p16[i]);
573 /* Erase the bootloader flash section. */
574 for (i = BOOTLOADER_ENTRYPOINT/SEC_SIZE; i < 0x200; i++)
575 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
577 pos = BOOTLOADER_ENTRYPOINT/2;
578 p16 = (uint16_t *)audiobuf;
579 for (i = 0; i < len/2; i++)
580 cfi_program_word(FB + pos + i, p16[i]);
582 /* Verify */
583 p8 = (char *)BOOTLOADER_ENTRYPOINT;
584 sum = MODEL_NUMBER;
585 for (i = 0; i < len; i++)
586 sum += p8[i];
588 if (sum != checksum)
590 rb->splash(HZ*3, "Verify failed!");
591 show_fatal_error();
592 return -5;
595 p8 = (char *)FB;
596 for (i = 0; i < 8; i++)
598 if (p8[i] != audiobuf[i])
600 rb->splash(HZ*3, "Bootvector corrupt!");
601 show_fatal_error();
602 return -6;
606 rb->splash(HZ*2, "Success");
608 return 0;
611 int flash_original_fw(int len)
613 unsigned char reset_vector[8];
614 char buf[32];
615 int pos, i, rc;
616 unsigned char *p8;
617 uint16_t *p16;
619 (void)buf;
621 rb->lcd_puts(0, 3, "Critical section...");
622 rb->lcd_update();
624 p8 = (char *)FB;
625 rb->memcpy(reset_vector, p8, sizeof reset_vector);
627 /* Erase the boot sector and write back the reset vector. */
628 cfi_erase_sector(FB);
629 p16 = (uint16_t *)reset_vector;
630 for (i = 0; i < (long)sizeof(reset_vector)/2; i++)
631 cfi_program_word(FB + i, p16[i]);
633 rb->lcd_puts(0, 4, "Flashing orig. FW");
634 rb->lcd_update();
636 /* Erase the program flash. */
637 for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len; i++)
639 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
640 rb->snprintf(buf, sizeof(buf), "Erase: 0x%03x (%d)", i, rc);
641 rb->lcd_puts(0, 5, buf);
642 rb->lcd_update();
645 rb->snprintf(buf, sizeof(buf), "Programming");
646 rb->lcd_puts(0, 6, buf);
647 rb->lcd_update();
649 pos = 0x00000008/2;
650 p16 = (uint16_t *)audiobuf;
651 for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++)
652 cfi_program_word(FB + pos + i, p16[i]);
654 rb->snprintf(buf, sizeof(buf), "Verifying");
655 rb->lcd_puts(0, 7, buf);
656 rb->lcd_update();
658 /* Verify reset vectors. */
659 p8 = (char *)FB;
660 for (i = 0; i < 8; i++)
662 if (p8[i] != reset_vector[i])
664 rb->splash(HZ*3, "Bootvector corrupt!");
665 show_fatal_error();
666 break;
670 /* Verify */
671 p8 = (char *)0x00000008;
672 for (i = 0; i < len; i++)
674 if (p8[i] != audiobuf[i])
676 rb->splash(HZ*3, "Verify failed!");
677 rb->snprintf(buf, sizeof buf, "at: 0x%08x", i);
678 rb->splash(HZ*10, buf);
679 return -5;
683 rb->splash(HZ*2, "Success");
685 return 0;
688 int load_original_bin(const char *filename)
690 unsigned long magic[2];
691 int len, rc;
692 int fd;
694 if (!confirm("Restore original firmware (bootloader will be kept)?"))
695 return -2;
697 fd = rb->open(filename, O_RDONLY);
698 if (fd < 0)
699 return -1;
701 len = rb->filesize(fd) - 0x228;
702 rb->lseek(fd, 0x220, SEEK_SET);
703 rb->read(fd, magic, 8);
704 if (magic[1] != 0x00000008 || len <= 0 || len > audiobuf_size)
706 rb->splash(HZ*2, "Not an original firmware file");
707 rb->close(fd);
708 return -1;
711 rc = rb->read(fd, audiobuf, len);
712 rb->close(fd);
714 if (rc != len)
716 rb->splash(HZ*2, "Read error");
717 return -2;
720 if (len % 2)
721 len++;
723 return flash_original_fw(len);
726 int load_romdump(const char *filename)
728 int len, rc;
729 int fd;
731 if (!confirm("Restore firmware section (bootloader will be kept)?"))
732 return -2;
734 fd = rb->open(filename, O_RDONLY);
735 if (fd < 0)
736 return -1;
738 len = rb->filesize(fd) - 8;
739 if (len <= 0)
740 return -1;
742 rb->lseek(fd, 8, SEEK_SET);
743 rc = rb->read(fd, audiobuf, len);
744 rb->close(fd);
746 if (rc != len)
748 rb->splash(HZ*2, "Read error");
749 return -2;
752 if (len % 2)
753 len++;
755 if (len > BOOTLOADER_ENTRYPOINT - 8)
756 len = BOOTLOADER_ENTRYPOINT - 8;
758 return flash_original_fw(len);
761 /* Kind of our main function, defines the application flow. */
762 void DoUserDialog(char* filename)
764 /* this can only work if Rockbox runs in DRAM, not flash ROM */
765 if ((uint16_t*)rb >= FB && (uint16_t*)rb < FB + 4096*1024) /* 4 MB max */
766 { /* we're running from flash */
767 rb->splash(HZ*3, "Not from ROM");
768 return; /* exit */
771 /* refuse to work if the power may fail meanwhile */
772 if (!rb->battery_level_safe())
774 rb->splash(HZ*3, "Battery too low!");
775 return; /* exit */
778 rb->lcd_setfont(FONT_SYSFIXED);
779 if (!show_info())
780 return ;
782 if (filename == NULL)
784 rb->splash(HZ*3, "Please use this plugin with \"Open with...\"");
785 return ;
788 audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuf_size);
790 if (rb->strcasestr(filename, "/rockbox.iriver"))
791 flash_rockbox(filename, SECT_RAMIMAGE);
792 else if (rb->strcasestr(filename, "/rombox.iriver"))
793 flash_rockbox(filename, SECT_ROMIMAGE);
794 else if (rb->strcasestr(filename, "/bootloader.iriver"))
795 flash_bootloader(filename);
796 else if (rb->strcasestr(filename, "/ihp_120.bin"))
797 load_original_bin(filename);
798 else if (rb->strcasestr(filename, "/internal_rom_000000-1FFFFF.bin"))
799 load_romdump(filename);
800 else
801 rb->splash(HZ*3, "Unknown file type");
805 /***************** Plugin Entry Point *****************/
807 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
809 int oldmode;
811 rb = api; /* copy to global api pointer */
813 /* now go ahead and have fun! */
814 oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */
815 DoUserDialog((char*) parameter);
816 rb->system_memory_guard(oldmode); /* re-enable memory guard */
818 return PLUGIN_OK;
821 #endif /* ifdef PLATFORM_ID */
822 #endif /* #ifndef SIMULATOR */