SevenZipSfx.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. 
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Reflection;
  7. using System.Text;
  8. using System.Xml;
  9. using System.Xml.Schema;
  10. namespace SevenZip
  11. {
  12. #if SFX
  13. using SfxSettings = Dictionary<string, string>;
  14. /// <summary>
  15. /// Sfx module choice enumeration
  16. /// </summary>
  17. public enum SfxModule
  18. {
  19. /// <summary>
  20. /// Default module (leave this if unsure)
  21. /// </summary>
  22. Default,
  23. /// <summary>
  24. /// The simple sfx module by Igor Pavlov with no adjustable parameters
  25. /// </summary>
  26. Simple,
  27. /// <summary>
  28. /// The installer sfx module by Igor Pavlov
  29. /// </summary>
  30. Installer,
  31. /// <summary>
  32. /// The extended installer sfx module by Oleg Scherbakov
  33. /// </summary>
  34. Extended,
  35. /// <summary>
  36. /// The custom sfx module. First you must specify the module file name.
  37. /// </summary>
  38. Custom
  39. }
  40. /// <summary>
  41. /// The class for making 7-zip based self-extracting archives.
  42. /// </summary>
  43. public class SevenZipSfx
  44. {
  45. private static readonly Dictionary<SfxModule, List<string>> SfxSupportedModuleNames =
  46. new Dictionary<SfxModule, List<string>>(3)
  47. {
  48. {SfxModule.Default, new List<string>(1) {"7zxSD_All.sfx"}},
  49. {SfxModule.Simple, new List<string>(2) {"7z.sfx", "7zCon.sfx"}},
  50. {SfxModule.Installer, new List<string>(2) {"7zS.sfx", "7zSD.sfx"}},
  51. {
  52. SfxModule.Extended,
  53. new List<string>(4) {"7zxSD_All.sfx", "7zxSD_Deflate", "7zxSD_LZMA", "7zxSD_PPMd"}
  54. }
  55. };
  56. private SfxModule _module = SfxModule.Default;
  57. private string _moduleFileName;
  58. private Dictionary<SfxModule, List<string>> _sfxCommands;
  59. /// <summary>
  60. /// Initializes a new instance of the SevenZipSfx class.
  61. /// </summary>
  62. public SevenZipSfx()
  63. {
  64. _module = SfxModule.Default;
  65. CommonInit();
  66. }
  67. /// <summary>
  68. /// Initializes a new instance of the SevenZipSfx class.
  69. /// </summary>
  70. /// <param name="module">The sfx module to use as a front-end.</param>
  71. public SevenZipSfx(SfxModule module)
  72. {
  73. if (module == SfxModule.Custom)
  74. {
  75. throw new ArgumentException("You must specify the custom module executable.", "module");
  76. }
  77. _module = module;
  78. CommonInit();
  79. }
  80. /// <summary>
  81. /// Initializes a new instance of the SevenZipSfx class.
  82. /// </summary>
  83. /// <param name="moduleFileName"></param>
  84. public SevenZipSfx(string moduleFileName)
  85. {
  86. _module = SfxModule.Custom;
  87. ModuleFileName = moduleFileName;
  88. CommonInit();
  89. }
  90. /// <summary>
  91. /// Gets the sfx module type.
  92. /// </summary>
  93. public SfxModule SfxModule
  94. {
  95. get
  96. {
  97. return _module;
  98. }
  99. }
  100. /// <summary>
  101. /// Gets or sets the custom sfx module file name
  102. /// </summary>
  103. public string ModuleFileName
  104. {
  105. get
  106. {
  107. return _moduleFileName;
  108. }
  109. set
  110. {
  111. if (!File.Exists(value))
  112. {
  113. throw new ArgumentException("The specified file does not exist.");
  114. }
  115. _moduleFileName = value;
  116. _module = SfxModule.Custom;
  117. string sfxName = Path.GetFileName(value);
  118. foreach (SfxModule mod in SfxSupportedModuleNames.Keys)
  119. {
  120. if (SfxSupportedModuleNames[mod].Contains(sfxName))
  121. {
  122. _module = mod;
  123. }
  124. }
  125. }
  126. }
  127. private void CommonInit()
  128. {
  129. LoadCommandsFromResource("Configs");
  130. }
  131. private static string GetResourceString(string str)
  132. {
  133. #if !WINCE
  134. return "SevenZip.sfx." + str;
  135. #else
  136. return "SevenZipSharpMobile.sfx." + str;
  137. #endif
  138. }
  139. /// <summary>
  140. /// Gets the sfx module enum by the list of supported modules
  141. /// </summary>
  142. /// <param name="name"></param>
  143. /// <returns></returns>
  144. private static SfxModule GetModuleByName(string name)
  145. {
  146. if (name.IndexOf("7z.sfx", StringComparison.Ordinal) > -1)
  147. {
  148. return SfxModule.Simple;
  149. }
  150. if (name.IndexOf("7zS.sfx", StringComparison.Ordinal) > -1)
  151. {
  152. return SfxModule.Installer;
  153. }
  154. if (name.IndexOf("7zxSD_All.sfx", StringComparison.Ordinal) > -1)
  155. {
  156. return SfxModule.Extended;
  157. }
  158. throw new SevenZipSfxValidationException("The specified configuration is unsupported.");
  159. }
  160. /// <summary>
  161. /// Loads the commands for each supported sfx module configuration
  162. /// </summary>
  163. /// <param name="xmlDefinitions">The resource name for xml definitions</param>
  164. private void LoadCommandsFromResource(string xmlDefinitions)
  165. {
  166. using (Stream cfg = Assembly.GetExecutingAssembly().GetManifestResourceStream(
  167. GetResourceString(xmlDefinitions + ".xml")))
  168. {
  169. if (cfg == null)
  170. {
  171. throw new SevenZipSfxValidationException("The configuration \"" + xmlDefinitions +
  172. "\" does not exist.");
  173. }
  174. using (Stream schm = Assembly.GetExecutingAssembly().GetManifestResourceStream(
  175. GetResourceString(xmlDefinitions + ".xsd")))
  176. {
  177. if (schm == null)
  178. {
  179. throw new SevenZipSfxValidationException("The configuration schema \"" + xmlDefinitions +
  180. "\" does not exist.");
  181. }
  182. var sc = new XmlSchemaSet();
  183. using (XmlReader scr = XmlReader.Create(schm))
  184. {
  185. sc.Add(null, scr);
  186. var settings = new XmlReaderSettings {ValidationType = ValidationType.Schema, Schemas = sc};
  187. string validationErrors = "";
  188. settings.ValidationEventHandler +=
  189. ((s, t) =>
  190. {
  191. validationErrors += String.Format(CultureInfo.InvariantCulture, "[{0}]: {1}\n",
  192. t.Severity.ToString(), t.Message);
  193. });
  194. using (XmlReader rdr = XmlReader.Create(cfg, settings))
  195. {
  196. _sfxCommands = new Dictionary<SfxModule, List<string>>();
  197. rdr.Read();
  198. rdr.Read();
  199. rdr.Read();
  200. rdr.Read();
  201. rdr.Read();
  202. rdr.ReadStartElement("sfxConfigs");
  203. rdr.Read();
  204. do
  205. {
  206. SfxModule mod = GetModuleByName(rdr["modules"]);
  207. rdr.ReadStartElement("config");
  208. rdr.Read();
  209. if (rdr.Name == "id")
  210. {
  211. var cmds = new List<string>();
  212. _sfxCommands.Add(mod, cmds);
  213. do
  214. {
  215. cmds.Add(rdr["command"]);
  216. rdr.Read();
  217. rdr.Read();
  218. } while (rdr.Name == "id");
  219. rdr.ReadEndElement();
  220. rdr.Read();
  221. }
  222. else
  223. {
  224. _sfxCommands.Add(mod, null);
  225. }
  226. } while (rdr.Name == "config");
  227. }
  228. if (!String.IsNullOrEmpty(validationErrors))
  229. {
  230. throw new SevenZipSfxValidationException(
  231. "\n" + validationErrors.Substring(0, validationErrors.Length - 1));
  232. }
  233. _sfxCommands.Add(SfxModule.Default, _sfxCommands[SfxModule.Extended]);
  234. }
  235. }
  236. }
  237. }
  238. /// <summary>
  239. /// Validates the sfx scenario commands.
  240. /// </summary>
  241. /// <param name="settings">The sfx settings dictionary to validate.</param>
  242. private void ValidateSettings(SfxSettings settings)
  243. {
  244. if (_module == SfxModule.Custom)
  245. {
  246. return;
  247. }
  248. List<string> commands = _sfxCommands[_module];
  249. if (commands == null)
  250. {
  251. return;
  252. }
  253. var invalidCommands = new List<string>();
  254. foreach (string command in settings.Keys)
  255. {
  256. if (!commands.Contains(command))
  257. {
  258. invalidCommands.Add(command);
  259. }
  260. }
  261. if (invalidCommands.Count > 0)
  262. {
  263. var invalidText = new StringBuilder("\nInvalid commands:\n");
  264. foreach (string str in invalidCommands)
  265. {
  266. invalidText.Append(str);
  267. }
  268. throw new SevenZipSfxValidationException(invalidText.ToString());
  269. }
  270. }
  271. /// <summary>
  272. /// Gets the stream containing the sfx settings.
  273. /// </summary>
  274. /// <param name="settings">The sfx settings dictionary.</param>
  275. /// <returns></returns>
  276. private static Stream GetSettingsStream(SfxSettings settings)
  277. {
  278. var ms = new MemoryStream();
  279. byte[] buf = Encoding.UTF8.GetBytes(@";!@Install@!UTF-8!" + '\n');
  280. ms.Write(buf, 0, buf.Length);
  281. foreach (string command in settings.Keys)
  282. {
  283. buf =
  284. Encoding.UTF8.GetBytes(String.Format(CultureInfo.InvariantCulture, "{0}=\"{1}\"\n", command,
  285. settings[command]));
  286. ms.Write(buf, 0, buf.Length);
  287. }
  288. buf = Encoding.UTF8.GetBytes(@";!@InstallEnd@!");
  289. ms.Write(buf, 0, buf.Length);
  290. return ms;
  291. }
  292. private SfxSettings GetDefaultSettings()
  293. {
  294. switch (_module)
  295. {
  296. default:
  297. return null;
  298. case SfxModule.Installer:
  299. var settings = new Dictionary<string, string> {{"Title", "7-Zip self-extracting archive"}};
  300. return settings;
  301. case SfxModule.Default:
  302. case SfxModule.Extended:
  303. settings = new Dictionary<string, string>
  304. {
  305. {"GUIMode", "0"},
  306. {"InstallPath", "."},
  307. {"GUIFlags", "128+8"},
  308. {"ExtractPathTitle", "7-Zip self-extracting archive"},
  309. {"ExtractPathText", "Specify the path where to extract the files:"}
  310. };
  311. return settings;
  312. }
  313. }
  314. /// <summary>
  315. /// Writes the whole to the other one.
  316. /// </summary>
  317. /// <param name="src">The source stream to read from.</param>
  318. /// <param name="dest">The destination stream to wrie to.</param>
  319. private static void WriteStream(Stream src, Stream dest)
  320. {
  321. src.Seek(0, SeekOrigin.Begin);
  322. var buf = new byte[32768];
  323. int bytesRead;
  324. while ((bytesRead = src.Read(buf, 0, buf.Length)) > 0)
  325. {
  326. dest.Write(buf, 0, bytesRead);
  327. }
  328. }
  329. /// <summary>
  330. /// Makes the self-extracting archive.
  331. /// </summary>
  332. /// <param name="archive">The archive stream.</param>
  333. /// <param name="sfxFileName">The name of the self-extracting executable.</param>
  334. public void MakeSfx(Stream archive, string sfxFileName)
  335. {
  336. using (Stream sfxStream = File.Create(sfxFileName))
  337. {
  338. MakeSfx(archive, GetDefaultSettings(), sfxStream);
  339. }
  340. }
  341. /// <summary>
  342. /// Makes the self-extracting archive.
  343. /// </summary>
  344. /// <param name="archive">The archive stream.</param>
  345. /// <param name="sfxStream">The stream to write the self-extracting executable to.</param>
  346. public void MakeSfx(Stream archive, Stream sfxStream)
  347. {
  348. MakeSfx(archive, GetDefaultSettings(), sfxStream);
  349. }
  350. /// <summary>
  351. /// Makes the self-extracting archive.
  352. /// </summary>
  353. /// <param name="archive">The archive stream.</param>
  354. /// <param name="settings">The sfx settings.</param>
  355. /// <param name="sfxFileName">The name of the self-extracting executable.</param>
  356. public void MakeSfx(Stream archive, SfxSettings settings, string sfxFileName)
  357. {
  358. using (Stream sfxStream = File.Create(sfxFileName))
  359. {
  360. MakeSfx(archive, settings, sfxStream);
  361. }
  362. }
  363. /// <summary>
  364. /// Makes the self-extracting archive.
  365. /// </summary>
  366. /// <param name="archive">The archive stream.</param>
  367. /// <param name="settings">The sfx settings.</param>
  368. /// <param name="sfxStream">The stream to write the self-extracting executable to.</param>
  369. public void MakeSfx(Stream archive, SfxSettings settings, Stream sfxStream)
  370. {
  371. if (!sfxStream.CanWrite)
  372. {
  373. throw new ArgumentException("The specified output stream can not write.", "sfxStream");
  374. }
  375. ValidateSettings(settings);
  376. using (Stream sfx = _module == SfxModule.Default
  377. ? Assembly.GetExecutingAssembly().GetManifestResourceStream(
  378. GetResourceString(SfxSupportedModuleNames[_module][0]))
  379. : new FileStream(_moduleFileName, FileMode.Open, FileAccess.Read,
  380. FileShare.ReadWrite))
  381. {
  382. WriteStream(sfx, sfxStream);
  383. }
  384. if (_module == SfxModule.Custom || _sfxCommands[_module] != null)
  385. {
  386. using (Stream set = GetSettingsStream(settings))
  387. {
  388. WriteStream(set, sfxStream);
  389. }
  390. }
  391. WriteStream(archive, sfxStream);
  392. }
  393. /// <summary>
  394. /// Makes the self-extracting archive.
  395. /// </summary>
  396. /// <param name="archiveFileName">The archive file name.</param>
  397. /// <param name="sfxFileName">The name of the self-extracting executable.</param>
  398. public void MakeSfx(string archiveFileName, string sfxFileName)
  399. {
  400. using (Stream sfxStream = File.Create(sfxFileName))
  401. {
  402. using (
  403. Stream archive = new FileStream(archiveFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
  404. )
  405. {
  406. MakeSfx(archive, GetDefaultSettings(), sfxStream);
  407. }
  408. }
  409. }
  410. /// <summary>
  411. /// Makes the self-extracting archive.
  412. /// </summary>
  413. /// <param name="archiveFileName">The archive file name.</param>
  414. /// <param name="sfxStream">The stream to write the self-extracting executable to.</param>
  415. public void MakeSfx(string archiveFileName, Stream sfxStream)
  416. {
  417. using (Stream archive = new FileStream(archiveFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
  418. )
  419. {
  420. MakeSfx(archive, GetDefaultSettings(), sfxStream);
  421. }
  422. }
  423. /// <summary>
  424. /// Makes the self-extracting archive.
  425. /// </summary>
  426. /// <param name="archiveFileName">The archive file name.</param>
  427. /// <param name="settings">The sfx settings.</param>
  428. /// <param name="sfxFileName">The name of the self-extracting executable.</param>
  429. public void MakeSfx(string archiveFileName, SfxSettings settings, string sfxFileName)
  430. {
  431. using (Stream sfxStream = File.Create(sfxFileName))
  432. {
  433. using (
  434. Stream archive = new FileStream(archiveFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
  435. )
  436. {
  437. MakeSfx(archive, settings, sfxStream);
  438. }
  439. }
  440. }
  441. /// <summary>
  442. /// Makes the self-extracting archive.
  443. /// </summary>
  444. /// <param name="archiveFileName">The archive file name.</param>
  445. /// <param name="settings">The sfx settings.</param>
  446. /// <param name="sfxStream">The stream to write the self-extracting executable to.</param>
  447. public void MakeSfx(string archiveFileName, SfxSettings settings, Stream sfxStream)
  448. {
  449. using (Stream archive = new FileStream(archiveFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
  450. )
  451. {
  452. MakeSfx(archive, settings, sfxStream);
  453. }
  454. }
  455. }
  456. #endif
  457. }