Category Archives: программы

Собственная капча на PHP своими руками за 5 минут

Приветствую вас!

Итак, мы наконец-то решились защитить свои формы капчей, т.к. спамеры вконец достали тупым спамом.

Хотя я и не приветствую капчи, т.к. они вынуждают совершить лишние телодвижения, да и могут оттолкнуть потенциальных клиентов от заполнения наших форм, но в ряде случаев они бывают жизненно необходимы.

Итак, в этом посте мы напишем собственный гибкий капча-механизм на PHP, который мы сможем поставить на свои формы за 5-10 минут.

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

Капчу мы будем рисовать на движке PHP GD, и для этого нам потребуются шрифт(ы) – да, можно использовать несколько шрифтов для некого затруднения работы по обходу капч.

Создадим папку fonts и закинем туда парочку бесплатных TTF-шрифтов, например, liber-mono.ttf и liber-sans.ttf. Сразу замечу, что полный комплект файлов – скрипты и шрифты вы можете скачать по ссылке: https://beotiger.com/download/jcaptcha.

Напишем наш серверный скрипт, который будет создавать и отображать капчу, назовём его скажем, jcaptcha.php

Вот этот скрипт с подробными комментариями:

// зададим имя куки для сохранения в ней кода капчи
define('CAPTCHA_COOKIE', 'imgcaptcha_');
 
/*
 
	Инициализируем генератор случайных чисел.
	Хотя в руководстве по PHP написано, что это делается автоматически
	каждый раз при запуске сценария, но... я им не верю 0_0
 
*/
 
mt_srand(time());
 
/*
 
	Определим путь к папке со шрифтами
	и список имен файлов со шрифтами в ней -
	из этого списка каждый раз будем выбирать случайный шрифт
 
*/
 
define('PATH_TTF', 'fonts/');
$fonts = array('liber-mono.ttf', 'liber-sans.ttf');
 
/*
 
	Основные параметры капчи.
 
	Для поддержки разных параметров капчи здесь можно	создать
	многомерный массив и обращаться к нему по индексу.
 
*/
 
$par = array(
	// ширина капчи
	'WIDTH' => 120,
	// высота капчи
	'HEIGHT' => 32,
	// размер шрифта на капче
	'FONT_SIZE' => 14,
 
	// кол-во символов на капче
	'CHARS_COUNT' => 5,
	// разрешенные символы капчи
	'ALLOWED_CHARS' => 'ABCDEFGHJKLMNPQRSTUVWXYZ23458',
 
	// фоновый цвет капчи - белый в нашем случае
	'BG_COLOR' => '#FFFFFF',
	// кол-во линий на капче
	'LINES_COUNT' => 3,
	// толщина линий
	'LINES_THICKNESS' => 2
);
 
/*
	Общие парметры капчи
*/
 
// цвета символов
define('CODE_CHAR_COLORS', '#880000,#008800,#000088,#888800,#880088,#008888,#000000');
// цвета линий
define('CODE_LINE_COLORS', '#880000,#008800,#000088,#888800,#880088,#008888,#000000');
 
// получаем цвета линий и символов в массивы для случайной выборки позднее
$line_colors = preg_split('/,\s*?/', CODE_LINE_COLORS);
$char_colors = preg_split('/,\s*?/', CODE_CHAR_COLORS);
 
// создаем пустой рисунок и заполняем его белым фоном
$img = imagecreatetruecolor($par['WIDTH'], $par['HEIGHT']);
imagefilledrectangle($img, 0, 0, $par['WIDTH'] - 1, $par['HEIGHT'] - 1, gd_color($par['BG_COLOR']));
 
// устанавливаем толщину линий и выводим их на капчу
imagesetthickness($img, $par['LINES_THICKNESS']);
 
for ($i = 0; $i < $par['LINES_COUNT']; $i++)
    imageline($img,
        mt_rand(0, $par['WIDTH'] - 1),
        mt_rand(0, $par['HEIGHT'] - 1),
        mt_rand(0, $par['WIDTH'] - 1),
        mt_rand(0, $par['HEIGHT'] - 1),
        gd_color($line_colors[mt_rand(0, count($line_colors) - 1)])
    );
 
// Переменная для хранения кода капчи
$code = '';
 
// Зададим координату по центру оси Y 
$y = ($par['HEIGHT'] / 2) + ($par['FONT_SIZE'] / 2);
 
// Выводим символы на капче
for ($i = 0; $i < $par['CHARS_COUNT']; $i++) {
		// выбираем случайный цвет из доступного набора
    $color = gd_color($char_colors[mt_rand(0, count($char_colors) - 1)]);
    // определяем случайный угол наклона символа от -45 до 45 градусов
    $angle = mt_rand(-45, 45);
    // выбираем случайный символ из доступного набора
    $char = substr($par['ALLOWED_CHARS'], mt_rand(0, strlen($par['ALLOWED_CHARS']) - 1), 1);
    // выбираем случайный шрифт из доступного набора
    $font = PATH_TTF . $fonts[mt_rand(0, count($fonts) - 1)];
    // вычислим координату текущего символа по оси X
    $x = (intval(($par['WIDTH'] / $par['CHARS_COUNT']) * $i) + ($par['FONT_SIZE'] / 2));
 
    // выводим символ на капчу
    imagettftext($img, $par['FONT_SIZE'], $angle, $x, $y, $color, $font, $char);
 
    // сохраняем код капчи
    $code .= $char;
}
 
// сохраним капчу в куках для дальнейшей проверки
setcookie(CAPTCHA_COOKIE, md5($code));
 
/*
 
	Посылаем сформированный рисунок в браузер и избавляемся от него, 
	хотя сборщик мусора обычно это делает за нас
 
*/
 
header("Content-Type: image/png");
imagepng($img);
imagedestroy($img);
 
// Преобразуем HTML 6-символьный цвет в GD цвет 
function gd_color($html_color)
{
  return preg_match('/^#?([\dA-F]{6})$/i', $html_color, $rgb)
    ? hexdec($rgb[1]) : false;
}

Вот как выглядит сформированная данным скриптом капча (щёлкните на ней для смены кода):

Капча

Теперь для создании формы с капчей достаточно добавить в неё дополнительный инпут с произвольным именем и разместить капчу, например так:

<form action="go.php" method="post">
	Введите имя: <input name="name"><br>
	Введите email: <input name="email"><br>
	Введите код с картинки: <input name="captcha">
	<img title="Щёлкните для нового кода" alt="Капча" src="jcaptcha.php" style="border: 1px solid black" onclick="this.src='jcaptcha.php?id=' + (+new Date());"><br>
	<input type="submit" value="Отправить!">
</form>

А в скрипте go.php после получения данных с формы, но перед дальнейшей их обработкой нужно будет проверить код капчи, и если он не совпадает с заданным, вывести соответствующее сообщение и вернуться к форме, например, так:

 
	// зададим имя куки для получения из неё кода капчи,
	// оно конечно же должно совпадать с соотв. именем в jcaptcha.php
	define('CAPTCHA_COOKIE', 'imgcaptcha_');
	// заметим: поле `captcha` обязательно для заполнения
	if(empty($_POST['captcha']) || md5($_POST['captcha']) != @$_COOKIE[CAPTCHA_COOKIE])
		die('Неверный код с картинки. Вернитесь и повторите попытку.');

Приведенный здесь код дан лишь для примера, в реальных условиях проверять капчу и выводить соотв. сообщение лучше ч/з AJAX, не покидая форму и не заставляя клиента каждый раз вводить одни те же данные по нескольку раз, с возможным таймаутом при превышении числа неудачных попыток для предотвращения брутфорс-атаки.

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

Итак, вы можете скачать полный комплект файлов данного примера – скрипты и шрифты по ссылке: https://beotiger.com/download/jcaptcha

А теперь – пока, пока.
До свидания, до новых встреч, друзья!

nginx+php-fpm под Windows

Здравствуйте!

nginx

Apache – король веб-серверов, если можно так сказать. Но на пятки ему наступает даже не IIS от Microsoft, не lighttpd, а nginx (произносится как Энджин-Икс, engine с английского мотор, двигатель) нашего соотечественника Сысоева.

Чем он хорош? Говорят, что статика отдаётся гораздо быстрее, чем у Апача, да и динамика я думаю тоже. Он жрёт меньше ресурсов, что может быть критически важно для нагруженных серверов. Раньше мнгоие применял связку nginx+Apache – nginx для отдачи статики (рисунков, js/css etc.), а Апач – для отдачи динамики (PHP/Perl/Python/Ruby etc.). Но теперь nginx можно применять без Апача, так как для него появилось куча плагинов и дополнений, поэтому вместо связки nginx+Apache+PHP (мы тут говорим о PHP-среде) легко настроить просто nginx+php-fpm. Ладно, об нём написано куча литературы, не буду повторяться, опишу лишь процесс установки nginx+php-fpm под Виндовс (Windows).

Хотя, конечно, nginx органичней всего чувствует себя в FreeBSD и Linux (любой Unix-среде, наверное), под Винду он тоже неплохо работает, по крайней мере я его у себя на домашнем компе установил, чтобы тестировать некоторые штуки.

Итак, процесс установки/первичной настройки. Этот процесс расписан здесь: http://nginx.org/ru/docs/windows.html
я приведу лишь выжимку.

Смотрим доступные версии nginx под windows здесь: http://nginx.org/en/download.html
Сейчас есть версия 1.8.0, несколько месяцев назад я устанавливал 1.6.2, которая и сейчас у меня работает.
Итак, скачиваем текущую версию под windows: http://nginx.org/download/nginx-1.8.0.zip

Для удобства примем то, что я пользую сейчас:
Создаём папку C:\usr. Заходим в неё и распаковываем nginx-1.8.0.zip здесь (это можно проделать через GUI-интерфэйс).
Затем запускам териминал и заходим:

> C:
> cd C:\usr\nginx-1.8.0
> start nginx

Тут Виньда может выкинуть окошко с предупреждением (см. скриншот), что nginx пытается получить доступ в сеть. Мы конечно же разрешаем.

allow nginx to network

Проверяем, запущен ли nginx и видим результат:

>tasklist /fi "imagename eq nginx.exe"
 
Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
nginx.exe                     1336 Console                    1      6,440 K
nginx.exe                     3136 Console                    1      6,268 K
nginx.exe                     4864 Console                    1      6,496 K
nginx.exe                     6544 Console                    1      5,872 K

Остановим nginx нормально: nginx -s quit. Есть ещё несколько полезных команд для nginx:
nginx -s stop – останов nginx в любом случае (применяется, если nginx -s quit не сработает).
nginx -s reload – перезагрузка .conf файлов (конфигурации)
nginx -s reopen – переоткрытие .log файлов (полезна, если мы удалили или переместили логи при работающем nginx).

Итак, мы остановили nginx сейчас, так как прежде чем его запускать, надо правильно настроить .conf файлы. Они расположены в папке conf. Стандартный файл настройки – nginx.conf, из него директивой include могут подсоединяться другие файлы из этой (впрочем, и из любой другой) папки.
Например, директива include mime.types; в секции http присоединит файл mime.types, в котором находится определения всех стандартных MIME-типов. Впрочем, сам конфиг я обсуждать здесь не буду, о нем много написано в инете, приведу лишь пример своего конфига с краткими пояснениями.

Предупреждение: это конфиг для моей домашней тестовой среды. Для рабочего сервера требуется более тонкая настройка.

worker_processes  1;

error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    
    #
    # Формат лога делаем как у Апача
    #
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;

    # sendfile        on;
    #tcp_nopush     on;
  
    keepalive_timeout  65;

    # Сжатие gzip на лету
    gzip  on;

    server {
        # listen       801;
        server_name  localhost;
				autoindex on; # allow dir listing
				root E:/sites;
				
        #charset koi8-r;
        #access_log  logs/host.access.log  main;

	#
        # запретим доступ ко всем файлам, начинающимся с точки 
	#
        location ~ /\. {
            deny  all;
        }

        location / {
            root   E:/sites;
            index  index.html index.htm index.php;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

	#
        # передаем все PHP скрипты серверу FastCGI, слушающему на 127.0.0.1:9123
        #
        location ~ \.php$ {
            root           E:/sites;
            fastcgi_pass   127.0.0.1:9123;
            fastcgi_index  index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }
}

Итак, в этом конфиге большинство настроек оставлено по умолчанию, а корень сайтов у нас в E:\sites, что в первую очередь делает команда root E:/sites. Обратите внимание на прямые слэши в стиле Unix в пути к папкам и файлам – это требование nginx, даже для Windows-версии.

Теперь можно запускать nginx (start nginx), если мы его останавливали перед этим, либо применить команду nginx -s reload, чтобы сервер перечитал конфиги без остановки своей работы, что полезно при работающем внешнем сайте.

Итак, теперь надо настроить PHP-FPM для Windows. Учтите, что мы уже в нашем конфиге сделали его поддержку на порту 9123 (под-секция location ~ \.php$)

PHP-FPM для Windows

1. Скачиваем свежий (или версию по выбору) .zip-архивчик с http://windows.php.net/download/. Архивчик должен быть VC11/VC9, что содержит в себе FastCGI-файл (phpcgi.exe).
2. Создаем папку в C:\usr, например с именем php-5.6.9 и распаковываем в неё содержимое архива.
3. Редактируем файл php.ini в соотв. со своими предпочтениями, единственное, убедиться, что у нас есть такая строка:

# nginx security setting
cgi.fix_pathinfo=0

Она закрывает одну из старых уязвимостей nginx. Далее можно подключить PHP-модули по вкусу, расскоментировав их в соотв. секции и произвести другие настройки.

4. Теперь создадим .bat-файл, например php-fpm-start.bat с таким содержимым:

@echo off
echo Starting PHP FastCGI...
set path=c:\usr\php-5.6.9;%PATH%
C:\usr\php-5.6.9\php-cgi.exe -b 127.0.0.1:9123 -c C:\usr\php-5.6.9\php.ini

и запустим его. Если мы его запускаем из GUI, то появится окно консоли и останется открытым, придётся с этим смириться.

Всё, наш сервер мы уже давно настроили на соединение с этим PHP процессом.

Для проверки создаём файл index.php в папке E:\sites с таким содержимым:

<?php
phpinfo();

Теперь направляем наш любимый браузер на http://localhost и видим такую примерно картину:

phpinfo() начало

phpinfo

phpinfo() с версией nginx

phpinfo nginx

Здесь же можно посмортеь переменные среды и подключаемые модули. Для совместимости nginx создет переменные среды, совместимые с апачевскими, например _SERVER[“SERVER_NAME”], _SERVER[“DOCUMENT_ROOT”], _SERVER[“REQUEST_URI”], _SERVER[“SCRIPT_NAME”] и т.д., и мы можем использовать их в своих PHP-сценариях, как делали это в случае с Апачем.

До свидания!

Retrospec: ещё немного ретро-игр для Linux

Здравствуйте!

И по следам предыдущего поста, посвященного крутым римэкам от BrainGames.

Есть ещё одна контора (и наверное не одна, но та, что мне на глаза попалась) – Retrospec – хорошее название для конторы, занимающейся retro-играми, верно?

У них куча ретро-игр, с улучшенной графикой, только одно плохо –
мало игр для Линукса и сорцы даны лишь для малой части игр.

Мне удалсоьс найти сорцы и скомпилить под Линукс только такие их игры:
Alien8, DeflektorX4, Humphrey, Jumping Jack, Jumping Jack 2 : Worryingly Familiar, Styx, TranzAm.

Не густо, не густо. Особенно жаль такие хиты как Exolon DX, Head over Heels, Jet Set Willy.
Ну, вы понимаете о чём я. Почему Retrospec не предоставили сорцы для таких игр, не ведаю.
Может, им написать? Возможно, они вышлют нам сорцы недостающих игр?

Итак, вытрем сопли и продолжим жить. Перейдем к делу, будем работать с тем, что есть.
Если ребята из BrainGames использовали кросс-платформенную библиотеку SDL (SDL 1.2),
(https://www.libsdl.org/) и это получилось у них совсем неплохо, имхо, даже супер-отлично,
то ребята из RetroSpec используют другую, не менее известную
кросс-платформенную библиотеку Allegro – http://alleg.sourceforge.net/ https://www.allegro.cc/

Честно говоря, я изучал SDL кое-как и даже умудрился перепиисать эмулятор Apple2 – AppleWin
под Линукс, используя эту либу (SDL 1.2), но больше ничего под неё так и не написал путного
(если не считать переделку gdwgraph – https://github.com/beotiger/gdwgraph, но это не то, что хотелось бы).

А про Аллегро я краем уха слышал что-то, пару примеров по-моему даже пробовал, но в итоге пока не пошло.
А жаль, надо бы поизучать эту либу подробнее.

Итак, ставим девелоперскую версию Allegro (для сборки из исходников нам всегда будут нужны девелоперские версии либ,
(обычные версии либ нужны тогда, когда у нас есть готовые бинарники, и мы не собираемся их пересобирать).

$ apt install build-essential liballegro4-dev liballegro4.4-plugin-alsa

build-essential я сюда привел для галочки, если вы не собирали игры из прошлого поста,
и у вас в системе не установлен стандартный GNU-компилятор GCC/G++ и сопуствующие ему утилиты и либы.
Так же, чтобы в Аллегро был звук, надо доустановить пакет liballegro4.4-plugin-alsa. Подробнее тему ищите где-то внизу.

Да, у меня по умолчанию установилась 4-ая версия Allegro, хотя в репозиториях уже есть 5-ая версия.
Под 4-ой все собралось и работает нормально, поэтому 5-ую версию я не испытвал.

Ещё для некоторых игр может потребоваться Adime либа (http://adime.sourceforge.net/) –
создание диалогов для Allegro. Можно попробовать собрать отдельно под себя эту либу,
если не сможете найти её для своего дистро.
И плюс FBlend – старый (здесь всё старое, это же РЕТРО!) движок смешивания рисунков.
Его можно взять здесь: https://sourceforge.net/projects/fblend

Я мучался с этими либами, уже не помню точно как (читайте во второй части поста),
но в итоге всё получилось.
Вы можете скачать архив с либами для x86_64 и i386 версий отсюда: http://beotiger.com/download/retrolibs. Туда входят fmodex, adime, fblend и allegro (на всякий случай, аллегро можно установить под любой дистро, отдельно i386 или x86_64 версии). Теоретически должны работать на любых версиях (точнее, дистрибутивах) Линукса, я использовал их на Linux Mint. Используйте их, если не найдете версии для своей системы.

Да, давайте напомним текущую дату (дату поста), систему и наш компилятор:

$ date ; uname -a ; g++ –version
Вт. июня 9 14:47:56 MSK 2015
Linux justy 3.13.0-51-generic-tuxonice #84~ppa1-Ubuntu SMP Wed Apr 29 19:39:16 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Так же для некоторых игр от Retrospec нам может понадобиться FMOD – http://www.fmod.org/
Ооох, это долгая история, с учётом того что исходники старые (им больше 10 лет наверное),
и за это вермя поменялось несколько версий FMOD.
Версия FMOD Ex (FMOD 4), которая пришла на смену FMOD 3, работает без проблем,
и её so-файлы и заголовки для include можно скачать с оф.сайта в секции Old/Legacy или как оно там называется.

Версии FMOD 3 для 64-битной платформы я вообще не нашёл, и судя по ответу оф.лица, её не было выпущено.
Только одна игра – TranzAm – требует FMOD3, но я благополучно переделал её под FMOD Ex. За пару дней.

Я лично установил libfmodex64.so в /usr/lib, где она всегда видна линковщику, и прилинковываю как -lfmodex64. А .h файлы закинул в /usr/include, без всяких подпапок, чтобы инклудить их напрямую. Хотя тут конечно, лучше выделить под это дело свою папку.

Процесс этот описан более подробно ниже. А сейчас приведем процесс сборки всех доступны игр.

Скачать архивчик с играми с сорцами от RetroSpec можно отюда: http://beotiger.com/download/retrospec
Архив весит около 33 мегабайт. Скачиваем архив, распаковываем его, и запускаем в его корневой папке терминал/консоль.

Итак, поехали:

Часть Первая

1. Alien8

$ cd Alien8/src
$ make
$ cp alien8 ..
$ cd ..
$ ./alien8

Красота! И звук и музыка есть (если Вы доустановили пакет ALSA, по крайней мере в моём случае).

Фирменная заставка от RetroSpec, мы её часто будем видеть в их играх:
 

Alien8 скриншоты/screenshots

al1.jpg

al2.jpg

al3.jpg

al4.jpg

2. DeflektorX4

$ cd DeflektorX4/src
$ make
$ cp deflector ..
$ cd ..
$ ./deflector
 

DeflektorX4 скриншоты/screenshots

def1.jpg

def2.jpg

Красота! Смысл игры – поворачивать линзы так, чтобы уничтожить лучом все шарики, и не попасть под колючки.
Даётся лимит энергии, если она кончается, уровень приходится начинать сначала! Я 1-ый уровень так и не смог пройти.

Линзы поворачиваются левой/правой кнопкой мыши, игра вообще рассчитана не на клаву, а на мышь.

3. Humphrey

$ cd Humphrey/src
$ make
$ cp humphrey ..
$ cd ..
$ ./humphrey

 

Humphrey скриншоты/screenshots

hum1.jpg

hum2.jpg

Музыка – класс! Обожаю! Есть выбор языка игры – Cervantes или Shakespeare. Жаль Pushkin нет. Или Tolstoy.

4. Jumping Jack

$ cd jj/src
$ make
$ cd ..
$ ./jj
 

Jumping Jack скриншоты/screenshots

jj1.jpg

jj2.jpg

Что за игра? Прыгать под пролетом, стараться не падать. Игра тупая и непорстая, я так и не дошёл до верха, всё время что-то мешало.
F10 – быстрый выход. Музыки нет, но звук есть.

5. Jumping Jack 2 : Worryingly Familiar

$ cd jjwf/src
$ make
$ cp jjwf ..
$ cd ..
$ ./jjwf
 

Jumping Jack 2 : Worryingly Familiar скриншоты/screenshots

jjwf1.jpg

jjwf2.jpg

Игра такая же, как и JJ, только другая?

6. Styx

$ cd styx/src
$ make
$ mv styx ..
$ cd ..
$ ./styx
 

Styx скриншоты/screenshots

styx1.jpg

styx2.jpg

styx3.jpg

Где-то я прочитал, что это первая игра одного из членов команды RetroSpec. Или нет?
Но похоже на то. Моя первая игра была не лучше. но и не хуже! А какая, она, наша первая игра?

7. TranzAm

С этой игрой – долгая история. Да, FMOD 3 -> FMOD Ex. Это пол-беды. Но она не работает,
если откомпилена в 64-битном режиме, выдает segmentation fault в самых разных местах,
и графика глючит, шрифты (FancyFonts – прыгающие или танцующие буквы), Blender – наложение текстур,
и спрайты испорчены частично.

Но когда мне удалось таки перекомпилить игру в 32-битном режиме (-m32), все эти косяки и глюки практически исчезли.

Игрушка неплоха, это Transport America – ездеешь по карте Америки, штатам, собираешь кубки.
Вредные машинки тебе стараются мешать. Бензин тоже кончается, но полно заправок.

Музыка – супер!

Итак, сейчас она компилится без проблем в 32-разрядном режиме, но возможно,
надо будет найти 32-разрядные версии некоторых библиотек, я нагло спер их из дистрибутива Ubuntu 32 bit.
Фишка в том, что 32-разрядные либы мы кладем в папку /usr/lib/i386-linux-gnu, где линкер сам их найдет, когда надо.
Инфу подробнее вы сможете найти ниже, в описании первичной сборки проектов RetroSpec.

$ cd TranzAm32/src
$ make
$ cd ..
$ ./TranzAm
 

TranzAm скриншоты/screenshots

tranzam1.jpg

tranzam2.jpg

tranzam3.jpg

Да, осталась пара issues, но они не смертельны. Удачи!

===================================================================================

На этом игры собственно все. Мне ещё удалось собрать несколько сторонних проектов – AXL_Projects, IsoMot, TileExtractor, но теперь думаю, вы сами без труда сможете при необохдимости собрать и потестировать их.

В этом нет ничего сложного!

AXL_Projects

, test2 :
 

axl.jpg

IsoMot

:
 

isomot.jpg

TileExtractor

 

tileex.jpg

===================================================================================

Всё, а ниже процесс грязной (первичной сборки), рывками конечно.

Часть Вторая

Retro games from: http://retrospec.sgn.net
Compiling with allegro and fmod?

Allegro:
liballegro4-dev (version 2:4.4.2-4) will be installed
liballegro4.4 (version 2:4.4.2-4) will be installed
libjpgalleg4.4 (version 2:4.4.2-4) will be installed
libxpm-dev (version 1:3.5.10-1) will be installed

Компилим примерно так:
$ gcc -o `allegro-config –cflags` `allegro-config –libs` 051sc.c

FMod нет в репозитории Mint'а?
See: https://wiki.debian.org/FMOD

ОК, качаем Линукс-версию с http://www.fmod.org/download
fmodstudioapi10602linux

Ой, новая версия несовместимса со старой. Ой-ой-ой… Ёёёёёёёперный театр!

Качаем отсюда: http://www.fmod.org/download-previous-products/
(http://www.fmod.org/download/fmodex/api/Linux/fmodapi44453linux.tar.gz)
Это fmod Ex, а не FMod Studio.

justy@justy ~/src/FMOD/fmodapi44453linux/api/inc $ sudo cp * /usr/include/
ls /usr/include/ | grep fmod

justy@justy ~/src/FMOD/fmodapi44453linux/api/lib $ ls
libfmodex-4.44.53.so libfmodex64.so libfmodexL64-4.44.53.so libfmodexL.so
libfmodex64-4.44.53.so libfmodexL-4.44.53.so libfmodexL64.so libfmodex.so
justy@justy ~/src/FMOD/fmodapi44453linux/api/lib $ sudo cp * /usr/lib/
justy@justy ~/src/FMOD/fmodapi44453linux/api/lib $ ls /usr/lib/ | grep fmod | wc -l
8
justy@justy ~/src/FMOD/fmodapi44453linux/api/lib $ ls | wc -l
8

Компилим Alien8

OK, вроде теперь audio откомпилилось!

justy@justy ~/src/retrospec/alien8_f $ make
c++ -w `allegro-config –cflags` -c -o internet.o internet.c
internet.c:13:22: fatal error: winalleg.h: No such file or directory
#include "winalleg.h"
^
compilation terminated.
make: *** [internet.o] Error 1

> Да, в intenent.c надо вставить свою ОС вместо Windows

justy@justy ~/src/retrospec/alien8_f $ make
g++ Alien8.o audio.o internet.o isomot.o juego.o tcj.o tiempo.o -o alien8 `allegro-config –libs` -lfmodex
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib/libfmodex.so when searching for -lfmodex
/usr/bin/ld: skipping incompatible /usr/lib/../lib/libfmodex.so when searching for -lfmodex
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../libfmodex.so when searching for -lfmodex
/usr/bin/ld: skipping incompatible //usr/lib/libfmodex.so when searching for -lfmodex
/usr/bin/ld: cannot find -lfmodex
collect2: error: ld returned 1 exit status
make: *** [alien8] Error 1

> Да, в Makefile надо всатвить -lfmodex64 вместо -lfmodex, так как мы компилим под x64 систему.

justy@justy ~/src/retrospec/alien8_f $ make
g++ Alien8.o audio.o internet.o isomot.o juego.o tcj.o tiempo.o -o alien8 `allegro-config –libs` -lfmodex64
internet.o: In function `inet_connect(char*)':
internet.c:(.text+0x38): undefined reference to `curl_global_init'
internet.c:(.text+0x50): undefined reference to `curl_easy_init'
internet.c:(.text+0x85): undefined reference to `curl_easy_setopt'
internet.c:(.text+0xa2): undefined reference to `curl_easy_setopt'
internet.c:(.text+0xc0): undefined reference to `curl_easy_setopt'
internet.c:(.text+0xcf): undefined reference to `curl_easy_perform'
internet.c:(.text+0xfb): undefined reference to `curl_easy_setopt'
internet.o: In function `inet_close()':
internet.c:(.text+0x13f): undefined reference to `curl_easy_cleanup'
internet.c:(.text+0x144): undefined reference to `curl_global_cleanup'
internet.o: In function `inet_sendscore(char*, char*)':
internet.c:(.text+0x1ca): undefined reference to `curl_easy_setopt'
internet.c:(.text+0x1d9): undefined reference to `curl_easy_perform'
collect2: error: ld returned 1 exit status
make: *** [alien8] Error 1

> Ой, он curl хочет? Сейчас дадим!

=========================================================================================

Compiling Humphrey:
Надо установить dumb вместо fmod'а для аудио!

DUMB is a tracker library with support for IT, XM, S3M and MOD files. It
targets maximum accuracy to the original formats, with low-pass resonant
filters for the IT files, accurate timing and pitching, and three resampling
quality settings (aliasing, linear interpolation and cubic interpolation).

Selecting previously unselected package libdumb1:amd64.
(Reading database … 174699 files and directories currently installed.)
Preparing to unpack …/libdumb1_1%3a0.9.3-6_amd64.deb …
Unpacking libdumb1:amd64 (1:0.9.3-6) …
Selecting previously unselected package libdumb1-dev:amd64.
Preparing to unpack …/libdumb1-dev_1%3a0.9.3-6_amd64.deb …
Unpacking libdumb1-dev:amd64 (1:0.9.3-6) …
Setting up libdumb1:amd64 (1:0.9.3-6) …
Setting up libdumb1-dev:amd64 (1:0.9.3-6) …
Processing triggers for libc-bin (2.19-0ubuntu6.6) …

> Нет, та же ошибка:
justy@justy ~/src/retrospec/Humphrey/src $ make
c++ -w `allegro-config –cflags` -c -o audio.o audio.c
audio.c:11:20: fatal error: aldumb.h: No such file or directory
#include <aldumb.h>
^
compilation terminated.
make: *** [audio.o] Error 1

> Оказывается, есть aldumb.h – версия DUMB для Allegro (al – это от allegro).

Устанавливаем пакет:
Selecting previously unselected package libaldmb1:amd64.
Preparing to unpack …/libaldmb1_1%3a0.9.3-6_amd64.deb …
Unpacking libaldmb1:amd64 (1:0.9.3-6) …

Нам нужна dev версия:
Preparing to unpack …/libaldmb1-dev_1%3a0.9.3-6_amd64.deb …
Unpacking libaldmb1-dev:amd64 (1:0.9.3-6) …
Setting up libaldmb1-dev:amd64 (1:0.9.3-6) …

> Да, прошло, но теперь:
c++ -w `allegro-config –cflags` -c -o humphrey.o humphrey.c
humphrey.c: In function ‘int main()’:
humphrey.c:126:12: error: ‘ini_texto’ was not declared in this scope
ini_texto();
^
humphrey.c:145:12: error: ‘fin_texto’ was not declared in this scope
fin_texto();
^
humphrey.c: In function ‘void elegir_idioma()’:
humphrey.c:172:65: error: ‘escribir_texto’ was not declared in this scope
escribir_texto(buffer,"elige tu idioma",96,150,f<31?256-f*8:0);

humphrey.c должен быть откомпилен последним?
Нет, просто include "text.h" потерялось для двух файлов.

Пришлось тажке добавить флаг компилятор -fpermissive, чтобы перестал ругаться на conversion error,
а также для линковщика присобачить две dumb либы: -ldumb -laldmb

Игра работает, но звука нет. Чёртов dumb не хочет звучать что-то…

А вот FMOD в предыдущей проге (alien8) вроде звучал нормально!

========================================================================================
15 May 2015AD: DeflektorX4 с помощью Makefile от Alien8 скомпилился практически сразу,
только в internet.c пришлось опять же закомментить строку WIN, и откомментить LIN.
========================================================================================

16 May 20156AD: Пробуем TranzAm

Очуметь, тут папки code и external.
Скопировал Makefile из Alien8, поменял TARGET, c => cpp, плюс ещё каталог external добавил

OBJECTS = $(SOURCES:.cpp=.o) – заметь, cpp вместо c раньше мы только с C работали.

Пошло вроде ОК, но:
c++ -w -fpermissive `allegro-config –cflags` -c external/lib_fmod.cpp -o external/lib_fmod.o
external/lib_fmod.cpp:1:29: fatal error: ..\game_transam.h: No such file or directory
#include "..\game_transam.h"

Так как мы в эту диру не переходим, всё ищется в текущей дире?
Попробуем убрать .. в этом include (файл external/lib_fmod.cpp).
А, нет, тут надо обратный слэш виндовый заменить на прямой. Тогда пошло вроде, но эти слеши ещё есть:
justy@justy ~/src/retrospec/TranzAm/src/code/code $ make
c++ -w -fpermissive `allegro-config –cflags` -c external/lib_fmod.cpp -o external/lib_fmod.o
In file included from external/lib_fmod.cpp:1:0:
external/../game_transam.h:13:36: fatal error: external\axl_framework.h: No such file or directory
#include "external\axl_framework.h"
^
compilation terminated.
make: *** [external/lib_fmod.o] Error 1

> Меняем здесь все обратные слэши на прямые.
> OK, пошло.

Зачем он mappyal.c пытается скомпилить? Перемещаем его в папку adime (в external),
чтобы не мешал блин. ОК, дальше:

c++ -w -fpermissive `allegro-config –cflags` -c external/retrospec_highscore.cpp -o external/retrospec_highscore.o
external/retrospec_highscore.cpp: In function ‘size_t WriteMemoryCallback(void*, size_t, size_t, void*)’:
external/retrospec_highscore.cpp:28:50: error: ‘memcpy’ was not declared in this scope
memcpy(&(mem->memory[mem->size]), ptr, realsize);

Блин, в каком инклуде есть этот memcpy? В unistd.h или в stdlib.h? ^
Нет, это как ни странно, в string.h: #include <string.h>

ОК, а вот ошибка сранная вылезла:
c++ -w -fpermissive `allegro-config –cflags` -c external/retrospec_highscore.cpp -o external/retrospec_highscore.o
external/retrospec_highscore.cpp:187:1: error: ‘dest’ does not name a type
dest[pos_dest-added_bytes] = 0;
^
external/retrospec_highscore.cpp:188:1: error: expected declaration before ‘}’ token
}
^
make: *** [external/retrospec_highscore.o] Error 1

В этом файле похоже вообще 2 висячие строки остались.
Комментим их… Да, прошло, но:
c++ -w -fpermissive `allegro-config –cflags` -c external/stringtokeniser.cpp -o external/stringtokeniser.o
external/stringtokeniser.cpp: In member function ‘int StringTokenizer::nextIntToken()’:
external/stringtokeniser.cpp:116:35: error: ‘atoi’ was not declared in this scope
return atoi(nextToken().c_str());
^
external/stringtokeniser.cpp: In member function ‘double StringTokenizer::nextFloatToken()’:
external/stringtokeniser.cpp:122:35: error: ‘atof’ was not declared in this scope
return atof(nextToken().c_str());
^
make: *** [external/stringtokeniser.o] Error 1

В какой либе есть atoi или atof?: #include <stdlib.h>

Да, всё остальное откопилилось без проблем, но линковщик выдал кучу ошибок.

——————————————————————-
adime – одна из них, что это за зверь таков?

Короче, в исходниках она у него не доделана,
берём отсюда: http://adime.sourceforge.net/#download
Инструкции по сборке берем отсюда: http://adime.sourceforge.net/build/linux.txt

Ага, линковщик ругается:
/usr/bin/ld: obj/unix/adimd/adialogf.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC

Надо добавить -fPIC, чтобы это значило?
Ответ Геральда:
In much older versions of the compiler gcc there was a flag "-fPIC" which was an abbreviation for Position Independent Code, and this had to be passed to create library code objects, without that flag, code that is specific to the source would be used, and then the library would fail.

Gerald

Спасибо тебе, Геральд!

ОК, добавим этот самый -fPIC в misc/makefile.uni этого пакета:
CFLAGS += -fPIC

Не помогло, почему-то именно этот злополучный файл компилится без -fPIC
Помогло: добавил -fPIC во все gcc вызовы компиляции.

Опять ошибка:
/usr/bin/ld: cannot find -lalleg-debug
На хер мне дебаг нужен??

ОК, пытаемся рекомпилить с выключенным дебагом:
$ make clean
$ ./fix.sh unix
$ make

Опппаа:
Compiling Adime for UNIX, optimized. Please wait.
gcc -fPIC -DADIME_SRC -Wall -Wno-unused -mcpu=pentium -O2 -funroll-loops -ffast-math -fomit-frame-pointer -fno-strength-reduce -Wno-deprecated-declarations -I./include -o obj/unix/adime/adialogf.o -c src/adialogf.c
gcc: warning: ‘-mcpu=’ is deprecated; use ‘-mtune=’ or ‘-march=’ instead
src/adialogf.c:1:0: error: CPU you selected does not support x86-64 instruction set
/* _ _ _

Зачем он cpu использует? GCC сам определим оптимальный cpu, наверное…
Убираем в makefile.uni -mcpu pentium. У меня давно уже sextium! 🙂

> Урра, теперь всё откомпилиось успешно:
The optimized library for UNIX has been compiled.
Run make install to complete the installation.

Пытаемся поставить в /usr/lib, а не в /usr/local/lib:
$ su
justy adime-2.2.1 # SYSTEM_DIR=/usr make install

OK:
install -m 755 -d /usr/lib
install -m 644 lib/unix/libadime.so /usr/lib/libadime.so
install -m 755 -d /usr/include
install -m 644 include/adime.h /usr/include/adime.h
install -m 755 -d /usr/include/adime
install -m 644 include/adime/adimecfg.h /usr/include/adime/adimecfg.h
install -m 755 -d /usr/include/adime
install -m 644 include/adime/adimeint.h /usr/include/adime/adimeint.h
The optimized UNIX library has been installed.
Run make install-man to install the man pages.
Run make install-info to install the info documentation.
You may conserve space by instead running make install-gzipped-man
and/or make install-gzipped-info.
justy adime-2.2.1 # exit
exit
justy@justy ~/src/retrospec/TranzAm/src/adime-2.2.1 $

Всё с этой ADIME??? Да, ошибки линковщика с adime исчезли, но осталась куча других ошибок!

——————————————————————-

Следующий зверь fblend:https://sourceforge.net/projects/fblend/

Description

Do you think the Allegro blenders are too slow? Do you want to do special
effects in 15/16/32-bpp but can't? Then FBlend is for you! FBlend is
a series of super-fast color blenders for Allegro.
FBlend is anywhere from 4 to 13 times faster than Allegro,

Да,скачиваем zip 2002 года (хо-хо, ой), следуем инстуркциям.
Файл ./fix.sh делаем исполняемым и приводим к Юникс-концам строк в sublime text.

$ ./fix.sh
$ make

ake: *** No rule to make target `/asmdef.c', needed by `obj/unix/asmdef.exe'. Stop.

В фалй make/makefile.lst они забыли добавить файлик asmdef.c, добавляем.
Нет, это не помогло. Да и что за ошибка тупая, какой obj/unix/asmdef.exe?
EXE файлы в DOS и Windows используются, но никак не в Unix/Linux

Убираем этот файлик из lst и ищем дальше.

В инете я наткнулся на адрес:
https://www.allegro.cc/forums/thread/363177
У них такие же проблемы возникали, как и у меня (это 2004 год!), и обсуждение остановилось как раз на этой тупой ошибке.

> ОК, в файле make.makefile.tst в конце в 4 строчках есть упоминания asmdef.exe
Комментируем их, и компиляция продолжается:
gcc -x assembler-with-cpp -c src/x86/cadd16.s -o obj/unix/release/x86/cadd16.o -I. -Isrc -Iinclude
src/x86/cadd16.s: Assembler messages:
src/x86/cadd16.s:33: Error: invalid instruction suffix for `push'

Там стоит pushl. Видимо, ошибка из-за того, что мы пытаемся откомпилить 32-битный код
64-битный компилятором. Попробуем флаг -m32

Добавляем в файл make/makefile.unx в 82-ой строке (где gcc -x assembler-with-cpp) этот флаг:
gcc -m32 -x assembler-with-cpp -c $< -o $@ $(GCC_INCLUDE_PATHS)

Всё, прошло:
justy@justy ~/src/retrospec/TranzAm/src/fblend $ make


gcc -m32 -x assembler-with-cpp -c src/x86/mem_test.s -o obj/unix/release/x86/mem_test.o -I. -Isrc -Iinclude
ar rs lib/unix/libfblend.a obj/unix/release/cadd.o obj/unix/release/mem_test.o obj/unix/release/csub.o obj/unix/release/ctrans.o obj/unix/release/radd.o obj/unix/release/rtrans.o obj/unix/release/2xstretch.o obj/unix/release/fade.o obj/unix/release/x86/cadd16.o obj/unix/release/x86/cadd15.o obj/unix/release/x86/cadd32.o obj/unix/release/x86/csub16.o obj/unix/release/x86/csub15.o obj/unix/release/x86/csub32.o obj/unix/release/x86/trans16.o obj/unix/release/x86/trans15.o obj/unix/release/x86/trans32.o obj/unix/release/x86/radd16.o obj/unix/release/x86/radd15.o obj/unix/release/x86/radd32.o obj/unix/release/x86/rtrans16.o obj/unix/release/x86/rtrans15.o obj/unix/release/x86/rtrans32.o obj/unix/release/x86/2xstretch.o obj/unix/release/x86/fade16.o obj/unix/release/x86/fade15.o obj/unix/release/x86/fade32.o obj/unix/release/x86/mem_test.o
ar: creating lib/unix/libfblend.a

Success!
Now run make install to install FBlend.
————————————————————————

Перед инсталляцией я поменял в фале makefile.unx:
INSTALL_DIR = /usr (вместо /usr/local)

$ sudo make install
cp lib/unix/libfblend.a /usr/lib
cp include/fblend.h /usr/include

FBlend is now installed.

————————————————————————
Возвращаемся в основную ветвь Tranzam, и для линковщика в Makefile добавим: -lfblend
Пробуем!

Неа:
Ошибка с fblend вылезли, но уже другие, типа:
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib/libfblend.a(rtrans.o): In function `fblend_rect_trans':
rtrans.c:(.text+0x159): undefined reference to `fblend_rect_trans_mmx_15'
rtrans.c:(.text+0x30e): undefined reference to `fblend_rect_trans_mmx_16'
rtrans.c:(.text+0x321): undefined reference to `fblend_rect_trans_sse_15'
rtrans.c:(.text+0x361): undefined reference to `fblend_rect_trans_mmx_32'
rtrans.c:(.text+0x374): undefined reference to `fblend_rect_trans_sse_16'
rtrans.c:(.text+0x5fc): undefined reference to `fblend_rect_trans_sse_32'
collect2: error: ld returned 1 exit status
make: *** [TranzAm] Error 1

То есть сама либа fblend внеутри себя не может найти функции
касающиеся mmx и sse. Возможно, потому что мы компилили ассемблер с флагом -m32?

Может перекомпилить fblend без них?

OK, fblend хрен с ним, но похоже он использует FMOD3, а не FMODEx, а они отличаются API.

http://www.fmod.org/index.php/release/version/fmodapi375linux.tar.gz
нету, нашел здесь: ftp://ftp.pl.freebsd.org/vol/rzm1/linux-gentoo/distfiles/fmodapi375linux.tar.gz,
но нету x64 версии, а мне она нужна! )))

Что быстрее – переделать вызовы FMOD 3 в FMOD Ex или откопилить прогу в 32 бита?
-m32 включает 32-битный компайлинг?

Сперва пробую перекомпилить fblend в 32-bit:
gcc -m32 -c src/cadd.c -o obj/unix/release/cadd.o -O2 -ffast-math -fomit-frame-pointer -I. -Isrc -Iinclude
In file included from /usr/include/errno.h:28:0,
from /usr/include/allegro/base.h:24,
from /usr/include/allegro.h:25,
from src/cadd.c:17:
/usr/include/features.h:374:25: fatal error: sys/cdefs.h: No such file or directory
# include <sys/cdefs.h>
^
compilation terminated.
make: *** [obj/unix/release/cadd.o] Error 1

Надо также доустанавливать 32-битные либы:
apt-get install gcc-multilib
и apt-get install g++-multilib libc6-dev-i386

Да, fblend устновлися вроде нормально.
Теперь пробуем adime.

Ага, подключили флаги -m32, откомпилися ОК, выдал вот что:
gcc -s -shared -o lib/unix/libadime.so obj/unix/adime/adialogf.o obj/unix/adime/adime.o obj/unix/adime/calcedit.o obj/unix/adime/dbool.o obj/unix/adime/dbutrow.o obj/unix/adime/dbutton.o obj/unix/adime/dchain.o obj/unix/adime/ddialogf.o obj/unix/adime/dfilenam.o obj/unix/adime/dfloat.o obj/unix/adime/dgreybol.o obj/unix/adime/dinteger.o obj/unix/adime/dline.o obj/unix/adime/dlists.o obj/unix/adime/dnothing.o obj/unix/adime/dpfloat.o obj/unix/adime/dpintege.o obj/unix/adime/dstring.o obj/unix/adime/fsel.o obj/unix/adime/gui.o obj/unix/adime/eval.o obj/unix/adime/keynames.o obj/unix/adime/nan.o obj/unix/adime/parsemod.o obj/unix/adime/register.o `allegro-config –libs release –shared` -m32
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/liballeg.so when searching for -lalleg
/usr/bin/ld: cannot find -lalleg
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libc.so when searching for -lc
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libc.a when searching for -lc
collect2: error: ld returned 1 exit status
make: *** [lib/unix/libadime.so] Error 1

Да, ему нужна в частности 32-битная либа allegro. А как её поставить на x86_64 machine?

Скачал deb и установил отюда:
https://launchpad.net/ubuntu/+source/allegro5/2:5.0.11-1/+build/7389169

liballegro5.0_5.0.11-1_i386.deb
liballegro5-dev_5.0.11-1_i386.deb

Да, тут неувязочска выходит. Дев пакет не ставится, ему мешает аллегро4-дев.
Но он также зависит от ряда других пакетов, а у меня стоят только их 64-битные аналоги,
то есть по идее мне нужно устанавливать 32-битные аналоги этих пакетов.

Проще в FMOD разобраться!

Итак, я перекомпилил
adime-2.2.1
fblend (отключил MMX и SSE в include/mmx.h include/sse.h),
так что ассемблерный код совсем не компилися.

$ sudo make install
cp lib/unix/libfblend.a /usr/lib
cp include/fblend.h /usr/include

FBlend is now installed.

Да, нашел minifmod в течение нескольких часов,
теперь пытаюсь скомпилить его вместе с проектом.

Ошибка gcc – не распознаёт констанут __LINUX__,
ему только __linux__ нужна?

В gcc добавляю флаг -D__LINUX__ Очуметь, прошло дальше но на ассемблрном коде запнулось:

gcc -D__LINUX__ -w -fpermissive `allegro-config –cflags` -c -o external/minifmod/mixer_clipcopy.o external/minifmod/mixer_clipcopy.c
external/minifmod/mixer_clipcopy.c: Assembler messages:
external/minifmod/mixer_clipcopy.c:42: Error: incorrect register `%rsi' used with `l' suffix
external/minifmod/mixer_clipcopy.c:53: Error: incorrect register `%rdi' used with `l' suffix
external/minifmod/mixer_clipcopy.c:54: Error: incorrect register `%rcx' used with `l' suffix
make: *** [external/minifmod/mixer_clipcopy.o] Error 1

удаляем (комментируем) 20 строку в mixer_clipcopy.c
#define USE_ASM_FOR_CLIPCOPY
чтобы он не использовал ассемблер.

Вылезла ещё тупая ошибка:
gcc -D__LINUX__ -w -fpermissive `allegro-config –cflags` -c -o external/minifmod/mixer_clipcopy.o external/minifmod/mixer_clipcopy.c
external/minifmod/mixer_clipcopy.c: In function ‘FSOUND_MixerClipCopy_Float32’:
external/minifmod/mixer_clipcopy.c:34:30: error: lvalue required as increment operand
int val = *((float *)src)++;
^
external/minifmod/mixer_clipcopy.c:35:21: error: lvalue required as increment operand
*((short *)dest)++ = (val < -32768 ? -32768 : val > 32767 ? 32767 : val);
^
make: *** [external/minifmod/mixer_clipcopy.o] Error 1

GCC 4 не разрешает такие вещи, что разрешал GCC 3
Итак, я ставлю g++3.3:
sudo apt-get install g++-3.3

попробую им сейчас всё скомпилить, тольо кбурать флаг -fpermissve надо будет?!
Он мне поставил CLANG (clang) – замена GCC (зачем?),
но этот тупица выдал ту же ошибку про lvalue

ОК, возвращаюсь к GCC (русть clang остётся, но зачем он нужен?)

Исправил эти две строки, разбил их на более простые с присвоением указателей.

Пошло, minifmod откомпилися!

Но опять же ошибки поехали в lib_fmod.cpp:
gcc -D__LINUX__ -w -fpermissive `allegro-config –cflags` -c external/lib_fmod.cpp -o external/lib_fmod.o
In file included from external/../game_transam.h:22:0,
from external/lib_fmod.cpp:1:
external/../external/lib_fmod.h:8:1: error: ‘FSOUND_STREAM’ does not name a type
FSOUND_STREAM* PlaySampleFromStream(const char* filename, int& channel,int volume=-1, long length=0, long additionalmode=0);
^
external/../external/lib_fmod.h:9:17: error: variable or field ‘StopStream’ declared void
void StopStream(FSOUND_STREAM* fs);
^
external/../external/lib_fmod.h:9:17: error: ‘FSOUND_STREAM’ was not declared in this scope
external/../external/lib_fmod.h:9:32: error: ‘fs’ was not declared in this scope
void StopStream(FSOUND_STREAM* fs);

Очуметь, оказывается в этом minifmod нет FSOUND_STREAM!
Да, похоже minifmod тут не подойдет – нет FSOUND_STREAM

Придётся с FMOD3 на FMODEx всё же переделывать!
Поиск google: fmod3 fmodex
Здесь полезная инфо: http://www.fmod.org/questions/question/forum-33249

Wine мне открыл fmodex.chm и я нашёл секцию TRANSITIONING BETWEEN FMOD 3 AND FMOD EX. API DIFFERENCES

Скопировал текст статьи, открыл LibreOffice Write, вставил туда текст
(получлось красиво 2 листа), распечатал их. Красота!
Сейчас буду изучать и переделывать код lib_fmod.cpp на FMODEX!

Музыку почти переделал, только не пойму где аналог FMUSIC_SetOrder под FMOD 4?
Чтобы это значило?
В документации написано:
Sets a songs order position / current playing position.

Что означает сия позиция? Что она даёт?? Это позиция отностилеьно чего?

Ладно, пока оставляем её в покое, двигаемся дальше.
Если что, вернемся к этому позднее.

Стоп, так он закомментирован, поэтому по любому двигаемся дальше!

Так, уже 18 мая, перделал lib_fmod с FMOD 3 на FMOD Ex (FMOD 4)
Исправил ещё кучу ляпов и вызовов старого fmod в game_*.cpp файлах.

Добавил функцию (помиомо некоторых прочих) UpdateSound()
и вызываю её в WinGameLogic() в файле game_wingame.cpp,
как того требует документацию к FMOD Ex.

Пробуем компилить, ох, линковщика я опасаюсь!

Охх, очередной FMOD'освский старый ляп вышел (в послденем файле проекта!):
gcc -D__LINUX__ -w -fpermissive `allegro-config –cflags` -c game_wingame.cpp -o game_wingame.o
game_wingame.cpp: In function ‘void GameEndInitialiseEndScreen()’:
game_wingame.cpp:110:41: error: ‘FMUSIC_SetMasterSpeed’ was not declared in this scope
FMUSIC_SetMasterSpeed(ActiveMusic,0.8);
^
Что за FMUSIC_SetMasterSpeed() ? Сейчас будем разбираться…

Делаем функцию SetSpeedTrackerSong в lib_fmod'е и вызываем её в game_wingame.cpp
вместо FMUSIC_SetMasterSpeed. ActiveMusic мы тут убираем, так она она одна, и так ясно.

Тем более в FMOD Ex есть подобная функция FMOD_Sound_SetMusicSpeed

Последний $ make!

Да, линкер ругается:
g++ external/axl_animations.o external/axl_config.o external/axl_framework.o external/funkyfont.o external/lib_fmod.o external/mappyal.o external/retrospec_highscore.o external/stringtokeniser.o external/tinystr.o external/tinyxml.o external/tinyxmlerror.o external/tinyxmlparser.o external/water.o game_astar.o game_car.o game_credits.o game_game.o game_help.o game_highscore.o game_levels.o game_logo.o game_menu.o game_particles.o game_pause.o game_ScrollingText.o game_startcountdown.o game_transam.o game_vars.o game_wingame.o -o TranzAm -lpthread -lfmodex64 -ladime -lfblend `allegro-config –libs` `curl-config –libs`
game_car.o: In function `Car::SetCurrentState()':
game_car.cpp:(.text+0x464): undefined reference to `MapChangeLayer'
game_car.cpp:(.text+0x487): undefined reference to `MapGetBlockInPixels'
game_car.cpp:(.text+0x534): undefined reference to `MapChangeLayer'

Какую либу я забыл присобачить в -l ?
Сейчас поищем в интернете эти функции.

А, нет, это вылезло видимо от того, что для компиляции мы испорльзовали gcc,
а для сборки – g++.

Я сейчас исправил это всё на gcc, и вылезла уже другая ошибка линковщика, но всего одна! Уррра!

gcc external/axl_animations.o external/axl_config.o external/axl_framework.o external/funkyfont.o external/lib_fmod.o external/mappyal.o external/retrospec_highscore.o external/stringtokeniser.o external/tinystr.o external/tinyxml.o external/tinyxmlerror.o external/tinyxmlparser.o external/water.o game_astar.o game_car.o game_credits.o game_game.o game_help.o game_highscore.o game_levels.o game_logo.o game_menu.o game_particles.o game_pause.o game_ScrollingText.o game_startcountdown.o game_transam.o game_vars.o game_wingame.o -o TranzAm -lpthread -lfmodex64 -ladime -lfblend `allegro-config –libs` `curl-config –libs`
/usr/bin/ld: external/axl_animations.o: undefined reference to symbol '_ZSt28_Rb_tree_rebalance_for_erasePSt18_Rb_tree_node_baseRS_@@GLIBCXX_3.4'
//usr/lib/x86_64-linux-gnu/libstdc++.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
make: *** [TranzAm] Error 1

Что за _ZSt28_Rb_tree_rebalance_for_erasePSt18_Rb_tree_node_baseRS_ ?
Ёперный театр, я не выговорить это не могу, не то что написать!

Перетасовал -l в Makefile, получилась другая немного ошибка:
/usr/bin/ld: external/funkyfont.o: undefined reference to symbol 'cos@@GLIBC_2.2.5'
//lib/x86_64-linux-gnu/libm.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

Уже cos ему не нравится, но это полегче, чем _ZSt28_Rb_tree_rebalance_for_erasePSt18_Rb_tree_node_baseRS_

Так мой же ответ здесь есть:
http://stackoverflow.com/questions/23809404/issue-with-simple-makefile-undefined-reference-to-symbol-cosglibc-2-2-5

"Also try to use c++ instead of gcc compiler. It includes this library by default."

Вот ещё инфо по этому вопросу:
use g++ to compile C++ programs, it'll link in the standard c++ library. gcc will not. gcc will also compile your code as C code if you give it a .c suffix. Give your files a .cpp suffix.

Сейчас попробуем переключиться на c++ и пересобрать проект!

Нет, линковщик стал ругаться на либы типа MapChangeLayer, MapGetBlockInPixels и подобные.
Они все находятся в mappyal.cpp, то есть мы неверно его подключаем?

Ага, в mappyal.h я нашел такую штуку:
#ifdef __cplusplus
extern "C" {
#endif

В других файлах проекта такой штуки нет, значит – убираем её???

Да, сделал make clean && make (может можно использовать make -B?)
и вылезло уже всего две жалобы линкера:
game_game.o: In function `LoadLevel()':
game_game.cpp:(.text+0x29fc): undefined reference to `MapLoadVRAM(char*)'
game_game.cpp:(.text+0x2b8e): undefined reference to `MapLoad(char*)'
collect2: error: ld returned 1 exit status
make: *** [TranzAm] Error 1

Это уже получше, вместо нескольих десятков в прошлый раз.
Ищем эти функции в mappyal.cpp и в mappyal.h.
Ага, видем несоответсие. Они объявлены в mappyal.h как char *,
а в mappyal.cpp как unsigned char *

Исправляем на unsigned char * в mappyal.h, make -B пробуем!

Ооо, компиляция удалась:
……
……
c++ -D__LINUX__ -w -fpermissive `allegro-config –cflags` -c game_vars.cpp -o game_vars.o
c++ -D__LINUX__ -w -fpermissive `allegro-config –cflags` -c game_wingame.cpp -o game_wingame.o
c++ external/axl_animations.o external/axl_config.o external/axl_framework.o external/funkyfont.o external/lib_fmod.o external/mappyal.o external/retrospec_highscore.o external/stringtokeniser.o external/tinystr.o external/tinyxml.o external/tinyxmlerror.o external/tinyxmlparser.o external/water.o game_astar.o game_car.o game_credits.o game_game.o game_help.o game_highscore.o game_levels.o game_logo.o game_menu.o game_particles.o game_pause.o game_ScrollingText.o game_startcountdown.o game_transam.o game_vars.o game_wingame.o -o TranzAm `curl-config –libs` `allegro-config –libs` -lfmodex64 -ladime -lfblend
o If there are no errors, TranzAm compiled succesfully

Что странно, так как я заметил краем глаза в MapLoadABM такой же косяк,
но линкер почему то его пропустил?
Скорее всего, это потому что не было вызова его извне. Будет – невнимательный косяк возникнет.

Итак, смотрим, что у нас получилось:
justy@justy ~/src/retrospec/TranzAm/src/src $ cp TranzAm ../..
justy@justy ~/src/retrospec/TranzAm/src/src $ cd ../..
justy@justy ~/src/retrospec/TranzAm $ ./TranzAm

Ууупс, чёрный дисплей, всё повисло. Ни на что не реагирует.
Но это линукс, его убить не так-то легко.

Я залогинся на другом tty (Ctrl+Alt+F1) – можно выбирать любой от F1 до F6
На Ctrl+Alt+F8 у нас висят X + Cinnamon, на Ctrl+Alt+F7 сообщения ядра, что ли, не пойму.
Они появлялись, только когда я пару раз переключился на этот tty.

Итак, залонигился на tty1 под рутом, нажал w, увидел висящий процесс ./TranzAm,
убил его killall TranzAm, потом переключился на tty8 и выставил разрешение экрана обычное
(игра мне его сбила). Если окна не видно, я использую Alt+F7 shorcut для сдвига окна в центр.

Уупс, видим ошибку:
*** Error in `./TranzAm': malloc(): memory corruption: 0x0000000002064130 ***
Aborted

Блин, где она? Использовать gdb? Я с 2008 года его не использовл, уже забыл как там и что!

Подключаем -lpthread в Makefile и пытаемся пересобрать проект:
$ make -B

Не помогло. Видимо, ошибка возникает здесь:
this->GameAnimationLibrary=new AnimationLibrary(AnimationFile, GameConfiguration->CapsSystem.fps,maxtype, BitmapCreator);

Нашёл, где он запинается:
//Now we have a proper mask structure allocated and mostly initialized, but the mask data has garbage! We have to initialize it to 0's:
for(INTVAR=maskout->h-1; INTVAR>=0; INTVAR–) {
for(INTVAR2=maskout->max_chunk; INTVAR2>=0; INTVAR2–) {
maskout->sp_mask[INTVAR][INTVAR2]=0;
}
}

в axl_animations.cpp

Здесь:
//Now that we have some more data, we allocate the mask data…
maskout->dat = malloc( (maskout->max_chunk+1) * 4 * Image->h );

Сделал * 8 и игра заработала, правда как-то кривова-то. Некоторые спрайты плывут,
некоторых вообще не видно. Зато музыка играет и некоторые эффекты!

И сама игра заверашется Segmentation fault!

Попробую использовать OpenLayer, ставлю новые пакеты:
liballeggl4.4:amd64 (2:4.4.2-4) …
libopenlayer2 (2.1-1) …
libopenlayer-dev (2.1-1) …

И ещё доставил: liballeggl4-dev (2:4.4.2-4) ..

В файле axl.h ставлю #define USE_OPENLAYER 1 (было 0)

Такая ошибка:

/usr/include/OpenLayer/Glyph.hpp:13:22: fatal error: ft2build.h: No such file or directory
#include <ft2build.h>
^
compilation terminated.
make: *** [external/axl_animations.o] Error 1

ОК, компиляция нужна с freetype, подключаем его для GCC:
CFLAGS: `freetype-config –cflags`
LDFLAGS: `freetype-config –libs`

Но вылезли тупые ошибки, откуда???:
external/axl_animations.h:224:8: error: expected unqualified-id before numeric constant
void DestroyAll();

external/axl_animations.cpp:1429:13: error: expression cannot be used as a function
DestroyAll();

external/axl_animations.cpp: At global scope:
external/axl_animations.cpp:1456:24: error: expected unqualified-id before numeric constant
void AnimationLibrary::DestroyAll()

В .h файле стояло:
AXL_BITMAP* GetFirstGraphic(const std::string& id) const;
void DestroyAll();
void draw() const;

Может ему не нравится const; на конце? Но почему ранее это прокатывалао?

ОК, отключаю openlayer, будем разбираться с тем что есть.
Кстати, для компиляции с openlayer также применяется связка
openlayer-config –cflags openlayer-config –libs : НА БУДУЩЕЕ!

Оказывыается в корне есть файлик config.xml, здесь можно настроить легко windowed режим окна,
работает иногда, но вылетает что-то.

———————————————————————————————
ОК, перехожу к компиляции JJ.
Удалил из корня три исходных файла – они все есть в дире sources.
mv sources src
Сделал все имена файлов маленькими буквами, а то опять файлы не находятся GCC.
Скопировал Makefile от Alien8, сделал некоторые изменения (c->cpp, TARGET->ff,
убрал лишние либы, тут кроме аллегро похоже ничего не примаенятеся и слава Богу)

Итак: любимый make
c++ -w `allegro-config –cflags` -c main.cpp -o main.o
main.cpp: In member function ‘virtual void CAllegro::init_game()’:
main.cpp:115:18: error: ‘GFX_GDI’ was not declared in this scope
if(set_gfx_mode(GFX_GDI,SVGA_W,SVGA_H,0,0)<0) abortsystem("Cannot set/reset 640×480 8 bit mode!")

Mon May 18 20:18:45 MSK 2015
Блин, что с этим GFX_GDI не так? Может не та версия Аллегро опять? Сейчас погуглю немного…

Ааа, это только для Windows режим! Прописан в файлике /usr/include/allegro/platform/alwin.h

Поэтому меняем на GFX_AUTODETECT!

ОК:
sh_alleg.cpp:6:22: fatal error: iostream.h: No such file or directory
#include <iostream.h>
^
Поменял на #include <iostream>, посмотрим, что получится:
jj compiled succesfully

$ cd ..
$ ./jj

Игра играет только без музыки и звука почему-то?!
Запустил виндовый вариант под wine, музыки нет, а эффекты есть
(в настройках прописано и то и другое, я проверял)

======================================================================================
Ладно, попробуем jjwf (или jjws, виндовый файл называется jjws.exe почему-то, опечятка?)

Проводим почти те же манипуляции, что и с jj. Используем Makefile т jj.
TARGET = jjwf

$ make
c++ -w `allegro-config –cflags` -c game.cpp -o game.o
game.cpp:1162:4: error: missing terminating " character
char code[256]="ZX82;
^
game.cpp: In function ‘int playerUpdate()’:
game.cpp:1163:4: error: expected primary-expression before ‘int’
int scores[1]={thescore};
^
make: *** [game.o] Error 1

> И сразу же ошибка. Сразу видно опечятку, пропущена закрывающая кавычка. Исправляем.
ОК, пошло дальше:
sh_alleg.cpp:6:22: fatal error: iostream.h: No such file or directory
#include <iostream.h>

Такая же туфта с iostream. Убираем .h, надеемся что namespace std нам не помешает…

jjwf compiled succesfully

Игра запустилась, такая же почти как jj, звука/музыки опять же нет.
Да, игра сложная, первый уровень даже пройти не могу.
Почему они делают первые уровни такими сложными???

==============================================================
Ладно, пробуем styx
Сразу используем Makefile от jjwf, выходит такая же ошибка с iostream – правим.

c++ Game.o Intro.o Main.o My_sprit.o Sh_alleg.o Sh_sprit.o -o styx `allegro-config –libs`
styx compiled succesfully

Запускаем из диры выше!
Игра играет без звука. Прикольная, но туповатя…

====================================================================
И посденяя из списка retrospec: Isomot.
Это либа, которая может применяться в проектах.

Скомпилил демо-проект в папке Demo (IsomotDemo0Eng.c).
Удалил испанскую версию файла, а то линковщик ругался на двойной main.

Так же использовал gcc и -lm для сборки проекта.

isomot_demo compiled succesfully

Жаль, retrospec не даёт исходники Exolon, JetSetWilly, Head over Heels,
вот это игрушки крутые!
Их бы скомпилить.

——————————–
ЛаднО, попробую скомпилить AXL_project и демки к нему.

Wed May 20 09:43:09 MSK 2015
Создал папку src, туда в папке axl скопировал файлы AXLAnimations.
Подключил 4 теста из animations/example*

Подправил соотв. Makefile для компиляции одного main.cpp из папки test*/

Скомпилил достаточно быстро, подправив некоторые пути к включаемым файлам.
Та же ошибка malloc(): memory corruption: 0x0000000001fa17d0 ***

Это недостаточное выделение памяти в axl/axl_animations.cpp
строка 1095: maskout->dat = malloc( (maskout->max_chunk+1) * 4 * Image->h );

* 4 меняем на * 8.
Компилим, запускаем.

Ура, test1 запустился. Но я хочу переделать Makefile так, что бы он собирал несколько
программ за раз.

Ищем инфо, нашел первый сайт:
http://stackoverflow.com/questions/5950395/makefile-to-compile-multiple-c-programs

Да, всё получилось, test1..test4 скомпилились в .elf файлы и запустились.
При нажатии 'd' один раз вылетел с Segmentation fault

Wed May 20 11:56:58 MSK 2015
Всё, наигрался, всё получилось, больше вроде не вылетало.
Чтож тогда tranzAm вылетает?

И не смог избавиться от удаления .o после сборки. Я не хочу их удалять!
Всё, погугили, понял – это станадартное поведление make.
Надо их в правило пустое включить, чтобы не удалялись,

That is not what -pedantic is for. -pedantic will remove any non-ansi features that even -ansi permits.
-Werror will change warnings to errors. – jtniehof May 10 '11 at 14:09

Note that if you want both programs to be built automatically, the all target must appear before the individual programs in the makefile, and be the first target in the makefile. – Randall Cook Jan 8 '14 at 2:15

=================================================================

Возвращаюсь к TranzAm.
Пересоберу проект с -g -ggdb3

Опппаа, помните, не было звука Аллегро?
Я нашёл в пакетах liballegro4.4-plugin-alsa:amd64
и в описании:
This plugin adds support for ALSA to the Allegro library. It is recommended
on Linux. If no audio plugin package is installed, OSS is used for audio.

Так OSS уже давно устарело, какого они его используют?
Может сейчас звук зарабоатет в играх с аллегро?

Да, заработал! Поразительно, как такое может быть??? Кааак?

Это же Линукс, блин! Но почему, почему?

Зачем они по умолчанию используют OSS, которая много лет назад устарела,
ав не используют ALSA по-умолчанию, которая уже много лет в линукс в фавритах?
Почему? И ведь это Аллегро 4 у меня установился по-умолчанию,
я вижу в репозитории Аллегро-5, но он почему-то не поставился по умолчанию мне.

ОК. Просто поставьте liballegro4.4-plugin-alsa:amd64 и у Вас заработает звук в Аллеграо-играх!

Итак, потерял много часов с этим TransAm, боролся с исходниками, потом с линковщиком.
Потом с AdiMe – всё же решил использовать внешние либы.
Потом с FBLEND – непонятно, даже когда USE_FBLEND=0 он использует функции
void fblend_rect_trans(BITMAP *dst, int x, int y, int w, int h, int color, int fact);
void fblend_trans(BITMAP *src, BITMAP *dst, int x, int y, int fact);
Я сделал их DUMMY, но всё равно вылетает где-то в funkyfont.cpp.

Блин, надо попробовать в 32- разрядной системе скомпилировать всё это добро!

Для этого мне нужен VirtualBox!

Вот собрал на 2-ой день TransAm32:

/usr/lib/gcc/i686-linux-gnu/4.8/../../../../lib/libfblend.a(rtrans.o): In function `fblend_rect_trans':
rtrans.c:(.text+0x161): undefined reference to `fblend_rect_trans_mmx_15'
rtrans.c:(.text+0x2fa): undefined reference to `fblend_rect_trans_mmx_16'
rtrans.c:(.text+0x32b): undefined reference to `fblend_rect_trans_sse_15'
rtrans.c:(.text+0x387): undefined reference to `fblend_rect_trans_mmx_32'
rtrans.c:(.text+0x3b8): undefined reference to `fblend_rect_trans_sse_16'
rtrans.c:(.text+0x619): undefined reference to `fblend_rect_trans_sse_32'
collect2: error: ld returned 1 exit status
make: *** [TranzAm] Error 1
ubu@ubu-VirtualBox:~/src/TranzAm/src$ sudo rm /usr/lib/gcc/i686-linux-gnu/4.8/../../../../lib/libfblend.a
[sudo] password for ubu:
ubu@ubu-VirtualBox:~/src/TranzAm/src$ make
c++ external/axl_animations.o external/axl_config.o external/axl_framework.o external/funkyfont.o external/lib_fmod.o external/mappyal.o external/retrospec_highscore.o external/stringtokeniser.o external/tinystr.o external/tinyxml.o external/tinyxmlerror.o external/tinyxmlparser.o external/water.o game_astar.o game_car.o game_credits.o game_game.o game_help.o game_highscore.o game_levels.o game_logo.o game_menu.o game_particles.o game_pause.o game_ScrollingText.o game_startcountdown.o game_transam.o game_vars.o game_wingame.o -o TranzAm -lfmod-3.75 -ladime -lfblend `curl-config –libs` `allegro-config –libs`
!! TranzAm compiled succesfully
mv TranzAm ..

То есть ошибка линковщика в данном случае решилась удалнием файла
/usr/lib/gcc/i686-linux-gnu/4.8/../../../../lib/libfblend.a

Но он остался:
ubu@ubu-VirtualBox:~/src/fblend$ find libfblend
find: `libfblend': No such file or directory
ubu@ubu-VirtualBox:~/src/fblend$ locate libfblend
ubu@ubu-VirtualBox:~/src/fblend$ sudo updatedb
ubu@ubu-VirtualBox:~/src/fblend$ locate libfblend
/home/ubu/src/fblend/lib/mingw32/libfblend.a
/lib/libfblend.a

Только звука нет и клава криво работает! )))
Это скорее всего из-за виртуалки.

Но переходы работают нормально и шрифт показывает нормально и игра не вылетает!

Клава вылечилась отключением джойстика в config.xml

А вот звук попробую virtualBox с pulseaudio на ALSA переключить…
Не-а, не работает, хотя в системе вроде звук есть, я ч/з настройки Audio проверил.

========================

ОК, решил всё же пересобрать TranzAm с флагом -m32
Ошибка: (привключении #include <string>)
/usr/include/c++/4.8/string:38:28: fatal error: bits/c++config.h: No such file or directory
#include <bits/c++config.h>

лечится видимо:
$ sudo apt-get install gcc-4.8-multilib g++-4.8-multilib
[sudo] password for justy:
Reading package lists… Done
Building dependency tree
Reading state information… Done
gcc-4.8-multilib is already the newest version.
gcc-4.8-multilib set to manually installed.
The following extra packages will be installed:
lib32stdc++-4.8-dev lib32stdc++6 libx32stdc++-4.8-dev libx32stdc++6
Suggested packages:
lib32stdc++6-4.8-dbg libx32stdc++6-4.8-dbg
The following NEW packages will be installed:
g++-4.8-multilib lib32stdc++-4.8-dev lib32stdc++6 libx32stdc++-4.8-dev
libx32stdc++6
0 upgraded, 5 newly installed, 0 to remove and 10 not upgraded.

!!! Да, компиляция прогла успешно,
я установил тогда gcc-4.8-multilib, а про g++-4.8-multilib забыл!

Но линкер выдал ошибку:
/usr/bin/ld: skipping incompatible /lib/../lib/libfmod-3.75.so when searching for -lfmod-3.75
/usr/bin/ld: skipping incompatible //lib/libfmod-3.75.so when searching for -lfmod-3.75
/usr/bin/ld: cannot find -lfmod-3.75
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib/libadime.so when searching for -ladime
/usr/bin/ld: skipping incompatible /usr/lib/../lib/libadime.so when searching for -ladime
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../libadime.so when searching for -ladime
/usr/bin/ld: skipping incompatible //usr/lib/libadime.so when searching for -ladime
/usr/bin/ld: cannot find -ladime
collect2: error: ld returned 1 exit status
make: *** [TranzAm] Error 1

Оппа, я линкер без -m32 запускаю, сейчас поправлю.
Не нашёл либы 32bit для cvurl и fblend.
Всё верно, у меня их нет, сейчас с Юбунты скопирую.

Всё, скомпилилось. Линкер пожаловался:
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libm.so when searching for -lm
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libm.a when searching for -lm
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libm.so when searching for -lm
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libm.a when searching for -lm
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libc.so when searching for -lc
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libc.a when searching for -lc
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libc.so when searching for -lc
/usr/bin/ld: skipping incompatible /usr/lib/x86_64-linux-gnu/libc.a when searching for -lc
!! TranzAm compiled succesfully

Но в итоге все скомилировалось и работает, правда, без звука.
Сейчас передалем с FMOD 3 на FMOD Ex (FMOD 4)

Итак, заменяем файлы lib_fmod.h и lib_fmod.cpp в папке external
(из прошлого проекта, где мы переделывали FMOD 3 в FMOD EX),
удаляем отсюда файлы fmod.h и fmod_errors.h

В файле game_transam.h удаляем external в ссылках на эти файлы

Все вхождения FSOUND_STREAM и FMUSIC_MODULE меняем на FMOD_SOUND

В game_car.cpp меняем FSOUND_LOOP_* на FMOD_LOOP,
для переменных int, относящиеся к каналам (начинаются на playing)
делаем тип FMOD CHANNEL *. И нормализуем их в соотв. с этим (NULL)

В game_car.h originalCarNoiseFrequency тип меняем на float.
Делаем правильный расчёт его.

game_game.cpp: тот же loop и замена звук. канала playingRadarBeep
FSOUND_StopSound(FSOUND_ALL) меняем на StopSFX(FSOUND_ALL);

ДАЛЕЕ:
game_logo.cpp:21:8: error: ‘FSOUND_STREAM’ does not name a type
Меняем на FMOD_SOUND, нормализуем обращения к функции PlaySampleFromStream

game_menu.cpp:566:72: error: ‘FSOUND_SetSFXMasterVolume’ was not declared in this scope

game_pause.cpp:4:8: error: ‘FSOUND_STREAM’ does not name a type
Он не используется (пока?)
а FMUSIC_SetPaused(ActiveMusic,FALSE); меняем на PauseTrackerSong(FALSE);

game_transam.cpp: 3 ошибки
FMUSIC_StopAllSongs -> RemoveSound();
FMUSIC_SetPaused -> PauseTrackerSong;

И наконец, game_vars.cpp:100:1: error: ‘FMUSIC_MODULE’ does not name a type
FMUSIC_MODULE *ActiveMusic=NULL;
^
game_vars.cpp:103:1: error: ‘FSOUND_SAMPLE’ does not name a type
FSOUND_SAMPLE *snd_menuchange = NULL;

Надо заменить всё на FMOD_SOUND!

game_wingame.cpp:110:41: error: ‘FMUSIC_SetMasterSpeed’ was not declared in this scope
FMUSIC_SetMasterSpeed(ActiveMusic,0.8);

меняем на SetSpeedTrackerSong(0.8);

Всё, заработало! Музыка есть, шрифт виден, переходы нормальные.
Круто!
Но иногда выдёт в конце по завершении: Segmentation fault

Найти будет не трудно, а оно надо?
Скорее всего где-то я с каналами намудрил или ещё что.

Да, звуковые эффекты плохо проявляются, точнее их совсем почти нет.
Только в меню и при столкновении, но при стокновении не полный.
Будем лечить? А оно надо?

Да, нашёл несколько ляпов в PlaySFX – неверное использование FMOD_LOOP_* и каналов.

Что-то звук есть ког-где, но в шуме мотора его нет.

ОК, нашел баг в AdjustVolumeSFX
У меня было: volume / 255, а надо было (float)volume / 255
С++ не умеет автоматом приводить к float, даже когда результат должен быть float.
Это тебе не PHP, дружок.

ОК, звук работает, но глюк в том, что звук мотора и звук конца бензина не затихают!
То есть не работают функции StopSFX? Может, в группе глючит?

У меня просто каналы теряются. А так всё ОК, прекрасная маркиза!
Да, потому что я забыл создать группу каналов, которую использую,
в функции InitialiseSound:
FMOD_System_CreateChannelGroup(fmodSystem, NULL, &fmodEffects);

Всё, теперь звуки работают 100%! Как в виндовой версии!

И ещё я хотел сделать так, чтобы после окончании мини-игры меню возращалось в выбор карты, а не в главное.
Как это сделать? Это я сделал: (закомментировал 2 строки в файле game_menu.cpp)
if(currentLevel)
{
// CurrentTopMenu=0;
// CurrentMenuItem=0;

Initialised=false;

Один глюк – неверное ADIME? меню в выборе карты для мини-игры:
Хочу перекомпилить ADIME библиотеку (Allegro Dialogs Made Easy)
для 64 и 32 разряда.

Итак, находим и распаковываем adime-2.2.1.tar.gz
Пишем:
$ ./fix.sh unix
$ make

Сразу вылезла ошибка:
gcc -DADIME_SRC -Wall -Wno-unused -mcpu=pentium -O2 -funroll-loops -ffast-math -fomit-frame-pointer -fno-strength-reduce -Wno-deprecated-declarations -I./include -o obj/unix/adime/adialogf.o -c src/adialogf.c
gcc: warning: ‘-mcpu=’ is deprecated; use ‘-mtune=’ or ‘-march=’ instead
src/adialogf.c:1:0: error: CPU you selected does not support x86-64 instruction set
/* _ _ _
^
make: *** [obj/unix/adime/adialogf.o] Error 1

Находим строку mcpu, убираем её.
/usr/bin/ld: obj/unix/adime/adialogf.o: relocation R_X86_64_32S against `adime_d_multiline_text_proc' can not be used when making a shared object; recompile with -fPIC
obj/unix/adime/adialogf.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
make: *** [lib/unix/libadime.so] Error 1

В misc/makefile.uni добавим два флага:

COMPILE_FLAGS += -fPIC
COMPILE_FLAGS_NO_OPTIMIZE += -fPIC

и всё в ажуре!
$ sudo make install
[sudo] password for justy:
install -m 755 -d /usr/lib
install -m 644 lib/unix/libadime.so /usr/lib/libadime.so
install -m 755 -d /usr/include/adime
install -m 644 include/adime/adimecfg.h /usr/include/adime/adimecfg.h
install -m 755 -d /usr/include/adime
install -m 644 include/adime/adimeint.h /usr/include/adime/adimeint.h
The optimized UNIX library has been installed.
Run make install-man to install the man pages.
Run make install-info to install the info documentation.
You may conserve space by instead running make install-gzipped-man
and/or make install-gzipped-info.

ОК, это версия 64 бит, теперь попробуем собрать 32 бит.
Добавляем флаг -m32 там где мы добавляли -fPIC

ОК, линкер запнулся:
/usr/bin/ld: i386 architecture of input file `obj/unix/adime/adialogf.o' is incompatible with i386:x86-64 output
/usr/bin/ld: i386 architecture of input file `obj/unix/adime/adime.o' is incompatible with i386:x86-64 output

Да я просто для линкера заблы добавить этот флаг: -m32

Опять выдал ошибку:
gcc -s -o examples/exanim obj/unix/adime/exanim.o lib/unix/libadime.so `allegro-config –libs release –shared`
lib/unix/libadime.so: error adding symbols: File in wrong format
collect2: error: ld returned 1 exit status

Но файл lib/unix/libadime.so есть, и он 32 битный:
$ file lib/unix/libadime.so
lib/unix/libadime.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=b4c6aa0171f446aaee9591ab552923765615186c, stripped

-s флаг добавил, а -m32 – нет
Добавляем ещё строчку в makefile.uni:
(рядом с соотв. строками)
LFLAGS += -m32

Всё, теперь make отработал до конца.

Итак, послдений шаг:
$ sudo cp lib/unix/libadime.so /usr/lib/i386-linux-gnu/

Пересобрал TranZam, а глюк с меню тот же остался.
Косяк в либе ADIME, где-то в функции adime_dialogf
Мне, сейчас всю либу переделывать?
Может проще написать свой диалог?

В принципе, TranZam готов на 99% –
игра играет, музыка звучит, эффекты все есть.

Что надо исправить для полного выпуска:
1. Исправить диалог Adime, чтобы список был всегда виден, а не только при нажатии клавиши.
2. Найти причину Segmentation Fault в конце, может не освобождаю какие ресурсы FSOUND?
Или неверная работа с каналами?
3. Глюк в уровнях громкости для музыки и эффектов. Я его видел в сорце, только руки не дошли исправить сразу.

Но я и так доволен, как СЛОН! У меня всё получилось как я хотел.

Хинт: компиляция TranzAm в 32-битном режиме, 64 бита он не поддерживает
(где-то на уровне PPACol – коллизий, выделение памяти с учетом 32 битных указателей, а не 64 бит?
Да, и FancyFont тоже глючат, и переходы (FBLend?))

================================================================================================
$ date
Вс. июня 7 00:53:37 MSK 2015

Ох-хох, примерно час убил на компиляцию и разборку с TileExtractor. Вроде прикольная штука, всё получлось скомпилить, только файл тяжело в этой самой ADIME выбрать.

Также скачал Mappy для Windows, но ч/з Wine, как и было написано на сайте, он нормально пошёл. поигрался чуток, да и забросил.

Пока всё, пока!

Приготовлю архивчик с новыми файлами retrospec, всё подчищу.

====================================================================================================

 

BrainGames retro-игры под Linux

Здравствуйте.

Задача: взять все доступные игры на http://www.braingames.getput.com/
и собрать их под современный Линукс (modern Linux).

Задача решена успешно, игры классные, живые скриншоты всех игр я представлю!
Результаты моей работы – переделанные готовые для сборки сорцы, без объектных файлов,
можно скачать здесь: http://beotiger.com/download/braingames
Архивчик весит окло 80 Мегабайт, для некоторых игр включены их Windows-версии со всеми нужными библиотеками. Все Windows-версии игр Вы можете скачать с сайта BrainGames, адрес вверху. Я оставил Windows версии для сравнения.

Ниже русский текст перемежается с английским, сам не знаю почему.
Я – двуязычен, bilingual, хорошо, что не двуличен, хотя кто его знает! 🙂

Для сборки игр надо поставить в систему SDL: Simple DirectMedia Layer
(старый SDL, 1.2), а также некоторые сопутствующие ему либы, как то:
SDL_image SDL_mixer SDL_sound SGE etc.
причем их девелоперские версии (обычно оканчивающиеся на -dev или -devel)

В системах на основе APT'а (Debian/Ubuntu/Mint и другие), это делается так:
$ apt install build-essential libsdl-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-sound1.2-dev libsdl-ttf2.0-dev libsdl-sge-dev

Начал я тогда с месяц, а то и более, назад:

Date: 7-14 May 2015 AD
System: Linux justy 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

justy@justy ~/src/braingames $ c++ –version
c++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ОК, у нас современный Линукс Минт (Linux Mint), который идентифицируется как Ubuntu, ну и хорошо,
и современный компилятор c++ 4.8 (это g++).

Да, в играх BrainGames Alt+Enter переключает полный экран/окно, F12 – быстрый выход из игры.

Поехали игры:
0. TransballGL – only demo for Windows? But in archive there is src directorty, that should be compiled later!?
OK, Makefile created, SDL_rotozoom.h not found, it the part of SDL_gfx?
Install libSDL-gfx1.2-4 ?

collision.cpp:28:20: fatal error: GLtile.h: No such file or directory
#include "GLtile.h"

OK, Makefile needed to be changed!

Смысл такой, что TransballGL – крутая штука, и надо собрать её под нашу систему,
но пока не знаем как. Что-то много всего отсуствует.
Но в итоге мне удалось его собрать, он идёт ближе к кноцу. Оно стоит того!

1. supertransball2: compiled without problems from SuperTransball/stransball2-v15-windows/stransball2sources/.
Эта игра – предшественник TransballGL, только графика попроще, и уровней поменьше.
Стоит того, чтобы поиграть пару-тройку уровней.

Как собирается:
$ cd braingames/SuperTransball/src
$ make
После этого исполняемый файл stransball2 будет перемещен в папку вверх.

чтобы поиграть, делаем
$ cd ..
$ ./stransball2
 

strb1.jpg

strb2.jpg

2. RoadFighter

Linking errors.
Need to change string in Makefile:
$(CC) $(LDFLAGS) $^ -o $@ to $(CC) $^ -o $@ $(LDFLAGS)
e.g. LDFLAGS should go last.

Then all's OK!

Да, в RoadFighter'е ошибки линкощика обнаружились,
новый GCC использует другой порядок линка? Не знаю точно.

Новый Makefile должен работать без проблем:
$ cd braingames/roadfighter
$ make
$ ./roadfighter

В директорию src заходить не нужно, Makefile расположен здесь, он сам распознает нужную папку.
Игра играет!

 

rf1.jpg

rf2.jpg

rf3.jpg

rf4.jpg

3. F1 Spirit

Same LDFLAGS issue as in RoadFighter.
Also: delete curl/types.h line (it's obsolete long time ago).
To avoid linking against libm and others libc++ libraries we should change default compiler from gcc to g++.

Да, те же проблемы с линком, что и у RoadFighter. Плюс ещё пара нюансов вылезло (см. выше, на английском).

Итого: игрушка супер, RoadFighter * 100, свои уровни, реальные гонки. Но не проста в прохождении.
На сайте написано, что не доделана, но 3 трассы играются на ура!

Собираем:
$ cd braingames/F-1\ Spirit/
$ make
$ ./f1spirit

То есть в sources, как и в случае с roadFighter, заходить не надо, игра собирается и запускается с корня.
Наслаждаемся:
 

f1s1.jpg

f1s2.jpg

f1s3.jpg

f1s4.jpg

f1s5.jpg

f1s6.jpg

4. The Goonies

Same LDFLAGS issue as in RoadFighter.
Same needing g++ compiler instead of gcc.
Then make is OK!

ОК, собираем:

$ cd braingames/TheGoonies
$ make
$ ./goonies
 

goon1.jpg

goon2.jpg

goon3.jpg

goon4.jpg

goon5.jpg

5. NETHER EARTH

Как я сначала компилировал, и какие ошибки исправил (на английском, не обесудьте):
Ниже будет сборка готовой игры на русском.

$ cd NETHER EARTH/Nether Earth v0.52/src
$ make
g++ -c cmc.cpp -o cmc.o
cmc.cpp:11:21: fatal error: GL/glut.h: No such file or directory
#include "GL/glut.h"
^
compilation terminated.
make: *** [cmc.o] Error 1

No glut dev lib?
OK:
$ sudo apt-get install freeglut3-dev freeglut3

Yep, now it compiles!

Now, after runnig I got following error:
freeglut ERROR: Function <glutStrokeWidth> called without first calling 'glutInit'.

In main.cpp in main function I added line:
glutInit(& argc, argv);

and added:
#include "GL/gl.h"
#include "GL/glut.h"
at the head of it.

OK, rm *.o && make done it well!
Nether Earth works now!

Да, для NetherEarth – крутой 3D ретро-стратегии – надо доустановить GLUT:

$ apt install freeglut3-dev
$ cd braingames/NETHER\ EARTH/src
$ make
$ cd ..
$ ./nether_earth

Смысл игры похоже – захватить все ресурсы или уничтожить вражескую базу?
Мы управляем каким-то подом, можем строить роботов из различных компонентов,
потом отдавать им приказы – захватывать ресурсы/уничтожать вражеских роботов и т.п.
Или управлять ими с помощью своего пода. Круто!
 

ne1.jpg

ne2.jpg

ne3.jpg

ne4.jpg

6. The Maze of Galious

Same $(LDFLAGS) as in RoadFighter.
All is OK!

В этой игрушке есть фишка: нажатие клавиши F10 меняет скины игры,
превращая её то в версию для Dendy, то в версию для MSX, то в современную ретро-версию и т.п.
Я насчитал 6 шкурок.

Итак, сборка проста:

$ cd braingames/The\ Maze\ of\ Galious/
$ make
$ ./mog
 

tmg1.jpg

То же самое с другой шкуркой:
 

tmg2.jpg

tmg3.jpg

tmg4.jpg

tmg5.jpg

Другая шкурка:

tmg6.jpg

7. Transball GL

Вот и добрались до неё!

Этапы начальной сборки игры показаны ниже, уже на русском.
Потратил наверное около часа на всё это.
Сборка игры сейчас описана ниже, там же идут скриншоты.

justy@justy ~/src/braingames/TransballGL/TGL/src $ make
g++ -w -c state_playerprofile.cpp -o state_playerprofile.o `sdl-config –cflags`
state_playerprofile.cpp: In member function ‘int TGLapp::playerprofile_cycle(KEYBOARDSTATE*)’:
state_playerprofile.cpp:120:7: error: ‘m_profile_profiles_names’ was not declared in this scope
m_profile_profiles_names.Add(tmp);
^
make: *** [state_playerprofile.o] Error 1

> Тут опечатка: не m_profile_profiles_names, а m_profile_profile_names

g++ -w -c XMLparser.cpp -o XMLparser.o `sdl-config –cflags`
XMLparser.cpp:19:20: fatal error: symbol.h: No such file or directory
#include "symbol.h"
^
compilation terminated.
make: *** [XMLparser.o] Error 1

> И опять опечатка, не symbol.h а Symbol.h
> Тоже самое было с GLtile.h (вместо GLTile.h)
> Linux это не Windows, тут регистр в именах файлов имеет значение

> Пришлось тж. добавлять #else include <dirent.h> etc. вместе с #ifdef _WIN…
> И вместо _mkdir и т.п. использовать mkdir в условных выражениях
> В некоторых файлах… где ругался на DIR и dirent и прочее, надо было делать так:

#ifdef _WIN32
#include "windows.h"
#else
#include <unistd.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include "ctype.h"
#endif

То есть под #else я тут подразумеваю Linux. Попробовать бы собрать под MacOS X!?

Игра в итоге собралась успешно и играет как надо, так что всё жуе позади!
Итак, собираем сейчас:

$ cd braingames/TransballGL/src
$ make
$ cd ..
$ ./tgl

Цель игры – управляя мини-ракетой, захватить шарик, избегая любых препятствий и вытащить его на самый верх уровня.
Любое столкновение, кроме шарика, карается моментальной гибелью и переигрыванием всего уровня.

Кроме того, ещё расходуется горючее, за которым надо следить на некоторых уровнях.

Клавиши управления можно переназначяить в меню. Выбор любого пройденного уровня теперь осуществляется легко,
не надо запоминать пароли, как для SuperTransball2.

Уровни есть те же, что и в SuperTransball2, а есть я так понял и новые, но до них я пока не дошёл.

 

trgl1.jpg

trgl2.jpg

trgl3.jpg

trgl4.jpg

И на закуску –

8. Magical Tree

Эта игра не доделана и вряд ли когда будет.
К сожалению, исходники не представлены, мне по крайней мере не удалось их найти.

Играется через WINE:

$ cd braingames/Magical\ Tree/
$ wine Demo.exe

Доходит до конца дерева, и всё, даже если все яблоки собрал.
 

mt1.jpg

mt2.jpg

mt3.jpg

Пока всё, от BrainGames ничего не слышно уже лет 6, с 2009 года. Жаль, классные римэйки делали!
И сорцы крутые!

Спасибо вам, господа из BrainGames за наше счастливое дество!

$ date
Пн. июня 8 22:31:04 MSK 2015

$ uname -a
Linux justy 3.13.0-51-generic-tuxonice #84~ppa1-Ubuntu SMP Wed Apr 29 19:39:16 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

$ g++ –version
g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ free -gho
total used free shared buffers cached
Память:7,8G 2,8G 5,0G 53M 49M 1,0G
Swap: 3,0G 0B 3,0G

~/src $ zip -r9 braingames.zip braingames/

$ du -sh *.zip
78M braingames.zip
34M retrospec.zip
 

Допиливаем VKontakteApi (WP-плагин)

Всё отлично!

Используем неплохой плагин для WordPress’а от Забродского Евгения (kowack): VKontakte API.

Плагин позволяет наряду с обычными комментариями вставлять и видеть комментарии VKontakte и Facebook.

Бонусом идёт также возможность добавлять кнопки соц.сетей – PlusOne button, Tweet button, Mail.ru+Ok.ru button, Ya.ru button.

Для Вконтакте имеется также возможность добавлять кнопки Мне нравится и Поделиться.

Настроить плагин также относительно легко – достаточно зарегистрировать свои приложения в VK и FB, ввести их ключи и админский ключ для плагина. Все ссылки даются тут же на соотв. страницах настройки плагина.

В общем плагин неплохой, можно сказать отличный, но нам хотелось бы его немного переделать.

Что не так?

Плагин выводит комменты VK,FB и WP блоками, если один блок виден, то другие скрыты, появление блока происходит с медленным эффектом, что немного имхо раздражает.

Кроме того, почему-то кнопки и надпись Comments: на английском языке, хотя наш блог на русском (может это из-за того что админку мы юзаем на английском? не знаю, не проверял).

Мы хотели бы, чтобы блоки были все сразу видны, то есть идёт первым VKontakte блок с комментариями, под ним – Facebook’чный блок комментариев, и уже под ним – стандартный WP (Word Press’овский блок с комментариями).

И надписи желательно переделать на русский насколько это возможно.

За работу!

Итак, заходим в папку wp-content/plugins/vkontakte-api.

Это родная папка плагина. Откроем файл js/callback.js и сразу видим блок Comments switcher с функциями showVK(),showFB(),showWP().

По названиям функций можно судить, что именно эти функции отвечают за появление/скрытие комментариев.

Нам достаточно закомментировать внутренность этих функций (но не сами функции конечно), чтобы комментарии были все видны. Наша задача выполнена? Да, почти, но по нажатию кнопок Vkontakte, Facebook и Site ничего не происходит.

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

Для этого внутри фуннкции добавляем простой переход на хэш-таг с соотв. ид.

Для VKontakte #vkapi, для Facebook #fb-comments (этого ид изнчально нету, ниже написано, как его добавить), для Site #comments.

Таким образом весь модернизированный блок будет выглядеть так:

// Comments switcher
function showVK(Tshow, Thide) {
/*    if (!Tshow && Tshow != 0) Tshow = 1000;
    if (!Thide && Thide != 0) Thide = 1500;
    jQuery("#vkapi").show(Tshow);
    jQuery(".fb-comments").hide(Thide);
    jQuery("#comments").hide(Thide);
    jQuery("#respond").hide(Thide);
*/
    if(Tshow == 1) window.location.hash = 'vkapi';
 
}
function showFB(Tshow, Thide) {
/*    if (!Tshow && Tshow != 0) Tshow = 1000;
    if (!Thide && Thide != 0) Thide = 1500;
    jQuery(".fb-comments").show(Tshow);
    jQuery("#vkapi").hide(Thide);
    jQuery("#comments").hide(Thide);
    jQuery("#respond").hide(Thide);
*/
    window.location.hash = 'fb-comments';
}
function showWP(Tshow, Thide) {
/*    if (!Tshow && Tshow != 0) Tshow = 1000;
    if (!Thide && Thide != 0) Thide = 1500;
    jQuery("#comments").show(Tshow);
    jQuery("#respond").show(Tshow);
    jQuery("#vkapi").hide(Thide);
    jQuery(".fb-comments").hide(Thide);
*/
    window.location.hash = 'comments';
}

Для чего для контакта мы добавили условие if(Tshow == 1)?

Дело в том, что при заходе на страницу вызывается первый раз функция showVK() (если включен показ сначала комментов VK, а не FB). И тогда из-за исправленого скрипта происходил бы перескок сразу на комменты VK, что не есть хорошо, так как по умолчанию читатель ещё не читал статью/пост.

Если же у Вас включен первым показ комментов от Facebook, тогда это условие надо добавить в его блок. Для универсальности наверное его можно добавить сразу в оба блока.

Как говорилось выше, нет хэша #fb-comments. Чтобы его добавить, откроем файл vkapi.php в папке плагина, найдём там функцию add_fb_comments() и перед строкой class=’fb-comments’ (или после неё, не важно) добавим строку id=’fb-comments’.

Таким образом функция add_fb_comments() станет выглядеть так:

function add_fb_comments()
{
		$width = get_option('vkapi_comm_width');
		$limit = get_option('vkapi_comm_limit');
		$url = get_permalink();
		echo "
	<div style='background:white'
			 id='fb-comments'
			 class='fb-comments'
			 data-href='{$url}'
			 data-num-posts='{$limit}'
			 data-width='{$width}'
			 colorscheme='light'>
	</div>";
}

Сохранить, Upload, Refresh – вуаля, всё работает.

А руссификация этих кнопок и надписи Comments?

Языковость у этого плагина происходит так:

$text = __('Comments:', $this->plugin_domain);

Комментируем соотв. строку и добавляем под ней свою такую:

$text = 'Комментарии: &nbsp;&nbsp;';

А &nbsp;&nbsp; я уже отсебя добавил, это отсебятина какая-то.

Это мы находим в функции add_tabs_button_start().

Аналогичные действия проделаем для функций add_tabs_button_vk(), add_tabs_button_fb(), add_tabs_button_wp() и называем кнопки как хотим. Я назвал ВКонтакте, Facebook и Сайт соответственно.

Всё, конец. Всё работает? Да!

В заключение можно добавить, что при обновлении плагина все проделанные нами изменения скорее всего сойдут на нет, поэтому их придётся проделывать снова. Либо же не обновлять плагин совсем, если он и так работает нормально и удовлетворяет все наши нужды.

До свидания, друзья!

audiojs: немного коррекции

Теория

Используем Colber’s audiojs: https://github.com/kolber/audiojs,
не знаю, хорошо это или плохо, может перейти на SoundManager2?

Сначала немного теории, если позволите.

Для загрузки аудио через audiojs мы используем такую конструкцию:

 var audi;
 audiojs.events.ready(function() {
     audi = audiojs.createAll();
 });

createAll найдет все элементы audio на страницы и обернёт их корректным набором audiojs.
В этой функции в качестве первого параметра можно указать новые свойства и свои обработчики событий.

Найденные элементы передаются в массив audi в нашем случае, и при желании мы можем управлять ими с его помощью.

Например, audi[0].load(‘songs/alice.mp3’) загрузит новую песню в первый найденный элемент audio, а audi[0].play() начнёт его воспроизведение.

Вообще, в API audiojs присутствуют стандартные функции проигрывания аудио, как то :

load – загрузить новый трек в элемент audiojs
skipTo – переход на новое место трека
playPause – переключение состояния игра/пауза
play – продолжить или начать игру трека,
pause – приостановить игру трека (пауза)
setVolume – установить новую громкость проигрывания трека

И стандартные обработчики событий:

updatePlayhead – вызывается во время проигрываения трека (в нем передаётся текущая позиция от 0 до 1)
loadError – ошибка загрузки трека
loadStarted – начало загрузки трека
loadProgress – прогресс загрузки трека
trackEnded – проигрывание трека завершено
init – инициализация

Наша проблема

Проблема в том, что при повторной загрузки аудио методом load(), само аудио нормально загружается (если оно есть конечно), но в элементе остаётся класс error, если перед этим произошла ошибка (т.е. предыдущее аудио не смогло загрузиться).

Даже если новое аудио загрузится нормально, мы не увидим кнопки старт/пауза, прогрессбар и время аудио, т.е. будет задействован класс error на нашем элементе, который почему-то не исчезает.

Хотя при этом мы сможем проиграть само аудио, но ни прогресс бара, ни кнопок старт/пауза, ни время аудио мы больше не увидим.

Это происходит, если мы используем один элемент audio для загрузки нескольких треков с помощью audiojs.

И я подумал – это не есть хорошо, надо бы исправить неполадку.

Практика

Покопавшись минут 20-30 в исходниках audiojs,
а также проигрывая два трека последовательно – один нормальный, другой фэйковый, который порождает ошибку, и при этом используя Firefox’s Firebug, мы видим что в элементе audiojs_wrapper (который обёртывает весь функционал audio), при ошибке добавляется класс error, который прячет scrubber div, на котором расположена нужная нам информация о треке.

Наша задача – найти обработчик события load в исходнике аудиоJS и каким-то образом удалять там этот класс.

g[o].prototype = {
...
 load : function (b) {
  this.loadStartedCalled = false;
  this.source.setAttribute("src", b);
  this.element.load();
  this.mp3 = b;
  g[h].events.trackLoadProgress(this)
},
...
}

Он находится в прототипе audiojsInstance.
И так, предпоследней добавляем строчку

g[o].prototype = {
...
 load : function (b) {
  this.loadStartedCalled = false;
  this.source.setAttribute("src", b);
  this.element.load();
  this.mp3 = b;
 
  // это наша новая строчка - мы убираем класс error с wrapper'а элемента аудио.
  g[h].helpers.removeClass(this.wrapper, this.settings.createPlayer.errorClass);
 
  g[h].events.trackLoadProgress(this)
},

Мы используем мин версию (обфусцированную немного), поэтому имена переменных здесь такие странные, при использовании девелоперской версии код будет немного понятнее наверное.

Итак, Сохранить, Аплоад, Обновить кэш, и теперь при загрузке нового аудио на месте ошибочного мы видим появление всех элементов плеера – кнопки Старт/Пауза, Прогрессбар и время текущей позии/общее время.

А при загрузке ошибочного аудио как обычно проявляется класс error.

Вообще audiojs глюковатый, его надо наверное ещё твикать, но в какую сторону?

В частности, у меня почему-то не всегда загружается полностью шкала трека, то есть место по которому можно кликать для быстрого перехода на новое место трека. Хотя трек полностью загружен и проигрывается нормально до конца.

Хотелось бы найти и исправить этот глюк, если он не зависит от браузера (может, некорректный обработчик события loadProgress?), и вообще исправима на уровне исходника audiojs.

Благодарю за внимание, на этом пока всё.

Удачи во всех делах,
а главное – отличного настроения сейчас, сегодня.
И если повезёт, то и завтра тоже. ))))

ffmpeg: создаём fade-through (с переходом затемнения) видео из 3-х рисунков

Итак, у нас имеются 3 рисунка (изображения/фотографии и т.п.).

Мы хотим создать видео с переходами типа fade-in fade-out (затеменения) между ними.

Для удобства приведём риснуки к одному размеру, преобразуем их все в jpg (можно использовать и PNG формат, если хочется), и назовём их img0.jpg, img1.jpg, img2.jpg.

Вот как они могут выглядеть к примеру:

img0.jpg

img0.jpg

img1.jpg

img1.jpg

img2.jpg

img2.jpg

Рисунки в видео? Но зачем???

Теперь мы создадим 2-х минутный видео ролик из 1-ых двух риснуков (img0.jpg и img1.jpg)
с переходом затемнения,
причём в этом случае наш img0.jpg исчезнет быстрее.

Вот команда для ffmpeg (всё в одной строке, я разбил на несколько строк для удобства чтения):

ffmpeg -loop 1 -i img0.jpg -loop 1 -i img1.jpg 
-filter_complex 
"[1:v][0:v]blend=all_expr='A*(if(gte(T,1),1,T/1))+B*(1-(if(gte(T,1),1,T/1)))'" 
-y -c:v libx264 -t 2 fade1.mp4

Затем создаём подобный ролик для второго и третьего рисунков, причём теперь второй рисунок больше держится, чем третий.

ffmpeg -loop 1 -i img1.jpg -loop 1 -i img2.jpg
-filter_complex
"[1:v][0:v]blend=all_expr='A*(if(gte(T,2),1,T/2))+B*(1-(if(gte(T,2),1,T/2)))'" 
-y -c:v libx264 -t 2 fade2.mp4

Параметры:
-loop в обоих случаях говорит, что для одного вывода надо использовать оба файла
-i img[0|1|2].jpg – задание имён входных рисунков.
-filter_complex – вызов сложного фильтра, который собственно и создаёт эффект затемнения и перехода одного рисунка в другой. Лучше не спрашивайте как он работает, обратите внимание на числа после T, я их подобрал экспериментально для 2-х секундного ролика
-y – перезаписывать выходной файл без вопрсов, если он есть, не обязательный параметр
-c:v libx264 – кодек для видео MP4
-t 2 – длительность видео 2 секунды, для меня достаточно, можно естественно увеилчивать этот парметр, и соотв. менять значения чисел после T в фильтре перехода (-filter_complex)
fade[1|2].mp4 – название выходного файла

Примечание: если ffmpeg выдаёт какую-то тупую ошибку при выполнении этих команд, удостоверьтесь, что все ваши рисунки имеют одинаковое разрешение – не размер, а именно разрешение для печати, измеряется в точках на дюйм обычно (например, открыть рисунки в IrfanView, нажать кнопку i и проверить пункт Resolution, его при желании можно тут же изменять).

Итак, в итоге у нас появятся такие вкусняшки:

Кодируем для DVD

Переделать видео для формата pal-DVD 16:9

ffmpeg -y -i fade1.mp4 -target pal-dvd -aspect 16:9 fade1.mpeg 
ffmpeg -y -i fade2.mp4 -target pal-dvd -aspect 16:9 fade2.mpeg 

Создать картинки из видео в папке 1: Частота кадров 25 в секунду (-r 25) (зачем?)

ffmpeg -i fade1.mpeg -r 25 1/vid%03d.png

Создаём свой собственный bit.ly – укорачиватель ссылок

Как всегда, предыстория

В одном из наших проектов ссылки так разрослись, что я решил всё же подрезать их, а то некрасиво стало выглядеть. Хотя в принципе, раньше мне было на это наплевать.

Да, я знаю bit.ly – bitly.com – был наверное одним из первых укорачивателей ссылок, сейчас этих сервисов расплодилось, хоть ешь.

Итак, захотел я ссылки укоротить, зашёл на bitly узнать как бы мне это быстрее и эффектнее сделать, и увидел, что надо регаться у них, получать всякие разные API tokens, и т.д. и .т.п. Короче, геморрно, неохота разбираться даже.

И тут мне на ум пришло – я же могу создать свой bitly, думаю, дело это не хитрое. Весь алгоритм – составил пару значений ключ-ссылка, и переходишь по ссылке, получив ключ.

И я сел, почесал голову, и примерно за пару часов накатал готовенький скрипт типа bitly, правда, отлаживать ещё пару дней пришлось, но если вы кодер, вы в курсе.

Начинаем

Первым делом определимся со структурой таблицы.

Сразу оговорюсь, что мы тут будем использовать троицу AMP – Apache, MySQL, PHP, но вместо MySQL можно использовать тот же SQlite или любую другую СУБД. Да и сервер и язык сервера может также быть любым по вашему вкусу – только надо будет соответственно тогда подредактировать наш скрипт.

В идеале нам нужно всего два текстовых поля – uri и key. Но нам желательно вести хотя бы простенькую статистику по ссылкам, ведь интересно, что с ними происходит, правда?

Итак, сядем и продумаем следующую структуру таблицы:

id INT(11) PRIMARY KEY AUTOINCREMENT – unique id
key VARCHAR(16) UNIQUE KEY – ключ
uri TEXT – полная ссылка
dtm DATETIME – дата и время создания
dthit DATETIME – дата и время последнего перехода по ссылке
hits INT(11) – число переходов по ссылке

Поле id тут в принципе не обязательно, наверное, уникального ключа key длиной до 16 символов достаточно, но я привык в каждой таблице определять ид, без него как-то скучно.

Протокол нашего скрипта

Назовём скрипт go.php – кратко и понятно.

Основные запросы к нему будут такие:

go.php?to=[KEY] – переход по ссылке, ассоциированной с данным ключом KEY.

go.php?new=[URI] – создание и вывод в браузер нового ключа для ссылки URI

Определимся, что ключ может быть длиной от 3-х до 16-ти символов и содержать только латинские буквы, большие и малые, и цифры.

Поиск ключа и переход по ссылке

Итак, при наличии параметра to выполняем поиск uri по значению ключа из этого параметра и переходим по нему. Всё.

Тут есть только один маленький нюанс: дефолтный супер-массив PHP $_GET нам не подойдёт, так как мы можем принять строку стороннего uri, в котором могут содержаться такие же имена параметров.

Поэтому разбираем PHP параметр от Апача – $_SERVER[‘QUERY_STRING’].

Для выявления корректного запроса перехода по ключу используем простое PCRE выражение: /^to=([a-zA-Z\d]{‘.BITLY_MIN.’,’.BITLY_MAX.’})$/

Константы BITLY_MIN и BITLY_MAX определяют мин и макс длину ключа (3 и 16 соотв. – смотрите ниже в листинге полного скрипта).
Часть кода, делающая соотв. проверку, может выглядеть так:

// строка запроса, $_GET нам не подходит
$q = $_SERVER['QUERY_STRING'];
$m = '/^to=([a-zA-Z\d]{'.BITLY_MIN.','.BITLY_MAX.'})$/';
 
if(preg_match($m,$q,$ar)) {
// передан ключ для перехода
}

Всё что нам теперь остаётся – найти uri по полученному ключу и перейти по нему, предварительно обновив статистику (см. полный код в листинге внизу поста).

Создаём новый ключ для ссылки

При создании нового ключа добавим пару интересных, имхо, возможностей – параметр len – определяет конкретную длину ключа (по умолчанию примем длину ключа за 8 символов – 2.2×1014 вариантов?) или параметр key – желаемый ключ для ссылки (длиной от 3 до 16 символов и также состоящий только из латинских букв и цифр). Я не опечатался с `или` – должен быть задан лишь один дополнительный параметр, потому что тогда второй параметр теряет смысл.

Кроме того, для универсальности, дополнительный параметр должен предшествовать параметру new, чтобы не влиять на содержимое ссылки, ведь в переданной нам ссылке могут быть свои параметры, среди которых случайно может оказаться параметр len или key.

А что, если там окажется параметр new, спросите вы и застанете меня врасплох (шутка). Для этого будем исследовать всю ту же PHP-переменную от Апача $_SERVER[‘QUERY’] и определим три PCRE выражения, жестко регламентирующие порядок и содержимое наших параметров:

$m1 = '/^len=(\d\d?)&new=(.*)$/';
$m2 = '/^key=(\w{'.BITLY_MIN.','.BITLY_MAX.'})&new=(.*)$/';
$m3 = '/^new=(.*)$/';

Таким образом, согласно этим 3-м простеньким PCRE выражениям мы в 1-ом выделяем len и new, во втором key и new, и в третьем – только new.

Тут главное обратите внимание на символ ^ в начале строк, которые означают начало строки. Мы выбираем наши параметры только из начала запроса, а всё что в середине его, например, в переданной ссылке, нас интересует только косвенно.

Теперь определяем переменные согласно preg_match‘ам, и создаём новый ключ, как показано в полном листинге внизу поста.

URI мы проверяем на нестрогое соответствие, а если такой URI уже есть в нашей базе, выдадим связанный с ним ключ, если только мы не создаём свой пользовательский ключ через параметр key.

Результат мы просто выводим в браузер в виде простой текстовой строки через мой любимый php метод die. Таким образом, для получения ключа надо просто прочитать вывод от браузера:

$url = 'http://www.some-site.com/очень-очень длинная неприятная ссылка, которую надо бы укоротить';
$key = file_get_contents('http://mysite.ru/go.php?new='.$url);
$shorten_url = 'http://mysite.ru/go.php?to='.$key;

Теперь вместо $url мы можем использовать $shorten_url и быть в шоколаде!
Здесь mysite.ru – сайт, на котором мы разместили свой супер-скрипт go.php, чей полный листинг вы найдёте в конце этого поста.

Вот ещё пример получения нового ключа и использовния его:

// полная ссылка на пост-оплату через сервер РБК Мани
$pars = "em={$email}&sum={$sum}&title=".urlencode('Оплата за комплект '.$set_title);
$rbk_href = 'http://mysite.ru/rbk.php?'.$pars;
// используем наш собственный bitly
$key = file_get_contents('http://mysite.ru/go.php?new='.$rbk_href);
$rbk_href = 'http://mysite.ru/go.php?to='.$key;

Здесь мы, не мудрствуя лукаво, забрали ключ через file_get_contents.

Немного статистики

Да, раз уж мы фиксируем дату/время последнего обращения к ссылке и число таких обращений (переходов), то неплохо было бы иметь возможность быстро получать инфо по отдельным ссылкам, чтобы каждый раз не залазить в PHPMyAdmin. Для это придумаем ещё один ключ – stat, который будем использовать совместно с ключом key.

И опять же по нашему соглашению в начале запроса должен идти параметр key, и сразу после него – параметр stat. Для выявления этого составим такое PCRE выражение:

$m = '/^key=(\w{'.BITLY_MIN.','.BITLY_MAX.'})&stat/';

Если мы нашли такое соответствие в строке запроса, выводим в одной простой строке дату/время в формате MySQL и через символ пайпа (|) счётчик обращений к ссылке – эту строку легко можно будет пропарсить и учесть.

Кусок такого код может выглядеть так:

$m = '/^key=(\w{'.BITLY_MIN.','.BITLY_MAX.'})&stat/';
 
if(preg_match($m,$q,$ar)) {
  $key = $ar[1];
  // ищем связанные данные
  $r = $db->prepare("SELECT dthit,hits FROM $bitly WHERE `key`=?");
  if($r->execute(array($key))) {
    list($dthit,$hits) = $r->fetch(PDO::FETCH_NUM);
    die($dthit.'|'.$hits);
  }
  // bad key, very bad
  die($biterr['badkey']);
}

Простенькая статистика готова, а если нам будет нужна полная статистика, можно использовать тот же PHPMyAdmin или написать свою админку под это дело.

Готовый продукт

Итак, наш скрипт практически готов – смотрите полный листинг рабочего скрипта ниже.

Правда, остаётся пара нюансов (issues по-английски) – о первом я уже упоминал, это отрезание хэша браузером от строки запроса, а второй – защита от спамерских атак, когда на нас может хлынуть поток “левых” запросов и зафлудить нашу таблицу ссылок.

Последний нюанс решается технически по IP, то есть можно добавить поле ip для получателя нового ключа, и если этот ip запрашивает, например, более 100 ссылок в час, блокируем его на какое-то время (сутки-двое), чтобы отдохнул, проспался. Совсем блокировать такой ip наверное не надо, так как спамеры в большинстве случаев используют протрояненные боксы обычных юзеров или взломанные сервера, которые после лечения становятся “белыми”.

А нюанс с символом хэша (#) я пока не знаю как обойти, если узнаю – добавлю сюда инфо, ОК?

А пока всё, я пью какао, а вы – изучайте полный скрипт нашего bitly!

Тем более он совсем невелик – не более 200 строк PHP кода вместе с комментариями и отступами.

<?php
/* 
     ** Файл GO.PHP - наш собственный bitly **
 
  Параметры GET:
 
	go - ключ для перехода по связанному с ним uri
 
	new - создать новый ключ для указанного в этом параметре URI
 
	При параметре new можно ПЕРЕД НИМ указать один из следующих параметров:
 
		key - ключ для связи (длиной BITLY_MIN..BITLY_MAX)
		len - желаемая длина BITLY_MIN..BITLY_MAX
 
	Примечание - можно указывать параметр 
	только либо key, либо len,
	ибо при применении одного второй теряет смысл.
 
	key должен состоять из ASCII символов A..Z, a..z и цифр
 
	Так же эти параметры (если есть) 
	должны ПРЕДШЕСТВОВАТЬ параметру new
 
        Используем простую таблицу БД, описание которой
        смотрите выше в этом посте
 
*/
 
//////////////////////////////////////////////////////
/////////////////	CONNECT DBASE	/////////////////
////////////////////////////////////////////////////
$db = new PDO(
	'mysql:host=localhost;dbname=OUR_DATABASE;charset=utf-8',
	'USER',
	'PASSWORD',
	array(PDO::MYSQL_ATTR_INIT_COMMAND=>'SET NAMES UTF8')
);
 
//////////////////////////////////////////////////////
///////////////	ОПРЕДЕЛИМ КОНСТАНТЫ	/////////////////
////////////////////////////////////////////////////
 
define('BITLY_MIN',3);		// min length of a key
define('BITLY_MAX',16);		// max length of a key
 
define('BITLY_DEFAULT_LENGTH',8);	// max wanted length of a key
 
// строки сообщений о непорядке
$biterr = array(
	'badreq' => '* BAD REQ *',	// плохой запрос
	'badkey' => '* BAD KEY *',	// плохой ключ
	'baduri' => '* BAD URI *'	// плохой URI
);
 
// рабочая таблица
$bitly = 'bitly';
 
// строка запроса, $_GET нам не подходит
$q = $_SERVER['QUERY_STRING'];
$m = '/^to=([a-zA-Z\d]{'.BITLY_MIN.','.BITLY_MAX.'})$/';
 
if(preg_match($m,$q,$ar)) {
	// чистим ключ
	$key = $ar[1];
 
	// запрашиваем связанный с ним uri
	$r = $db->prepare("SELECT uri FROM $bitly WHERE `key`=?");
	$r->execute(array($key));
	$ar = $r->fetch(PDO::FETCH_ASSOC);
 
	if(!$ar) {
	// не используем первый die, так как он может 
	// привести в замешательство пользователя?
	//	die($biterr['badkey']);
		header('Location: /');
		die;
	}
 
	$r = $db->prepare("UPDATE $bitly SET dthit=NOW(), hits=hits+1 WHERE `key`=?");
	$r->execute(array($key));
 
	header('Location: '.$ar['uri']);
	die;
}
// может, запрос статистики по ключу?
$m = '/^key=(\w{'.BITLY_MIN.','.BITLY_MAX.'})&stat/';
if(preg_match($m,$q,$ar)) {
  $key = $ar[1];
  // ищем связанные данные
  $r = $db->prepare("SELECT dthit,hits FROM $bitly WHERE `key`=?");
  if($r->execute(array($key))) {
    list($dthit,$hits) = $r->fetch(PDO::FETCH_NUM);
    die($dthit.'|'.$hits);
  }
  // bad key, very bad
  die($biterr['badkey']);
}
 
// если не переход и не статистика, может создать новый ключ для ссылки?
$key = false;
$len = BITLY_DEFAULT_LENGTH;
 
$m1 = '/^len=(\d\d?)&new=(.*)$/';
$m2 = '/^key=([a-zA-Z\d]{'.BITLY_MIN.','.BITLY_MAX.'})&new=(.*)$/';
$m3 = '/^new=(.*)$/';
 
if(preg_match($m1,$q,$ar)) {
	$len = intval($ar[1]);
	$uri = $ar[2];
}
else if(preg_match($m2,$q,$ar)) {
	$key = $ar[1];
	$uri = $ar[2];
}
else if(preg_match($m3,$q,$ar))
	$uri = $ar[1];
else
// bad request
	die($biterr['badreq']);
 
// проверим URI на соотвтествие (нестрогое,
// нам тут строгое в принципе и не нужно)
if(!isUrl($uri))
	die($biterr['baduri']);
 
// пробуем определить есть ли уже такой uri
$r = $db->prepare("SELECT `key` FROM $bitly WHERE uri=?");
$r->execute(array($uri));
$ar = $r->fetch(PDO::FETCH_ASSOC);
// если есть, выведем его
// при условии, что мы не просим свой ключ
if(!$key && $ar)
	die($ar['key']);
 
if($len < BITLY_MIN || $len > BITLY_MAX)
	$len = BITLY_DEFAULT_LENGTH;
 
$r = $db->prepare("INSERT INTO $bitly (`key`, `uri`, `dtm`) 
							VALUES (:key, :uri, NOW())");
$r->bindParam(':key', $key);
$r->bindParam(':uri', $uri);
 
if(!$key)
  do {
	$key = randomKey($len);
  } while(!$r->execute());
else {
	if(!$r->execute())
		die($biterr['badkey']);
}
 
die($key);	// all is OK?
 
/////////////////////////////////////////////////////////////////////////////
function randomKey($len = 7)
{
	$chars = 'ABCDEFGHIJKLM012345NOPQRSTUVWXYZabcdefghijklm6789nopqrstuvwxyz';
	$key = '';
	$c = strlen($chars);
 
	for ($i = 0; $i < $len; ++$i)
		$key .= substr($chars, (mt_rand() % $c), 1);
	return $key;
}
////////////////////////////////////////////////////////////////////
function isUrl($test)
{
// этот скрипт я откопал где-то на stackoverflow
    if (strpos($test, ' ') > -1)
        return false;
	if(strlen($test) > 10000)
		return false;
 
    if (strpos($test, '.') > 1) {
        $check = @parse_url($test);
        return is_array($check)
            && isset($check['scheme'])
            && isset($check['host']) 
			&& count(explode('.', $check['host'])) > 1;
	}
    return false;
}

Что за зверь – Rhino 1.7R4 ?

Прочитал в книге Давида Флэнагана (David Flanagan) (6-ое издание) о JavaScript-интерпретаторе Rhino – смесь JavaScript и Java, точнее, это JavaScript над Java, то есть JavaScript имеющий доступ к родным Java объектам, но использующий в основном свой родной JavaScript, наш любимый синтаксис. Причём сам Rhino написан на Java!

Мне очень понравилось, и я хочу сейчас попробовать его в деле. Флэнаган дал лишь краткое знакомство с этим чудо-зверем, для полноценной работы нам надо будет ещё шариться по инету и искать достойную литературу, но пока можно попробовать написанный Давидом пример в конце раздела про Rhino, который якобы позволяет скачивать файлы из интернета в указанное пользователем место, используя простой Java swing интерфейс и оконный менеджер.

Итак, я скачиваю Rhino по адресу, указанному в книге – https://developer.mozilla.org/en-US/docs/Rhino. Да, это продукт от Mozilla, корпорации типа Microsoft, только менее известной и беднее, чем детище Билла Гейтса (шутка, я обожаю Firefox – продукт Mozilla corp.)

В книге описывается версия 1.7R2, сейчас, 27 ноября 2013РХ доступна версия Rhino 1.7R4. Беглый просмотр добавленный возможностей выявил пару полезных моментов (https://developer.mozilla.org/en-US/docs/New_in_Rhino_1.7R4):

  • Теперь по умолчанию используется версия JavaScript 1.7 (ранее согласно книге Давида, нам надо было указывать version(170) для использования продвинутых возможностей новой версии JavaScript) – теперь, судя по всему, это делать не надо.
  • Доступны JavaScript 1.8 выражения-генераторы (на момент написания книги они не были доступны)

И ещё ряд полезных исправлений, улучшений и добавлений!

Ладно, всё это круто, но пора попробовать этого носорога на вкус, что ли. Скачиваем пример Флэнагана (http://examples.oreilly.com/9780596805531/examples/12.01.rhinoURLFetcher.js) для Rhino и пробуем его запустить (см. скриншот из Far manager’а):

far-scr

Уууууупс, ошибочка вышла. А я люблю ошибки! Каждая ошибка пробуждает во мне дух соперничества, я всегда хочу выявить истоки возникновения ошибки и исправить её! Как это сделать? Парсим инет, господа!

Запрос для Гугла такой такой:
js: Cannot convert function to interface java.awt.event.WindowListener since it contains methods with different names

Понятно, что ошибка возникает видимо здесь:

// When anything happens to the window, call this function.
frame.addWindowListener(function(e, name) {
    // If the user closes the window, exit the application.
    if (name === "windowClosing")                // Rhino adds the name argument
        java.lang.System.exit(0);
});

но как её обойти? И работает ли этот код в версии 1.7R2, которую использовал Давид? То что Давид – профессионал, и я восхищён его работой – это так, но всё же есть такая старая японская поговорка – «Доверяй, но проверяй»

Гуглим “Rhino 1.7R2”, на 4-ой строчке результатов поиска попадаем на http://mvnrepository.com/artifact/rhino/js/1.7R2, скачиваем jar и кидаем его в папку с ранее скачанным Rhino с сайта Mozilla (у меня это Desktop/rhino1_7R4/, и вновь запускаем пример Флэнагана на выполнение, только используем не js.jar, а js-1.7R2.jar:

java -jar js-1.7R2.jar 12.01.rhinoURLFetcher.js

И – вуаля – программа запустилась как миленькая. Флэнаган, Давид – ты чудо! При вводе корректного url в поле ввода, программа спросит локацию для нового файла через окно выбора файлов, и скачивает файл в отдельном потоке, то есть мы можем скачивать несколько больших файлов одновременно!

far_screen2

Но как же быть с новым Rhino, ведь не останемся же мы со старой версией, мы хотим двигаться дальше и использовать больше возможностей.

Будем исправлять ошибку или нет?

А она исправляется достаточно легко. (Благодарю ссылку http://mhayden.iclouddevelopers.net/jsserverside/phasellus-fringilla/, если ссылку можно благодарить?)

Судя по всему, теперь нельзя использовать общий перехватичик всех событий окна, надо писать либо отдельные перехватчики для каждого события, либо создать объект, собирающий и выполняющий нужные нам перехватчики. Итого, мы комментируем или удаляем устаревший код и добавляем работающий на Rhino1.7R4 такой код:

// When anything happens to the window, call this function.
/* 
-- такой способ в новом Rhino1.7R4 уже не работает почему-то,
поэтому мы его здесь закомментировали, его можно удалить
 
frame.addWindowListener(function(e, name) {
    // If the user closes the window, exit the application.
    if (name === "windowClosing")                // Rhino adds the name argument
        java.lang.System.exit(0);
});
*/
 
// Новый путь перехвата Window Events
var listener = new java.awt.event.WindowListener({
	windowClosing: function(event) { java.lang.System.exit(0); },
 
// ... тут можно определить и другие обработчки, как то например:
// windowClosed, windowIconified, windowOpened и т.д.
 
}); 
 
frame.addWindowListener(listener);

Всё, сохраняем скрипт и запускаем его на выполнение уже новым Rhino:

java -jar js.jar 12.01.rhinoURLFetcher.js

Теперь всё работает, и мы в шоколаде, а всё благодаря Давиду!
far_screen3

Да, у меня были сомнения, пойдёт ли он под Windows, но мои сомнения были напрасны – Rhino наверное будет работать с любой системой, где стоит Java, на Debian я тоже удачно запустил пример Давида (см. скриншот ниже):
amd64_screen

Ещё один интереснейший проект интерпретатор JavaScript, упоминаемый в книге Давида Фланагана, это nodejs (http://nodejs.org), видимо, тоже крутая штука, тема для отдельных постов.

До скорой встречи, друзья!

JavaScript: Геометрия элемента – позиция, размеры, прокрутка и перемещение

Как Web-программисту передо мной иногда возникают вопросы позиционирования и определение размеров элементов на HTML-странице или в HTML-документе. Приходится каждый раз копаться в книгах, парсить интернет и т.п. Вопросы на форумах я уже давно не задаю, так как по-моему почти все вопросы уже заданы и наша задача – найти правильные ответы на них.

И вот я решил соединить все полученные мной знания в этом посте, где я постараюсь подробно описать тему БЕЗ применения сторонних библиотек, таких как jQuery или Prototype, то есть мы будем использовать pure JavaScript, то есть чистый JavaScript, и код в принципе по идее должен корректно работать в большинстве браузеров, как старых так и новых версий.

Определяем позицию полос прокрутки

Как правило, для каждой страницы HTML существует две точки отсчёта координат, относительно которых мы получаем координаты мыши и элементов – это начало документа или страницы и левый верхний угол видимого окна, называемый иногда viewport, или просто window.

Например, при получении координат мыши мы можем обратиться к свойствам pageX,pageY – координаты отчёта от начала документа, и clientX,clientY – координаты относительно текущего окна (viewport или window). Т.о. если мы прокручмвали документ, эти координаты будут отличаться друг от друга, а нам надо привести всё к общему знаменателю для точного позиционирования элементов.

Для этого нам надо знать величины прокрутки, чтобы оттлакиваться уже от них, и поэтому пишем универсальную функцию getScrollOffsets, возвращающую эти значения и корректно работающую для практически всех браузеров:

	// Возвращает текущий сдвиг полос прокрутки
        // всего документа по умолчанию или любого элемента,
        // если мы передадим его в параметре
	var getScrollOffsets = function(w) {
		w = w || window;
		if(w.pageXOffset != null)
			return { x: w.pageXOffset, y: w.pageYOffset };
		var d = w.document;
		if(document.compatMode == 'CSS1Compat')
			return { 
                           x: d.documentElement.scrollLeft,
                           y: d.documentElement.scrollTop
                               };
		// quirks mode?
		return { x: d.body.scrollLeft, y: d.body.scrollTop };
	}

Эту полезную функцию я взял из книги Флэнагана “JavaScript. Подробное руководство”, протестировал её и она реально работает.

Находим позицию элемента

Теперь мы без проблем сможем найти позицию элемента относительно документа (а не окна), используя универсальный метод getBoundingClientRect и прибавив значения полос прокрутки:

	var findPos = function(e) {
		var x = 0, y = 0;
		if(e.getBoundingClientRect) {
                // 1. Используем основной вариант
			var box = e.getBoundingClientRect();
			var scrolls = getScrollOffsets();
			return { 
                           x: box.left + scrolls.x,
                           y:box.top + scrolls.y
                        };
		}
                // 2. Используем резервный вариант
		while(e) {
			x += e.offsetLeft;
			y += e.offsetTop;
			e = e.offsetParent;
		}
		return { x:x, y:y };
       }

В этой функции мы добавили проверку на наличии в текущем агенте (т.е. браузере) читателя метода getBoundingClientRect(), и если его нет- используем второй вариант, менее надёжный, так как он не всегда возвращает правильные результаты для различных версий браузеров.

Но в принципе сегодня у всех агентах, что я тестировал эту функцию, присутствует getBoundingClientRect() метод, поэтому 2-ой вариант с использованием offset[Left,Top,Parent] можно наверное удалить.

Втискиваем элемент в видимую часть окна

Иногда нам бывает нужно, что бы создаваемый на лету элемент с абсолютным позиционированием влезал в пределы видимой части окна, то есть всегда был виден целиком, а не торчала какая-то его часть, если он был вызван где-нибудь у края окна.

Для этого нам нужно помимо того, что мы уже знаем, определять размеры элемента, а также размеры viewport’а (то есть текущие размеры окна).

Первое досигается легко за счёт offsetWidth,offsetHeight свойств элемента, а для второго нам опять-таки надо написать вспомогательную (helper) функцию.

        // получить размеры видимой части окна
	var getViewportSize = function() {
		var d = window.document;
 
		if(document.compatMode == 'CSS1Compat')
			return { 
                           w: d.documentElement.clientWidth,
                           h: d.documentElement.clientHeight 
                        };
		// quirks mode?
		return { w: d.body.clientWidth, h: d.body.clientHeight };
	}

Эту функцию я также взял из книги Флэнагана, с одним важным нюансом – Флэнаган пишет, что надо использовать свойства innerWidth,innerHeight, если они доступны (не в IE<9) - они могут быть у всех элементов, не только у объекта window), но как показала практика и мои тесты, эти свойства включают в себя ширину полос прокрутки, и нам не годятся поэтому, так как при позиционировании элемент в этом случае будет залазить под полосы прокрутки, если они есть. Поэтому мы используем window.document.documentElement.[clientWidth,clientHeight] для получения нужного результата, и это работало во всех агентах, что я тестировал - IE>=7, Fx, Chrome, Opera,Safari (Windows 7).

Итак, теперь вписываем наш элемент в границы видимой части окна так:

		// высчитываем новую позицию нашего элемента
 
                // сперва найдём нужные данные
                // описание этих функций ищите выше в этом посте
		var  scroll = getScrollOffsets(), // полосы прокрутки
	             view = getViewportSize();    // размер viewport'а
 
		// логические координаты right,bottom для viewport'а
		var winright = scroll.x + view.w,
			winbottom = scroll.y + view.h;
 
                // предположим, что наш элемент - widget
                // и для него уже определена некая
                // начальная позиция в координатах xpos, ypos.
                // И мы должны убедиться, что наш элемент
                // влезает в видимую часть окна:
 
                // Сперва проверим вылезание за правый/нижний края
		if(xpos + widget.offsetWidth  > winright)
			xpos = winright - widget.offsetWidth;
		if(ypos + widget.offsetHeight  > winbottom)
			ypos = winbottom - widget.offsetHeight;
                // теперь - за левый или верхний края.
                // Вы можете делать и наоборот, 
                // смотря какая часть окна для вас важнее
		if(xpos < scroll.x)
			xpos = scroll.x;
		if(ypos < scroll.y)
			ypos = scroll.y;
 
		// Теперь просто позиционируем элемент,
                // и если ему хватает места,
                // он всегда должен быть полностью виден в окне
		widget.style.left = xpos + 'px';
		widget.style.top = ypos + 'px';

Вуаля! Красиво и профессионально, как и должно быть.
Теперь перейдём к ещё одной вкусняшке – drag&drop, или в просторечье, буксировке.

Перетаскиваем элемент

Эту функциональность наверное правильно будет назвать просто dragging, то есть буксировка, перетаскивание с места на место, возможно, с какой-нибудь определенной целью или без оной.
Она осуществляется довольно просто и работает независимо от агента пользователя (которые понимают JavaScript и рендерят оконные страницы).

Сперва определимся с алгоритмом. Нам надо перехватывать три мышиных события – mousedown,mousemove и mouseup.

В первом (mousedown) – если оно произошло в пределах участка нашего элемента, предназначенного для его перетаскивания (этот может быть сам элемент целиком, либо, например, его заголовок) – мы фиксируем разницу между текущими координатами мыши и позицией левого верхнего угла нашего элемента, а также включаем условный флаг dragging, сигнализирующий о том, что мы готовы перемещать наш элемент.

Во втором событии (mousemove), если поднят флаг dragging, мы отнимаем полученные в обработчкие mousedown смещения от текущей позиции курсора и по ним позиционируем наш элемент.

В третьем, самом простом, но не менее важном обработчкике (mouseup) мы просто сбрасываем (опускаем) наш условный флаг dragging, сигнализируя, что перетаскивание элемента завершено.

Всё это простое действо может уместиться буквально в десятке строчек JavaScript-кода и реально работает!

Смотрите функциональный код с дополнительными короткими комментариями:

	// функция getScrollOffsets() определена выше в этом посте
	// функция attach - кросс-браузерный вариант 
	// привязки обработчиков событий - смотрите ниже в этом посте
 
	// определим три глобальные переменные
	// две - для хранения вычисленного смещения (см.выше)
	var widgetOffLeft,widgetOffTop,
	// и ещё одна - для ссылки на стиль виджета,
	// которая также будет являться флагом dragging
		dragPos = null;
 
	// условимся, что перетаскивать можно за любую часть виджета
	// поэтому начинаем операцию dragging при захвате его любой части
	// для чего привязываем к нему обработчик mousedown
	attach(widget,'mousedown',function(event) {
		if(!event) event = window.event; // для IE<9
 
		// определим позицию полос прокрутки (scrollbars)
		var scroll = getScrollOffsets();
 
		// вот оно - смещение текущих координат мыши
		// и top,left координат нашего виджета.
		// Сво-ва offsetLeft,offsetTop
                // определены для любого элемента на странице
                // и обозначают их координаты X,Y
		widgetOffLeft = event.clientX + scroll.x - 
                         widget.offsetLeft;
		widgetOffTop = event.clientY + scroll.y - 
                         widget.offsetTop;
 
		// определяем ссылку на стиль виджета
		// параллельно поднимая флаг dragging
		dragPos = widget.style;
 
		return false; // дальше обрабатывать это событие не нужно?
	});
 
	// остальные два обработчика привязываем к документу
	attach(document,'mousemove',function (event) {
		if(!event) event = window.event; // для IE<9
		// тащим наш виджет
		if (dragPos) {
			// определим позицию полос прокрутки (scrollbars)
			var scroll = getScrollOffsets();
 
			// позиционируем наш виджет
			dragPos.left = (event.clientX + scroll.x - widgetOffLeft) + 'px';
			dragPos.top = (event.clientY + scroll.y - widgetOffTop) + 'px';
		}
	});
 
	attach(document,'mouseup',function() {
		// опускаем флаг dragging, 
		// когда кнопка мыши отпущена
		dragPos = null; 
	});

Замечу, что этот код позволяет перетаскивать виджет любой кнопкой мыши – как левой, так и правой и даже средней. Для предотвращения этого в обработчике mousedown проверяем флаг нажатой кнопки мыши (свойство события button: 0 – левая, 1 – средняя, 2 – правая) и действуем соответственно.

В заключении приведу функцию attach – кросс-браузерный вариант привязки обработчкиков событий.

	var attach = function(element, type, fn){
		if (element.addEventListener)
			element.addEventListener(type, fn, false);
		else if (element.attachEvent)
			element.attachEvent('on' + type, fn);
	};

Всё гениальное – просто. Нет, я не устану это повторять. пусть оно у меня в зубах завязнет. )))

Определяем ширину скролл-баров

Зачем это нужно? Вот и я думаю, зачем. Но когда я использовал оригинальную версию из книги Фланагана функции getViewportSize, как я уже упоминал, она возвращала размеры видимой части окна вместе с шириной скроллбаров (точнее, эти значения возвращали сво-ва innerWidth,innerHeight), и я решил идти напролом и найти ширину бордюра, чтобы отнимать её из полученных значений и корректно позиционировать свой элемент.

И я после недолгих поисков нарыл такую, видимо корректную функцию:

function defineScrollbarsWidth() {
// работала во всех моих браузерах под Win7
// и возвращала ширину в 17px
	var inner = document.createElement('p');
	inner.style.width = "100%";
	inner.style.height = "200px";
 
	var outer = document.createElement('div');
	outer.style.position = "absolute";
	outer.style.top = "0px";
	outer.style.left = "0px";
	outer.style.visibility = "hidden";
	outer.style.width = "200px";
	outer.style.height = "150px";
	outer.style.overflow = "hidden";
	outer.appendChild(inner);
 
	document.body.appendChild(outer);
	var w1 = inner.offsetWidth;
	outer.style.overflow = 'scroll';
	var w2 = inner.offsetWidth;
 
	if (w1 == w2) {
		w2 = outer.clientWidth;
	}
 
	document.body.removeChild(outer);
 
	return (w1 - w2);
}

Но использование лишнего кода, тем более относительного длинного и замороченного (я сторонник минималистских и простых решений везде, где это возможно), было нежелательно для моего проекта, поэтому я нашёл способ определять размеры viewport’а без ширины полос (используя window.document.documentElement.[clientWidth,clientHeight], как я и писал выше), и эта функция мне в принципе не понадобилась, но на всякий случай я её здесь всё же привёл, вдруг когда-нибудь понадобится определить ширину полос скроллбара.

А пока всё!

Крепкого чая, крепкой любви и крепкого счастья.