Crear un instalador/repack con Inno Setup Compiler + Inno Script Studio. Caso práctico: Sega Rally 2
No hay muchos tutoriales que muestren de manera práctica, sencilla, ordenada y lógica el proceso completo de realización de un repack de un juego con su respectivo instalador. Lo más común es encontrar guías farragosas, caóticas, enrevesadas, liosas e incompletas. Voy a intentar ponerle remedio de la mejor manera posible. Aunque, no te voy a engañar: todo lo que anote aquí será, sobre todo, un recordatorio personal para no olvidar cómo hice el instalador del Sega Rally 2. Dicho esto, y para llevar a cabo nuestro propósito, utilizaremos una serie de programas gratuitos que nos facilitarán enormemente la vida, y que iremos viendo a lo largo de esta entrada.
El esquema de trabajo va a ser el siguiente:
- Arreglar el juego y dejarlo totalmente funcional.
- Averiguar qué archivos instala y dónde, y qué entradas de registro, claves y valores escribe.
- Copiar todos los archivos y claves de registro a una carpeta de trabajo.
- Construir el script de instalación con Inno Setup Compiler + Inno Script Studio.
- Compilar el script y construir el archivo o archivos instalables.
- Probar el instalador, a ser posible en otro equipo, y comprobar que el juego funciona como debería.
╔════════════════════════════╗
1.- ARREGLO DEL JUEGO
╚════════════════════════════╝
Absolutamente toda la información y algo más está disponible en mi reciente entrada dedicada al Sega Rally 2. Te garantizo que, si sigues las instrucciones con cuidado, conseguirás hacer funcionar el juego en Windows 10-11 sin mayores problemas que los especificados a lo largo del texto (los cuelgues tras guardar replays y que el texto que escribes en el modo multi-player antes de entrar a la sala de espera no se ve). Este apartado va a ser, con diferencia, el que más tiempo te lleve.
╔════════════════════════════╗
2.- ARCHIVOS Y REGISTRO
╚════════════════════════════╝
Un instalador no es más que un programa diseñado para copiar una serie de archivos a uno (normalmente) o varios sitios, hacer alguna que otra configuración y escribir un puñado de entradas en el registro de Windows, así como en el grupo de programas del menú de inicio. Nuestro instalador hará algo parecido, pero para que funciona deberemos recorrer primero el camino inverso: averiguar lo que hace el instalador original y volver a empaquetar todo lo que ha desempaquetado, con las variaciones y añadidos que queramos, tanto a nivel de archivos como a nivel de registro.
Centrándonos primero en los archivos, lo más normal suele ser que el juego instale todos los archivos y carpetas dentro de un mismo directorio principal, ya sea en "Archivos de Programa (x86)" o donde le hayamos indicado durante la instalación. Puede darse el caso de que el juego meta cosas en otros sitios como Windows \ System32 o Windows \ SysWOW64, dependiendo de nuestro sistema operativo, pero no es lo más común. Este juego sí que escribe dos archivos DLL en SysWOW64. Lógicamente nadie es adivino, así que puedes valerte de programas como Process Monitor o Cameyo. Este último es el que he utilizado, con resultados muy positivos.
Hay que decir que Cameyo era, originalmente, una aplicación gratuita para crear instalaciones portables que, en un momento dado, pasó a ser de pago. Como no creo que quieras descargar la versión de pago desde su web oficial, lo que haremos será buscar la última versión gratuita, la cual se aloja en webs como uptodown o softonic.
Sí, lo sé... Puedo ver esa ceja arqueada en señal de escepticismo, pero déjame decirte que la versión alojada en dichas webs, esto es, la 3.1.1446, está limpia. En Virustotal está el resultado el análisis, dando positivo en 10 de 75 motores de análisis de antivirus:
De estos antivirus, no conozco ninguno. Si acaso el de Google (?), y sospecho que dichos positivos como troyano se basan en la forma de funcionar que tiene para capturar los cambios en el sistema. El resto de antivirus, 65, entre los que están todos los nombres importantes (Acronis, AVG, Avira, Avast, BitDefender, ESET-NOD32, GData, Kaspersky, Malwarebytes, McAfee, Microsoft, Panda, Symantec, TrendMicro, entre otros) dan TODOS negativo. Creo que es importante saber interpretar los resultados de este tipo de análisis. Ahora bien: de ti depende si te fías o no. Yo, lo tengo claro.
Cameyo sirve para crear versiones portables de programas y juegos. Básicamente hace una radiografía del sistema antes y después de la instalación del programa. Con los cambios producidos en carpetas y registro, extrae toda la información necesaria para generar un pack portable que contiene todos los archivos y entradas de registro, los cuales "monta" en un registro virtual. En resumen: crea una máquina virtual con el juego "instalado" dentro. Aunque nuestro fin no va a ser el de crear un portable, Cameyo nos servirá para echar un ojo a todos los archivos, entradas y claves de registro que el juego ha creado, para poder luego extraerlas del registro de manera manual, pues las necesitaremos más adelante.
Pues buen, lo primero que haremos será cerrar la mayor cantidad posible de aplicaciones y procesos, montaremos la imagen del CD de instalación del juego y abriremos Cameyo. Nos encontraremos con la siguiente ventana:
Escogeremos la opción "Capture an installation", haremos clic en OK y nos saldrá esta diminuta ventana en la parte inferior derecha de nuestra pantalla:
El programa estará unos minutos escaneando el "antes" del sistema. Esperaremos a que termine el proceso. Una vez concluido, nos aparecerá una nueva ventana invitándonos a instalar el programa o juego:
Una vez finalizada la instalación, haremos clic en "Install done". En ese momento, Cameyo realizará un nuevo escaneo del sistema para ver qué cambios se han producido:
Cuando el proceso termine, Cameyo generará de manera automática la versión portable del programa que acabamos de instalar:
Aceptaremos y el programa se cerrará. Lo ejecutaremos de nuevo, pero esta vez escogeremos la opción "Edit a package":
Nos aparecerá un listado de los portables que hayamos creado hasta el momento:
Elegiremos el package de nuestro juego y esperaremos a que se abra el editor:
El editor nos permitirá modificar varias cosas, entre ellas los archivos y las claves / entradas de registro, cada uno en su respectiva pestaña:
Antes hemos comentado la importancia de cerrar el máximo de aplicaciones posible antes de instalar el juego y hacer la captura de la instalación con Cameyo. Yo no lo he hecho, y ahora puede verse cómo aparecen cosas relacionadas con Google, por ejemplo. Si estuviéramos haciendo un portable, gracias a este editor las podríamos quitar, así como las entradas de registro no pertenecientes al juego.
Pero lo único que vamos a hacer realmente con Cameyo es tomar nota de:
- Archivos y ubicaciones de los mismos relacionados con el juego.
- Entradas y claves de registro relacionados con el juego (y que sean significativas).
Gracias a esta herramienta podemos ver, por ejemplo, que el juego coloca dos archivos en Windows \ System32 (SysWOW64 en sistemas de 64 bits): sr2_cpl.cpl y CABINET.DLL. Sería muy común olvidarse de estos archivos a la hora de hacer el instalador, por no ser siquiera conscientes de su existencia, y ya tendríamos un problema. Bueno, en realidad, con CABINET.DLL no pasaría nada: se trata de un archivo perteneciente a Microsoft, que gestiona la compresión / descompresión de archivos .cab. El instalador original de Sega Rally 2 intenta copiar su versión arcaica 1.0.601.0, de 64 KB de tamaño y fecha de modificación 20/03/1997 a un directorio de sistema protegido. Windows 10 y 11 ya tienen sus propias versiones mucho más modernas de ese archivo. Concretamente, yo tengo en la carpeta SysWOW64 en Windows 11 la versión 5.0.1.1, de 121 KB y fecha de modificación 16/11/2023. Cuando se copian archivos a una carpeta del sistema protegida, si el archivo no está ya allí, el instalador lo copiará sin problema. Pero si ya hay un archivo con el mismo nombre, no lo sobrescribirá. Por esta razón fue un poco complicado saber qué archivos realmente copió (o quería copiar) el instalador a la carpeta System32/SysWOW64. Entonces, ¿cómo me chivó Cameyo que el instalador había copiado ese archivo si, como he dicho, Windows no lo permite? Porque, en mi caso, sí que lo permite. ¿Cómo? Porque previamente había tomado posesión del directorio SysWOW64 con la característica 'Take Ownership' de la que he hablado en la anterior entrada, dedicada al juego. ¿Por qué lo había hecho? Por los problemas que estaba teniendo con el archivo dinput.dll de dicha carpeta. Al tomar posesión, Windows deja de tener el control total sobre dicha carpeta, recayendo en el administrador. Por eso, y de forma totamente fortuita, pude ver que el instalador colocaba ese archivo CABINET.DLL. Y en mi caso, de hecho, sustituyó a la versión moderna que venía con Windows. ¿Significa eso que tenemos que tener instalada la versión antigua de CABINET.DLL para que el juego funcione correctamente? Para nada. De hecho, no te recomiendo en absoluto que sustituyas la versión moderna por la antigua. Es más, podría haber seguido sin saber sobre cabinet.dll y sin incluirlo en mi repack, y el juego seguiría funcionando perfectamente, pero creo que es una buena práctica llevar un seguimiento de todo lo que hace el instalador original.
Igualmente, esta herramienta nos muestra qué entradas y claves de registro escribe el instalador con el juego recién instalado. Todas ellas están en HKEY_LOCAL_MACHINE (HKLM). Tendremos que ir al registro de Windows (Win + R → regedit) y exportarlas para luego incluirlas en nuestro instalador. Las que no incluiremos serán aquellas relacionadas con la desinstalación, por la sencilla razón de que el instalador que vamos a crear ya se va a encargar, de forma totalmente automática y transparente para nosotros, de gestionar la escritura y el borrado de archivos, accesos directos, grupos de programas, entradas y claves del registro: todos ellos serán eliminados cuando desinstalemos el juego, ya sea ejecutando directamente el desinstalador que se creará en la carpeta del juego, a través del acceso directo del grupo de programas del menú de inicio, o a través de "Programas y características" (antiguo "Agregar o quitar programas") del panel de control.
En el caso de este juego, además, hay partes de la configuración, como el tema de controles, que no se guarda en archivos sino en entradas del registro de Windows. Esto significa que podemos configurar los controles a nuestro gusto y luego exportar las claves. Pero, de nuevo, surge la duda: ¿en qué parte del registro se almacenan estos ajustes exactamente? Como comenté en la entrada dedicada al juego, todos los ajustes que hemos realizado en el applet de configuración de los controles del juego se quedan guardados en el registro de Windows, concretamente en esta dirección:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\InputSettings\0
Lo he podido averiguar mediante la aplicación RegScanner, que nos permite ordenar todas las entradas del registro por fecha de modificación, aunque también nos serviría perfectamente RegShot, que es la típica aplicación recomendada para estos menesteres.
Entonces, si queremos que los controles de nuestro juego queden ya configurados con la instalación, exportaremos dicha parte del registro para incorporarla al instalador, como luego veremos.
Con el mismo sistema (Cameyo, RegScanner o RegShot), averiguaremos, también, que el programa que estamos aplicando para que la música funcione en el juego, "_inmm.dll" coloca una copia del archivo "_inmm.dll" en Windows \ System32 (SysWOW64 en sistemas de 64 bits), y que escribe una serie de entradas en el registro que es necesario replicar si queremos que el sistema reconozca al programa como instalado e, incluso, configurado (si añadimos las entradas y claves de registro donde se almacenan los ajustes del mismo).
╔════════════════════════════╗
3.- COPIA DE TODOS LOS ARCHIVOS
Y CONFIGURACIONES A UNA
CARPETA DE TRABAJO
Y CONFIGURACIONES A UNA
CARPETA DE TRABAJO
╚════════════════════════════╝
Antes de proceder con la creación de nuestro instalador, es muy recomendable copiar todos los elementos que ya tenemos a una carpeta de trabajo. En esa carpeta podemos ir creando una serie de subcarpetas donde tenerlo todo ordenado (el esquema así como los nombres utilizados son sólo una sugerencia):
- "GAME" → Aquí colocaremos los archivos y carpetas del juego y de idiomas. Contendrá las siguientes subcarpetas:
- "BASE GAME" → Colocaremos en este carpeta el contenido de la carpeta del juego que ya tenemos reparado y funcionando, pero no todo: como daremos al usuario la opción de instalar el juego en inglés o en español, esta carpeta base contendrá sólo los archivos comunes a las dos versiones. Haciendo esto, cuando toque elegir entre instalar la versión en inglés o la versión en español, no tendremos que volver a copiar ninguno de los archivos comunes, pues la carpeta de cada idioma contendrá sólo los archivos correspondientes a dicho idioma, por lo que ahorraremos tiempo durante la instalación y el instalador ocupará menos espacio. No voy a relacionar aquí todo el contenido de la carpeta "JUEGO BASE", pero sí el de las carpetas de idioma, con lo cual simplemente tendrás que sustraer dichos archivos (lo cual incluye, por ejemplo, el ejecutable del juego) para quedarte justamente con lo que es el juego base.
- "SYSWOW64" → En esta carpeta irán los dos archivos que se habrán de instalar en la carpeta homónima de Windows. Crearemos tantas carpetas separadas como lugares distintos donde el juego instala cosas. En este caso, sólo instala cosas en la carpeta principal y en SysWOW64, por lo que no necesitamos crear ninguna otra carpeta para lo que es el juego en sí. No necesitamos que la carpeta con los dos archivos se llame "SYSWOW64"; el nombre no tiene por qué coincidir con el de la carpeta donde se van a instalar.
- "LANGUAGES" → Dentro se encuentra la carpeta "Inglés" y la carpeta "Español". Cada una contendrá la misma relación de archivos y carpetas, pero lógicamente cada una en su idioma. Para obtener el listado de archivos y carpetas que voy a mostrar a continuación he abierto una instancia de CMD en la carpeta del juego (botón derecho en cualquier zona libre de la carpeta - Abrir en terminal, o también tecleando CMD en la barra de direcciones superior, cuando estemos dentro de la carpeta) y he tecleado el siguiente comando:
tree /a /f > .\output.txt
Funciona exactamente igual en PowerShell. Esto nos generará un archivo llamado "output.txt" en la misma carpeta que estamos analizando. En mi caso, con el siguiente contenido:Spanish (<-esta primera línea la he añadido yo a mano) ¦ MSelect.dll ¦ ReplayGallery.dll ¦ SEGA RALLY 2.exe ¦ SR2_MSG.dll ¦ +---BINDATA ¦ ¦ desert.bg ¦ ¦ des_AC.bg ¦ ¦ empire.txr ¦ ¦ gameover.bg ¦ ¦ isle.bg ¦ ¦ loading.bg ¦ ¦ meter00us.txr ¦ ¦ meter01us.txr ¦ ¦ meter02us.txr ¦ ¦ mnt_AC.bg ¦ ¦ mountain.bg ¦ ¦ muddy.bg ¦ ¦ net_result.txr ¦ ¦ result.txr ¦ ¦ riviera.bg ¦ ¦ riv_AC.bg ¦ ¦ snowy.bg ¦ ¦ snw_AC.bg ¦ ¦ superSS.bg ¦ ¦ ¦ +---800x600 ¦ ¦ desert.bg ¦ ¦ des_AC.bg ¦ ¦ empire.txr ¦ ¦ gameover.bg ¦ ¦ isle.bg ¦ ¦ loading.bg ¦ ¦ mnt_AC.bg ¦ ¦ mountain.bg ¦ ¦ muddy.bg ¦ ¦ riviera.bg ¦ ¦ riv_AC.bg ¦ ¦ snowy.bg ¦ ¦ snw_AC.bg ¦ ¦ superSS.bg ¦ ¦ ¦ +---ARCADE ¦ ¦ +---DES_SS1 ¦ ¦ ¦ finish.txr ¦ ¦ ¦ ¦ ¦ +---MNT_SS2 ¦ ¦ ¦ finish.txr ¦ ¦ ¦ ¦ ¦ +---RIVIERA ¦ ¦ ¦ finish.txr ¦ ¦ ¦ ¦ ¦ \---SNW_SS3 ¦ ¦ finish.txr ¦ ¦ snw_ss3.txr ¦ ¦ ¦ +---BGM ¦ ¦ cp_037.wav ¦ ¦ cp_106.wav ¦ ¦ cp_205.wav ¦ ¦ cp_306.wav ¦ ¦ cp_a110.wav ¦ ¦ cp_coro.wav ¦ ¦ cp_del16.wav ¦ ¦ cp_delhf.wav ¦ ¦ cp_evo3.wav ¦ ¦ cp_evo4.wav ¦ ¦ cp_evo6.wav ¦ ¦ cp_f131.wav ¦ ¦ cp_imp555.wav ¦ ¦ cp_impwrc.wav ¦ ¦ cp_megane.wav ¦ ¦ cp_st185.wav ¦ ¦ cp_st205.wav ¦ ¦ cp_strat.wav ¦ ¦ ¦ +---CAR ¦ ¦ carpro00.txr ¦ ¦ carpro01.txr ¦ ¦ carpro02.txr ¦ ¦ carpro03.txr ¦ ¦ carpro04.txr ¦ ¦ carpro05.txr ¦ ¦ carpro06.txr ¦ ¦ carpro07.txr ¦ ¦ carpro08.txr ¦ ¦ carpro09.txr ¦ ¦ carpro10.txr ¦ ¦ carpro11.txr ¦ ¦ carpro12.txr ¦ ¦ carpro13.txr ¦ ¦ carpro14.txr ¦ ¦ carpro15.txr ¦ ¦ carpro16.txr ¦ ¦ carpro17.txr ¦ ¦ carpro18.txr ¦ ¦ carpro19.txr ¦ ¦ ¦ +---CHAMPAGN ¦ ¦ NEWCAR.txr ¦ ¦ ¦ +---chat ¦ ¦ CHAT.BMP ¦ ¦ CHAT_2.BMP ¦ ¦ CHAT_4.BMP ¦ ¦ menu_back.BMP ¦ ¦ MENU_error_US.BMP ¦ ¦ MENU_setumei_US.BMP ¦ ¦ MENU_title_US.BMP ¦ ¦ tab_menu_on.BMP ¦ ¦ tab_menu_on2.BMP ¦ ¦ ¦ +---connect ¦ ¦ ¦ MULTI_BG.BMP ¦ ¦ ¦ ¦ ¦ +---button ¦ ¦ ¦ cancel_off.BMP ¦ ¦ ¦ cancel_on.BMP ¦ ¦ ¦ cancel_on2.BMP ¦ ¦ ¦ create_off.BMP ¦ ¦ ¦ create_on.BMP ¦ ¦ ¦ create_on2.BMP ¦ ¦ ¦ delete_off.BMP ¦ ¦ ¦ delete_on.BMP ¦ ¦ ¦ delete_on2.BMP ¦ ¦ ¦ join_off.BMP ¦ ¦ ¦ join_on.BMP ¦ ¦ ¦ join_on2.BMP ¦ ¦ ¦ quit_off.BMP ¦ ¦ ¦ quit_on.BMP ¦ ¦ ¦ quit_on2.BMP ¦ ¦ ¦ showteam_off.BMP ¦ ¦ ¦ showteam_on.BMP ¦ ¦ ¦ showteam_on2.BMP ¦ ¦ ¦ ¦ ¦ +---DRV_NAME ¦ ¦ ¦ Window1_US.bmp ¦ ¦ ¦ ¦ ¦ +---IP_ENTRY ¦ ¦ ¦ Ip_entry_US.bmp ¦ ¦ ¦ ¦ ¦ +---MESSAGE ¦ ¦ ¦ MESSAGE_CLOSE_US.BMP ¦ ¦ ¦ MESSAGE_CONNECT_US.BMP ¦ ¦ ¦ MESSAGE_NOCONNECT_US.BMP ¦ ¦ ¦ MESSAGE_NOENTRY_US.BMP ¦ ¦ ¦ MESSAGE_NOTEAM_US.BMP ¦ ¦ ¦ MESSAGE_SEARCHTEAM_US.BMP ¦ ¦ ¦ ¦ ¦ +---MODEM ¦ ¦ ¦ MODEM_GAME_OFF.BMP ¦ ¦ ¦ MODEM_GAME_ON.BMP ¦ ¦ ¦ MODEM_GAME_ON2.BMP ¦ ¦ ¦ MODEM_NUMBER_OFF.BMP ¦ ¦ ¦ MODEM_NUMBER_ON.BMP ¦ ¦ ¦ MODEM_NUMBER_ON2.BMP ¦ ¦ ¦ MODEM_QUIT_OFF.BMP ¦ ¦ ¦ MODEM_QUIT_ON.BMP ¦ ¦ ¦ MODEM_QUIT_ON2.BMP ¦ ¦ ¦ WINDOW1.BMP ¦ ¦ ¦ ¦ ¦ +---PHONE_NO ¦ ¦ ¦ Window1_US.bmp ¦ ¦ ¦ ¦ ¦ +---PROTOCOL ¦ ¦ ¦ CONNECT.BMP ¦ ¦ ¦ CONNECT_OTHER_OFF.BMP ¦ ¦ ¦ CONNECT_OTHER_ON.BMP ¦ ¦ ¦ CONNECT_OTHER_ON2.BMP ¦ ¦ ¦ CONNECT_SERIAL_OFF.BMP ¦ ¦ ¦ CONNECT_SERIAL_ON.BMP ¦ ¦ ¦ CONNECT_SERIAL_ON2.BMP ¦ ¦ ¦ ¦ ¦ +---SEL_DRV ¦ ¦ ¦ WINDOW2_1_US.BMP ¦ ¦ ¦ WINDOW2_2_US.BMP ¦ ¦ ¦ WINS.BMP ¦ ¦ ¦ ¦ ¦ +---SEL_TEAM ¦ ¦ ¦ WINDOW2.BMP ¦ ¦ ¦ ¦ ¦ +---SERIAL ¦ ¦ ¦ FLOW.BMP ¦ ¦ ¦ PARITY.BMP ¦ ¦ ¦ SERIAL1.BMP ¦ ¦ ¦ ¦ ¦ \---Tm_entry ¦ ¦ Window1_US.bmp ¦ ¦ ¦ +---MISC ¦ ¦ ADV_TXT.TXR ¦ ¦ MAINMODE.TXR ¦ ¦ MS10YEAR.TXR ¦ ¦ MS2P.TXR ¦ ¦ MSARCADE.TXR ¦ ¦ M_CARPRO.TXR ¦ ¦ M_CARSET.TXR ¦ ¦ M_NETWRK.TXR ¦ ¦ M_TIMATK.TXR ¦ ¦ N_TIMATK.TXR ¦ ¦ OPTIONS.TXR ¦ ¦ Rank10.txr ¦ ¦ RankAC.txr ¦ ¦ RankTA.txr ¦ ¦ Record.txr ¦ ¦ RG_10.txr ¦ ¦ RG_AC.txr ¦ ¦ RG_RG.txr ¦ ¦ RG_TA.txr ¦ ¦ TITLE.TXR ¦ ¦ ¦ +---SEDATA ¦ ¦ +---Navi_f ¦ ¦ ¦ f030.wav ¦ ¦ ¦ f050.wav ¦ ¦ ¦ f070.wav ¦ ¦ ¦ f100.wav ¦ ¦ ¦ f150.wav ¦ ¦ ¦ f200.wav ¦ ¦ ¦ f300.wav ¦ ¦ ¦ f400.wav ¦ ¦ ¦ f500.wav ¦ ¦ ¦ f600.wav ¦ ¦ ¦ f700.wav ¦ ¦ ¦ f800.wav ¦ ¦ ¦ f900.wav ¦ ¦ ¦ fbeaut.wav ¦ ¦ ¦ fbridge.wav ¦ ¦ ¦ fbump.wav ¦ ¦ ¦ fcaution.wav ¦ ¦ ¦ fcheckp.wav ¦ ¦ ¦ fconcen.wav ¦ ¦ ¦ fcongra.wav ¦ ¦ ¦ fcrest.wav ¦ ¦ ¦ fdoublet.wav ¦ ¦ ¦ feasyl.wav ¦ ¦ ¦ feasyr.wav ¦ ¦ ¦ fexcell.wav ¦ ¦ ¦ ffinish.wav ¦ ¦ ¦ fgo.wav ¦ ¦ ¦ fgreat.wav ¦ ¦ ¦ fhairpinl.wav ¦ ¦ ¦ fhairpinr.wav ¦ ¦ ¦ fhazard.wav ¦ ¦ ¦ fhurry.wav ¦ ¦ ¦ fice.wav ¦ ¦ ¦ fin_gra.wav ¦ ¦ ¦ fin_tar.wav ¦ ¦ ¦ fjump.wav ¦ ¦ ¦ fkeep_l.wav ¦ ¦ ¦ fkeep_r.wav ¦ ¦ ¦ fk_left.wav ¦ ¦ ¦ fk_right.wav ¦ ¦ ¦ flong.wav ¦ ¦ ¦ fmaybe.wav ¦ ¦ ¦ fmed_l.wav ¦ ¦ ¦ fmed_r.wav ¦ ¦ ¦ fnarrow.wav ¦ ¦ ¦ fno.wav ¦ ¦ ¦ foh.wav ¦ ¦ ¦ fohno.wav ¦ ¦ ¦ fone.wav ¦ ¦ ¦ fopen.wav ¦ ¦ ¦ foutstan.wav ¦ ¦ ¦ fov_bump.wav ¦ ¦ ¦ fov_cres.wav ¦ ¦ ¦ fo_h_l.wav ¦ ¦ ¦ fo_h_r.wav ¦ ¦ ¦ fperfect.wav ¦ ¦ ¦ fslip.wav ¦ ¦ ¦ fsudden.wav ¦ ¦ ¦ fthree.wav ¦ ¦ ¦ fth_kink.wav ¦ ¦ ¦ ftighten.wav ¦ ¦ ¦ fturn_ar.wav ¦ ¦ ¦ ftwo.wav ¦ ¦ ¦ fv_l.wav ¦ ¦ ¦ fv_v_l.wav ¦ ¦ ¦ fwater.wav ¦ ¦ ¦ fwide.wav ¦ ¦ ¦ fwow.wav ¦ ¦ ¦ fw_done.wav ¦ ¦ ¦ fw_out.wav ¦ ¦ ¦ fydgreat.wav ¦ ¦ ¦ ¦ ¦ \---Navi_m ¦ ¦ m030.wav ¦ ¦ m050.wav ¦ ¦ m070.wav ¦ ¦ m100.wav ¦ ¦ m150.wav ¦ ¦ m200.wav ¦ ¦ m300.wav ¦ ¦ m400.wav ¦ ¦ m500.wav ¦ ¦ m600.wav ¦ ¦ m700.wav ¦ ¦ m800.wav ¦ ¦ m900.wav ¦ ¦ mbeaut.wav ¦ ¦ mbridge.wav ¦ ¦ mbump.wav ¦ ¦ mcaution.wav ¦ ¦ mcheckp.wav ¦ ¦ mcocen.wav ¦ ¦ mcongra.wav ¦ ¦ mcrest.wav ¦ ¦ mdoublet.wav ¦ ¦ measyl.wav ¦ ¦ measyr.wav ¦ ¦ mexcell.wav ¦ ¦ mfinish.wav ¦ ¦ mgo.wav ¦ ¦ mgreat.wav ¦ ¦ mhairpinl.wav ¦ ¦ mhairpinr.wav ¦ ¦ mhazard.wav ¦ ¦ mhurry.wav ¦ ¦ mice.wav ¦ ¦ min_gra.wav ¦ ¦ min_tar.wav ¦ ¦ mjump.wav ¦ ¦ mkeep_l.wav ¦ ¦ mkeep_r.wav ¦ ¦ mk_left.wav ¦ ¦ mk_right.wav ¦ ¦ mlong.wav ¦ ¦ mmaybe.wav ¦ ¦ mmed_l.wav ¦ ¦ mmed_r.wav ¦ ¦ mnarrow.wav ¦ ¦ mno.wav ¦ ¦ moh.wav ¦ ¦ mohno.wav ¦ ¦ mone.wav ¦ ¦ mopen.wav ¦ ¦ moutstan.wav ¦ ¦ mov_bump.wav ¦ ¦ mov_cres.wav ¦ ¦ mo_h_l.wav ¦ ¦ mo_h_r.wav ¦ ¦ mperfect.wav ¦ ¦ mslip.wav ¦ ¦ msudden.wav ¦ ¦ mthree.wav ¦ ¦ mth_kink.wav ¦ ¦ mtighten.wav ¦ ¦ mturn_ar.wav ¦ ¦ mtwo.wav ¦ ¦ mv_l.wav ¦ ¦ mv_v_l.wav ¦ ¦ mwater.wav ¦ ¦ mwide.wav ¦ ¦ mwow.wav ¦ ¦ mw_done.wav ¦ ¦ mw_out.wav ¦ ¦ mydgreat.wav ¦ ¦ ¦ \---TENYEAR ¦ +---10_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---10_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---10_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---10_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---11_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---1_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---1_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---1_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---1_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---2_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---2_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---2_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---2_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---3_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---3_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---3_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---3_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---4_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---4_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---4_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---4_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---5_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---5_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---5_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---5_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---6_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---6_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---6_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---6_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---7_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---7_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---7_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---7_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---8_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---8_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---8_3 ¦ ¦ finish.txr ¦ ¦ ¦ +---8_4 ¦ ¦ finish.txr ¦ ¦ ¦ +---9_1 ¦ ¦ finish.txr ¦ ¦ ¦ +---9_2 ¦ ¦ finish.txr ¦ ¦ ¦ +---9_3 ¦ ¦ finish.txr ¦ ¦ ¦ \---9_4 ¦ finish.txr ¦ \---MUSASHI MGAudio.dll SR2.dll
- "REGISTRY" → En esta carpeta meteremos todos los archivos .reg que hemos exportado del registro de Windows. En realidad no necesitaríamos tenerlos físicamente en ningún sitio, pues el script del instalador guardará todo el texto contenido en dichos archivos y los podremos eliminar. No obstante, no viene mal conservarlos para futuras referencias.
- "ASSETS" → Aquí colocaremos los archivos que van a dar forma, color y sonido al instalador. Contendrá las siguientes subcarpetas:
- "BITMAPS" → Las imágenes que conforman el instalador (fondo, lateral, superior).
- "ICONS" → Contendrá el icono que tendrá incrustado el instalador final y el del acceso directo en el grupo de programas al desinstalador.
- "INFO" → Contiene los archivos txt con el texto informativo que aparecerá en la ventana de información previa a la instalación.
- "LICENSE" → Contiene los arxchivos txt con el texto de licencia que aparecerá en la ventana de licencia previa a la instalación.
- "MUSIC" → Contiene el archivo mp3 que sonará durante el proceso de instalación, así como un archivo dll que contiene al reproductor Bass que se ejecutará durante la instalación.
- "POWERSHELL SCRIPTS" → Aquí meteremos todos los scripts de PowerShell, archivos .BAT y .EXE adicionales que podamos necesitar.
- "RESHADE" → Contiene los archivos de la famosa suite de shaders, incluyendo los propios shaders, el perfil personalizado, el archivo de configuración y el overlay personalizado. Todo lo necesario para funcionar, y con rutas relativas.
╔════════════════════════════╗
4.- CONSTRUCCIÓN
DEL SCRIPT DE INSTALACIÓN
CON INNO SETUP COMPILER
E INNO SCRIPT STUDIO
E INNO SCRIPT STUDIO
╚════════════════════════════╝
Como hemos dicho al comienzo, un instalador no es más que un programa diseñado para copiar una serie de archivos a uno (normalmente) o varios sitios, hacer alguna que otra configuración y escribir un puñado de entradas en el registro de Windows, así como en el grupo de programas del menú de inicio. Y todo ello lo realiza siguiendo una "receta", un script que le dice qué hacer paso a paso.
Nuestro script va a servir para construir el instalador y para decirle a éste dónde tiene que instalar las cosas. Lo vamos a construir utilizando la aplicación gratuita Inno Setup Compiler, adornada o facilitada con una interfaz más amigable y funcional para crear scripts, gracias a Inno Script Studio. Por lo tanto, debemos descargar e instalar ambas aplicaciones:
Aparte del asistente, tenemos, también, el menú de ajustes / asistente de la parte izquierda en la ventana principal. En realidad, se trata de dos asistentes: uno de ellos centrado en la sección [Setup] del script, y el otro para cubrir el resto de secciones:
Digamos que los asistentes serían la manera de crear el script a alto nivel, marcando casillas y rellenando huecos, pero también podemos ir construyendo el script directamente, a "bajo nivel", utilizando la propia "gramática" del script, incluyendo la sección [Code], donde podremos programar directamente en Pascal script, el cual se ha revelado como uno de los más adecuados para estas tareas.
Voy a utilizar como ejemplo mi script de Sega Rally 2. Iré comentando todas las secciones del script, explicando su función y cómo están construidas. Comenzaremos por el principio:
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "Sega Rally 2"
#define MyAppVersion "25th Anniversary"
; #define MyAppPublisher "OldNewPixel"
; #define MyAppURL "oldnewpixel.blogspot.com"
#define MyAppExeName "SEGA RALLY 2.exe"
Ni que decir tiene que yo no he escrito todo eso de manera manual. Me he limitado a seguir el asistente. Como ves, varias líneas empiezan con un ";". El punto y coma al comienzo de una línea sirve para escribir un comentario o para anular dicha línea. En este caso, el Publisher y la URL no aparecerán en el instalador porque los he anulado.
Siguiente sección: [Setup]. Veámosla:
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{ABB3D299-1160-4AB7-A375-070D60898C52}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
; AppPublisher={#MyAppPublisher}
; AppPublisherURL={#MyAppURL}
; AppSupportURL={#MyAppURL}
; AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\Sega\SEGA RALLY 2
;DefaultDirName={pf}\Sega\{#MyAppName}
DefaultGroupName={#MyAppName}
OutputBaseFilename=Sega Rally 2 25th Anniversary
VersionInfoVersion=1.0.6
Compression=lzma
SolidCompression=no
WizardImageFile=ASSETS\BITMAPS\Left.bmp
SetupIconFile=ASSETS\ICONS\Installer icon.ico
DisableWelcomePage=False
WindowVisible=True
PrivilegesRequired=admin
AppReadmeFile={app}Readme.txt
- AppId → es un código único que Inno Setup asigna a nuestra aplicación. En caso de querer cambiarlo, podemos Generar uno nuevo desde Setup Options - Application details - Application ID.
- DefaultDirName → el directorio donde el instalador nos propondrá instalar el juego por defecto. En este caso, {pf} significa "Program files", es decir, que propondrá instalarlo en "Archivos de Programa" o, por tratarse de una aplicación de 32 bits, en "Archivos de Programa (x86). Como quería que la carpeta del juego se llamase como en el juego original, es decir, con mayúsculas, he anulado la línea en la que cogía el nombre directamente de {#MyAppName}, y lo he nombrado directamente "SEGA RALLY 2".
- VersionInfoVersion=1.0.6 → indica la versión del programa. En mi caso, como era la sexta vez que rehacía el instalador con pequeños cambios y añadidos en el juego, lo he llamado 1.0.6.
- Compression=lzma → es la compresión recomendada. En cuanto a SolidCompression, particularmente la tengo desactivada cuando aglutino todo el instalador en un único archivo .exe, en vez de tenerlo en un .exe y uno o varios archivos .bin. En Setup options - Compiler settings podremos marcar la opción "Use setup loader" para que el instalador sea un solo archivo exe, o desmarcarla si queremos que se distribuya en varios archivos (exe + bin).
- WizardImageFile → la imagen que nos saldrá en pa parte izquierda de la venta a del instalador. Ha de ser un archivo bmp de 164x314 pixels:
- SetupIconFile → será el icono que tenga nuestro archivo instalador. En mi caso, he usado un archivo de 256x256.
- PrivilegesRequired=admin → el instalador funcionará con permisos de administrador, lo cual los puede venir de perlas para copiar archivos en carpetas rebeldes o en zonas conflictivas del sistema.
- AppReadmeFile={app}Readme.txt → le indica al instalador dónde se encuentra el archivo Readme.txt que dará opción de abrir cuando finalice la instalación. "{app}" hace referencia al directorio donde se ha instalado el juego o, mejor dicho, donde se ubica el ejecutable SEGA RALLY 2.EXE.
[Messages]
; Fuerza el cambio del mensaje original con la ID SelectLanguageTitle y SelectLanguageLabel contenidos en el archivo Default.inl por los que se idican a continuación:
SelectLanguageTitle=Select Setup Language / Elija idioma instalador
SelectLanguageLabel=Select the language to use during the installation. Elija el idioma a utilizar durante la instalación.
Aquí he modificado los mensajes originales contenidos en las IDs SelectLanguageTitle y SelectLanguageLabel en el archivo Default.inl (por defecto en C:\ Archivos de programa (x86) \ Inno Setup 6) para que se muestre un mensaje bilingüe. Ese mensaje bilingüe viene justo antes del selector de idioma del instalador, por lo que es necesario que esté ahí:
Es decir, los mensajes que escribamos en el script tendrán preferencia sobre los contenidos en el archivo Default.inl:
Gracias a eso no necesitamos modificar el archivo Default.isl para nada.
Sección [CustomMessages]:
[CustomMessages]
english.BaseFilesDescription=Base game files
spanish.BaseFilesDescription=Archivos base del juego
english.EnglishLanguageDescription=English Language
spanish.EnglishLanguageDescription=Idioma Inglés
english.SpanishLanguageDescription=Spanish Language
spanish.SpanishLanguageDescription=Idioma Español
english.CheckingDirectPlay=Checking and enabling DirectPlay
spanish.CheckingDirectPlay=Comprobando y habilitando DirectPlay
english.OpenReadmeCheckBox=Open Readme file
spanish.OpenReadmeCheckBox=Abrir archivo Readme
english.InstallReShadeTask=Install ReShade
spanish.InstallReShadeTask=Instalar ReShade
english.AddExtrasTask=Add goodies (artwork, manuals, guides)
spanish.AddExtrasTask=Añadir extras (carátulas, manuales, guías)
english.AdditionalOptionsGroup=Additional Options
spanish.AdditionalOptionsGroup=Opciones adicionales
english.SelectComponentsLabel2=Select the language you want to play this game in:
spanish.SelectComponentsLabel2=Selecciona el idioma en el que deseas jugar:
Todos éstos son mensajes personalizados, creados para pantallas y funciones específicas de nuestro instalador, los cuales no encajan en ninguna de las ID que ya utiliza el script. Los veremos en [Components], en [Tasks] y en otros sitios. Si te fijas, añadiendo delante "english." o "spanish." haremos que el instalador muestre uno u otro según el idioma escogido al principio.
Sección [Types]:
[Types]
; Previene mostrar la lista desplegable del ComboBox de Types
Name: "custom"; Description: "Custom installation"; Flags: iscustom; Languages: english spanish
La sección [Types] sirve para crear "Tipos" o categorías de instalación, es decir, qué tipo de instalación queremos realizar (Ej.: completo, personalizado, etc.). Puede ser útil para, por ejemplo, dar al usuario la opción de instalar el juego en español o en inglés. Sin embargo, en esta ocasión no vamos a utilizar la categoría [Types] porque los distintos tipos de instalación se muestran en una lista desplegable, cosa que no me convence. Me parece mucho más "profesional" marcar una opción de tipo "radio button", que es excluyente, permitiéndote sólo escoger una. Lo veremos en la siguiente sección. En definitiva, esta sección [Types] que hemos pegado está destinada únicamente a asegurarnos de que no se muestre dicha lista desplegable.
Sección [Components]:
[Components]
Name: "base"; Description: "{cm:BaseFilesDescription}"; Types: custom; Flags: fixed
Name: "english"; Description: "{cm:EnglishLanguageDescription}"; Flags: exclusive
Name: "spanish"; Description: "{cm:SpanishLanguageDescription}"; Flags: exclusive
Esta sección nostrará una ventana en el instalador con los componentes que hayamos designado. Los “componentes” son conjuntos de archivos que se pueden instalar juntos. Por ejemplo, puedes tener un componente llamado “Aplicación” que incluye archivos específicos y otro componente llamado “Motor de base de datos” con sus propios archivos. Los componentes se utilizan dentro del script para especificar qué archivos se instalarán según los tipos seleccionados por el usuario.
Pueden realizar la misma función que los [Types]. En este caso daremos al usuario a elegir entre instalar el juego en inglés o en español. Para ello se han creado tres componentes:
- base → contiene los archivos del juego base (lo veremos más adelante en la sección [Files]). Aparecerá seleccionado por defecto sin posibilidad de quitarlo. La parte {cm:BaseFilesDescription} significa que el texto referido al componente "base" que se mostrará en ese listado de componentes que nos mostrará el instalador será un mensaje personalizado (de ahí el "cm"), el que indica la etiqueta BaseFilesDescription en la sección [CustomMessages]. Ese mensaje variará en función del idioma que hayamos escogido para el proceso de instalación:
[CustomMessages] english.BaseFilesDescription=Base game files spanish.BaseFilesDescription=Archivos base del juego
- english → contiene los archivos específicos del idioma inglés (lo veremos más adelante en la sección [Files]). Aparecerá seleccionado por defecto si el idioma del instalador es el inglés (lo veremos en el apartado [Code]), per podremos marcar el español si lo preferimos. La parte {cm:EnglishLanguageDescription} funciona igual que con el componente "base" y, de nuevo, el mensaje que muestra variará en función del idioma que hayamos escogido para el proceso de instalación.
- spanish → contiene los archivos específicos del idioma español (lo veremos más adelante en la sección [Files]). Aparecerá seleccionado por defecto si el idioma del instalador es el español (lo veremos en el apartado [Code]), per podremos marcar el inglés si lo preferimos. La parte {cm:SpanishLanguageDescription} funciona igual que con el componente "base" y "english" y, de nuevo, el mensaje que muestra variará en función del idioma que hayamos escogido para el proceso de instalación.
Así es como se vería esta parte del instalador según escojamos inglés o español como idioma en el proceso de instalación:
Los textos de la parte superior ("Seleccione los componentes que...") e inferior ("La selección actual requiere...") están contenidos dentro del archivo Default.inl. Si quisiéramos cambiarlos, tendríamos que abrir dicho archivo, localizar las IDs o etiquetas asociadas a dichos mensajes y añadir un par de líneas en el apartado [Messages] para forzar el cambio. Como hemos hecho antes, vaya. La opción cutre y menos elegante, que aquí ni se contempla, sería editar a mano el archivo Default.inl, lo cual es una cutrez total y una mierda de solución que no tendría ni que mencionar para no dar ideas a nadie. Por cierto: enseguida veremos cómo se pone la imagen de la parte superior de la ventana.
Sección [Tasks]:
[Tasks]
; Tareas opcionales para ReShade, extras y colocación acceso directo en escritorio
Name: "installReShade"; Description: "{cm:InstallReShadeTask}"; GroupDescription: "{cm:AdditionalOptionsGroup}"
Name: "addExtras"; Description: "{cm:AddExtrasTask}"; GroupDescription: "{cm:AdditionalOptionsGroup}"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Esta sección sirve para añadir una pantalla al instalador donde aparezcan varios elementos instalables de manera opcional, seleccionables con casillas de verificación, pudiendo marcar varios a la vez, todos o ninguno. Igual que antes, en el apartado [CustomMessages] tenemos todo el texto relacionado con con estas Tasks. Y en [Files] están las rutas a los archivos relacionados con las Tasks, como enseguida veremos. Así se ve la ventana con las tareas:
El comportamiento por defecto de las checkboxes en Inno Setup es que vengan marcadas. Si queremos que alguna de ellas venga desmarcada, como es el caso de la tercera, tendremos que añadir el flag "unchecked", tal y como puede verse en la última línea de la sección [Tasks] que acabamos de poner.
Sección [Files]:
[Files]
Source: "ASSETS\INFO\InfoBefore_English.txt"; Flags: dontcopy
Source: "ASSETS\INFO\InfoBefore_Spanish.txt"; Flags: dontcopy
Source: "ASSETS\BITMAPS\SETUP.BMP"; Flags: dontcopy
Source: "POWERSHELL SCRIPTS\EnableDirectPlay.ps1"; DestDir: "{tmp}"; Flags: deleteafterinstall
Source: "ASSETS\BITMAPS\up.bmp"; Flags: dontcopy
; Música y reproductor para que suene durante la instalación
Source: "ASSETS\MUSIC\Bass.dll"; Flags: dontcopy
Source: "ASSETS\MUSIC\Install.mp3"; Flags: dontcopy
; Inno Setup asigna el mismo icono del instalador al archivo de desinstalación (unins000.exe), y no permite cambiarlo. La siguiente línea, junto con la penúltima línea de la sección [Icons] sirve para solucionarlo
Source: "ASSETS\ICONS\Uninstall icon.ico"; DestDir: "{app}"; Flags: ignoreversion
; Archivos base del juego, siempre instalados
Source: "GAME\BASE GAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: base
; Archivos para el idioma inglés
Source: "GAME\LANGUAGES\English\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: english
; Archivos para el idioma español
Source: "GAME\LANGUAGES\Spanish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: spanish
; Archivos de ReShade, sólo si se selecciona la tarea
Source: "RESHADE\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Tasks: installReShade
; Archivos extras (guías), solo si se selecciona la tarea
Source: "EXTRAS\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Tasks: addExtras
; Applet de configuración del mando/teclado/volante y otro archivo dll. Los coloca en Windows\System32 o en Windows\SysWOW64 dependiendo de la arquitectura del sistema (32 o 64 bits respectivamente)
Source: "GAME\SYSWOW64\*"; DestDir: "{syswow64}"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: IsWin64
Source: "GAME\SYSWOW64\*"; DestDir: "{sys}"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not IsWin64
En esta sección se enlaza a todos los archivos que serán incluidos en el instalador. Las rutas a los elementos pueden ser absolutas o relativas. En este caso, son todas relativas a la ubicación donde tengamos guardado nuestro archivo .iss de Inno Script Setup. Por ejemplo:
[Files]
Source: "GAME\LANGUAGES\Spanish\*"
Significa que la carpeta "GAME" está al mismo nivel o en la misma carpeta que nuestro archivo de script de Inno Setup con extensión .iss. Usando estas direcciones relativas el script no necesita saber la ruta completa de la carpeta principal que aloja a todos esos archivos y carpetas, siempre y cuando el propio archivo del script esté ubicado en el mismo lugar.
Aparte de eso, el asterisco del final indica que se van a incluir en el instalador todos los archivos y carpetas o subcarpetas contenidos en la carpeta "Spanish".
Todos los archivos que no consten en [Files] no serán incluidos en el instalador. Recalco este hecho porque más adelante veremos cómo forzar el cambio de icono de un elemento. Ese cambio se materializa poniendo la ruta al archivo .ico en dicho apartado, pero si no lo incluimos también en [Files], jamás será incluido dicho icono en el instalador. Lo descubrí al instalar el juego en otro equipo, donde el icono no salía. En el PC host donde creeé el script sí se veía, simplemente porque el archivo estaba ya en el disco duro y el script apuntaba a él, pero, al no estar en [Files], el instalador no lo incluía y, por lo tanto, no se mostraba en otros equipos distintos al de creación del instalador.
Vamos a ver y comentar por separado algunas de las secciones de [Files]:
[Files]
Source: "ASSETS\INFO\InfoBefore_English.txt"; Flags: dontcopy
Source: "ASSETS\INFO\InfoBefore_Spanish.txt"; Flags: dontcopy
Source: "ASSETS\BITMAPS\SETUP.BMP"; Flags: dontcopy
Source: "POWERSHELL SCRIPTS\EnableDirectPlay.ps1"; DestDir: "{tmp}"; Flags: deleteafterinstall
Source: "ASSETS\BITMAPS\up.bmp"; Flags: dontcopy
Las dos primeras líneas contienen los archivos de texto cuyo contenido se va a mostrar en una de las primeras pantallas del instalador. En el apartado [Languages] que mostraremos en breve, queda definido cuál de los dos se tiene que mostrar en función del idioma escogido al inicio del proceso de instalación. El flag "dontcopy" indica al instalador que esos archivos contenidos en el paquete no deben ser copiados a la carpeta del juego ni a ningún otro sitio. Así es como se muestra el texto informativo en el instalador:
La siguiente línea corresponde al archivo "SETUP.BMP" que hace de fondo de pantalla durante todo el proceso de instalación. Es el mismo fondo de pantalla del instalador original, reescalado por IA (waifu2x, que en este caso ofrece mucho mejores resultados que Topaz y otros). Para que no se estire, he completado el marco 16:9 con el overlay que hice para ReShade. El archivo final tiene una resolución de 1920x1080.
La siguiente línea incluye un script de PowerShell que el instalador ejecutará al final del proceso. Dicho script es para comprobar si DirectPlay está activado, y activarlo en caso de no estarlo. Lo veremos más adelante.
Por último, tenemos el archivo "up.bmp", que se corresponde con la franja superior del instalador. Es un archivo de 495x60 pixels. En el apartado [Code] que luego veremos daremos instrucciones al instalador de qué hacer con ese archivo. Este y otros elementos del script pueden ser fácilmente incluidos en el mismo desde el asistente, sin necesidad de incluirlo a mano en el script ni escribir código.
Vamos con el siguiente bloque dentro de [Files]:
; Música y reproductor para que suene durante la instalación
Source: "ASSETS\MUSIC\Bass.dll"; Flags: dontcopy
Source: "ASSETS\MUSIC\Install.mp3"; Flags: dontcopy
Con esto incluimos en el instalador un reproductor de audio y el archivo a reproducir durante la instalación. Esto se verá completado con el código correspondiente del apartado [Code] que luego veremos.
Continuando con el siguiente fragmento de [Files]:
; Inno Setup asigna el mismo icono del instalador al archivo de desinstalación (unins000.exe), y no permite cambiarlo. La siguiente línea, junto con la penúltima línea de la sección [Icons] sirve para solucionarlo
Source: "ASSETS\ICONS\Uninstall icon.ico"; DestDir: "{app}"; Flags: ignoreversion
Aquí hacemos que el acceso directo a unins000.exe que se crea en el grupo de programas del menú de inicio tenga un icono diferente al que lleva incrustado el propio exe. Pero para que la cosa funcione es necesario que el icono que queremos utilizar esté físicamente en algún lugar del sistema. Con DestDir: "{app}" estamos indicando al instalador que el directorio de destino del icono sea el de la propia aplicación o juego (que es, a su vez, donde se encuentra el ejecutable del juego). En el apartado [Icons] veremos con capturas por qué hemos cambiado el icono por defecto.
Siguiendo con [Files]:
; Archivos base del juego, siempre instalados
Source: "GAME\BASE GAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: base
; Archivos para el idioma inglés
Source: "GAME\LANGUAGES\English\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: english
; Archivos para el idioma español
Source: "GAME\LANGUAGES\Spanish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: spanish
; Archivos de ReShade, sólo si se selecciona la tarea
Source: "RESHADE\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Tasks: installReShade
; Archivos extras (guías), solo si se selecciona la tarea
Source: "EXTRAS\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Tasks: addExtras
Tomando como ejemplo los archivos base del juego, vemos cómo el origen de donde va a tomar todos los archivos y carpetas está en la dirección relativa "GAME\BASE GAME\*". El añadido "\*" indica al script que lo que tiene que incorporar al instalador en el momento de la compilación son todos los archivos y carpetas contenidos en "BASE GAME", pero no la propia carpeta "BASE GAME" como tal. Por eso debemos ser cuidadosos con las direcciones y la estructura de trabajo previa a la construcción del instalador.
Si nos fijamos en el final de la parte correpondiente al juego base, vemos que asigna todos los archivos al componente "base". Lo mismo con "english" y con "spanish". Sin embargo, en cuanto a ReShade y a los extras, vemos que los asigna a las tareas "installReShade" y "addExtras", respectivamente. Tanto los "Components" como las "Tasks" los hemos fijado previamente.
Y la última parte de [Files]:
; Applet de configuración del mando/teclado/volante y otro archivo dll. Los coloca en Windows\System32 o en Windows\SysWOW64 dependiendo de la arquitectura del sistema (32 o 64 bits respectivamente)
Source: "GAME\SYSWOW64\*"; DestDir: "{syswow64}"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: IsWin64
Source: "GAME\SYSWOW64\*"; DestDir: "{sys}"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not IsWin64
Aquí tenemos dos archivos que hemos metido en una carpeta aparte. La he llamado SYSWOW64 para recordar que son los archivos que el juego original instala en System32 o en SysWOW64, en función de nuestro sistema operativo (32 o 64 bits). Podemos ver que el instalador cambiará el directorio de destino previa comprobación de si el sistema es de 64 bits. Si lo es, copiará los dos archivos a SysWOW64. Si no lo es, a System32.
Vamos ahora con el apartado [Icons]:
[Icons]
; Son los accesos directos que irán en el menú de inicio de Windows
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\{#MyAppName} Launcher"; Filename: "{app}\LAUNCH.EXE"
Name: "{group}\{#MyAppName} Uninstall"; Filename: "{uninstallexe}"; IconFilename: "{app}\Uninstall icon.ico"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Son los elementos que aparecerán en el grupo de programas del menú de inicio de Windows:
En el caso del desinstalador, como hemos comentado hace un momento en la sección [Files], usremos un icono diferente al que usaría por defecto el instalador a la hora de crear estos accesos directos. La parte de IconFilename: "{app}\Uninstall icon.ico" de la sección [Icons] sirve para colocar en el acceso directo del grupo de programas del menú de inicio el icono que se ve en la captura sobre estas líneas, que es el clásico, típico y genérico icono de instalador / desinstalador que todo el mundo conoce y entiende, pues es mucho más representativo de un desinstalador que el icono que el instalador colocaría por defecto ahí de no haberle forzado a usar el otro. ¿Cuál sería? Pues el del propio archivo desinstalador. ¿Y qué icono es ése? Por defecto, Inno Setup le asigna al desinstalador exactamente el mismo icono que le hayamos puesto al archivo instalador; muy bonito para un instalador, pero nada adecuado para un desinstalador:
La última línea termina de fijar las características de la Task "desktop icon" que hemos definido en el apartado [Tasks]: añade en el escritorio un acceso directo al juego (SEGA RALLY 2.EXE) y le pone el mismo nombre. Por lo tanto, para la tarea de crear un acceso directo en el escritorio necesitamos, también, ese extra de información de la sección [Icons].
Pasamos ahora al apartado [Languages]:
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"; InfoBeforeFile: "ASSETS\INFO\InfoBefore_English.txt"; LicenseFile: "ASSETS\LICENSE\License_English.txt"
Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl"; InfoBeforeFile: "ASSETS\INFO\InfoBefore_Spanish.txt"; LicenseFile: "ASSETS\LICENSE\License_Spanish.txt"
Ésta es la parte del script que muestra el instalador en el idioma seleccionado, al igual que la información previa y la de licencia. Por si te lo estás preguntando: en Inno Setup, aunque hayas definido las rutas a los archivos de licencia e información en la sección [Files], también necesitas especificar estas rutas en la sección [Languages]. Esto se debe a que la sección [Languages] se utiliza para definir los archivos específicos de cada idioma que se mostrarán durante la instalación.
Vamos ahora con la sección [Registry]. Todos los archivos .reg que arrastremos y soltemos en el apartado "Registry" de la columna izquierda del Inno Script Studio verán su contenido incluido en la sección [Registry] del script. Hay algunas partes concretas que contienen direcciones específicas del equipo donde se instaló el juego originalmente, y que habrá que "universalizar":
[Registry]
; Imported Registry File
Root: "HKLM"; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\SEGA RALLY 2.exe"; ValueType: string; ValueName: "Path"; ValueData: "{app}"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\SEGA RALLY 2.exe"; ValueType: string; ValueData: "{app}\SEGA RALLY 2.exe"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Microsoft\DirectPlay\Applications\SEGA RALLY 2"; ValueType: string; ValueName: "Guid"; ValueData: "{{6A470280-0790-11d3-9E1B-00A0C9A0F04F}"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Microsoft\DirectPlay\Applications\SEGA RALLY 2"; ValueType: string; ValueName: "File"; ValueData: "SEGA RALLY 2.exe"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Microsoft\DirectPlay\Applications\SEGA RALLY 2"; ValueType: string; ValueName: "CommandLine"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Microsoft\DirectPlay\Applications\SEGA RALLY 2"; ValueType: string; ValueName: "Path"; ValueData: "{app}"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Microsoft\DirectPlay\Applications\SEGA RALLY 2"; ValueType: string; ValueName: "CurrentDirectory"; ValueData: "{app}"; Flags: uninsdeletekey
El registro de Windows suele mostrar completa la dirección donde está el ejecutable del juego. La forma de "universalizar" una dirección es sustituir la ruta a la carpeta del ejecutable por "{app}", ccomo puede veres en varias líneas en el texto anterior. Esto indicará al instalador que debe escribir une entrada en el registro de Windows en la dirección indicada, pero poniendo como valor la ruta donde hayamos instalado el juego. Esto lo hace el instalador automáticamente porque guarda la dirección donde le hemos indicado que instale el juego.
Continuando con la sección [Registry]:
Root: "HKLM"; Subkey: "SOFTWARE\Sega"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Sega\SEGA RALLY 2"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Sega\SEGA RALLY 2\1.00.000"; Flags: uninsdeletekey
; Imported Registry File
Root: "HKLM"; Subkey: "SOFTWARE\Classes\SR2File\DefaultIcon"; ValueType: string; ValueData: "{app}\MUSASHI\SR2.DLL,0"; Flags: uninsdeletekey
Y ahora la sección de [Registry] donde se almacenan los ajustes de control que hagamos en el applet de configuración de mandos:
; Imported Registry File
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\Profiles\0"; ValueType: binary; ValueName: "nDeivce"; ValueData: "01 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\Profiles\0"; ValueType: binary; ValueName: "nType"; ValueData: "02 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\Profiles\0"; ValueType: binary; ValueName: "nDeviceType"; ValueData: "07 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\Profiles\1"; ValueType: binary; ValueName: "nDeivce"; ValueData: "00 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\Profiles\1"; ValueType: binary; ValueName: "nType"; ValueData: "01 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\Profiles\1"; ValueType: binary; ValueName: "nDeviceType"; ValueData: "00 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Microsoft SideWinder 3D Pro"; ValueData: "01 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Microsoft SideWinder Force Feedback Pro"; ValueData: "02 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Microsoft SideWinder Force Feedback Wheel"; ValueData: "03 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Microsoft SideWinder Freestyle Pro"; ValueData: "04 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Microsoft SideWinder Freestyle Pro (USB)"; ValueData: "04 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Microsoft SideWinder game pad"; ValueData: "05 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Microsoft SideWinder Precision Pro"; ValueData: "06 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Microsoft SideWinder Precision Pro (USB)"; ValueData: "06 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "SEGA SATURN PAD"; ValueData: "07 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Logitech WingMan Formula Force Serial"; ValueData: "0a 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\ControlPanel\SpecialProducts"; ValueType: binary; ValueName: "Logitech WingMan Formula Force USB"; ValueData: "0a 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\InputSettings"; ValueType: string; ValueName: "0"; ValueData: "7"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\InputSettings"; ValueType: string; ValueName: "1"; ValueData: "0"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\InputSettings\0"; ValueType: binary; ValueData: "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 2d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 c8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 39 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 2f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 c8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 cb 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 cd 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\InputSettings\0"; ValueType: binary; ValueName: "7"; ValueData: "02 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 4f 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 50 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 4d 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 4e 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 02 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 c8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 cb 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 cd 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 06 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 05 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 56 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 54 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 02 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 04 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\InputSettings\0"; ValueType: binary; ValueName: "0"; ValueData: "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 2d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 c8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 2c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 2f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 c8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 cb 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 cd 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Sega\SEGA RALLY 2\InputSettings\1"; ValueType: binary; ValueName: "0"; ValueData: "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 1f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 15 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 c8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 0a 00 00 00 03 00 00 00 00 00 00 00 10 27 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 39 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 27 00 00 39 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; Flags: uninsdeletekey
Y ahora la parte donde se registran los archivos DLL contenidos en la carpeta Musashi:
; Imported Registry File: MEvent.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{F8743DC0-627C-11D2-BD4E-0000C02DB0F3}"; ValueType: string; ValueData: "MEvent"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{F8743DC0-627C-11D2-BD4E-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MEvent.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{F8743DC0-627C-11D2-BD4E-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{F8743DC0-627C-11D2-BD4E-0000C02DB0F3}\ProgID"; ValueType: string; ValueData: "MUSASHI.MEvent.1"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{F8743DC0-627C-11D2-BD4E-0000C02DB0F3}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MEvent"; Flags: uninsdeletekey
; Imported Registry File: MGameD3D.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{1A413041-D93C-11d1-8F44-00A0C9697E45}"; ValueType: string; ValueData: "MUSASHI GameD3D Library"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{1A413041-D93C-11d1-8F44-00A0C9697E45}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MGameD3D.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{1A413041-D93C-11d1-8F44-00A0C9697E45}\ProgID"; ValueType: string; ValueData: "MUSASHI.GameD3D.0"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{1A413041-D93C-11d1-8F44-00A0C9697E45}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.GameD3D"; Flags: uninsdeletekey
; Imported Registry File: MGameGL.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{27AFF141-DA17-11d1-8F44-00A0C9697E45}"; ValueType: string; ValueData: "MUSASHI Graphic Library"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{27AFF141-DA17-11d1-8F44-00A0C9697E45}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MGameGL.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{27AFF141-DA17-11d1-8F44-00A0C9697E45}\ProgID"; ValueType: string; ValueData: "MUSASHI.GameGL.0"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{27AFF141-DA17-11d1-8F44-00A0C9697E45}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.GameGL"; Flags: uninsdeletekey
; Imported Registry File: MGameReg.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{EE799FC0-D56F-11D2-8D16-00105A6B7166}"; ValueType: string; ValueData: "MGameRegistry"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{EE799FC0-D56F-11D2-8D16-00105A6B7166}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MGameReg.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{EE799FC0-D56F-11D2-8D16-00105A6B7166}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{EE799FC0-D56F-11D2-8D16-00105A6B7166}\ProgID"; ValueType: string; ValueData: "MUSASHI.MGameRegistry.1"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{EE799FC0-D56F-11D2-8D16-00105A6B7166}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MGameRegistry"; Flags: uninsdeletekey
; Imported Registry File: MGAudio.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{ACEF8F00-D517-11D1-A496-0000C02DB0F3}"; ValueType: string; ValueData: "MGameAudio"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{ACEF8F00-D517-11D1-A496-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MGAudio.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{ACEF8F00-D517-11D1-A496-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{ACEF8F00-D517-11D1-A496-0000C02DB0F3}\ProgID"; ValueType: string; ValueData: "MUSASHI.MGameAudio.1"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{ACEF8F00-D517-11D1-A496-0000C02DB0F3}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MGameAudio"; Flags: uninsdeletekey
; Imported Registry File: MGInput.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{5784B940-F4BC-11D1-A496-0000C02DB0F3}"; ValueType: string; ValueData: "MGameInput"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{5784B940-F4BC-11D1-A496-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MGInput.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{5784B940-F4BC-11D1-A496-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{5784B940-F4BC-11D1-A496-0000C02DB0F3}\ProgID"; ValueType: string; ValueData: "MUSASHI.MGameInput.1"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{5784B940-F4BC-11D1-A496-0000C02DB0F3}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MGameInput"; Flags: uninsdeletekey
; Imported Registry File: MGLBackground.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{452593F0-F878-11d2-ADB9-00A0C9A0FB23}"; ValueType: string; ValueData: "MUSASHI MGLBackground"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{452593F0-F878-11d2-ADB9-00A0C9A0FB23}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MGLBackground.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{452593F0-F878-11d2-ADB9-00A0C9A0FB23}\ProgID"; ValueType: string; ValueData: "MUSASHI.MGLBackground.0"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{452593F0-F878-11d2-ADB9-00A0C9A0FB23}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MGLBackground"; Flags: uninsdeletekey
; Imported Registry File: MGNetWk.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{0D5837F0-3E3C-11D2-924E-00A0C9697E45}"; ValueType: string; ValueData: "MGameNetwork"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{0D5837F0-3E3C-11D2-924E-00A0C9697E45}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MGNetWk.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{0D5837F0-3E3C-11D2-924E-00A0C9697E45}\ProgID"; ValueType: string; ValueData: "MUSASHI.MGameNetwork.1"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{0D5837F0-3E3C-11D2-924E-00A0C9697E45}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MGameNetwork"; Flags: uninsdeletekey
; Imported Registry File: MGSound.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{6177AF40-D601-11D1-A496-0000C02DB0F3}"; ValueType: string; ValueData: "MGameSound"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{6177AF40-D601-11D1-A496-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MGSound.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{6177AF40-D601-11D1-A496-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{6177AF40-D601-11D1-A496-0000C02DB0F3}\ProgID"; ValueType: string; ValueData: "MUSASHI.MGameSound.1"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{6177AF40-D601-11D1-A496-0000C02DB0F3}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MGameSound"; Flags: uninsdeletekey
; Imported Registry File: MStream.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}"; ValueType: string; ValueData: "MStreamingFile"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MStream.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}\ProgID"; ValueType: string; ValueData: "MUSASHI.MStreamingFile.1"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\Classes\WOW6432Node\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MStreamingFile"; Flags: uninsdeletekey
; Imported Registry File: MStream2.dll.reg
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Classes\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}"; ValueType: string; ValueData: "MStreamingFile"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Classes\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueData: "{app}\MUSASHI\MStream.dll"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Classes\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Classes\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}\ProgID"; ValueType: string; ValueData: "MUSASHI.MStreamingFile.1"; Flags: uninsdeletekey
Root: "HKLM"; Subkey: "SOFTWARE\WOW6432Node\Classes\CLSID\{{08A33BE0-61C6-11D2-BD4E-0000C02DB0F3}\VersionIndependentProgID"; ValueType: string; ValueData: "MUSASHI.MStreamingFile"; Flags: uninsdeletekey
Si te fijas, todas las entradas de esta parte de la sección [Registry] contienen un código alfanumérico entre llaves que cambia de una instalación a otra. Por suerte, el código en las claves de registro que exportermos de nuestra instalación servirá sin ningún problem en otros equipos con Windows 10 y 11.
Siguiendo con [Registry], vamos a centrarnos ahora en los ajustes de compatibilidad del juego, que quedan guardados en el registro de Windows y son fácilmente exportables:
; Ajustes de compatibilidad de los ejecutables del juego
Root: "HKCU"; Subkey: "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\SEGA RALLY 2.exe"; ValueData: "~ RUNASADMIN HIGHDPIAWARE WINXPSP3 DWM8And16BitMitigation"
Root: "HKCU"; Subkey: "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\LAUNCH.EXE"; ValueData: "~ RUNASADMIN"
Root: "HKCU"; Subkey: "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; ValueType: string; ValueName: "{app}\dgVoodooCpl.exe"; ValueData: "~ RUNASADMIN"
Por último, incluiremos las claves y valores de _inmm que el instalador se encargará de restaurar en el registro para que no recibamos ningún mensaje de error relacionado con la instalación no aplicada al iniciar el juego:
; Claves de registro de _inmm (registro de la aplicación y ajustes)
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "Version"; ValueData: "$00000236"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: string; ValueName: "_inmmserv"; ValueData: "{app}\_inmmserv.exe"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: string; ValueName: "WA_Path"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "WA_Hide"; ValueData: "$00000000"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "WA_SkipWaveOutCheck"; ValueData: "$00000000"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: string; ValueName: "SMX_Path"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "SMX_Hide"; ValueData: "$00000000"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: string; ValueName: "DCDP_Path"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "DCDP_Hide"; ValueData: "$00000000"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "DCDP_SkipWaveOutCheck"; ValueData: "$00000000"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "DCDP_EmulatePositionFunc"; ValueData: "$00000001"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: string; ValueName: "Lilith_Path"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "Lilith_Hide"; ValueData: "$00000000"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "ErrDispLevel"; ValueData: "$00000002"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "ErrLogLevel"; ValueData: "$00000003"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: string; ValueName: "LogName"; ValueData: "_inmm.log"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "NoticeLogged"; ValueData: "$00000001"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "SE_Priority"; ValueData: "$00000001"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll"; ValueType: dword; ValueName: "Enabled"; ValueData: "$00000001"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: string; ValueName: "FileMask0"; ValueData: "*.wav"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "Player0"; ValueData: "$00000006"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: string; ValueName: "FileMask1"; ValueData: "*.mp3"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "Player1"; ValueData: "$00000006"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: string; ValueName: "FileMask2"; ValueData: "*.mid|*.rmi"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "Player2"; ValueData: "$00000001"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: string; ValueName: "FileMask3"; ValueData: "*.ogg"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "Player3"; ValueData: "$00000006"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: string; ValueName: "FileMask4"; ValueData: "*.flac"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "Player4"; ValueData: "$00000006"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: string; ValueName: "FileMask5"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "Player5"; ValueData: "$00000000"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "default"; ValueData: "$00000002"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "cdplayer"; ValueData: "$00000001"; Flags: uninsdeletekey
Root: "HKCU"; Subkey: "Software\irori\_inmm.dll\Player"; ValueType: dword; ValueName: "cddrive"; ValueData: "$00000000"; Flags: uninsdeletekey
Con estas claves, "_inmm.dll" funcionará perfectamente sin necesidad de instalación.
Si te has fijado en todas estas entradas del registro, todas las claves del juego se almacenan en "Local Machine" salvo los ajustes de compatibilidad de los ejecutables y la configuración de _inmm.dll, almacenándose todos ellos en "Current User".
Siguiente sección: [UninstallDelete]. Como su nombre indica, determinará qué se va a borrar durante el proecso de desinstalación del juego:
[UninstallDelete]
Type: filesandordirs; Name: "{app}"
No es necesario incluir aquí una referencia específica a los dos archivos de SysWOW64 porque el instalador guarda un registro de los archivos que se le añaden al compilar el script, de modo que eliminará todo lo que haya instalado, incluyendo el archivo sr2_cpl.cpl. El archivo CABINET.DLL no lo eliminará porque, recordemos, Windows no ha dejado al instalador copiar ese archivo, al haber una versión mucho más moderna ya presente en SysWOW64.
Vamos ahora con la sección [Code]. Esta sección es la más potente de Inno Setup ya que permite programar cualquier cosa que podamos imaginar, utilizando el lenguaje Pascal Script. Como no se trata a estas alturas de aprender a programar en Pascal Script, nos limitaremos a incorporar, modificar y adaptar partes de código hechas por otros (ChatGPT, StackOverflow, etc). Voy a poner primer el código completo del script de Sega Rally 2 y lo irécomentando poco a poco:
[Code]
const
BASS_SAMPLE_LOOP = 4;
BASS_UNICODE = $80000000;
BASS_CONFIG_GVOL_STREAM = 5;
const
#ifndef UNICODE
EncodingFlag = 0;
#else
EncodingFlag = BASS_UNICODE;
#endif
type
HSTREAM = DWORD;
function BASS_Init(device: LongInt; freq, flags: DWORD;
win: HWND; clsid: Cardinal): BOOL;
external 'BASS_Init@files:bass.dll stdcall';
function BASS_StreamCreateFile(mem: BOOL; f: string; offset1: DWORD;
offset2: DWORD; length1: DWORD; length2: DWORD; flags: DWORD): HSTREAM;
external 'BASS_StreamCreateFile@files:bass.dll stdcall';
function BASS_ChannelPlay(handle: DWORD; restart: BOOL): BOOL;
external 'BASS_ChannelPlay@files:bass.dll stdcall';
function BASS_SetConfig(option: DWORD; value: DWORD ): BOOL;
external 'BASS_SetConfig@files:bass.dll stdcall';
function BASS_Free: BOOL;
external 'BASS_Free@files:bass.dll stdcall';
procedure InitializeWizard();
var
StreamHandle: HSTREAM;
BitmapImage: TBitmapImage;
BackgroundImage: TBitmapImage;
begin
ExtractTemporaryFile('Install.mp3');
if BASS_Init(-1, 44100, 0, 0, 0) then
begin
StreamHandle := BASS_StreamCreateFile(False,
ExpandConstant('{tmp}\Install.mp3'), 0, 0, 0, 0,
EncodingFlag or BASS_SAMPLE_LOOP);
BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, 2500);
BASS_ChannelPlay(StreamHandle, False);
end;
ExtractTemporaryFile('up.bmp');
BitmapImage := TBitmapImage.Create(WizardForm);
BitmapImage.Parent := WizardForm.MainPanel;
BitmapImage.Width := WizardForm.MainPanel.Width;
BitmapImage.Height := WizardForm.MainPanel.Height;
BitmapImage.Stretch := True;
BitmapImage.AutoSize := False;
BitmapImage.Bitmap.LoadFromFile(ExpandConstant('{tmp}\up.bmp'));
BackgroundImage := TBitmapImage.Create(MainForm);
BackgroundImage.Parent := MainForm;
BackgroundImage.SetBounds(0, 0, MainForm.ClientWidth, MainForm.ClientHeight);
BackgroundImage.Stretch := True;
ExtractTemporaryFile('setup.bmp');
BackgroundImage.Bitmap.LoadFromFile(ExpandConstant('{tmp}\setup.bmp'));
WizardForm.WizardSmallBitmapImage.Visible := False;
WizardForm.PageDescriptionLabel.Visible := False;
WizardForm.PageNameLabel.Visible := False;
end;
procedure DeinitializeSetup;
begin
BASS_Free;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectComponents then
begin
// Marcar el componente de idioma correspondiente al idioma del instalador
if ActiveLanguage = 'spanish' then
WizardForm.ComponentsList.Checked[2] := True
else
WizardForm.ComponentsList.Checked[1] := True;
end;
end;
La estructura general es similar a la de cualquier otro programa:
- Definición de constantes y variables: Primero, se definen las constantes y las variables que vamos a utilizar a lo largo del programa.
- Declaración de funciones externas: Luego, se declaran las funciones externas que harán uso de dichas constantes y variables. Estas funciones suelen provenir de bibliotecas externas.
- Establecimiento de procedimientos: Por último, se establecen los procedimientos, que son como la receta que hará uso de todo lo anterior para realizar tareas específicas.
- Definición de constantes y variables: Primero, se reúnen y se miden los ingredientes que vamos a utilizar para hacer el postre (harina, azúcar, huevos, etc.).
- Declaración de funciones externas: Luego, se preparan los utensilios y herramientas que vamos a necesitar (batidora, horno, moldes, etc.).
- Establecimiento de procedimientos: Por último, se siguen los pasos de la receta, que son las instrucciones para mezclar los ingredientes, hornear, decorar, etc., utilizando los ingredientes y utensilios preparados anteriormente.
Lo que pasa que, en este caso, tenemos varias "recetas" mezcladas entre sí, que el instalador "cocinará" al mismo tiempo:
1.- Reproductor de música para el proceso de instalación:
- Constantes y variables:
- BASS_SAMPLE_LOOP: Indica que el audio debe reproducirse en bucle.
- BASS_UNICODE: Define el uso de codificación Unicode.
- BASS_CONFIG_GVOL_STREAM: Configura el volumen global del stream.
- EncodingFlag: Utilizará BASS_UNICODE si el script usa codificación Unicode.
- HSTREAM: Define HSTREAM como un tipo de dato DWORD, que se utilizará para manejar streams de audio.
- Funciones externas:
- BASS_Init: Inicializa un dispositivo de salida de audio. Debe ser llamada con éxito antes de usar cualquier función de muestra, stream o música MOD. Esta función configura el dispositivo de audio con los parámetros especificados, como la frecuencia de muestreo y las banderas de configuración.
- BASS_StreamCreateFile: Crea un stream de muestra desde un archivo de audio (como MP3, MP2, MP1, OGG, WAV, AIFF, etc.). Esta función permite reproducir el archivo de audio especificado desde una ubicación determinada y con las banderas de configuración especificadas.
- BASS_ChannelPlay: Inicia (o reanuda) la reproducción de una muestra, stream, música MOD o grabación. Esta función toma el handle del canal y un parámetro para reiniciar la reproducción desde el principio si es necesario.
- BASS_SetConfig: Establece el valor de una opción de configuración. Esta función permite ajustar diversas configuraciones del sistema de audio, como el tamaño del buffer de reproducción o el período de actualización
- BASS_Free: Libera todos los recursos utilizados por el dispositivo de salida, incluyendo todas sus muestras, streams y músicas MOD. Esta función debe ser llamada para todos los dispositivos inicializados antes de que el programa salga.
- Procedimientos:
- InitializeWizard (parte relacionada con la música):
- Extraer el archivo suministrado "Install.mp3".
- Inicializar el reproductor BASS.
- Crear stream de audio.
- Configurar volumen.
- Reproducir audio.
- DeinitializeSetup:
- Liberar los recursos utilizados por el reproductor BASS.
const
BASS_SAMPLE_LOOP = 4;
BASS_UNICODE = $80000000;
BASS_CONFIG_GVOL_STREAM = 5;
const
#ifndef UNICODE
EncodingFlag = 0;
#else
EncodingFlag = BASS_UNICODE;
#endif
type
HSTREAM = DWORD;
function BASS_Init(device: LongInt; freq, flags: DWORD;
win: HWND; clsid: Cardinal): BOOL;
external 'BASS_Init@files:bass.dll stdcall';
function BASS_StreamCreateFile(mem: BOOL; f: string; offset1: DWORD;
offset2: DWORD; length1: DWORD; length2: DWORD; flags: DWORD): HSTREAM;
external 'BASS_StreamCreateFile@files:bass.dll stdcall';
function BASS_ChannelPlay(handle: DWORD; restart: BOOL): BOOL;
external 'BASS_ChannelPlay@files:bass.dll stdcall';
function BASS_SetConfig(option: DWORD; value: DWORD ): BOOL;
external 'BASS_SetConfig@files:bass.dll stdcall';
function BASS_Free: BOOL;
external 'BASS_Free@files:bass.dll stdcall';
procedure InitializeWizard();
var
StreamHandle: HSTREAM;
begin
ExtractTemporaryFile('Install.mp3');
if BASS_Init(-1, 44100, 0, 0, 0) then
begin
StreamHandle := BASS_StreamCreateFile(False,
ExpandConstant('{tmp}\Install.mp3'), 0, 0, 0, 0,
EncodingFlag or BASS_SAMPLE_LOOP);
BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, 2500);
BASS_ChannelPlay(StreamHandle, False);
end;
end;
procedure DeinitializeSetup;
begin
BASS_Free;
end;
2.- Imágenes de fondo del instalador:
- Constantes y variables:
- BitmapImage: Crea y configura una imagen en el panel principal del asistente.
- BackgroundImage: Crea y configura una imagen de fondo en el formulario principal.
- Procedimientos:
- InitializeWizard (parte relacionada con las imágenes).
- Extraer "up.bmp".
- Crear y configurar "BitmapImage".
- Extraer "setup.bmp"
- Crear y configurar "BackgroundImage".
- Ocultar elementos del asistente.
Este sería el código por separado:
procedure InitializeWizard();
var
BitmapImage: TBitmapImage;
BackgroundImage: TBitmapImage;
begin
ExtractTemporaryFile('up.bmp');
BitmapImage := TBitmapImage.Create(WizardForm);
BitmapImage.Parent := WizardForm.MainPanel;
BitmapImage.Width := WizardForm.MainPanel.Width;
BitmapImage.Height := WizardForm.MainPanel.Height;
BitmapImage.Stretch := True;
BitmapImage.AutoSize := False;
BitmapImage.Bitmap.LoadFromFile(ExpandConstant('{tmp}\up.bmp'));
BackgroundImage := TBitmapImage.Create(MainForm);
BackgroundImage.Parent := MainForm;
BackgroundImage.SetBounds(0, 0, MainForm.ClientWidth, MainForm.ClientHeight);
BackgroundImage.Stretch := True;
ExtractTemporaryFile('setup.bmp');
BackgroundImage.Bitmap.LoadFromFile(ExpandConstant('{tmp}\setup.bmp'));
WizardForm.WizardSmallBitmapImage.Visible := False;
WizardForm.PageDescriptionLabel.Visible := False;
WizardForm.PageNameLabel.Visible := False;
end;
3.- Selección de componentes de idioma:
- Procedimientos:
- CurPageChanged: Marcar automáticamente el componente de idioma correspondiente al idioma del instalador.
Este sería el código por separado:
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectComponents then
begin
// Marcar el componente de idioma correspondiente al idioma del instalador
if ActiveLanguage = 'spanish' then
WizardForm.ComponentsList.Checked[2] := True
else
WizardForm.ComponentsList.Checked[1] := True;
end;
end;
Es posible conseguir una versión algo más limpia y lógica de la sección [Code] de nuestro script. Es totalmente equivalente en cuanto a funcionalidad y grado de optimización, aunque en mi equipo noto que la pantalla de fondo hace un parpadeo antes de cargar la imagen definitiva. No sé por qué ocurre. Debe de tener relación con el orden de presentación de los elementos. Aun así, lo pongo por aquí para tenerlo como referencia:
[Code]
const
BASS_SAMPLE_LOOP = 4;
BASS_UNICODE = $80000000;
BASS_CONFIG_GVOL_STREAM = 5;
const
#ifndef UNICODE
EncodingFlag = 0;
#else
EncodingFlag = BASS_UNICODE;
#endif
type
HSTREAM = DWORD;
function BASS_Init(device: LongInt; freq, flags: DWORD;
win: HWND; clsid: Cardinal): BOOL;
external 'BASS_Init@files:bass.dll stdcall';
function BASS_StreamCreateFile(mem: BOOL; f: string; offset1: DWORD;
offset2: DWORD; length1: DWORD; length2: DWORD; flags: DWORD): HSTREAM;
external 'BASS_StreamCreateFile@files:bass.dll stdcall';
function BASS_ChannelPlay(handle: DWORD; restart: BOOL): BOOL;
external 'BASS_ChannelPlay@files:bass.dll stdcall';
function BASS_SetConfig(option: DWORD; value: DWORD ): BOOL;
external 'BASS_SetConfig@files:bass.dll stdcall';
function BASS_Free: BOOL;
external 'BASS_Free@files:bass.dll stdcall';
procedure InitializeWizard();
var
StreamHandle: HSTREAM;
BitmapImage: TBitmapImage;
BackgroundImage: TBitmapImage;
begin
// Reproductor de Música
ExtractTemporaryFile('Install.mp3');
if BASS_Init(-1, 44100, 0, 0, 0) then
begin
StreamHandle := BASS_StreamCreateFile(False,
ExpandConstant('{tmp}\Install.mp3'), 0, 0, 0, 0,
EncodingFlag or BASS_SAMPLE_LOOP);
BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, 2500);
BASS_ChannelPlay(StreamHandle, False);
end;
// Imágenes de Fondo del Instalador
ExtractTemporaryFile('up.bmp');
BitmapImage := TBitmapImage.Create(WizardForm);
BitmapImage.Parent := WizardForm.MainPanel;
BitmapImage.Width := WizardForm.MainPanel.Width;
BitmapImage.Height := WizardForm.MainPanel.Height;
BitmapImage.Stretch := True;
BitmapImage.AutoSize := False;
BitmapImage.Bitmap.LoadFromFile(ExpandConstant('{tmp}\up.bmp'));
BackgroundImage := TBitmapImage.Create(MainForm);
BackgroundImage.Parent := MainForm;
BackgroundImage.SetBounds(0, 0, MainForm.ClientWidth, MainForm.ClientHeight);
BackgroundImage.Stretch := True;
ExtractTemporaryFile('setup.bmp');
BackgroundImage.Bitmap.LoadFromFile(ExpandConstant('{tmp}\setup.bmp'));
WizardForm.WizardSmallBitmapImage.Visible := False;
WizardForm.PageDescriptionLabel.Visible := False;
WizardForm.PageNameLabel.Visible := False;
end;
procedure DeinitializeSetup;
begin
BASS_Free;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectComponents then
begin
// Marcar el componente de idioma correspondiente al idioma del instalador
if ActiveLanguage = 'spanish' then
WizardForm.ComponentsList.Checked[2] := True
else
WizardForm.ComponentsList.Checked[1] := True;
end;
end;
Por último, nuestro script tiene una sección [Run]. La sección [Run] de un script de Inno Setup suele ir al final del todo y se utiliza para especificar programas que deben ejecutarse después de que el programa principal se haya instalado correctamente, pero antes de que el asistente de instalación muestre el cuadro de diálogo final. En general, consta de las siguientes partes:
- Ejecución de programas: Permite ejecutar cualquier número de programas después de la instalación.
- Orden de ejecución: Los programas se ejecutan en el orden en que aparecen en el script.
- Espera de finalización: Por defecto, el asistente de instalación espera a que cada programa termine antes de proceder al siguiente, a menos que se utilicen las banderas nowait, shellexec o waituntilidle.
- Reinicio del sistema: Si un programa ejecutado en la sección [Run] requiere un reinicio del sistema, el asistente de instalación puede detectar esto y solicitar al usuario que reinicie el equipo al final de la instalación. Esto se puede controlar con la directiva RestartIfNeededByRun.
En el caso de nuestro script de Sega Rally 2, tenemos la siguiente sección [Run], la cual explicaremos en un momento (el código comentado siempre ayuda a entender su funcionamiento):
[Run]
; Ejecuta el script de PowerShell para comprobar y habilitar DirectPlay
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File {tmp}\EnableDirectPlay.ps1"; Description: "{cm:CheckingDirectPlay}"; Flags: runhidden
; Abre el archivo Readme.txt si el usuario ha seleccionado la opción openReadme
Filename: "{app}\Readme.txt"; Description: "{cm:OpenReadmeCheckBox}"; Flags: postinstall shellexec skipifsilent;
Centrándonos en el script de PowerShell para comprobar y habilitar DirectPlay, antes hemos visto la parte relacionada en [Files] y en [CustomMessages]:
[Files]
Source: "POWERSHELL SCRIPTS\EnableDirectPlay.ps1"; DestDir: "{tmp}"; Flags: deleteafterinstall
[CustomMessages]
english.CheckingDirectPlay=Checking and enabling DirectPlay
spanish.CheckingDirectPlay=Comprobando y habilitando DirectPlay
Lo que hará nuestro instalador, una vez finalizada la instalación de Sega Rally 2, será ejecutar el script de PowerShell para comprobar si DirectPlay está habilitado, y habilitarlo en caso de no estarlo. Como se puede ver en las sección [Run], ejecutará "powershell.exe" con el parámetro "-ExecutionPolicy Bypass" para que PowerShell ejecute lo que le demos sin preguntar. Y lo que le daremos para ejecutar será el script "EnableDirectPlay.ps1" que nuestro instalador habrá extraído a un directorio temporal, según lo que le hemos indicado en la sección [Files], donde hemos especificado, a su vez, que sea eliminado tras la instalación, gracias al flag "deleteafterinstall".
Éste es el contenido del script de PowerShell, que no es más que un archivo .txt con la extensión cambiada a .ps1:
# Nombre del archivo: EnableDirectPlay.ps1
# Comprobar si DirectPlay está habilitado
$feature = Get-WindowsOptionalFeature -Online -FeatureName DirectPlay
if ($feature.State -ne 'Enabled') {
# Habilitar DirectPlay
Enable-WindowsOptionalFeature -Online -FeatureName DirectPlay -All
Write-Output "DirectPlay has been enabled."
} else {
Write-Output "DirectPlay was already enabled."
}
En realidad todo esto del script para habilitar DirectPlay no sería necesario, ya que Windows, por defecto, detectará que se trata de un juego que utiliza DirectPlay y, sino está habilitado, lanzará una ventana pop-up tras la instalación instándonos a activarlo. Con este script nos ahorramos esa ventana y hacer clic en "Instalar esta característica":
Respecto a la parte que nos queda por comentar de la sección [Run]:
[Run]
; Abre el archivo Readme.txt si el usuario ha seleccionado la opción openReadme
Filename: "{app}\Readme.txt"; Description: "{cm:OpenReadmeCheckBox}"; Flags: postinstall shellexec skipifsilent;
Busca el archivo Readme.txt en el directorio de instalación del juego y muestra el mensaje custom que hayamos prefijado en la sección [CustomMessages] según del idioma que hayamos escogido para el proceso de instalación en la primera pantalla del instalador:
[CustomMessages]
english.OpenReadmeCheckBox=Open Readme file
spanish.OpenReadmeCheckBox=Abrir archivo Readme
Observa la sintaxis de estas líneas de CustomMessages. Inno Setup tiene una sintaxis predefinida para abrir los archivos Readme, "OpenReadme...", pero lo interesante viene justo después: "...CheckBox". Con esto estamos indicando al instalador que muestre la opción de abrir el archivo Readme en una checkbox. Pero, ¿por qué muestra dicha opción en la última pantalla del instalador y no en cualquier otra? Pues porque en el script hemos especificado que así sea, concretamente en la sección [Run]: si te fijas en los flags que hemos asignado a la operación, verás que uno de ellos dice "postinstall". Ése es el responsable. De hecho, ves que hay tres flags:
- postinstall: indica que aquello a lo que acompaña deberá mostrarse en la página final del asistente de instalación.
- shellexec: se utiliza cuando el archivo especificado no es un archivo ejecutable directo (.exe o .com), permitiendo abrirlo con la aplicación predeterminada del sistema para este tipo de archivo, siempre y cuando el tipo de archivo esté registrado en el sistema. Si tenemos instalada una aplicación para ello, seguramente lo estará).
- skipifsilent: indica que la acción debe omitirse si la instalación se está ejecutando en modo silencioso. Es decir, evita abrir el archivo Readme.txt si la instalación se realiza sin interacción del usuario.
Como no hemos indicado lo contrario, el checkbox de abrir el archivo Readme aparecerá marcado por defecto:
Si queremos que aparezca desmarcado por defecto, deberíamos añadir el flag "unchecked" en la parte correspondiente de la sección [Run]:
[Run]
; Abre el archivo Readme.txt si el usuario ha seleccionado la opción openReadme
Filename: "{app}\Readme.txt"; Description: "{cm:OpenReadmeCheckBox}"; Flags: postinstall shellexec skipifsilent unchecked;
Con esto quedaría completo nuestro script de Inno Setup. Ahora sólo faltaría compilarlo para generar el paquete instalador.
╔════════════════════════════╗
5.- y 6.- COMPILACIÓN DEL SCRIPT,
CONSTRUCCIÓN DEL ARCHIVO
INSTALADOR Y PRUEBAS FINALES
╚════════════════════════════╝
Tenemos tres caminos para llegar al mismo sitio:
- Mediante el atajo de teclado Ctrl + F9.
- Desde el menú superior Project - Compile.
- Desde el botón "Compile" (dibujo de una rueda dentada con un botón de play verde sobre ella)
Y ya estaría. El resultado será un instalador completamente independiente y funcional, que nos crearía una instalación del juego totalmente funcional. No se trata de un portable como el que lleva años dando vueltas por internet, sino de un instalador que nos instala el juego ya parcheado y con todo configurado, listo para funcionar. Que lo disfrutes.
╔════════════════════════════╗
APÉNDICES.- ELEMENTOS DESCARTADOS
╚════════════════════════════╝
Hay una cosa más que estaba originalmente en el script pero que tuve que quitar porque el antivirus informaba de un falso positivo: un sistema automático para añadir los ejecutables del juego a la lista de exclusiones (whitelist) del sistema de Prevención de Ejecución de Datos (Data Execution Prevention, abreviado "DEP") de Windows. Esta tecnología está integrada en Windows y ayuda a proteger contra la ejecución de código malintencionado desde áreas de memoria designadas solo para datos. Es normal, entonces, que a Windows Defender no le haga gracia que una aplicación externa esté incluyendo dos ejecutables desconocidos (SEGA RALLY.EXE y LAUNCH.EXE) en la lista de permitidos:
Este juego no lo necesita, pero hay juegos antiguos que requieren que su ejecutable sea añadido a dicha lista de excepciones. Para acceder a esa ventana pulsaremos Win +R y escribiremos "sysdm.cpl". Se nos abrirá la ventana de propiedades del sistema. Haremos clic en la pestaña "Avanzado". En ella, en la sección "Rendimiento" haremos clic en "Configuración" (o puede que en español se llame "Ajustes"):
Se nos abrirá, entonces, la ventana de Opciones de Rendimiento. En la tercera pestaña, "Prevención de Ejecución de Datos", añadiremos manualmente el ejecutable del juego, tal y como muestra la penúltima captura que hemos puesto.
Pues bien, todo esto se puede hacer de forma totalmente automática, con el riesgo que ya hemos comentado: que el antivirus nos bloquee el archivo instalador o que, directamente, lo ponga en cuarentena o elimine. Aun así, merece la pena dejar constancia por escrito de la forma de lograrlo.
En primer lugar, añadiremos lo siguiente a la sección [CustomMessages]:
[CustomMessages]
english.AddingtoDEP=Adding game's exe files to DEP whitelist...
spanish.AddingtoDEP=Añadiendo ejecutables a la whitelist del DEP...
Luego, en la sección [Files], añadiremos:
[Files]
Source: "POWERSHELL SCRIPTS\AddToDEPWhitelist.exe"; DestDir: "{tmp}"; Flags: ignoreversion
Source: "POWERSHELL SCRIPTS\run_as_admin.bat"; DestDir: "{tmp}"; Flags: ignoreversion
Por último, en la sección [Run], añadiremos:
[Run]
; Ejecuta un .bat que ejecuta como administrador un exe que contiene un script de PowerShell para añadir los dos ejecutables del juego a la whitelist del Data Execution Prevention de Windows.
Filename: "{tmp}\run_as_admin.bat"; Parameters: """{app}\SEGA RALLY 2.exe"" ""{app}\LAUNCH.EXE"""; Flags: runhidden waituntilterminated; StatusMsg: "{cm:AddingtoDEP}"
El script de PowerShell que añade los dos ejecutables a la whitelist del DEP es el siguiente:
param (
[string]$exePath1,
[string]$exePath2
)
# Add the executables to the DEP whitelist
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" -Name $exePath1 -Value "DisableNXShowUI"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" -Name $exePath2 -Value "DisableNXShowUI"
Write-Output "Los archivos ejecutables han sido añadidos a la whitelist del DEP."
Si necesitamos añadir menos ejecutables o más, bastará con quitar o añadir las líneas correspondientes.
Si recuerdas, antes hemos ejecutado un script de PowerShell para habilitar DirectPlay, lo cual ha funcionado sin ningún problema. Pero en el caso de este script, PowerShell se hace el remolón y no lo ejecuta directamente por las políticas de seguridad, y esta vez no sirve el parámetro "-ExecutionPolicy Bypass" para que se lo trague. Como no he conseguido que lo ejecute de ninguna de las maneras, he tenido que hacer una carambola para engañarlo y que lo ejecute sin rechistar:
- Convertir el script de PowerShell a un archivo .exe.
- Ejecutar dicho exe como administrador, para lo cual he tenido que crear un archivo .bat expresamente para eso.
Vamos a ir viendo todo paso a paso:
- Convertir el script de PowerShell (.ps1) a ejecutable (.exe): para ello utilizaremos la aplicación o, mejor dicho, módulo de PowerShell llamado "PS2EXE" (referencias aquí y aquí). Para ello sólo tendremos que ejecutar PowerShell como administrador y escribir lo siguiente:
Install-Module -Name ps2exe
Esperaremos a que termine de descargar el módulo y ya lo tendremos listo para usar. Ahora, abriremos una instancia de PowerShell donde tengamos nuestro script de PowerShell y escribiremos el siguiente comando:ps2exe .\Nuestro archivo.ps1 .\AddToDEPWhitelist.exe
Si abrimos PowerShell de manera general y no en la carpeta del script, tendremos que escribir la dirección completa donde se encuentra nuestro script y donde queremos guardar la versión ejecutable del mismo, en vez de la dirección relativa ".\". Esperamos a que termine el proceso y habremos convertido el script en un archivo ejecutable que lo único que hará será abrir PowerShell y hacer que ejecute el script que está ya contenido en el propio AddToDEPWhitelist.exe. - Crearemos un archivo .bat ("run_as_admin.bat") para ejecutar el .exe con privilegios de administrador. El archivo contendrá lo siguiente:
@echo off :: Check for administrative privileges net session >nul 2>&1 if %errorlevel% neq 0 ( echo Requesting administrative privileges... powershell.exe -Command "Start-Process '%~f0' -Verb RunAs" exit /b ) :: Run the executable with parameters "%~dp0AddToDEPWhitelist.exe" "%~1" "%~2"
Este archivo .bat realiza las siguientes acciones:2.1 Verifica si el script se está ejecutando con privilegios administrativos.Si no tiene privilegios administrativos, solicita al usuario que reinicie el script con privilegios administrativos.2.2 Una vez que tiene privilegios administrativos, ejecuta el archivo AddToDEPWhitelist.exe con dos parámetros, que los toma de la sección [Run]: son los dos ejecutables que queremos añadir a la whitelist del DEP.
Con esto conseguiremos nuestro propósito pero, como digo, más de un antivirus detectará nuestro instalador como un troyano, especialmente Windows Defender, pero nada más lejos de la realidad. Sin embargo, la gente no tiene por qué saber el motivo, y muchas veces no puede o no quiere entenderlo. Que se lo digan al creador de la versión portable, que acabó hasta los cojones de los insultos y del odio gratuito por parte de los ignorantes que afirmaban que su portable del Sega Rally 2 contenía un troyano, pues se lo había dicho su antivirus. Así que Alcino Major, el creador, terminó tan quemado que acabó mandando todo a la mierda al grito de...
Comentarios
Publicar un comentario