| /* | |
| * Copyright 2003 Vincent Béron | |
| * Copyright 2007, 2008 Mikolaj Zalewski | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library 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 | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <assert.h> | |
| #include "dumpres.h" | |
| #include "utils.h" | |
| #include "wrc.h" | |
| #define MASTER_LANGUAGE LANG_ENGLISH | |
| #define MASTER_SUBLANGUAGE SUBLANG_ENGLISH_US | |
| #define NB_LANG 0x94 | |
| enum lang_type_e { | |
| lang_type_master = 0, | |
| lang_type_neutral, | |
| lang_type_normal | |
| }; | |
| static language_t get_language(resource_t *resource) { | |
| switch(resource->type) { | |
| case res_acc: | |
| return *resource->res.acc->lvc.language; | |
| case res_bmp: | |
| return *resource->res.bmp->data->lvc.language; | |
| case res_cur: | |
| return *resource->res.cur->lvc.language; | |
| case res_curg: | |
| return *resource->res.curg->lvc.language; | |
| case res_dlg: | |
| return *resource->res.dlg->lvc.language; | |
| case res_fnt: | |
| return *resource->res.fnt->data->lvc.language; | |
| case res_fntdir: | |
| return *resource->res.fnd->data->lvc.language; | |
| case res_ico: | |
| return *resource->res.ico->lvc.language; | |
| case res_icog: | |
| return *resource->res.icog->lvc.language; | |
| case res_men: | |
| return *resource->res.men->lvc.language; | |
| case res_rdt: | |
| return *resource->res.rdt->data->lvc.language; | |
| case res_stt: | |
| return *resource->res.stt->lvc.language; | |
| case res_usr: | |
| return *resource->res.usr->data->lvc.language; | |
| case res_msg: | |
| return *resource->res.msg->data->lvc.language; | |
| case res_ver: | |
| return *resource->res.ver->lvc.language; | |
| case res_dlginit: | |
| return *resource->res.dlgi->data->lvc.language; | |
| case res_toolbar: | |
| return *resource->res.tbt->lvc.language; | |
| case res_anicur: | |
| case res_aniico: | |
| return *resource->res.ani->data->lvc.language; | |
| case res_html: | |
| return *resource->res.html->data->lvc.language; | |
| default: | |
| /* Not supposed to reach here */ | |
| fprintf(stderr, "Not supposed to reach here (get_language_id())\n"); | |
| abort(); | |
| } | |
| } | |
| static int get_language_id(resource_t *resource) { | |
| return get_language(resource).id; | |
| } | |
| static int compare_lang(language_t lang1, language_t lang2) | |
| { | |
| return memcmp(&lang1, &lang2, sizeof(language_t)); | |
| } | |
| #if 0 | |
| #define PRETTYPRINTLANG(langid) \ | |
| if(LANG_##langid == lid) { \ | |
| return #langid; \ | |
| } | |
| static const char *get_language_name(int lid) { | |
| PRETTYPRINTLANG(NEUTRAL) | |
| PRETTYPRINTLANG(AFRIKAANS) | |
| PRETTYPRINTLANG(ALBANIAN) | |
| PRETTYPRINTLANG(ARABIC) | |
| PRETTYPRINTLANG(ARMENIAN) | |
| PRETTYPRINTLANG(ASSAMESE) | |
| PRETTYPRINTLANG(AZERI) | |
| PRETTYPRINTLANG(BASQUE) | |
| PRETTYPRINTLANG(BELARUSIAN) | |
| PRETTYPRINTLANG(BENGALI) | |
| PRETTYPRINTLANG(BULGARIAN) | |
| PRETTYPRINTLANG(CATALAN) | |
| PRETTYPRINTLANG(CHINESE) | |
| PRETTYPRINTLANG(CROATIAN) | |
| PRETTYPRINTLANG(CZECH) | |
| PRETTYPRINTLANG(DANISH) | |
| PRETTYPRINTLANG(DIVEHI) | |
| PRETTYPRINTLANG(DUTCH) | |
| PRETTYPRINTLANG(ENGLISH) | |
| PRETTYPRINTLANG(ESTONIAN) | |
| PRETTYPRINTLANG(FAEROESE) | |
| PRETTYPRINTLANG(FARSI) | |
| PRETTYPRINTLANG(FINNISH) | |
| PRETTYPRINTLANG(FRENCH) | |
| PRETTYPRINTLANG(GALICIAN) | |
| PRETTYPRINTLANG(GEORGIAN) | |
| PRETTYPRINTLANG(GERMAN) | |
| PRETTYPRINTLANG(GREEK) | |
| PRETTYPRINTLANG(GUJARATI) | |
| PRETTYPRINTLANG(HEBREW) | |
| PRETTYPRINTLANG(HINDI) | |
| PRETTYPRINTLANG(HUNGARIAN) | |
| PRETTYPRINTLANG(ICELANDIC) | |
| PRETTYPRINTLANG(INDONESIAN) | |
| PRETTYPRINTLANG(ITALIAN) | |
| PRETTYPRINTLANG(JAPANESE) | |
| PRETTYPRINTLANG(KANNADA) | |
| PRETTYPRINTLANG(KASHMIRI) | |
| PRETTYPRINTLANG(KAZAK) | |
| PRETTYPRINTLANG(KONKANI) | |
| PRETTYPRINTLANG(KOREAN) | |
| PRETTYPRINTLANG(KYRGYZ) | |
| PRETTYPRINTLANG(LATVIAN) | |
| PRETTYPRINTLANG(LITHUANIAN) | |
| PRETTYPRINTLANG(MACEDONIAN) | |
| PRETTYPRINTLANG(MALAY) | |
| PRETTYPRINTLANG(MALAYALAM) | |
| PRETTYPRINTLANG(MANIPURI) | |
| PRETTYPRINTLANG(MARATHI) | |
| PRETTYPRINTLANG(MONGOLIAN) | |
| PRETTYPRINTLANG(NEPALI) | |
| PRETTYPRINTLANG(NORWEGIAN) | |
| PRETTYPRINTLANG(ORIYA) | |
| PRETTYPRINTLANG(POLISH) | |
| PRETTYPRINTLANG(PORTUGUESE) | |
| PRETTYPRINTLANG(PUNJABI) | |
| PRETTYPRINTLANG(ROMANIAN) | |
| PRETTYPRINTLANG(RUSSIAN) | |
| PRETTYPRINTLANG(SANSKRIT) | |
| PRETTYPRINTLANG(SERBIAN) | |
| PRETTYPRINTLANG(SINDHI) | |
| PRETTYPRINTLANG(SLOVAK) | |
| PRETTYPRINTLANG(SLOVENIAN) | |
| PRETTYPRINTLANG(SPANISH) | |
| PRETTYPRINTLANG(SWAHILI) | |
| PRETTYPRINTLANG(SWEDISH) | |
| PRETTYPRINTLANG(SYRIAC) | |
| PRETTYPRINTLANG(TAMIL) | |
| PRETTYPRINTLANG(TATAR) | |
| PRETTYPRINTLANG(TELUGU) | |
| PRETTYPRINTLANG(THAI) | |
| PRETTYPRINTLANG(TURKISH) | |
| PRETTYPRINTLANG(UKRAINIAN) | |
| PRETTYPRINTLANG(URDU) | |
| PRETTYPRINTLANG(UZBEK) | |
| PRETTYPRINTLANG(VIETNAMESE) | |
| PRETTYPRINTLANG(GAELIC) | |
| PRETTYPRINTLANG(MALTESE) | |
| PRETTYPRINTLANG(MAORI) | |
| PRETTYPRINTLANG(RHAETO_ROMANCE) | |
| PRETTYPRINTLANG(SAAMI) | |
| PRETTYPRINTLANG(SORBIAN) | |
| PRETTYPRINTLANG(SUTU) | |
| PRETTYPRINTLANG(TSONGA) | |
| PRETTYPRINTLANG(TSWANA) | |
| PRETTYPRINTLANG(VENDA) | |
| PRETTYPRINTLANG(XHOSA) | |
| PRETTYPRINTLANG(ZULU) | |
| PRETTYPRINTLANG(ESPERANTO) | |
| PRETTYPRINTLANG(WALON) | |
| PRETTYPRINTLANG(CORNISH) | |
| PRETTYPRINTLANG(WELSH) | |
| PRETTYPRINTLANG(BRETON) | |
| return "Unknown language"; | |
| } | |
| #endif | |
| static int compare_accelerator(accelerator_t *accelerator1, accelerator_t *accelerator2) { | |
| int different = 0; | |
| event_t *ev1 = NULL, *ev2 = NULL; | |
| if(!different && | |
| ((accelerator1->memopt != accelerator2->memopt) || | |
| (accelerator1->lvc.version != accelerator2->lvc.version) || | |
| (accelerator1->lvc.characts != accelerator2->lvc.characts))) | |
| different = 1; | |
| ev1 = accelerator1->events; | |
| ev2 = accelerator2->events; | |
| while(!different && ev1 && ev2) { | |
| if(!different && | |
| ((ev1->id != ev2->id) || | |
| (ev1->flags != ev2->flags))) | |
| different = 1; | |
| ev1 = ev1->next; | |
| ev2 = ev2->next; | |
| } | |
| if(!different && | |
| ((ev1 && !ev2) || (!ev1 && ev2))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_bitmap(bitmap_t *bitmap1, bitmap_t *bitmap2) { | |
| int different = 0; | |
| if(!different && | |
| ((bitmap1->memopt != bitmap2->memopt) || | |
| (bitmap1->data->lvc.version != bitmap2->data->lvc.version) || | |
| (bitmap1->data->lvc.characts != bitmap2->data->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_cursor(cursor_t *cursor1, cursor_t *cursor2) { | |
| int different = 0; | |
| if(!different && | |
| ((cursor1->id != cursor2->id) || | |
| (cursor1->width != cursor2->width) || | |
| (cursor1->height != cursor2->height) || | |
| (cursor1->xhot != cursor2->xhot) || | |
| (cursor1->yhot != cursor2->yhot))) | |
| different = 1; | |
| if(!different && | |
| ((cursor1->lvc.version != cursor2->lvc.version) || | |
| (cursor1->lvc.characts != cursor2->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_cursor_group(cursor_group_t *cursor_group1, cursor_group_t *cursor_group2) { | |
| int different = 0; | |
| cursor_t *cursor1 = NULL, *cursor2 = NULL; | |
| if(!different && | |
| ((cursor_group1->memopt != cursor_group2->memopt) || | |
| (cursor_group1->lvc.version != cursor_group2->lvc.version) || | |
| (cursor_group1->lvc.characts != cursor_group2->lvc.characts))) | |
| different = 1; | |
| if(!different && | |
| (cursor_group1->ncursor != cursor_group2->ncursor)) | |
| different = 1; | |
| if(!different) { | |
| cursor1 = cursor_group1->cursorlist; | |
| cursor2 = cursor_group2->cursorlist; | |
| while(!different && cursor1 && cursor2) { | |
| different = compare_cursor(cursor1, cursor2); | |
| cursor1 = cursor1->next; | |
| cursor2 = cursor2->next; | |
| } | |
| if(!different && | |
| ((cursor1 && !cursor2) || | |
| (!cursor1 && cursor2))) | |
| different = 1; | |
| } | |
| return different; | |
| } | |
| static int compare_control(control_t *control1, control_t *control2) { | |
| int different = 0; | |
| char *nameid = NULL; | |
| int ignore_style; | |
| if(!different && | |
| ((control1 && !control2) || | |
| (!control1 && control2))) | |
| different = 1; | |
| if(different || !control1 || !control2) | |
| return different; | |
| nameid = strdup(get_nameid_str(control1->ctlclass)); | |
| if(!different && strcmp(nameid, get_nameid_str(control2->ctlclass))) | |
| different = 1; | |
| free(nameid); | |
| if (different) | |
| return different; | |
| /* allow the translators to set some styles */ | |
| ignore_style = 0; | |
| if (control1->ctlclass->type == name_ord && control1->ctlclass->name.i_name == CT_BUTTON) | |
| ignore_style = 0x2000; /* BS_MULTILINE*/ | |
| if(!different && | |
| (control1->id != control2->id)) | |
| different = 1; | |
| if(!different && control1->gotstyle && control2->gotstyle) { | |
| if((!control1->style || !control2->style) || | |
| (control1->style->and_mask || control2->style->and_mask) || | |
| ((control1->style->or_mask & ~ignore_style) != (control2->style->or_mask & ~ignore_style))) | |
| different = 1; | |
| } else if(!different && | |
| ((control1->gotstyle && !control2->gotstyle) || | |
| (!control1->gotstyle && control2->gotstyle))) | |
| different = 1; | |
| if(!different && control1->gotexstyle && control2->gotexstyle) { | |
| if((!control1->exstyle || !control2->exstyle) || | |
| (control1->exstyle->and_mask || control2->exstyle->and_mask) || | |
| (control1->exstyle->or_mask != control2->exstyle->or_mask)) | |
| different = 1; | |
| } else if(!different && | |
| ((control1->gotexstyle && !control2->gotexstyle) || | |
| (!control1->gotexstyle && control2->gotexstyle))) | |
| different = 1; | |
| if(!different && control1->gothelpid && control2->gothelpid) { | |
| if(control1->helpid != control2->helpid) | |
| different = 1; | |
| } else if(!different && | |
| ((control1->gothelpid && !control2->gothelpid) || | |
| (!control1->gothelpid && control2->gothelpid))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_dialog(dialog_t *dialog1, dialog_t *dialog2) { | |
| int different = 0; | |
| char *nameid = NULL; | |
| control_t *ctrl1, *ctrl2; | |
| if(!different && | |
| ((dialog1->memopt != dialog2->memopt) || | |
| (dialog1->lvc.version != dialog2->lvc.version) || | |
| (dialog1->lvc.characts != dialog2->lvc.characts))) | |
| different = 1; | |
| if(!different && dialog1->gotstyle && dialog2->gotstyle) { | |
| if((!dialog1->style || !dialog2->style) || | |
| (dialog1->style->and_mask || dialog2->style->and_mask) || | |
| (dialog1->style->or_mask != dialog2->style->or_mask)) | |
| different = 1; | |
| } else if(!different && | |
| ((dialog1->gotstyle && !dialog2->gotstyle) || | |
| (!dialog1->gotstyle && dialog2->gotstyle))) | |
| different = 1; | |
| if(!different && dialog1->gotexstyle && dialog2->gotexstyle) { | |
| if((!dialog1->exstyle || !dialog2->exstyle) || | |
| (dialog1->exstyle->and_mask || dialog2->exstyle->and_mask) || | |
| (dialog1->exstyle->or_mask != dialog2->exstyle->or_mask)) | |
| different = 1; | |
| } else if(!different && | |
| ((dialog1->gotexstyle && !dialog2->gotexstyle) || | |
| (!dialog1->gotexstyle && dialog2->gotexstyle))) | |
| different = 1; | |
| if(!different && dialog1->gothelpid && dialog2->gothelpid) { | |
| if(dialog1->helpid != dialog2->helpid) | |
| different = 1; | |
| } else if(!different && | |
| ((dialog1->gothelpid && !dialog2->gothelpid) || | |
| (!dialog1->gothelpid && dialog2->gothelpid))) | |
| different = 1; | |
| nameid = strdup(get_nameid_str(dialog1->menu)); | |
| if(!different && strcmp(nameid, get_nameid_str(dialog2->menu))) | |
| different = 1; | |
| free(nameid); | |
| nameid = strdup(get_nameid_str(dialog1->dlgclass)); | |
| if(!different && strcmp(nameid, get_nameid_str(dialog2->dlgclass))) | |
| different = 1; | |
| free(nameid); | |
| ctrl1 = dialog1->controls; | |
| ctrl2 = dialog2->controls; | |
| while(!different && (ctrl1 || ctrl2)) | |
| { | |
| different = compare_control(ctrl1, ctrl2); | |
| if (ctrl1) ctrl1 = ctrl1->next; | |
| if (ctrl2) ctrl2 = ctrl2->next; | |
| } | |
| return different; | |
| } | |
| static int compare_font(font_t *font1, font_t *font2) { | |
| int different = 0; | |
| if(!different && | |
| ((font1->memopt != font2->memopt) || | |
| (font1->data->lvc.version != font2->data->lvc.version) || | |
| (font1->data->lvc.characts != font2->data->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_fontdir(fontdir_t *fontdir1, fontdir_t *fontdir2) { | |
| int different = 0; | |
| if(!different && | |
| ((fontdir1->memopt != fontdir2->memopt) || | |
| (fontdir1->data->lvc.version != fontdir2->data->lvc.version) || | |
| (fontdir1->data->lvc.characts != fontdir2->data->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_icon(icon_t *icon1, icon_t *icon2) { | |
| int different = 0; | |
| if(!different && | |
| ((icon1->id != icon2->id) || | |
| (icon1->width != icon2->width) || | |
| (icon1->height != icon2->height))) | |
| different = 1; | |
| if(!different && | |
| ((icon1->lvc.version != icon2->lvc.version) || | |
| (icon1->lvc.characts != icon2->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_icon_group(icon_group_t *icon_group1, icon_group_t *icon_group2) { | |
| int different = 0; | |
| icon_t *icon1 = NULL, *icon2 = NULL; | |
| if(!different && | |
| ((icon_group1->memopt != icon_group2->memopt) || | |
| (icon_group1->lvc.version != icon_group2->lvc.version) || | |
| (icon_group1->lvc.characts != icon_group2->lvc.characts))) | |
| different = 1; | |
| if(!different && | |
| (icon_group1->nicon != icon_group2->nicon)) | |
| different = 1; | |
| if(!different) { | |
| icon1 = icon_group1->iconlist; | |
| icon2 = icon_group2->iconlist; | |
| while(!different && icon1 && icon2) { | |
| different = compare_icon(icon1, icon2); | |
| icon1 = icon1->next; | |
| icon2 = icon2->next; | |
| } | |
| if(!different && | |
| ((icon1 && !icon2) || | |
| (!icon1 && icon2))) | |
| different = 1; | |
| } | |
| return different; | |
| } | |
| static int compare_menu_item(menu_item_t *menu_item1, menu_item_t *menu_item2) { | |
| int different = 0; | |
| while(!different && menu_item1 && menu_item2) { | |
| if(menu_item1->popup && menu_item2->popup) { | |
| if(!different && menu_item1->gotid && menu_item2->gotid) { | |
| if(menu_item1->id != menu_item2->id) | |
| different = 1; | |
| } else if(!different && | |
| ((menu_item1->gotid && !menu_item2->gotid) || | |
| (!menu_item1->gotid && menu_item2->gotid))) | |
| different = 1; | |
| if(!different && menu_item1->gottype && menu_item2->gottype) { | |
| if(menu_item1->type != menu_item2->type) | |
| different = 1; | |
| } else if(!different && | |
| ((menu_item1->gottype && !menu_item2->gottype) || | |
| (!menu_item1->gottype && menu_item2->gottype))) | |
| different = 1; | |
| if(!different && menu_item1->gotstate && menu_item2->gotstate) { | |
| if(menu_item1->state != menu_item2->state) | |
| different = 1; | |
| } else if(!different && | |
| ((menu_item1->gotstate && !menu_item2->gotstate) || | |
| (!menu_item1->gotstate && menu_item2->gotstate))) | |
| different = 1; | |
| if(!different && menu_item1->gothelpid && menu_item2->gothelpid) { | |
| if(menu_item1->helpid != menu_item2->helpid) | |
| different = 1; | |
| } else if(!different && | |
| ((menu_item1->gothelpid && !menu_item2->gothelpid) || | |
| (!menu_item1->gothelpid && menu_item2->gothelpid))) | |
| different = 1; | |
| if(!different) | |
| different = compare_menu_item(menu_item1->popup, menu_item2->popup); | |
| } else if(!menu_item1->popup && !menu_item2->popup) { | |
| if(menu_item1->name && menu_item2->name) { | |
| if(!different && menu_item1->gotid && menu_item2->gotid) { | |
| if(menu_item1->id != menu_item2->id) | |
| different = 1; | |
| } else if(!different && | |
| ((menu_item1->gotid && !menu_item2->gotid) || | |
| (!menu_item1->gotid && menu_item2->gotid))) | |
| different = 1; | |
| if(!different && menu_item1->gottype && menu_item2->gottype) { | |
| if(menu_item1->type != menu_item2->type) | |
| different = 1; | |
| } else if(!different && | |
| ((menu_item1->gottype && !menu_item2->gottype) || | |
| (!menu_item1->gottype && menu_item2->gottype))) | |
| different = 1; | |
| if(!different && menu_item1->gotstate && menu_item2->gotstate) { | |
| if(menu_item1->state != menu_item2->state) | |
| different = 1; | |
| } else if(!different && | |
| ((menu_item1->gotstate && !menu_item2->gotstate) || | |
| (!menu_item1->gotstate && menu_item2->gotstate))) | |
| different = 1; | |
| if(!different && menu_item1->gothelpid && menu_item2->gothelpid) { | |
| if(menu_item1->helpid != menu_item2->helpid) | |
| different = 1; | |
| } else if(!different && | |
| ((menu_item1->gothelpid && !menu_item2->gothelpid) || | |
| (!menu_item1->gothelpid && menu_item2->gothelpid))) | |
| different = 1; | |
| } else if((menu_item1->name && !menu_item2->name) || | |
| (!menu_item1->name && menu_item2->name)) | |
| different = 1; | |
| } else | |
| different = 1; | |
| menu_item1 = menu_item1->next; | |
| menu_item2 = menu_item2->next; | |
| } | |
| if(!different && | |
| ((menu_item1 && !menu_item2) || | |
| (!menu_item1 && menu_item2))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_menu(menu_t *menu1, menu_t *menu2) { | |
| int different = 0; | |
| if(!different && | |
| ((menu1->memopt != menu2->memopt) || | |
| (menu1->lvc.version != menu2->lvc.version) || | |
| (menu1->lvc.characts != menu2->lvc.characts))) | |
| different = 1; | |
| if(!different) | |
| different = compare_menu_item(menu1->items, menu2->items); | |
| return different; | |
| } | |
| static int compare_rcdata(rcdata_t *rcdata1, rcdata_t *rcdata2) { | |
| int different = 0; | |
| if(!different && | |
| ((rcdata1->memopt != rcdata2->memopt) || | |
| (rcdata1->data->lvc.version != rcdata2->data->lvc.version) || | |
| (rcdata1->data->lvc.characts != rcdata2->data->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_html(html_t *rcdata1, html_t *rcdata2) { | |
| int different = 0; | |
| if(!different && | |
| ((rcdata1->memopt != rcdata2->memopt) || | |
| (rcdata1->data->lvc.version != rcdata2->data->lvc.version) || | |
| (rcdata1->data->lvc.characts != rcdata2->data->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_stringtable(stringtable_t *stringtable1, stringtable_t *stringtable2) { | |
| int different = 0; | |
| int i; | |
| while(!different && stringtable1 && stringtable2) { | |
| if((stringtable1->memopt != stringtable2->memopt) || | |
| (stringtable1->lvc.version != stringtable2->lvc.version) || | |
| (stringtable1->lvc.characts != stringtable2->lvc.characts)) | |
| different = 1; | |
| if(!different) { | |
| if((stringtable1->nentries != stringtable2->nentries) || | |
| (stringtable1->idbase != stringtable2->idbase)) | |
| different = 1; | |
| else | |
| for(i = 0 ; i < stringtable1->nentries; i++) | |
| if((stringtable1->entries[i].id != stringtable2->entries[i].id) || | |
| (stringtable1->entries[i].memopt != stringtable2->entries[i].memopt) || | |
| (stringtable1->entries[i].str && !stringtable2->entries[i].str) || | |
| (!stringtable1->entries[i].str && stringtable2->entries[i].str)) { | |
| different = 1; | |
| break; | |
| } | |
| } | |
| stringtable1 = stringtable1->next; | |
| stringtable2 = stringtable2->next; | |
| } | |
| return different; | |
| } | |
| static int compare_user(user_t *user1, user_t *user2) { | |
| int different = 0; | |
| char *nameid = NULL; | |
| if(!different && | |
| ((user1->memopt != user2->memopt) || | |
| (user1->data->lvc.version != user2->data->lvc.version) || | |
| (user1->data->lvc.characts != user2->data->lvc.characts))) | |
| different = 1; | |
| nameid = strdup(get_nameid_str(user1->type)); | |
| if(!different && strcmp(nameid, get_nameid_str(user2->type))) | |
| different = 1; | |
| free(nameid); | |
| return different; | |
| } | |
| static int compare_messagetable(messagetable_t *messagetable1, messagetable_t *messagetable2) { | |
| int different = 0; | |
| if(!different && | |
| ((messagetable1->memopt != messagetable2->memopt) || | |
| (messagetable1->data->lvc.version != messagetable2->data->lvc.version) || | |
| (messagetable1->data->lvc.characts != messagetable2->data->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_string(string_t *string1, string_t *string2) { | |
| int different = 0; | |
| if(!different && | |
| ((string1->size != string2->size) || | |
| (string1->type != string2->type))) | |
| different = 1; | |
| if(!different) { | |
| if(string1->type == str_char) | |
| different = memcmp(string1->str.cstr, string2->str.cstr, string1->size); | |
| else if(string1->type == str_unicode) | |
| different = memcmp(string1->str.wstr, string2->str.wstr, string1->size*sizeof(WCHAR)); | |
| else | |
| different = 1; | |
| } | |
| return different; | |
| } | |
| static int compare_ver_block(ver_block_t *ver_block1, ver_block_t *ver_block2); | |
| static int compare_ver_value(ver_value_t *ver_value1, ver_value_t *ver_value2) { | |
| int different = 0; | |
| int i = 0; | |
| if(!different && | |
| (ver_value1->type == ver_value2->type)) { | |
| switch(ver_value1->type) { | |
| case val_str: | |
| if(!different && ver_value1->key && ver_value2->key) | |
| different = compare_string(ver_value1->key, ver_value2->key); | |
| else if(!different && | |
| ((ver_value1->key && !ver_value2->key) || | |
| (!ver_value1->key && ver_value2->key))) | |
| different = 1; | |
| break; | |
| case val_words: | |
| if(!different && ver_value1->key && ver_value2->key) | |
| different = compare_string(ver_value1->key, ver_value2->key); | |
| else if(!different && | |
| ((ver_value1->key && !ver_value2->key) || | |
| (!ver_value1->key && ver_value2->key))) | |
| different = 1; | |
| if(!different && ver_value1->value.words && ver_value2->value.words) { | |
| if(!different && | |
| (ver_value1->value.words->nwords != ver_value2->value.words->nwords)) | |
| different = 1; | |
| if(!different) | |
| for(i = 0; i < ver_value1->value.words->nwords; i++) { | |
| if(ver_value1->value.words->words[i] != ver_value2->value.words->words[i]) { | |
| different = 1; | |
| break; | |
| } | |
| } | |
| } else if(!different && | |
| ((ver_value1->value.words && !ver_value2->value.words) || | |
| (!ver_value1->value.words && ver_value2->value.words))) | |
| different = 1; | |
| break; | |
| case val_block: | |
| if(!different && ver_value1->value.block && ver_value2->value.block) | |
| different = compare_ver_block(ver_value1->value.block, ver_value2->value.block); | |
| else if(!different && | |
| ((ver_value1->value.block && !ver_value2->value.block) || | |
| (!ver_value1->value.block && ver_value2->value.block))) | |
| different = 1; | |
| break; | |
| default: | |
| different = 1; | |
| } | |
| } else | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_ver_block(ver_block_t *ver_block1, ver_block_t *ver_block2) { | |
| int different = 0; | |
| ver_value_t *ver_value1 = NULL, *ver_value2 = NULL; | |
| if(!different) { | |
| ver_value1 = ver_block1->values; | |
| ver_value2 = ver_block2->values; | |
| while(!different && ver_value1 && ver_value2) { | |
| different = compare_ver_value(ver_value1, ver_value2); | |
| ver_value1 = ver_value1->next; | |
| ver_value2 = ver_value2->next; | |
| } | |
| if(!different && | |
| ((ver_value1 && !ver_value2) || | |
| (!ver_value1 && ver_value2))) | |
| different = 1; | |
| } | |
| return different; | |
| } | |
| static int compare_versioninfo(versioninfo_t *versioninfo1, versioninfo_t *versioninfo2) { | |
| int different = 0; | |
| ver_block_t *ver_block1 = NULL, *ver_block2 = NULL; | |
| if(!different && | |
| ((versioninfo1->memopt != versioninfo2->memopt) || | |
| (versioninfo1->lvc.version != versioninfo2->lvc.version) || | |
| (versioninfo1->lvc.characts != versioninfo2->lvc.characts))) | |
| different = 1; | |
| if(!different && versioninfo1->gotit.fv && versioninfo2->gotit.fv) { | |
| if((versioninfo1->filever_maj1 != versioninfo2->filever_maj1) || | |
| (versioninfo1->filever_maj2 != versioninfo2->filever_maj2) || | |
| (versioninfo1->filever_min1 != versioninfo2->filever_min1) || | |
| (versioninfo1->filever_min2 != versioninfo2->filever_min2)) | |
| different = 1; | |
| } else if(!different && | |
| ((versioninfo1->gotit.fv && !versioninfo2->gotit.fv) || | |
| (!versioninfo1->gotit.fv && versioninfo2->gotit.fv))) | |
| different = 1; | |
| if(!different && versioninfo1->gotit.pv && versioninfo2->gotit.pv) { | |
| if((versioninfo1->prodver_maj1 != versioninfo2->prodver_maj1) || | |
| (versioninfo1->prodver_maj2 != versioninfo2->prodver_maj2) || | |
| (versioninfo1->prodver_min1 != versioninfo2->prodver_min1) || | |
| (versioninfo1->prodver_min2 != versioninfo2->prodver_min2)) | |
| different = 1; | |
| } else if(!different && | |
| ((versioninfo1->gotit.pv && !versioninfo2->gotit.pv) || | |
| (!versioninfo1->gotit.pv && versioninfo2->gotit.pv))) | |
| different = 1; | |
| if(!different && versioninfo1->gotit.fo && versioninfo2->gotit.fo) { | |
| if(versioninfo1->fileos != versioninfo2->fileos) | |
| different = 1; | |
| } else if(!different && | |
| ((versioninfo1->gotit.fo && !versioninfo2->gotit.fo) || | |
| (!versioninfo1->gotit.fo && versioninfo2->gotit.fo))) | |
| different = 1; | |
| if(!different && versioninfo1->gotit.ff && versioninfo2->gotit.ff) { | |
| if(versioninfo1->fileflags != versioninfo2->fileflags) | |
| different = 1; | |
| } else if(!different && | |
| ((versioninfo1->gotit.ff && !versioninfo2->gotit.ff) || | |
| (!versioninfo1->gotit.ff && versioninfo2->gotit.ff))) | |
| different = 1; | |
| if(!different && versioninfo1->gotit.ffm && versioninfo2->gotit.ffm) { | |
| if(versioninfo1->fileflagsmask != versioninfo2->fileflagsmask) | |
| different = 1; | |
| } else if(!different && | |
| ((versioninfo1->gotit.ffm && !versioninfo2->gotit.ffm) || | |
| (!versioninfo1->gotit.ffm && versioninfo2->gotit.ffm))) | |
| different = 1; | |
| if(!different && versioninfo1->gotit.ft && versioninfo2->gotit.ft) { | |
| if(versioninfo1->filetype != versioninfo2->filetype) | |
| different = 1; | |
| } else if(!different && | |
| ((versioninfo1->gotit.ft && !versioninfo2->gotit.ft) || | |
| (!versioninfo1->gotit.ft && versioninfo2->gotit.ft))) | |
| different = 1; | |
| if(!different && versioninfo1->gotit.fst && versioninfo2->gotit.fst) { | |
| if(versioninfo1->filesubtype != versioninfo2->filesubtype) | |
| different = 1; | |
| } else if(!different && | |
| ((versioninfo1->gotit.fst && !versioninfo2->gotit.fst) || | |
| (!versioninfo1->gotit.fst && versioninfo2->gotit.fst))) | |
| different = 1; | |
| if(!different) { | |
| ver_block1 = versioninfo1->blocks; | |
| ver_block2 = versioninfo2->blocks; | |
| while(!different && ver_block1 && ver_block2) { | |
| different = compare_ver_block(ver_block1, ver_block2); | |
| ver_block1 = ver_block1->next; | |
| ver_block2 = ver_block2->next; | |
| } | |
| if(!different && | |
| ((ver_block1 && !ver_block2) || | |
| (!ver_block1 && ver_block2))) | |
| different = 1; | |
| } | |
| return different; | |
| } | |
| static int compare_dlginit(dlginit_t *dlginit1, dlginit_t *dlginit2) { | |
| int different = 0; | |
| if(!different && | |
| ((dlginit1->memopt != dlginit2->memopt) || | |
| (dlginit1->data->lvc.version != dlginit2->data->lvc.version) || | |
| (dlginit1->data->lvc.characts != dlginit2->data->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_toolbar_item(toolbar_item_t *toolbar_item1, toolbar_item_t *toolbar_item2) { | |
| int different = 0; | |
| while(!different && toolbar_item1 && toolbar_item2) { | |
| if((toolbar_item1->id && !toolbar_item2->id) || | |
| (!toolbar_item1->id && toolbar_item2->id)) | |
| different = 1; | |
| toolbar_item1 = toolbar_item1->next; | |
| toolbar_item2 = toolbar_item2->next; | |
| } | |
| if(!different && | |
| ((toolbar_item1 && !toolbar_item2) || | |
| (!toolbar_item1 && toolbar_item2))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare_toolbar(toolbar_t *toolbar1, toolbar_t *toolbar2) { | |
| int different = 0; | |
| if(!different && | |
| ((toolbar1->memopt != toolbar2->memopt) || | |
| (toolbar1->lvc.version != toolbar2->lvc.version) || | |
| (toolbar1->lvc.characts != toolbar2->lvc.characts))) | |
| different = 1; | |
| if(!different) | |
| different = compare_toolbar_item(toolbar1->items, toolbar2->items); | |
| return different; | |
| } | |
| static int compare_ani_curico(ani_curico_t *ani_curico1, ani_curico_t *ani_curico2) { | |
| int different = 0; | |
| if(!different && | |
| ((ani_curico1->memopt != ani_curico2->memopt) || | |
| (ani_curico1->data->lvc.version != ani_curico2->data->lvc.version) || | |
| (ani_curico1->data->lvc.characts != ani_curico2->data->lvc.characts))) | |
| different = 1; | |
| return different; | |
| } | |
| static int compare(resource_t *resource1, resource_t *resource2) { | |
| switch(resource1->type) { | |
| case res_acc: | |
| return compare_accelerator(resource1->res.acc, resource2->res.acc); | |
| case res_bmp: | |
| return compare_bitmap(resource1->res.bmp, resource2->res.bmp); | |
| case res_cur: | |
| return compare_cursor(resource1->res.cur, resource2->res.cur); | |
| case res_curg: | |
| return compare_cursor_group(resource1->res.curg, resource2->res.curg); | |
| case res_dlg: | |
| return compare_dialog(resource1->res.dlg, resource2->res.dlg); | |
| case res_fnt: | |
| return compare_font(resource1->res.fnt, resource2->res.fnt); | |
| case res_fntdir: | |
| return compare_fontdir(resource1->res.fnd, resource2->res.fnd); | |
| case res_ico: | |
| return compare_icon(resource1->res.ico, resource2->res.ico); | |
| case res_icog: | |
| return compare_icon_group(resource1->res.icog, resource2->res.icog); | |
| case res_men: | |
| return compare_menu(resource1->res.men, resource2->res.men); | |
| case res_rdt: | |
| return compare_rcdata(resource1->res.rdt, resource2->res.rdt); | |
| case res_stt: | |
| return compare_stringtable(resource1->res.stt, resource2->res.stt); | |
| case res_usr: | |
| return compare_user(resource1->res.usr, resource2->res.usr); | |
| case res_html: | |
| return compare_html(resource1->res.html, resource2->res.html); | |
| case res_msg: | |
| return compare_messagetable(resource1->res.msg, resource2->res.msg); | |
| case res_ver: | |
| return compare_versioninfo(resource1->res.ver, resource2->res.ver); | |
| case res_dlginit: | |
| return compare_dlginit(resource1->res.dlgi, resource2->res.dlgi); | |
| case res_toolbar: | |
| return compare_toolbar(resource1->res.tbt, resource2->res.tbt); | |
| case res_anicur: | |
| case res_aniico: | |
| return compare_ani_curico(resource1->res.ani, resource2->res.ani); | |
| default: | |
| /* Not supposed to reach here */ | |
| fprintf(stderr, "Not supposed to reach here (compare())\n"); | |
| abort(); | |
| return -1; | |
| } | |
| } | |
| typedef struct resource_lang_node | |
| { | |
| language_t lang; | |
| resource_t *res; | |
| struct resource_lang_node *next; | |
| } resource_lang_node_t; | |
| typedef struct resource_id_node | |
| { | |
| name_id_t *id; | |
| resource_lang_node_t *langs; | |
| struct resource_id_node *next; | |
| } resource_id_node_t; | |
| static struct | |
| { | |
| int enabled; | |
| struct resource_id_node *ids; | |
| } verify_tab[res_usr+1]; | |
| static void add_resource(resource_t *res) | |
| { | |
| resource_id_node_t *idnode; | |
| resource_lang_node_t *langnode; | |
| if (!verify_tab[res->type].enabled) | |
| { | |
| fprintf(stderr, "ERR: Report this: unknown resource type parsed %08x\n", res->type); | |
| return; | |
| } | |
| for (idnode = verify_tab[res->type].ids; idnode; idnode = idnode->next) | |
| if (compare_name_id(idnode->id, res->name) == 0) | |
| break; | |
| if (idnode == NULL) | |
| { | |
| idnode = xmalloc(sizeof(resource_id_node_t)); | |
| idnode->id = res->name; | |
| idnode->langs = NULL; | |
| idnode->next = verify_tab[res->type].ids; | |
| verify_tab[res->type].ids = idnode; | |
| } | |
| for (langnode = idnode->langs; langnode; langnode = langnode->next) | |
| if (compare_lang(langnode->lang, get_language(res)) == 0) | |
| { | |
| fprintf(stderr, "ERR: resource %s [type %x] language %03x:%02x duplicated!\n", | |
| get_nameid_str(res->name), res->type, langnode->lang.id, langnode->lang.sub); | |
| return; | |
| } | |
| langnode = xmalloc(sizeof(resource_lang_node_t)); | |
| langnode->res = res; | |
| langnode->lang = get_language(res); | |
| langnode->next = idnode->langs; | |
| idnode->langs = langnode; | |
| } | |
| static void setup_tabs(void) | |
| { | |
| int i; | |
| for (i = 0; i <= res_usr; i++) | |
| switch(i) { | |
| case res_acc: | |
| case res_bmp: | |
| case res_cur: | |
| case res_curg: | |
| case res_dlg: | |
| case res_fnt: | |
| case res_fntdir: | |
| case res_ico: | |
| case res_icog: | |
| case res_men: | |
| case res_rdt: | |
| case res_stt: | |
| case res_usr: | |
| case res_msg: | |
| case res_ver: | |
| case res_dlginit: | |
| case res_toolbar: | |
| case res_anicur: | |
| case res_aniico: | |
| case res_html: | |
| verify_tab[i].enabled = 1; | |
| break; | |
| } | |
| } | |
| static const char *get_typename_for_int(int type) { | |
| resource_t res; | |
| res.type = type; | |
| return get_typename(&res); | |
| } | |
| static resource_t *find_main(int type, name_id_t *id, resource_lang_node_t *langnode) | |
| { | |
| resource_t *neutral = NULL, *en = NULL, *en_US = NULL; | |
| for (; langnode; langnode = langnode->next) | |
| { | |
| if (langnode->lang.id == LANG_NEUTRAL && langnode->lang.sub == SUBLANG_NEUTRAL) | |
| neutral = langnode->res; | |
| if (langnode->lang.id == MASTER_LANGUAGE && langnode->lang.sub == SUBLANG_NEUTRAL) | |
| en = langnode->res; | |
| if (langnode->lang.id == MASTER_LANGUAGE && langnode->lang.sub == MASTER_SUBLANGUAGE) | |
| en_US = langnode->res; | |
| } | |
| if (neutral != NULL && (en != NULL || en_US != NULL)) | |
| { | |
| fprintf(stderr, "INFO: Resource %04x/%s has both NEUTRAL and MASTER language translarion\n", | |
| type, get_nameid_str(id)); | |
| } | |
| if (en_US != NULL) return en_US; | |
| if (en != NULL) return en; | |
| return neutral; | |
| } | |
| void verify_translations(resource_t *top) { | |
| resource_t *curr = top; | |
| resource_id_node_t *idnode; | |
| resource_lang_node_t *langnode; | |
| int type; | |
| setup_tabs(); | |
| while (curr) | |
| { | |
| add_resource(curr); | |
| curr = curr->next; | |
| } | |
| for (type = 0; type <= res_usr; type++) | |
| { | |
| printf("TYPE NEXT [%s]\n", get_typename_for_int(type)); | |
| for (idnode = verify_tab[type].ids; idnode; idnode = idnode->next) | |
| { | |
| resource_t *mainres; | |
| printf("RESOURCE [%s]\n", get_nameid_str(idnode->id)); | |
| mainres = find_main(type, idnode->id, idnode->langs); | |
| if (!mainres) | |
| { | |
| fprintf(stderr, "ERR: resource %04x/%s has translation(s) but not available in NEUTRAL or MASTER language\n", | |
| type, get_nameid_str(idnode->id)); | |
| for (langnode = idnode->langs; langnode; langnode = langnode->next) | |
| printf("EXTRA %03x:%02x\n", langnode->lang.id, langnode->lang.sub); | |
| continue; | |
| } | |
| if (get_language_id(mainres) == LANG_NEUTRAL && idnode->langs->next == NULL) { | |
| printf("NOTRANSL\n"); | |
| continue; | |
| } | |
| for (langnode = idnode->langs; langnode; langnode = langnode->next) | |
| { | |
| printf("EXIST %03x:%02x\n", langnode->lang.id, langnode->lang.sub); | |
| if (compare(langnode->res, mainres)) | |
| { | |
| printf("DIFF %03x:%02x\n", langnode->lang.id, langnode->lang.sub); | |
| } | |
| } | |
| } | |
| } | |
| } |