Предлагаю в этой теме рассказывать о тех неурядицах, с которыми приходится столкнуться PSP-неофиту во время первых опытов. Начну.
Внешние файлы с ресурсами на флешке
Полчаса ушло на то, чтобы понять, почему не открывается файл на флешке. Оказывается, далеко не всякое расширение файла подходит. Мой файл имел расширение .bmp и поэтому не открывался ни под каким предлогом. Пришлось сменить расширение на .bin.
Чтение файлов
Чтение надо всегда организовывать (слово-то какое) в aligned storage. То есть, например, фокус со стеком типа такого не пройдет:
t_bitmapfileheader bmfh; sceIoRead(fp, &bmfh, 40); // - здесь получим bus-error
Вместо этого, надо так:
static t_bitmapfileheader bmfh __attribute(( aligned( 64))); sceIoRead( fp, &bmfh, 40);
Или динамически выделять память под структуру с помощью memalign, не забывая потом освободить с free.
Little and Big endian
Сразу нужно иметь в виду, что архитектура main cpu PSP подразумевает "little endian" порядок следования битов в числах. В то время, как на привычном x86 это может быть и "big endian", и "little endian". Хоть в компиляторе CW и есть "возможность" выбора endianness, но она не работает.
Структуры, классы, портирование кода и ресурсов с PC
Вот известная вам структура - заголовок BMP-файла:
typedef struct { // 54 bytes unpadded u_int16 bfType; u_int32 bfSize; u_int16 bfReserved1; u_int16 bfReserved2; u_int32 bfOffBits; . . . } t_bitmapheader __attribute__(( packed));
Ею воспользоваться не получится, потому что поля автоматически выравниваются. Чтобы посмотреть, как именно они выравниваются, можно сделать так:
snprintf(s, 64, "bfType=%u bfSize=%u bfReserved1=%u bfReserved2=%u bfOffBits=%u", ( char*)&pBMH->bfType-( char*)pBMH, ( char*)&pBMH->bfSize-( char*)pBMH, ( char*)&pBMH->bfReserved1-( char*)pBMH, ( char*)&pBMH->bfReserved2-( char*)pBMH, ( char*)&pBMH->bfOffBits-( char*)pBMH ); sceGuDebugPrint( 8, 96, 0x0000ff00, s);
То, есть, получим bfType=0, bfSize=4, bfReserved1=8, bfReserved2=10, bfOffBits=12. Иначе сказать, всё выравнивается либо по размеру элементов, либо по заданным индивидуальным значеням для элементов, либо по заданному значению для структур (structure align). Подробнее об этом:
<dDIMA> Как формируется структура. Сейчас поясню. <dDIMA> Мы начинаем складывать в структуру поля. Сначала offset = 0. <dDIMA> Берем очередное поле. Выясняем, какого оно размера. Если его sizeof() равен 1, то его можно класть прямо так <dDIMA> Если sizeof() равен двойке, то надо проверить текущий offset и сделать его кратным 2 <dDIMA> Если sizeof() равен четверке, то надо проверить текущий offset и сделать его кратным 4 <dDIMA> И так далее. <Padawan> спасибо <dDIMA> Если мембырь класса имеет явно заданный align(), то offset выравнивается до указаного align <dDIMA> Если мембырь имеет размер больше, чем "structure align" (для PC, например, это 8), то используется structure align <dDIMA> Наконец, в конце структуры добиваются байтики для того, чтобы sizeof() структуры стал "хорошим" <Padawan> кратным structure align value? <dDIMA> таким, что бы если ты работаешь с массивом этих структур, не произошло bus error <Padawan> понятно. зависит от архитектуры значит. <dDIMA> Если структура состоит только из однобайтовых полей - ничего добавлять не надо <dDIMA> Если есть int16 - размер структуры должен быть четным <dDIMA> *итоговый размер структуры <dDIMA> Если int32 - то sizeof() структуры должен делиться на 4 <dDIMA> И так далее - либо до structure align, либо до размера фвно выровненных мемберов <Padawan> ясно. определяется размером наибольшего элемента, либо structure align <dDIMA> Для PS2 structure align = 16. Для PSP, подозреваю, сколько же
Поэтому при проектировании структур необходимо учитывать эти факты. А при переносе кода/ресурсов стоит внимательно оценивать существующие структуры, так как они по-просту могут быть непригодными, как например в моем случае с BMP.
С классами всё становится немного запутанным. С появлением наследования, а тем более множественного, от нашего взгляда всё дальше ускользают детали распределения памяти. Что часто приводит к неоправданным расходам.
<dDIMA> теперь можем поговорить о наследовании классов и выравнивании наследованных структур :) <Padawan> ужос <Padawan> лучше на С всё писать <dDIMA> в том случае, если у тебя есть class A и class B : public class A, ситуация следующая Правка: классы склеиваются в структуру, которая потом выравнивается <dDIMA> Проблема только в том, если например class A имеет виртуальные функции <dDIMA> Все известные мне реализацыи пишут адрес VTBL в первые 4 байта структуры. <dDIMA> Поэтому потом начинается жопа <dDIMA> Если не дай бог в классе есть мемберь, который требует выравнивания на 16, получается следующая хуйня <Padawan> выравниться должно <dDIMA> class A: { virtual ~A(); var a; } // var - структура с выравниванием на 16 <dDIMA> Получаем следующую карту памяти: [vtbl] со смещением 0 <dDIMA> char padding[12]; // 12 байт выравнивания после vtbl <dDIMA> var a; // сама переменная, которая идет со смещением на 16 байт <dDIMA> множественное наследование смотреть будем? :-) <Padawan> понятно убийство получается. <Padawan> (памяти)
Тема в архиве.