when changing settings from the Talk and Voice window also update the main widgets...
[Rockbox.git] / apps / plugins / iriver_flash.c
blobd9ea73b62236dfde876d170181fd8b38ebc27bd4
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 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
23 /* All CFI flash routines are copied and ported from firmware_flash.c */
25 #ifndef SIMULATOR /* only for target */
27 unsigned char *audiobuf;
28 ssize_t audiobuf_size;
30 #ifdef IRIVER_H100_SERIES
31 #define PLATFORM_ID ID_IRIVER_H100
32 #else
33 #undef PLATFORM_ID /* this platform is not (yet) flashable */
34 #endif
36 #ifdef PLATFORM_ID
38 PLUGIN_HEADER
40 #if CONFIG_KEYPAD == IRIVER_H100_PAD
41 #define KEY1 BUTTON_OFF
42 #define KEY2 BUTTON_ON
43 #define KEY3 BUTTON_SELECT
44 #define KEYNAME1 "[Stop]"
45 #define KEYNAME2 "[On]"
46 #define KEYNAME3 "[Select]"
47 #endif
49 struct flash_info
51 uint8_t manufacturer;
52 uint8_t id;
53 int size;
54 char name[32];
57 static struct plugin_api* rb; /* here is a global api struct pointer */
59 #ifdef IRIVER_H100_SERIES
60 #define SEC_SIZE 4096
61 #define BOOTLOADER_ERASEGUARD (BOOTLOADER_ENTRYPOINT / SEC_SIZE)
62 enum sections {
63 SECT_RAMIMAGE = 1,
64 SECT_ROMIMAGE = 2,
67 static volatile uint16_t* FB = (uint16_t*)0x00000000; /* Flash base address */
68 #endif
70 /* read the manufacturer and device ID */
71 bool cfi_read_id(volatile uint16_t* pBase, uint8_t* pManufacturerID, uint8_t* pDeviceID)
73 uint8_t not_manu, not_id; /* read values before switching to ID mode */
74 uint8_t manu, id; /* read values when in ID mode */
76 pBase = (uint16_t*)((uint32_t)pBase & 0xFFF80000); /* down to 512k align */
78 /* read the normal content */
79 not_manu = pBase[0]; /* should be 'A' (0x41) and 'R' (0x52) */
80 not_id = pBase[1]; /* from the "ARCH" marker */
82 pBase[0x5555] = 0xAA; /* enter command mode */
83 pBase[0x2AAA] = 0x55;
84 pBase[0x5555] = 0x90; /* ID command */
85 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
87 manu = pBase[0];
88 id = pBase[1];
90 pBase[0] = 0xF0; /* reset flash (back to normal read mode) */
91 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
93 /* I assume success if the obtained values are different from
94 the normal flash content. This is not perfectly bulletproof, they
95 could theoretically be the same by chance, causing us to fail. */
96 if (not_manu != manu || not_id != id) /* a value has changed */
98 *pManufacturerID = manu; /* return the results */
99 *pDeviceID = id;
100 return true; /* success */
102 return false; /* fail */
106 /* erase the sector which contains the given address */
107 bool cfi_erase_sector(volatile uint16_t* pAddr)
109 unsigned timeout = 430000; /* the timeout loop should be no less than 25ms */
111 FB[0x5555] = 0xAA; /* enter command mode */
112 FB[0x2AAA] = 0x55;
113 FB[0x5555] = 0x80; /* erase command */
114 FB[0x5555] = 0xAA; /* enter command mode */
115 FB[0x2AAA] = 0x55;
116 *pAddr = 0x30; /* erase the sector */
118 /* I counted 7 instructions for this loop -> min. 0.58 us per round */
119 /* Plus memory waitstates it will be much more, gives margin */
120 while (*pAddr != 0xFFFF && --timeout); /* poll for erased */
122 return (timeout != 0);
126 /* address must be in an erased location */
127 inline bool cfi_program_word(volatile uint16_t* pAddr, uint16_t data)
129 unsigned timeout = 85; /* the timeout loop should be no less than 20us */
131 if (~*pAddr & data) /* just a safety feature, not really necessary */
132 return false; /* can't set any bit from 0 to 1 */
134 FB[0x5555] = 0xAA; /* enter command mode */
135 FB[0x2AAA] = 0x55;
136 FB[0x5555] = 0xA0; /* byte program command */
138 *pAddr = data;
140 /* I counted 7 instructions for this loop -> min. 0.58 us per round */
141 /* Plus memory waitstates it will be much more, gives margin */
142 while (*pAddr != data && --timeout); /* poll for programmed */
144 return (timeout != 0);
148 /* this returns true if supported and fills the info struct */
149 bool cfi_get_flash_info(struct flash_info* pInfo)
151 rb->memset(pInfo, 0, sizeof(struct flash_info));
153 if (!cfi_read_id(FB, &pInfo->manufacturer, &pInfo->id))
154 return false;
156 if (pInfo->manufacturer == 0xBF) /* SST */
158 if (pInfo->id == 0xD6)
160 pInfo->size = 256* 1024; /* 256k */
161 rb->strcpy(pInfo->name, "SST39VF020");
162 return true;
164 else if (pInfo->id == 0xD7)
166 pInfo->size = 512* 1024; /* 512k */
167 rb->strcpy(pInfo->name, "SST39VF040");
168 return true;
170 else if (pInfo->id == 0x82)
172 pInfo->size = 2048* 1024; /* 2 MiB */
173 rb->strcpy(pInfo->name, "SST39VF160");
174 return true;
176 else
177 return false;
179 return false;
183 /*********** Utility Functions ************/
186 /* Tool function to calculate a CRC32 across some buffer */
187 /* third argument is either 0xFFFFFFFF to start or value from last piece */
188 unsigned crc_32(const unsigned char* buf, unsigned len, unsigned crc32)
190 /* CCITT standard polynomial 0x04C11DB7 */
191 static const unsigned crc32_lookup[16] =
192 { /* lookup table for 4 bits at a time is affordable */
193 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
194 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
195 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
196 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD
199 unsigned char byte;
200 unsigned t;
202 while (len--)
204 byte = *buf++; /* get one byte of data */
206 /* upper nibble of our data */
207 t = crc32 >> 28; /* extract the 4 most significant bits */
208 t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */
209 crc32 <<= 4; /* shift the CRC register left 4 bits */
210 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
212 /* lower nibble of our data */
213 t = crc32 >> 28; /* extract the 4 most significant bits */
214 t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */
215 crc32 <<= 4; /* shift the CRC register left 4 bits */
216 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
219 return crc32;
223 /***************** User Interface Functions *****************/
224 int wait_for_button(void)
226 int button;
230 button = rb->button_get(true);
231 } while (button & BUTTON_REL);
233 return button;
236 /* helper for DoUserDialog() */
237 void ShowFlashInfo(struct flash_info* pInfo)
239 char buf[32];
241 if (!pInfo->manufacturer)
243 rb->lcd_puts(0, 0, "Flash: M=?? D=??");
244 rb->lcd_puts(0, 1, "Impossible to program");
246 else
248 rb->snprintf(buf, sizeof(buf), "Flash: M=%02x D=%02x",
249 pInfo->manufacturer, pInfo->id);
250 rb->lcd_puts(0, 0, buf);
253 if (pInfo->size)
255 rb->lcd_puts(0, 1, pInfo->name);
256 rb->snprintf(buf, sizeof(buf), "Size: %d KB", pInfo->size / 1024);
257 rb->lcd_puts(0, 2, buf);
259 else
261 rb->lcd_puts(0, 1, "Unsupported chip");
266 rb->lcd_update();
269 bool show_info(void)
271 struct flash_info fi;
273 rb->lcd_clear_display();
274 cfi_get_flash_info(&fi);
275 ShowFlashInfo(&fi);
276 if (fi.size == 0) /* no valid chip */
278 rb->splash(HZ*3, "Sorry!");
279 return false; /* exit */
282 return true;
285 bool confirm(const char *msg)
287 char buf[128];
288 bool ret;
290 rb->snprintf(buf, sizeof buf, "%s ([PLAY] to CONFIRM)", msg);
291 rb->splash(0, buf);
293 ret = (wait_for_button() == BUTTON_ON);
294 show_info();
296 return ret;
299 int load_firmware_file(const char *filename, uint32_t *checksum)
301 int fd;
302 int len, rc;
303 int i;
304 uint32_t sum;
306 fd = rb->open(filename, O_RDONLY);
307 if (fd < 0)
308 return -1;
310 len = rb->filesize(fd);
312 if (audiobuf_size < len)
314 rb->splash(HZ*3, "Aborting: Out of memory!");
315 rb->close(fd);
316 return -2;
319 rb->read(fd, checksum, 4);
320 rb->lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
321 len -= FIRMWARE_OFFSET_FILE_DATA;
323 rc = rb->read(fd, audiobuf, len);
324 rb->close(fd);
325 if (rc != len)
327 rb->splash(HZ*3, "Aborting: Read failure");
328 return -3;
331 /* Verify the checksum */
332 sum = MODEL_NUMBER;
333 for (i = 0; i < len; i++)
334 sum += audiobuf[i];
336 if (sum != *checksum)
338 rb->splash(HZ*3, "Aborting: Checksums mismatch!");
339 return -4;
342 return len;
345 unsigned long valid_bootloaders[][2] = {
346 /* Size-8 CRC32 */
347 #ifdef IRIVER_H120 /* Iriver H120/H140 checksums */
348 { 63788, 0x08ff01a9 }, /* 7-pre3, improved failsafe functions */
349 { 48764, 0xc674323e }, /* 7-pre4. Fixed audio thump & remote bootup */
350 #endif
351 #ifdef IRIVER_H100
352 { 48760, 0x2efc3323 }, /* 7-pre4 */
353 #endif
354 { 0, 0 }
358 bool detect_valid_bootloader(const unsigned char *addr, int len)
360 int i;
361 unsigned long crc32;
363 /* Try to scan through all valid bootloaders. */
364 for (i = 0; valid_bootloaders[i][0]; i++)
366 if (len > 0 && len != (long)valid_bootloaders[i][0])
367 continue;
369 crc32 = crc_32(addr, valid_bootloaders[i][0], 0xffffffff);
370 if (crc32 == valid_bootloaders[i][1])
371 return true;
374 return false;
377 static int get_section_address(int section)
379 if (section == SECT_RAMIMAGE)
380 return FLASH_RAMIMAGE_ENTRY;
381 else if (section == SECT_ROMIMAGE)
382 return FLASH_ROMIMAGE_ENTRY;
383 else
384 return -1;
387 int flash_rockbox(const char *filename, int section)
389 struct flash_header hdr;
390 char buf[64];
391 int pos, i, len, rc;
392 unsigned long checksum, sum;
393 unsigned char *p8;
394 uint16_t *p16;
396 if (get_section_address(section) < 0)
397 return -1;
399 p8 = (char *)BOOTLOADER_ENTRYPOINT;
400 if (!detect_valid_bootloader(p8, 0))
402 rb->splash(HZ*3, "Incompatible bootloader");
403 return -1;
406 if (!rb->detect_original_firmware())
408 if (!confirm("Update Rockbox flash image?"))
409 return -2;
411 else
413 if (!confirm("Erase original firmware?"))
414 return -3;
417 len = load_firmware_file(filename, &checksum);
418 if (len <= 0)
419 return len * 10;
421 pos = get_section_address(section);
423 /* Check if image relocation seems to be sane. */
424 if (section == SECT_ROMIMAGE)
426 uint32_t *p32 = (uint32_t *)audiobuf;
428 if (pos+sizeof(struct flash_header) != *p32)
430 rb->snprintf(buf, sizeof(buf), "Incorrect relocation: 0x%08lx/0x%08lx",
431 *p32, pos+sizeof(struct flash_header));
432 rb->splash(HZ*10, buf);
433 return -1;
438 /* Erase the program flash. */
439 for (i = 0; i + pos < BOOTLOADER_ENTRYPOINT && i < len + 32; i += SEC_SIZE)
441 /* Additional safety check. */
442 if (i + pos < SEC_SIZE)
443 return -1;
445 rb->snprintf(buf, sizeof(buf), "Erasing... %d%%",
446 (i+SEC_SIZE)*100/len);
447 rb->lcd_puts(0, 3, buf);
448 rb->lcd_update();
450 rc = cfi_erase_sector(FB + (i + pos)/2);
453 /* Write the magic and size. */
454 rb->memset(&hdr, 0, sizeof(struct flash_header));
455 hdr.magic = FLASH_MAGIC;
456 hdr.length = len;
457 // rb->strncpy(hdr.version, APPSVERSION, sizeof(hdr.version)-1);
458 p16 = (uint16_t *)&hdr;
460 rb->snprintf(buf, sizeof(buf), "Programming...");
461 rb->lcd_puts(0, 4, buf);
462 rb->lcd_update();
464 pos = get_section_address(section)/2;
465 for (i = 0; i < (long)sizeof(struct flash_header)/2; i++)
467 cfi_program_word(FB + pos, p16[i]);
468 pos++;
471 p16 = (uint16_t *)audiobuf;
472 for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++)
474 if (i % SEC_SIZE == 0)
476 rb->snprintf(buf, sizeof(buf), "Programming... %d%%",
477 (i+1)*100/(len/2));
478 rb->lcd_puts(0, 4, buf);
479 rb->lcd_update();
482 cfi_program_word(FB + pos + i, p16[i]);
485 /* Verify */
486 rb->snprintf(buf, sizeof(buf), "Verifying");
487 rb->lcd_puts(0, 5, buf);
488 rb->lcd_update();
490 p8 = (char *)get_section_address(section);
491 p8 += sizeof(struct flash_header);
492 sum = MODEL_NUMBER;
493 for (i = 0; i < len; i++)
494 sum += p8[i];
496 if (sum != checksum)
498 rb->splash(HZ*3, "Verify failed!");
499 /* Erase the magic sector so bootloader does not try to load
500 * rockbox from flash and crash. */
501 if (section == SECT_RAMIMAGE)
502 cfi_erase_sector(FB + FLASH_RAMIMAGE_ENTRY/2);
503 else
504 cfi_erase_sector(FB + FLASH_ROMIMAGE_ENTRY/2);
505 return -5;
508 rb->splash(HZ*2, "Success");
510 return 0;
513 void show_fatal_error(void)
515 rb->splash(HZ*30, "Disable idle poweroff, connect AC power and DON'T TURN PLAYER OFF!!");
516 rb->splash(HZ*30, "Contact Rockbox developers as soon as possible!");
517 rb->splash(HZ*30, "Your device won't be bricked unless you turn off the power");
518 rb->splash(HZ*30, "Don't use the device before further instructions from Rockbox developers");
521 int flash_bootloader(const char *filename)
523 char *bootsector;
524 int pos, i, len, rc;
525 unsigned long checksum, sum;
526 unsigned char *p8;
527 uint16_t *p16;
529 bootsector = audiobuf;
530 audiobuf += SEC_SIZE;
531 audiobuf_size -= SEC_SIZE;
533 if (!confirm("Update bootloader?"))
534 return -2;
536 len = load_firmware_file(filename, &checksum);
537 if (len <= 0)
538 return len * 10;
540 if (len > 0xFFFF)
542 rb->splash(HZ*3, "Too big bootloader");
543 return -1;
546 /* Verify the crc32 checksum also. */
547 if (!detect_valid_bootloader(audiobuf, len))
549 rb->splash(HZ*3, "Incompatible/Untested bootloader");
550 return -1;
553 rb->lcd_puts(0, 3, "Flashing...");
554 rb->lcd_update();
556 /* Backup the bootloader sector first. */
557 p8 = (char *)FB;
558 rb->memcpy(bootsector, p8, SEC_SIZE);
560 /* Erase the boot sector and write a proper reset vector. */
561 cfi_erase_sector(FB);
562 p16 = (uint16_t *)audiobuf;
563 for (i = 0; i < 8/2; i++)
564 cfi_program_word(FB + i, p16[i]);
566 /* And restore original content for original FW to function. */
567 p16 = (uint16_t *)bootsector;
568 for (i = 8/2; i < SEC_SIZE/2; i++)
569 cfi_program_word(FB + i, p16[i]);
571 /* Erase the bootloader flash section. */
572 for (i = BOOTLOADER_ENTRYPOINT/SEC_SIZE; i < 0x200; i++)
573 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
575 pos = BOOTLOADER_ENTRYPOINT/2;
576 p16 = (uint16_t *)audiobuf;
577 for (i = 0; i < len/2; i++)
578 cfi_program_word(FB + pos + i, p16[i]);
580 /* Verify */
581 p8 = (char *)BOOTLOADER_ENTRYPOINT;
582 sum = MODEL_NUMBER;
583 for (i = 0; i < len; i++)
584 sum += p8[i];
586 if (sum != checksum)
588 rb->splash(HZ*3, "Verify failed!");
589 show_fatal_error();
590 return -5;
593 p8 = (char *)FB;
594 for (i = 0; i < 8; i++)
596 if (p8[i] != audiobuf[i])
598 rb->splash(HZ*3, "Bootvector corrupt!");
599 show_fatal_error();
600 return -6;
604 rb->splash(HZ*2, "Success");
606 return 0;
609 int flash_original_fw(int len)
611 unsigned char reset_vector[8];
612 char buf[32];
613 int pos, i, rc;
614 unsigned char *p8;
615 uint16_t *p16;
617 (void)buf;
619 rb->lcd_puts(0, 3, "Critical section...");
620 rb->lcd_update();
622 p8 = (char *)FB;
623 rb->memcpy(reset_vector, p8, sizeof reset_vector);
625 /* Erase the boot sector and write back the reset vector. */
626 cfi_erase_sector(FB);
627 p16 = (uint16_t *)reset_vector;
628 for (i = 0; i < (long)sizeof(reset_vector)/2; i++)
629 cfi_program_word(FB + i, p16[i]);
631 rb->lcd_puts(0, 4, "Flashing orig. FW");
632 rb->lcd_update();
634 /* Erase the program flash. */
635 for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len; i++)
637 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
638 rb->snprintf(buf, sizeof(buf), "Erase: 0x%03x (%d)", i, rc);
639 rb->lcd_puts(0, 5, buf);
640 rb->lcd_update();
643 rb->snprintf(buf, sizeof(buf), "Programming");
644 rb->lcd_puts(0, 6, buf);
645 rb->lcd_update();
647 pos = 0x00000008/2;
648 p16 = (uint16_t *)audiobuf;
649 for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++)
650 cfi_program_word(FB + pos + i, p16[i]);
652 rb->snprintf(buf, sizeof(buf), "Verifying");
653 rb->lcd_puts(0, 7, buf);
654 rb->lcd_update();
656 /* Verify reset vectors. */
657 p8 = (char *)FB;
658 for (i = 0; i < 8; i++)
660 if (p8[i] != reset_vector[i])
662 rb->splash(HZ*3, "Bootvector corrupt!");
663 show_fatal_error();
664 break;
668 /* Verify */
669 p8 = (char *)0x00000008;
670 for (i = 0; i < len; i++)
672 if (p8[i] != audiobuf[i])
674 rb->splash(HZ*3, "Verify failed!");
675 rb->snprintf(buf, sizeof buf, "at: 0x%08x", i);
676 rb->splash(HZ*10, buf);
677 return -5;
681 rb->splash(HZ*2, "Success");
683 return 0;
686 int load_original_bin(const char *filename)
688 unsigned long magic[2];
689 int len, rc;
690 int fd;
692 if (!confirm("Restore original firmware (bootloader will be kept)?"))
693 return -2;
695 fd = rb->open(filename, O_RDONLY);
696 if (fd < 0)
697 return -1;
699 len = rb->filesize(fd) - 0x228;
700 rb->lseek(fd, 0x220, SEEK_SET);
701 rb->read(fd, magic, 8);
702 if (magic[1] != 0x00000008 || len <= 0 || len > audiobuf_size)
704 rb->splash(HZ*2, "Not an original firmware file");
705 rb->close(fd);
706 return -1;
709 rc = rb->read(fd, audiobuf, len);
710 rb->close(fd);
712 if (rc != len)
714 rb->splash(HZ*2, "Read error");
715 return -2;
718 if (len % 2)
719 len++;
721 return flash_original_fw(len);
724 int load_romdump(const char *filename)
726 int len, rc;
727 int fd;
729 if (!confirm("Restore firmware section (bootloader will be kept)?"))
730 return -2;
732 fd = rb->open(filename, O_RDONLY);
733 if (fd < 0)
734 return -1;
736 len = rb->filesize(fd) - 8;
737 if (len <= 0)
738 return -1;
740 rb->lseek(fd, 8, SEEK_SET);
741 rc = rb->read(fd, audiobuf, len);
742 rb->close(fd);
744 if (rc != len)
746 rb->splash(HZ*2, "Read error");
747 return -2;
750 if (len % 2)
751 len++;
753 if (len > BOOTLOADER_ENTRYPOINT - 8)
754 len = BOOTLOADER_ENTRYPOINT - 8;
756 return flash_original_fw(len);
759 /* Kind of our main function, defines the application flow. */
760 void DoUserDialog(char* filename)
762 /* this can only work if Rockbox runs in DRAM, not flash ROM */
763 if ((uint16_t*)rb >= FB && (uint16_t*)rb < FB + 4096*1024) /* 4 MB max */
764 { /* we're running from flash */
765 rb->splash(HZ*3, "Not from ROM");
766 return; /* exit */
769 /* refuse to work if the power may fail meanwhile */
770 if (!rb->battery_level_safe())
772 rb->splash(HZ*3, "Battery too low!");
773 return; /* exit */
776 rb->lcd_setfont(FONT_SYSFIXED);
777 if (!show_info())
778 return ;
780 if (filename == NULL)
782 rb->splash(HZ*3, "Please use this plugin with \"Open with...\"");
783 return ;
786 audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuf_size);
788 if (rb->strcasestr(filename, "/rockbox.iriver"))
789 flash_rockbox(filename, SECT_RAMIMAGE);
790 else if (rb->strcasestr(filename, "/rombox.iriver"))
791 flash_rockbox(filename, SECT_ROMIMAGE);
792 else if (rb->strcasestr(filename, "/bootloader.iriver"))
793 flash_bootloader(filename);
794 else if (rb->strcasestr(filename, "/ihp_120.bin"))
795 load_original_bin(filename);
796 else if (rb->strcasestr(filename, "/internal_rom_000000-1FFFFF.bin"))
797 load_romdump(filename);
798 else
799 rb->splash(HZ*3, "Unknown file type");
803 /***************** Plugin Entry Point *****************/
805 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
807 int oldmode;
809 rb = api; /* copy to global api pointer */
811 /* now go ahead and have fun! */
812 oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */
813 DoUserDialog((char*) parameter);
814 rb->system_memory_guard(oldmode); /* re-enable memory guard */
816 return PLUGIN_OK;
819 #endif /* ifdef PLATFORM_ID */
820 #endif /* #ifndef SIMULATOR */