Author Archives: Сергей Царь

Комментарии VKontakte/Facebook – перехватываем и отправляем нам на почту

Добавляем виджеты комментариев VK/FB на свою страничку

Создаём приложения

Добавить комменты VK/FB на свой сайт относительно просто – создаём соотв. приложения в VK/FB, получаем их id. Вот адреса страничек для создания приложений: https://vk.com/dev/widgets_for_sites для ВК и https://developers.facebook.com/apps/ для Фэйсбук.

Подробно на этой теме останавливаться нет смысла, так как у них меняется всё очень часто, я имею в виду всякую воду и интерфейс, и каждый раз вникать в одно и тоже под другим соусом не особенно хочется. Но принцип видимо остаётся одним и тем же, и созданное нами несколько лет назад приложения продолжают работать. Честно говоря, назвать приложением это от FB/VK у меня язык с трудом поворачивается, скорее это надстройка над виджетами, контроллер виджетов точнее. Главное – они обеспечивают связь нашего сайта с многотысячной армией пользователей этих популярных сетей.
Continue reading

ffmpeg: склеиваем два видео-файла в один и исправляем ошибку “width not divisible by 2”

Здоровья и благополучия!

1.mov + 2.mov = output.mp4

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

Итак, самый простой и верный вариант.

Имеется два видео-файлика, 1.mov и 2.mov, мы пытаемся их склеить в output.mp4

Лобовой метод с применением фильтра concat как со списком файликов так и с инжектом в команду не прокатил, возможно, версия моего виндового FFMPEG не подходила, возможно, ещё что. Но нам некогда возиться с апгрейдами, надо всё сделать быстро и в срок.
Continue reading

Часовые пояса и расширение tar.lz в Windows

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

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

function getOptTimeZones($dk_timezone = 0)
{
	// построим select для dk_timezone
	$dk_tzs = array(
-15 => '(GMT -12:00) Эневеток, Кваджалейн',
-14 => '(GMT -11:00) Остров Мидуэй, Самоа',
-13 => '(GMT -10:00) Гавайи',
-12 => '(GMT -9:00) Аляска',
-11 => '(GMT -8:00) Тихоокеанское время (США и Канада), Тихуана',
-10 => '(GMT -7:00) Горное время (США и Канада), Аризона',
-9 => '(GMT -6:00) Центральное время (США и Канада), Мехико',
-8 => '(GMT -5:00) Восточное время (США и Канада), Богота, Лима',
-7 => '(GMT -4:00) Атлантическое время (Канада), Каракас, Ла Пас',
-6 => '(GMT -3:00) Бразилия, Буэнос-Айрес, Джорджтаун',
-5 => '(GMT -2:00) Среднеатлантическое время',
-4 => '(GMT -1:00) Азорские острова, острова Зелёного Мыса',
-3 => '(GMT  0:00) Дублин, Лондон, Лиссабон, Касабланка, Эдинбург',
-2 => '(GMT +1:00) Брюсель, Копенгаген, Мадрид, Париж, Берлин',
-1 => '(GMT +2:00) Афины, Киев, Минск, Бухарест, Рига, Таллин',
0 => '(GMT +3:00) Москва, Санкт-Петербург, Волгоград',
1 => '(GMT +4:00) Абу-Даби, Баку, Тбилиси, Ереван',
2 => '(GMT +5:00) Екатеринбург, Исламабад, Карачи, Ташкент',
3 => '(GMT +6:00) Омск, Новосибирск, Алма-Ата, Астана',
4 => '(GMT +7:00) Красноярск, Норильск, Бангкок, Ханой, Джакарта',
5 => '(GMT +8:00) Иркутск, Пекин, Перт, Сингапур, Гонконг',
6 => '(GMT +9:00) Якутск, Токио, Сеул, Осака, Саппоро',
7 => '(GMT +10:00) Владивосток, Восточная Австралия, Гуам',
8 => '(GMT +11:00) Магадан, Сахалин, Соломоновы Острова',
9 => '(GMT +12:00) Камчатка, Окленд, Уэллингтон, Фиджи');
 
	$s = '';
 
	for($i = -15; $i < 10; $i++) {
		$s .= '<option value="' . $i . '"';
		if($i == $dk_timezone)
			$s .= ' selected';
		$s .= '>' . $dk_tzs[$i] . '</option>';
	}
 
        return $s;
}

То есть мы построили список из option элементов для select‘а для сдвига от МСК. Т.о. (GMT +3:00) Москва, Санкт-Петербург, Волгоград у нас будет иметь значение 0, а всё остальное плюс-минус от этого значения. Переменная $dk_timezone содержит текущий сдвиг, что позвлояет выделить нужный option. Полученный результат надо облечь в select элемент.

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

При поиске готового архивчика с таким зонами я часто натыкался на ссылку http://www.iana.org/time-zones, где якобы представлна вся инфо по теме.

На этом уважаемом сайте действитльено даётся свободная скачка трёх интресных файликов:
Latest version
2016j (Released 2016-11-23)
File Description
tzdb-2016j.tar.lz (380.4kb) Complete Distribution (Data, Code and Extras)
tzdata2016j.tar.gz (313.7kb) Data Only Distribution
tzcode2016j.tar.gz (203.8kb) Code Only Distribution

Заметьте, что два файлика имеют расширение tar.gz, и один, самый интересный, за каким-то лешим имеет расширение .tar.lz. я не знаю, что творится в голове администратора данного ресурса, но выглядит это забавно. Что стукнуло ему в голову самый нужный файлик облечь в расширение .tar.lz, вместо доброго старого .tar.gz или на худой конец такого же доброго и такого же старого и проверенного временем tar.bz2?

Я сейчас пока вишу в Виньде (Windows 10), для распаковки пользуюсь 7zip (http://www.7-zip.org/) – огромная благодарность Игорю Павлову со-товарищи за разработку столь замечательного продукта. tar.gz и tar.bz2 (как и многие другие архивы) он щёлкает как семечки, а вот об tar.lz (по крайней мере на текущий момент, что я сейчас пишу) обламывает свои острые мощные зубки:

7zip fails to open tar.lz archive

Я даже проапгрейдил свой 7zip до версии 16.04, топовой на сегодня – 2016-11-23, т.е. 23 ноября 2016 РХ.

Быстрый гугл вывел на зверя lzip, я где-то нарыл скомпиленную под винду версию – lzip.exe, быстро запустил его в терминале,и вот что он выдаёт, собака:


С:\>lzip -d tzdb.tar.lz
tzdb.tar.lz: file ends unexpectedly at pos 21
lzip: Deleting output file `tzdb.tar', if it exists.
lzip: WARNING: deletion of output file (apparently) failed.

Я перименовал яновский tzdb-2016j.tar.lz в tzdb.tar.lz

Итак, что же делать? Далее гуглим и находим такую страничку: http://mm.icann.org/pipermail/tz/2016-September/024112.html

Вкратце, там человек по имени Oscar van Vlijmen жалуется, что не может открыть файлик lz в windows, такой же бедолага как и я, и упоминает тот же lzip, который я только что описал чуть выше.

Зачем, зачем, зачем админы http://www.iana.org/time-zones запаковали lzip‘ом только один файлик, а не все три тогда уж? Что у них, повторяюсь в третий раз, в головах творится?

Итак, кто-то ему отвечает:

Q: Why did the tachyon cross the road?
A: Because it was on the other side.

К чему это? Сколько лет ответившему так? он считает себя самым умным или самым остроумным? нет слов.

Следующий чуть лучше отвечает Оскару:

> do we get a decent application program with a graphical user interface
You mean, like the other tools we’re using? Like ‘tar’, ‘make’, and ‘cc’? 🙂

С одной стороны он прав,а с другой нет. Если ты выкладываешь файл для всего мира на скачку без явного уклона в одну систему, сделай его максимально доступным для мира, хотя бы для наиболее распространенных систем.

Ладно, этот же человек подсказал, что в CygWin‘е есть lzip, сейас его попробую, он скачивает кучу зависимостей за собой, что обычно для Unix-way. Спасибо ему, всё же он не так плох, как я думал вначале. 🙂 шутка

О, урра, получилось!!

Итак, по шагам, как я распаковал tzdb-2016j.tar.lz с сайта http://www.iana.org/time-zones в Windows 10:

1. Инсталлируем CygWin (https://cygwin.com/install.html), я это сделал в папку E:\cygwin64 – скачиваем setup-x86_64.exe файлик, запускаем его, выбираем папки (e:\cygwin64 для самого Cygwin‘а и e:\cygwin64\_local\ для временных локальных фалов.

2. Далее ищем в фильтре lzip, выбираем пакеты (я выбрал оба пакета, хотя боюсь можно выбрать только сам lzip), нажимаем Next или Далее, он выдаст окошко с зависимостями, которые по умолчанию все выбраны, нажимаем ОК, далее или что там ещё, не помню, и минут через 5 инсталлятор закончит работу.

3. Кидаем в папку e:\cygwin64\home\ВашеИмя\ нужный архивчик (tzdb-2016j.tar.lz)

4. Запускаем e:\cygwin64\Cygwin.bat, делаем ls, чтобы убедиться, что он тут:

$ ls
tzdb-2016j.tar.lz

И даём команду на его распаковку в tar:


$ lzip -d tzdb-2016j.tar.lz

На экран ничего не вывелось, кроме нового приглашения, что для Линукса говорит о том, что всё прошло ОК (хотя это не всегда так есть), т.к. если бы была какая-то ошибка, нам бы об этом с радостью сообщили.

Смотрим ещё ls:

$ ls
tzdb-2016j.tar

7zip fails to open tar.lz archive

О, у нас появился tar архивчик. Пробуем открыть его уже штатными виндовыми методами, через 7zip, и он отлично открывается, показывая список файлов.

Всё, задача решена, можно пойти выпить чая или какао или на худой конец коньяка с кофе. Или кофе без коньяка, до НГ, точнее до 12:00 31 декабря 2016 года я спиртного не потребляю, печень берегу и голову.

На сим кланяюсь. Доброй ночи!

P.S. Да, в итоге, в этом архивчике много всего интересного, но то, что мне нужно – список часовых поясов со сдвигом GMT+- для простого пользователя не оказалось!

Потом я в архиве какого-то форума нашёл приведенный выше (в самом начале поста) список, быстро переделал его под PHP, убрал +-30 минут кое-где, т.к. они мне не были нужны. И так получилась функция getOptTimeZones.

Node.js: пишем простой сервер с middleware, используя Connect

Предисловие. Пара слов о том, почему Node.js

Да, Node.js – это Вещь (с большой буквы, как и написано).
Чем больше я читаю и узнаю о нём, и пробую его, тем больше он мне нравится. С Ruby и Python‘ом пока не складывается (все мои проекты были на PHP, поэтому поддержка новой платформы была не актуальна – это учить новые языки, новые библиотеки, соглашения, короче, всю платформу, тогда как на PHP я программирую более-менее активно уже более 10 лет).

Но вот пришёл Node.js, который может заменить собой полностью серверную платформу, и он использует язык, на котором я также программирую свыше 10 лет – великий и ужасный JavaScript.

Причём пользоваться им легко и приятно, как оказалось, с учётом npmnode package manager‘а, который предоставляет быстрый и удобный доступ к тысячам или десяткам тысяч пакетов для Node.js с учётом всех зависимостей и прочая и прочая.

Пишем собственный (custom) middleware

Итак, давайте сваяем простой сервер на Connect, используя добавочно свою middleware. У меня в папке git скопилось наверное сотня разных проектов, и для серфинга по ним я использую либо Total Commander для просмотра и редактирования файлов, либо браузер ч/з запущенный сервер Apache или nginx также для наглядного просмотра папок и файлов.

Но с приходом Node.js логичней будет использовать сервер на нём для тренировки и вообще.

Так как в проектах на git частенько используется файл README.md в Markdown-разметке, добавим к нашему серверу функцию просмотра таких файлов в виде HTML на лету, написав простенький middleware.

Как известно, middleware для Connect/Express – это функция с тремя параметрами – req, res и next – запрос, ответ и функция обратного вызова для продолжения цепочки обработки запроса клиента.

Если наш middleware оканчивает запрос, он должен послать команду res.end(…) (в случае использования Connect‘а, в Express выбор богаче, это отдельная тема) для отдачи ответа клиенту, иначе он должен вызвать функцию next без параметров для обычного продолжения обработки, либо next(err) – с объектом ошибки – для дальнейшей обработки и рендеринга возникшей ошибки.

Вкратце алгоритм нашего middleware (назовём его any_md) будет таков: если строка запрашиваемого ресурса оканчивается на `.md`, то мы читаем файл ресурса, прогоняем его через marked (модуль формирования HTML из MD) и отдаём клиенту как обычный HTML файл. Считается, что файлы расположены начиная от текущей папки (git).
Круто? Круто. Неслыханно? Да. Восхитительно? Ещё как!

Вот как это выглядит на JavaScript под платформу Node.js:

// MIDDLEWARE: отдаём запрошенный файл .md как .html
var any_md = function(req, res, next){
// это md-файл?
if(req.url.toLowerCase().slice(-3) == '.md') {
 fs.readFile('.' + req.url, function(err, data){
  if(err) return next(err); // ошибку отправляем дальше
 
  // преобразуем MD -> HTML и отсылаем клиенту
  var html = '<!doctype html>\n<html lang="en">\n' +
  '<head>\n\t<meta charset="utf-8">\n\t<title>' + 
  req.url.slice(1) + '</title>\n</head>\n' + 
  '<body>\n' + marked(data.toString()) + '\n</body>\n</html>';
 
  res.writeHead(200, {
   'Content-Length': Buffer.byteLength(html),
   'Content-Type': 'text/html; charset=utf-8',
  });
  res.end(html);
 });
}
else
 next();
};

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

Так же в нашем сервере мы не рассматриваем кастомную обработку ошибок, оставляя её на совесть Connect‘а, точнее модуля errorhandler.

Готовый node.js сервер на Connect

Для нашего сервера мы будем использовать следующие middleware (модули) для Connect/Express:

  • compression – gzip/deflate сжатие данных на лету
  • morgan – бывший logger, лог запросов на консоль либо в файл
  • serve-favicon – отдача браузеру favicon.ico
  • serve-index – отдача списка файлов (каталога) клиенту
  • serve-static – отдача статических файлов (т.е. в нашем случае всех) клиенту
  • errorhandler – обработка ошибок
  • marked – рендер md разметки в html

Итак, для создания сервера заходим в папку git, где у нас расположены папки десятков или сотен git-проектов, и выполняем команду:

npm install -g errorhandler connect compression morgan serve-favicon serve-index serve-static marked

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

NODE_PATH=C:\Users\Andrey\AppData\Roaming\npm\node_modules

Тут `Andrey` надо поменять на имя пользователя, которое используете вы.
Также модули compression, morgan, serve-favicon, и в принципе errorhandler, которые отвечают за сжатие запроса, логирование, обслуживание favicon и обработку ошибок, не обязательны и используются тут в качестве примера создания более функционального сервера.

Затем создаём такой файл Node-сервера с именем git-server.js:

/* **************************************** */
/* Статический сервер на текущий каталог    */
/* с возможностью просмотров папок и файлов */
/* **************************************** */
var connect = require('connect');
var errorhandler = require('errorhandler');
var compression = require('compression');	// gzip/deflate
var morgan = require('morgan'); // logger
var favicon = require('serve-favicon');	// favorite icon
var serveIndex = require('serve-index');	// directory
var serveStatic = require('serve-static');	// static
 
var fs = require('fs');
var marked = require('marked');
 
// MIDDLEWARE: отдаём запрошенный файл .md как .html
var any_md = function(req, res, next){
 // это md-файл?
 if(req.url.toLowerCase().slice(-3) == '.md') {
   fs.readFile('.' + req.url, function(err, data){
    if(err) return next(err);
    // преобразуем MD -> HTML и отсылаем клиенту
    var html = '<!doctype html>\n<html lang="en">\n' +
    '<head>\n\t<meta charset="utf-8">\n\t<title>' + 
    req.url.slice(1) + '</title>\n</head>\n' + 
    '<body>\n' + marked(data.toString()) + '\n</body>\n</html>';
 
    res.writeHead(200, {
       'Content-Length': Buffer.byteLength(html),
       'Content-Type': 'text/html; charset=utf-8',
    });
    res.end(html);
   });
 }
 else
  next();
};
 
var app = connect()
.use(morgan('combined'))
.use(favicon('./favicon.ico'))
.use(any_md)
.use(compression({level: 3, memLevel: 5}))
.use(serveStatic('.'))
.use(serveIndex('.', {'icons': true}));
 
//if (process.env.NODE_ENV === 'development') {
// такую обработку ошибок использовать
// только при разработке и тестинге 
app.use(errorhandler());
//}
 
app.listen(80, function(){
	console.log('Server started at localhost port 80');
});

Для использования модуля favicon‘а закиньте файл favicon.ico сюда же, в каталог git, если лень, закомментируйте строчку `.use(favicon(‘./favicon.ico’))`.

Итак, запускаем наш сервер:

node git-server.js

Сервер работает по адресу localhost, порт 80 (в Linux для использования этого порта могут понадобиться права root, поэтому лучше поменять порт на 3000, например).

Теперь заходим браузером по адресу localhost (если мы используем порт 80 его тут указывать не нужно) и видим список папок git-проектов. Зайдя в какую-нибудь, где есть файл README.md (или любой md-файл), щёлкнем на нём и увидим его как обычный HTML-файл в браузере.

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

Но главное – это любовь и хорошее настроения, блин.

bcrypt в NodeJS в Windows 10 Home и Linux Mint

Я работаю с nodejs в Windows 10 Home (параллельно иногда загружаюсь в Linux Mint, но редко).

Изучаю книгу `Node.JS в действии`, она, к сожалению, в некоторых местах устарела, но в целом даёт представление о теме.

Итак, в главе 9 нам нужен был brcypt, но простой npm install bcrypt –save завершился с ошибкой.
Пошарив инете пару минут, я понял, что просто так его не установить, и я уже думал проверять примеры в Linux Mint‘е своём любимом, но наткнулся здесь: http://stackoverflow.com/questions/14573488/error-compiling-bcrypt-node-js

Оказывается, bcrypt можно заменить bcrypt-nodejs, который у меня установился без проблем за пару секунд.
Далее по теме увидел такую надпись: The performance is a lot slower though, то есть, мол, производительность гораздо ниже, и даётся ссылка на https://github.com/adrianblynch/bcrypt-vs-bcrypt/blob/master/index.js.

Там тестятся следующие bcrypt пакеты для nodejs:

И даётся простенький скрипт-тестирование (его мы дополним и рассмотрим ниже).

Можно запустить, потестить какие результаты даст, сделаю это через какое-то время в Linux Mint’е (т.к. под свою Windows bcrypt я не установил и не буду заморачиваться, время терять).

Но в этом тесте нет bcrypt-nodejs, я его добавлю тогда, ОК, и результаты позже выведу **** обещанного три года ждут. 🙂

Но какова мораль сей басни? Nodejs настолько стремительно развивающийся проект со столькими вовлеченными и увлеченными пользователями, что для любой более-менее известной и популярной либы есть несколько решений под эту удивительную платформу. И если какая-то либа по тем или иным причинам не подойдёт (как например в нашем случае с bcrypt под Windows 10 (8, 8.1, 7 да любой наверное), мы с большой вероятностью можем найти полноценную замену.

Например, для mongodb есть mongoose, и есть monk, для sqlite куча пакетов, и т.д.

А почему стремительно развивающийся? Например, на оф.страничке Express (http://expressjs.com/en/guide/migrating-4.html) есть устаревший уже пример с multer (обработка запросов multipart/form-data) – там приводится пример для работы с “multer”: “^0.1.3”, а сейчас уже есть multer 1.1.0 (на 4 февраля 2016 года – 2016-02-04), который не совместим с указанным multer‘ом, то есть он не может напрямую выступать в качестве middleware теперь, а надо использовать по особому его методы single, array и т.п. (http://stackoverflow.com/questions/31495499/multer-configuration-with-app-use-returns-typeerror/31495796#31495796):

multer({dest:'./uploads/'}).single(...)
multer({dest:'./uploads/'}).array(...)
multer({dest:'./uploads/'}).fields(...)

На сим всё, откланиваюсь – за мной ещё тесты производительности, если не забуду. )))

Тесты производительности различных пакетов bcrypt для NodeJS

Итак, что мы делаем?
Перезагржаемся в LinuxMint, смотрим систему:

$ 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

Создаём папку, в ней nodejs-проект

$ mkdir bcrypt-bench
$ cd bcrypt-bench/
$ npm init
...
... - все параметры можно оставить по умолчанию,
они в принципе не играют большой роли здесь

Инсталлируем нужные пакеты с добавлением в packaje.json (флаг –save)

$ npm install bcrypt bcryptjs twin-bcrypt bcrypt-nodejs commander --save
 
> bcrypt@0.8.5 install /home/justy/nodejs/bcrypt-bench/node_modules/bcrypt
> node-gyp rebuild
 
make: Вход в каталог `/home/justy/nodejs/bcrypt-bench/node_modules/bcrypt/build'
  CXX(target) Release/obj.target/bcrypt_lib/src/blowfish.o
  CXX(target) Release/obj.target/bcrypt_lib/src/bcrypt.o
  CXX(target) Release/obj.target/bcrypt_lib/src/bcrypt_node.o
  SOLINK_MODULE(target) Release/obj.target/bcrypt_lib.node
  COPY Release/bcrypt_lib.node
make: Выход из каталога `/home/justy/nodejs/bcrypt-bench/node_modules/bcrypt/build'
bcrypt-bench@1.0.0 /home/justy/nodejs/bcrypt-bench
├─┬ bcrypt@0.8.5
│ ├── bindings@1.2.1
│ └── nan@2.0.5
├── bcrypt-nodejs@0.0.3
├── bcryptjs@2.3.0
├─┬ commander@2.9.0
│ └── graceful-readlink@1.0.1
└── twin-bcrypt@2.1.1
 
npm WARN EPACKAGEJSON bcrypt-bench@1.0.0 No repository field.

В результате наш package.json будет выглядеть примерно так:

{
  "name": "bcrypt-bench",
  "version": "1.0.0",
  "description": "Simple benchmarking for varios nodejs bcrypt packages",
  "main": "test.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "brcypt",
    "bench",
    "test"
  ],
  "author": "adrianblynch",
  "license": "MIT",
  "dependencies": {
    "bcrypt": "^0.8.5",
    "bcrypt-nodejs": "0.0.3",
    "bcryptjs": "^2.3.0",
    "commander": "^2.9.0",
    "twin-bcrypt": "^2.1.1"
  }
}

Теперь создаём скрипт test.js, скопировав его отсюда: https://github.com/adrianblynch/bcrypt-vs-bcrypt/blob/master/index.js и добавив в него пакет bcrypt-nodejs:

var bcrypt = require('bcrypt');
var bcryptJS = require('bcryptjs');
var twinBcrypt = require('twin-bcrypt');
var bcryptNodeJs = require('bcrypt-nodejs');
 
var commander = require('commander');
 
commander
.option('-r, --rounds [num]', 'Number of rounds to use', parseInt)
.option('-i, --iterations [num]', 'Number of iterations to use', parseInt)
.parse(process.argv);
 
var password = "password";
var rounds = commander.rounds || 12;
var iterations = commander.iterations || 10;
var hash, start, end, i, total;
 
console.log("Bcrypts ready? Fight!");
 
// bcrypt
 
total = 0;
 
for (i = 0; i < iterations; i++) {
	start = new Date();
	hash = bcrypt.hashSync(password, bcrypt.genSaltSync(rounds));
	end = new Date();
	total += (end - start);
}
 
console.log("bcrypt - " + iterations + " iterations took " + total + "ms which is an average of " + parseInt(total / iterations) + "ms each");
 
// bcryptjs
 
total = 0;
 
for (i = 0; i < iterations; i++) {
	start = new Date();
	hash = bcryptJS.hashSync(password, bcryptJS.genSaltSync(rounds));
	end = new Date();
	total += (end - start);
}
 
console.log("bcryptjs - " + iterations + " iterations took " + total + "ms which is an average of " + parseInt(total / iterations) + "ms each");
 
// twin-bcrypt
 
total = 0;
 
for (i = 0; i < iterations; i++) {
	start = new Date();
	hash = twinBcrypt.hashSync(password, twinBcrypt.genSalt(rounds));
	end = new Date();
	total += (end - start);
}
 
console.log("twin-bcrypt - " + iterations + " iterations took " + total + "ms which is an average of " + parseInt(total / iterations) + "ms each");
 
// bcrypt-nodejs
 
total = 0;
 
for (i = 0; i < iterations; i++) {
	start = new Date();
	hash = bcryptNodeJs.hashSync(password, bcryptNodeJs.genSaltSync(rounds));
	end = new Date();
	total += (end - start);
}
 
console.log("bcrypt-nodejs - " + iterations + " iterations took " + total + "ms which is an average of " + parseInt(total / iterations) + "ms each");

Всё готово для запуска тестов:

$ node test.js
Bcrypts ready? Fight!
bcrypt - 10 iterations took 2813ms which is an average of 281ms each
bcryptjs - 10 iterations took 8523ms which is an average of 852ms each
twin-bcrypt - 10 iterations took 8650ms which is an average of 865ms each
bcrypt-nodejs - 10 iterations took 8144ms which is an average of 814ms each

Как видим, стандартный bcrypt практически в 3 раза быстрее своих собратьев,
что не удивительно, так как он использует пре-компилированную версию, в то время как остальные написаны на чистом JavaScript и прогоняются ч/з интерпретатор V8, как бы он быстр не был.

Вторая мораль сей басни такова – если в нашем проекте требуется много и часто хэшировать с помощью алгоритма bcrypt’а, следует предпочесть bcrypt.

Сладкого чая и вкусной слойки на столе! С цукерками в вазочке.

Emmet: balance tag исправляем

Здравствуйте.
На страничке http://docs.emmet.io/actions/match-pair/ у Сергея написано следующее (перевод мой с английского):
********************************************************************************

Balance

“Balance Outward” (Ctrl+D)

“Balance Inward (Shift+Ctrl+D)

Известная балансировка тэгов: поиск границ тэгов и выбор их от текущей позиции каретки. Она может быть расширенной (outward balancing) или суженной (inward balancing) при вызове несколько раз подряд. Не каждый редактор поддерживает одновременно суженную и расширенную балансировку.

Балансировка тэгов Emmet достаточно уникальна. В отличие от остальных, она ищет границы тегов от текущей позиции каретки, а не от начала документа. Это означает, что она может работать не только в HTML документах.
********************************************************************************

Да, классная штука, правда, в реальной жизни нас скорее интересует текущий тег – просмотр границ и переход в начало конец этого тэга (для этого у Emmet‘а есть действие matching pair).

Но если она (балансировка) есть, хотелось бы использовать её на полную мощь. Так как я добавил поддержку Emmet для редактора AkelPad (http://akelpad.sourceforge.net/forum/viewtopic.php?p=30266#30266), мне захотелось подробнее разобраться в вопросе, а именно меня задели слова, что “Не каждый редактор поддерживает одновременно суженную и расширенную балансировку.“.

Экспериментируя с Emmet‘ом в AkelPad‘e, я заметил недоделанность эту (отсутствие балансировки вглубь, в частности), и решил посмотреть, что можно сделать.

Итак, нас интересует такой участок кода в файле emmet-app.js (я добавлял поддержку Emmet’а для AkelPad’а, используя плагин для Komodo Edit‘а, а потом проанализировал подобный файл от плагина к Notepad++):

/**
 * HTML pair matching (balancing) actions
 * @constructor
 * @memberOf __matchPairActionDefine
 * @param {Function} require
 * @param {Underscore} _
 */
emmet.exec(function(require, _) {
	/** @type emmet.actions */
	var actions = require('actions');
	var matcher = require('htmlMatcher');
	var lastMatch = null;
 
	/**
	 * Find and select HTML tag pair
	 * @param {IEmmetEditor} editor Editor instance
	 * @param {String} direction Direction of pair matching: 'in' or 'out'. 
	 * Default is 'out'
	 */
	function matchPair(editor, direction) {
		direction = String((direction || 'out').toLowerCase());
		var info = require('editorUtils').outputInfo(editor);
 
		var range = require('range');
		/** @type Range */
		var sel = range.create(editor.getSelectionRange());
		var content = info.content;
 
		// validate previous match
		if (lastMatch && !lastMatch.range.equal(sel)) {
			lastMatch = null;
		}
 
		if (lastMatch && sel.length()) {
			if (direction == 'in') {
				// user has previously selected tag and wants to move inward
				if (lastMatch.type == 'tag' && !lastMatch.close) {
					// unary tag was selected, can't move inward
					return false;
				} else {
					if (lastMatch.range.equal(lastMatch.outerRange)) {
						lastMatch.range = lastMatch.innerRange;
					} else {
						var narrowed = require('utils').narrowToNonSpace(content, lastMatch.innerRange);
						lastMatch = matcher.find(content, narrowed.start + 1);
						if (lastMatch && lastMatch.range.equal(sel) && lastMatch.outerRange.equal(sel)) {
							lastMatch.range = lastMatch.innerRange;
						}
					}
				}
			} else {
				if (
						!lastMatch.innerRange.equal(lastMatch.outerRange) 
						&& lastMatch.range.equal(lastMatch.innerRange) 
						&& sel.equal(lastMatch.range)) {
					lastMatch.range = lastMatch.outerRange;
				} else {
					lastMatch = matcher.find(content, sel.start);
					if (lastMatch && lastMatch.range.equal(sel) && lastMatch.innerRange.equal(sel)) {
						lastMatch.range = lastMatch.outerRange;
					}
				}
			}
		} else {
			lastMatch = matcher.find(content, sel.start);
		}
 
		if (lastMatch && !lastMatch.range.equal(sel)) {
			editor.createSelection(lastMatch.range.start, lastMatch.range.end);
			return true;
		}
 
		lastMatch = null;
		return false;
	}
 
	...
 
}

при беглом изучении этого кода, можно выделить такую фразу:

		// validate previous match
		if (lastMatch && !lastMatch.range.equal(sel)) {
			lastMatch = null;
		}

Вон оно что. Тут у нас статическая переменная lastMatch. Дело в том, что если JavaScript скрипт каждый раз подключается заново, статические переменные теряют смысл, так как они каждый раз заново инициализируются.

В браузерах после загрузки страницы все скрипты остаются в памяти и статические переменные работают как надо. В Notepad++ также подключение скрипта emmet-app.js происходит только один раз при первом вызове (используется Python-wrapper для JavaScript), поэтому там также всё работает более-менее нормально.

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

Штука такая, что на самом деле от статической составляющей lastMatch можно уйти. Предположив, что если у нас что-то выбрано, lastMatch уже отработал и нам надо искать дальше.

То есть мы в любом случае ищем как сначала, переставив местами части.

Вот что в итоге получается:

/**
 * HTML pair matching (balancing) actions
 * @constructor
 * @memberOf __matchPairActionDefine
 * @param {Function} require
 * @param {Underscore} _
 */
emmet.exec(function(require, _) {
	/** @type emmet.actions */
	var actions = require('actions');
	var matcher = require('htmlMatcher');
	var lastMatch = null;
 
	/**
	 * Find and select HTML tag pair
	 * @param {IEmmetEditor} editor Editor instance
	 * @param {String} direction Direction of pair matching: 'in' or 'out'. 
	 * Default is 'out'
	 */
	function matchPair(editor, direction) {
		direction = String((direction || 'out').toLowerCase());
		var info = require('editorUtils').outputInfo(editor);
 
		var range = require('range');
		/** @type Range */
		var sel = range.create(editor.getSelectionRange());
		var content = info.content;
 
		// we do not have static lastMatch here
		lastMatch = matcher.find(content, sel.start);
		// try for in balancing
		if(!lastMatch)
			lastMatch = matcher.find(content, sel.start + 1);
 
		if (lastMatch && sel.length()) {
			if (direction == 'in') {
				// user has previously selected tag and wants to move inward
				if (lastMatch.type == 'tag' && !lastMatch.close) {
					// unary tag was selected, can't move inward
					return false;
				} else {
					if (lastMatch.range.equal(lastMatch.outerRange)) {
						lastMatch.range = lastMatch.innerRange;
					} else {
						var narrowed = require('utils').narrowToNonSpace(content, lastMatch.innerRange);
						lastMatch = matcher.find(content, narrowed.start + 1);
						if (lastMatch && lastMatch.range.equal(sel) && lastMatch.outerRange.equal(sel)) {
							lastMatch.range = lastMatch.innerRange;
						}
					}
				}
			} else {
				if (
						!lastMatch.innerRange.equal(lastMatch.outerRange) 
						&& lastMatch.range.equal(lastMatch.innerRange) 
						&& sel.equal(lastMatch.range)) {
					lastMatch.range = lastMatch.outerRange;
				} else {
					lastMatch = matcher.find(content, sel.start);
					if (lastMatch && lastMatch.range.equal(sel) && lastMatch.innerRange.equal(sel)) {
						lastMatch.range = lastMatch.outerRange;
					}
				}
			}
		}
 
		if (lastMatch && !lastMatch.range.equal(sel)) {
			editor.createSelection(lastMatch.range.start, lastMatch.range.end);
			return true;
		}
 
		return false;
	}
 
	...
 
}

Для balancing inward я добавил такую фичу:

		// try for in balancing
		if(!lastMatch)
			lastMatch = matcher.find(content, sel.start + 1);

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

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

Но сам движок Сергея по поиску HTML/XML тэгов (matcher.find) нуждается в доработке, так он вне HTML может спотыкаться при использовании экранированных кавычек и ряда других случаях. Но этим, насколько я мог заметить, грешат практически все наверное подобные алгоритмы, так как найти matching tag в определённых случаях бывает довольно тяжело.

На сим всё, низкий поклон, пошёл завтракать яичницу с мясом (не беконом, а свининой, оставшейся после вчерашнего ужина).

Благодарю Вас за чтение и Господа за еду.

Читаем/пишем бинарники на JavaScript (JScript) для Win Wscript

Да, проблема в том, что JavaScript пытается преобразовать полученные байты в юникод (unicode), и портит большинство байт, которые больше 0x7F. Поэтому при чтении/записи бинарников получается не то, что мы хотим.

Пошарив какое-то время в инете, я нашёл табличку преобразований символов больше либо равным 0x80, но она в итоге оказалась не полной, и мои бинарники читались и писались с ошибками.

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

Идея такая – создать бинарный файл, состоящий из последовательности байт от 0 до 255, то есть размером 256 байт, а потом распрасить его JavaScript’ом и проанализировать полученные символы.

Пишем краткий сценрий на PHP, ибо PHP всегда под рукой, что в Виньде ,что в Линуксе, что на серверах.

<?php
/* create file of bytes from 0 to 255 */
	$s = '';
	for($i = 0; $i < 256; $i++)
		$s .= chr($i);
 
	file_put_contents('256.dat', $s);

В итоге, мы имеем файл 256.dat, который проанализируем JavaScript’ и составим табличку преобразований на лету.

var name = '256.dat', i; // путь к нашему файлу и переменная д/цикла
 
/* ******************************************
* Используем API AkelPad's Scripts плагина  *
******************************************* */
var bytes = AkelPad.ReadFile(name, 0);	// binary
log('bytes.length=' + bytes.length); // должно быть 256
 
var abytes = bytes.split('');
log('abytes.length=' + abytes.length); // должно быть 256
 
var ascii = '', fromAscii = '', c;
for(i = 0; i < abytes.length; i++) {
        // table asciiCodeAt
        if(i > 127)
	  ascii += 'case 0x' + bin2hex(abytes[i]) +
            ': return 0x' + i.toString(16) + '; break;\n'
	// REVERSE TABLE fromAscii
	c = bin2hex(abytes[i]);
	if(c.length == 3)
		c = '0' + c;
	else
	if(c.length == 2)
		c = '00' + c;
	// count only high values
	if(i > 127)
	  fromAscii += 'case 0x' + i.toString(16) + ': s += \'\\u' +
            c.toUpperCase() + '\'; break;\n'
}
 
log('// ASCII:');
log(ascii)
log('// fromAscii:');
log(fromAscii);
 
function bin2hex(s) {
  //  discuss at: http://phpjs.org/functions/bin2hex/
  // original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // bugfixed by: Onno Marsman
  // bugfixed by: Linuxworld
  // improved by: ntoniazzi (http://phpjs.org/functions/bin2hex:361#comment_177616)
  //   example 1: bin2hex('Kev');
  //   returns 1: '4b6576'
  //   example 2: bin2hex(String.fromCharCode(0x00));
  //   returns 2: '00'
 
  var i, l, o = '',
    n;
 
  s += '';
 
  for (i = 0, l = s.length; i < l; i++) {
    n = s.charCodeAt(i)
      .toString(16);
    o += n.length < 2 ? '0' + n : n;
  }
 
  return o;
}
 
// Логируем текст с помощью AkelPad'овского Log плагина
function log(text)
{
  AkelPad.Call("Log::Output", 4, text + '\n');
}

Потом копируем полученный вывод в отдельный файл, добавляем необохдимую разметку и получаем в итоге полные таблицы преобразований to ASCII и from Ascii Code для JavaScript:

/*
	Needed for reading from binary files
 	See actionUtils.getImageSize method in emmet-app.js
*/
String.prototype.asciiCodeAt = function(i) {
    // charCodeAt returns some bytes translated to unicode characters. 
    // this function means to counteract that.
    switch(this.charCodeAt(i)) {
        case 0x402: return 0x80; break;
				case 0x403: return 0x81; break;
				case 0x201a: return 0x82; break;
				case 0x453: return 0x83; break;
				case 0x201e: return 0x84; break;
				case 0x2026: return 0x85; break;
				case 0x2020: return 0x86; break;
				case 0x2021: return 0x87; break;
				case 0x20ac: return 0x88; break;
				case 0x2030: return 0x89; break;
				case 0x409: return 0x8a; break;
				case 0x2039: return 0x8b; break;
				case 0x40a: return 0x8c; break;
				case 0x40c: return 0x8d; break;
				case 0x40b: return 0x8e; break;
				case 0x40f: return 0x8f; break;
				case 0x452: return 0x90; break;
				case 0x2018: return 0x91; break;
				case 0x2019: return 0x92; break;
				case 0x201c: return 0x93; break;
				case 0x201d: return 0x94; break;
				case 0x2022: return 0x95; break;
				case 0x2013: return 0x96; break;
				case 0x2014: return 0x97; break;
				case 0x98: return 0x98; break;
				case 0x2122: return 0x99; break;
				case 0x459: return 0x9a; break;
				case 0x203a: return 0x9b; break;
				case 0x45a: return 0x9c; break;
				case 0x45c: return 0x9d; break;
				case 0x45b: return 0x9e; break;
				case 0x45f: return 0x9f; break;
				case 0xa0: return 0xa0; break;
				case 0x40e: return 0xa1; break;
				case 0x45e: return 0xa2; break;
				case 0x408: return 0xa3; break;
				case 0xa4: return 0xa4; break;
				case 0x490: return 0xa5; break;
				case 0xa6: return 0xa6; break;
				case 0xa7: return 0xa7; break;
				case 0x401: return 0xa8; break;
				case 0xa9: return 0xa9; break;
				case 0x404: return 0xaa; break;
				case 0xab: return 0xab; break;
				case 0xac: return 0xac; break;
				case 0xad: return 0xad; break;
				case 0xae: return 0xae; break;
				case 0x407: return 0xaf; break;
				case 0xb0: return 0xb0; break;
				case 0xb1: return 0xb1; break;
				case 0x406: return 0xb2; break;
				case 0x456: return 0xb3; break;
				case 0x491: return 0xb4; break;
				case 0xb5: return 0xb5; break;
				case 0xb6: return 0xb6; break;
				case 0xb7: return 0xb7; break;
				case 0x451: return 0xb8; break;
				case 0x2116: return 0xb9; break;
				case 0x454: return 0xba; break;
				case 0xbb: return 0xbb; break;
				case 0x458: return 0xbc; break;
				case 0x405: return 0xbd; break;
				case 0x455: return 0xbe; break;
				case 0x457: return 0xbf; break;
				case 0x410: return 0xc0; break;
				case 0x411: return 0xc1; break;
				case 0x412: return 0xc2; break;
				case 0x413: return 0xc3; break;
				case 0x414: return 0xc4; break;
				case 0x415: return 0xc5; break;
				case 0x416: return 0xc6; break;
				case 0x417: return 0xc7; break;
				case 0x418: return 0xc8; break;
				case 0x419: return 0xc9; break;
				case 0x41a: return 0xca; break;
				case 0x41b: return 0xcb; break;
				case 0x41c: return 0xcc; break;
				case 0x41d: return 0xcd; break;
				case 0x41e: return 0xce; break;
				case 0x41f: return 0xcf; break;
				case 0x420: return 0xd0; break;
				case 0x421: return 0xd1; break;
				case 0x422: return 0xd2; break;
				case 0x423: return 0xd3; break;
				case 0x424: return 0xd4; break;
				case 0x425: return 0xd5; break;
				case 0x426: return 0xd6; break;
				case 0x427: return 0xd7; break;
				case 0x428: return 0xd8; break;
				case 0x429: return 0xd9; break;
				case 0x42a: return 0xda; break;
				case 0x42b: return 0xdb; break;
				case 0x42c: return 0xdc; break;
				case 0x42d: return 0xdd; break;
				case 0x42e: return 0xde; break;
				case 0x42f: return 0xdf; break;
				case 0x430: return 0xe0; break;
				case 0x431: return 0xe1; break;
				case 0x432: return 0xe2; break;
				case 0x433: return 0xe3; break;
				case 0x434: return 0xe4; break;
				case 0x435: return 0xe5; break;
				case 0x436: return 0xe6; break;
				case 0x437: return 0xe7; break;
				case 0x438: return 0xe8; break;
				case 0x439: return 0xe9; break;
				case 0x43a: return 0xea; break;
				case 0x43b: return 0xeb; break;
				case 0x43c: return 0xec; break;
				case 0x43d: return 0xed; break;
				case 0x43e: return 0xee; break;
				case 0x43f: return 0xef; break;
				case 0x440: return 0xf0; break;
				case 0x441: return 0xf1; break;
				case 0x442: return 0xf2; break;
				case 0x443: return 0xf3; break;
				case 0x444: return 0xf4; break;
				case 0x445: return 0xf5; break;
				case 0x446: return 0xf6; break;
				case 0x447: return 0xf7; break;
				case 0x448: return 0xf8; break;
				case 0x449: return 0xf9; break;
				case 0x44a: return 0xfa; break;
				case 0x44b: return 0xfb; break;
				case 0x44c: return 0xfc; break;
				case 0x44d: return 0xfd; break;
				case 0x44e: return 0xfe; break;
				case 0x44f: return 0xff; break;
 
        default: return this.charCodeAt(i);
    }
}
 
/*
	Needed for writing to binary files.
 	See base64.decode method in emmet-app.js
*/
String.fromAsciiCode = function() {
    // reverse for asciiCodeAt() method
    var s = '', i, c = arguments.length;
 
    for(i = 0; i < c; i++)
    	switch(arguments[i]) {
				case 0x80: s += '\u0402'; break;
				case 0x81: s += '\u0403'; break;
				case 0x82: s += '\u201A'; break;
				case 0x83: s += '\u0453'; break;
				case 0x84: s += '\u201E'; break;
				case 0x85: s += '\u2026'; break;
				case 0x86: s += '\u2020'; break;
				case 0x87: s += '\u2021'; break;
				case 0x88: s += '\u20AC'; break;
				case 0x89: s += '\u2030'; break;
				case 0x8a: s += '\u0409'; break;
				case 0x8b: s += '\u2039'; break;
				case 0x8c: s += '\u040A'; break;
				case 0x8d: s += '\u040C'; break;
				case 0x8e: s += '\u040B'; break;
				case 0x8f: s += '\u040F'; break;
				case 0x90: s += '\u0452'; break;
				case 0x91: s += '\u2018'; break;
				case 0x92: s += '\u2019'; break;
				case 0x93: s += '\u201C'; break;
				case 0x94: s += '\u201D'; break;
				case 0x95: s += '\u2022'; break;
				case 0x96: s += '\u2013'; break;
				case 0x97: s += '\u2014'; break;
				//case 0x98: s += '\u0098'; break;
				case 0x99: s += '\u2122'; break;
				case 0x9a: s += '\u0459'; break;
				case 0x9b: s += '\u203A'; break;
				case 0x9c: s += '\u045A'; break;
				case 0x9d: s += '\u045C'; break;
				case 0x9e: s += '\u045B'; break;
				case 0x9f: s += '\u045F'; break;
				//case 0xa0: s += '\u00A0'; break;
				case 0xa1: s += '\u040E'; break;
				case 0xa2: s += '\u045E'; break;
				case 0xa3: s += '\u0408'; break;
				//case 0xa4: s += '\u00A4'; break;
				case 0xa5: s += '\u0490'; break;
				//case 0xa6: s += '\u00A6'; break;
				//case 0xa7: s += '\u00A7'; break;
				case 0xa8: s += '\u0401'; break;
				//case 0xa9: s += '\u00A9'; break;
				case 0xaa: s += '\u0404'; break;
				//case 0xab: s += '\u00AB'; break;
				//case 0xac: s += '\u00AC'; break;
				//case 0xad: s += '\u00AD'; break;
				//case 0xae: s += '\u00AE'; break;
				case 0xaf: s += '\u0407'; break;
				//case 0xb0: s += '\u00B0'; break;
				//case 0xb1: s += '\u00B1'; break;
				case 0xb2: s += '\u0406'; break;
				case 0xb3: s += '\u0456'; break;
				case 0xb4: s += '\u0491'; break;
				//case 0xb5: s += '\u00B5'; break;
				//case 0xb6: s += '\u00B6'; break;
				//case 0xb7: s += '\u00B7'; break;
				case 0xb8: s += '\u0451'; break;
				case 0xb9: s += '\u2116'; break;
				case 0xba: s += '\u0454'; break;
				//case 0xbb: s += '\u00BB'; break;
				case 0xbc: s += '\u0458'; break;
				case 0xbd: s += '\u0405'; break;
				case 0xbe: s += '\u0455'; break;
				case 0xbf: s += '\u0457'; break;
				case 0xc0: s += '\u0410'; break;
				case 0xc1: s += '\u0411'; break;
				case 0xc2: s += '\u0412'; break;
				case 0xc3: s += '\u0413'; break;
				case 0xc4: s += '\u0414'; break;
				case 0xc5: s += '\u0415'; break;
				case 0xc6: s += '\u0416'; break;
				case 0xc7: s += '\u0417'; break;
				case 0xc8: s += '\u0418'; break;
				case 0xc9: s += '\u0419'; break;
				case 0xca: s += '\u041A'; break;
				case 0xcb: s += '\u041B'; break;
				case 0xcc: s += '\u041C'; break;
				case 0xcd: s += '\u041D'; break;
				case 0xce: s += '\u041E'; break;
				case 0xcf: s += '\u041F'; break;
				case 0xd0: s += '\u0420'; break;
				case 0xd1: s += '\u0421'; break;
				case 0xd2: s += '\u0422'; break;
				case 0xd3: s += '\u0423'; break;
				case 0xd4: s += '\u0424'; break;
				case 0xd5: s += '\u0425'; break;
				case 0xd6: s += '\u0426'; break;
				case 0xd7: s += '\u0427'; break;
				case 0xd8: s += '\u0428'; break;
				case 0xd9: s += '\u0429'; break;
				case 0xda: s += '\u042A'; break;
				case 0xdb: s += '\u042B'; break;
				case 0xdc: s += '\u042C'; break;
				case 0xdd: s += '\u042D'; break;
				case 0xde: s += '\u042E'; break;
				case 0xdf: s += '\u042F'; break;
				case 0xe0: s += '\u0430'; break;
				case 0xe1: s += '\u0431'; break;
				case 0xe2: s += '\u0432'; break;
				case 0xe3: s += '\u0433'; break;
				case 0xe4: s += '\u0434'; break;
				case 0xe5: s += '\u0435'; break;
				case 0xe6: s += '\u0436'; break;
				case 0xe7: s += '\u0437'; break;
				case 0xe8: s += '\u0438'; break;
				case 0xe9: s += '\u0439'; break;
				case 0xea: s += '\u043A'; break;
				case 0xeb: s += '\u043B'; break;
				case 0xec: s += '\u043C'; break;
				case 0xed: s += '\u043D'; break;
				case 0xee: s += '\u043E'; break;
				case 0xef: s += '\u043F'; break;
				case 0xf0: s += '\u0440'; break;
				case 0xf1: s += '\u0441'; break;
				case 0xf2: s += '\u0442'; break;
				case 0xf3: s += '\u0443'; break;
				case 0xf4: s += '\u0444'; break;
				case 0xf5: s += '\u0445'; break;
				case 0xf6: s += '\u0446'; break;
				case 0xf7: s += '\u0447'; break;
				case 0xf8: s += '\u0448'; break;
				case 0xf9: s += '\u0449'; break;
				case 0xfa: s += '\u044A'; break;
				case 0xfb: s += '\u044B'; break;
				case 0xfc: s += '\u044C'; break;
				case 0xfd: s += '\u044D'; break;
				case 0xfe: s += '\u044E'; break;
				case 0xff: s += '\u044F'; break;
 
        default: s += String.fromCharCode(arguments[i]);
    }
    return s;
}

Первую табличку мы используем при чтении из бинарного файла средствами JavaScript, вторую – при записи в бинарник.

Например, используем эти таблички для base64 кодирования/декодирования бинарных данных (методы из emmet-app.js)

/**
 * @author Sergey Chikuyonok (serge.che@gmail.com)
 * @link http://chikuyonok.ru
 */
emmet.define('base64', function(require, _) {
	var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
 
	return {
		/**
		 * Encodes data using base64 algorithm
		 * @author Tyler Akins (http://rumkin.com)
		 * @param {String} input
		 * @returns {String}
		 * @memberOf emmet.base64
		 */
		encode : function(input) {
			var output = [];
			var chr1, chr2, chr3, enc1, enc2, enc3, enc4, cdp1, cdp2, cdp3;
			var i = 0, il = input.length, b64 = chars;
 
			while (i < il) {
 
				// call .asciiCodeAt() instead of .charCodeAt()
				// see AkelEmmet.js in Scripts folder for details
				cdp1 = input.asciiCodeAt(i++);
				cdp2 = input.asciiCodeAt(i++);
				cdp3 = input.asciiCodeAt(i++);
 
				chr1 = cdp1 & 0xff;
				chr2 = cdp2 & 0xff;
				chr3 = cdp3 & 0xff;
 
				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;
 
				if (isNaN(cdp2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(cdp3)) {
					enc4 = 64;
				}
 
				output.push(b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4));
			}
 
			return output.join('');
		},
 
		/**
		 * Decodes string using MIME base64 algorithm
		 * 
		 * @author Tyler Akins (http://rumkin.com)
		 * @param {String} data
		 * @return {String}
		 */
		decode : function(data) {
			var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, tmpArr = [];
			var b64 = chars, il = data.length;
 
			if (!data) {
				return data;
			}
 
			data += '';
 
			do { // unpack four hexets into three octets using index points in b64
				h1 = b64.indexOf(data.charAt(i++));
				h2 = b64.indexOf(data.charAt(i++));
				h3 = b64.indexOf(data.charAt(i++));
				h4 = b64.indexOf(data.charAt(i++));
 
				bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
 
				o1 = bits >> 16 & 0xff;
				o2 = bits >> 8 & 0xff;
				o3 = bits & 0xff;
 
				if (h3 == 64) {
					tmpArr[ac++] = String.fromAsciiCode(o1); // String.fromCharCode(o1);
				} else if (h4 == 64) {
					tmpArr[ac++] = String.fromAsciiCode(o1, o2); //String.fromCharCode(o1, o2);
				} else {
					tmpArr[ac++] = String.fromAsciiCode(o1, o2, o3); //String.fromCharCode(o1, o2, o3);
				}
			} while (i < il);
 
			return tmpArr.join('');
		}
	};
});

Крепкого Вам чая в бокале, сладкого лимона и главное конечно, я знаю, что банален, но желаю хорошего настроения. От чего оно зависит?

Используем Hurricane Electric Free DNS Management

Введение

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

При создании своего сервера не всегда нужно и можно поднимать свой собственный DNS-сервер, во первых, чтобы сохранить ресурсы, во-вторых, за отсутствием лишнего ip, ведь для регистрации DNS-сервера требуется указать как минимум два ip – основной и резервный.

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

Одним из таких сервисов является Hurricane Electric Free DNS Management – https://dns.he.net/. Здесь мы попытаемся описать, какие основные настройки для своего домена требуется задать, и как это сделать. Сейчас (на июнь 2015) Hurricane Electric позволяет добавлять до 50 доменов бесплатно, включая обратно-проксированные: At this time, we are limiting the free service to 50 zones which includes your reverse zones (if any).

Начнём!

Итак, заходим на https://dns.he.net, регистрируемся и добавляем новый домен: mydomain.ru
Система может задуматься на какое-то время, а потом выдать надпись типа того, что домен не делегирован им. Правильно, для того, чтобы наш домен работал через их DNS, надо у соответствующего регистратора нашего домена прописать новые NS-зоны: ns1.he.net, ns2.he.net, ns3.he.net, ns4.he.net и ns5.he.net
Это можно сделать сразу или потом, когда мы добавим все нужные DNS-записи.

ОК, теперь заходим в редактор нового домена:
HE DNS_2
и видим, что у нас уже добавлены записи SOA и NS.
HE DNS_3

В первую очередь добавляем две A-записи: mydomain.ru и www.mydomain.ru, которые должны указывать на ip-адрес сервера, где хостится домен. Жмём New-A в меню вверху, в качестве Name пишем адрес домена mydomain.ru, а в качестве IPv4 Address – ip адрес сервера в формате IPv4. Остальное можно оставить по умолчанию. Если вы захотите добавить поддержку домена для IPv6, добавьте AAAA-записи для него.

Теперь добавляем MX-запись для почты: New MX в верхнем меню. Name: mydomain.ru, Priority: 10, Hostname: mail.mydomain.ru

Добавим CNAME-запись: Name: mail.mydomain.ru, Hostname: mydomain.ru
Это позволяет при доступе к mail.mydomain.ru обратиться к mydomain.ru

Внимание! Я обнаружил, что почта на некоторые домены (например, gmx.de или mail.com),с нашего сервера не проходит, и мы получаем bounceback-письмо примерно такого содержания:

<akstandilliya@mail.com>: host mx01.mail.com[74.208.5.22] said: 550-Requested
action not taken: mailbox unavailable 550 invalid DNS MX or A/AAAA resource
record (in reply to MAIL FROM command)

Оказывается, не позволяется делать CNAME-записи для своих MX адресов, как мы это сделали сперва (см. выше перечёкнутый текст), согласно RFC2181: https://tools.ietf.org/html/rfc2181#section-10.3

Решение проблемы – удаление CNAME-записи для своего MX адреса (mail.mydomain.ru) – при условии, что мы её раньше добавили, и добавление новой A записи с адресом mail.mydomain.ru, указывающей на IP-адрес нашего сервера.

Добавим CNAME запись для www.mydomain.ru – mydomain.ru

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

Жмём New TXT. Name: mydomain.ru Text data: v=spf1 ip4:127.0.0.1 +a +mx +ip4:127.0.0.1 ?all
Здесь 127.0.0.1 надо поменять на ip-адрес вашего сервера с доменом, который мы указывали при добавлении A-записи.

В итоге наши DNS-записи для домена в общем случае могут выглядеть так:
HE DNS_4

Вот и всё, общий случай добавления DNS-зоны для домена. Теперь, как и писалось выше, надо направить NS-записи регистратора домена на ns1.he.net, ns2.he.net, ns3.he.net, ns4.he.net и ns5.he.net. Теоретически можно прописать любые два адреса, но лучше больше!

Удачи Вам и крепкого чая на столе!

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, всё подчищу.

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