Exemple #1
0
int CEXEBuild::GenerateLangTables() {
  int i;
  LanguageTable *lt = (LanguageTable*)lang_tables.get();

  SCRIPT_MSG("Generating language tables... ");

  if (
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
      ubuild_langstring_num > MAX_CODED ||
#endif
      build_langstring_num > MAX_CODED
     )
  {
    ERROR_MSG("\nError: too many LangStrings. Maximum allowed is %u.\n", MAX_CODED);
    return PS_ERROR;
  }

  // If we have no tables (user didn't set any string and didn't load any NLF) create the default one
  if (!lang_tables.getlen()) {
    LANGID lang = NSIS_DEFAULT_LANG;
    LanguageTable *table = GetLangTable(lang);
    if (!table) return PS_ERROR;

    lt = (LanguageTable*)lang_tables.get();
  }

  // Apply default font
  if (*build_font)
  {
    try {
      init_res_editor();

#define ADD_FONT(id) { \
        BYTE* dlg = res_editor->GetResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); \
        if (dlg) { \
          CDialogTemplate td(dlg); \
          free(dlg); \
          td.SetFont(build_font, build_font_size); \
          DWORD dwSize; \
          dlg = td.Save(dwSize); \
          res_editor->UpdateResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), dlg, dwSize); \
          free(dlg); \
        } \
      }

#ifdef NSIS_CONFIG_LICENSEPAGE
      ADD_FONT(IDD_LICENSE);
      ADD_FONT(IDD_LICENSE_FSRB);
      ADD_FONT(IDD_LICENSE_FSCB);
#endif
      ADD_FONT(IDD_DIR);
#ifdef NSIS_CONFIG_COMPONENTPAGE
      ADD_FONT(IDD_SELCOM);
#endif
      ADD_FONT(IDD_INST);
      ADD_FONT(IDD_INSTFILES);
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
      ADD_FONT(IDD_UNINST);
#endif
#ifdef NSIS_CONFIG_CRC_SUPPORT
      ADD_FONT(IDD_VERIFY);
#endif
#undef ADD_FONT
    }
    catch (exception& err) {
      ERROR_MSG("\nError while applying font: %s\n", err.what());
      return PS_ERROR;
    }
  }

  // Fill tables with defaults (if needed) and with instruction strings
  // Create language specific resources (currently only dialogs with different fonts)
  int num_lang_tables = lang_tables.getlen() / sizeof(LanguageTable);
  // if there is one string table then there is no need for two sets of dialogs
  int cur_offset = num_lang_tables == 1 ? 0 : 100;
  for (i = 0; i < num_lang_tables; i++)
  {
    if ((lt[i].nlf.m_szFont && !*build_font) || lt[i].nlf.m_bRTL)
    {
      lt[i].dlg_offset = cur_offset;

      char *font = lt[i].nlf.m_szFont;
      if (*build_font) font = 0;

      try {
        init_res_editor();

#define ADD_FONT(id) { \
          BYTE* dlg = res_editor->GetResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); \
          if (dlg) { \
            CDialogTemplate td(dlg,lt[i].nlf.m_uCodePage); \
            free(dlg); \
            if (font) td.SetFont(font, lt[i].nlf.m_iFontSize); \
            if (lt[i].nlf.m_bRTL) td.ConvertToRTL(); \
            DWORD dwSize; \
            dlg = td.Save(dwSize); \
            res_editor->UpdateResource(RT_DIALOG, MAKEINTRESOURCE(id+cur_offset), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), dlg, dwSize); \
            free(dlg); \
          } \
        }

#ifdef NSIS_CONFIG_LICENSEPAGE
        ADD_FONT(IDD_LICENSE);
        ADD_FONT(IDD_LICENSE_FSRB);
        ADD_FONT(IDD_LICENSE_FSCB);
#endif
        ADD_FONT(IDD_DIR);
#ifdef NSIS_CONFIG_COMPONENTPAGE
        ADD_FONT(IDD_SELCOM);
#endif
        ADD_FONT(IDD_INST);
        ADD_FONT(IDD_INSTFILES);
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
        ADD_FONT(IDD_UNINST);
#endif
#ifdef NSIS_CONFIG_CRC_SUPPORT
        ADD_FONT(IDD_VERIFY);
#endif
#undef ADD_FONT
      }
      catch (exception& err) {
        ERROR_MSG("\nError while applying NLF font/RTL: %s\n", err.what());
        return PS_ERROR;
      }

      cur_offset += 100;
    }
  }

  // Add all installer language strings
  int j, l, tabsset;
  struct langstring* lang_strings;
  TinyGrowBuf *string_ptrs = new TinyGrowBuf[num_lang_tables];

  tabsset = 1;
  while (tabsset)
  {
    tabsset = 0;
    for (i = num_lang_tables; i--; )
    {
      // Fill in default values for all used language strings that we can
      FillLanguageTable(&lt[i]);
      // Make sure the string lists are large enough
      string_ptrs[i].set_zeroing(1);
      string_ptrs[i].resize(build_langstring_num * sizeof(int));
    }

    // For all current language strings
    lang_strings = build_langstrings.sort_index(&l);
    for (j = 0; j < l; j++)
    {
      // Is this language string used (in the installer)?
      if (lang_strings[j].index >= 0)
      {
        // For each language
        for (i = num_lang_tables; i--; )
        {
          // Get the current string pointer
          int *ptr = (int *)string_ptrs[i].get() + lang_strings[j].index;
          // Not already set?
          if (!*ptr)
          {
            // Get the language string and its name
            const char *str = lt[i].lang_strings->get(lang_strings[j].sn);
            const char *lsn = build_langstrings.offset2name(lang_strings[j].name);
            if (!str || !*str)
            {
              // No string is defined; give a warning (for user strings only)
              if (lsn[0] != '^')
                warning("LangString \"%s\" is not set in language table of language %d", lsn, lt[i].lang_id);
            }
            else
            {
              // Add the language string to the string data block
              char fn[1024];
              sprintf(fn, "LangString %s", lsn);
              curfilename = fn;
              linecnt = lt[i].lang_id;
              *ptr = add_string(str, lang_strings[j].process, lt[i].nlf.m_uCodePage);
              curfilename = 0;
              // Indicate that we should check again for any newly referenced language strings
              tabsset++;
            }
          }
        }
      }
    }
  }

  // Optimize langstrings and check for recursion
  for (i = num_lang_tables; i--; )
  {
    TinyGrowBuf rec;
    int *lst = (int *)string_ptrs[i].get();
    for (j = 0; j < build_langstring_num; j++)
    {
      // Does this string reference another language string directly?
      while (lst[j] < 0)
      {
        // Search through list of language string references
        for (l = 0; (unsigned int)l < rec.getlen() / sizeof(int); l++)
        {
          if (((int*)rec.get())[l] == lst[j])
          {
            // We have the index of a recursive language string; now find the name
            const char *name = "(unnamed)";
            for (l = 0; l < build_langstring_num; l++)
              if (lang_strings[l].index == j)
                name = build_langstrings.offset2name(lang_strings[l].name);
            ERROR_MSG("Error: LangString %s is recursive!\n", name);
            delete [] string_ptrs;
            return PS_ERROR;
          }
        }
        // Add this reference to the list
        rec.add(&lst[j], sizeof(int));
        // and dereference it
        lst[j] = lst[-lst[j] - 1];
      }
      rec.resize(0);
    }
  }

  // Add language tables into their datablock
  for (i = num_lang_tables; i--; )
  {
    build_langtables.add(&lt[i].lang_id, sizeof(LANGID));
    build_langtables.add(&lt[i].dlg_offset, sizeof(int));
    int rtl = lt[i].nlf.m_bRTL ? 1 : 0;
    build_langtables.add(&rtl, sizeof(int));
    build_langtables.add(string_ptrs[i].get(), string_ptrs[i].getlen());
    string_ptrs[i].resize(0);
  }

  build_header.blocks[NB_LANGTABLES].num = num_lang_tables;
  build_header.langtable_size = build_langtables.getlen() / num_lang_tables;

#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  // Now do it all again, this time for the uninstaller
  set_uninstall_mode(1);

  tabsset = 1;
  while (tabsset)
  {
    tabsset = 0;
    for (i = num_lang_tables; i--; )
    {
      // Fill in default values for all used language strings that we can
      FillLanguageTable(&lt[i]);
      // Make sure the string lists are large enough
      string_ptrs[i].set_zeroing(1);
      string_ptrs[i].resize(ubuild_langstring_num * sizeof(int));
    }

    // For all current language strings
    lang_strings = build_langstrings.sort_uindex(&l);
    for (j = 0; j < l; j++)
    {
      // Is this language string used (in the uninstaller)?
      if (lang_strings[j].uindex >= 0)
      {
        // For each language
        for (i = num_lang_tables; i--; )
        {
          // Get the current string pointer
          int *ptr = (int *)string_ptrs[i].get() + lang_strings[j].uindex;
          // Not already set?
          if (!*ptr)
          {
            // Get the language string and its name
            const char *str = lt[i].lang_strings->get(lang_strings[j].sn);
            const char *lsn = build_langstrings.offset2name(lang_strings[j].name);
            if (!str || !*str)
            {
              // No string is defined; give a warning (for user strings only)
              if (lsn[0] != '^')
                warning("LangString \"%s\" is not set in language table of language %d", lsn, lt[i].lang_id);
            }
            else
            {
              // Add the language string to the string data block
              char fn[1024];
              sprintf(fn, "LangString %s", lsn);
              curfilename = fn;
              linecnt = lt[i].lang_id;
              *ptr = add_string(str, lang_strings[j].process, lt[i].nlf.m_uCodePage);
              curfilename = 0;
              // Indicate that we should check again for any newly referenced language strings
              tabsset++;
            }
          }
        }
      }
    }
  }

  // Optimize langstrings and check for recursion
  for (i = num_lang_tables; i--; )
  {
    TinyGrowBuf rec;
    int *lst = (int *)string_ptrs[i].get();
    for (j = 0; j < ubuild_langstring_num; j++)
    {
      // Does this string reference another language string directly?
      while (lst[j] < 0)
      {
        // Search through list of language string references
        for (l = 0; (unsigned int)l < rec.getlen() / sizeof(int); l++)
        {
          if (((int*)rec.get())[l] == lst[j])
          {
            // We have the index of a recursive language string; now find the name
            const char *name = "(unnamed)";
            for (l = 0; l < ubuild_langstring_num; l++)
              if (lang_strings[l].uindex == j)
                name = build_langstrings.offset2name(lang_strings[l].name);
            ERROR_MSG("Error: LangString %s is recursive!\n", name);
            delete [] string_ptrs;
            return PS_ERROR;
          }
        }
        // Add this reference to the list
        rec.add(&lst[j], sizeof(int));
        // and dereference it
        lst[j] = lst[-lst[j] - 1];
      }
      rec.resize(0);
    }
  }

  // Add language tables into their datablock
  for (i = num_lang_tables; i--; )
  {
    ubuild_langtables.add(&lt[i].lang_id, sizeof(LANGID));
    ubuild_langtables.add(&lt[i].dlg_offset, sizeof(int));
    int rtl = lt[i].nlf.m_bRTL ? 1 : 0;
    ubuild_langtables.add(&rtl, sizeof(int));
    ubuild_langtables.add(string_ptrs[i].get(), string_ptrs[i].getlen());
    string_ptrs[i].resize(0);
  }

  build_uninst.blocks[NB_LANGTABLES].num = num_lang_tables;
  build_uninst.langtable_size = ubuild_langtables.getlen() / num_lang_tables;

  set_uninstall_mode(0);
#endif

  SCRIPT_MSG("Done!\n");

  delete [] string_ptrs;
  return PS_OK;
}
Exemple #2
0
int CEXEBuild::WriteStringTables() {
    int i;

    SCRIPT_MSG("Generating language tables... ");

    // If we have no tables (user didn't set any string and didn't load any NLF) create the default one
    if (!string_tables.size()) {
        LANGID lang = 1033;
        StringTable *table = GetTable(lang);
        if (!table) return PS_ERROR;
    }

    // Fill tables with defaults (if needed) and with instruction strings
    int st_num = string_tables.size();
    for (i = 0; i < st_num; i++)
        FillDefaultsIfNeeded(string_tables[i]);

    // check for missing LangStrings
    int userstrings_num = build_userlangstrings.getnum();
    for (i = 0; i < userstrings_num; i++) {
        int counter = 0;
        for (int j = 0; j < st_num; j++) {
            counter += !((int*)string_tables[j]->user_strings.get())[i];
        }
        if (counter) {
            int offset=build_userlangstrings.idx2pos(i);
            if (offset<0) continue;
            warning("LangString \"%s\" is not present in all language tables!", build_userlangstrings.get()+offset);
        }
    }

    int userustrings_num = ubuild_userlangstrings.getlen();
    for (i = 0; i < userustrings_num; i++) {
        int counter = 0;
        for (int j = 0; j < st_num; j++) {
            counter += !((int*)string_tables[j]->user_ustrings.get())[i];
        }
        if (counter) {
            int offset=ubuild_userlangstrings.idx2pos(i);
            if (offset<0) continue;
            warning("LangString \"%s\" is not present in all language tables!", ubuild_userlangstrings.get()+offset);
        }
    }

    // Add string tables into their datablock
    for (i = 0; i < st_num; i++) {
        build_langtables.add(&string_tables[i]->lang_id, sizeof(LANGID));
        build_langtables.add(&string_tables[i]->common, sizeof(common_strings));
        build_langtables.add(&string_tables[i]->installer, sizeof(installer_strings));
        if (build_userlangstrings.getnum())
            build_langtables.add(string_tables[i]->user_strings.get(), string_tables[i]->user_strings.getlen());
    }
    build_header.common.language_tables_num = st_num;
    build_header.common.language_table_size = build_langtables.getlen() / st_num;

    for (i = 0; i < st_num; i++) {
        ubuild_langtables.add(&string_tables[i]->lang_id, sizeof(LANGID));
        ubuild_langtables.add(&string_tables[i]->ucommon, sizeof(common_strings));
        ubuild_langtables.add(&string_tables[i]->uninstall, sizeof(uninstall_strings));
        if (ubuild_userlangstrings.getnum())
            ubuild_langtables.add(string_tables[i]->user_ustrings.get(), string_tables[i]->user_ustrings.getlen());
    }
    build_uninst.common.language_tables_num = st_num;
    build_uninst.common.language_table_size = ubuild_langtables.getlen() / st_num;
    SCRIPT_MSG("Done!\n");

    return PS_OK;
}
Exemple #3
0
int CEXEBuild::GenerateLangTables() {
  int i;
  LanguageTable *lt = (LanguageTable*)lang_tables.get();

  SCRIPT_MSG("Generating language tables... ");

  if (
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
      ubuild_langstring_num > MAX_CODED ||
#endif
      build_langstring_num > MAX_CODED
     )
  {
    ERROR_MSG("\nError: too many LangStrings. Maximum allowed is %u.\n", MAX_CODED);
    return PS_ERROR;
  }

  // If we have no tables (user didn't set any string and didn't load any NLF) create the default one
  if (!lang_tables.getlen()) {
    LANGID lang = NSIS_DEFAULT_LANG;
    LanguageTable *table = GetLangTable(lang);
    if (!table) return PS_ERROR;

    lt = (LanguageTable*)lang_tables.get();
  }

  // Apply default font
  if (*build_font)
  {
    try {
      init_res_editor();

#define ADD_FONT(id) { \
        BYTE* dlg = res_editor->GetResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); \
        if (dlg) { \
          CDialogTemplate td(dlg); \
          free(dlg); \
          td.SetFont(build_font, build_font_size); \
          DWORD dwSize; \
          dlg = td.Save(dwSize); \
          res_editor->UpdateResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), dlg, dwSize); \
          free(dlg); \
        } \
      }

#ifdef NSIS_CONFIG_LICENSEPAGE
      ADD_FONT(IDD_LICENSE);
      ADD_FONT(IDD_LICENSE_FSRB);
      ADD_FONT(IDD_LICENSE_FSCB);
#endif
      ADD_FONT(IDD_DIR);
#ifdef NSIS_CONFIG_COMPONENTPAGE
      ADD_FONT(IDD_SELCOM);
#endif
      ADD_FONT(IDD_INST);
      ADD_FONT(IDD_INSTFILES);
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
      ADD_FONT(IDD_UNINST);
#endif
#ifdef NSIS_CONFIG_CRC_SUPPORT
      ADD_FONT(IDD_VERIFY);
#endif
#undef ADD_FONT
    }
    catch (exception& err) {
      ERROR_MSG("\nError while applying font: %s\n", err.what());
      return PS_ERROR;
    }
  }

  // Fill tables with defaults (if needed) and with instruction strings
  // Create language specific resources (currently only dialogs with different fonts)
  int num_lang_tables = lang_tables.getlen() / sizeof(LanguageTable);
  // if there is one string table then there is no need for two sets of dialogs
  int cur_offset = num_lang_tables == 1 ? 0 : 100;
  for (i = 0; i < num_lang_tables; i++)
  {
    if ((lt[i].nlf.m_szFont && !*build_font) || lt[i].nlf.m_bRTL)
    {
      lt[i].dlg_offset = cur_offset;

      char *font = lt[i].nlf.m_szFont;
      if (*build_font) font = 0;

      try {
        init_res_editor();

#define ADD_FONT(id) { \
          BYTE* dlg = res_editor->GetResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); \
          if (dlg) { \
            CDialogTemplate td(dlg,lt[i].nlf.m_uCodePage); \
            free(dlg); \
            if (font) td.SetFont(font, lt[i].nlf.m_iFontSize); \
            if (lt[i].nlf.m_bRTL) td.ConvertToRTL(); \
            DWORD dwSize; \
            dlg = td.Save(dwSize); \
            res_editor->UpdateResource(RT_DIALOG, MAKEINTRESOURCE(id+cur_offset), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), dlg, dwSize); \
            free(dlg); \
          } \
        }

#ifdef NSIS_CONFIG_LICENSEPAGE
        ADD_FONT(IDD_LICENSE);
        ADD_FONT(IDD_LICENSE_FSRB);
        ADD_FONT(IDD_LICENSE_FSCB);
#endif
        ADD_FONT(IDD_DIR);
#ifdef NSIS_CONFIG_COMPONENTPAGE
        ADD_FONT(IDD_SELCOM);
#endif
        ADD_FONT(IDD_INST);
        ADD_FONT(IDD_INSTFILES);
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
        ADD_FONT(IDD_UNINST);
#endif
#ifdef NSIS_CONFIG_CRC_SUPPORT
        ADD_FONT(IDD_VERIFY);
#endif
#undef ADD_FONT
      }
      catch (exception& err) {
        ERROR_MSG("\nError while applying NLF font/RTL: %s\n", err.what());
        return PS_ERROR;
      }

      cur_offset += 100;
    }
  }

  // Add language tables into their datablock
  int j, l, cnt, tabsset;
  struct langstring* lang_strings;

  i = num_lang_tables;
  while (i--) {
    build_langtables.add(&lt[i].lang_id, sizeof(LANGID));
    build_langtables.add(&lt[i].dlg_offset, sizeof(int));
    {
      int rtl = lt[i].nlf.m_bRTL ? 1 : 0;
      build_langtables.add(&rtl, sizeof(int));
    }

    int *lst = NULL;
    unsigned int oldlen = build_langtables.getlen();

    cnt = 0;
    tabsset = 1;

    // write langstrings
    while (tabsset) {
      FillLanguageTable(&lt[i]);

      int lastcnt = cnt;
      cnt = 0;
      tabsset = 0;

      lang_strings = build_langstrings.sort_index(&l);

      for (j = 0; j < l; j++) {
        lst = (int *)((char *)build_langtables.get() + oldlen);
        if (lang_strings[j].index >= 0) {
          if (cnt >= lastcnt || !lst[lang_strings[j].index]) {
            const char *str = lt[i].lang_strings->get(lang_strings[j].sn);
            int tab = 0;

            const char *lsn = build_langstrings.offset2name(lang_strings[j].name);

            if (!str || !*str) {
              if (lsn[0] != '^')
                warning("LangString \"%s\" is not set in language table of language %d", lsn, lt[i].lang_id);
            }
            else {
              char fn[1024];
              sprintf(fn, "LangString %s", lsn);
              curfilename = fn;
              linecnt = lt[i].lang_id;

              tab = add_string(str, lang_strings[j].process, lt[i].nlf.m_uCodePage);
              tabsset++;

              curfilename = 0;
            }

            if (cnt < lastcnt)
              lst[lang_strings[j].index] = tab;
            else
              build_langtables.add(&tab, sizeof(int));
          }

          cnt++;
        }
      }
    }

    lst = (int *)((char *)build_langtables.get() + oldlen);

    // optimize langstrings and check for recursion
    TinyGrowBuf rec;
    for (j = 0; j < build_langstring_num; j++) {
      while (lst[j] < 0) {
        for (int k = 0; (unsigned int)k < rec.getlen() / sizeof(int); k++) {
          if (((int*)rec.get())[k] == lst[j]) {
            const char *name = "(unnamed)";
            for (k = 0; k < l; k++) {
              if (lang_strings[k].index == j) {
                name = build_langstrings.offset2name(lang_strings[k].name);
              }
            }
            ERROR_MSG("Error: LangString %s is recursive!\n", name);
            return PS_ERROR;
          }
        }
        rec.add(&lst[j], sizeof(int));
        lst[j] = lst[-lst[j] - 1];
      }
      rec.resize(0);
    }
  }

  build_header.blocks[NB_LANGTABLES].num = num_lang_tables;
  build_header.langtable_size = build_langtables.getlen() / num_lang_tables;

#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  set_uninstall_mode(1);

  i = num_lang_tables;
  while (i--) {
    ubuild_langtables.add(&lt[i].lang_id, sizeof(LANGID));
    ubuild_langtables.add(&lt[i].dlg_offset, sizeof(int));
    {
      int rtl = lt[i].nlf.m_bRTL ? 1 : 0;
      ubuild_langtables.add(&rtl, sizeof(int));
    }

    int *lst = NULL;
    unsigned int oldlen = ubuild_langtables.getlen();

    cnt = 0;
    tabsset = 1;

    // write langstrings
    while (tabsset) {
      FillLanguageTable(&lt[i]);

      int lastcnt = cnt;
      cnt = 0;
      tabsset = 0;

      lang_strings = build_langstrings.sort_uindex(&l);

      for (j = 0; j < l; j++) {
        lst = (int *)((char *)ubuild_langtables.get() + oldlen);
        if (lang_strings[j].uindex >= 0) {
          if (cnt >= lastcnt || !lst[lang_strings[j].uindex]) {
            const char *str = lt[i].lang_strings->get(lang_strings[j].sn);
            int tab = 0;

            const char *lsn = build_langstrings.offset2name(lang_strings[j].name);

            if (!str || !*str) {
              if (lsn[0] != '^')
                warning("LangString \"%s\" is not set in language table of language %d", lsn, lt[i].lang_id);
            }
            else {
              char fn[1024];
              sprintf(fn, "LangString %s", lsn);
              curfilename = fn;
              linecnt = lt[i].lang_id;

              tab = add_string(str, lang_strings[j].process, lt[i].nlf.m_uCodePage);
              tabsset++;

              curfilename = 0;
            }

            if (cnt < lastcnt)
              lst[lang_strings[j].uindex] = tab;
            else
              ubuild_langtables.add(&tab, sizeof(int));
          }

          cnt++;
        }
      }
    }

    lst = (int *)((char *)ubuild_langtables.get() + oldlen);

    // optimize langstrings and check for recursion
    TinyGrowBuf rec;
    for (j = 0; j < ubuild_langstring_num; j++) {
      while (lst[j] < 0) {
        for (int k = 0; (unsigned int)k < rec.getlen() / sizeof(int); k++) {
          if (((int*)rec.get())[k] == lst[j]) {
            const char *name = "(unnamed)";
            for (k = 0; k < l; k++) {
              if (lang_strings[k].uindex == j) {
                name = build_langstrings.offset2name(lang_strings[k].name);
              }
            }
            ERROR_MSG("Error: LangString %s is recursive!\n", name);
            return PS_ERROR;
          }
        }
        rec.add(&lst[j], sizeof(int));
        lst[j] = lst[-lst[j] - 1];
      }
      rec.resize(0);
    }
  }

  build_uninst.blocks[NB_LANGTABLES].num = num_lang_tables;
  build_uninst.langtable_size = ubuild_langtables.getlen() / num_lang_tables;

  set_uninstall_mode(0);
#endif
  
  SCRIPT_MSG("Done!\n");

  return PS_OK;
}