Библиотека simple directmedia layermiu.by/rus/kaf_ep/kaf_download/17641_96780514.pdf ·...

Post on 24-Jul-2020

3 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Библиотека Simple DirectMedia Layer

Гедранович Александр Брониславович

gedranovich@gmail.com

СИСТЕМНОЕ ПРОГРАММИРОВАНИЕ1,2

1За все ошибки на слайдах ответственность несет автор2Студенты, обнаружившие ошибки, категорически поощряются

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 1 / 56

Вопросы

1 Возможности и сфера применения

2 Использование основных возможностей

Инициализация библиотеки

Видео/Графика

Аудио

CD-ROM / DVD-ROM

События

Таймеры

Потоки

3 Взаимодействие с внешними библиотеками

Низкоуровневые библиотеки

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 2 / 56

Возможности и сфера применения

Вопросы

1 Возможности и сфера применения

2 Использование основных возможностей

3 Взаимодействие с внешними библиотеками

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 3 / 56

Возможности и сфера применения

Что это такое?

Simple DirectMedia Layer (SDL)

– это свободная мультимедийная кроссплатформенная библиотека,

обеспечивающая низкоуровневый доступ к 2D видеобуферу, 3D

оборудованию, аудиоустройствам, клавиатуре, джойстику и др.

http://www.libsdl.org/

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 4 / 56

Возможности и сфера применения

Архитектура SDL

Кроссплатформенность

Windows / Windows CE

Linux

MacOS / MacOS X

FreeBSD

Solaris

Sembian

др.

Многоязычность

C / C++ / C#

Java

Python

Ruby

Pascal

др.

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 5 / 56

Возможности и сфера применения

Архитектура SDL

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 6 / 56

Возможности и сфера применения

Для чего используется?

Разработка игр

Разработка игровых SDK

Разработка эмуляторов

Демонстрации

Мультимедиа-приложения

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 7 / 56

Battle For Wesnoth

Quake4

Использование основных возможностей

Вопросы

1 Возможности и сфера применения

2 Использование основных возможностей

Инициализация библиотеки

Видео/Графика

Аудио

CD-ROM / DVD-ROM

События

Таймеры

Потоки

3 Взаимодействие с внешними библиотеками

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 10 / 56

Использование основных возможностей Инициализация библиотеки

Использование библиотеки SDL

SDL чаще всего используется как динамическая библиотека. Такой

режим позволяет использовать ее в коммерческих приложениях

(GPL2).

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 11 / 56

Использование основных возможностей Инициализация библиотеки

Функция инициализации

Для динамической загрузки библиотеки используется функция

SDL_Init()

Которая может принимать следующие параметры:

SDL_INIT_AUDIO

SDL_INIT_VIDEO

SDL_INIT_CDROM

SDL_INIT_TIMER

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 12 / 56

Использование основных возможностей Инициализация библиотеки

Функция деинициализации

Для выгрузки библиотеки используется функция

SDL_Quit()

SDL динамически загружает свои библиотеки из стандартных

системных директорий. Для использования альтернативного

размещения библиотек вызывается функция

SDL_SetLibraryPath()

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 13 / 56

Использование основных возможностей Инициализация библиотеки

Пример. Инициализация SDL

1 #include <stdlib.h>2 #include "SDL.h"

4 main(int argc, char *argv[])5 {6 if(SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO)<0)7 {8 fprintf(stderr, "Ошибка инициализации SDL:9 %s\n", SDL_GetError());

10 exit(1);11 }12 atexit(SDL_Quit);13 ...14 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 14 / 56

Использование основных возможностей Видео/Графика

Возможности по работе с видео

Поддержка видеорежимов с глубиной цвета от 8 бит и выше с

необязательным преобразованием, если режим не

поддерживается аппаратно

Прямая запись в линейный графический видеобуфер

Создание поверхностей с атрибутами прозрачности (alpha

blending)

Копирование поверхностей с автоматическим преобразованием в

целевой формат, используя ММХ-оптимизированные процедуры

копирования и, по возможности, аппаратное ускорение

Аппаратное ускорение операций копирования и заполнения, если

это поддерживается оборудованием

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 15 / 56

Использование основных возможностей Видео/Графика

Режимы работы

Для определения оптимальной глубины цвета, поддерживаемой

аппаратурой, используется функция

SDL_GetVideoInfo()

Для получения списка всех поддерживаемых разрешений и

соответствующих им глубин цвета, используется функция

SDL_ListModes()

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 16 / 56

Использование основных возможностей Видео/Графика

Установка режима

Установка видеорежима проводится с помощью функции

SDL_SetVideoMode(WIDTH, HEIGHT, BITS, MODE)

Параметр функции MODE может принимать значения:

SDL_FULLSCREEN – для работы в полноэкранном режиме

SDL_HWSURFACE – использование видеопамяти

SDL_SWSURFACE – использование оперативной памяти

др.

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 17 / 56

Использование основных возможностей Видео/Графика

Пример. Установка видеорежима

1 ...2 SDL_Surface *screen;3 screen = SDL_SetVideoMode(640,480,16,SDL_SWSURFACE);

5 if(screen==NULL)6 {7 // Если установить разрешение не удалось8 fprintf(stderr, "Невозможно установить разрешение9 640x480: %s\n", SDL_GetError());

10 exit(1);11 }12 ...

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 18 / 56

Использование основных возможностей Видео/Графика

Структура видеопамяти

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 19 / 56

Использование основных возможностей Видео/Графика

Прямое обращение к видеопамяти

Для обращения к видеопамяти нужно прочитать/записать элемент

одномерного массива screen->pixels, построчно хранящего

изображение размером pitch*height.

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 20 / 56

Использование основных возможностей Видео/Графика

Пример. Рисование точки (1 из 3)

1 void putpixel(SDL_Surface *screen,2 Uint8 R, Uint8 G, Uint8 B)3 {4 // Преобразуем компоненты в цвет5 Uint32 color = SDL_MapRGB(screen->format,R,G,B);

7 // Блокируем поверхность для фоновой прорисовки8 if(SDL_MUSTLOCK(screen))9 {

10 if(SDL_LockSurface(screen)<0)11 {12 return;13 }14 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 21 / 56

Использование основных возможностей Видео/Графика

Пример. Рисование точки (2 из 3)

17 // Cколько байт на пиксель?18 switch(screen->format->BytesPerPixel)19 {20 case 1: // Если 8-битный цвет21 {22 Uint8 *buf = (Uint8*)screen->pixels23 + y*screen->pitch + x;24 *buf = color;25 } break;

27 case 2: // Если 15-битный или 16-битный цвет28 {29 Uint16 *buf = (Uint16*)screen->pixels30 + y*screen->pitch/2 + x;31 *buf = color;32 } break;

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 22 / 56

Использование основных возможностей Видео/Графика

Пример. Рисование точки (3 из 3)

34 case 4: // Если 32-битный цвет35 {36 Uint32 *buf = (Uint32*)screen->pixels37 + y*screen->pitch/4 + x;38 *buf = color;39 } break;40 }

42 // Снимаем блокировку экрана43 if(SDL_MUSTLOCK(screen))44 {45 SDL_UnlockSurface(screen);46 }47 // Перерисовываем часть экрана (одну точку)48 SDL_UpdateRect(screen,x,y,1,1);49 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 23 / 56

Использование основных возможностей Видео/Графика

Рисование точки

Если глубина цвета известна (32 бита), а управление блокировкой

экрана осуществляет основная программа, то функция для рисования

пикселя будет выглядеть следующим образом:

1 void putpixel(int x, int y, int color)2 {3 unsigned int *ptr = (unsigned int*)screen->pixels;4 int lineoffset = y*(screen->pitch/4);5 ptr[lineoffset + x] = color;6 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 24 / 56

Использование основных возможностей Видео/Графика

Использование изображений

Библиотека SDL содержит одну функцию для загрузки изображений

SDL_Surface* SDL_LoadBMP(const char* filename)

При этом изображение представляется в виде поверхности

(surface), что позволяет использовать все функции по их

обработке.

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 25 / 56

Использование основных возможностей Видео/Графика

Использование изображений

Для копирования поверхностей используется команда

SDL_BlitSurface()

Если при этом необходимо указать какой цвет будет прозрачным

(BMP), то используется функция

SDL_SetColorKey()

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 26 / 56

Использование основных возможностей Видео/Графика

Пример. Загрузка изображения (1 из 2)

1 ...2 // Загрузка BMP файла на поверхность3 SDL_Surface *image = SDL_LoadBMP(filename);4 if(image == NULL)5 {6 fprintf(stderr, "Couldn’t load %s: %s\n",7 filename, SDL_GetError());8 return;9 }

11 // Создание прямоугольника "приемника"12 SDL_Rect dest;13 dest.x = (screen->w - image->w)/2;14 dest.y = (screen->h - image->h)/2;15 dest.w = image->w;16 dest.h = image->h;

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 27 / 56

Использование основных возможностей Видео/Графика

Пример. Загрузка изображения (2 из 2)

18 // Блитирование на экранную поверхность19 // Поверхность не должна быть заблокирована20 SDL_BlitSurface(image, 0, screen, &dest);

22 // Обновление измененной части экрана23 SDL_UpdateRects(screen, 1, &dest);24 ...

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 28 / 56

Использование основных возможностей Видео/Графика

Использование изображений

Для использования форматов PNG, TIFF, JPEG и др. необходимо

использовать библиотеку SDL_Image

#include "SDL_Image.h"

А для загрузки изображений функцию

SDL_Surface* IMG_Load(const char* filename)

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 29 / 56

Использование основных возможностей Аудио

Этапы проигрывания аудио

1 Разработка callback-функции для микширования данных и

помещения их в аудиопоток

2 Установка формата воиспроизведения (частота, разрядность,

количество каналов)

3 Открытие аудиоустройства

4 Загрузка внешнего файла

5 Преобразование к формату аудиоустройства

6 Предоставление доступа к данным для callback-функции

7 Освобождение аудиоустройства

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 30 / 56

Использование основных возможностей Аудио

Использование аудио

Для использования возможностей по проигрыванию аудио файлов

необходимо подключить библиотеку

#include "SDL_audio.h"

Существует несколько готовых реализаций низкоуровневых

аудио-микшеров (см. http://www.libsdl.org).

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 31 / 56

Использование основных возможностей CD-ROM / DVD-ROM

Работа с приводами CD-ROM / DVD-ROM

Для того, чтобы воспользоваться возможностями по работе с

приводами, необходимо узнать в каком количестве они присутствуют

в системе

SDL_CDNumDrives()

а затем открывается один из них

SDL_CDOpen()

Основной CD-ROM всегда нумеруется как 0.

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 32 / 56

Использование основных возможностей CD-ROM / DVD-ROM

Работа с приводами CD-ROM / DVD-ROM

Для определения текущего состояния устройства используется

функция

SDL_CDStatus()

Закрывается устройство функцией

SDL_CDClose()

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 33 / 56

Использование основных возможностей CD-ROM / DVD-ROM

Работа с приводами CD-ROM / DVD-ROM

SDL имеет две функции для воспроизведения CD-ROM. Для

проигрывания определенного трека используется

SDL_CDPlayTracks()

Для всего диска

SDL_CDPlay()

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 34 / 56

Использование основных возможностей CD-ROM / DVD-ROM

Пример. Проигрывание аудиодиска (1 из 2)

1 void PlayTrack(SDL_CD *cdrom, int track)2 {3 // Если в приводе есть диск4 if(CD_INDRIVE(SDL_CDStatus(cdrom)))5 {6 // то проигрываем его7 SDL_CDPlayTracks(cdrom, track, 0, track+1, 0);8 }

10 // Ждем пока проигрывается трэк11 while(SDL_CDStatus(cdrom) == CD_PLAYING)12 {13 SDL_Delay(1000);14 }15 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 35 / 56

Использование основных возможностей CD-ROM / DVD-ROM

Пример. Проигрывание аудиодиска (2 из 2)

17 int PlayCD()18 {19 SDL_CD *cdrom;20 if(SDL_CDNumDrives() > 0)21 {22 cdrom = SDL_CDOpen(0);23 if(cdrom == NULL)24 {25 fprintf(stderr, "Не могу открыть CD-ROM по26 умолчанию: %s\n", SDL_GetError());27 return;28 }29 }30 PlayTrack(cdrom, 1);31 SDL_CDClose(cdrom);32 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 36 / 56

Использование основных возможностей События

Функции для обработки событий

Для ожидания события используется функция

SDL_WaitEvent()

Для опроса событий используется функция

SDL_PollEvent()

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 37 / 56

Использование основных возможностей События

Пример. Обработка событий (1 из 2)

1 SDL_Event event;

3 // Опрашиваем события и отработываем нужные4 while(SDL_PollEvent(&event))5 {6 switch(event.type)7 {8 case SDL_KEYDOWN:9 break;

11 case SDL_KEYUP:12 // Нажатие ESC13 if (event.key.keysym.sym == SDLK_ESCAPE)14 return 0;

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 38 / 56

Использование основных возможностей События

Пример. Обработка событий (2 из 2)

17 // Нажатие пробела18 if (event.key.keysym.sym == SDLK_SPACE)19 my_func();20 break;

22 case SDL_QUIT:23 return(0);24 }25 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 39 / 56

Использование основных возможностей Таймеры

Использование таймеров

Функция

SDL_GetTicks()

позволяет узнать, сколько миллисекунд прошло с произвольно

выбранного момента.

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 40 / 56

Использование основных возможностей Таймеры

Пример. Использование тамера

1 #define TICK_INTERVAL 30

3 unsigned int TimeLeft()4 {5 static unsigned int next_time = 0;6 unsigned int now = SDL_GetTicks();

8 if(next_time <= now)9 {

10 next_time = now + TICK_INTERVAL;11 return 0;12 }

14 return (next_time-now);15 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 41 / 56

Использование основных возможностей Потоки

Использование потоков

Потоки создаются через вызов функции

SDL_CreateThread()

Для чего должна быть подключена библиотека

#include "SDL_thread.h"

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 42 / 56

Использование основных возможностей Потоки

Пример. Создание потока (1 из 3)

1 #include "SDL_thread.h"

3 int global_data = 0;4 int thread_func(void *unused)5 {6 int last_value = 0;7 while(global_data != -1)8 {9 if(global_data != last_value)

10 {11 printf("Данные изменены: %d\n", global_data);12 last_value = global_data;13 }14 SDL_Delay(100);15 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 43 / 56

Использование основных возможностей Потоки

Пример. Создание потока (2 из 3)

17 printf("Завершение потока...\n");18 return 0;19 }

21 int main()22 {23 ...24 SDL_Thread *thread =25 SDL_CreateThread(thread_func, NULL);26 if(thread == 0)27 {28 fprintf(stderr, "Ошибка создания потока: %s\n",29 SDL_GetError());30 return -1;31 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 44 / 56

Использование основных возможностей Потоки

Пример. Создание потока (3 из 3)

34 for (int i=0; i<10; ++i)35 {36 printf("Новое значение - %d\n", i);37 global_data = i;38 SDL_Delay(1000);39 }40 printf("Сообщаем потоку, что нужно выходить\n");41 global_data = -1;42 SDL_WaitThread(thread, 0);43 ...44 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 45 / 56

Взаимодействие с внешними библиотеками

Вопросы

1 Возможности и сфера применения

2 Использование основных возможностей

3 Взаимодействие с внешними библиотеками

Низкоуровневые библиотеки

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 46 / 56

Взаимодействие с внешними библиотеками

Особенности использования

SDL чаще всего используется как динамическая библиотека, в ее

подключении отсутствуют какие-либо особенности.

В случае необходимости разработки для SDL – нужно установить

пакет SDL-devel, содержащий заголовочные файлы и библиотеки.

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 47 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Особенности использования

Т.к. для библиотеки SDL основным языком является C++, то при

подключении внешней функции из низкоуровневой (ассемблерной)

библиотеки необходимо указать тип функции как "C".

extern "C" int mmx_func(void*, int, int);

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 48 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Пример. Вызов низкоуровневой функции (1 из 3)

1 extern "C" void* mmx_invert(void* img, int length);2 ...3 #include "SDL.h"4 #include "SDL_image.h"5 ...6 #define WIDTH 12807 #define HEIGHT 10248 #define DEPTH 329 SDL_Surface *screen, *image;

10 SDL_Rect dest;11 ...12 if(SDL_Init(SDL_INIT_VIDEO) < 0) {13 fprintf(stderr, "Unable to init SDL: %s\n",14 SDL_GetError());15 exit(1);16 }

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 49 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Пример. Вызов низкоуровневой функции (2 из 3)

17 ...18 screen = SDL_SetVideoMode(WIDTH, HEIGHT, DEPTH,19 SDL_FULLSCREEN|SDL_SWSURFACE);20 if(screen == NULL) {21 fprintf(stderr, "Unable to set %dx%d video: %s\n",22 WIDTH, HEIGHT, SDL_GetError());23 exit(1);24 }25 ...26 image = IMG_Load(filename);27 if(image == 0) {28 fprintf(stderr, "Couldn’t load %s: %s\n",29 filename, SDL_GetError());30 return;31 }32 ...

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 50 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Пример. Вызов низкоуровневой функции (3 из 3)

34 ...35 dest.x = (screen->w - image->w)/2;36 dest.y = (screen->h - image->h)/2;37 dest.w = image->w;38 dest.h = image->h;39 ...40 SDL_BlitSurface(image, 0, screen, &dest);41 SDL_UpdateRect(screen, 0, 0, WIDTH, HEIGHT);42 ...43 mmx_invert((void*)image->pixels, image->w*image->h);44 ...

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 51 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Пример. Инвертирование изображения (1 из 5)

1 .data2 tmp DB 8 DUP(255)3 ...4 mmx_invert PROC C USES esi ecx edx5 image:DWORD, len:DWORD6 mov ecx, len7 shr ecx, 38 mov edx, image9 lea esi, tmp

10 .WHILE ecx>011 movq mm0, QWORD PTR [edx]12 movq mm1, QWORD PTR [edx+8]13 movq mm2, QWORD PTR [edx+16]14 movq mm3, QWORD PTR [edx+24]15 movq mm4, mm016 movq mm5, mm2

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 52 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Пример. Инвертирование изображения (2 из 5)

18 punpcklbw mm0, mm119 punpckhbw mm4, mm120 movq mm1, mm0

22 punpcklbw mm2, mm323 punpckhbw mm5, mm324 movq mm3, mm2

26 punpcklbw mm0, mm427 punpckhbw mm1, mm428 punpcklbw mm2, mm529 punpckhbw mm3, mm530 movq mm4, mm031 movq mm5, mm1

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 53 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Пример. Инвертирование изображения (3 из 5)

34 punpckldq mm5, mm3 ; Red35 punpckhdq mm0, mm2 ; Green36 punpckldq mm4, mm2 ; Blue37 punpckhdq mm1, mm3 ; Alpha

39 movq mm7, QWORD PTR [esi]40 psubb mm7, mm541 movq mm5, mm7

43 movq mm7, QWORD PTR [esi]44 psubb mm7, mm045 movq mm0, mm7

47 movq mm7, QWORD PTR [esi]48 psubb mm7, mm449 movq mm4, mm7

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 54 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Пример. Инвертирование изображения (4 из 5)

51 movq mm7, mm552 movq mm5, mm153 movq mm1, mm454 movq mm4, mm055 movq mm0, mm7

57 movq mm2, mm058 movq mm3, mm1

60 punpcklbw mm0, mm561 punpckhbw mm2, mm562 punpcklbw mm1, mm463 punpckhbw mm3, mm464 movq mm5, mm165 movq mm4, mm3

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 55 / 56

Взаимодействие с внешними библиотеками Низкоуровневые библиотеки

Пример. Инвертирование изображения (5 из 5)

68 punpcklwd mm1, mm069 punpckhwd mm5, mm070 punpcklwd mm3, mm271 punpckhwd mm4, mm2

73 movq QWORD PTR [edx], mm174 movq QWORD PTR [edx+8], mm575 movq QWORD PTR [edx+16], mm376 movq QWORD PTR [edx+24], mm477 add edx, 3278 dec ecx79 .ENDW

81 mov eax, 082 ret83 mmx_invert ENDP

Гедранович А.Б. (СистПр) Библиотека SDL Осень, 2011 56 / 56

top related