пʼятницю, 12 серпня 2022 р.

Анонс -- 64-бітний плагін Total Commander для роботи із образами FAT

Займаючись "ретрокомп'ютингом", постійно доводиться мати справу з образами дисків різних епох. Для мене найзручнішим способом є відповідні плагіни для Total Commander (TCmd). Однак, їх так і не було перенесено на 64-бітний TCmd. 

Коли, посередині літа, втома стала закритичною, для відпочинку вирішив створити такий плагін -- з одного боку, задача проста, з іншого -- мозок таки зайнятий, і не відволікається на дурні думки. Здається, це перше програмне забезпечення, написане мною, призначене для (відносно) широкого кола користувачів. 

Код знаходиться на Github: FATImage_TCMD_plugin. Бінарники -- в релізах репозиторію.

Багато подробиць є в ReadMe.md репозиторію. Дуже коротко:

  • Поки read-only.
    • Працюю над версією, що вмітиме писати в образи та створювати їх.
  • Підтримує FAT12, FAT16, FAT32.
  • Підтримує VFAT -- довгі імена файлів, не залежно від розрядності FAT.
  • Вміє працювати із образами, розбитими на диски, якщо вони користуються MBR.
  • exFAT i GPT поки не підтримуються.

Буду вдячний за відгуки, знайдені баги, pull-request-и. 

 

Подробиці, не висвітлені на GitHub

 

Оскільки плагін потрібен мені для роботи із історичними образами, додано підтримку всіляких аберацій, що траплялися в минулому. Навіть якщо обмежитися світом (пост)DOS та файловими системами FAT, на початку свого існування DOS не використовував BPB (DOS 1.xx), потім, до версії 3.31, дивно з ним поводилися. Крім того, багато образів, що блукають ретро-сайтами, містять перед не-модифікованим образом якусь метаінформацію. 

Тому поточний алгоритм роботи наступний:

  • Шукаємо boot-сектор із адекватним BPB.
    • BPB -- BIOS parameter block, структуру даних в boot-секторі диску, що описує його будову. 
    • Опціонально, сприймає boot-сектор без сигнатури 0x55AA в кінці сектора -- існує певна кількість таких образів із першої половини 1980-х.
  • Якщо не знайдено -- перевіряє, чи це не є образ епохи DOS 1.xx. 
    • Для цього дивиться на 512-й байт (рахуючи з нуля) -- перший байт FAT у таких образах, так-званий media descriptor, по якому ті системи розрізняли дискети та розмір образу. 
    • Перевірка розміру -- чи співпадає із чотирма можливими варіантами, 160Кб/180Кб/320Кб/360Кб тут важлива, оскільки детектувати образ за одним байтом -- відверто ненадійно. 
    • Якщо інформація в media descriptor відповідає розміру образу, формується коректний для нього BPB в пам'яті.
    • Образ MS-DOS 1.12.ver.1.12 OEM [Compaq], що блукає Інетом, містить після кінця 4112 порожніх байт -- для нього, заради моєї зручності, зроблено виняток.
    • Поки такий виняток один, але їх всіх, скільки б не було в майбутньому, можна буде вимикати з файлу конфігурації.
    • 8-дюймові образи поки не підтримуються.
  • Якщо не образ DOS 1.xx, відбувається пошук MBR.
  • Якщо MBR знайдено, намагається у кожному розділі знайти FAT-образи. Розділи із невідомими плагіну файловими системами показує у вигляді "D_Unknown", де літери C, D, E тощо, позначають послідовно знайдені розділи. 
    • Якщо розділів більше, ніж вистачає літер, для них буде використовувати інше позначення.
    • Випробувано поки далеко не всі можливі комбінації, зокрема більше 11 розділів у образі не тестувалося, не перевірялася поведінка на образах чи файлах, більших за 2Гб тощо. Чекаю на багрепорти, разом із образами дисків, які створюють проблеми.
    • CHS MBR розділи поки не підтримуються. В дикій природі вони мені ще не траплялися, але планую визначати геометрію за першим розділом із BPB, якщо не спрацює -- питатися користувача.
  • Якщо правдоподібного MBR немає -- відбувається пошук boot-сектора за шаблоном "0xB8, 0xx, 0x90, 0xx ... 0xx, 0x55, 0xAA", розміром 512 байт, де 0xx -- довільні байти. 
    • По замовчуванню, пошук обмежено першими 64Кб. Можливо, це значення завелике --  утиліти типу fdisk, format чи sys можуть містити копії boot-сектора, даючи помилкові детектування.
    • Зауважте, пошук MBR потрібно робити перед цим етапом -- інакше він просто може знаходити перший розділ MBR-диску із FAT.
  • Якщо не спрацювала жодна спроба, вважати, що це не образ диску із FAT.

Цією поведінкою можна керувати за допомогою файлу конфігурації -- вимикати кожен із етапів, крім першого. Подробиці -- див. ReadMe репозиторію

Тестовий образ, що містить 11 розділів (включаючи extended, якій не відповідає диск) із ext2fs, ext3fs, ext4fs, exFAT, NTFS -- позначеними як Unknown, та FAT-диски із FAT12, FAT16, FAT32 і кириличними довгими іменами всередині.


UI -- FLTK and logging

TCmd не передбачає якогось інтерфейсу користувача для взаємодії із плагіном, окрім як при архівації. Однак, іноді це зручно. Додатково, із GUI на WinAPI я зовсім не дружу -- писати на ньому діалоги перестало б бути відпочинком. Тому, як експериментальний і потенційно нестабільний засіб, для діалогів використав FLTK. По замовчуванню він не вкомпільовується, а діалоги можна вимкнути в конфігурації -- через потенційні багатопоточні проблеми, цей засіб не рекомендовано для широкого використання.

Хоча проблеми поки жодного разу не проявилися, однак, FLTK очікує, що GUI маніпулює головний потік програми, і користується статичними змінними, а TCmd запускає плагіни в окремих потоках. Колись це може датися взнаки. Особливо за використання фонових операцій. З іншого боку, існує підтримка написання плагінів на Qt -- яке помітно більш вимогливе, але лише для wlx -- плагінів Lister. Тому, може й не проблема. 



Додатково -- для ознайомлення з внутрішніми особливостями образів та для відлагодження, підтримується логування. Шлях до файлу із логом (журналом) задається у файлі конфігурації. Приклад журналу:

Info# Bytes in cluster: 512
Info# FAT1 area offset: 0x0025D04000
Info# Root area offset: 0x0025E8E000
Info# Data area offset: 0x0025E8E000
Info# Total sectors in FAT: 204800
Info# Preliminary FAT type: FAT32
Info# FAT32 hidden sectors: 0
Info# FAT32 total sectors 32-bit: 204800
Info# FAT32 sectors per FAT: 1576
Info# FAT32 FAT mirroring: 1
Info# FAT32 Information Sector: 1
Info# FAT32 backup of boot sector: 6
Info# FAT32 Volume ID: 0x00B5D366B8
Info# FAT32 Volume label: NO
Info# Processed partition 6, offset: 0x0000100000

 

Warning# Error processing partition 7: 14
Info# Bytes per sector: 512
Info# Sectors per cluster: 8
Info# Reserved sectors: 0
Info# Number of FATs: 0
Info# Root entries count: 0
Info# Total sectors 16-bit: 0
Info# Media descriptor: 248
Info# Sectors per FAT: 0
Info# Sectors per track: 0
Info# Heads: 0

Плагін розпочав свою історію як перенесення 32-бітного IMG 0.9 від IvGzury -- завдяки наявним джерельним текстом. Але зараз від вихідного коду залишилося, може, пара назв змінних та трохи загального настрою. 

 

Код

Архіваторні плагіни TCmd -- wcx-плагіни, мають доволі простий інтерфейс. Це динамічна бібліотека, dll, яка повинна експортувати певні функції, які потім викликає TCmd. Видно, що це перші плагіни, які в TCmd з'явилися -- API виглядає архаїчно. 

  • TCmd спочатку викликає OpenArchive, яка має повернути дескриптор об'єкту для керування архівом -- його потім передаватимуть іншим функціям. 
  • Для підтримки фонової розархівації плагін повинен залишатися реєнтрантним -- не використовувати глобальні та статичні змінні, крім конфігурації -- яка буде тільки читатися. Тому вся інформація повинна зберігатися в тому об'єкті.
  • Коли TCmd завершив, він викличе CloseArchive.
  • ReadHeader викликається доти, поки вона повертає 0, та повинна передавати TCmd інформацію про наступний файл в архіві.
    • Зацитую документацію: "For example, you may want to store the position in the archive when returning files information in ReadHeader."
  •  ProcessFile викликається, коли потрібно "добути" файл з архіву. Але викликається вона хитро... 
    • Цитата з документації: "When Total Commander first opens an archive, it scans all file names with OpenMode==PK_OM_LIST, so ReadHeader() is called in a loop with calling ProcessFile(...,PK_SKIP,...). When the user has selected some files and started to decompress them, Total Commander again calls ReadHeader() in a loop. For each file which is to be extracted, Total Commander calls ProcessFile() with Operation==PK_EXTRACT immediately after the ReadHeader() call for this file. If the file needs to be skipped, it calls it with Operation==PK_SKIP."
    • Тобто, під час розархівування ReadHeader і  ProcessFile викликаються двічі для кожного файлу.
  • Функціям SetChangeVolProc і SetProcessDataProc передаються callback-и, які плагін може використати, щоб попросити вставити наступну частину чи повідомити про прогрес. Для цього плагіну їх функціонал не використовується.
  • Інші функції опціональні та використовуються, якщо присутні і/або якщо вказано про їх підтримку в (опціональній) GetPackerCaps. 
  • Детальніше див. на офіційному форумі: "Plugin interface descriptions for TC 7.55" і вікі: "Developer's corner".

Код написано із використанням C++20, без зовнішніх залежностей -- крім опціонального FLTK. 

  • Ядро використовує самописні мінімалістичні стрічки фіксованого розміру, але для читання конфігурації використано std::string, std::optional, std::map та виключення. 
  • Із кумедного: переписуючи, прибрав def-файл -- експорт здійснюю __declspec(dllexport) та extern "C", як частину підготовки портування під Linux. Потім довелося повернути -- під Win32 воно не допомагає, імена все рівно декоруються. А додавати щось типу 
    #pragma comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)
    до кожної експортованої функції -- краще вже def-файл.
  • Std::format -- дуже зручна річ! І, в цілому, не дорога. Але перші ж використання збільшили плагін (release з debug info) на 200 Кб, з 71 Кб до 271 Кб, а оскільки потрібно воно лише для логування -- відмовився, замінив на snprintf. До речі, статичне підключення FLTK -- простої, але повноцінної бібліотеки GUI, збільшує розмір лише  на 350 Кб.

Враховуючи загальну структуру wcx-плагінів, складний режим роботи цього плагіну -- підтримку дисків із розділами та без, потребу для дисків із розділами створювати віртуальні директорі для кожного із розділів, пошук MBR здійснювати до пошуку boot-сектора (після метаінформації), потребу коду роботи із boot-сектором знати розмір файлу із образом, та й загальний режим написання коду -- розслаблений, код трохи страшненький. В принципі, для мене -- не дуже поганий, добре витримав кілька радикальних переробок, але стандартам не відповідає. Можливо, в майбутньому -- виправлятиму.

Для відлагодження зручною є команда TCmd cm_UnloadPlugins, для якої варто створити кнопку -- вона вивантажує всі плагіни з пам'яті, та дозволяє замінити файл плагіну без перезапуску TCmd. 


Плани 

  • Підтримка запису та створення образів.
  • Підтримка варіанту під Linux -- для Double Commander. (У Windows-варіанті плагін працює із Double Commander, хоча ретельно поки не тестувався).
  • Підтримка exFAT та GPT.
  • Lister-плагін (wlx) для огляду інформації про образ та Content плагін (wdx) для пошуку по характеристиках образів. 
  • Поки образ має бути "сирим" -- побайтовою копією диску, можливо -- із якоюсь метаінформацією перед ним. В майбутньому, можливо, додам підтримку образів, створених популярними програмами, типу TeleDisk. (Див. також посилання в кінці FAT_definitions.h)
  • Підтримка інших файлових систем.
  • Stand-alone варіант.

Жодних гарантій щодо часу виконання цих планів, на жаль, не маю -- від цього літа до безмежності. Підтримка образів CP/M та UCSD p-System розглядалася, але поки не планується, через їх сильну прив'язку до геометрії тогочасних дисків, та неможливість, зазвичай, визначити її із самого образу.


Немає коментарів:

Дописати коментар