Опис формату конфігураційних файлів (CF, EPF, ERF)
Внутрішня структура конфігураційного файлу (*.cf) не є секретом. Добрі люди давно розібрали його та створили чимало цікавих утиліт, що дозволяють працювати з цим форматом. На Інфостарті знайдеться добрий десяток (якщо не більше) цікавих публікацій, які так чи інакше читають вміст *.cf файлів, так що тема ця зовсім не нова.
Однак, доброї, добротної документації на цей формат, на жаль, зовсім небагато. На написання цієї статті мене здивувала публікація шановного awa, який докладно, у всіх деталях описав структуру формату файлової ІБ 1С:Підприємства (*.1CD).
Передісторія
Як уже говорилося вище, структура формату давно відома і в мережі є інформація щодо його структури (хоча, досить мізерна). Мені ця інформація знадобилася при розробці програми V8Viewer, працюючи над якою, я спирався на такі матеріали:
Термінологія
Перейдемо безпосередньо до теми нашого обговорення.
Щоб розставити точки над i, давайте визначимося з назвою самого формату.
По-перше, у цьому форматі створюються як файли конфігурації, але й файли зовнішніх звітів і обробок. В інтернеті мені траплялася назва Compound-файл. Можливо, воно є усталеним серед старожилів 1С, але мені воно не дуже подобається.
Дивимося всередину
Логічною одиницею зберігання даних усерединіконтейнера єдокумент. Документ це осмислений закінчений набір даних, який можна прочитати і якимось чином інтерпретувати. Я спеціально не користуюся терміном «файл », оскільки цю назву я прибережу для іншої сутності, про яку трохи пізніше.
Отже, загалом, файл CF (EPF/ERF) єконтейнер, в якому зберігаютьсядокументи.

Кожен документ всередині контейнера може бути розбитий на блоки. Мінімальною фізичною одиницею зберігання даних єблок, але осмисленою логічною одиницею єдокумент. Іншими словами, документи всередині контейнера можуть лежати у вигляді розрізнених шматочків (блоків ) і для того, щоб прочитати вміст документа, всі його шматочки потрібно зібрати та об'єднати.

Структура контейнера
Контейнер включає наступні частини (по порядку):
- Заголовок контейнера
- Адреса першого порожнього блоку, до якого можна додавати дані
- Розмір блоку за замовчуванням
- Кількість файлів у контейнері
Щоб прочитати вміст контейнера, необхідно прочитати документ змісту. Однак, оскільки,документ складається зблоків, то спочатку необхідно навчитися збирати повний документ із цих самих блоків.
Структура блоку
Усередині контейнера тут і там зустрічається чарівна константа, що позначає якусь "порожнечу" - це число 0x7fffffff.
Константа 0x7fffffff - значення INT_MAX, тобто. максимальне значення 4-байтового цілого числа зі знаком.
Логічні "файли"
Я згадував, що термін «файл » я прибережу до кращих часів. Ці часи настали :)
Вся конфігурація зберігається у контейнері якфайлов. Якщо ми згадаємо шкільний курс інформатики, то згадаємо, що «файл», казали нам – це іменований документ.
Файл відрізняється від «документа» тим, що має ім'я, і з цього імені до нього можна звертатися. Якщо ми будемо розбирати вміст конфігурації та будувати дерево метаданих, тознайдемо всередині файлів багато згадок інших файлів. Процедура читання конфігурації оперує іменамифайлів і посилається ними на ім'я.
Якщо підбити підсумки, можна сказати наступне: у контейнері лежать різнідокументы, але з них мають ім'я. Такі документи називаються «файли » і вони мають не службовий, а безпосередньо прикладний характер. Самефайли зберігають інформацію про метадані конфігурації.
Компоненти файлу
Кожен файл складається з двохдокументів :
- Документ атрибутів, який містить ім'я файлу та дати створення/зміни
- Документ вмісту, який містить власне тіло файлу
Зміст контейнера
Тепер, коли всі складові озвучені, залишилося розглянути, мабуть, найголовніший документ контейнера – документ змісту, у якому зазначено розташування всіх файлів контейнера. Як говорилося вище, документ змісту це найперший документ контейнера і він іде відразу ж за заголовком контейнера.
Зміст являє собою масив записів, кожна з яких вказує на файл . Оскільки файл складається з двох документів (атрибути та вміст), то запис змісту вказує на обидва з них. Запис змісту є три числа INT32:
- Адреса (зміщення у файлі) документа атрибутів
- Адреса (зміщення у файлі) документа вмісту
- Число 0x7fffffff (маркер кінця запису).

Нагадую, кожен документ може бути розбитий на блоки (фрагментований). Алгоритм складання документа з блоків буде розглянуто нижче.
Особливості стиснення даних.
Контейнер може містити різні файли. Як правило, це текстові файли у кодуванні UTF-8. Однак середфайлів контейнера можуть траплятися інші файли-контейнери. Найпростіше провести аналогію з файловою системою. Контейнер – це директорія, а файли усередині контейнера – це її вміст. Директорія може містити інші директорії.
Кореневий каталог цієї "файлової системи" - це сам файл *.CF. Усередині нього можуть бути інші файли-контейнери, по суті – вкладені директорії, які зчитуються рівно за тим самим алгоритмом і мають однаково ту саму структуру.
Проте є одна особливість кореневої директорії. Усідокументи вмісту файлів усередині кореневої директорії стиснуті за алгоритмом Deflate. Вміст файлів всередині вкладених директорій не стиснуто. Простіше кажучи, на верхньому рівні файла-контейнера тіла всіх файлів стиснуті, але якщо файл усередині контейнера сам є контейнером, то всередині нього файли вже будуть записані у чистому вигляді (без стиснення).
Ланцюжок вільних блоків
Короткий підсумок з теоретичної частини
Давайте вже помацаємо байти
Отже, настав час розглянути, наскільки безпосередньо влаштовані всі згадані вище сутності.
Основним способом читання даних із контейнера є читання ланцюжка блоків, що становлять ті чи інші документи. Здається, що правильно розпочатиме з принципу читання блокових документів.
Читання документа з блоків
Кожен документ у контейнері обов'язково починається із заголовка блоку. При цьому документ може бути розбитий на кілька блоків. Щоб прочитати документ, необхідно його «зібрати» з блоків.
Отже, заголовок блоку є рядок довжиною 31 байт. Рядок цей має такий вигляд:
[CRLF][Розмір всього документа][Пробіл][Розмір поточного блоку][Пробіл][Адреса наступного блоку] [Пробіл] [CRLF], де:
Відразу за заголовком блокуйде тіло блоку, яке має довжину, вказану в полі "Розмір поточного блоку".

Розглянемо малюнок: довжина всього документа становить 0x54 байта, червоною рамкою виділено ці 0x54 байта. Це дані документа. Довжина блоку становить 0×200 байт, тобто. більше, ніж довжина самого документа. З цієї причини інші дані блоку становлять "нулі" простору, що не використовується. Значні байти – це ті, що позначені червоною рамкою.
Читання необхідно продовжувати до тих пір, поки в полі "Адреса наступного блоку" не зустрінеться значення 0x7fffffff або поки не буде зчитано кількість байт, вказану в полі "Розмір всього документа".
Поле «Розмір всього документа» має сенс лише першого блоку. У всіх наступних блоках документа має значення 0x00000000.
Формат заголовка контейнера
Заголовок контейнера має довжину 16 байт і складається з наступних полів: