/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #include "lib/configfile.h" /* taken from apps/gui/wps_parser.c */ #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps" #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps" #define CONFIG_FILENAME "theme_remove.cfg" #define LOG_FILENAME "/theme_remove_log.txt" #define RB_FONTS_CONFIG VIEWERS_DIR "/rockbox-fonts.config" enum remove_option { ALWAYS_REMOVE, NEVER_REMOVE, REMOVE_IF_NOT_USED, ASK_FOR_REMOVAL, NUM_REMOVE_OPTION }; struct remove_setting { const char *name; const char *prefix, *suffix; char value[MAX_PATH]; int option; int (*func)(struct remove_setting *); bool used; }; static int remove_wps(struct remove_setting *); #ifdef HAVE_LCD_BITMAP static int remove_icons(struct remove_setting *setting); #endif enum remove_settings { #ifdef HAVE_LCD_BITMAP REMOVE_FONT, #endif REMOVE_WPS, #ifdef HAVE_LCD_BITMAP REMOVE_SBS, #endif #ifdef HAVE_REMOTE_LCD REMOVE_RWPS, REMOVE_RSBS, #endif #if LCD_DEPTH > 1 REMOVE_BACKDROP, #endif #ifdef HAVE_LCD_BITMAP REMOVE_ICON, REMOVE_VICON, #endif #ifdef HAVE_REMOTE_LCD REMOVE_RICON, REMOVE_RVICON, #endif #ifdef HAVE_LCD_COLOR REMOVE_COLOURS, #endif NUM_REMOVE_ITEMS }; static bool create_log = true; static struct remove_setting remove_list[NUM_REMOVE_ITEMS] = { #ifdef HAVE_LCD_BITMAP [REMOVE_FONT] = { "font", FONT_DIR "/", ".fnt", "", ASK_FOR_REMOVAL, NULL, false }, #endif [REMOVE_WPS] = { "wps", WPS_DIR "/", ".wps", "", REMOVE_IF_NOT_USED, remove_wps, false }, #ifdef HAVE_LCD_BITMAP [REMOVE_SBS] = { "sbs", SBS_DIR "/", ".sbs", "", REMOVE_IF_NOT_USED, remove_wps, false }, #endif #ifdef HAVE_REMOTE_LCD [REMOVE_RWPS] = { "rwps", WPS_DIR "/", ".rwps", "", REMOVE_IF_NOT_USED, remove_wps, false }, [REMOVE_RSBS] = { "rsbs", SBS_DIR "/", ".rsbs", "", REMOVE_IF_NOT_USED, remove_wps, false }, #endif #if LCD_DEPTH > 1 [REMOVE_BACKDROP] = { "backdrop", BACKDROP_DIR "/", ".bmp", "", REMOVE_IF_NOT_USED, NULL, false }, #endif #ifdef HAVE_LCD_BITMAP [REMOVE_ICON] = { "iconset", ICON_DIR "/", ".bmp", "", ASK_FOR_REMOVAL, NULL, false }, [REMOVE_VICON] = { "viewers iconset", ICON_DIR "/", ".bmp", "", ASK_FOR_REMOVAL, remove_icons, false }, #endif #ifdef HAVE_REMOTE_LCD [REMOVE_RICON] = { "remote iconset", ICON_DIR "/", ".bmp", "", ASK_FOR_REMOVAL, NULL, false }, [REMOVE_RVICON] = { "remote viewers iconset", ICON_DIR "/", ".bmp", "", ASK_FOR_REMOVAL, NULL, false }, #endif #ifdef HAVE_LCD_COLOR [REMOVE_COLOURS] = { "filetype colours", THEME_DIR "/", ".colours", "", ASK_FOR_REMOVAL, NULL, false }, #endif }; static char *option_names[NUM_REMOVE_OPTION] = { "always", "never", "not used", "ask", }; static struct configdata config[] = { #ifdef HAVE_LCD_BITMAP { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_FONT].option }, "remove font", option_names }, #endif { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_WPS].option }, "remove wps", option_names }, #ifdef HAVE_LCD_BITMAP { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_SBS].option }, "remove sbs", option_names }, #endif #ifdef HAVE_REMOTE_LCD { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_RWPS].option }, "remove rwps", option_names }, { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_RSBS].option }, "remove rsbs", option_names }, #endif #if LCD_DEPTH > 1 { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_BACKDROP].option }, "remove backdrop", option_names }, #endif #ifdef HAVE_LCD_BITMAP { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_ICON].option }, "remove iconset", option_names }, { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_VICON].option }, "remove viconset", option_names }, #endif #ifdef HAVE_REMOTE_LCD { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_RICON].option }, "remove riconset", option_names }, { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_RVICON].option }, "remove rviconset", option_names }, #endif #ifdef HAVE_LCD_COLOR { TYPE_INT, 0, NUM_REMOVE_OPTION, { .int_p = &remove_list[REMOVE_COLOURS].option }, "remove colours", option_names }, #endif {TYPE_BOOL, 0, 1, { .bool_p = &create_log }, "create log", NULL}, }; static const int nb_config = sizeof(config)/sizeof(*config); static char themefile[MAX_PATH]; static int log_fd = -1; static int show_mess(const char *text, const char *file) { static char buf[MAX_PATH*2]; if (file) rb->snprintf(buf, sizeof(buf), "%s: %s", text, file); else rb->snprintf(buf, sizeof(buf), "%s", text); DEBUGF("%s\n", buf); if (log_fd >= 0) rb->fdprintf(log_fd, "%s\n", buf); rb->splash(0, buf); rb->sleep(HZ/4); return 0; } /* set full path of file. */ static void set_file_name(char *buf, const char*file, struct remove_setting *setting) { int len1, len2; if (rb->strncasecmp(file, setting->prefix, rb->strlen(setting->prefix))) rb->snprintf(buf, MAX_PATH, "%s%s", setting->prefix, file); else rb->strlcpy(buf, file, MAX_PATH); len1 = rb->strlen(buf); len2 = rb->strlen(setting->suffix); if (rb->strcasecmp(buf+len1-len2, setting->suffix)) rb->strlcpy(&buf[len1], setting->suffix, MAX_PATH-len1); } /* taken from apps/onplay.c */ /* helper function to remove a non-empty directory */ static int remove_dir(char* dirname, int len) { int result = 0; DIR* dir; int dirlen = rb->strlen(dirname); dir = rb->opendir(dirname); if (!dir) return -1; /* open error */ while (true) { struct dirent* entry; /* walk through the directory content */ entry = rb->readdir(dir); if (!entry) break; dirname[dirlen] ='\0'; /* append name to current directory */ rb->snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name); struct dirinfo info = rb->dir_get_info(dir, entry); if (info.attribute & ATTR_DIRECTORY) { /* remove a subdirectory */ if (!rb->strcmp((char *)entry->d_name, ".") || !rb->strcmp((char *)entry->d_name, "..")) continue; /* skip these */ result = remove_dir(dirname, len); /* recursion */ if (result) break; } else { /* remove a file */ result = rb->remove(dirname); } if (ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK)) { show_mess("Canceled", NULL); result = -1; break; } } rb->closedir(dir); if (!result) { /* remove the now empty directory */ dirname[dirlen] = '\0'; /* terminate to original length */ result = rb->rmdir(dirname); show_mess("Removed", dirname); } return result; } static int remove_wps(struct remove_setting *setting) { char bmpdir[MAX_PATH]; char *p; rb->strcpy(bmpdir, setting->value); p = rb->strrchr(bmpdir, '.'); if (p) *p = 0; if (!rb->dir_exists(bmpdir)) return 0; return remove_dir(bmpdir, MAX_PATH); } #ifdef HAVE_LCD_BITMAP static int remove_icons(struct remove_setting *setting) { char path[MAX_PATH]; char *p; rb->strcpy(path, setting->value); p = rb->strrchr(path, '.'); rb->strlcpy(p, ".icons", path+MAX_PATH-p); if (!rb->file_exists(path)) { return 0; } if (rb->remove(path)) { show_mess("Failed", path); return 1; } show_mess("Removed", path); return 0; } #endif #ifdef HAVE_LCD_BITMAP static char font_file[MAX_PATH]; #endif static bool is_deny_file(const char *file) { const char *deny_files[] = { WPS_DEFAULTCFG, RWPS_DEFAULTCFG, #ifdef HAVE_LCD_BITMAP font_file, #endif NULL }; const char **p = deny_files; while ( *p ) { if (!rb->strcmp(file, *p)) return true; p++; } return false; } static void check_whether_used_in_setting(void) { const char *setting_files[] = { #ifdef HAVE_LCD_BITMAP rb->global_settings->font_file, #endif rb->global_settings->wps_file, #ifdef HAVE_LCD_BITMAP rb->global_settings->sbs_file, #endif #ifdef HAVE_REMOTE_LCD rb->global_settings->rwps_file, rb->global_settings->rsbs_file, #endif #if LCD_DEPTH > 1 rb->global_settings->backdrop_file, #endif #ifdef HAVE_LCD_BITMAP rb->global_settings->icon_file, rb->global_settings->viewers_icon_file, #endif #ifdef HAVE_REMOTE_LCD rb->global_settings->remote_icon_file, rb->global_settings->remote_viewers_icon_file, #endif #ifdef HAVE_LCD_COLOR rb->global_settings->colors_file, #endif }; char tempfile[MAX_PATH]; int i; for (i=0; ivalue[0]) { set_file_name(tempfile, setting_files[i], setting); if (!rb->strcasecmp(tempfile, setting->value)) setting->used = true; } } } static void check_whether_used_in_file(const char *cfgfile) { char line[MAX_PATH]; char settingfile[MAX_PATH]; char *p; int fd; char *name, *value; int i; if (!rb->strcasecmp(themefile, cfgfile)) return; fd = rb->open(cfgfile, O_RDONLY); if (fd < 0) return; while (rb->read_line(fd, line, sizeof(line)) > 0) { if (!rb->settings_parseline(line, &name, &value)) continue; /* remove trailing spaces. */ p = value+rb->strlen(value)-1; while (*p == ' ') *p-- = 0; if (*value == 0 || !rb->strcmp(value, "-")) continue; for (i=0; istrcmp(name, setting->name)) { if (setting->value[0]) { set_file_name(settingfile, value, setting); if (!rb->strcasecmp(settingfile, setting->value)) setting->used = true; } break; } } } rb->close(fd); } static void check_whether_used(void) { char cfgfile[MAX_PATH]; DIR *dir; check_whether_used_in_setting(); #ifdef HAVE_LCD_BITMAP /* mark font files come from rockbox-font.zip as used and don't remove * them automatically as themes may depend on those fonts. */ if (remove_list[REMOVE_FONT].option == REMOVE_IF_NOT_USED) check_whether_used_in_file(RB_FONTS_CONFIG); #endif dir = rb->opendir(THEME_DIR); if (!dir) return; /* open error */ while (true) { struct dirent* entry; char *p; int i; /* walk through the directory content */ entry = rb->readdir(dir); if (!entry) break; p = rb->strrchr(entry->d_name, '.'); if (!p || rb->strcmp(p, ".cfg")) continue; rb->snprintf(cfgfile, MAX_PATH, "%s/%s", THEME_DIR, entry->d_name); check_whether_used_in_file(cfgfile); /* break the loop if all files need to be checked in the theme * turned out to be used. */ for (i = 0; i < NUM_REMOVE_ITEMS; i++) { struct remove_setting *setting = &remove_list[i]; if (setting->option == REMOVE_IF_NOT_USED) { if (setting->value[0] && !setting->used) break; } } if (i == NUM_REMOVE_ITEMS) break; } rb->closedir(dir); } static int remove_file(struct remove_setting *setting) { if (!rb->file_exists(setting->value)) { show_mess("Doesn't exist", setting->value); return 0; } if (is_deny_file(setting->value)) { show_mess("Denied", setting->value); return 0; } switch (setting->option) { case ALWAYS_REMOVE: break; case NEVER_REMOVE: show_mess("Skipped", setting->value); return 0; break; case REMOVE_IF_NOT_USED: if (setting->used) { show_mess("Used", setting->value); return 0; } break; case ASK_FOR_REMOVAL: default: { const char *message_lines[] = { "Delete?", setting->value }; const struct text_message text_message = { message_lines, 2 }; if (rb->gui_syncyesno_run(&text_message, NULL, NULL) != YESNO_YES) { show_mess("Skipped", setting->value); return 0; } } break; } if (rb->remove(setting->value)) { show_mess("Failed", setting->value); return -1; } if (setting->func && setting->func(setting)) return -1; show_mess("Removed", setting->value); return 1; } static int remove_theme(void) { static char line[MAX_PATH]; int fd; int i, num_removed = 0; char *name, *value; bool needs_to_check_whether_used = false; /* initialize for safe */ for (i=0; iopen(themefile, O_RDONLY); if (fd < 0) return fd; while (rb->read_line(fd, line, sizeof(line)) > 0) { if (!rb->settings_parseline(line, &name, &value)) continue; /* remove trailing spaces. */ char *p = value+rb->strlen(value)-1; while (*p == ' ') *p-- = 0; if (*value == 0 || !rb->strcmp(value, "-")) continue; for (i=0; istrcmp(name, setting->name)) { set_file_name(setting->value, value, setting); if(setting->option == REMOVE_IF_NOT_USED) needs_to_check_whether_used = true; break; } } } rb->close(fd); if(needs_to_check_whether_used) check_whether_used(); /* now remove file assosiated to the theme. */ for (i=0; istrncasecmp(themefile, THEME_DIR "/", sizeof(THEME_DIR "/")-1)) { show_mess("Skipped", themefile); } else if (rb->remove(themefile)) { show_mess("Failed", themefile); return -1; } else { show_mess("Removed", themefile); rb->reload_directory(); num_removed++; } return num_removed; } static bool option_changed = false; static bool option_menu(void) { MENUITEM_STRINGLIST(option_menu, "Remove Options", NULL, /* same order as remove_list */ #ifdef HAVE_LCD_BITMAP "Font", #endif "WPS", #ifdef HAVE_LCD_BITMAP "Statusbar Skin", #endif #ifdef HAVE_REMOTE_LCD "Remote WPS", "Remote Statusbar Skin", #endif #if LCD_DEPTH > 1 "Backdrop", #endif #ifdef HAVE_LCD_BITMAP "Iconset", "Viewers Iconset", #endif #ifdef HAVE_REMOTE_LCD "Remote Iconset", "Remote Viewers Iconset", #endif #ifdef HAVE_LCD_COLOR "Filetype Colours", #endif "Create Log File"); struct opt_items remove_names[] = { {"Always Remove", -1}, {"Never Remove", -1}, {"Remove if not Used", -1}, {"Ask for Removal", -1}, }; int selected = 0, result; while (1) { result = rb->do_menu(&option_menu, &selected, NULL, false); if (result >= 0 && result < NUM_REMOVE_ITEMS) { struct remove_setting *setting = &remove_list[result]; int prev_option = setting->option; if (rb->set_option(option_menu_[result], &setting->option, INT, remove_names, NUM_REMOVE_OPTION, NULL)) return true; if (prev_option != setting->option) option_changed = true; } else if (result == NUM_REMOVE_ITEMS) { bool prev_value = create_log; if(rb->set_bool("Create Log File", &create_log)) return true; if (prev_value != create_log) option_changed = true; } else if (result == MENU_ATTACHED_USB) return true; else return false; } return false; } enum plugin_status plugin_start(const void* parameter) { static char title[64]; char *p; MENUITEM_STRINGLIST(menu, title, NULL, "Remove Theme", "Remove Options", "Quit"); int selected = 0, ret; bool exit = false; if (!parameter) return PLUGIN_ERROR; rb->snprintf(title, sizeof(title), "Remove %s", rb->strrchr(parameter, '/')+1); if((p = rb->strrchr(title, '.'))) *p = 0; #ifdef HAVE_LCD_BITMAP rb->snprintf(font_file, MAX_PATH, FONT_DIR "/%s.fnt", rb->global_settings->font_file); #endif rb->strlcpy(themefile, parameter, MAX_PATH); if (!rb->file_exists(themefile)) { rb->splash(HZ, "File open error!"); return PLUGIN_ERROR; } configfile_load(CONFIG_FILENAME, config, nb_config, 0); while (!exit) { switch (rb->do_menu(&menu, &selected, NULL, false)) { case 0: if(create_log) { log_fd = rb->open(LOG_FILENAME, O_WRONLY|O_CREAT|O_APPEND, 0666); if(log_fd >= 0) rb->fdprintf(log_fd, "---- %s ----\n", title); else show_mess("Couldn't open log file.", NULL); } ret = remove_theme(); p = (ret >= 0? "Successfully removed!": "Remove failure"); show_mess(p, NULL); if(log_fd >= 0) { rb->fdprintf(log_fd, "----------------\n"); rb->close(log_fd); log_fd = -1; } rb->lcd_clear_display(); rb->lcd_update(); rb->splashf(0, "%s %s", p, "Press any key to exit."); rb->button_clear_queue(); rb->button_get(true); exit = true; break; case 1: if (option_menu()) return PLUGIN_USB_CONNECTED; break; case 2: exit = true; break; case MENU_ATTACHED_USB: return PLUGIN_USB_CONNECTED; break; default: break; } } if(option_changed) configfile_save(CONFIG_FILENAME, config, nb_config, 0); return PLUGIN_OK; } specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FIXED_DEBUG_H #define FIXED_DEBUG_H #include <stdio.h> extern long long spx_mips; #define MIPS_INC spx_mips++, #define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) #define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) #define VERIFY_SHORT(x) ((x)<=32767&&(x)>=-32768) #define VERIFY_INT(x) ((x)<=2147483647LL&&(x)>=-2147483648LL) static inline short NEG16(int x) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "NEG16: input is not short: %d\n", (int)x); } res = -x; if (!VERIFY_SHORT(res)) fprintf (stderr, "NEG16: output is not short: %d\n", (int)res); spx_mips++; return res; } static inline int NEG32(long long x) { long long res; if (!VERIFY_INT(x)) { fprintf (stderr, "NEG16: input is not int: %d\n", (int)x); } res = -x; if (!VERIFY_INT(res)) fprintf (stderr, "NEG16: output is not int: %d\n", (int)res); spx_mips++; return res; } #define EXTRACT16(x) _EXTRACT16(x, __FILE__, __LINE__) static inline short _EXTRACT16(int x, char *file, int line) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "EXTRACT16: input is not short: %d in %s: line %d\n", x, file, line); } res = x; spx_mips++; return res; } #define EXTEND32(x) _EXTEND32(x, __FILE__, __LINE__) static inline int _EXTEND32(int x, char *file, int line) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "EXTEND32: input is not short: %d in %s: line %d\n", x, file, line); } res = x; spx_mips++; return res; } #define SHR16(a, shift) _SHR16(a, shift, __FILE__, __LINE__) static inline short _SHR16(int a, int shift, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHR16: inputs are not short: %d >> %d in %s: line %d\n", a, shift, file, line); } res = a>>shift; if (!VERIFY_SHORT(res)) fprintf (stderr, "SHR16: output is not short: %d in %s: line %d\n", res, file, line); spx_mips++; return res; } #define SHL16(a, shift) _SHL16(a, shift, __FILE__, __LINE__) static inline short _SHL16(int a, int shift, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHL16: inputs are not short: %d %d in %s: line %d\n", a, shift, file, line); } res = a<<shift; if (!VERIFY_SHORT(res)) fprintf (stderr, "SHL16: output is not short: %d in %s: line %d\n", res, file, line); spx_mips++; return res; } static inline int SHR32(long long a, int shift) { long long res; if (!VERIFY_INT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHR32: inputs are not int: %d %d\n", (int)a, shift); } res = a>>shift; if (!VERIFY_INT(res)) { fprintf (stderr, "SHR32: output is not int: %d\n", (int)res); } spx_mips++; return res; } static inline int SHL32(long long a, int shift) { long long res; if (!VERIFY_INT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHL32: inputs are not int: %d %d\n", (int)a, shift); } res = a<<shift; if (!VERIFY_INT(res)) { fprintf (stderr, "SHL32: output is not int: %d\n", (int)res); } spx_mips++; return res; } #define PSHR16(a,shift) (SHR16(ADD16((a),((1<<((shift))>>1))),shift)) #define PSHR32(a,shift) (SHR32(ADD32((a),((1<<((shift))>>1))),shift)) #define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) #define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) #define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) //#define SHR(a,shift) ((a) >> (shift)) //#define SHL(a,shift) ((a) << (shift)) #define ADD16(a, b) _ADD16(a, b, __FILE__, __LINE__) static inline short _ADD16(int a, int b, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "ADD16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); } res = a+b; if (!VERIFY_SHORT(res)) { fprintf (stderr, "ADD16: output is not short: %d+%d=%d in %s: line %d\n", a,b,res, file, line); } spx_mips++; return res; } #define SUB16(a, b) _SUB16(a, b, __FILE__, __LINE__) static inline short _SUB16(int a, int b, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "SUB16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); } res = a-b; if (!VERIFY_SHORT(res)) fprintf (stderr, "SUB16: output is not short: %d in %s: line %d\n", res, file, line); spx_mips++; return res; } #define ADD32(a, b) _ADD32(a, b, __FILE__, __LINE__) static inline int _ADD32(long long a, long long b, char *file, int line) { long long res; if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "ADD32: inputs are not int: %d %d in %s: line %d\n", (int)a, (int)b, file, line); } res = a+b; if (!VERIFY_INT(res)) { fprintf (stderr, "ADD32: output is not int: %d in %s: line %d\n", (int)res, file, line); } spx_mips++; return res; } static inline int SUB32(long long a, long long b) { long long res; if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "SUB32: inputs are not int: %d %d\n", (int)a, (int)b); } res = a-b; if (!VERIFY_INT(res)) fprintf (stderr, "SUB32: output is not int: %d\n", (int)res); spx_mips++; return res; } #define ADD64(a,b) (MIPS_INC(a)+(b)) /* result fits in 16 bits */ static inline short MULT16_16_16(int a, int b) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_16: inputs are not short: %d %d\n", a, b); } res = a*b; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_16: output is not short: %d\n", res); spx_mips++; return res; } #define MULT16_16(a, b) _MULT16_16(a, b, __FILE__, __LINE__) static inline int _MULT16_16(int a, int b, char *file, int line) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); } res = ((long long)a)*b; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16: output is not int: %d in %s: line %d\n", (int)res, file, line); spx_mips++; return res; } #define MAC16_16(c,a,b) (spx_mips--,ADD32((c),MULT16_16((a),(b)))) #define MAC16_16_Q11(c,a,b) (EXTRACT16(ADD16((c),EXTRACT16(SHR32(MULT16_16((a),(b)),11))))) #define MAC16_16_Q13(c,a,b) (EXTRACT16(ADD16((c),EXTRACT16(SHR32(MULT16_16((a),(b)),13))))) #define MAC16_16_P13(c,a,b) (EXTRACT16(ADD32((c),SHR32(ADD32(4096,MULT16_16((a),(b))),13)))) #define MULT16_32_QX(a, b, Q) _MULT16_32_QX(a, b, Q, __FILE__, __LINE__) static inline int _MULT16_32_QX(int a, long long b, int Q, char *file, int line) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_INT(b)) { fprintf (stderr, "MULT16_32_Q%d: inputs are not short+int: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line); } if (ABS32(b)>=(1<<(15+Q))) fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line); res = (((long long)a)*(long long)b) >> Q; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_32_Q%d: output is not int: %d*%d=%d in %s: line %d\n", Q, (int)a, (int)b,(int)res, file, line); spx_mips+=5; return res; } static inline int MULT16_32_PX(int a, long long b, int Q) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_INT(b)) { fprintf (stderr, "MULT16_32_P%d: inputs are not short+int: %d %d\n", Q, (int)a, (int)b); } if (ABS32(b)>=(1<<(15+Q))) fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d\n", Q, (int)a, (int)b); res = ((((long long)a)*(long long)b) + ((1<<Q)>>1))>> Q; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_32_P%d: output is not int: %d*%d=%d\n", Q, (int)a, (int)b,(int)res); spx_mips+=5; return res; } #define MULT16_32_Q11(a,b) MULT16_32_QX(a,b,11) #define MAC16_32_Q11(c,a,b) ADD32((c),MULT16_32_Q11((a),(b))) #define MULT16_32_Q12(a,b) MULT16_32_QX(a,b,12) #define MULT16_32_Q13(a,b) MULT16_32_QX(a,b,13) #define MULT16_32_Q14(a,b) MULT16_32_QX(a,b,14) #define MULT16_32_Q15(a,b) MULT16_32_QX(a,b,15) #define MULT16_32_P15(a,b) MULT16_32_PX(a,b,15) #define MAC16_32_Q15(c,a,b) ADD32((c),MULT16_32_Q15((a),(b))) static inline int SATURATE(int a, int b) { if (a>b) a=b; if (a<-b) a = -b; return a; } static inline int MULT16_16_Q11_32(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q11: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res >>= 11; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16_Q11: output is not short: %d*%d=%d\n", (int)a, (int)b, (int)res); spx_mips+=3; return res; } static inline short MULT16_16_Q13(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q13: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res >>= 13; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_Q13: output is not short: %d*%d=%d\n", a, b, (int)res); spx_mips+=3; return res; } static inline short MULT16_16_Q14(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q14: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res >>= 14; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_Q14: output is not short: %d\n", (int)res); spx_mips+=3; return res; } static inline short MULT16_16_Q15(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q15: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res >>= 15; if (!VERIFY_SHORT(res)) { fprintf (stderr, "MULT16_16_Q15: output is not short: %d\n", (int)res); } spx_mips+=3; return res; } static inline short MULT16_16_P13(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P13: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res += 4096; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16_P13: overflow: %d*%d=%d\n", a, b, (int)res); res >>= 13; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_P13: output is not short: %d*%d=%d\n", a, b, (int)res); spx_mips+=4; return res; } static inline short MULT16_16_P14(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P14: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res += 8192; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16_P14: overflow: %d*%d=%d\n", a, b, (int)res); res >>= 14; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_P14: output is not short: %d*%d=%d\n", a, b, (int)res); spx_mips+=4; return res; } static inline short MULT16_16_P15(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P15: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res += 16384; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16_P15: overflow: %d*%d=%d\n", a, b, (int)res); res >>= 15; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_P15: output is not short: %d*%d=%d\n", a, b, (int)res); spx_mips+=4; return res; } #define DIV32_16(a, b) _DIV32_16(a, b, __FILE__, __LINE__) static inline int _DIV32_16(long long a, long long b, char *file, int line) { long long res; if (b==0) { fprintf(stderr, "DIV32_16: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line); return 0; } if (!VERIFY_INT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "DIV32_16: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line); } res = a/b; if (!VERIFY_SHORT(res)) { fprintf (stderr, "DIV32_16: output is not short: %d / %d = %d in %s: line %d\n", (int)a,(int)b,(int)res, file, line); if (res>32767) res = 32767; if (res<-32768) res = -32768; } spx_mips+=20; return res; } #define DIV32(a, b) _DIV32(a, b, __FILE__, __LINE__) static inline int _DIV32(long long a, long long b, char *file, int line) { long long res; if (b==0) { fprintf(stderr, "DIV32: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line); return 0; } if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "DIV32: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line); } res = a/b; if (!VERIFY_INT(res)) fprintf (stderr, "DIV32: output is not int: %d in %s: line %d\n", (int)res, file, line); spx_mips+=36; return res; } #define PDIV32(a,b) DIV32(ADD32((a),(b)>>1),b) #define PDIV32_16(a,b) DIV32_16(ADD32((a),(b)>>1),b) #endif