Blog. Just Blog

Парсинг метаданных MMF-файлов на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Парсинг метаданных MMF-файлов на Ассемблере
Парсинг метаданных MMF-файлов на Ассемблере

SMAF (Synthetic Music Mobile Application File), он же MMF - мультимедийный формат данных, разработанный компанией Yamaha. В эпоху кнопочных сотовых телефонов этот формат использовался для создания очень компактных по размеру мелодий, хоть и невысокого качества звучания. Формат MMF может содержать не только мелодии, но и голоса, целые песни, и даже команды управления подсветкой телефона, если таковые поддерживаются аппаратом. Сейчас этот формат потерял актуальность, даже официальный сайт проекта окончательно закрылся несколько лет назад и доступен только в архиве. Описаний внутреннего формата MMF-файлов тоже практически нет, всю информацию пришлось собирать по крупицам из разных источников, в том числе и на основе анализа файлов "из дикой природы".

Файл в формате MMF состоит из глобального заголовка и последовательно расположенных секций. Заголовок представляет собой четырехсимвольную сигнатуру "MMMD", за которой следует DWORD с размером данных в формате Big-endian. Далее в статье будет подразумеваться, что все размеры данных указаны в формате Big-endian, это чтобы не повторяться. Итого на глобальный заголовок отводится 8 байт. Размер данных равняется общему размеру файла минус 8.

Дальше идут секции. Каждая секция также начинается с четырехбайтного заголовка и DWORD с размером данных секции. В качестве заголовка может быть одна из следующих строк: "CNTI", "OPDA", "MSTR", "MTRx" или "ATRx", других названий не предусмотрено. При этом в заголовках "MTRx" и "ATRx" четвертый байт означает версию формата. После заголовка следует содержимое секции. Музыкальные данные меня не интересуют, для получения необходимых данных понадобятся только секция "CNTI" (CoNTents Info) и/или "OPDA" (OPtional DAta). Данные по ним в основном подчерпнуты из упомянутой выше "дикой природы", то есть в результате анализа готовых файлов, так что их полноту и 100%-ю корректность я не гарантирую.

Начнем с секции CNTI. Согласно спецификации, она должна содержать пять обязательных значений в строго определенной последовательности: Contents Class, Contents Type, Contents Code Type, Copy Status и Copy Counts. У меня имеются две разновидности MMF-файлов, в одном из которых размер этой секции составляет ровно 5 байт, отсюда можно сделать вывод, что каждый из этих параметров имеет размерность в 1 байт. Справочники каких-либо конкретных значений по каждому из параметров я не нашел.

Вариант секции CNTI
Вариант секции CNTI

Есть и второй вариант, в котором секция CNTI занимает гораздо больше 5 байт. По ее структуре можно предположить, что первые 5 байт аналогичны предыдущему варианту, а следующие за ними как раз содержат метаданные и другую информацию в виде блоков.

Вариант секции CNTI
Вариант секции CNTI

Формат этих блоков следующий. Первые два байта - идентификатор данных, после него следует 1 байт ":" и сами данные, оканчивающиеся символом "," в том числе и последний блок. Если в данных, например, в оригинальном названии композиции, встречается символ ",", то в блоке он экранируется символом "\". Точного списка зарезервированных символов у меня также нет, но, как можно предположить, любой символ, следующий за "\", при получении данных надо просто копировать в результирующую строку. Списка идентификаторов я тоже нигде не нашел, опытным путем удалось выяснить, что "ST" - название композиции, "SW" и/или "AW" - название исполнителя, а остальное меня не интересует. Но стоит не забывать, что наличие ни одного из перечисленных идентификаторов не является обязательным. Строки записаны в кодировке UTF-8, это надо учитывать при загрузке строки и последующей ее конвертации.

Секция OPDA
Секция OPDA

Переходим к секции OPDA. Главное, что требуется знать, что теоретически в файле ее может и не быть. Но если секция CNTI содержит только 5 байт, то все основные данные с максимальной вероятностью содержатся в секции OPDA. Первые 8 байт содержимого секции OPDA достоверно опознать не удалось, в разных файлах они мало отличаются. После них следуют блоки в формате, очень похожем на описанные выше: два символа с идентификатором, WORD с размером данных, затем сами данные. Идентификаторы данных аналогичны секции CNTI.

Для воспроизведения рингтонов в формате MMF удобнее всего использовать фирменный плеер YAMAHA MidRadio Player. Не самый великий шедевр софтостроения, но лучше, чем ничего.

Осталось сделать универсальный парсер секций. В нем учтены все известные мне варианты загрузки строк, а так же их преобразование в кодировку windows-1251 из UTF-8. При необходимости можно легко добавить в обработку нужные вам идентификаторы данных.
  1.         ; Прочитать заголовок файла
  2.         invoke  _lread,[desc],buff,200h
  3.  
  4.         ; Проверить корректность файла
  5.         mov     esi,buff
  6.  
  7.         lodsd
  8.         cmp     eax,'MMMD'
  9.         jne     loc_close
  10.  
  11.         lodsd
  12.  
  13. loc_scan:
  14.         lodsd
  15.         cmp     eax,'CNTI'
  16.         je      loc_process_cnti
  17.         cmp     eax,'OPDA'
  18.         je      loc_process_opda
  19.  
  20.         jmp     loc_done
  21.  
  22.         ; Обработка секции CNTI
  23. loc_process_cnti:
  24.         lodsd
  25.         bswap   eax
  26.         cmp     eax,5
  27.         jne     @f
  28.  
  29.         add     esi,eax
  30.         jmp     loc_scan
  31. @@:
  32.         mov     ebx,eax
  33.         add     ebx,esi
  34.  
  35.         add     esi,5
  36. loc_parse_cnti:
  37.         lodsw
  38.         inc     esi
  39.         ; Скопировать название трека
  40.         mov     edi,title
  41.         cmp     ax,'ST'
  42.         je      loc_load_str_1
  43.  
  44.         ; Скопировать название артиста
  45.         mov     edi,artist
  46.         cmp     ax,'SW'
  47.         je      loc_load_str_1
  48.         cmp     ax,'AW'
  49.         je      loc_load_str_1
  50. @@:
  51.         lodsb
  52.         cmp     al,','
  53.         je      loc_chk_bound_1
  54.         cmp     al,'\'
  55.         jne     @b
  56.         lodsb
  57.         jmp     @b
  58.  
  59. loc_chk_bound_1:
  60.         cmp     esi,ebx
  61.         jb      loc_parse_cnti
  62.         jmp     loc_done
  63.  
  64. loc_load_str_1:
  65.         lodsb
  66.         cmp     al,','
  67.         je      loc_chk_bound_1
  68.         cmp     al,'\'
  69.         jne     @f
  70.         lodsb
  71. @@:
  72.         stosb
  73.         jmp     loc_load_str_1
  74.  
  75.         ; Обработка секции OPDA
  76. loc_process_opda:
  77.         lodsd
  78.         bswap   eax
  79.  
  80.         mov     ebx,eax
  81.         add     ebx,esi
  82.  
  83.         add     esi,8
  84. loc_parse_opda:
  85.         lodsw
  86.         ; Скопировать название трека
  87.         mov     edi,title
  88.         cmp     ax,'ST'
  89.         je      loc_load_str_2
  90.  
  91.         ; Скопировать название артиста
  92.         mov     edi,artist
  93.         cmp     ax,'SW'
  94.         je      loc_load_str_2
  95.         cmp     ax,'AW'
  96.         je      loc_load_str_2
  97.  
  98.         lodsw
  99.         xchg    al,ah
  100.         movzx   eax,ax
  101.         add     esi,eax
  102. loc_chk_bound_2:
  103.         cmp     esi,ebx
  104.         jb      loc_parse_opda
  105.  
  106.         jmp     loc_done
  107.  
  108. loc_load_str_2:
  109.         lodsw
  110.         xchg    al,ah
  111.         movzx   ecx,ax
  112.         rep     movsb
  113.         mov     al,0
  114.         stosb
  115.         jmp     loc_chk_bound_2
  116.  
  117. loc_done:
  118.         ; Удалить начальные и конечные пробелы
  119.         invoke  PathRemoveBlanks,artist
  120.         invoke  PathRemoveBlanks,title
  121.  
  122.         ; Получить нужную длину строки для конвертирования
  123.         invoke  MultiByteToWideChar,CP_UTF8,0,artist,-1,0,0
  124.         ; Промежуточное конвертирование UTF-8 -> UTF-16
  125.         invoke  MultiByteToWideChar,CP_UTF8,0,artist,-1,buff,eax
  126.         ; Получить нужную длину строки для конвертирования
  127.         invoke  WideCharToMultiByte,1251,0,buff,-1,0,0,0,0
  128.         ; Финальное конвертирование UTF-16 -> cp1251
  129.         invoke  WideCharToMultiByte,1251,0,buff,-1,artist,eax,0,0
  130.  
  131.         ; Получить нужную длину строки для конвертирования
  132.         invoke  MultiByteToWideChar,CP_UTF8,0,title,-1,0,0
  133.         ; Промежуточное конвертирование UTF-8 -> UTF-16
  134.         invoke  MultiByteToWideChar,CP_UTF8,0,title,-1,buff,eax
  135.         ; Получить нужную длину строки для конвертирования
  136.         invoke  WideCharToMultiByte,1251,0,buff,-1,0,0,0,0
  137.         ; Финальное конвертирование UTF-16 -> cp1251
  138.         invoke  WideCharToMultiByte,1251,0,buff,-1,title,eax,0,0
В приложении пример программы с исходным текстом, которая парсит метаданные из MMF-файлов и выводит их на экран.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

MMF.Metadata.Demo.zip (22,085 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 196 | Комментариев: 1

Комментарии

Отзывы посетителей сайта о статье
Илья (15.09.2024 в 21:48):
Спасибо за статью. Интересно, наверное вряд ли мы доживем до того момента, когда Microsoft полностью перейдет на utf8. А если и доживем, то старым программам это не понравится.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0148 сек.) / Память: 4.5 Mb
Наверх