diff --git a/Contrib/ExDLL/SConscript b/Contrib/ExDLL/SConscript index 55380302..bf578cb6 100644 --- a/Contrib/ExDLL/SConscript +++ b/Contrib/ExDLL/SConscript @@ -24,19 +24,27 @@ example = Split(""" extdll.inc """) -Import('env plugin_env') +Import('env plugin_env plugin_uenv') # build library - -api_env = env.Clone() -api_env.Append(CPPPATH = ['#Source/exehead']) -lib = api_env.Library(lib_target, lib_files) +# in Unicode compilation, we should really build both variants but I can't get it to work [Wizou] +if env['UNICODE']: + api_uenv = plugin_uenv.Clone() + api_uenv.Append(CPPPATH = ['#Source/exehead']) + libW = api_uenv.Library(lib_target+'W', lib_files) + lib = libW +else: + api_env = plugin_env.Clone() + api_env.Append(CPPPATH = ['#Source/exehead']) + lib = api_env.Library(lib_target, lib_files) # distribute library, files and examples env.DistributeExamples(api_files, path='Plugin/nsis') if env['PLATFORM'] == 'win32': + if env['UNICODE']: + env.DistributeExamples(libW, lib_target+".lib", path='Plugin/nsis') env.DistributeExamples(lib, path='Plugin/nsis') else: @@ -57,3 +65,6 @@ env.Install('#$BUILD_PREFIX/api/nsis', api_files + lib) plugin_env.Append(CPPPATH = ['#$BUILD_PREFIX/api']) plugin_env.Append(LIBPATH = ['#$BUILD_PREFIX/api/nsis']) plugin_env.Append(LIBS = [lib_target]) +plugin_uenv.Append(CPPPATH = ['#$BUILD_PREFIX/api']) +plugin_uenv.Append(LIBPATH = ['#$BUILD_PREFIX/api/nsis']) +plugin_uenv.Append(LIBS = [lib_target+'W']) diff --git a/Contrib/InstallOptions/InstallOptions.nsh b/Contrib/InstallOptions/InstallOptions.nsh index 1038ae25..61687e15 100644 --- a/Contrib/InstallOptions/InstallOptions.nsh +++ b/Contrib/InstallOptions/InstallOptions.nsh @@ -126,7 +126,7 @@ Macros and conversion functions for InstallOptions InitPluginsDir File "/oname=$PLUGINSDIR\${FILE}" "${FILE}" !ifdef NSIS_UNICODE - InstallOptions::make_unicode "$PLUGINSDIR\${FILE}" + InstallOptionsW::make_unicode "$PLUGINSDIR\${FILE}" !endif !insertmacro INSTALLOPTIONS_WRITE "${FILE}" "Settings" "RTL" "$(^RTL)" @@ -139,7 +139,7 @@ Macros and conversion functions for InstallOptions InitPluginsDir File "/oname=$PLUGINSDIR\${FILENAME}" "${FILE}" !ifdef NSIS_UNICODE - InstallOptions::make_unicode "$PLUGINSDIR\${FILENAME}" + InstallOptionsW::make_unicode "$PLUGINSDIR\${FILENAME}" !endif !insertmacro INSTALLOPTIONS_WRITE "${FILENAME}" "Settings" "RTL" "$(^RTL)" diff --git a/Contrib/Makensisw/makensisw.cpp b/Contrib/Makensisw/makensisw.cpp index 4f9d4a5c..d204a9df 100644 --- a/Contrib/Makensisw/makensisw.cpp +++ b/Contrib/Makensisw/makensisw.cpp @@ -706,7 +706,9 @@ BOOL CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { } DWORD WINAPI MakeNSISProc(LPVOID p) { +#ifdef _UNICODE TCHAR buf[1024]; +#endif char iobuf[1024]; //i/o buffer STARTUPINFO si={sizeof(si),}; SECURITY_ATTRIBUTES sa={sizeof(sa),}; diff --git a/SCons/Config/default b/SCons/Config/default index e0809334..32e1b961 100644 --- a/SCons/Config/default +++ b/SCons/Config/default @@ -53,4 +53,4 @@ test_env.Append(CPPPATH = ['#$BUILD_CONFIG']) # return -Return('stub_env makensis_env plugin_env util_env cp_util_env test_env') +Return('stub_env makensis_env plugin_env util_env cp_util_env test_env stub_env plugin_env') diff --git a/SCons/Config/gnu b/SCons/Config/gnu index 40009131..dc5498b5 100644 --- a/SCons/Config/gnu +++ b/SCons/Config/gnu @@ -309,4 +309,4 @@ if makensis_env['PLATFORM'] == 'hpux': ### return -Return('stub_env makensis_env plugin_env util_env cp_util_env test_env') +Return('stub_env makensis_env plugin_env util_env cp_util_env test_env stub_env plugin_env') diff --git a/SCons/Config/hpc++ b/SCons/Config/hpc++ index a6b5a158..4c1a4359 100644 --- a/SCons/Config/hpc++ +++ b/SCons/Config/hpc++ @@ -125,4 +125,4 @@ test_env.Append(CPPPATH = ['#$BUILD_CONFIG']) # return -Return('stub_env makensis_env plugin_env util_env cp_util_env test_env') +Return('stub_env makensis_env plugin_env util_env cp_util_env test_env stub_env plugin_env') diff --git a/SCons/Config/ms b/SCons/Config/ms index ab6e46f5..11c22989 100644 --- a/SCons/Config/ms +++ b/SCons/Config/ms @@ -41,8 +41,9 @@ if defenv['DEBUG']: defenv.Append(LINKFLAGS = ['/debug']) ### unicode -if defenv['UNICODE']: - defenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE']) +tdefenv = defenv.Clone() +if tdefenv['UNICODE']: + tdefenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE']) ### workarounds @@ -101,19 +102,19 @@ stub_env.Append(CCFLAGS = ['/W3']) # level 3 warnings stub_env.Append(LINKFLAGS = ['/opt:nowin98']) # 512 bytes align -if defenv['UNICODE']: - stub_env.Append(LINKFLAGS = ['/entry:wWinMain']) # Unicode entry point -else: - stub_env.Append(LINKFLAGS = ['/entry:WinMain']) # ANSI entry point - stub_env.Append(LINKFLAGS = ['$NODEFLIBS_FLAG']) # no default libraries stub_env.Append(LINKFLAGS = ['$MAP_FLAG']) # generate map file stub_env.Append(CCFLAGS = ['/FAcs']) # full listing files stub_env.Append(CCFLAGS = ['/Fa${TARGET}.lst']) # listing file name +stub_uenv = stub_env.Clone() +stub_uenv.Append(LINKFLAGS = ['/entry:wWinMain']) # Unicode entry point +stub_uenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE']) +stub_env.Append(LINKFLAGS = ['/entry:WinMain']) # ANSI entry point + ### makensis environment -makensis_env = defenv.Clone() +makensis_env = tdefenv.Clone() makensis_env.Append(CPPPATH = ['#$BUILD_CONFIG']) @@ -140,9 +141,12 @@ plugin_env.Append(CCFLAGS = ['/W3']) # level 3 warnings plugin_env.Append(LINKFLAGS = ['/opt:nowin98']) # 512 bytes align plugin_env.Append(LINKFLAGS = ['$MAP_FLAG']) # generate map file +plugin_uenv = plugin_env.Clone() +plugin_uenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE']) + ### util environment -util_env = defenv.Clone() +util_env = tdefenv.Clone() if not defenv['DEBUG']: util_env.Append(CCFLAGS = ['/O1']) # optimize for speed @@ -209,8 +213,10 @@ def add_file_to_emitter(env, emitter_name, file): def add_file(file): file = File(file) add_file_to_emitter(stub_env, 'PROGEMITTER', file) + add_file_to_emitter(stub_uenv, 'PROGEMITTER', file) add_file_to_emitter(util_env, 'PROGEMITTER', file) add_file_to_emitter(plugin_env, 'SHLIBEMITTER', file) + add_file_to_emitter(plugin_uenv, 'SHLIBEMITTER', file) # # MSVC 6 SP6 doesn't like direct shifting of 64-bit integers. @@ -240,6 +246,7 @@ else: if not conf.TryLink(int64test, '.c'): stub_env.Append(CPPDEFINES = ['_NSIS_NO_INT64_SHR']) + stub_uenv.Append(CPPDEFINES = ['_NSIS_NO_INT64_SHR']) conf.Finish() @@ -256,4 +263,4 @@ conf.Finish() ### return -Return('stub_env makensis_env plugin_env util_env cp_util_env test_env') +Return('stub_env makensis_env plugin_env util_env cp_util_env test_env stub_uenv plugin_uenv') diff --git a/SConstruct b/SConstruct index 98034a1b..5e50d9db 100644 --- a/SConstruct +++ b/SConstruct @@ -420,8 +420,10 @@ plugin_env = envs[2] util_env = envs[3] cp_util_env = envs[4] test_env = envs[5] +stub_uenv = envs[6] +plugin_uenv = envs[7] -Export('stub_env makensis_env plugin_env util_env cp_util_env test_env') +Export('stub_env makensis_env plugin_env plugin_uenv util_env cp_util_env test_env') ###################################################################### ####### Distribution ### @@ -484,12 +486,17 @@ defenv.DistributeConf('nsisconf.nsh') ####### Stubs ### ###################################################################### -def BuildStub(compression, solid): - env = stub_env.Clone() +def BuildStub(compression, solid, unicode): suffix = '' if solid: suffix = '_solid' + if unicode: + suffix += 'W' + env = stub_uenv.Clone() + else: + env = stub_env.Clone() + build_dir = '$BUILD_PREFIX/stub_%s%s' % (compression, suffix) @@ -507,8 +514,12 @@ for stub in stubs: if stub in defenv['SKIPSTUBS']: continue - BuildStub(stub, False) - BuildStub(stub, True) + if defenv['UNICODE']: + BuildStub(stub, False, True) + BuildStub(stub, True, True) + + BuildStub(stub, False, False) + BuildStub(stub, True, False) defenv.DistributeStubs('Source/exehead/uninst.ico',names='uninst') @@ -569,11 +580,25 @@ def DistributeExtras(env, target, examples, docs): ####### Plug-ins ### ###################################################################### -def BuildPlugin(target, source, libs, examples = None, docs = None, +def BuildPluginBoth(target, source, libs, examples = None, docs = None, entry = 'DllMain', res = None, resources = None, defines = None, flags = None, nodeflib = True, cppused = False): - env = plugin_env.Clone() + # this function should build the ANSI & Unicode variant of the DLL, but I can't get it to work... help!!! [Wizou] + if defenv['UNICODE']: + #VariantDir('$BUILD_PREFIX/' + plugin + 'W', path) + BuildPlugin(target, source, libs, examples, docs, entry, res, resources, defines, flags, nodeflib, cppused, True) + else: + BuildPlugin(target, source, libs, examples, docs, entry, res, resources, defines, flags, nodeflib, cppused, False) + +def BuildPlugin(target, source, libs, examples = None, docs = None, + entry = 'DllMain', res = None, resources = None, + defines = None, flags = None, nodeflib = True, + cppused = False, unicode = False): + if unicode: + env = plugin_uenv.Clone() + else: + env = plugin_env.Clone() if cppused and env['CPP_REQUIRES_STDLIB']: nodeflib = False @@ -595,7 +620,10 @@ def BuildPlugin(target, source, libs, examples = None, docs = None, plugin = i break env.DistributePlugin(plugin) + if unicode: + env.DistributePlugin(plugin, str(plugin)[:-4]+"W.dll") # tweak to generate both plugin.dll & pluginW.dll (until we get to build really both variant) + #if not unicode: # distribute extras only for ANSI builds DistributeExtras(env, target, examples, docs) for plugin in plugin_libs + plugins: @@ -604,8 +632,10 @@ for plugin in plugin_libs + plugins: path = 'Contrib/' + plugin build_dir = '$BUILD_PREFIX/' + plugin - exports = {'BuildPlugin' : BuildPlugin, 'env' : plugin_env.Clone()} - + if defenv['UNICODE']: + exports = {'BuildPlugin' : BuildPluginBoth, 'env' : plugin_uenv.Clone()} + else: + exports = {'BuildPlugin' : BuildPlugin, 'env' : plugin_env.Clone()} defenv.SConscript(dirs = path, build_dir = build_dir, duplicate = False, exports = exports) ###################################################################### diff --git a/Source/DialogTemplate.cpp b/Source/DialogTemplate.cpp index 2bbe0a3f..1ec4bf7e 100644 --- a/Source/DialogTemplate.cpp +++ b/Source/DialogTemplate.cpp @@ -91,8 +91,9 @@ void ReadVarLenArr(LPBYTE &seeker, WCHAR* &readInto, unsigned int uCodePage) { // Construction/Destruction ////////////////////////////////////////////////////////////////////// -CDialogTemplate::CDialogTemplate(BYTE* pbData, unsigned int uCodePage) { +CDialogTemplate::CDialogTemplate(BYTE* pbData, bool build_unicode, unsigned int uCodePage) { m_uCodePage = uCodePage; + m_build_unicode = build_unicode; m_dwHelpId = 0; m_szClass = 0; @@ -598,7 +599,7 @@ BYTE* CDialogTemplate::Save(DWORD& dwSize) { // Write class variant length array WCHAR *szClass = m_vItems[i]->szClass; #ifdef _UNICODE - if (!IS_INTRESOURCE(szClass) && !_wcsicmp(szClass, L"RichEdit20A")) + if (!IS_INTRESOURCE(szClass) && m_build_unicode && !_wcsicmp(szClass, L"RichEdit20A")) szClass = L"RichEdit20W"; // transmute ANSI RichEdit control into Unicode RichEdit #endif WriteStringOrId(szClass); diff --git a/Source/DialogTemplate.h b/Source/DialogTemplate.h index 00c072ea..c2ccb584 100644 --- a/Source/DialogTemplate.h +++ b/Source/DialogTemplate.h @@ -107,7 +107,7 @@ typedef struct { class CDialogTemplate { public: - CDialogTemplate(BYTE* pbData, unsigned int uCodePage=CP_ACP); + CDialogTemplate(BYTE* pbData, bool build_unicode, unsigned int uCodePage=CP_ACP); virtual ~CDialogTemplate(); short GetWidth(); @@ -136,6 +136,7 @@ public: private: bool m_bExtended; + bool m_build_unicode; DWORD m_dwHelpId; // Extended only diff --git a/Source/Plugins.cpp b/Source/Plugins.cpp index 3596367c..167c9345 100644 --- a/Source/Plugins.cpp +++ b/Source/Plugins.cpp @@ -47,7 +47,7 @@ void Plugins::FindCommands(const tstring &path, bool displayInfo) for (dir_reader::iterator files_itr = dr->files().begin(); files_itr != dr->files().end(); - files_itr++) + files_itr++) // note: files are listed alphabetically, so plugin.dll will be listed before pluginW.dll { if (!dir_reader::matches(*files_itr, _T("*.dll"))) continue; @@ -109,7 +109,10 @@ void Plugins::GetExports(const tstring &pathToDll, bool displayInfo) return; } - const tstring dllName = remove_file_extension(get_file_name(pathToDll)); + tstring dllName = remove_file_extension(get_file_name(pathToDll)); +#ifdef _UNICODE + bool unicodeDll = dllName[dllName.size()-1] == 'W'; +#endif FIX_ENDIAN_INT16_INPLACE(NTHeaders->FileHeader.Characteristics); if (NTHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) @@ -143,6 +146,12 @@ void Plugins::GetExports(const tstring &pathToDll, bool displayInfo) const tstring lcsig = lowercase(signature); m_command_to_path[lcsig] = pathToDll; m_command_lowercase_to_command[lcsig] = signature; +#ifdef _UNICODE + const tstring lcsigA = lowercase(dllName.substr(0,dllName.size()-1) + _T("::") + tstring(CtoTString(name))); + if (unicodeDll && m_command_to_path.find(lcsigA) != m_command_to_path.end()) + m_unicode_variant[lcsigA] = signature; + else +#endif if (displayInfo) _ftprintf(g_output, _T(" - %s\n"), signature.c_str()); } @@ -180,6 +189,10 @@ tstring Plugins::NormalizedCommand(const tstring& command) const { return get_value(m_command_lowercase_to_command, lowercase(command)); } +tstring Plugins::UseUnicodeVariant(const tstring& command) const { + return get_value(m_unicode_variant, lowercase(command), command); +} + int Plugins::GetPluginHandle(bool uninst, const tstring& command) const { if (uninst) { return get_value(m_command_to_uninstall_data_handle, command, -1); diff --git a/Source/Plugins.h b/Source/Plugins.h index ba623e1d..44092c05 100644 --- a/Source/Plugins.h +++ b/Source/Plugins.h @@ -30,6 +30,7 @@ class Plugins tstring NormalizedCommand(const tstring& command) const; int GetPluginHandle(bool uninst, const tstring& command) const; tstring GetPluginPath(const tstring& command) const; + tstring UseUnicodeVariant(const tstring& lcsig) const; void SetDllDataHandle(bool uninst, const tstring& command, int dataHandle); private: // methods @@ -38,6 +39,7 @@ class Plugins private: // data members std::map m_command_lowercase_to_command; std::map m_command_to_path; + std::map m_unicode_variant; std::map m_command_to_data_handle; std::map m_command_to_uninstall_data_handle; }; diff --git a/Source/build.cpp b/Source/build.cpp index 6f85d7fc..9602e734 100644 --- a/Source/build.cpp +++ b/Source/build.cpp @@ -116,12 +116,8 @@ CEXEBuild::CEXEBuild() : definedlist.add(_T("NSIS_VERSION"), NSIS_VERSION); -#ifdef _UNICODE - definedlist.add(_T("NSIS_UNICODE")); - definedlist.add(_T("NSIS_CHAR_SIZE"), _T("2")); -#else - definedlist.add(_T("NSIS_CHAR_SIZE"), _T("1")); -#endif + build_unicode=false; + definedlist.add(_T("NSIS_CHAR_SIZE"), _T("1")); // this can change after a UnicodeInstaller instruction is found // automatically generated header file containing all defines #include @@ -230,8 +226,8 @@ CEXEBuild::CEXEBuild() : uninstaller_writes_used=0; - build_strlist.add(_T(""),0); - ubuild_strlist.add(_T(""),0); + build_strlist.add(_T(""), CP_ACP, false, build_unicode); + ubuild_strlist.add(_T(""), CP_ACP, false, build_unicode); build_langstring_num=0; ubuild_langstring_num=0; @@ -469,11 +465,11 @@ int CEXEBuild::add_string(const TCHAR *string, int process/*=1*/, WORD codepage/ if (idx < 0) return idx; } - if (!process) return cur_strlist->add(string,2); + if (!process) return cur_strlist->add(string, codepage, false, build_unicode); TCHAR buf[NSIS_MAX_STRLEN*4]; preprocess_string(buf,string,codepage); - return cur_strlist->add(buf,2); + return cur_strlist->add(buf,codepage, true, build_unicode); } int CEXEBuild::add_intstring(const int i) // returns offset in stringblock @@ -483,6 +479,35 @@ int CEXEBuild::add_intstring(const int i) // returns offset in stringblock return add_string(i_str); } +#ifdef _UNICODE +char* convert_processed_string_to_ansi(char *out, const TCHAR *in, WORD codepage) +{ + const TCHAR *p=in; + for (;;) + { + _TUCHAR i = (_TUCHAR)*p++; + if (NS_IS_CODE(i)) // Note: this include '\0' + { + // convert all character up to, and including this code + int cb = WideCharToMultiByte(codepage, 0, in, p-in, out, (p-in)*2, NULL, NULL); + out += cb; + if (i == _T('\0')) + break; + else if (i == NS_SKIP_CODE) + *out++ = (char) *in++; // simply copy escaped code (01..04) + else + { + WORD w = *p++; // special NSIS code is following by a WORD we need to output unchanged + *out++ = LOBYTE(w); + *out++ = HIBYTE(w); + } + in = p; + } + } + return out; +} +#endif + // based on Dave Laundon's code int CEXEBuild::preprocess_string(TCHAR *out, const TCHAR *in, WORD codepage/*=CP_ACP*/) { @@ -1498,7 +1523,7 @@ int CEXEBuild::resolve_coderefs(const TCHAR *str) else { // normal string - section_name = cur_strlist->get() + x; + section_name = cur_strlist->getTchar() + x; } if (x) wsprintf(fname,_T("%s section \"%s\" (%d)"),str,section_name,cnt); else wsprintf(fname,_T("unnamed %s section (%d)"),str,cnt); @@ -2080,7 +2105,7 @@ again: #define REMOVE_ICON(id) if (disable_window_icon) { \ BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \ if (dlg) { \ - CDialogTemplate dt(dlg,uDefCodePage); \ + CDialogTemplate dt(dlg,build_unicode,uDefCodePage); \ res_editor->FreeResource(dlg); \ if (dt.RemoveItem(IDC_ULICON)) { \ DialogItemTemplate* text = dt.GetItem(IDC_INTROTEXT); \ @@ -2210,7 +2235,7 @@ void CEXEBuild::AddStandardStrings() void CEXEBuild::PrepareHeaders(IGrowBuf *hdrbuf) { GrowBuf blocks_buf; - growbuf_writer_sink sink(&blocks_buf); + growbuf_writer_sink sink(&blocks_buf, build_unicode); #ifdef NSIS_CONFIG_VISIBLE_SUPPORT cur_header->blocks[NB_PAGES].offset = sizeof(header) + blocks_buf.getlen(); @@ -2224,7 +2249,12 @@ void CEXEBuild::PrepareHeaders(IGrowBuf *hdrbuf) entry_writer::write_block(cur_entries, &sink); cur_header->blocks[NB_STRINGS].offset = sizeof(header) + blocks_buf.getlen(); - blocks_buf.add(cur_strlist->get(), cur_strlist->getcount()*sizeof(TCHAR)); +#ifdef _UNICODE + if (!build_unicode) + blocks_buf.add(cur_strlist->getAnsi(), cur_strlist->getcount()); + else +#endif + blocks_buf.add(cur_strlist->getTchar(), cur_strlist->getcount()*sizeof(TCHAR)); cur_header->blocks[NB_LANGTABLES].offset = sizeof(header) + blocks_buf.getlen(); lang_table_writer::write_block(cur_langtables, &sink, cur_header->langtable_size); @@ -2244,7 +2274,7 @@ void CEXEBuild::PrepareHeaders(IGrowBuf *hdrbuf) } #endif - growbuf_writer_sink sink2(hdrbuf); + growbuf_writer_sink sink2(hdrbuf, build_unicode); header_writer header(&sink2); header.write(cur_header); @@ -2259,7 +2289,8 @@ int CEXEBuild::SetVarsSection() VerifyDeclaredUserVarRefs(&m_UserVarNames); int MaxUserVars = m_UserVarNames.getnum(); // -1 because the default size is 1 - if (!res_editor->AddExtraVirtualSize2PESection(NSIS_VARS_SECTION, (MaxUserVars - 1) * sizeof(NSIS_STRING))) + int stringSize = NSIS_MAX_STRLEN*(build_unicode?sizeof(TCHAR):sizeof(char)); + if (!res_editor->AddExtraVirtualSize2PESection(NSIS_VARS_SECTION, (MaxUserVars - 1) * stringSize)) { ERROR_MSG(_T("Internal compiler error #12346: invalid exehead cannot find section \"%s\"!\n"), _T(NSIS_VARS_SECTION)); return PS_ERROR; @@ -2658,7 +2689,7 @@ int CEXEBuild::write_output(void) int ne=build_header.blocks[NB_ENTRIES].num; INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ne*sizeof(entry)); int ns=build_strlist.getnum(); - INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),build_strlist.getcount()*sizeof(TCHAR)); + INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),build_strlist.getcount()*(build_unicode ? sizeof(CHAR) : sizeof(TCHAR))); int nlt=build_header.blocks[NB_LANGTABLES].num; INFO_MSG(_T("%d language table%s (%d bytes).\n"),nlt,nlt==1?_T(""):_T("s"),build_langtables.getlen()); if (ubuild_entries.getlen()) @@ -2687,7 +2718,7 @@ int CEXEBuild::write_output(void) ne=build_uninst.blocks[NB_ENTRIES].num; INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ubuild_entries.getlen()); ns=ubuild_strlist.getnum(); - INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),ubuild_strlist.getcount()*sizeof(TCHAR)); + INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),ubuild_strlist.getcount()*(build_unicode ? sizeof(CHAR) : sizeof(TCHAR))); nlt=build_uninst.blocks[NB_LANGTABLES].num; INFO_MSG(_T("%d language table%s (%d bytes).\n"),nlt,nlt==1?_T(""):_T("s"),ubuild_langtables.getlen()); } @@ -3014,7 +3045,7 @@ int CEXEBuild::uninstall_generate() MMapBuf udata; { - growbuf_writer_sink sink(&udata); + growbuf_writer_sink sink(&udata, build_unicode); firstheader_writer w(&sink); w.write(&fh); } @@ -3532,12 +3563,40 @@ void CEXEBuild::VerifyDeclaredUserVarRefs(UserVarsStringList *pVarsStringList) } } -int CEXEBuild::set_compressor(const tstring& compressor, const bool solid) { - tstring stub = stubs_dir + PLATFORM_PATH_SEPARATOR_STR + compressor; - if (solid) - stub += _T("_solid"); +#ifdef _UNICODE +int CEXEBuild::set_build_unicode(bool unicode_installer) +{ + build_unicode = unicode_installer; + definedlist.del(_T("NSIS_UNICODE")); + definedlist.del(_T("NSIS_CHAR_SIZE")); + if (unicode_installer) // update defines depending on target installer type + { + definedlist.add(_T("NSIS_UNICODE")); + definedlist.add(_T("NSIS_CHAR_SIZE"), _T("2")); + } + else + { + definedlist.add(_T("NSIS_CHAR_SIZE"), _T("1")); + } + return load_stub(); +} +#endif - return update_exehead(stub, &m_exehead_original_size); +int CEXEBuild::set_compressor(const tstring& compressor, const bool solid) { + stub_filename = stubs_dir + PLATFORM_PATH_SEPARATOR_STR + compressor; + if (solid) + stub_filename += _T("_solid"); + return load_stub(); +} + +int CEXEBuild::load_stub() +{ +#ifdef _UNICODE + if (build_unicode) + return update_exehead(stub_filename+_T('W'), &m_exehead_original_size); + else +#endif + return update_exehead(stub_filename, &m_exehead_original_size); } int CEXEBuild::update_exehead(const tstring& file, size_t *size/*=NULL*/) { diff --git a/Source/build.h b/Source/build.h index c3282792..528a27c7 100644 --- a/Source/build.h +++ b/Source/build.h @@ -136,7 +136,9 @@ class CEXEBuild { int prepare_uninstaller(); int pack_exe_header(); + int set_build_unicode(bool unicode_installer); int set_compressor(const tstring& compressor, const bool solid); + int load_stub(); int update_exehead(const tstring& file, size_t *size=NULL); void update_exehead(const unsigned char *new_exehead, size_t new_size); @@ -377,6 +379,7 @@ class CEXEBuild { // a whole bunch O data. tstring stubs_dir; + tstring stub_filename; #ifdef NSIS_CONFIG_COMPRESSION_SUPPORT ICompressor *compressor; @@ -392,6 +395,7 @@ class CEXEBuild { int build_compress_dict_size; bool no_space_texts; + bool build_unicode; bool has_called_write_output; @@ -442,7 +446,7 @@ class CEXEBuild { GrowBuf build_instruction_entry_map,ubuild_instruction_entry_map, *cur_instruction_entry_map; TinyGrowBuf build_functions, ubuild_functions, *cur_functions; TinyGrowBuf build_labels, ubuild_labels, *cur_labels; - StringList build_strlist, ubuild_strlist, *cur_strlist; + MLStringList build_strlist, ubuild_strlist, *cur_strlist; GrowBuf build_langtables, ubuild_langtables, *cur_langtables; TinyGrowBuf build_pages, ubuild_pages, *cur_pages; TinyGrowBuf build_ctlcolors, ubuild_ctlcolors, *cur_ctlcolors; diff --git a/Source/exehead/Ui.c b/Source/exehead/Ui.c index 763e136c..e5ee77e5 100644 --- a/Source/exehead/Ui.c +++ b/Source/exehead/Ui.c @@ -722,7 +722,7 @@ DWORD CALLBACK StreamLicense(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) return 0; } #ifdef _UNICODE -// on-the-fly conversion of Unicode to ANSI (because Windows don't recognize Unicode RTF data) +// on-the-fly conversion of Unicode to ANSI (because Windows doesn't recognize Unicode RTF data) DWORD CALLBACK StreamLicenseRTF(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { size_t len = lstrlen(((LPWSTR) dwCookie)+dwRead); diff --git a/Source/exehead/fileform.h b/Source/exehead/fileform.h index ff43b772..5b7f1d80 100644 --- a/Source/exehead/fileform.h +++ b/Source/exehead/fileform.h @@ -489,17 +489,10 @@ typedef struct { // We are doing this to store an integer value into a char string and we // don't want false end of string values -#if _UNICODE -#define CODE_SHORT(x) ((WORD)x + 1) -#define MAX_CODED 0xFFFE -// This macro takes a pointer to WCHAR -#define DECODE_SHORT(c) (c[0]-1) -#else #define CODE_SHORT(x) (WORD)((((WORD)(x) & 0x7F) | (((WORD)(x) & 0x3F80) << 1) | 0x8080)) #define MAX_CODED 0x3FFF // This macro takes a pointer to CHAR -#define DECODE_SHORT(c) (((c[1] & 0x7F) << 7) | (c[0] & 0x7F)) -#endif +#define DECODE_SHORT(c) (((((char*)c)[1] & 0x7F) << 7) | (((char*)c)[0] & 0x7F)) #define NSIS_INSTDIR_INVALID 1 #define NSIS_INSTDIR_NOT_ENOUGH_SPACE 2 diff --git a/Source/lang.cpp b/Source/lang.cpp index a3cf4e97..09f513c7 100644 --- a/Source/lang.cpp +++ b/Source/lang.cpp @@ -381,15 +381,10 @@ const TCHAR *CEXEBuild::GetLangNameAndCP(LANGID lang, unsigned int *codepage/*=N return table->nlf.m_szName; } else { - // If the language table does not exist, then we default to Unicode in the - // Unicode version and English in the ANSI version. -#ifdef _UNICODE + // If the language table does not exist, then we default to Unicode or ANSI + // depending on the target installer type if (codepage) - *codepage = 1200; // Unicode -#else - if (codepage) - *codepage = 1252; // ANSI CP1252 -#endif + *codepage = build_unicode ? 1200 : 1252; // Unicode or CP1252 if (lang == 1033) return _T("English"); @@ -703,7 +698,7 @@ int CEXEBuild::GenerateLangTables() { #define ADD_FONT(id) { \ BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \ if (dlg) { \ - CDialogTemplate td(dlg); \ + CDialogTemplate td(dlg,build_unicode); \ res_editor->FreeResource(dlg); \ td.SetFont(build_font, (WORD) build_font_size); \ DWORD dwSize; \ @@ -758,7 +753,7 @@ int CEXEBuild::GenerateLangTables() { #define ADD_FONT(id) { \ BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \ if (dlg) { \ - CDialogTemplate td(dlg,lt[i].nlf.m_uCodePage); \ + CDialogTemplate td(dlg,build_unicode,lt[i].nlf.m_uCodePage); \ res_editor->FreeResource(dlg); \ if (font) td.SetFont(font, (WORD) lt[i].nlf.m_iFontSize); \ if (lt[i].nlf.m_bRTL) { \ diff --git a/Source/script.cpp b/Source/script.cpp index 5cd0268a..e68130ad 100644 --- a/Source/script.cpp +++ b/Source/script.cpp @@ -867,12 +867,10 @@ int CEXEBuild::LoadLicenseFile(TCHAR *file, TCHAR** pdata, LineParser &line, BOO } if (!memcmp(data+1,_T("{\\rtf"),5*sizeof(TCHAR))) *data = SF_RTF; - else -#ifdef _UNICODE + else if (unicode) *data = SF_TEXT|SF_UNICODE; -#else + else *data = SF_TEXT; -#endif return PS_OK; } @@ -2155,7 +2153,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) BYTE* dlg = res_editor->GetResource(RT_DIALOG, IDD_INSTFILES, NSIS_DEFAULT_LANG); if (!dlg) throw runtime_error("IDD_INSTFILES doesn't exist!"); - CDialogTemplate dt(dlg,uDefCodePage); + CDialogTemplate dt(dlg,build_unicode,uDefCodePage); free(dlg); DialogItemTemplate* progress = dt.GetItem(IDC_PROGRESS); if (!progress) { @@ -2448,7 +2446,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) init_res_editor(); // Search for required items - #define GET(x) dlg = uire->GetResource(RT_DIALOG, x, 0); if (!dlg) return PS_ERROR; CDialogTemplate UIDlg(dlg, uDefCodePage); + #define GET(x) dlg = uire->GetResource(RT_DIALOG, x, 0); if (!dlg) return PS_ERROR; CDialogTemplate UIDlg(dlg, build_unicode, uDefCodePage); #define SEARCH(x) if (!UIDlg.GetItem(x)) {ERROR_MSG(_T("Error: Can't find %s (%u) in the custom UI!\n"), _T(#x), x);delete [] dlg;delete uire;return PS_ERROR;} #define SAVE(x) dwSize = UIDlg.GetSize(); res_editor->UpdateResource(RT_DIALOG, x, NSIS_DEFAULT_LANG, dlg, dwSize); delete [] dlg; @@ -2568,7 +2566,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) init_res_editor(); BYTE* dlg = res_editor->GetResource(RT_DIALOG, IDD_INST, NSIS_DEFAULT_LANG); - CDialogTemplate dt(dlg, uDefCodePage); + CDialogTemplate dt(dlg, build_unicode, uDefCodePage); res_editor->FreeResource(dlg); @@ -2687,6 +2685,25 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) } return PS_OK; +#ifdef _UNICODE + case TOK_UNICODEINSTALLER: + { + if (build_compressor_set) { + ERROR_MSG(_T("Error: can't change type of installer after data already got compressed or header already changed!\n")); + return PS_ERROR; + } + int param=line.gettoken_enum(1,_T("off\0on\0")); + if (param==-1) PRINTHELP() + SCRIPT_MSG(_T("UnicodeInstaller: %s\n"),line.gettoken_str(1)); + if (set_build_unicode(param != 0) != PS_OK) + { + ERROR_MSG(_T("Error: error while setting type of installer! (stub not found?)\n")); + return PS_ERROR; + } + } + return PS_OK; +#endif + // Ability to change compression methods from within the script case TOK_SETCOMPRESSOR: #ifdef NSIS_CONFIG_COMPRESSION_SUPPORT @@ -3681,7 +3698,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) init_res_editor(); BYTE* dlg = res_editor->GetResource(RT_DIALOG, IDD_INST, NSIS_DEFAULT_LANG); - CDialogTemplate td(dlg,uDefCodePage); + CDialogTemplate td(dlg,build_unicode,uDefCodePage); free(dlg); if (trim) { @@ -5913,8 +5930,13 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) { int ret; - const tstring command = m_plugins.NormalizedCommand(line.gettoken_str(0)); - const tstring dllPath = m_plugins.GetPluginPath(command); + tstring command = m_plugins.NormalizedCommand(line.gettoken_str(0)); +#ifdef _UNICODE + if (build_unicode) + command = m_plugins.UseUnicodeVariant(command); +#endif + tstring dllPath = m_plugins.GetPluginPath(command); + tstring dllName = get_file_name(dllPath); int data_handle = m_plugins.GetPluginHandle(uninstall_mode?true:false, command); if (uninstall_mode) uninst_plugin_used = true; @@ -5930,7 +5952,6 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) // DLL name on the user machine TCHAR tempDLL[NSIS_MAX_STRLEN]; - tstring dllName = get_file_name(dllPath); wsprintf(tempDLL, _T("$PLUGINSDIR\\%s"), dllName.c_str()); // Add the DLL to the installer diff --git a/Source/strlist.cpp b/Source/strlist.cpp index 81484f41..48214ad5 100644 --- a/Source/strlist.cpp +++ b/Source/strlist.cpp @@ -18,6 +18,97 @@ #include "strlist.h" +MLStringList::MLStringList() +{ + m_gr.set_zeroing(1); +#ifdef _UNICODE + m_grAnsi.set_zeroing(1); +#endif +} + +#ifdef _UNICODE +char* convert_processed_string_to_ansi(char *out, const TCHAR *in, WORD codepage); // defined in build.cpp + +// use 2 for case sensitive end-of-string matches too +int MLStringList::findAnsi(const char *str, int case_sensitive) const // returns -1 if not found +{ + const char *s=(const char*) m_grAnsi.get(); + int ml=getcount(); + int offs=0; + + size_t str_slen = strlen(str); + size_t offs_slen; + + while (offs < ml) + { + // Check if the whole string matches str. + if ((case_sensitive && !strcmp(s+offs,str)) || + (!case_sensitive && !stricmp(s+offs,str))) + { + return offs; + } + + offs_slen = strlen(s+offs); + + // Check if just the end of the string matches str. + if (case_sensitive==2 && + str_slen < offs_slen && // check for end of string + !strcmp(s + offs + offs_slen - str_slen,str)) + { + return offs + offs_slen - str_slen; + } + offs += offs_slen + 1; + } + return -1; +} +#endif + +int MLStringList::add(const TCHAR *str, WORD codepage /*= CP_ACP*/, bool processed, bool build_unicode) +{ +#ifndef _UNICODE + int a=find(str,2); + if (a >= 0) + return a; + int len = _tcslen(str)+1; + return m_gr.add(str,len*sizeof(TCHAR))/sizeof(TCHAR); +#else + if (build_unicode) + { + int a=find(str,2); + if (a >= 0) + return a; + } + // convert to ANSI + int len = _tcslen(str)+1; + char* ansiBuf = new char[len*2]; + int cbMultiByte; + if (processed) + cbMultiByte = convert_processed_string_to_ansi(ansiBuf, str, codepage)-ansiBuf; + else + cbMultiByte = WideCharToMultiByte(codepage, 0, str, len, ansiBuf, len*2, NULL, NULL); + if (!build_unicode) + { + int a=findAnsi(ansiBuf,2); + if (a >= 0) + { + delete[] ansiBuf; + return a; + } + } + // string not found, add it + int a=m_gr.add(str,len*sizeof(TCHAR))/sizeof(TCHAR); + m_grAnsi.add(ansiBuf,cbMultiByte); + delete[] ansiBuf; + if (len != cbMultiByte) + { // resize buffers to align future strings on same offsets + len = a+max(len,cbMultiByte); + m_gr.resize(len*sizeof(TCHAR)); + m_grAnsi.resize(len); + } + return a; +#endif +} + int StringList::add(const TCHAR *str, int case_sensitive) { int a=find(str,case_sensitive); @@ -103,15 +194,6 @@ int StringList::getnum() const return idx; } -const TCHAR *StringList::get() const -{ - return (const TCHAR*) m_gr.get(); -} - -int StringList::getcount() const -{ - return m_gr.getlen() / sizeof(TCHAR); -} // ========== // DefineList diff --git a/Source/strlist.h b/Source/strlist.h index 8c6718b7..4a974a4e 100644 --- a/Source/strlist.h +++ b/Source/strlist.h @@ -104,18 +104,43 @@ public: * Get the buffer straight as a const TCHAR pointer. Very unwise to use. * @return m_gr.m_s cast as a TCHAR*. */ - const TCHAR *get() const; + const TCHAR *get() const { return (const TCHAR*) m_gr.get(); } /** * Get the buffer size (number of TCHARs). * @return The buffer size (number of TCHARs). */ - int getcount() const; + int getcount() const { return m_gr.getlen() / sizeof(TCHAR); } -private: +protected: GrowBuf m_gr; }; +/** + * Similar to StringList with case_sensitive=2, but stores strings as both Unicode AND ANSI (codepaged) + */ +class MLStringList : private StringList +{ +private: // don't copy instances + MLStringList(const MLStringList&); + void operator=(const MLStringList&); + +public: + MLStringList(); + ~MLStringList() {} + + int add(const TCHAR *str, WORD codepage, bool processed, bool build_unicode); + int getnum() const { return StringList::getnum(); } + int getcount() const { return StringList::getcount(); } + const TCHAR *getTchar() const { return (const TCHAR*) m_gr.get(); } +#ifdef _UNICODE + const char *getAnsi() const { return (const char*) m_grAnsi.get(); } + int findAnsi(const char *str, int case_sensitive) const; +private: + GrowBuf m_grAnsi; +#endif +}; + /** * This class maintains a list of T types in a GrowBuf sorted by T.name which * is assumed to be a string (TCHAR*). So it's really sort of a diff --git a/Source/tokens.cpp b/Source/tokens.cpp index 7b3f7b1f..e747c645 100644 --- a/Source/tokens.cpp +++ b/Source/tokens.cpp @@ -216,6 +216,9 @@ static tokenType tokenlist[TOK__LAST] = {TOK_STRCPY,_T("StrCpy"),2,2,_T("$(user_var: output) str [maxlen] [startoffset]"),TP_CODE}, {TOK_STRLEN,_T("StrLen"),2,0,_T("$(user_var: length output) str"),TP_CODE}, {TOK_SUBCAPTION,_T("SubCaption"),2,0,_T("page_number(0-4) new_subcaption"),TP_GLOBAL}, +#ifdef _UNICODE +{TOK_UNICODEINSTALLER,_T("UnicodeInstaller"),1,0,_T("(on|off)"),TP_GLOBAL}, +#endif {TOK_UNINSTALLEXENAME,_T("UninstallExeName"),0,0,_T("no longer supported, use WriteUninstaller from section."),TP_ALL}, {TOK_UNINSTCAPTION,_T("UninstallCaption"),1,0,_T("uninstaller_caption"),TP_GLOBAL}, {TOK_UNINSTICON,_T("UninstallIcon"),1,0,_T("icon_on_local_system.ico"),TP_GLOBAL}, diff --git a/Source/tokens.h b/Source/tokens.h index bd997f86..50eb486e 100644 --- a/Source/tokens.h +++ b/Source/tokens.h @@ -70,6 +70,9 @@ enum TOK_DEFVAR, TOK_VI_ADDKEY, TOK_VI_SETPRODUCTVERSION, +#ifdef _UNICODE + TOK_UNICODEINSTALLER, +#endif TOK_MISCBUTTONTEXT, TOK_DETAILSBUTTONTEXT, diff --git a/Source/writer.cpp b/Source/writer.cpp index bb4fe929..b453cadb 100644 --- a/Source/writer.cpp +++ b/Source/writer.cpp @@ -54,18 +54,29 @@ void writer_sink::write_int_array(const int i[], const size_t len) void writer_sink::write_string(const TCHAR *s, size_t size) { #ifdef _UNICODE - bool strEnd = false; - TCHAR ch; - for (; size ; size--) + if (m_build_unicode) { - if (!strEnd) + bool strEnd = false; + TCHAR ch; + for (; size ; size--) { - ch = *s++; - if (ch == _T('\0')) - strEnd = true; + if (!strEnd) + { + ch = *s++; + if (ch == _T('\0')) + strEnd = true; + } + write_short(ch); } - write_short(ch); } + else + { + char *wb = new char[size]; + memset(wb, 0, size); + WideCharToMultiByte(CP_ACP, 0, s, -1, wb, size, NULL, NULL); + write_data(wb, size); + delete [] wb; + } #else char *wb = new char[size]; memset(wb, 0, size); diff --git a/Source/writer.h b/Source/writer.h index 0f4a970e..438a67aa 100644 --- a/Source/writer.h +++ b/Source/writer.h @@ -27,7 +27,7 @@ class writer_sink { public: - writer_sink() {} + writer_sink() : m_build_unicode(false) {} virtual ~writer_sink() {} virtual void write_byte(const unsigned char b); @@ -38,7 +38,8 @@ public: virtual void write_growbuf(const IGrowBuf *b); virtual void write_data(const void *data, const size_t size) = 0; - +protected: + bool m_build_unicode; }; class writer { @@ -53,7 +54,7 @@ protected: class growbuf_writer_sink : public writer_sink { public: - growbuf_writer_sink(IGrowBuf *buf) : m_buf(buf) {} + growbuf_writer_sink(IGrowBuf *buf, bool build_unicode) : m_buf(buf) { m_build_unicode=build_unicode; } virtual void write_data(const void *data, const size_t size);