— Тот, кто хочет стать Чародеем Моря, должен знать Настоящее Имя каждой капли воды.
Урсула Ле Гуин. Волшебник Земноморья.
Сам же дисковый файл, хранящий внутри себя структурированное хранилище, называется «файл-документ» (docfile) или «составной файл» (compound file). Первый термин применялся во времена OLE 1, второй появился в середине 90-х годов вместе с OLE 2, сейчас они обычно используются как синонимы.
В OLE существует еще понятие «составной документ» (compound document), но этот термин обозначает абстрактный подкласс хранилищ особого вида. в-общем, это уже ария несколько из другой оперы.
Внутри составной файл состоит из следующих элементов:
Еще в литературе про OLE можно встретить термин «корневое хранилище» (root storage). Нетрудно сообразить, что он на самом деле означает всего лишь корневой каталог, т.е. главный каталог в составном файле. Фактически, так оно и есть. Но в литературе этот термин иногда используется в качестве синонима для составного файла и файл-документа, ведь они без корневого каталога не могут существовать, также как и он без них. Ну что ж, будем иметь это в виду.
Наконец, потоки (streams), на которые имеются ссылки в корневом хранилище (root storage), могут называться «наборы свойств» (property sets). На самом деле, так имеют право наименоваться не все потоки (streams), а только те из них, кто организован особым родом: имеют стандартизованный заголовок, разбиты на специфические разделы и т.п. Для нас это не слишком важно.
. Стоило ему прослышать, что где-то результаты многообещающих исследований сданы на консервацию, как он приходил в зоологическое неистовство и рвался разоблачать, обличать и срывать покровы.
А. и Б. Стругацкие. Жук в муравейнике.
Хотя официально фирма Microsoft информацией по этой теме мало с кем делилась, и даже одно время пыталась препятствовать ее распространению, все же нельзя сказать, что эта информация закрыта. Ей на самом деле владеют многие. Без этой информации не существовали бы такие продукты, как антивирусы KAV и DrWEb, переводчик Stylus, пакет 1С:Предприятие и т.п.
Сейчас в Интернете ее вполне достаточное количество, просто она разбросана мелкими порциями по разным малоизвестным источникам, и, что еще более неприятно, озаглавлена совсем не так, как хотелось бы нам. К сожалению, это как раз тот случай, о котором предупреждал незабвенный Козьма Прутков: на клетке слона частенько можно встретить надписи «буйвол», «мышь», «муравей», «динозавр», но только не «слон».
1. Общее описание. Терминология
— Тот, кто хочет стать Чародеем Моря, должен знать Настоящее Имя каждой капли воды.
Урсула Ле Гуин. Волшебник Земноморья.
Сам же дисковый файл, хранящий внутри себя структурированное хранилище, называется «файл-документ» (docfile) или «составной файл» (compound file). Первый термин применялся во времена OLE 1, второй появился в середине 90-х годов вместе с OLE 2, сейчас они обычно используются как синонимы.
В OLE существует еще понятие «составной документ» (compound document), но этот термин обозначает абстрактный подкласс хранилищ особого вида. в-общем, это уже ария несколько из другой оперы.
Внутри составной файл состоит из следующих элементов:
Еще в литературе про OLE можно встретить термин «корневое хранилище» (root storage). Нетрудно сообразить, что он на самом деле означает всего лишь корневой каталог, т.е. главный каталог в составном файле. Фактически, так оно и есть. Но в литературе этот термин иногда используется в качестве синонима для составного файла и файл-документа, ведь они без корневого каталога не могут существовать, также как и он без них. Ну что ж, будем иметь это в виду.
Наконец, потоки (streams), на которые имеются ссылки в корневом хранилище (root storage), могут называться «наборы свойств» (property sets). На самом деле, так имеют право наименоваться не все потоки (streams), а только те из них, кто организован особым родом: имеют стандартизованный заголовок, разбиты на специфические разделы и т.п. Для нас это не слишком важно.
2. Доступ к STRUCTURED STORAGE через OLE2 API
— Добро пожаловать! Это автобус для ведьм и волшебников, попавших в трудное положение! Взмахните палочкой и входите в салон: мы домчим вас куда угодно!
Дж. К. Ролинг. Гарри Поттер и узник Азбакана
Microsoft предоставляет мощные средства для работы в рамках технологии OLE, они сконцентрированы в библиотеках OLE2.DLL (для 16-разрядных приложений) и OLE32.DLL (для 32-разрядных приложений). В принципе, к ним можно обращаться из любой системы программирования (например, из MS VB и даже из языка ассемблера), использовав традиционный механизм GetModuleHandle/GetProcAddress. Но проще всего это делать из MS VC или Borland C/C++. По крайней мере, все примеры из этой статьи компилировались при помощи Borland C/C++ v5.01.
Итак, в довольно объемной и сложной библиотеке OLE32.DLL для нас с точки зрения темы статьи интересны около дюжины API- функций, позволяющих создавать и открывать составные файлы в различных режимах, обращаться к их каталогам и т.п. Рассмотрим некоторые из них.
Обычно для параметра ФлагиДоступа используется конкатенация из битов STGM_CREATE (создать новый составной файл), STGM_READ (разрешить чтение), STGM_WRITE (разрешить запись) или STGM_READWRITE (разрешить чтение и запись). Но можно указать STGM_CONVERT, это якобы позволяет преобразовывать «сырые» данные в структурированное хранилище, помещая их в поток с предопределенныи именем CONTENTS.
Дальше кратко описываются некоторые, наиболее интересные методы этого интерфейса.
Фактически, сервисных методов гораздо больше. Например, существует большой набор методов для работы с lock-bytes, но он нам в контексте статьи не слишком актуален.
Создадим в Word 97/2000/XP пустой DOC-файл без текста и напустим на него нашу программу. Мы увидим примерно следующее:
Для DOC-файла в формате Word 6/7 (его можно создать, например, при помощи Wordpad) картинка будет попроще:
Примерно так, как описано в этом разделе, устроены популярный FAR-плагин DocFile Browser Игоря Павлова и утилита OLE2View из MS Visual C/С++.
Если хотите подробностей, то рекомендую:
1) хорошую статью Артема Каева «ActiveX по шагам»; 2)фирменный материал от Microsoft «OLE 2 programmers reference» (его можно найти в файлах OLE.HLP или OLE2.HLP).
3. Низкоуровневый доступ к STRUCTURED STORAGE
Вокруг разобранной автомашины моментально собралась толпа коротышек. Каждому было интересно взглянуть на устройство механизма.
Н. Носов. Незнайка в Солнечном городе
Широко известен народный персонаж по имени Чукча, который служил «писателем» и по этой причине не считал для себя нужным учиться читать. Наверное, имеются автомобилисты, которые никогда не заглядывали под капот своего четырехколесного друга. Не исключено, что существуют нежно любящие друг друга супруги, которые ни разу в жизни. гм. надеюсь, мы к их числу не относимся?
Давайте же приоткроем «кошмарную тайну кровавой фирмы Microsoft»: посмотрим на составной файл невооруженными глазками и пощупаем его голыми ручками. Впрочем, «программисты», которые умеют только кликать по иконкам и таскать мышом картинки между окошкам, могут этот раздел с чистой совестью пропустить.
Примечание. Есть предположние, что по смещению 40h располагается поле, описывающее размер сектора. В подавляющем большинстве DOC-файлов длиной до 1 Мб в этом поле содержится значение 1 (т.е. размер сектора равен 512), но изредка встречается 2 (т.е. размер сектора равен 1024). Вероятно, возможны 3, 4, и так далее. В этом случае лучше говорить не о «секторах», но о «кластерах». Но идеология организации составного файла от этого не меняется.
Теперь очень легко проверить, организован ли какой-нибудь файл по правилам структурированного хранилища: достаточно проверить его первые 4 байта. Они должны быть: 0D0h, 0CFh, 11h, E0h.
Все остальное, не занятое заголовком, пространство составного файла разбито на четыре (не обязательно непрерывных!) области:
3.1. FAT «Больших Блоков»
Содержимое 4-байтовой строчки FAT может быть выбрано из следующих вариантов:
Если мы знаем номер стартового сектора для какого-либо объекта (например, для потока), мы легко можем вытянуть всю цепочку принадлежащих ему секторов. Вот конкретный пример, фрагмент дампа файла, содержащий начало FAT:
Давайте выпишем значения «строчек» таблицы в более удобной для глаза форме и в скобочках каждой строчке припишем ее номер:
Допустим, известно, что стартовый сектор потока имеет значение 0, т.е. этот сектор заведомо уже принадлежит потоку. В нулевой строчке читаем: следующий сектор потока имеет номер 1. Переходим к строчке номер 1, и т.д., окончательно получаем цепочку номеров секторов: <0, 1, 2, 5, 3, 6>. Именно так, именно в таком порядке и разместил MS Word фрагменты какого-то потока внутри составного файла!
Кстати, обратите внимание на вклинившиеся куски какого-то другого объекта, живущего в секторах <4, 7, 8>и на пустые сектора с адресами 0A и 0B. Вероятно, это свидетельство того, что над документом долго и мучительно работали: многократно удаляли и вставляли фрагменты текста, рисунки, формулы и т.п.
Осталось выяснить, как же до этой FAT добраться. Смотрим на заголовок составного файла: по смещению 4Ch живет адрес стартового сектора в составном файле, а количество этих секторов живет по смещению 2Ch. Сектора располагаются непрерывно.
3.2. FAT «Малых Блоков»
Хранить множество малоразмерных объектов в 512-байтных секторах довольно нерационально. Поэтому в структуре составного файла предусмотрена возможность размещать данные в маленьких, 64-байтовых «блочках» (small blocks).
Идеология расположения 64-байтовых «блочков» мало отличается от рассмотренной нами в предыдущем разделе для 512- байтовых «больших блоков». Под них выделяются область составного файла, отдельные «блочки» в которой пронумерованы с 0.
Но область «блочков» носит подчиненный характер. Она описана в «главной» FAT как отдельный объект, под который отведена отдельная цепочка секторов. Для нее же существует отдельная FAT, которая тоже фактически представлена цепочкой 512-байтовых секторов в «главной» FAT.
Числа в строчках «маленькой» FAT соответствуют не абсолютным номерам 512-байтовых секторов, а относительным номерам 64-байтовых «блочков» внутри своей области.
Местоположение стартового сектора FAT «блочков» берется из заголовка составного файла (смещение 3Сh), остальные можно вытянуть по цепочке в «главной» FAT. Не хватает только информации о том, где брать стартовый сектор области, распределенной под «блочки», но речь об этом пойдет в следующем разделе.
Кстати, область «блочков» в файле может просто отсутствовать.
3.3. Структура каталога
Каталог состоит из 128-байтовых записей следующей структуры:
Хотя записи в каталогах обычно называются термином «entry» (вхождение), разработчики OLE решили (IMHO, напрасно!) взглянуть на них с точки зрения объектного подхода и назвали их (а также те объекты, которые они описывают) словом «property» (свойство). Остается только сокрушенно покачать головой по этому поводу и продолжать называть вещи пусть и «нелегальными», но более понятными именами.
Имя объекта записывается латинскими буквами и хранится в формате UNICODE, т.е. для того, чтобы перевести его в удобочитаемую форму, потребуется собрать вместе все нечетные байты. Имейте в виду: самый первый символ имени может оказаться «нечитабельным» и иметь значение, например, 05h («трефы») или 01h («рожица»). Не пугайтесь, это так и должно быть. Фактическая длина имени хранится в записи по смещению 40h.
Местоположение объекта задается номером его стартового сектора (см. смещение 74h), под которым его и нужно искать в FAT. А фактическая длина объекта (например, потока) располагается по смещению 78h. Необходимо иметь в виду: если длина объекта >= 1000h = 4096, то он располагается в больших 512- байтовых блоках (т.е. в секторах). В противном случае значение по адресу 74h указывает на стартовый «блочок» в области 64-байтовых «блочков». Сама эта область представлена в каталоге как «Root Entry», и искать ее следует в FAT больших блоков (даже если ее длина J
К счастью, большинство читателей восприняли ту статью благожелательно. А правильней всех поступил С. Новодворский, который очень удачно углубил рассмотренную тему в своей собственной статье «Доступ к MD-файлам при помощи VBA», попутно исправив самые крупные мои неточности и дописав иллюстрирующий софт. Спасибо, Сергей, и так держать!
Итак, самая крупная неточность в моей статье касалась формата заголовка структурированного хранилища. Часть полей я тогда намеренно не описал (посчитав не важными), одно поле описал ошибочно, а о назначении некоторых полей в то время лишь смутно догадывался. Теперь я имею возможность привести новый, уточненный формат заголовка структурированного хранилища:
Не исключено, что и это еще не конец.
Выражаю благодарность Андрею Каримову, участнику проекта «Антивирус Stop!», в беседах с которым эта статья появилась на свет. Также ряд ценных критических замечаний сделал сотрудник «Лаборатории Касперского» Андрей Крюков.
Благодарю за внимание. С глубочайшим почтением и искреннейшей преданностию есмь, милостивые государи, Ваш покорный слуга
Статья написана специально для UInC (http://www.uinc.ru).
Все документы и программы на этом сайте собраны ТОЛЬКО для образовательных целей, мы не отвечаем ни за какие последствия, которые имели место как следствие использования этих материалов\программ. Вы используете все вышеперечисленное на свой страх и риск.
Любые материалы с этого сайта не могут быть скопированы без разрешения автора или администрации.
Форматы файлов, в которых можно сохранять документы
В таблице ниже перечислены различные виды документов, которые можно сохранять в приложении Word.
Документ Word (DOCX).
Используемый по умолчанию XML-формат документов Word 2008 для Mac, Word для Mac 2011, Word 2016 для Windows, Word 2007 для Windows, Word 2010 для Windows, Word 2013 для Windows и Word 2016 для Windows.
Документ Word 97–2004 (DOC)
Формат документов, совместимый с версиями от Word 98 до Word 2004 для Mac и от Word 97 до Word 2003 для Windows.
Сохранение документа в виде XML-шаблона, на базе которого можно создавать новые документы. Сохранение содержимого документа и его параметров, в том числе стилей, разметки страниц, элементов автотекста, пользовательских сочетаний клавиш и меню.
Шаблон Word 97–2004 (DOT)
Сохранение документа в виде шаблона, на основе которого можно создавать новые документы. Сохранение содержимого документа и его параметров, в том числе стилей, разметки страниц, элементов автотекста, пользовательских сочетаний клавиш и меню. Совместим с версиями Word 97–2003 для Windows и Word 98–2004 для Mac.
Экспорт содержимого и форматирования документа в формате, распознаваемом и читаемом другими приложениями, включая совместимые программы Майкрософт.
Экспорт содержимого документа в текстовый файл и сохранение текста без форматирования. Этот формат следует выбирать лишь в том случае, если целевая программа не способна читать файлы других доступных форматов. В этом формате используется расширенный набор символов ASCII для Mac.
Сохранение документа в формате, предназначенном для просмотра в Интернете. HTML — это стандартный веб-формат, который отображается в браузерах Macintosh и Windows.
Экспорт документа в PDF-файл, который выглядит одинаково на компьютерах Macintosh и Windows.
Документ Word с поддержкой макросов (DOCM)
Формат документов на основе XML, в котором сохраняется код макросов VBA. Макросы VBA выполняются в Word 2016 для Mac и Word для Mac 2011, но не в Word 2008.
Шаблон Word с поддержкой макросов (DOTM)
Сохранение документа в виде XML-шаблона с кодом макросов VBA. Макросы VBA выполняются в Word 2016 для Mac и Word для Mac 2011, но не в Word 2008.
XML-документ Word (XML)
Экспорт содержимого документа в XML-файл. Преобразование всех инструкций форматирования и текста в формат XML. Совместим с Word 2007 для Windows.
XML-документ Word 2003 (XML)
Экспорт содержимого документа в XML-файл. Преобразование всех инструкций форматирования и текста в формат XML. Совместим с Word 2003 для Windows.
Веб-страница в одном файле (MHT)
Сохранение документа в формате, предназначенном для просмотра в Интернете, с созданием единого файла со всеми элементами страницы, такими как графические объекты. Используется интернет-стандарт MIME HTML.
Шаблон документа Word (DOC)
Сохранение документа с пометкой «Шаблон» для системы поиска. При открытии такого файла будет открываться новый документ без названия.
Настраиваемый словарь (DIC)
Сохранение содержимого документа в качестве файла словаря, предназначенного для хранения слов и терминов, которые не входят в основной словарь.
Словарь исключений (DIC)
Сохранение содержимого документа в качестве файла словаря, предназначенного для хранения предпочтительных вариантов правильно написанных слов. Выбирайте этот вариант, если нужно сохранить в словаре исключений слово наподобие «нуль», чтобы приложение Word не помечало его как неправильно написанное.
Совместимый с Word 4.0–6.0/95 (RTF)
Этот формат RTF совместим с версиями от Word 4.0 до Word 6.0 для Mac, а также с Word 6.0 и Word 95 для Windows.
Сохранение шрифта, цветовой схемы и фона файла для использования в качестве новой темы.
Чтобы применить к документу тему из другого документа, на вкладке Главная в разделе Темы выберите команду Обзор тем. Чтобы сохранить измененную тему как новую, на вкладке Главная в разделе Темы выберите команду Сохранить тему.
Вопросы и ответы в сфере it технологий и настройке ПК
Форматы документов Microsoft Word — от старого DOC до нового DOCX
Документ Microsoft Word представляет собой файл с расширением doc или docx. До 90-х годов XX века расширение doc использовалось для текстовых файлов, которые не содержат разметки. И сейчас можно убедиться, что файл такого типа легко создать с помощью, например, программы «Блокнот». Правда, он не будет иметь богатого форматирования, но легко открывается программой Ворд, в которой набранному в блокноте тексту можно придать более оформленный вид. Когда корпорация Майкрософт выпустила программу Ворд, разработчики решили использовать в ней расширение doc. К настоящему времени этот текстовый редактор является самым популярным в мире, поэтому файл с расширением doc ассоциируют с этим программным продуктом и называют просто «вордовский формат».
Что такое формат?
Старый формат DOC отличался сложным содержанием, и нормально читался только в MS Office
В отличие от других текстовых форматов этого же производителя, например, RTF (Rich Text Format File), внутреннее содержание doc более сложное и запутанное. В формате Word сохраняется не только символьная информация, но и различные объекты (например, схемы, диаграммы, формулы, сценарии). Разработчики используют закрытый код, в котором посторонний разобраться не сможет. Чтобы увидеть «внутренности», файл можно открыть, например, в блокноте. В RTF любопытствующий увидит стройные ряды данных, похожие на содержание кода интернет-страниц. Во втором случае — пугающую кашу из знаков самого разнообразного происхождения и вида. В отличие от зашифрованного документа Word, файл RTF не содержит непечатных бинарных символов, легко открывается во всех текстовых и даже графических редакторах. В то же время практически ни одна программа (кроме соответствующего продукта от компании Майкрософт) не обладает способностью адекватно открыть для просмотра и редактирования документ Word. Сейчас офисный пакет этой компании очень широко распространен и доступен любому пользователю.
Современные форматы «вордовских» документов
Даже бесплатные современные редакторы документов умеют корректно работать с DOCX
Кроме того, сложности могут возникнуть даже при использовании разных версий офисного пакета Майкрософт. Дело в том, что начиная с версии Word 2007 года документ по умолчанию сохраняется с расширением не doc, а docx. Поэтому если установлен пакет офиса, выпущенный ранее, то необходима конвертация для устаревших версий. Конверторы для обеспечения совместимости предлагаются самой компанией Майкрософт (Microsoft Office Compatibility Pack), так что обычно при открытии docx, например, в версии Ворд 2003, сложностей не возникает, но при этом может потеряться часть данных форматирования и возможностей редактирования.
Компания Майкрософт была вынуждена пойти на изменение формата Word под давлением международных организаций, которые требовали подвести его под какие-либо стандарты. Был выбран вариант, основанный на языке разметки XML, который часто используется в документах интернет. Документ с расширением docx имеет некоторые преимущества перед устаревшим форматом, в частности, меньший вес, что имеет значение при передаче документа через интернет. Поэтому при ограниченных скоростях передачи данных пользователям имеет смысл использовать более современный формат Word.
Отблагодари меня, поделись ссылкой с друзьями в социальных сетях:
О сложности и жуткости вордовских файлов давно ходили легенды. Известно было, что формат этот крайне запутанный, а к тому же еще и полностью засекреченный, так что о половине тамошних полей можно было только догадываться.
Не скрою, что и меня эти файлы интересовали, но дальше первой страницы описания я так продвинуться и не смог. Однако незакрытый гештальт остался.
А теперь вот жизнь заставила (или подкинула возможность) все-таки разобраться во внутренностях всем хорошо известных документов, тем более, что в Штирлица теперь играть не обязательно, достаточно скачать с сайта «Майкрософта» официальные спецификации.
Что тут можно сказать? Невольно вспоминается старый пошлый анекдот: ну ужас. Ну просто ужас, но ведь не ужас-ужас-ужас.
Слава богу, что я разбирал эти файлы на Перле, а не на каком-нибудь автокоде Си. Высокий уровень языка и куча готовых библиотек (например, для чтения разных кодовых страниц) — это дар божий.
Так что работа в совокупности заняла неделю, и самым сложным было понять внутренний формат. Конечно, понимание это не совсем полное, потому что моей задачей было вытащить из документа одни тексты без всякого форматирования, но уж это я сделал тщательно.
Итак, как же устроены вордовские файлы?
Контейнер
Начнем с того, что это совсем не вордовские файлы, а некий универсальный контейнер, в который упакованы собственно документы. В такой контейнер засунуты все файлы Офиса, а, может быть, и еще что-нибудь.
Формат контейнера называется по-разному — docfile, ole storage, compound document file. Уже сам разнобой в названиях намекает на то, что он не сильно-то и нужен, поскольку действительно полезная вещь обычно имеет одно и четкое название. Основная его идея — иметь возможность запихать в один файл несколько других. Самым разумным было бы (как и сделали в OpenOffice) упаковать все в архив ZIP (можно без архивации, если она не нужна). Формат известен огромному множеству программ, компактен, легко разбирается. Но в «Майкрософте», очевидно, процветает синдром «Not invented here». Главное — изобрести велосипед, пусть и трехколесный, но собственный.
В принципе, формат OLE Storage достаточно разумен и не производит впечатление чего-то совсем идиотского. Но… он совершенно не нужен. Фактически, это что-то типа файловой системы FAT16, засунутой внутрь отдельного файла. Это не то что стрельба из пушки по воробьям, а истребление этих самых воробьев ядерными торпедами. В документе, внутри которого лежит несколько файлов, не нужна файловая система, находящаяся не менее чем на уровне ФС времен ДОС.
Итак, файлы CDF (как их называет юниксовская утилитка file) начинаются с заголовка. В заголовке отведено место под первую сотню записей ТРФ (таблицы размещения файлов, в просторечии — ФАТ). ФАТ там самый натуральный, очень похож на то, что можно найти на досовских дискетках. Остальные записи ТРФ лежат в отдельных секторах, соединенных связанным списком. Дополнительные сектора бывают только в больших (>7мб) файлах.
По табличке ФАТ/ТРФ можно собрать содержимое любого внутреннего файла (в терминологии МС он называется потоком, но это только запутывает дело), если знать, с какого блока он начинается. Начальные блоки разных структур написаны, естественно, в заголовке. Дальше из таблицы можно вытянуть всю цепочку секторов, в котором записано содержимое этого псевдофайла.
В частности, у CDF есть корневой каталог, физически размазанный по куче секторов. Это самый настоящий каталог, опять-таки, очень напоминающий старый досовский. Правда, для эффективности (или для выпендрежа) он выполнен не просто линейным списком, а сбалансированным двоичным деревом. Это значит, что в него без потери эффективности поиска можно записывать десятки тысяч отдельных записей. Зачем это нужно в файле, в котором записей бывает обычно штук пять, ну иногда, двадцать, ну максимум сто штук (презентация с огромным количество картинок) — знают только в Редмонде. Кстати, имена файлов в каталоге хранятся в UTF16 — тоже на всякий случай.
По каталогу можно определить начальный сектор любого файла и с помощью ТРФ вытянуть всю его цепочку размещения.
Поскольку размер блока немаленький (обычно 512 байт, по спецификации возможно также 4096), то при хранении мелких псевдофайлов, теоретически можно потерять много свободного места. Поэтому существует отдельное хранилище, поделенное на блочки по 64 байта. Хранилище опять-таки вытягивается по цепочке ФАТ.
Чтобы указать, какие блочки какому файлу принадлежат, существует отдельная табличка ФАТ или, вернее сказать, миниФАТ.
Итак, чтобы добраться до вордовского документа, надо сделать следующее:
1. Прочитать заголовок CDF 2. Загрузить в память ФАТ — таблицу размещения файлов, собрав ее по цепочке секторов. 3. Загрузить табличку МиниФАТ, собрав ее по цепочке ТРФ 4. Загрузить хранилище блочков, собрав ее по цепочке ТРФ 5. Загрузить корневой каталог, собрав ее по цепочке ТРФ 6. Разобрать каталог и преобразовать его во что-то читаемое 7. Найти в каталоге запись WordDocument 8. Если это маленький файл, то собрать его с помощью миниТРФ из хранилища для блочков. 9. Если большой, то вытянуть с диска сектора по цепочке ТРФ.
Каждый шаг сам по себе не особенно сложен, но в совокупности они вызывают исключительно недоумение. Зачем такие сложности? Почему нельзя было разместить после заголовка обыкновенный линейный каталог, а после него непрерывно, друг за другом записывать внутренние файлы?
Единственное, что можно предположить — все это сделано для того, чтобы была возможность дописывать подфайлы, не трогая начало основного файла. Надо заметить, что, во-первых, это не сильно востребованная операция, поскольку все программы обычно записывают документы от начала до конца в один проход. Исключение составляет только MS Word и то только в пресловутом режиме быстрого сохранения, проклятом пользователями. А во-вторых, даже в этих условиях все равно не получится не трогать начало основного файла, поскольку надо обновлять каталоги, ТРФ и заголовки.
В общем, «Майкрософт» в своем амплуа. Зачем делать просто, если можно сложно и запутанно?
WordDocument
Формат CDF при всей своей монструозности хотя бы логичен и не очень сложен (если сравнивать с остальным содержимым вордовского документа). Его описание занимает всего каких-то двадцать страниц — тьфу по сравнению с 300 страницами формата Ворда.
Формат документа сложно даже назвать форматом, гораздо больше к нему подойдет определение каменной летописи. Представьте себе такой каменный обрыв, на котором отпечаталось пятьдесят миллионов лет истории планеты. Вот мезозойский слой, вот кайнозойский, вот отпечаток крыла птеродактиля, а сверху уже третичные отложения. Примерно так же выглядит и документ изнутри.
Достаточно посмотреть на заголовок, который занимает чуть ли не треть файла. Заголовков целых три. Сначала идет один небольшой, в котором половина записей зияет дырами «Reserved» или «Not used». Раньше, в мезозое, там явно что-то лежало, но потом было выкинуто на свалку истории. Здесь же имеется версия записавшей программы, по которой в коде, похоже, выполняется огромный switch/case.
Затем идет второй заголовок, состоящий из шестнадцатибитовых слов. В нем нет вообще ничего полезного. В его начале прописан размер явно с таким расчетом, что здесь будут в будущем откладываться панцири простейших.
После этого идет третий заголовок, на этот раз современный, из длинных слов (32 бита). Он немерянной длины, в начале тоже указывает количество записей с прицелом на дальнейшее расширение, и в основном представляет собой список, где искать различные таблицы и куски файла — пары начало/размер. Сами таблицы, кстати, лежат не здесь, а в отдельном псевдофайле CDF под названием 0Table или 1Table (возможны варианты).
В первом заголовке написана длина самого текста и его начало. Очевидно, что во времена царя Гороха именно так его и можно было прочитать. Текст лежал одним большим куском. Забавно, что можно читать его так и сейчас, но… не всегда! На десять читаемых файлов найдется такой, у которого в середине окажутся невразумительные куски, в конце — сноски, которых там быть не должно, а в самом начале — большой кусок текста, который стерли в прошлом году. Кроме того, половина файла окажется написана китайскими иероглифами. Прискорбно заметить, что известная утилита catdoc Витуса Вагнера в некоторых случаях именно такие результаты и дает, из чего можно сделать вывод, что формат она разбирает недокорректно.
Начала этих потоков указаны в специальных местах заголовка, но самый первый заголовок почему-то показывает общую длину — не самого текста, а текста плюс все этих извращений. Вот и первая причина, почему в вывод многих утилит попадают надписи типа Page 1.
Где-то в архее в редактор добавили быстрое сохранение. Смысл его в том, что файл целиком не переписывается, а добавления и изменения просто дописываются в его конец, что теоретически должно быть быстрее. Предполагалось радовать этим пользователей, но фактически они остались недовольны. Особой разницы в скорости записи при этом не получается, но в файле образуется много мусора, причем из кусков, которые теоретически уже стерты. Если там была какая-нибудь секретная информация, то простым просмотром дампа файла ее можно легко обнаружить.
Для поддержки быстрого сохранения была заведена особая таблица огрызков (piece table), в которую записывается начало каждого куска и его адрес в файле. Длины нет, но ее можно высчитать, вычтя начало текущего куска из начала следующего. Однако тут тоже надо быть осторожным, поскольку огрызки перечисляются из всех потоков. Слава богу, что они идут в определенном порядке, поэтому, зная общую длину текста, легко вовремя остановиться.
Теоретически, этот сложный формат задействован только, если в заголовке установлен специальный флажок fComplex. Но… Вот на этом очередном «но» тоже прокалываются многие конверторы.
Уже в наше время в документы добавили возможность записи в Юникоде. При этом встала проблема (как по мне, надуманная): а ведь файлы получаются ровно в два раза длиннее. Поскольку ПО разрабатывают американцы, которые в душе вообще не верят в существование других азбук, и тайно считают, что всякие странные буквы бывают только в диссертациях про Древнюю Грецию, да и там встречаются только иногда, первое, что пришло им на ум — отделить чистые символы ASCII от грязных юникодовских. Первые писать по байту на символ, вторые — как получится.
Из этой идеи возникла, например, элегантная кодировка UTF-8, где двухбайтовые символы кодируются хитрыми последовательностями в духе кодирования Хаффмана. В «Майкрософте» сделали то же самое, только не так красиво. Раз уж у нас есть таблица огрызков, то запишем туда заодно и какие куски текста написаны в чистом ASCII (на самом деле сp1252), а какие — на всякого рода невразумительных алфавитах, требующих Юникода и, соответственно два байта на символ. Поэтому нынешние файлы всегда нужно разбирать с помощью таблицы кусков, невзирая на всякие там флажки. Юникодовские фрагменты там берутся как есть, только надо учитывать, что количество читаемых байтов должно быть в два раза больше количества читаемых символов. Однобайтовые фрагменты отмечаются в адресе установленным вторым слева старшим битом (почему не первым?). Чтобы узнать настоящий адрес, нужно этот бит сбросить, а адрес разделить на два (!).
Если учесть, что сама эта таблица огрызков тоже занимает место, а еще больше места в файле занимают разные двоичные деревья и таблички цепочек секторов от формата CDF, то размеры экономии текста на символах Юникода не поразят воображения даже в древнегреческих диссертациях. О файлах на великом и могучем языке и говорить нечего. Положили бы все в UTF-16 и не страдали. Ну заархивировали бы поток, раз уж так жаба давит.
После героических усилий по чтению текста, в нем самом, как ни странно, нет ничего сложного. Обычный текст (с поправкой на кодировку), кое-какие коды ниже пробела играют служебную роль. Например, 0х9 обозначает, как и положено, табуляцию, 0хА — конец страницы, 0х7 — конец ячейки таблицы и т.д. Единственная тонкость связана с полями. Начало содержимого поля обозначается как 0х13, конец поля — 0х15, имя и параметры поля отделяются символом 0х14 от того, что, собственно, видно в тексте пользователю. Но… Вторая часть может иметь в себе вложенное поле, чего многие программы не учитывают. В результате в тексте остаются огрызки вроде INCLUDEPICTURE или PAGEREF *.
Впрочем, есть еще одна мелкая пакость. Некоторые символы могут означать что-нибудь совсем другое, вроде текущей даты. Чтобы понять, простой это символ или нет, надо разбирать таблицы свойств символов, о которых ниже. Каюсь, я просто вырезал все символы с кодом ниже пробела, что не совсем достаточно, но дешево, быстро и практично.
Выдрав текст, дальше в формат я углубляться не стал. Это уже занятие для молодых и сильных духом — разобрать все эти таблицы с такими многообещающими названиями как CHP, PAPX, SHST, PLCF и все в том же духе. Занятие совсем уже для титанов — вопроизвести форматирование в точности, как это делает сам Ворд.
Кратко изложу только, что все хранится в специальных таблицах, входом в которые служит адрес символа с начала потока. Стили лежат в длинных списках, изменения в стилях — в специальных списках исключений. Локальные изменения стиля, например, при редактировании абзаца или символа хранятся в таблицах как специальные команды по изменению родительских таблиц стилей. Сами команды очень напоминают команды виртуальной машины от типичной игры-квеста.
Осталось только подвести мораль, а она банальна: что один человек придумал, то другой завсегда поломать может. Что не делает формат Ворда менее позорным, уродливым и совершенно неприспособленным для задач массового обмена информацией в гетерогенных системах.
Думаю, что «Майкрософт» столько лет его не открывала не потому, что боялась конкуренции, а просто потому что было… стыдно.