Teledisk

Материал из Emuverse
Этот документ создан для Emuverse и распространяется на условиях лицензии CC-BY-SA-3.0.

Teledisk — формат файлов, содержащий посекторную копию дискеты. Создавался с помощью одноименной утилиты. Внутри файла находятся данные, упакованные по принципу RLE (RLE+LZSS в режиме «Advanced compression»).

Описание формата

Формат файла следующий:

Заголовок
Данные

Формат заголовка:

type 	
    TTDHeader = packed record
      sig:   array [0..1] of Char;   //Сигнатура "TD" или "td"
      vol:   Byte;                   //Номер тома. 0 для TD0
      chk:   Byte;                   //Сигнатура, одинаковая для всех томов
      ver:   Byte;                   //Для версий 2.11-2.16 равно 15H
      dens:  Byte;                   //Плотность записи. Обычно 0.
      typ:   Byte;                   //Тип устройства. 1=360K, 2=1.2M,
                                     //3=720K, 4=1.44M.
      flag:  Byte;                   //Старший бит - наличие комментария
      dos:   Byte;                   //DOS mode? Обычно 00H
      sides: Byte;                   //Кол-во сторон
      crc:   Word;                   //CRC первых 10 байт записи
    end;

Если сигнатура равна «TD», за заголовком следуют данные без упаковки LZSS, иначе они упакованы, и требуется распаковка. Далее распакованные данные обрабатываются аналогично формату без компрессии.

Формат данных (отступы сделаны для удобства):

Комментарий (если TTDHeader.flag > 80H)
 Заголовок дорожки 0
   Заголовок сектора 1
     Данные сектора 1
   Заголовок сектора 2
     Данные сектора 2
   ..........
   Заголовок сектора N
     Данные сектора N
 Заголовок дорожки 1
 ..........
 Заголовок дорожки M
 ..........

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

Формат комментария:

Заголовок комментария
 Данные комментария (текст)

Формат заголовка комментария:

TTDComment = packed record
  crc: Word;                 //Контрольная сумма комментария
  len: Word;                 //Длина комментария в байтах
  yr, mon, day,              //Дата. Год отсчитывается от 1990.
  hr, min, sec:	Byte;
end;

Данные комментария представляют собой строки, оканчивающиеся #0. Строк может быть несколько.

Формат заголовка дорожки:

TTDTrack = packed record
  nsec: Byte;                //Кол-во последующих записей секторов.
                             //Часть записей должна игнорироваться
  trk:  Byte;                //Номер дорожки
  head: Byte;                //Номер стороны
  crc:  Byte;                //Контрольная сумма заголовка
end;

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

Заголовок сектора:

TTDSector = packed record
  trk:   Byte;               //Номер дорожки;
  head:  Byte;               //Номер стороны;
  sec:   Byte;               //Номер сектора;
  secz:  Byte;               //Код размера сектора;
  cntrl: Byte;               //Тип данных;
  crc:   Byte;               //Контрольная сумма распакованных данных
end;

TDSector.sec=$65 обозначает, что достигнут конец файла, остаток данных нужно пропустить.

Если TDSector.sec > TDTrack.nsec, последующие данные сектора должны быть проигнорированы.

Вычисление размера сектора в байтах: SectSize := 1 shl (TDSector.secz+7);

Данные относятся к реальному сектору, если выполняется следующее условие: ((TDSector.cntrl and $30) = 0) and ((TDSector.secz and $F8) = 0), иначе данные нужно игнорировать.

Если TDSector.cntrl=$10, то сектор содержит пустые данные (возможно, не был прочитан корректно), нужно переходить к чтению заголовка следующего сектора.

Формат блока данных сектора (если сектор непустой):

Длина блока (2 байта)
Тип блока (1 байт)
Данные блока

Если Тип блока=0, то «данные блока» представляют собой содержание сектора в неупакованном формате, при этом «длина блока» должна совпадать с размером сектора плюс один байт для поля "Тип блока".

Если Тип блока=1, то «данные блока» представляют собой следующую запись:

TTDRepeat = packed record
  count: Word;                   //Количество повторений
  pat:   array [0..1] of Byte;   //Данные для повторения (2 байта)
end;

«Количество повторений» должно быть в два раза меньше размера сектора.

Если Тип блока=2, то «данные блока» представляют собой последовательность записей:

Заголовок записи
Данные записи

Формат заголовка записи:

TTDPattern = packed record
  flag:  Byte;
  count: Byte;
end;

Если TDPattern.flag=0, то за заголовком следуют данные длиной TDPattern.count. Иначе, TDPattern.flag обозначает размер блока для повторения, следующий за заголовком, TDPattern.count — количество повторений. Размер блока в байтах вычисляется как PatSize := 1 shl TDPattern.flag;.

Обобщенный алгоритм

--------- основная процедура ---------

Чтение заголовка файла TDHeader
Если TDHeader.sig == ”td” то распаковка данных
Если TDHeader.sig != ”TD” то выход с ошибкой «неправильный формат»

Если TDHeader.flag & 80H > 0 то
  Чтение заголовка комментария
  Чтение блока данных комментария

Пока не конец данных
  Чтение заголовка дорожки TDTrack
  Если TDTrack.nsec == 0FFH то окончание работы
  
   Для секторов от 1 до TDTrack.nsec
     Чтение заголовка сектора TDSector
     Если TDSector.sec == 65H то окончание работы
     SectSize := 1 shl (TDSector.secz+7);
     Если ((TDSector.cntrl & 30H) == 0) && ((TDSector.secz & 0F8H) == 0) то
       Если TDSector.cntrl == 10H то 
         сектор пустой, переход к следующему
       Иначе
         Чтение 2-х байт «Длина данных»
         Если TDSector.sec <= TDTrack.nsec то
           Распаковка_данных(Длина данных)
         Иначе
           Пропускаем «Длина данных» из входного потока

--------- подпрограмма ---------

Процедура Распаковка_данных(Длина данных)

  Чтение 1 байта «Тип блока»
  Если «Тип блока» == 0 то
     Читаем «Длина данных»-1 в качестве данных сектора
  Иначе
  Если «Тип блока» == 1 то
     Читаем запись TDRepeat
     Помещаем в буфер сектора TDRepeat.count повторений TDRepeat.pat
  Иначе
  Если «Тип блока» == 2 то
     Пока не достигнута «Длина данных»-1
       Читаем запись TDPattern
       Если TDPattern.flag == 0 то
         Читаем TDPattern.count байт и помещаем их в буфер сектора
       Иначе
         PatSize := 1 shl TDPattern.flag;
         Читаем «PatSize» данных из входного потока
         Пишем эти данные TDPattern.count раз в буфер сектора

Источники и ссылки

Программы