Интро

Это захватывающее время в сообществе энтузиастов 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)


  1. NeoCode
    16.10.2023 11:37
    +4

    Досовский псевдографический софт обладает определенным очарованием, хотя я его застал совсем немного - в основном Norton и Borland C. Глядя на все это, пришла в голову мысль, что было бы неплохо, если бы знатоки-энтузиасты выпустили некий образ, скажем на основе DosBox, содержащий систему с подробкой распространенного тогда софта - офисного, программерского, игрушек и т.д., и выложили бы на торрентах.


    1. Javian
      16.10.2023 11:37
      +6

      Недавно тоже на это обратил внимание.


    1. Tyusha
      16.10.2023 11:37

      Вендоры радостно таких энтузиастов засудят.


      1. vit1251
        16.10.2023 11:37
        +2

        А что мешает выпустить аналоги этого софта? Вроде бы Turbo Vision реализация для Linux выпущена, а код некоторых утилит давно открыт. Почему бы не выпустить нативные пакеты тех же редакторов или IDE?


      1. NeoCode
        16.10.2023 11:37
        +1

        И много вендоры засудили тех кто торренты раздает? А тут софт 25+ летней давности, представляющий чисто музейно-исторический интерес.

        Да, очевидно что данный проект не должен быть официальным ввиду чрезвычайно высокой бюрократической и организационной сложности... Многих компаний как минимум уже просто не существует.


    1. jobless
      16.10.2023 11:37

      Параллельно с проектом HytechSql распространялась шикарная хорошо документированная библиотека интерфейсов для Pascal и С. И даже дизайнер форм. В дальнейшем она же использовалась для консольных утилит. У меня много чего сохранилось, но вот именно эта библиотека утрачена.

      Вдруг кто знает места археологические?


  1. ilyamodder
    16.10.2023 11:37
    +2

    Мне пришлось перепроектировать API драйвера для этого, но он работает прекрасно.

    Надеюсь, вам понравилось об этом читать. Маловероятно, но если вы действительно захотите попробовать сами

    Что ж, я законный лицензированный владелец 1-2-3 с коробочной копией 1-2-3

    Опять надмозговый перевод на хабре( Дословно переводить с английского - не лучшая идея.


    1. bel1k0v Автор
      16.10.2023 11:37

      14 раз эти предложения перечитывал, оставил так. Если критикуете - предлагайте.

      Что значит "надмозговый"? Если вы имели ввиду "в лоб", то это не так очевидно, тогда уж "вмозговый".

      Как вы можете заметить в вашем комментарии подмена слов усложняет понимание, что опровергает ваш тезис.


      1. BadDancer
        16.10.2023 11:37
        +1

        1. bel1k0v Автор
          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" повторяющееся убрать.


          1. BadDancer
            16.10.2023 11:37

            I am a legitimate licensed 1-2-3 owner with a boxed copy of 1-2-3
            Предложил бы:
            Я являюсь законным владельцем коробочной версии Lotus 1-2-3.


            1. bel1k0v Автор
              16.10.2023 11:37

              Поправил, слово "licensed" относилось к копии (диску), а не к автору.


              1. funca
                16.10.2023 11:37

                Licensed это он про себя. Стилистически это не вполне корректно. Но такое нарочитое преувеличение обычно считывается как ирония.


          1. datacompboy
            16.10.2023 11:37
            +7

            "reverse engineer" это не "перепроектировать". Если хочется перевести -- "реконструировать". Это совсем другой смысл.


  1. IvanLokh
    16.10.2023 11:37

    Ранние версии linux имели в ядре встроенную поддержку запуска SCO COFF.