Формат .manifest

Для тех, кто хочет сделать мир лучше.
Сообщение
Автор
Аватара пользователя
wowks
Майор
Майор
Сообщения: 525
Зарегистрирован: 09.12.2008
Благодарил (а): 67 раз
Поблагодарили: 37 раз

#1 Сообщение 16.09.2015, 02:05

Выкладываю информацию по структуре .manifest файлов и код для их чтения
Для начала в качестве примера и в дополнение к коду ниже вырезка из PAYLOAD блока,
что лежит в начале .manifest-а 71_778444776426820105.manifest - Half-Life Base Content (Sep 05 2013)

Код: Выделить всё

d0 17 f6 71 - PAYLOAD id
ae 05 00 00 - размер PAYLOAD (не закодирован)
0a 73 0a - размер текущего блока информации о файле (между двумя 0x0a)
18 - длина строки 
76 61 6c 76 65 5c 63 6c 5f 64 6c 6c 73 5c 63 6c 69 65 6e 74 2e 64 6c 6c - строка "valve\cl_dlls\client.dll"
10 - байт конца строки
80 bc 26 - закодированный размер файла. Его можно получить по формуле в коде ниже
18 - разделитель
00 - флаг типа файла. В данном случае "обычный файл"
22 - разделитель 
14 - предположительно размер sha1. Всегда равен 20 (в десятичом виде)
b1 9f 9b 59 5d af 67 3f 05 6d 81 af 24 ce 09 5a 3b 99 f4 b4 - SHA1 строки "valve\cl_dlls\client.dll"
2a 14 - разделитель
e8 76 2b 8d ac 01 15 fb e0 73 46 3a 9c b4 06 38 df 0b 07 7d - SHA1 файла. Если папка, то заполнен нулями

прочие данные
32 25 0a 14 e8 76 2b 8d ac 01
15 fb e0 73 46 3a 9c b4 06 38 df 0b 07 7d 15 85
39 10 2c 18 00 20 80 bc 26 28 80 9d 0f 0a 75 0a
1a 76 61 6c 76 65 5c 63 6c 5f 64 6c 6c 73 5c 63
6c 69 65 6e 74 2e 64 79 6c 69 62 10 b4 93 38 18
00 22 14 ad 12 c2 d3 3d bc 52 42 47 2c 77 a8 91
66 b7 c1 dc af 3f 10 2a 14 98 ce a8 c0 e2 d9 a5
aa 22 f2 fb f6 cd 8f db 7f cb db 3a b4 32 25 0a
14 98 ce a8 c0 e2 d9 a5 aa 22 f2 fb f6 cd 8f db
7f cb db 3a b4 15 ba 91 9e 82 18 00 20 b4 93 38
28 c0 9b 12
METADATA, который следует следующим

Код: Выделить всё

be 12 48 1f - METADATA id
2c 00 00 00 - размер METADATA 

08 - разделитель 
47 - depot id = 71 из "71_778444776426820105"
10  - разделитель 
89 a4 b7 c1 e5 ec e5 e6 0a - depot version = 778444776426820105 из "71_778444776426820105"
18 - разделитель 
a7 ad 9e 91 05 - last update - Unix время
20 - разделитель
00 - булев значение filenames encrypted
28 - разделитель
прочие данные
9d f3 b1 07 30 d0 c6 e2 02 38 13 40 fb e9 f1 a0 02 48 c8
f0 b0 fc 04
а теперь код. В нём и в комментариях к нему вся основная информация темы.
рекомендуемый порядок чтения комментариев и алгоритма:
load_content_manifest(...)
encode_payload(...)
encode_metadata(...)

"content_manifest.h"
[syntax lang="c" lines="100"]
/*
* загрузка .manifest файла
* Автор: wowks ( memberlist.php?mode=viewprofile&u=59383 )
* Благодаря информации из
* https://wiki.singul4rity.com/steam:file ... s:manifest
* http://cs.rin.ru/forum/viewtopic.php?f= ... =.manifest
*
* и Pr0Ger ( memberlist.php?mode=viewprofile&u=63369 )
*/


#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#ifndef ___MANIFET___
#define ___MANIFET___



#define MANIFEST_CHUNK_PAYLOAD_ID 0x71F617D0
#define MANIFEST_CHUNK_METADATA_ID 0x1F4812BE
#define MANIFEST_CHUNK_SIGNATURE_ID 0x1B81B817


// MANIFEST_PAYLOAD_FILE_INFO.file_type:
#define MANIFEST_PAYLOAD_REGULAR_FILE 0
#define MANIFEST_PAYLOAD_USER_CONFIG 1
#define MANIFEST_PAYLOAD_VERSIONED_USER_CONFIG 2
#define MANIFEST_PAYLOAD_ENCRYPTED 4
#define MANIFEST_PAYLOAD_READ_ONLY 8
#define MANIFEST_PAYLOAD_HIDDEN 16
#define MANIFEST_PAYLOAD_EXECUTABLE 32
#define MANIFEST_PAYLOAD_DIRECTORY 64
//
#define SHA1_SIZE 20

typedef struct {
char filename[MAX_PATH];
int32_t filesize;
uint8_t file_type;
uint8_t sha1_filename[SHA1_SIZE];
uint8_t sha1_filedata[SHA1_SIZE];
} MANIFEST_PAYLOAD_FILE_INFO;



typedef struct {
int32_t depot_id;
int64_t depot_version;
time_t last_updated;
bool filenames_encrypted;
} MANIFEST_METADATA;



typedef void (* ___content_manifest_load_callback_fn) (const uint32_t manifest_chunk_id, const void * data, bool * cancel_load,
const void * optional_ptr);
// возвращаемые значения load_content_manifest
#define LOAD_CONTENT_MANIFEST___SUCCESS 0
#define LOAD_CONTENT_MANIFEST___FILE_ERROR 1
#define LOAD_CONTENT_MANIFEST___LOAD_CANCELED 2
//
int load_content_manifest(const wchar_t * manifest_filename, ___content_manifest_load_callback_fn callback,
const void * optional_ptr = NULL);

#endif
[/syntax]

"content_manifest.cpp"
[syntax lang="c" lines="100"]

#include "content_manifest.h"

#define ___varint_to_int(varint, varint_len, result_int) \
result_int = 0; \
for (register uintptr_t x = (varint_len - 1); x >= 1; x--) { \
result_int += (varint[x] - 1); \
result_int *= 128; \
} \
result_int += varint[0]; \

#define ___get_varint_len(varint, result_len) \
result_len = 0; \
do { \
result_len++; \
} while (varint[result_len - 1] & 0x80); \



typedef struct {
uint32_t id;
uint32_t size;
} CONTENT_MANIFEST_CHUNK;



static inline void encode_payload(const void * payload, MANIFEST_PAYLOAD_FILE_INFO * file_info ) {
memset(file_info, 0, sizeof(MANIFEST_PAYLOAD_FILE_INFO));
register uint8_t * ptr = (uint8_t *) payload;
register size_t varint_len;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ptr += 1; // пропуск закрывающего байта 0x0A

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 1. строка относительного пути файла.
uint8_t filename_len = *ptr; // cледущий байт после закрывающего 0x0A содержит длину стоки. А после него идёт сама строка. Считываем этот байт
ptr += 1; // пропуск байта с длиной стоки
strncpy(file_info->filename, (char const *) ptr, filename_len);
file_info->filename[filename_len] = '\0';
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ptr += filename_len; // пропуск строки
ptr += 1; // после строки всегда идёт байт 0x10, а после него размер файла. Пропускаем этот байт

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 2. размер файла.
// Он может быть длиной от 1 до нескольких байт. Чтобы не выводить формулы, объясню на примерах
// (значения в десятичной системе)
// 1 байт
// всё просто - этот байт содержит сам размер.
// ////////////////////////////////////////////////////////////////////////////////////////
// 2 байта {248, 20}
// 20 - 1 = 19
// размер = 248 + (19 * 128)
// ////////////////////////////////////////////////////////////////////////////////////////
// 3 байта {128, 188, 38}
// 188 - 1= 187 38 - 1= 37
// 37 * 128 = 4736
// (187 + 4736) * 128 = 630144
// размер = 128 + 630144
// ////////////////////////////////////////////////////////////////////////////////////////
// 4 байта {216, 211, 148, 2}
// 211 - 1 = 210 148 - 1 = 147 2 - 1 = 1
// 1 * 128 = 128
// (147 + 128) * 128 = 35200
// (210 + 35200) * 128 = 4532480
// размер = 216 + 4532480
//
// Возможно, есть и 5 и более байт для размера, но я с ними не сталкивался в пределах манифестов для игр
// Valve, на основе анализа которых я писал этот код.
___get_varint_len(ptr, varint_len);
___varint_to_int(ptr, varint_len, file_info->filesize);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ptr += varint_len; // пропуск filesize
ptr += 1; // пропуск 0x18

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 3. тип файла
file_info->file_type = *ptr;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ptr += 1; // пропуск типа файла
ptr += 2; // пропуск 0x22 и 0x14

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 4. sha1 относительного пути
memcpy(&file_info->sha1_filename[0], ptr, SHA1_SIZE);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ptr += SHA1_SIZE; // пропуск sha1 относительного пути
ptr += 2; // пропуск 0x2A, 0x14

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 5. sha1 файла
// для папки этот sha1 по понятным причинам заполнен нулями и копировать его нет смысла
if ((file_info->file_type & MANIFEST_PAYLOAD_DIRECTORY) != MANIFEST_PAYLOAD_DIRECTORY) {
memcpy(&file_info->sha1_filedata[0], ptr, SHA1_SIZE);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// ptr += SHA1_SIZE; // пропуск sha1 файла
// пропуск чего-то ещё

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 5. далее могут идти какие-то данные, и иногда много, но их оставляю
// ...

return;
}



static inline void encode_metadata(const void * metadata, MANIFEST_METADATA * manifest_info ) {
memset(manifest_info, 0, sizeof(MANIFEST_METADATA));
register uint8_t * ptr = (uint8_t *) metadata;
register size_t varint_len;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ptr += 1; // пропуск 0x08

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// пример имени "depotcache\[depot_id]_[depot_version].manifest"
// струтура начала такая:
// 0x08
// varint [depot_id]
// 0x10
// varint [depot_version]
// 0x18
// varint [last_updated]
// 0x20
// (bool)uint8_t [filenames_encrypted]
// 0x28
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 1. depot_id
___get_varint_len(ptr, varint_len);
___varint_to_int(ptr, varint_len, manifest_info->depot_id);
////////////////////////////////////////////////////////////////////

ptr += varint_len; // пропуск depot_id
ptr += 1; // пропуск 0x10

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 2. depot_version
___get_varint_len(ptr, varint_len);
___varint_to_int(ptr, varint_len, manifest_info->depot_version);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ptr += varint_len; // пропуск байт depot_version
ptr += 1; // прпуск 0x18

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 3. last_updated
___get_varint_len(ptr, varint_len);
___varint_to_int(ptr, varint_len, manifest_info->last_updated); //
//////////////////////////////////////////////////////////

ptr += varint_len; // пропуск байт last_updated
ptr += 1; // пропуск 0x20

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 4. filenames_encrypted
manifest_info->filenames_encrypted = *ptr;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// ptr += 1; // пропуск filenames_encrypted
// ptr += 1; // пропуск 0x28

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// дальше должны идти:
// varint [cb_disk_original]
// 0x30
// varint [cb_disk_compressed]
// 0x38
// varint [unique_chunks]
// 0x40
// varint [crc_encrypted]
// 0x48
// varint [crc_clear]
// MANIFEST_CHUNK_SIGNATURE_ID
// но на мой взгляд эти данные не важны

return;
}



int load_content_manifest(const wchar_t * manifest_filename, ___content_manifest_load_callback_fn callback,
const void * optional_ptr) {

int res = LOAD_CONTENT_MANIFEST___FILE_ERROR;
bool cancel_load = false;

FILE * f = _wfopen(manifest_filename, L"rb");
if (f != NULL) {
CONTENT_MANIFEST_CHUNK manifest_chunk;

// PAYLOAD
if (fread(&manifest_chunk, sizeof(CONTENT_MANIFEST_CHUNK), 1, f) == 1) {
if (manifest_chunk.id == MANIFEST_CHUNK_PAYLOAD_ID) {
if (manifest_chunk.size > 0) {
void * payload = malloc(manifest_chunk.size);
if (payload != NULL) {
if (fread(payload, manifest_chunk.size, 1, f) == 1) {
register uint8_t * ptr = (uint8_t *) payload;
MANIFEST_PAYLOAD_FILE_INFO file_info;
do {
// получаем размер участка информации о файле (file_info_size)
// это varint!
// он экранируется 2-мя байтами со значением 0x0A. Пример для 1 и 2 байтной цепочки:
// 1 байт
// [0x0A, размер, 0x0A]
// размер участка = размер
// 2 байта
// [0x0A, размер, инкремент_размера, 0x0A]
// размер участка = размер + ((инкремент_размера - 1) * 128)
//
ptr += 1; // пропуск 0x0A
manifest_chunk.size -= 1;

// узнать длину цепочки байт varint можно в цикле, проверяя выставлен ли самый младший (8-й) бит в значение 1.
// Для этого есть макрос ___get_varint_len(varint, result_len)
size_t varint_len;
___get_varint_len(ptr, varint_len);
// Для получения значения из varint по его длине есть другой макрос ___varint_to_int(varint, varint_len, result_int)
size_t file_info_size;
___varint_to_int(ptr, varint_len, file_info_size);

// блок данных начинается с закрывающего 0x0A включительно
// перематываем до него
ptr += varint_len;
manifest_chunk.size -= varint_len;

encode_payload(ptr, &file_info);
callback(MANIFEST_CHUNK_PAYLOAD_ID, &file_info, &cancel_load, optional_ptr);
if (cancel_load) {
res = LOAD_CONTENT_MANIFEST___LOAD_CANCELED;
break;
}

ptr += file_info_size;
manifest_chunk.size -= file_info_size;

} while (manifest_chunk.size > 0);

free(payload);

if ( ! cancel_load) {
goto load_metadata_chunk;
}
} else {
free(payload);
}
}
}
}
}

goto finish;

// METADATA
load_metadata_chunk:
if (fread(&manifest_chunk, sizeof(CONTENT_MANIFEST_CHUNK), 1, f) == 1) {
if (manifest_chunk.id == MANIFEST_CHUNK_METADATA_ID) {
if (manifest_chunk.size > 0) {
void * metadata = malloc(manifest_chunk.size);
if (metadata != NULL) {
if (fread(metadata, manifest_chunk.size, 1, f) == 1) {
MANIFEST_METADATA manifest_info;

encode_metadata(metadata, &manifest_info);
callback(MANIFEST_CHUNK_METADATA_ID, &manifest_info, &cancel_load, optional_ptr);
if (cancel_load) {
res = LOAD_CONTENT_MANIFEST___LOAD_CANCELED;
}

free(metadata);

if ( ! cancel_load) {
goto load_signature_chunk;
}
} else {
free(metadata);
}
}
}
}
}


goto finish;

// SIGNATURE (есть не всегда)
load_signature_chunk:
/*
// не встречал манифесты, гле этот блок содержит данные, и не знаю важны ли они
if (fread(&manifest_chunk, sizeof(CONTENT_MANIFEST_CHUNK), 1, f) == 1) {
if (manifest_chunk.id == MANIFEST_CHUNK_SIGNATURE_ID) {
if (manifest_chunk.size > 0) {
void * signature = malloc(manifest_chunk.size);
if (signature != NULL) {
if (fread(signature, manifest_chunk.size, 1, f) == 1) {
//
// ...
//
free(signature);
} else {
free(signature);
goto finish;
}
}
}
}
}
*/

// и после SIGNATURE лежит uint32_t со значением 0x32C415AB, означающим конец

res = LOAD_CONTENT_MANIFEST___SUCCESS;

finish:
fclose(f);
}

return res;
}
[/syntax]

"main.cpp" (пример)
[syntax lang="c" lines="100"]
///////////////////// пример ////////////////////////////
#include "content_manifest.h"
#include <time.h>

typedef struct {
const char * root_path;
uint32_t file_nr;
} MY_CUSTOM_DATA;

void ManifestCallback(const uint32_t manifest_chunk_id, const void * data, bool * calcel_load,
const void * optional_ptr) {

if (manifest_chunk_id == MANIFEST_CHUNK_PAYLOAD_ID) {
MANIFEST_PAYLOAD_FILE_INFO * file_info = (MANIFEST_PAYLOAD_FILE_INFO *) data;
MY_CUSTOM_DATA * custom_data = (MY_CUSTOM_DATA *) optional_ptr;

custom_data->file_nr++;
printf("%4d) File: \"%s\\%s\"\n",
custom_data->file_nr,
(char *) custom_data->root_path,
file_info->filename);

printf(" Size: %d\n", file_info->filesize);
printf(" Type: %s\n", ((file_info->file_type & MANIFEST_PAYLOAD_DIRECTORY) ? "directory" : "file"));

#define ___print_sha1(pre_str, sha1_ptr, post_str) \
printf("%s", pre_str); \
for (uintptr_t x = 0; x < 20; x++) { \
printf("%x", sha1_ptr[x]); \
} \
printf("%s", post_str); \

___print_sha1(" SHA1 File Name:", file_info->sha1_filename, "\n");
___print_sha1(" SHA1 File Data:", file_info->sha1_filedata, "\n");

return;
}

if (manifest_chunk_id == MANIFEST_CHUNK_METADATA_ID) {
MANIFEST_METADATA * manifest_info = (MANIFEST_METADATA *) data;

printf("\n======================================================================\n");
printf("Depot Id: %d\n", manifest_info->depot_id);
printf("Depot Version: %llu\n", manifest_info->depot_version);
printf("Last Updated: %s", ctime(&manifest_info->last_updated));
printf("FileNames Encrypted: %s\n", (manifest_info->filenames_encrypted ? "TRUE" : "FALSE"));

return;
}

return;
}




int main() {

MY_CUSTOM_DATA custom_data;
custom_data.root_path = "Steam\\SteamApps\\common\\Portal 2";
custom_data.file_nr = 0;

switch (load_content_manifest(L"C:\\621_1861956698869753646.manifest", ManifestCallback, &custom_data)) {
case LOAD_CONTENT_MANIFEST___SUCCESS: MessageBoxA(NULL, "OK", "", MB_OK);
break;

case LOAD_CONTENT_MANIFEST___LOAD_CANCELED: MessageBoxA(NULL, "CANCELED", "", MB_OK);
break;

case LOAD_CONTENT_MANIFEST___FILE_ERROR: MessageBoxA(NULL, "FILE ERROR", "", MB_OK);
break;

default:
break;
}

return 0;
}
[/syntax]

*архив с исходником (это проект для CLion): https://mega.nz/#!94A3hBLC!R9MUOiMfTcn7 ... 3ITm8LGLpw



этой информации достаточно для:
- проверки целостности кеша, и сравнения с другиим кешем
- создание патчей
- если файлы игр однго движка свалены в одну папку можно по списку из manifest отделить их (игры) и их части (например озвучку) друг от друга
- по depot_id можно понять что это такое. Например в данном случае 71 - Half-Life Base Content
Последний раз редактировалось wowks 18.09.2015, 17:29, всего редактировалось 16 раз.

Аватара пользователя
Pr0Ger
Модератор
Модератор
Сообщения: 1829
Зарегистрирован: 16.01.2009
Благодарил (а): 17 раз
Поблагодарили: 214 раз
Контактная информация:

#2 Сообщение 16.09.2015, 10:26

Чисто случайно вчера вечером тоже смотрел этот формат. Там по сути идет подряд 2-3 блока вида type-size-payload, где этот самый payload является сериализованной протобафом структурой. Собственно у тебя сделан парсинг протобафа в частном виде, который может сломаться когда они решат добавить пару полей в структуру, что не очень хорошо.
Насчет того как парсишь значение размера, судя по всеми понимал сам и понял идею правильно, там кстати до 10 байт оно может быть (если записывать int64 в поле), вот я какое-то время назад писал парсинг https://github.com/Pr0Ger/protobuf3/blo ... age.py#L73

Аватара пользователя
wowks
Майор
Майор
Сообщения: 525
Зарегистрирован: 09.12.2008
Благодарил (а): 67 раз
Поблагодарили: 37 раз

#3 Сообщение 16.09.2015, 11:46

Pr0Ger
Pr0Ger &raquo; Ср сен 16, 2015 8:26 am писал(а):Собственно у тебя сделан парсинг протобафа в частном виде, который может сломаться когда они решат добавить пару полей в структуру, что не очень хорошо.
ну, нужно просто смотреть. Если что-то поменяют, то сравнить со старым и проанализировать.

Предположение: в том виде в котором он, формат, есть сейчас в начале идут самые важные данные - имя файла, размер, sha, и если они добавят доп. нифу, то скорее всего в конец, который я игнорирую, тк там идут (а иногда и нет) каие-то данные в разном количестве.

Вообще эта их гибкая структура с разделителями очень неоднозначная.
Вот например: размер_разной_длины, разделитель_0x10, размер_разной_длины
и может быть такое - ...0x10, 0x10, 0x10... и фиг поймёшь где этот разделитель, тк длины размеров по бокам разные
лучше бы тут была фиксированная структура, без этих множителей - просто инты(16, 32, 64)
Pr0Ger &raquo; Ср сен 16, 2015 8:26 am писал(а):Насчет того как парсишь значение размера, судя по всеми понимал сам и понял идею правильно
да долго понимал, с калькулятором, пока не наткнулся на простую запись типа (остаток, инкремент = 2) и благодаря 2-1 вышел на 128.
потом долго зависал над 3-мя байтами (остаток, инкремент, инкремент_инкремента) пока не угадал = остаток + ( (инкремент+(инкремент_инкремента * 128))*128 )
а дальше уже по аналогии :)
Pr0Ger &raquo; Ср сен 16, 2015 8:26 am писал(а):там кстати до 10 байт оно может быть (если записывать int64 в поле)
Вот опять странное решение. Запись 9-ю, 10-ю байтми числа, которое умещяется в 8-байт int64
У меня там цикл под макросом ___encode_int_value(value_ptr, value_len, result), и можно отправлять в него запись какой угодно длины

MOZGIII
Разработчик
Разработчик
Сообщения: 910
Зарегистрирован: 09.01.2009
Откуда: Переезжаю в /dev/null
Благодарил (а): 7 раз
Поблагодарили: 65 раз
Контактная информация:

#4 Сообщение 16.09.2015, 12:26

Я тут подумал вот над чем: можно, наверное, реализовать какой-то алгоритм, который будет выявлять структуру в поданных на вход данных. Что-то для автоматизации веселухи с калькуляторами.
Никто не встречал такого уже готового или в процессе реализации?

Аватара пользователя
Pr0Ger
Модератор
Модератор
Сообщения: 1829
Зарегистрирован: 16.01.2009
Благодарил (а): 17 раз
Поблагодарили: 214 раз
Контактная информация:

#5 Сообщение 16.09.2015, 13:14

wowks &raquo; Сентябрь 16th, 2015, 11:46 am писал(а):Предположение: в том виде в котором он, формат, есть сейчас в начале идут самые важные данные - имя файла, размер, sha, и если они добавят доп. нифу
В какой то мере прав, самые важные поля размещают с более низким id поля, но дока протобафа говорит о том что поля в конечной структуре могут находится в произвольном порядке, хотя и рекомендуется размешение в правильном порядке.
wowks &raquo; Сентябрь 16th, 2015, 11:46 am писал(а):лучше бы тут была фиксированная структура, без этих множителей - просто инты(16, 32, 64)
Это там есть, всякие типа fixed32, fixed64, но обычно использование varint дает более компактный результат
wowks &raquo; Сентябрь 16th, 2015, 11:46 am писал(а):да долго понимал, с калькулятором, пока не наткнулся на простую запись типа (остаток, инкремент = 2) и благодаря 2-1 вышел на 128.
потом долго зависал над 3-мя байтами (остаток, инкремент, инкремент_инкремента) пока не угадал = остаток + ( (инкремент+(инкремент_инкремента * 128))*128 )
Там на самом деле все просто, из байта старший бит значит нужно ли читать следующий байт, а остальные 7 значащие, потом просто собираем все значащие и обрабатываем их как обычное число
wowks &raquo; Сентябрь 16th, 2015, 11:46 am писал(а):Вот опять странное решение. Запись 9-ю, 10-ю байтми числа, которое умещяется в 8-байт int64
На самом деле правильное решение. Суть в том, что обычно хранятся маленькие числа которые помещаются в 1-2 байта, получается много излишних нулей. Тут же их нету. А если семантически нужно хранить большие числа, то если fixed64, который всегда занимает ровно 8 байт

Вообще, вот ссылка на то как внутри работает протобаф https://developers.google.com/protocol- ... s/encoding

Добавлено спустя 1 минуту 2 секунды:
MOZGIII
Если ты знаешь структуру и что она значит то это какие-нибудь нейронные сети. А иначе, как можно глядя на набор байт определить в нем структуру? может это вообще из /dev/random набор пришел

Аватара пользователя
NiGHt-LEshiY
Полковник
Полковник
Сообщения: 10258
Зарегистрирован: 13.06.2008
Откуда: Россия
Благодарил (а): 752 раза
Поблагодарили: 2667 раз
Контактная информация:

#6 Сообщение 16.09.2015, 15:30

Pr0Ger &raquo; 16 сен 2015, 10:26 писал(а):Чисто случайно вчера вечером тоже смотрел этот формат.
Pr0Ger
Случайно? :(

Добавлено спустя 2 часа 13 минут 28 секунд:
P.S.: у нас есть тэг для кода:
[syntax lang="c" lines="n"]#include <stdio.h>

#define MANIFEST_CHUNK_PAYLOAD_ID 0x71F617D0
#define MANIFEST_CHUNK_METADATA_ID 0x1F4812BE
#define MANIFEST_CHUNK_SIGNATURE_ID 0x1B81B817

typedef struct {
uint32_t id;
uint32_t size;
} CONTENT_MANIFEST_CHUNK;

[/syntax]
Кодекс поведения участников сообщества — обязательно к прочтению.
Просьба присылать сообщения об ошибках в ЛС.

Аватара пользователя
wowks
Майор
Майор
Сообщения: 525
Зарегистрирован: 09.12.2008
Благодарил (а): 67 раз
Поблагодарили: 37 раз

#7 Сообщение 16.09.2015, 16:09

Pr0Ger
Pr0Ger &raquo; Ср сен 16, 2015 11:13 am писал(а):Вообще, вот ссылка на то как внутри работает протобаф
вот спасибо! :)
надо будет по возможности изучить и улучшить код в шапке.


NiGHt-LEshiY
NiGHt-LEshiY &raquo; Ср сен 16, 2015 11:16 am писал(а):у нас есть тэг для кода
удобно
можно пример такого тега на манер [*spoiler="Скрытый текст"][*/spoiler] ?
у меня при написании сообщения есть только [*code] ... [*/code]

Аватара пользователя
NiGHt-LEshiY
Полковник
Полковник
Сообщения: 10258
Зарегистрирован: 13.06.2008
Откуда: Россия
Благодарил (а): 752 раза
Поблагодарили: 2667 раз
Контактная информация:

#8 Сообщение 16.09.2015, 16:18

wowks
При попытке процитировать моё сообщение будет видно, какие теги я использовал.

Код: Выделить всё

[syntax lang="c" lines="n"]#include <stdio.h>[/syntax]
Добавлено спустя 2 минуты 33 секунды:
Pr0Ger &raquo; 16 сен 2015, 13:13 писал(а):А иначе, как можно глядя на набор байт определить в нем структуру? может это вообще из /dev/random набор пришел
Теоретически можно. Анализ энтропии и закономерностей очень поможет. Вольфрам вон умеет в последовательностях искать закономерности и "угадывать", какое было бы следующее число в последовательности.
Кодекс поведения участников сообщества — обязательно к прочтению.
Просьба присылать сообщения об ошибках в ЛС.

Аватара пользователя
Pr0Ger
Модератор
Модератор
Сообщения: 1829
Зарегистрирован: 16.01.2009
Благодарил (а): 17 раз
Поблагодарили: 214 раз
Контактная информация:

#9 Сообщение 16.09.2015, 16:36

NiGHt-LEshiY
Если у нас есть несколько одинаковых структур, но с разными данными, то да, закономерности какие-то можно найти. А в одном наборе разве что какие-нибудь строки искать, и то даже простой xor отломает это.
Вот к примеру, '\x08\xc0\xc4\x07', с точки зрения это протобафа это число 123456 записанное в поле с индексом 1
NiGHt-LEshiY &raquo; Сентябрь 16th, 2015, 4:15 pm писал(а):Вольфрам вон умеет в последовательностях искать закономерности и "угадывать", какое было бы следующее число в последовательности.
Это проще будет, я такое в магистратуре писал, по набору (x, y) находилось зависимость между ними

MOZGIII
Разработчик
Разработчик
Сообщения: 910
Зарегистрирован: 09.01.2009
Откуда: Переезжаю в /dev/null
Благодарил (а): 7 раз
Поблагодарили: 65 раз
Контактная информация:

#10 Сообщение 16.09.2015, 18:23

Нейросети да, кажется подойдут для такого. Идея в следующем: на вход подавать различные наборы данных с похожей структурой, но, возможно, полями разной длинны, возможно в перемешанном порядке итп. На выходе получать некие общие свойства присущие данным из всех этих наборов.
Надо подумать на фичами, которые позволят выделять такие данные...
Полезна такая штука будет много где: для анализа сетевых пакетов, файлов стима, скомпилированных исполняемых файлов...
Кстати вот, вспомнил кое-что, что может быть похоже - кто-то делал штуку для замены функционального одинакового кода в программах на более эффективный, заимствованный из других программ. Кажется, там было всё как надо, по научному, с доказательствами итп. Программы, имеется ввиду, уже откомпилированные. Только не помню - целиком статически она работала, или её нужно было сначала в рантайме на программы посмотреть.

Аватара пользователя
NiGHt-LEshiY
Полковник
Полковник
Сообщения: 10258
Зарегистрирован: 13.06.2008
Откуда: Россия
Благодарил (а): 752 раза
Поблагодарили: 2667 раз
Контактная информация:

#11 Сообщение 16.09.2015, 21:01

Накодил на питончике парсер манифестов с дампом в Json, раз уж это популярная/нужная штука.
В архиве исходники и манифест для теста.
Нужен python-protobuf и protobuf-compiler (в частности утилита protoc из этого пакета).

Подготовка (генерация manifest_pb2.py на основе описания из manifest.proto):

Код: Выделить всё

protoc -I=. --python_out=. ./manifest.proto
Использование:

Код: Выделить всё

./parse_manifest.py 1251_9033622738747915238.manifest
Результат: 1251_9033622738747915238.manifest.json
Вложения
manifest_parser.tar.gz
(98.79 КБ) 153 скачивания
manifest_parser.tar.gz
(98.79 КБ) 153 скачивания
Кодекс поведения участников сообщества — обязательно к прочтению.
Просьба присылать сообщения об ошибках в ЛС.

Аватара пользователя
wowks
Майор
Майор
Сообщения: 525
Зарегистрирован: 09.12.2008
Благодарил (а): 67 раз
Поблагодарили: 37 раз

#12 Сообщение 18.09.2015, 16:55

Обновил код:
- теперь длина varint проверяется в цикле под новым макросом ___get_varint_len(varint, result_len) (спасибо Pr0Ger-у за инфу)
- улучшен и упрощён код
- лишние комментарии удалены, а оставшиеся улучшены и дополнены
- добавлена проверка на ошибки в load_content_manifest и возможность прерывания загрузки
- MANIFEST_PAYLOAD_FILE_INFO.file_type - больше не перечисляемый тип, тк
------- размер переменной 8 бит и типов тоже 8
------- судя по степеням 2-ки, переменная может при желании нести несколько флагов (например "только чтение" и "конфиг")
--------------- поэтому надёжнее проверять тип через "&"
- всё вынесено во внешнюю статичную библиотеку "content_manifest.h/content_manifest.cpp"

Код с примером и архив с тем же самым в шапке темы.

Ответить Вложения 1