Интро
Это захватывающее время в сообществе энтузиастов Lotus 1-2-3 – шутка, нет никакого сообщества энтузиастов, есть только я! ????
Хотя это действительно захватывающее время – и это уже не шутка!
За последние несколько недель произошёл ряд важных событий, и я думаю, что это довольно необычно для заброшенного ПО 30-летней давности.
Я перейду к делу; благодаря сочетанию невероятных открытий, сумасшедших хаков и варез-сцены BBS 90-х годов мне удалось портировать Lotus 1-2-3 в Linux – операционную систему, которой буквально не существовало, когда была выпущена 1-2-31!
Если вы хотите узнать, как проприетарное приложение можно перенести на новые операционные системы, спустя 30 лет после выпуска, читайте дальше.
Бэкграунд
Мне очень нравится Lotus 1-2-3, я даже поддерживаю драйвер, чтобы убедиться, что он хорошо работает в современных системах. Мне пришлось реконструировать API драйвера для этого, но он работает прекрасно.
Заставить этот драйвер работать было настоящим приключением, но всё еще не хватает одной части головоломки: надстроек. 1-2-3 был разработан с возможностью расширения с помощью плагинов (или «надстроек») — теоретически вы могли бы добавить поддержку современных функций электронных таблиц или интегрироваться с Google Finance или что-то в этом роде!
Проблема в том, что надстройки нужно было писать на специальном языке LPL, а компилятор и SDK, к сожалению, были утеряны. В этом нет ничего удивительного, это был нишевый продукт, и разработчики Lotus не просто так раздавали SDK — они заряжали за него 395 долларов. Не могу себе представить, что нашлось бы много людей, у которых копия просто валялась где-то поблизости.
В те времена было доступно множество коммерческих плагинов, некоторые из них были действительно впечатляющими. Я отправлял электронные письма нескольким разработчикам, которые работали над некоторыми из них, чтобы узнать, есть ли у них старые резервные копии, ответ всегда был один и тот же — 1-2-3 было самым известным программным обеспечением, никто не думал, что оно куда-то денется, так зачем им хранить резервные копии?
О LPL также были написаны две сторонние книги, мне удалось разыскать их по старым библиотекам. Нравится эта обложка, это очень серьезное дело. Это так обидно, что я вижу скриншоты отладчика, выходные данные компилятора и примеры надстроек, но без SDK просто не могу ничего с этим поделать!
Прогресс
Пропустим год или два, и я нашел человека, который раньше был системным оператором на сцене BBS 90-х. Посетите его веб-сайт, у него до сих пор есть каталог NFO и telnet BBS, если вы хотите увидеть крутой ANSI арт!
Он хранил резервные копии старых систем BBS и смог восстановить варез-копию SDK – невероятно! Это действительно сработало, мне удалось создать несколько плагинов!
Вы можете скачать ADK прямо здесь, а вот пример программы LPL.
Этого уже было бы достаточно, чтобы развлечь меня на какое-то время, но на этом дело не заканчивается. Оказывается, у BBS также была варез-копия Lotus 1-2-3 для UNIX. Многие считали, что она утеряна: мне сказали, что он не может конкурировать с более популярным офисным пакетом для UNIX под названием SCO Professional, поэтому было продано не так много копий.
Мне это особо не пригодилось, но определенно было любопытно покопаться в установочном носителе и посмотреть, что внутри!
Lotus 1-2-3 для UNIX
Варезный релиз представлял собой набор файлов в формате TD0, этого формата я никогда раньше не видел – очевидно, это старый формат сжатого образа диска 80-х годов. Я нашел эту страницу, на которой рекомендуется использовать samdisk для преобразования в полный образ диска.
$ ls
123UNIX1.TD0 123UNIX2.TD0 123UNIX3.TD0 123UNIX4.TD0 123UNIX5.TD0 LEGAL.NFO WHATITIS
$ file *.TD0
123UNIX1.TD0: floppy image data (TeleDisk)
123UNIX2.TD0: floppy image data (TeleDisk)
123UNIX3.TD0: floppy image data (TeleDisk)
123UNIX4.TD0: floppy image data (TeleDisk)
123UNIX5.TD0: floppy image data (TeleDisk)
Это казалось многообещающим, samdisk
собирается и кажется работает! Я загрузил полные образы в Интернет-архив на случай, если кому-то еще будет любопытно взглянуть.
$ samdisk info 123UNIX1.TD0
[123UNIX1.TD0]
Type: TD0
Size: 80 Cyls, 2 Heads
created : 1991-06-22 20:24:04
Lotus 1-2-3 for UNIX System V
$ for i in *.TD0; do samdisk copy ${i} ${i/.TD0/.RAW}; done
Wrote 80 cyls, 2 heads, 18 sectors, 512 bytes/sector = 1474560 bytes
Wrote 80 cyls, 2 heads, 18 sectors, 512 bytes/sector = 1474560 bytes
Wrote 80 cyls, 2 heads, 18 sectors, 512 bytes/sector = 1474560 bytes
Wrote 80 cyls, 2 heads, 18 sectors, 512 bytes/sector = 1474560 bytes
Wrote 80 cyls, 2 heads, 18 sectors, 512 bytes/sector = 1474560 bytes
$ file *.RAW
123UNIX1.RAW: tar archive
123UNIX2.RAW: ASCII cpio archive (pre-SVR4 or odc)
123UNIX3.RAW: ASCII cpio archive (pre-SVR4 or odc)
123UNIX4.RAW: ASCII cpio archive (pre-SVR4 or odc)
123UNIX5.RAW: ASCII cpio archive (pre-SVR4 or odc)
Я знаю, что программное обеспечение в UNIX обычно распространялось в виде архивов, и предполагалось, что вы вставите дискету и запустите что-то вроде tar -C / -xvf /dev/fd0
, поэтому эти файлы выглядят правильно. Думаю, это разумно, зачем тратить драгоценные байты на файловую систему?
$ tar xf 123UNIX1.RAW
$ for i in 123UNIX{2..5}.RAW; do cpio -id < $i; done
1555 blocks
2606 blocks
2510 blocks
2481 blocks
Полный листинг директории
$ tree --prune -Dh
.
├── [ 512 May 20 13:53] lotus
│ ├── [ 512 May 20 13:53] 123.v10
│ │ ├── [ 512 May 20 13:53] cbd
│ │ │ ├── [2.4K May 20 13:53] dfbcp437.bun.z
│ │ │ ├── [2.4K May 20 13:53] dfbcp850.bun.z
│ │ │ ├── [3.0K May 20 13:53] l13cUSA3.cbd
│ │ │ ├── [3.0K May 20 13:53] l13cUSF3.cbd
│ │ │ ├── [3.0K May 20 13:53] l13cUSL3.cbd
│ │ │ ├── [1.6K May 20 13:53] l_ascii.bun.z
│ │ │ ├── [1.8K May 20 13:53] l_latin1.bun.z
│ │ │ └── [ 874 May 20 13:53] l_lrf.bun.z
│ │ ├── [ 512 May 20 13:53] fonts
│ │ │ ├── [4.6K Sep 20 1990] aa012laa.lrf.z
│ │ │ ├── [4.3K Sep 20 1990] aa012lfa.lrf.z
│ │ │ ├── [5.4K Sep 20 1990] aa012lha.lrf.z
│ │ │ ├── [5.9K Sep 20 1990] aa017laa.lrf.z
│ │ │ ├── [7.8K Sep 20 1990] aa017lia.lrf.z
│ │ │ ├── [7.8K Sep 20 1990] aa024lfa.lrf.z
│ │ │ ├── [ 11K Sep 20 1990] aa024lha.lrf.z
│ │ │ ├── [8.8K Sep 20 1990] aa025laa.lrf.z
│ │ │ ├── [ 11K Sep 20 1990] aa030laa.lrf.z
│ │ │ ├── [6.6K Sep 20 1990] aa030lga.lrf.z
│ │ │ ├── [ 12K Sep 20 1990] aa033laa.lrf.z
│ │ │ ├── [ 18K Sep 20 1990] aa033lia.lrf.z
│ │ │ ├── [ 22K Sep 20 1990] aa050laa.lrf.z
│ │ │ ├── [ 29K Sep 20 1990] aa060laa.lrf.z
│ │ │ ├── [ 15K Sep 20 1990] aa060lga.lrf.z
│ │ │ ├── [ 53K Sep 20 1990] aa100laa.lrf.z
│ │ │ ├── [5.4K Sep 20 1990] ac016lfa.lrf.z
│ │ │ ├── [7.1K Sep 20 1990] ac016lha.lrf.z
│ │ │ ├── [6.1K Sep 20 1990] ac017laa.lrf.z
│ │ │ ├── [8.1K Sep 20 1990] ac022laa.lrf.z
│ │ │ ├── [ 11K Sep 20 1990] ac022lia.lrf.z
│ │ │ ├── [ 13K Sep 20 1990] ac033laa.lrf.z
│ │ │ ├── [ 16K Sep 20 1990] ac040laa.lrf.z
│ │ │ ├── [9.4K Sep 20 1990] ac040lga.lrf.z
│ │ │ ├── [ 34K Sep 20 1990] ac066laa.lrf.z
│ │ │ ├── [4.7K Sep 20 1990] ai012laa.lrf.z
│ │ │ ├── [4.3K Sep 20 1990] ai012lfa.lrf.z
│ │ │ ├── [5.5K Sep 20 1990] ai012lha.lrf.z
│ │ │ ├── [6.1K Sep 20 1990] ai017laa.lrf.z
│ │ │ ├── [8.0K Sep 20 1990] ai017lia.lrf.z
│ │ │ ├── [7.9K Sep 20 1990] ai024lfa.lrf.z
│ │ │ ├── [ 11K Sep 20 1990] ai024lha.lrf.z
│ │ │ ├── [9.0K Sep 20 1990] ai025laa.lrf.z
│ │ │ ├── [ 11K Sep 20 1990] ai030laa.lrf.z
│ │ │ ├── [6.5K Sep 20 1990] ai030lga.lrf.z
│ │ │ ├── [ 12K Sep 20 1990] ai033laa.lrf.z
│ │ │ ├── [ 19K Sep 20 1990] ai033lia.lrf.z
│ │ │ ├── [ 23K Sep 20 1990] ai050laa.lrf.z
│ │ │ ├── [ 29K Sep 20 1990] ai060laa.lrf.z
│ │ │ ├── [ 15K Sep 20 1990] ai060lga.lrf.z
│ │ │ ├── [ 55K Sep 20 1990] ai100laa.lrf.z
│ │ │ ├── [5.5K Sep 20 1990] aj016lfa.lrf.z
│ │ │ ├── [7.1K Sep 20 1990] aj016lha.lrf.z
│ │ │ ├── [6.2K Sep 20 1990] aj017laa.lrf.z
│ │ │ ├── [8.1K Sep 20 1990] aj022laa.lrf.z
│ │ │ ├── [ 11K Sep 20 1990] aj022lia.lrf.z
│ │ │ ├── [ 13K Sep 20 1990] aj033laa.lrf.z
│ │ │ ├── [ 17K Sep 20 1990] aj040laa.lrf.z
│ │ │ ├── [9.4K Sep 20 1990] aj040lga.lrf.z
│ │ │ ├── [ 35K Sep 20 1990] aj066laa.lrf.z
│ │ │ ├── [3.6K Sep 20 1990] bc008lba.lrf.z
│ │ │ ├── [4.0K Sep 20 1990] bc010lba.lrf.z
│ │ │ ├── [5.3K Sep 20 1990] bc012lea.lrf.z
│ │ │ ├── [5.5K Sep 20 1990] bc014lda.lrf.z
│ │ │ ├── [5.8K Sep 20 1990] bc016lba.lrf.z
│ │ │ ├── [5.8K Sep 20 1990] bc016lca.lrf.z
│ │ │ ├── [5.7K Sep 20 1990] bc016lda.lrf.z
│ │ │ ├── [6.1K Sep 20 1990] bc018lba.lrf.z
│ │ │ ├── [6.8K Sep 20 1990] bc021laa.lrf.z
│ │ │ ├── [ 12K Sep 20 1990] bc031lca.lrf.z
│ │ │ ├── [ 12K Sep 20 1990] bc031lda.lrf.z
│ │ │ ├── [ 14K Sep 20 1990] bc042laa.lrf.z
│ │ │ ├── [1.7K Sep 20 1990] bo008lda.lrf.z
│ │ │ ├── [2.6K Sep 20 1990] bo014lca.lrf.z
│ │ │ ├── [2.6K Sep 20 1990] bo014lda.lrf.z
│ │ │ ├── [3.1K Sep 20 1990] bo016lba.lrf.z
│ │ │ └── [3.5K Sep 20 1990] bo019laa.lrf.z
│ │ ├── [ 512 May 20 13:53] hlp
│ │ │ └── [ 512 May 20 13:53] USA-English
│ │ │ ├── [407K May 20 13:53] 123_sysV.hlp.z
│ │ │ ├── [7.3K May 20 13:53] keyedit.hlp.z
│ │ │ └── [8.2K May 20 13:53] setup123.hlp.z
│ │ ├── [ 512 May 20 13:53] keymaps
│ │ │ ├── [ 512 May 20 13:53] a
│ │ │ │ └── [3.7K May 20 13:53] AT386.z
│ │ │ ├── [ 512 May 20 13:53] p
│ │ │ │ └── [2.7K May 20 13:53] PC-NFS.z
│ │ │ ├── [ 512 May 20 13:53] s
│ │ │ │ ├── [2.6K May 20 13:53] sco386-101.z
│ │ │ │ ├── [3.7K May 20 13:53] sun.z
│ │ │ │ ├── [3.7K May 20 13:53] sysV386-101.z
│ │ │ │ └── [5.7K May 20 13:53] sysV386-xterm
│ │ │ ├── [ 512 May 20 13:53] v
│ │ │ │ ├── [4.7K May 20 13:53] vt100.z
│ │ │ │ └── [ 10K May 20 13:53] vtxxx.z
│ │ │ └── [ 512 May 20 13:53] w
│ │ │ ├── [3.2K May 20 13:53] wyse-pce.z
│ │ │ ├── [ 14K May 20 13:53] wyse50-lts123
│ │ │ └── [5.9K May 20 13:53] wyse50.z
│ │ ├── [ 512 May 20 13:53] pbd
│ │ │ ├── [ 604 Sep 20 1990] egas25ca.vbd.z
│ │ │ ├── [ 604 Sep 20 1990] egas25ci.vbd.z
│ │ │ ├── [ 605 Sep 20 1990] egas25cs.vbd.z
│ │ │ ├── [ 602 Sep 20 1990] egas25la.vbd.z
│ │ │ ├── [ 602 Sep 20 1990] egas25li.vbd.z
│ │ │ ├── [ 604 Sep 20 1990] egas25ls.vbd.z
│ │ │ ├── [ 574 Sep 20 1990] egas25ma.vbd.z
│ │ │ ├── [ 574 Sep 20 1990] egas25mi.vbd.z
│ │ │ ├── [ 576 Sep 20 1990] egas25ms.vbd.z
│ │ │ ├── [ 592 Sep 20 1990] egas25pa.vbd.z
│ │ │ ├── [ 592 Sep 20 1990] egas25pi.vbd.z
│ │ │ ├── [ 593 Sep 20 1990] egas25ps.vbd.z
│ │ │ ├── [ 604 Sep 20 1990] egas43ca.vbd.z
│ │ │ ├── [ 604 Sep 20 1990] egas43ci.vbd.z
│ │ │ ├── [ 605 Sep 20 1990] egas43cs.vbd.z
│ │ │ ├── [ 574 Sep 20 1990] egas43ma.vbd.z
│ │ │ ├── [ 574 Sep 20 1990] egas43mi.vbd.z
│ │ │ ├── [ 575 Sep 20 1990] egas43ms.vbd.z
│ │ │ ├── [ 588 Sep 20 1990] egas43pa.vbd.z
│ │ │ ├── [ 588 Sep 20 1990] egas43pi.vbd.z
│ │ │ ├── [ 589 Sep 20 1990] egas43ps.vbd.z
│ │ │ ├── [2.4K Sep 20 1990] epdtfx.pbd.z
│ │ │ ├── [2.4K Sep 20 1990] epdtmx.pbd.z
│ │ │ ├── [3.7K Sep 20 1990] eplql15p.pbd.z
│ │ │ ├── [3.3K Sep 20 1990] eplql25m.pbd.z
│ │ │ ├── [4.2K Sep 20 1990] eplql25p.pbd.z
│ │ │ ├── [3.4K Sep 20 1990] eplql2mc.pbd.z
│ │ │ ├── [4.3K Sep 20 1990] eplql2pc.pbd.z
│ │ │ ├── [2.9K Sep 20 1990] eplql80m.pbd.z
│ │ │ ├── [3.6K Sep 20 1990] eplql80p.pbd.z
│ │ │ ├── [1020 Sep 20 1990] gc_bs.pbd
│ │ │ ├── [ 754 Sep 20 1990] gc_nobs.pbd
│ │ │ ├── [3.9K Sep 20 1990] hplj1flo.pbd
│ │ │ ├── [2.6K Sep 20 1990] hplj1jlo.pbd.z
│ │ │ ├── [2.6K Sep 20 1990] hplj1nol.pbd
│ │ │ ├── [5.2K Sep 20 1990] hplj1zlo.pbd.z
│ │ │ ├── [4.3K Sep 20 1990] hplj2fhi.pbd.z
│ │ │ ├── [3.4K Sep 20 1990] hplj2flo.pbd.z
│ │ │ ├── [4.5K Sep 20 1990] hplj2jhi.pbd.z
│ │ │ ├── [3.6K Sep 20 1990] hplj2jlo.pbd.z
│ │ │ ├── [3.9K Sep 20 1990] hplj2noh.pbd.z
│ │ │ ├── [3.0K Sep 20 1990] hpljnolo.pbd.z
│ │ │ ├── [4.3K Sep 20 1990] hpljpfhi.pbd.z
│ │ │ ├── [3.4K Sep 20 1990] hpljpflo.pbd.z
│ │ │ ├── [3.7K Sep 20 1990] hpljpjhi.pbd.z
│ │ │ ├── [2.9K Sep 20 1990] hpljpjlo.pbd.z
│ │ │ ├── [3.1K Sep 20 1990] hpljpnoh.pbd.z
│ │ │ ├── [2.3K Sep 20 1990] hpljpnol.pbd.z
│ │ │ ├── [6.3K Sep 20 1990] hpljzhi.pbd.z
│ │ │ ├── [5.4K Sep 20 1990] hpljzlo.pbd.z
│ │ │ ├── [2.9K Sep 20 1990] hppjpapr.pbd.z
│ │ │ ├── [2.9K Sep 20 1990] hppjtran.pbd.z
│ │ │ ├── [ 976 Sep 20 1990] hrcf25ma.vbd.z
│ │ │ ├── [ 976 Sep 20 1990] hrcf25mi.vbd.z
│ │ │ ├── [ 977 Sep 20 1990] hrcf25ms.vbd.z
│ │ │ ├── [1006 Sep 20 1990] hrcs43ma.vbd
│ │ │ ├── [1006 Sep 20 1990] hrcs43mi.vbd
│ │ │ ├── [1006 Sep 20 1990] hrcs43ms.vbd
│ │ │ ├── [7.6K Sep 20 1990] ps00alw0.pbd.z
│ │ │ ├── [ 12K Sep 20 1990] ps00alwp.pbd.z
│ │ │ ├── [ 604 Sep 20 1990] vgas25ca.vbd.z
│ │ │ ├── [ 604 Sep 20 1990] vgas25ci.vbd.z
│ │ │ ├── [ 606 Sep 20 1990] vgas25cs.vbd.z
│ │ │ ├── [ 574 Sep 20 1990] vgas25ma.vbd.z
│ │ │ ├── [ 573 Sep 20 1990] vgas25mi.vbd.z
│ │ │ ├── [ 576 Sep 20 1990] vgas25ms.vbd.z
│ │ │ ├── [ 601 Sep 20 1990] vgas34ca.vbd.z
│ │ │ ├── [ 601 Sep 20 1990] vgas34ci.vbd.z
│ │ │ ├── [ 602 Sep 20 1990] vgas34cs.vbd.z
│ │ │ ├── [ 575 Sep 20 1990] vgas34ma.vbd.z
│ │ │ ├── [ 575 Sep 20 1990] vgas34mi.vbd.z
│ │ │ ├── [ 575 Sep 20 1990] vgas43ms.vbd.z
│ │ │ ├── [ 602 Sep 20 1990] vgas60ca.vbd.z
│ │ │ ├── [ 602 Sep 20 1990] vgas60ci.vbd.z
│ │ │ ├── [ 603 Sep 20 1990] vgas60cs.vbd.z
│ │ │ ├── [ 575 Sep 20 1990] vgas60ma.vbd.z
│ │ │ └── [ 575 Sep 20 1990] vgas60mi.vbd.z
│ │ ├── [ 512 May 20 13:53] ri
│ │ │ └── [ 512 May 20 13:53] USA-English
│ │ │ ├── [1001 May 20 13:53] 123.ri.z
│ │ │ ├── [ 602 May 20 13:53] dr123txt.ri
│ │ │ ├── [1.9K May 20 13:53] inst_dl.ri
│ │ │ ├── [5.3K May 20 13:53] keyedit.ri.z
│ │ │ ├── [2.0K May 20 13:53] l123smp3.ri
│ │ │ ├── [ 49K May 20 13:53] l123txt3.ri.z
│ │ │ ├── [ 844 May 20 13:53] license.ri.z
│ │ │ ├── [6.9K May 20 13:53] printer.ri.z
│ │ │ ├── [2.6K May 20 13:53] prsetup.ri.z
│ │ │ └── [ 23K May 20 13:53] setup123.ri.z
│ │ ├── [ 512 May 20 13:53] smpfiles
│ │ │ ├── [1.3K May 20 13:53] ACCTG.WK3.z
│ │ │ ├── [1.5K May 20 13:53] CONSOL.WK3.z
│ │ │ ├── [1.7K May 20 13:53] DATA.WK3.z
│ │ │ ├── [3.2K May 20 13:53] DBT13S.WK3.z
│ │ │ ├── [3.2K May 20 13:53] DBT14S.WK3.z
│ │ │ ├── [1.3K May 20 13:53] INC10S.WK3.z
│ │ │ ├── [3.0K May 20 13:53] INC11S.WK3.z
│ │ │ ├── [4.1K May 20 13:53] INC12S.WK3.z
│ │ │ ├── [3.7K May 20 13:53] INC16S.WK3.z
│ │ │ ├── [ 619 May 20 13:53] INC2S.WK3
│ │ │ ├── [ 965 May 20 13:53] INC4S.WK3
│ │ │ ├── [ 712 May 20 13:53] INC5S.WK3.z
│ │ │ ├── [1.8K May 20 13:53] INC6S.WK3
│ │ │ ├── [1.2K May 20 13:53] INC7S.WK3.z
│ │ │ ├── [1.4K May 20 13:53] INC8S.WK3.z
│ │ │ ├── [1.7K May 20 13:53] INC9S.WK3.z
│ │ │ ├── [3.8K May 20 13:53] MAC17S.WK3.z
│ │ │ ├── [2.0K May 20 13:53] MFG.WK3
│ │ │ ├── [1.8K May 20 13:53] SALES.WK3
│ │ │ ├── [4.7K May 20 13:53] SAMPMACS.WK3.z
│ │ │ ├── [1.5K May 20 13:53] SHOES.WK3.z
│ │ │ ├── [2.6K May 20 13:53] SMPSCHED.WK3.z
│ │ │ ├── [1.3K May 20 13:53] SUM1988S.WK3.z
│ │ │ ├── [1.5K May 20 13:53] SUMMARY.WK3
│ │ │ ├── [2.2K May 20 13:53] TABLES.WK3.z
│ │ │ ├── [6.4K May 20 13:53] consale.wk1.z
│ │ │ ├── [6.3K May 20 13:53] consale.wk3.z
│ │ │ ├── [6.8K May 20 13:53] consaler.wk1.z
│ │ │ ├── [7.2K May 20 13:53] income.wk3.z
│ │ │ ├── [8.7K May 20 13:53] income2.wk3.z
│ │ │ ├── [8.7K May 20 13:53] income3.wk3.z
│ │ │ ├── [8.7K May 20 13:53] income4.wk3.z
│ │ │ ├── [7.2K May 20 13:53] incomer.wk3.z
│ │ │ ├── [ 512 May 20 13:53] intro
│ │ │ │ ├── [ 874 Sep 20 1990] chicago.wk3.z
│ │ │ │ ├── [1.8K Sep 20 1990] debugmac.wk3
│ │ │ │ ├── [ 886 Sep 20 1990] houston.wk3.z
│ │ │ │ ├── [ 799 Sep 20 1990] less1ex1.wk3
│ │ │ │ ├── [ 565 Sep 20 1990] less1ex2.wk3.z
│ │ │ │ ├── [ 581 Sep 20 1990] less2ex1.wk3.z
│ │ │ │ ├── [ 877 Sep 20 1990] less2ex2.wk3
│ │ │ │ ├── [4.1K Sep 20 1990] less3ex2.wk3.z
│ │ │ │ ├── [ 706 Sep 20 1990] less4ex1.wk3.z
│ │ │ │ ├── [ 834 Sep 20 1990] less4ex2.wk3.z
│ │ │ │ ├── [ 798 Sep 20 1990] less5ex1.wk3.z
│ │ │ │ ├── [ 811 Sep 20 1990] less5ex2.wk3.z
│ │ │ │ ├── [ 931 Sep 20 1990] less5ex3.wk3.z
│ │ │ │ ├── [ 690 Sep 20 1990] less6ex1.wk3.z
│ │ │ │ ├── [ 781 Sep 20 1990] less6ex2.wk3.z
│ │ │ │ ├── [ 839 Sep 20 1990] less7ex1.wk3.z
│ │ │ │ ├── [ 856 Sep 20 1990] less7ex2.wk3.z
│ │ │ │ ├── [ 896 Sep 20 1990] less8ex2.wk3.z
│ │ │ │ ├── [1.7K Sep 20 1990] less9ex1.wk3
│ │ │ │ ├── [1.8K Sep 20 1990] macros.wk3
│ │ │ │ ├── [1.9K Sep 20 1990] menuexam.wk3
│ │ │ │ ├── [ 774 Sep 20 1990] menumac.wk3
│ │ │ │ ├── [ 647 Sep 20 1990] mis.wk3.z
│ │ │ │ ├── [ 939 Sep 20 1990] mlibrary.wk3
│ │ │ │ ├── [ 890 Sep 20 1990] nyc.wk3.z
│ │ │ │ ├── [1.8K Sep 20 1990] parts.wk3.z
│ │ │ │ ├── [ 698 Sep 20 1990] planning.wk3.z
│ │ │ │ ├── [2.0K Sep 20 1990] q2graph.wk3
│ │ │ │ ├── [1.3K Sep 20 1990] q2graph2.wk3.z
│ │ │ │ ├── [ 690 Sep 20 1990] q2inc.wk3
│ │ │ │ ├── [ 763 Sep 20 1990] q2inc2.wk3.z
│ │ │ │ ├── [ 770 Sep 20 1990] q2mac.wk3.z
│ │ │ │ ├── [1.2K Sep 20 1990] q2macro.wk3.z
│ │ │ │ ├── [1.3K Sep 20 1990] q2price.wk3.z
│ │ │ │ ├── [ 674 Sep 20 1990] research.wk3.z
│ │ │ │ ├── [1.9K Sep 20 1990] salary.wk3
│ │ │ │ ├── [ 878 Sep 20 1990] sanfran.wk3.z
│ │ │ │ └── [2.0K Sep 20 1990] supplier.wk3.z
│ │ │ ├── [ 12K May 20 13:53] menu2.wk3.z
│ │ │ ├── [ 12K May 20 13:53] menu3.wk3.z
│ │ │ ├── [ 11K May 20 13:53] menu4.wk3.z
│ │ │ └── [ 512 May 20 13:53] nf
│ │ │ ├── [1.4K Sep 20 1990] acctg.wk3.z
│ │ │ ├── [5.3K Sep 20 1990] actual.wk3.z
│ │ │ ├── [ 871 Sep 20 1990] asset.wk3.z
│ │ │ ├── [1023 Sep 20 1990] bud91.wk3
│ │ │ ├── [5.0K Sep 20 1990] budget.wk3.z
│ │ │ ├── [1.5K Sep 20 1990] calcex.wk3.z
│ │ │ ├── [2.9K Sep 20 1990] consol.wk3.z
│ │ │ ├── [3.2K Sep 20 1990] data.wk3.z
│ │ │ ├── [4.9K Sep 20 1990] dataex.wk3.z
│ │ │ ├── [4.8K Sep 20 1990] datatab.wk3.z
│ │ │ ├── [ 948 Sep 20 1990] dattim.wk3.z
│ │ │ ├── [ 677 Sep 20 1990] dept_emp.wk3.z
│ │ │ ├── [ 690 Sep 20 1990] deptexp.wk3.z
│ │ │ ├── [ 852 Sep 20 1990] deptname.wk3
│ │ │ ├── [4.4K Sep 20 1990] dfillex.wk3.z
│ │ │ ├── [6.9K Sep 20 1990] graphex.wk3.z
│ │ │ ├── [ 588 Sep 20 1990] hotel90.wk3.z
│ │ │ ├── [2.0K Sep 20 1990] hotel_q1.wk1.z
│ │ │ ├── [2.0K Sep 20 1990] hotel_q2.wk1.z
│ │ │ ├── [2.0K Sep 20 1990] hotel_q3.wk1.z
│ │ │ ├── [2.0K Sep 20 1990] hotel_q4.wk1.z
│ │ │ ├── [2.4K Sep 20 1990] joinex.wk3.z
│ │ │ ├── [2.5K Sep 20 1990] macex.wk3.z
│ │ │ ├── [3.4K Sep 20 1990] macros.wk3.z
│ │ │ ├── [1.4K Sep 20 1990] mfg.wk3.z
│ │ │ ├── [1.8K Sep 20 1990] mis.wk1.z
│ │ │ ├── [1.8K Sep 20 1990] planning.wk1.z
│ │ │ ├── [8.4K Sep 20 1990] print.wk3.z
│ │ │ ├── [8.4K Sep 20 1990] print_bw.wk3.z
│ │ │ ├── [1.3K Sep 20 1990] printex.wk3.z
│ │ │ ├── [1.3K Sep 20 1990] q2price.wk3.z
│ │ │ ├── [1.8K Sep 20 1990] research.wk1.z
│ │ │ ├── [3.1K Sep 20 1990] rev-1.wk3.z
│ │ │ ├── [3.1K Sep 20 1990] rev-2.wk3.z
│ │ │ ├── [1.9K Sep 20 1990] salary.wk3
│ │ │ ├── [1.3K Sep 20 1990] sales.wk3.z
│ │ │ ├── [8.4K Sep 20 1990] shoes.wk3.z
│ │ │ ├── [8.4K Sep 20 1990] shoes_bw.wk3.z
│ │ │ ├── [ 842 Sep 20 1990] summary.wk3.z
│ │ │ ├── [3.3K Sep 20 1990] tables.wk3.z
│ │ │ ├── [3.5K Sep 20 1990] temp.wk3.z
│ │ │ ├── [ 985 Sep 20 1990] util.wk3.z
│ │ │ └── [4.2K Sep 20 1990] world.wk3.z
│ │ └── [ 512 May 20 13:53] sysV386
│ │ ├── [ 512 May 20 13:53] bin
│ │ │ ├── [ 924 May 20 13:53] 123.sh.z
│ │ │ ├── [1.1M May 20 13:53] 123_exe.z
│ │ │ ├── [8.6K May 20 13:53] inst_dl.z
│ │ │ ├── [ 96K May 20 13:53] keyedit.z
│ │ │ ├── [ 14K May 20 13:53] l13pbanr.z
│ │ │ ├── [1.4K May 20 13:53] l13pupif.z
│ │ │ ├── [9.5K May 20 13:53] prsetup123.z
│ │ │ └── [ 95K May 20 13:53] setup123_exe.z
│ │ ├── [ 512 May 20 13:53] dvr
│ │ │ └── [ 97K May 20 13:53] l13pepdt.dvr.z
│ │ ├── [ 512 May 20 13:53] lib
│ │ │ ├── [1.1M May 20 13:53] 123.o.z_1
│ │ │ ├── [717K May 20 13:53] 123.o.z_2
│ │ │ ├── [ 913 May 20 13:53] dl_init.o.z
│ │ │ ├── [ 76K May 20 13:53] ld.z
│ │ │ ├── [ 14K May 20 13:53] mkdlobj.z
│ │ │ ├── [ 649 May 20 13:53] stub.o
│ │ │ ├── [ 328 May 20 13:53] tail.o
│ │ │ └── [1.3K May 20 13:53] wyse50-lts123
│ │ └── [ 56 May 20 13:53] lotus.bcf
│ └── [ 512 May 20 13:53] man
│ ├── [ 512 May 20 13:53] cat.LOCAL
│ │ ├── [3.0K May 20 13:53] 123.1.z
│ │ ├── [3.1K May 20 13:53] 123.LOCAL.z
│ │ ├── [ 738 May 20 13:53] inst_dl.1.z
│ │ ├── [1.2K May 20 13:53] inst_dl.LOCAL
│ │ ├── [ 706 May 20 13:53] keyedit.1.z
│ │ ├── [1.1K May 20 13:53] keyedit.LOCAL
│ │ ├── [1.5K May 20 13:53] setup123.1.z
│ │ └── [2.7K May 20 13:53] setup123.LOCAL
│ └── [ 512 May 20 13:53] man1
│ ├── [2.6K May 20 13:53] 123.1.z
│ ├── [ 931 May 20 13:53] inst_dl.1
│ ├── [ 946 May 20 13:53] keyedit.1
│ └── [2.0K May 20 13:53] setup123.1
├── [ 512 May 20 13:53] tmp
│ ├── [ 512 May 20 13:53] _lbl
│ │ └── [ 512 May 20 13:53] prd=123
│ │ └── [ 512 May 20 13:53] typ=n386
│ │ └── [ 512 May 20 13:53] rel=1.0
│ │ └── [ 0 Sep 20 1990] vol=01
│ ├── [ 178 Sep 20 1990] init.123
│ └── [ 512 May 20 13:53] perms
│ └── [ 985 Sep 20 1990] 123
└── [ 512 May 20 13:53] usr
└── [ 512 May 20 13:53] tmp
└── [ 512 May 20 13:53] lotus_install
└── [ 512 May 20 13:53] 123
├── [ 19K Sep 20 1990] Install
├── [ 860 Sep 20 1990] banner
├── [ 20K Sep 20 1990] chkdskno
├── [ 25K Sep 20 1990] combine_exe
├── [ 208 Sep 20 1990] copyright.d
├── [ 167 Sep 20 1990] filelist.1
├── [4.8K Sep 20 1990] filelist.2
├── [ 351 Sep 20 1990] filelist.3
├── [ 294 Sep 20 1990] filelist.4
├── [ 257 Sep 20 1990] filelist.5
├── [ 12K Sep 20 1990] messages
└── [ 25K Sep 20 1990] mk_banner
35 directories, 338 files
Таинственный файл
Пока копался, на глаза попался вот этот каталог, что это вообще такое?
$ ls -l
total 2.0M
-rw-r--r-- 1 taviso taviso 1.2M May 20 13:53 123.o.z_1
-rw-r--r-- 1 taviso taviso 717K May 20 13:53 123.o.z_2
-rw------- 1 taviso taviso 913 May 20 13:53 dl_init.o.z
-rwx------ 1 taviso taviso 77K May 20 13:53 ld.z*
-rwx------ 1 taviso taviso 14K May 20 13:53 mkdlobj.z*
-rw------- 1 taviso taviso 649 May 20 13:53 stub.o
-rw------- 1 taviso taviso 328 May 20 13:53 tail.o
-rw-r--r-- 1 taviso taviso 1.4K May 20 13:53 wyse50-lts123
Этот файл 123.o
огромен, даже в сжатом виде его пришлось разделить на два диска. Давайте посмотрим поближе…
$ cat 123.o.z_? | gzip -d > 123.o
$ file 123.o
123.o: Intel 80386 COFF object file, not stripped, 5 sections, symbol offset=0x1efbdc, 19755 symbols, optional header size 28
Да, это оригинальный не урезанный объектный файл из 1-2-3. В нём имеется около 20 000 символов, включая частные символы и отладочную информацию.
Зачем Lotus выпускать это? Он настолько большой, что, должно быть, им пришлось физически доставлять дополнительный диск каждому покупателю? Могла ли это быть ошибка, случайно оставленная в последнем выпуске?
У меня было так много вопросов, но я недостаточно взрослый, чтобы иметь какой-либо опыт работы с SysV, поэтому я спросил седобородых на alt.folklore.computers, видели ли они это раньше и почему это могло произойти.
Ответ заключался в том, что это, вероятно, сделано намеренно — dlopen()
не была широко доступна в UNIX в начале 90-х, поэтому не было простого способа загрузки собственных плагинов или расширений. Чтобы решить эту проблему, поставщики отправляли кучу частично связанных объектных файлов со скриптом, который повторно связывал их с вашими расширениями — Умно!
Взлом
Я не могу передать вам, насколько полезным было это открытие: отладочная информация ответила на многие мои вопросы о внутреннем устройстве Lotus 1-2-3! Это был прямой порт исходного кода из DOS, поэтому в основном он работал так же, но теперь у меня были отладочные данные. Например, мне очень хотелось подключить растеризатор в моем драйвере, чтобы улучшить внешний вид графиков в терминале… но это было слишком сложно для понимания без документации.
Теперь я знаю, что растеризатор динамически генерирует небольшие программы с байт-кодом, которые интерпретируются графическим движком. Теперь, когда я знаю, что такое коды операций (опкоды), я могу дизассемблировать и изменить их в своем драйвере, чтобы улучшить результат!
GNU objcopy
Хорошо… но есть еще один большой вопрос: я знаю, что objcopy
может конвертировать объектные файлы COFF в ELF, формат, используемый Linux. Это кажется маловероятным, но возможно ли, чтобы я мог собрать это в нативную программу для Linux? ????
$ objcopy -I coff-i386 -O elf32-i386 123.o 123elf.o
$ file 123elf.o
123elf.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
Забавно, но на момент компиляции этого объектного файла еще не была выпущена первая версия Linux – но я думаю, что это возможно! Если вы хотите услышать о технических трудностях, читайте дальше!
$ objdump -p 123.o | grep Date
Time/Date Sat Sep 8 06:23:50 1990
Проблемы с портированием
Системные вызовы
Первая проблема заключается в том, что Linux и UNIX не используют совместимый интерфейс системных вызовов. UNIX использует интерфейс lcall7, поэтому нам нужно найти эти вызовы и исправить их. Вот как этот объектный файл вызывает open()
:
$ objdump -M intel --disassemble=open 123elf.o
123elf.o: file format elf32-i386
Disassembly of section .text:
000e20d4 <open>:
e20d4: b8 05 00 00 00 mov eax,0x5
e20d9: 9a 00 00 00 00 07 00 call 0x7:0x0
e20e0: 0f 82 c6 01 00 00 jb e22ac <_cerror>
e20e6: c3 ret
e20e7: 90 nop
Инструкция call
называется callgate и не поддерживается в Linux2, это просто сломается. Вместо этого я хочу направить все вызовы через glibc. Моей первой мыслью было просто пометить эти символы как неопределенные, а затем позволить компоновщику это исправить, импортировав символ-заменитель из glibc.
Перемещения
Ничего не бывает легко, оказывается, это не будет работать! Если мы попытаемся, objcopy
просто откажет:
$ objcopy -I coff-i386 -O elf32-i386 --strip-symbol open 123.o 123elf.o
objcopy: not stripping symbol `open' because it is named in a relocation
Что objcopy
пытается нам здесь сказать?
Это перемещаемый объектный файл, что означает, что его можно загрузить по любому адресу и при этом он будет работать. Это возможно, потому что он содержит всю необходимую информацию – перемещения – для его корректировки.
Перемещения действительно просты, компилятор просто записывает имя символа и ссылки на него. Теперь компоновщик может просто пройтись по каждой ссылке и исправить ее, чтобы она указывала на новое местоположение – легко.
Итак, objcopy
говорит, что вы не можете удалить этот символ, потому что он не будет знать, что патчить, когда его перемещает. Это достаточно справедливо – но то, что objcopy
этого не делает, не означает, что это невозможно! Мы могли бы просто исправить перемещения, верно?
Я не знаю ни одного инструмента, который мог бы это сделать, но COFF — несложный формат — я напишу его!
Представляем coffsyrup, крошечный инструмент, который удалит эти надоедливые COFF символы, даже если objcopy
откажет!
$ coffsyrup 123.o 123new.o open
MATCH open
RELOC rel open @0x180fa ~0xc9ede
RELOC rel open @0x4c9a1 ~0x95637
RELOC rel open @0x4d348 ~0x94c90
RELOC rel open @0x4ec13 ~0x933c5
Несовместимые функции
Теперь, когда мы можем перенаправлять функции, нам нужно беспокоиться о несовместимых функциях.
Многие стандартные функции UNIX совместимы на уровне исходного кода, но не на двоичном уровне, поскольку никто не обещает, что структуры будут иметь одинаковый размер или компоновку в версиях UNIX. Очевидный пример — struct stat
.
Например, этот код, скорее всего, будет работать на любой UNIX-подобной системе, на которой можно его скомпилировать:
struct stat sb;
if (stat("/etc/passwd", &sb) == 0) {
printf("Size: %u\n", sb.st_size);
}
Однако полученный объектный файл вряд ли будет работать в какой-либо другой системе. Это потому, что размер struct stat
и смещение st_size
будут разными — это, скорее всего, просто испортит ваш стек и приведет к сбою!
К счастью, в UNIX не так уж и много подобных функций. На самом деле их число настолько мало, что я, вероятно, смогу написать обёртки для их перевода. Важными из них являются stat()
, times()
, uname()
, fcntl()
, ioctl()
и так далее.
Все, что мне нужно сделать, это переименовать эти символы с помощью objcopy
, а затем пометить их как неопределенные с помощью coffsyrup
. Я даже могу написать небольшую обёртку, которая преобразует struct stat
Linux в struct stat
UNIX, и она будет работать!
Termios
Ну… я сказал «маленькие» обертки – но местами есть большие несовместимости. Ночным кошмаром был termios. Взгляните на справочную страницу termios(3)
, это довольно сложно, не так ли? Что ж, здесь все работает по-разному, тонкими, несовместимыми и сложными для отладки способами в каждой UNIX системе.
Лицензирование
Невероятно, но после нескольких взломов он действительно работает без сбоев!
…и отказывается работать без лицензии, блин! Что ж, я являюсь законным владельцем лицензионной коробочной копии Lotus 1-2-3, а это заброшенное программное обеспечение 32-летней давности. Думаю, Митч Капор простит меня за то, что я обошел эту проверку.
Из прерывания на exit()
я вижу, что существует внутренний символ lic_init()
, отвечающий за проверку действующей лицензии. Я посмотрел код в IDA и понял логику.
Он просто ищет файл LICENSE.000
, который содержит дату истечения срока действия, имя пользователя и имя системы. Если всё соответствует тому, что сообщает система, проверка пройдена! ????☠️
Результат
Всё, Lotus 1-2-3 портирован на новую операционную систему. Есть несколько недостатков, которые необходимо устранить, и мне нужно перенести драйвер терминала, но его можно использовать на 100%. На данный момент версия для DOS, работающая в режиме эмуляции, выглядит лучше — это можно исправить!
Надеюсь, вам понравилось об этом читать. Маловероятно, но если вы действительно захотите попробовать сами — весь мой код находится на github.
1 Я говорю конкретно про классический R3, там были выпуски до 2002 года
2 В какой-то момент в Linux была поддержка совместимости lcall7 и lcall27, но, увы, больше нет.
Комментарии (15)
ilyamodder
16.10.2023 11:37+2Мне пришлось перепроектировать API драйвера для этого, но он работает прекрасно.
Надеюсь, вам понравилось об этом читать. Маловероятно, но если вы действительно захотите попробовать сами
Что ж, я законный лицензированный владелец 1-2-3 с коробочной копией 1-2-3
Опять надмозговый перевод на хабре( Дословно переводить с английского - не лучшая идея.
bel1k0v Автор
16.10.2023 11:3714 раз эти предложения перечитывал, оставил так. Если критикуете - предлагайте.
Что значит "надмозговый"? Если вы имели ввиду "в лоб", то это не так очевидно, тогда уж "вмозговый".
Как вы можете заметить в вашем комментарии подмена слов усложняет понимание, что опровергает ваш тезис.
BadDancer
16.10.2023 11:37+1bel1k0v Автор
16.10.2023 11:37Хм, в таком случае:
Мне пришлось перепроектировать API драйвера для этого, но он работает прекрасно.
В оригинале "I had to reverse engineer the driver api to make that happen, but it works beautifully". Смысл не нарушен, идёт отсылка к предыдущему предложению. "Перепроектировать" именно то, что пришлось сделать автору.
Надеюсь, вам понравилось об этом читать. Маловероятно, но если вы действительно захотите попробовать сами
Дословно (гугл):
Надеюсь, вам понравилось об этом читать. В крайне маловероятном случае, если вы действительно захотите попробовать это сами — весь мой код находится на github.
Убраны лишние слова, чтобы снизить нагрузку на читателя.
Что ж, я законный лицензированный владелец 1-2-3 с коробочной копией 1-2-3
В оригинале:
I am a legitimate licensed 1-2-3 owner with a boxed copy of 1-2-3
Тут я не вижу альтернатив, не выкинешь ничего и не перефразируешь, разве что только "1-2-3" повторяющееся убрать.
BadDancer
16.10.2023 11:37I am a legitimate licensed 1-2-3 owner with a boxed copy of 1-2-3
Предложил бы:
Я являюсь законным владельцем коробочной версии Lotus 1-2-3.
datacompboy
16.10.2023 11:37+7"reverse engineer" это не "перепроектировать". Если хочется перевести -- "реконструировать". Это совсем другой смысл.
NeoCode
Досовский псевдографический софт обладает определенным очарованием, хотя я его застал совсем немного - в основном Norton и Borland C. Глядя на все это, пришла в голову мысль, что было бы неплохо, если бы знатоки-энтузиасты выпустили некий образ, скажем на основе DosBox, содержащий систему с подробкой распространенного тогда софта - офисного, программерского, игрушек и т.д., и выложили бы на торрентах.
Javian
Недавно тоже на это обратил внимание.
Tyusha
Вендоры радостно таких энтузиастов засудят.
vit1251
А что мешает выпустить аналоги этого софта? Вроде бы Turbo Vision реализация для Linux выпущена, а код некоторых утилит давно открыт. Почему бы не выпустить нативные пакеты тех же редакторов или IDE?
NeoCode
И много вендоры засудили тех кто торренты раздает? А тут софт 25+ летней давности, представляющий чисто музейно-исторический интерес.
Да, очевидно что данный проект не должен быть официальным ввиду чрезвычайно высокой бюрократической и организационной сложности... Многих компаний как минимум уже просто не существует.
jobless
Параллельно с проектом HytechSql распространялась шикарная хорошо документированная библиотека интерфейсов для Pascal и С. И даже дизайнер форм. В дальнейшем она же использовалась для консольных утилит. У меня много чего сохранилось, но вот именно эта библиотека утрачена.
Вдруг кто знает места археологические?