Опис формату конфігураційних файлів (CF, EPF, ERF)

Внутрішня структура конфігураційного файлу (*.cf) не є секретом. Добрі люди давно розібрали його та створили чимало цікавих утиліт, що дозволяють працювати з цим форматом. На Інфостарті знайдеться добрий десяток (якщо не більше) цікавих публікацій, які так чи інакше читають вміст *.cf файлів, так що тема ця зовсім не нова.

Однак, доброї, добротної документації на цей формат, на жаль, зовсім небагато. На написання цієї статті мене здивувала публікація шановного awa, який докладно, у всіх деталях описав структуру формату файлової ІБ 1С:Підприємства (*.1CD).

Передісторія

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

Термінологія

Перейдемо безпосередньо до теми нашого обговорення.

Щоб розставити точки над i, давайте визначимося з назвою самого формату.

По-перше, у цьому форматі створюються як файли конфігурації, але й файли зовнішніх звітів і обробок. В інтернеті мені траплялася назва Compound-файл. Можливо, воно є усталеним серед старожилів 1С, але мені воно не дуже подобається.

Дивимося всередину

Логічною одиницею зберігання даних усерединіконтейнера єдокумент. Документ це осмислений закінчений набір даних, який можна прочитати і якимось чином інтерпретувати. Я спеціально не користуюся терміном «файл », оскільки цю назву я прибережу для іншої сутності, про яку трохи пізніше.

Отже, загалом, файл CF (EPF/ERF) єконтейнер, в якому зберігаютьсядокументи.

контейнера

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

файлів

Структура контейнера

Контейнер включає наступні частини (по порядку):

  1. Заголовок контейнера
  1. Адреса першого порожнього блоку, до якого можна додавати дані
  2. Розмір блоку за замовчуванням
  3. Кількість файлів у контейнері
  • Документ змісту контейнера
  • Власне дані, які перераховані в змісті
  • Щоб прочитати вміст контейнера, необхідно прочитати документ змісту. Однак, оскільки,документ складається зблоків, то спочатку необхідно навчитися збирати повний документ із цих самих блоків.

    Структура блоку

    Усередині контейнера тут і там зустрічається чарівна константа, що позначає якусь "порожнечу" - це число 0x7fffffff.

    Константа 0x7fffffff - значення INT_MAX, тобто. максимальне значення 4-байтового цілого числа зі знаком.

    Логічні "файли"

    Я згадував, що термін «файл » я прибережу до кращих часів. Ці часи настали :)

    Вся конфігурація зберігається у контейнері якфайлов. Якщо ми згадаємо шкільний курс інформатики, то згадаємо, що «файл», казали нам – це іменований документ.

    Файл відрізняється від «документа» тим, що має ім'я, і ​​з цього імені до нього можна звертатися. Якщо ми будемо розбирати вміст конфігурації та будувати дерево метаданих, тознайдемо всередині файлів багато згадок інших файлів. Процедура читання конфігурації оперує іменамифайлів і посилається ними на ім'я.

    Якщо підбити підсумки, можна сказати наступне: у контейнері лежать різнідокументы, але з них мають ім'я. Такі документи називаються «файли » і вони мають не службовий, а безпосередньо прикладний характер. Самефайли зберігають інформацію про метадані конфігурації.

    Компоненти файлу

    Кожен файл складається з двохдокументів :

    1. Документ атрибутів, який містить ім'я файлу та дати створення/зміни
    2. Документ вмісту, який містить власне тіло файлу

    Зміст контейнера

    Тепер, коли всі складові озвучені, залишилося розглянути, мабуть, найголовніший документ контейнера – документ змісту, у якому зазначено розташування всіх файлів контейнера. Як говорилося вище, документ змісту це найперший документ контейнера і він іде відразу ж за заголовком контейнера.

    Зміст являє собою масив записів, кожна з яких вказує на файл . Оскільки файл складається з двох документів (атрибути та вміст), то запис змісту вказує на обидва з них. Запис змісту є три числа INT32:

    1. Адреса (зміщення у файлі) документа атрибутів
    2. Адреса (зміщення у файлі) документа вмісту
    3. Число 0x7fffffff (маркер кінця запису).

    файлів

    Нагадую, кожен документ може бути розбитий на блоки (фрагментований). Алгоритм складання документа з блоків буде розглянуто нижче.

    Особливості стиснення даних.

    Контейнер може містити різні файли. Як правило, це текстові файли у кодуванні UTF-8. Однак середфайлів контейнера можуть траплятися інші файли-контейнери. Найпростіше провести аналогію з файловою системою. Контейнер – це директорія, а файли усередині контейнера – це її вміст. Директорія може містити інші директорії.

    Кореневий каталог цієї "файлової системи" - це сам файл *.CF. Усередині нього можуть бути інші файли-контейнери, по суті – вкладені директорії, які зчитуються рівно за тим самим алгоритмом і мають однаково ту саму структуру.

    Проте є одна особливість кореневої директорії. Усідокументи вмісту файлів усередині кореневої директорії стиснуті за алгоритмом Deflate. Вміст файлів всередині вкладених директорій не стиснуто. Простіше кажучи, на верхньому рівні файла-контейнера тіла всіх файлів стиснуті, але якщо файл усередині контейнера сам є контейнером, то всередині нього файли вже будуть записані у чистому вигляді (без стиснення).

    Ланцюжок вільних блоків

    Короткий підсумок з теоретичної частини

    Давайте вже помацаємо байти

    Отже, настав час розглянути, наскільки безпосередньо влаштовані всі згадані вище сутності.

    Основним способом читання даних із контейнера є читання ланцюжка блоків, що становлять ті чи інші документи. Здається, що правильно розпочатиме з принципу читання блокових документів.

    Читання документа з блоків

    Кожен документ у контейнері обов'язково починається із заголовка блоку. При цьому документ може бути розбитий на кілька блоків. Щоб прочитати документ, необхідно його «зібрати» з блоків.

    Отже, заголовок блоку є рядок довжиною 31 байт. Рядок цей має такий вигляд:

    [CRLF][Розмір всього документа][Пробіл][Розмір поточного блоку][Пробіл][Адреса наступного блоку] [Пробіл] [CRLF], де:

    Відразу за заголовком блокуйде тіло блоку, яке має довжину, вказану в полі "Розмір поточного блоку".

    конфігураційних

    Розглянемо малюнок: довжина всього документа становить 0x54 байта, червоною рамкою виділено ці 0x54 байта. Це дані документа. Довжина блоку становить 0×200 байт, тобто. більше, ніж довжина самого документа. З цієї причини інші дані блоку становлять "нулі" простору, що не використовується. Значні байти – це ті, що позначені червоною рамкою.

    Читання необхідно продовжувати до тих пір, поки в полі "Адреса наступного блоку" не зустрінеться значення 0x7fffffff або поки не буде зчитано кількість байт, вказану в полі "Розмір всього документа".

    Поле «Розмір всього документа» має сенс лише першого блоку. У всіх наступних блоках документа має значення 0x00000000.

    Формат заголовка контейнера

    Заголовок контейнера має довжину 16 байт і складається з наступних полів: