Сообщество - Лига программистов

Лига программистов

2 246 постов 11 951 подписчик

Популярные теги в сообществе:

7

Обмен паролями и ссылками между устройствами. Закрыл в Copy Sync дыру, про которую честно предупреждал в прошлом посте

Передача буфера обмена между устройствами через защищённый канал

Передача буфера обмена между устройствами через защищённый канал

Короткое напоминание

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

Многие тогда справедливо спросили в комментах: "а ключи-то от шифрования откуда устройства берут?" Вопрос в точку. И в той статье я сам честно написал, что тут осталась слабое место. Вот её и чиню.

В чём была дыра

Объясню простым языком:

Чтобы зашифровать сообщение для вашего второго устройства, первому нужен "публичный ключ" второго, что-то вроде открытого почтового ящика, куда можно бросить письмо, но достать его может только владелец. Вопрос: откуда первое устройство этот ящик берёт? Спрашивает у сервера. У моего сервера.

И вот тут собака зарыта. А что если сервер (или тот, кто его взломал) вместо настоящего ящика вашего устройства подсунет свой? Тогда:

  • ваше первое устройство шифрует письмо в подставной ящик;

  • сервер его спокойно открывает, читает, потом перепаковывает в настоящий ящик и отправляет дальше;

  • второе устройство получает письмо как ни в чём не бывало.

Оба ваших устройства довольны, а посередине кто-то всё прочитал. Это называется "человек посередине" (man-in-the-middle). И самое обидное шифрование тут ни при чём, оно идеальное. Просто зашифровали не туда. А "не туда" подсунул именно тот, кому мы вроде как не должны были доверять, мой сервер.

Неприятно, что мой же сервис, про который я писал "нечем вас прочитать", в этом сценарии всё-таки мог. Так не пойдёт.


Как защититься

Способ старый и проверенный, такой же используют в Signal и WhatsApp, когда показывают "код безопасности". Идея простая:

У каждого ящика-ключа есть короткий отпечаток - набор букв и цифр, который из этого ключа однозначно вычисляется. Как отпечаток пальца: у разных ключей он разный, у одного и того же - всегда одинаковый.

Вы открываете Copy Sync на двух своих устройствах и сравниваете отпечатки глазами. Совпали - значит, между устройствами реально один и тот же ключ, никто посередине не встрял. А если сервер подсунул свой ключ - отпечаток сразу разойдётся, и вы это увидите. Подделать его нельзя: отпечаток намертво привязан к ключу.

Пять секунд работы - и MITM из "незаметно читает всё" превращается в "попадётся на первой же сверке".


А теперь - самое главное, ради чего и пишу

Когда вы сверили отпечатки и нажали "Сверено", эту галочку надо где-то запомнить. И вот тут у меня был соблазн сделать "как удобно": сохранить её на сервере, чтобы синхронизировалась между устройствами.

Я этого не сделал. Сознательно.

Смотрите. Мы защищаемся от кого? От сервера. А теперь представьте, что я бы доверил тому же серверу хранить отметку "этому ключу можно верить". Взломщик просто поставил бы галочку "проверено" своему поддельному ключу и вся защита превратилась бы в спектакль. Нельзя ставить сторожить дверь того, от кого ты эту дверь запираешь.

Поэтому галочка "сверено" живёт только в вашем браузере и на сервер не уходит вообще никогда. У этого есть приятный побочный эффект: галочка привязана к самому ключу. Если сервер завтра попробует подменить ключ, отметка "проверено" слетит сама собой, и устройство снова покажет "не сверено". Вы заметите, что что-то поменялось. Подмена ключа автоматически сбрасывает доверие - это не баг, это и есть защита.

Цена честная: сверять придётся на каждом устройстве отдельно. Но это в сто раз лучше, чем доверять "проверку" тому, кого проверяешь.


Бонусом - подключение второго устройства по QR

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

И тут важная деталь в том же духе: по QR передаётся только разовый "пропуск" (живёт 5 минут, срабатывает один раз). Ключи шифрования по нему не передаются - второе устройство создаёт свои собственные, прямо у себя, и они его никогда не покидают. Так что даже перехваченный QR не даст доступа к вашим данным.


Честно о том, чего ещё нет

Как и в прошлый раз - про минусы сам, без прикрас:

  1. Сверка ручная. Она работает, только если вы реально сравнили отпечатки. Не сравнили - защита не сработает. Я не заставляю это делать насильно, но честно: это инструмент, а не автоматический щит.

  2. Сервис пока не предупреждает сам, если ключ вдруг сменился - он лишь снимает галочку "проверено". Автоматическое уведомление "эй, ключ поменялся!" следующая задача.

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


Посмотреть

  • Сайт: copysync.ru (заведите два устройства, подключите второе по QR и сверьте отпечатки — увидите всё своими глазами)

  • Исходники: gitlab.com/razgiva/copy-sync

    Буду рад вопросам и критике - особенно от тех, кто шарит в безопасности. Нашли, как обойти сверку, - для меня это ценнее лайка.

А вопрос к вам такой: вы вообще когда-нибудь сверяли "код безопасности" в Signal или WhatsApp? Или это та штука, которую все видели, но никто ни разу не нажимал?

Показать полностью
1

Расширение с поиском для Chrome

Привет, хочу реализовать удобный и быстрый поиск при выделении текста наподобие того как это реализовано в Яндекс браузере.
По задумке выглядеть это должно как три кнопки [ Search Copy 🔍]

Расширение с поиском для Chrome

1. Search - поиск в выбранной поисковой системе / ЛКМ поиск / ПКМ выбор поисковой системы;

2. Copy - просто копирует выделенный текст;

3. 🔍 - поиск, который настраивается под любой сайт например YouTube, Википедия и так далее. ПКМ открывает список уже настроенных сайтов и дополнительную кнопку [ + ] для добавления поиска нового сайта. ПКМ по уже добавленному сайту позволяло бы его удалить или отредактировать ссылку.

К сожалению я не шарю совсем за программирование, а с ChatGPT мы добились сомнительного результата - выложил на Гугл диск, то что получилось. Буду рад, если кто-то откликнется и поможет реализовать задумку.

Показать полностью 1
204

Почему мы до сих пор неправильно пишем физические движки и 3D-графику

Стоит открыть исходники любого современного игрового движка – неважно, это C++-рендер, сделанный на коленке, или какая-нибудь гигантская экосистема вроде Unity или Unreal Engine – вы первым делом натыкаетесь на одни и те же знакомые сущности. Все вокруг живет в Vector3: координаты, направления движения, точки столкновений. Каждая частица указывает, куда она смотрит, с помощью Quaternion. А если требуется что-то покруче – переносить и одновременно крутить объект, то Matrix4x4. Это уже как стандарт де-факто: кто пробовал крутить объекты руками, тот точно переписывал код с этими структурами. Ещё конечно же отдельно существуют лучи, плоскости, сферы, bounding boxes, а между ними тянутся километры функций вроде dot(), cross(), normalize(), lookAt(), inverse(), project() и бесконечных преобразований типов.

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

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


И тут интересно вспомнить XIX век. Тогда математики как раз ломали головы над тем, чтобы придумать нормальный универсальный язык для описания пространства – не мелочиться кучей частных формул на каждый случай жизни. William Rowan Hamilton придумывает кватернионы: компактный инструмент для вращений в пространстве, который становится сегодня основой всей компьютерной анимации (даже те же Unity и Unreal ими внутри манипулируют).

<a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%B2%D0%B0%D1%82%D0%B5%D1%80%D0%BD%D0%B8%D0%BE%D0%BD" target="_blank" rel="nofollow noopener">Источник</a>

Источник

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

<a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%B2%D0%B0%D1%82%D0%B5%D1%80%D0%BD%D0%B8%D0%BE%D0%BD" target="_blank" rel="nofollow noopener">Источник</a>

Источник

Параллельно Hermann Grassmann выносит мозг коллегам идеей: почему бы алгебре не оперировать сразу плоскостями и объемами целиком? Ну действительно: мы привыкли складывать числа и векторы, а как быть с более сложными сущностями? Дальше подключается William Kingdon Clifford, собирает всё это в одну систему – получает Геометрическую Алгебру.

По сути, Клиффорд пытался создать универсальную операционную систему для геометрии.Но индустрия пошла другим путем.

Когда физикам и инженерам понадобилась удобная математика для электромагнетизма и механики, Josiah Willard Gibbs и Oliver Heaviside взяли идеи Клиффорда и разрезали их на части. Из цельной алгебры были извлечены только самые прикладные куски – скалярное и векторное произведения. Так появился привычный нам vector calculus, который сегодня преподают во всех технических вузах и используют почти все графические API.

Фактически современная графика работает не на полной геометрической теории, а на ее урезанной инженерной версии. Проблема этой урезанности особенно хорошо видна на векторном произведении. Все знают формулу:

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

Клиффорд предложил гораздо более общую идею. Вместо отдельного скалярного и отдельного векторного произведения он ввел единую операцию:

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

Первая часть привычное скалярное произведение. Число. Ничего нового. А вот – это внешнее произведение Грассмана. И его результатом является не вектор, а новый геометрический объект – бивектор.

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

Бивектор хранит площадь и ориентацию обхода. По сути, это элемент поверхности. Вот тут у многих мир переворачивается: оказывается наше привычное векторное произведение («дай-ка найду нормаль к плоскости») – это такой засекреченный хак ради удобства старой учебной математики! Мы подсознательно заменяем настоящую плоскость перпендикулярным ей вектором просто потому, что так проще считать по старинке. Но Геометрическая Алгебра говорит: зачем вообще выбрасывать информацию о самой плоскости?

<a href="https://habr.com/ru/articles/542030/" target="_blank" rel="nofollow noopener">Источник</a>

Источник

В итоге из этой идеи рождается целая лестница геометрических объектов: есть значение (число), есть направление (вектор), есть площадь (бивектор), есть объем (тривектор) – все это элементы единой структуры под капотом! У людей технических такое вызывает лёгкое недоверие («подожди… какой еще вектор-площадь?») – словно кто-то предлагает напрямую сложить яблоки с квадратными метрами. Но идея-то именно в этом: собрать всю геометрию пространства под одной крышей, чтобы перестать тащить за собой гору несовместимых матрешек.

А затем появляется самая странная и одновременно самая мощная концепция – мультивектор. А дальше начинается магия серьёзной математики. Стоит расширить наше 3D-пространство до пятимерного (!) за счет двух спец-направлений – одно станет отвечать за начало координат вселенной, другое символизирует бесконечность во всех смыслах слова. Получаем Conformal Geometric Algebra (CGA): звучит максимально экзотично и сначала похоже на сугубо теоретические упражнения… Но вот что удивительно: CGA позволяет описывать сферы, окружности и прочие объекты как такие же элементы своей алгебры так же естественно, как вы оперируете обычными точками или прямыми.

Выглядит всё это так будто из учебника магии для программиста. На практике же происходит нечто почти магическое: все геометрические объекты начинают описываться одинаково.Точка становится мультивектором. Сфера становится мультивектором. Плоскость тоже становится мультивектором. Причем в CGA плоскость фактически является сферой бесконечного радиуса. Это уже не отдельный тип сущности, а частный случай более общего объекта.

Для программиста это звучит почти еретически. Представьте движок, в котором Plane, Sphere, Ray и Lineперестают быть независимыми структурами и становятся вариациями одной и той же геометрической сущности. Но самое важное начинается дальше.

Сегодня практически любой физический движок содержит десятки специализированных функций:

IntersectRaySphere()
IntersectRayPlane()
IntersectSphereSphere()
IntersectCapsuleTriangle()

Каждая написана отдельно. У каждой свои edge cases. В каждой свои проверки на epsilon, вырожденные случаи и численные артефакты.

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

Современная графика использует целый зоопарк представлений. Повороты кодируются кватернионами. Переносы – матрицами. Масштабирование – другими матрицами. Для анимации часто добавляются dual quaternions. Внутри движка постоянно происходит конвертация между разными представлениями одной и той же геометрии.

В CGA все это заменяется единым объектом – ротором. Любое преобразование записывается одинаково:

И неважно, что именно делает RR. Если он кодирует вращение – объект повернется. Если перенос – объект сдвинется. Если масштаб – масштабируется. Формула остается одной и той же.

Для человека, который годами писал графический код, это выглядит почти незаконно. Особенно впечатляет то, как меняется сам стиль программирования. Например, в библиотеке clifford код начинает напоминать скорее школьную геометрию, чем традиционный graphics programming:

from clifford.g3c import *

# Создаем две точки в конформном пространстве
point1 = up(eo + 1*e1)
point2 = up(eo + 5*e1)

# Линия - это просто внешнее произведение двух точек и бесконечности
line = point1 ^ point2 ^ einf

# Сфера с центром в point1 и радиусом r
r = 2.0
sphere = point1 - 0.5 * (r**2) * einf

# Ищем пересечение линии и сферы. ОДНА СТРОЧКА!
intersection = line.meet(sphere)

В этом фрагменте почти шокирует отсутствие привычных вещей. Нет матриц. Нет ручной тригонометрии. Нет вызовов cos() и sin(). Нет километров условий вроде if(dot < 0). Код начинает выражать не алгоритм вычисления, а саму геометрическую идею. Именно поэтому многие люди, впервые столкнувшиеся с GA, испытывают странное чувство. Возникает ощущение, будто вся современная 3D-графика десятилетиями решала геометрические задачи окольным путем.

Но тогда возникает очевидный вопрос: если Геометрическая Алгебра настолько красива, почему индустрия до сих пор массово не перешла на нее?

Потому что у этой красоты есть цена. Главная проблема – производительность. В конформной алгебре мультивектор в 5D содержит  коэффициента. Для сравнения: обычный Vector3 хранит всего три числа. То есть даже простая точка внезапно становится огромной структурой данных. Для CPU-кэша и особенно для GPU это настоящая катастрофа.

Современные видеокарты десятилетиями оптимизировались под операции над четырехкомпонентными векторами и матрицами 4×4. Под них существуют SIMD-инструкции, специализированные блоки вычислений, драйверы и шейдерные пайплайны. Вся индустрия буквально выращена вокруг матричной математики. GA пока остается чужаком на этом празднике жизни.

Есть и другая проблема – психологическая. Геометрическая Алгебра требует полностью перестроить мышление. Разработчик должен отказаться от привычной модели вектор + матрица + кватернион и начать воспринимать геометрию как единую алгебраическую систему. Это не просто новая библиотека. Это почти смена языка мышления.


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

Самое интересное, что индустрия может прийти к Геометрической Алгебре не через университеты, а через компиляторы. Уже появляются генераторы кода, которые умеют анализировать мультивекторы на этапе сборки, выкидывать нулевые коэффициенты и превращать красивые абстрактные формулы в очень эффективные SIMD-инструкции. То есть разработчик пишет чистую геометрию, а компилятор превращает ее в быстрый машинный код.

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

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

Всем спасибо за внимание!


Автор текста: asomegoodboy

Написано при поддержке Timeweb Cloud

Больше интересных статей и новостей в нашем блоге на Хабре и телеграм-канале.


📚 Вам может быть интересно:

Реклама. ООО «ТАЙМВЭБ.КЛАУД», ИНН: 7810945525

Показать полностью 7
10

Стоит ли читать «Грокаем алгоритмы»?

Серия Айтишник читает
Стоит ли читать «Грокаем алгоритмы»?

Прочитал книгу Адитьи Бхаргавы «Грокаем алгоритмы» и попробовал понять, кому она действительно нужна.

Если вы практикующий разработчик и давно знакомы с базовыми структурами данных и алгоритмами, ничего принципиально нового книга, скорее всего, не даст. В ней разобрано около десятка классических алгоритмов, причём очень подробно: с картинками, схемами и простыми примерами. Всё очень просто и понятно.

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

Зачем её читал я?

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

Плюсы и минусы такой подачи

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

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

Вердикт:

«Грокаем алгоритмы» — хорошая книга для начального уровня. Она понятно и наглядно объясняет базовые алгоритмы, но глубины от неё ждать не стоит.

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

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

Моя оценка 7 из 10 мышат. Хорошая книга для того чтобы познакомиться с базой.

Показать полностью 1

Я написал бота для игры в Пятерочке ради 5 рублей

Полное видео

Если нравится подобный контент, поддержите канал подпиской

Показать полностью 1
8

Retro68: если вам скучно жить на свете

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

Видимо по этой причине, одним мрачным осенним днем мне захотелось создать графическое приложение на современном C++ под.. классический Mac из 90х.

В действии.

История

Честно не знаю что вам сказать, на этот раз у меня нет оправданий или логических объяснений для того что я опять сотворил.

Cпишем на плохую погоду и рептилоидов с Нибиру:

рассказываю и показываю как создавать приложения на современном C++ для классического Apple Macintosh начала 90х.

Примерно такого:

Macintosh Quadra 700. Компьютер как компьютер.

Macintosh Quadra 700. Компьютер как компьютер.

The Macintosh Quadra 700 is a personal computer designed, manufactured and sold by Apple Computer from October 1991 to March 1993.

Сам я никогда не видел такие машины в живую, поскольку в годы когда они выпускались был совсем зеленой школотой и жил в далеких сибирских пердях. Хотя даже живи я посреди Нью-Йорка в 90е — врядли бы смог позволить компьютер за почти шесть штук баксов:

The Quadra 700 originally had a list price of US$5,700

Такое и сегодня далеко не для всех.

Однако времена славы Макинтошей давно прошли, ныне все эти некогда «звездные» компьютеры — те что еще остались в рабочем состоянии, являются музейными экспонатами и предметами коллекционирования.

Поэтому, то что опишу ниже — точно не имеет никакого практического применения:

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

Это на тот случай, если вы вдруг ожидаете от статьи чего-то большего.

Весь сетап целиком.

Весь сетап целиком.

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

A GCC-based cross-compilation environment for 68K and PowerPC Macs. Why? Because there is no decent C++17 Compiler targeting Apple's System 6. If that's not a sufficient reason for you, I'm sure you will find something more useful elsewhere.

Разве можно было пройти мимо столь вдохновляющего описания?

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

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

Матчасть

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

Хотя именно <a href="https://pikabu.ru/story/retro68_esli_vam_skuchno_zhit_na_svete_14072507?u=https%3A%2F%2Flinuxdevices.org%2Ftiny-linux-mips-system-runs-on-3-watts%2F&t=%D1%8D%D1%82%D0%BE%D1%82%20%D0%B4%D0%B5%D0%B2%D0%B0%D0%B9%D1%81&h=3f102c8688b75f6dcd0978108dd586021fafe38e" title="https://linuxdevices.org/tiny-linux-mips-system-runs-on-3-watts/" target="_blank" rel="nofollow noopener">этот девайс</a> на самом деле весьма мощный.

Хотя именно этот девайс на самом деле весьма мощный.

Да, на фото выше самый настоящий компьютер:

c процессором, памятью и портами ввода-вывода — все как положено.

Но только слишком слабый для развертывания полноценной среды разработки непосредственно на нем.

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

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

смартфоне, роутере, соковыжималке, встраиваемой платформе умного дома, коптере или еще каком боевом роботе.

Или на другой операционной системе — сборка на линуксе под Windows это тоже вполне себе кросс-компиляция.

Тулчейн для кросс-компиляции обычно устанавливается (и даже собирается) отдельно, часто бывая очень сложным. Еще он содержит компиляторы, линкеры, ассемблеры для «вражеской» архитектуры и многое другое, не менее интересное.

В некоторых случаях тулчейн включает и части целевой системы:

заголовочные файлы, ресурсы и даже готовые библиотеки для линковки.

Без которых создавать что-то сложнее «Hello, world» с помощью кросс-компиляции было бы проблематично.

Так выглядит тот самый "Hello,world", собранный через кросс-компиляцию и запущенный в эмуляторе с MacOS 7.

Так выглядит тот самый "Hello,world", собранный через кросс-компиляцию и запущенный в эмуляторе с MacOS 7.

Retro68

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

a gcc-based cross-compiler for classic 68K and PPC Macintoshes

Во-первых «gcc-based», что означает определенную совместимость с нормальными и современными компиляторами, а также отсутствие экзотических проблем, характерных для редких и/или устаревших компиляторов, вроде bcc.

Во-вторых тут две разных целевых архитектуры: 68k и PowerPC.

А значит можно собирать софт для всей линейки Маков с начала 90х и до начала 2000х — до MacOS X Tiger, последней поддерживающей архитектуру PowerPC.

Но пожалуй самое важное это поддержка трех разных UI-фреймворков на целевых системах:

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

Типа такого:

Так выглядит демо с диалогом, созданное при помощи кросс-компиляции.

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

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

Терминал

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

Консоль появилась только в <a href="https://pikabu.ru/story/retro68_esli_vam_skuchno_zhit_na_svete_14072507?u=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FMacOS&t=MacOS%20X&h=5fffa674be624100557b82e8803e28d255798bb0" title="https://en.wikipedia.org/wiki/MacOS" target="_blank" rel="nofollow noopener">MacOS X</a>, в 2001м году.

Консоль появилась только в MacOS X, в 2001м году.

Поэтому тот факт, что классическое консольное приложение на С:

#include <stdio.h>

int main(int argc, char** argv)
{
printf("Hello, world.\n");
printf("\n(Press Return)\n");
getchar();
return 0;
}

возможно собрать в графическое, с таким псевдо-терминалом в комплекте:

Тоже является заслугой тулчейна Retro68 и ключем линковки при сборке:

LDFLAGS=-lRetroConsole

Сборка на FreeBSD

Тулчейн официально поддерживает несколько популярных операционных систем:

  • Linux, Mac OS X or Windows (via Cygwin)

И действительно совершенно спокойно, без каких-либо проблем и нюансов собирается на линуксе.

Что мне показалось слишком скучным, поэтому ради гусарской забавы портировал этот проект на FreeBSD.

Всегда так делаю.

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

Ну а мы как обычно пойдем путем хардкора — соберем весь тулчейн на FreeeBSD с нуля.

Кстати форк Retro68 с поддержкой сборки на FreeBSD выложен в отдельном репозитории. Первым делом надо установить ряд инструментов для разработки и системных библиотек:

pkg install cmake gmp mpfr mpc boost-all bison flex texinfo ruby

Забираем исходники и поскольку репозиторий большой — выгружаем без истории коммитов (ключ depth):

git clone --depth 1 https://github.com/autc04/Retro68.git

Выкачиваем зависимые репозитории:

git submodule update --init
git pull
git submodule update

Теперь надо немного изменить скрипт сборки, для учета особенностей FreeBSD.

Первым делом заменяем вызовы make на gmake, поскольку FreeBSD использует свою версию утилиты make, несовместимую с GNU-версией. Которая конечно также присутствует в пакетах, но только называется gmake.

Дальше необходимо переделать вызовы configure для вложенных проектов из скрипта сборки, суть правок:

пробросить указание на использование библиотек из каталога /usr/local, куда устанавливаются все внешние с точки зрения системы библиотеки в FreeBSD.

Например для строки:

$SRC/binutils/configure --target=m68k-apple-macos --prefix=$PREFIX --disable-doc

необходимо дописать в конец:

CFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib

Таким образом переменные CFLAGS и LDFLAGS будут переданы скрипту configure в качестве аргумента при вызове.

Чуть сложнее со сборкой GCC (да, мы тут собираем компилятор из исходников), строка:

$SRC/gcc/configure --target=m68k-apple-macos --prefix=$PREFIX \
--enable-languages=c,c++ --with-arch=m68k --with-cpu=m68000 \
--disable-libssp MAKEINFO=missing

Тут уже нельзя использовать переменные окружения CFLAGS и LDFLAGS из-за особенностей сборки GCC и необходимо передавать пути для ключевых библиотек специальными аргументами:

--with-mpc=/usr/local --with-mpfr=/usr/local --with-gmp=/usr/local --with-isl=/usr/local --with-libiconv-prefix=/usr/local

Дописав их в конец вызова configure.

Таким же образом необходимо поправить еще два места в скрипте сборки: раз и два.

После чего можно запускать сборку.

Запускается она из отдельного каталога:

mkdir ../Retro68-build
cd ../Retro68-build
../Retro68/build-toolchain.bash

После весьма продолжительной (даже на мощном компьютере) и жутко выглядящей сборки, по накалу страстей на экране напоминающей знаменитый make buildworld из FreeBSD:

..все упадет на вот такой «страшной» ошибке:

Для исправления ситуации, надо всего лишь изменить один import в файле portable_endian.h , в этом месте:

#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)

# include <sys/endian.h>

Суть проблемы в том что расположение заголовка endian.h поменялось и теперь его необходимо включать без указания каталога sys:

# include <endian.h>

Исправляем импорт и перезапускаем сборку.

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

На этот раз падение произойдет на стадии сборки из cmake:

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

удалением импорта этого заголовка из файла PEFTools/MakePEF.cc

Поскольку его содержимое в последних версиях FreeBSD добавили в stdlib. Так что смело удаляем импорт и перезапускаем сборку.

Следующее падение сборки и следующая проблема заключается в пересечении заголовков — системного, из самой FreeBSD и локального — из библиотеки libelf:

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

скопировал файл gelf.h из libelf/include в каталог Elf2Mac, и поменял ссылки на этот заголовок из глобального на локальный.

Было:

#include <gelf.h>

Стало:

#include "gelf.h"

Поменять ссылки необходимо во всех файлах проекта Elf2Mac где встречается данный импорт. Наконец последняя ошибка сборки, связанная с особенностями FreeBSD:

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

Для исправления достаточно добавить:

#include <netinet/in.h>

в файл LaunchAPPL/Client/TCP.cc

Так выглядит успешное завершение сборкиRetro68:

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

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

Эмулятор

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

Такова сила бренда, созданного Стивом Джобсом.

Я буду использовать один из самых популярных и известных эмуляторов классических Macintosh:

Basilisk II is an Open Source 68k Macintosh emulator.

Разработан он был еще в начале 2000х, немецким инженером Christian Bauer, ныне активная разработка уже не ведется. Поэтому вместо нее я использовал более современный форк, в котором решены вопросы совместимости с современным же окружением и обновлены используемые библиотеки.

И внезапно добавлен JIT (Just-In-Time Compiler).

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

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

pkg install sdl2 autoconf automake mpfr gmp

Теперь забираем исходники:

git clone https://github.com/kanjitalk755/macemu.git

Собираем:

cd macemu/BasiliskII/src/Unix
$ ./autogen.sh
$ gmake

В результате в текущем каталоге появится бинарник BasiliskII, которым и запускается эмулятор:

Для того чтобы запускать MacOS7 в эмуляторе, нужен загружаемый образ диска с установленной системой (или ее инсталлятор) и ROM-файл.

Образ диска можно взять например тут или тут, ROM-файл например отсюда или отсюда. Еще пара вариантов.

Еще вот тут находится крайне интересная табличка, с описанием практически каждого доступного ROM, его особенностей и работоспособности.

Напомню, что для этой статьи я эмулировал модель Quadra 600:

Для эмулятора использовался вот такой ROM-файл:

Указав в разделе «Volumes» путь к образу диска с MacOS7, затем в разделе «Memory/Misc» путь к ROM-файлу, можно наконец запускать эмулятор:

Теперь переходим к самому веселому — к разработке.

Разработка с помощью Retro68

Собственно после сборки Retro68, у вас будет все что нужно для разработки конечного софта, поскольку в каталоге build-target/Samples будут собранные примеры приложений:

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

Для сборки во всех случаях используется cmake, исходники находятся в каталоге Retro68/Samples.

Чтобы собрать проект с HelloWorld отдельно, выполняем:

сd /opt/src/Retro68/Samples/HelloWorld
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=/opt/src/Retro68-build/toolchain/m68k-apple-macos/cmake/retro68.toolchain.cmake
gmake

Результат сборки:

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

Достаточно указать путь к dsk-файлу, который находится в каталоге build и запустить эмулятор:

На рабочем столе появится иконка с названием, совпадающим с названием примера:

После двойного клика по этой иконке, раскроется окно с содержимым этого виртуального диска в виде единственного запускаемого файла:

Двойной клик по файлу HelloWorld запустит наше приложение.

Примеры приложений

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

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

Автор тулчейна постарался, поэтому все примеры логичны, просты и понятны.

Retro68/Samples/Raytracer

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

В проекте на самом деле присутствуют сразу две реализации, одна на С, другая на C++, видимо для сравнения во время работы.

Так выглядит финальный результат рендеринга:

Проект ценен в первую очередь как демонстрация расчетной логики и операций с числами с плавающей точкой.

Retro68/Samples/Dialog

Демонстрация работы с модальными диалогами — как задавать размеры и расположение элементов, как создавать формы ввода, как получать вводимые данные и так далее.

В работе:

Retro68/Samples/WDEF

Демонстрация работы с многооконными интерфейсами (MDI), создание окна с кастомным оформлением и работа с ресурсами окон.

Так это выглядит в действии:

Кратко по остальным примерам, реализующим какую-то специфическую логику.

Retro68/Samples/MPWTool

Реализует расширение для MPW (Macintosh Programmer's Workshop), точнее для MPWShell, с обработкой запуска оттуда и корректного возвращения в консоль.

Retro68/Samples/SharedLibrary

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

Retro68/Samples/SystemExtension

Пример простейшего расширения для самой операционной системы MacOS 7, в данном случае вся видимая логика заключается в показе новой иконки при запуске ОС.

Retro68/Samples/Launcher

Реализует запуск сторонних приложений через перетаскивание (Drag&Drop).

"Masters of Hardcore" - вбейте в поиск если не жалко уши и соседей.

"Masters of Hardcore" - вбейте в поиск если не жалко уши и соседей.

Настоящий хардкор

Вы же не думали, что я успокоюсь на банальной сборке тулчейна с эмулятором под FreeBSD и запуске пары тестов?

Праздник должен продолжаться:

Clapkit (CLassic APplication KIT) is a framework for developing applications for Classic Mac OS (System 7.x - Mac OS 9.x), basically a C++/object-oriented wrapper for Macintosh Toolbox functions.

Да, глаза вас не подводят, это самый настоящий современный графический фреймворк для компьютеров начала 90х.

Так это выглядит:

Картина неизвестного питерского художника "Мак под маком".

Картина неизвестного питерского художника "Мак под маком".

Так выглядит код:

#include <ckApp.h>

int main() {

CKApp* app = CKNew CKApp();

app->CKNewMsgBoxNote("Hello world!", nullptr, "OK",
nullptr, [app](int button) {
app->CKQuit();
});
// Run loop: without this, your app will quit as soon as it launches.
while (!app->CKLoop(5));
delete app;
return 0;
}

Так выглядит мое тестовое приложение на современном C++, собранное с помощью cmake на FreeBSD и запущенное на MacOS 7 из 90х:

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

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

Ну что дорогие читатели, ударим шизой по осенней летней хандре?

MPW

Конечно ударим, но прежде стоит рассказать про нормальную разработку для продукции Apple, как это происходило у нормальных людей.

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

Macintosh Programmer's Workshop (MPW) is a software development environment for the Classic Mac OS operating system, written by Apple Computer.

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

Как-то так MPW выглядит в эмуляторе:

Тут находится отличная статья по созданию «Hello, world» на MPW в эмуляторе, которой вполне хватит для начала приключений. Но в этот раз нам нужен не MPW а только его часть — системные заголовки, которые мы будем использовать для злодейства сборки.

В Retro68, в каталоге InterfacesAndLibraries находится файл Readme.md, содержимое которого явно намекает на дальнейшие действия:

Find a copy of Apple's Universal Interfaces 3.x (preferrably 3.4) and put them here.

Вообще «Find a copy» это такой толерантный и социально приемлемый синоним старого доброго «скачать варез», прямо из 90х. Потому как купить официально старое ПО уже не представляется возможным, если подобное вдруг придет в голову. Даже если компания-разработчик существует (как в случае Apple), старые версии своих продуктов она так просто не продаст.

Потому что есть поддержка, гарантии и защита прав потребителей.

И репутация компании.

Поэтому сообщаю по секрету, что «найти» эти самые универсальные интерфейсы можно например тут:

wget https://henlin.net/images/Interfaces\&Libraries.zip
unzip "Interfaces&Libraries.zip" -d /opt/src/Retro68/InterfacesAndLibraries

Ссылка кстати взята из другой интересной статьи, в которой автор реализует слой рендера для фреймворка Nuklear, о котором я уже внезапно писал.

Так это демо выглядит в работе, порт эмулятора на Javascript внезапно оказался встроен в статью:

JS-версия загружается довольно долго, браузер может подвисать.

JS-версия загружается довольно долго, браузер может подвисать.

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

Разгадка оказалась в необходимости использовать другой эмулятор и собственно полностью другую эмулируемую систему.

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

А пока для затравки, показываю эту связку в действии:

Не пугайтесь размеров элементов интерфейса в эмуляторе - игрался с зумом, проиграл.

Не пугайтесь размеров элементов интерфейса в эмуляторе - игрался с зумом, проиграл.

Но вернемся к фреймворку ClapKit, благо история с ним еще не закончена.

После распаковки заголовков из MPW, необходимо перезапустить сборку самого тулчейна Retro68:

cd Retro68-build
../Retro68/build-toolchain.bash

И наконец можно приступать к половым утехам с самим ClapKit. Забираем исходники:

git clone https://github.com/macinlink/clapkit.git

Сборка построена на cmake, но перед тем как собирать Clapkit, необходимо указать путь к Retro68 в скрипте сборки:

# Ensure Retro68 toolchain is set BEFORE defining project
set(CMAKE_TOOLCHAIN_FILE "/opt/src/Retro68-build/toolchain/m68k-apple-macos/cmake/retro68.toolchain.cmake")

и чуть ниже:

# Define Retro68 path properly
set(RETRO68_PATH /opt/src/Retro68-build/toolchain/m68k-apple-macos)

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

Для исправления достаточно добавить в include/ckTypes.h два импорта:

#include <Quickdraw.h>
#include <Events.h>

В репозитории проекта находится готовый Showcase с демонстрацией возможностей фреймворка, на базе которого я и делал свое тестовое приложение (см. ниже).

Так выглядит сам Showcase в работе:

Напоминаю, что все это создано с помощью кросс-компиляции и затем запущено в целевой MacOS 7.

Напоминаю, что все это создано с помощью кросс-компиляции и затем запущено в целевой MacOS 7.

Выглядит заметно веселее, чем примеры из самого Retro68, согласитесь.

Тестовый образец

Я не хотел сразу пускаться во все тяжкие и портировать Doom/Quake/Cyberpunk 2077 под винтажные Маки из 90х — надо же оставить место воображению, поэтому мой тестовый образец для этой статьи это в первую очередь пример автономного, отделяемого приложения с минимальной логикой.

Которое собирается без привязки к иерархии каталогов Clapkit или Retro68.

Проект выложен в отдельном репозитории на Github.

Так мое приложение выглядит в работе на MacOS 7:

Все что оно делает:

отображает меню «Yo» в Finder, с единственным пунктом «Run me», по клику на который вызывается модальный диалог с двумя кнопками. По нажатию на «Quit» произойдет завершение работы программы.

Фигня-фигней, но только разработано и собрано оно полностью и целиком вне инфраструктуры MacOS и без средств разработки от Apple, даже без техники Apple.

На современном С++, под FreeBSD темной питерской ночью.

Так выглядит основной код на C++, файл main.cpp:

#include "main.h"
#include <ckMenu.h>

CKTestAlexs* app;

int main() {
// класс переименован ради тестов
app = CKNew CKTestAlexs();
// false - не использовать стандартные пункты меню
CKMenuBar* menuBar = CKNew CKMenuBar(false);
// добавляем свой пункт меню в Finder
CKMenu* menuTests = CKNew CKMenu("Yo");
menuBar->AddMenu(menuTests);
// затем элемент в выпадающем списке
CKMenuItem* item = CKNew CKMenuItem("Run me", 'R', [&item](CKEvent e) {
// по клику произойдет отображение стандартного
// модального диалога
app->CKNewMsgBoxNote("Welcome to old school!",
"Hello world", "Mkay", "Quit", [](int button) {
// если нажали Quit - завершить работу приложения.
if (button == 0) {
app->CKQuit();
}
});
});
// связывание элемента с пунктом меню
menuTests->AddItem(item);
// связывание меню с приложением
app->CKSetMenu(menuBar);
// бесконечный цикл ожидания для отрисовки,
// без которого приложение сразу завершится
while (!app->CKLoop(5));
delete app;
return 0;
}

Класс описан в заголовке main.h, декларации методов я убрал за ненадобностью:

#include <ckApp.h>

class CKTestAlexs : public CKApp {
};

Так выглядит скрипт сборки для cmake, как можно заметить тут происходит автоматическое скачивание родительского фреймворка Clapkit и наложение патча с пропущенными импортами (описаны выше):

cmake_minimum_required(VERSION 3.16)

# Ensure Retro68 toolchain is set BEFORE defining project
set(RETRO68_PATH "/opt/src/Retro68-build/toolchain/m68k-apple-macos")
set(CMAKE_TOOLCHAIN_FILE "${RETRO68_PATH}/cmake/retro68.toolchain.cmake")

project(clapkit VERSION 1.0)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# Include Retro68 headers
include_directories(${RETRO68_PATH}/include)

# Set default build type to Release if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (Debug or Release)" FORCE)
endif()

set(CLAPKIT_LOCAL_PATH "${CMAKE_SOURCE_DIR}/../../")
option(USE_LOCAL_CLAPKIT "Use local checkout of clapkit instead of fetching from Git" ON)

if(USE_LOCAL_CLAPKIT AND EXISTS "${CLAPKIT_LOCAL_PATH}/CMakeLists.txt")
message(STATUS "Using local clapkit at ${CLAPKIT_LOCAL_PATH}")
add_subdirectory(${CLAPKIT_LOCAL_PATH} clapkit)
set(USE_LOCAL_CLAPKIT ON)
else()

set(cktypes_patch git apply ${CMAKE_SOURCE_DIR}/patches/cktypes.patch)

message(STATUS "Fetching clapkit from GitHub...")
include(FetchContent)
FetchContent_Declare(
clapkit
GIT_REPOSITORY https://github.com/macinlink/clapkit.git
GIT_TAG main
PATCH_COMMAND ${cktypes_patch}
UPDATE_DISCONNECTED 1
)
FetchContent_MakeAvailable(clapkit)
set(USE_LOCAL_CLAPKIT OFF)
endif()

# Add application
add_application(CKTestAlexs
TYPE "APPL"
CREATOR "CKTS"
main.cpp
cktest.r
)

# Debug vs Release settings
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Configuring Debug Build")
target_compile_definitions(CKTestAlexs PRIVATE kCKAPPDEBUG=1)
target_compile_definitions(clapkit PRIVATE kCKAPPDEBUG=1)

set_target_properties(CKTestAlexs PROPERTIES
COMPILE_FLAGS "-O0 -Wall -g -fdata-sections -ffunction-sections"
LINK_FLAGS "-Wl,--mac-single"
)

elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
message(STATUS "Configuring Release Build")
target_compile_definitions(CKTestAlexs PRIVATE NDEBUG) # Disable debugging in Release
target_compile_definitions(clapkit PRIVATE NDEBUG)

set_target_properties(CKTestAlexs PROPERTIES
COMPILE_FLAGS "-s -O1 -Wall -fmerge-all-constants -fmerge-constants -fstrict-aliasing -fomit-frame-pointer -fdata-sections -ffunction-sections -finline-functions"
LINK_FLAGS "-Wl,--mac-single -Wl,--mac-strip-macsbug"
)
endif()

# Link clapkit
target_link_libraries(CKTestAlexs PRIVATE clapkit)

Поскольку пути к Retro68 задаются внутри скрипта сборки, а фреймворк Clapkit автоматически скачивается из Github, сама сборка максимально упрощена:

mkdir build
cd build
cmake ..
gmake

При успешной сборке будет создан образ диска с приложением внутри, который можно легко подцепить в эмулятор:

Продолжение банкета

Ниже несколько интересных (но все еще социально одобряемых) идей, на тему что еще можно сделать с эмулятором винтажного Macintosh.

Для начала, можно довести накал программерского угара до предела — интегрировав Retro68 с полноценным IDE и отладчиком:

Developing vintage 68K Macintosh apps with CodeLite IDE, Retro68 and pce-macplus emulator

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

С запуском собранного приложения в эмуляторе сразу из среды разработки:

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

Тут можно найти отдельное видео с демонстрацией работы.

Следующая замечательная идея в моем списке это очевидная попытка использовать Rust для кросс-компиляции под старые Mac. Нет, автор не шутит, он действительно это сделал:

Through some luck and a little persistence I have actually managed to get Rust code running on classic Mac OS (I’ve tried Mac OS 7.5 and 8.1)

Репозиторий находится тут, проект достаточно свежий (2023 год) и вполне рабочий.

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

Так оно выглядит в работе:

Кстати приложение сетевое, так что на Rust реализована не только работа с интерфейсом, но и с TCP/IP и даже HTTP протоколом.

Для операционной системы из 1994 года.

P.S.

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

Показать полностью 41
3

Развлечения джентельменов: Binary Golf

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

Нет это не баг, это спецэффект!

Нет это не баг, это спецэффект!

После четырех лет жизни в Питере, IOCCC, запусков всевозможной дичи из далекого прошлого, самопальных патчей ядра Linux и исправления ошибок в драйверах для FreeBSD я пребывал в полной уверенности, что удивить столь искушенного джентельмена не сможет уже ничто.

Я ошибался.

То что описано в этой статье — за гранью понимания обычного разработчика и тем более обычного человека, далекого от ИТ. Честно и при всем желании не смогу объяснить даже подготовленным «молодым специалистам» от разработки, протирающим штаны в опенспейсе за шлепаньем форм для очередного корпоративного маркетплейса насколько круто то что описано ниже.

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

Добро пожаловать, снова: <a href="https://pikabu.ru/story/razvlecheniya_dzhentelmenov_binary_golf_14068864?u=https%3A%2F%2Fbinary.golf%2F&t=https%3A%2F%2Fbinary.golf%2F&h=24e67b8051d9cea0d28f96b2a437c4d76cc5f62e" title="https://binary.golf/" target="_blank" rel="nofollow noopener">https://binary.golf/</a>

Добро пожаловать, снова: https://binary.golf/

Любовь и байты

Однажды в хорошем пабе собрались несколько джентельменов от мира разработки:

демосценеры, реверс-инженеры, ИБ-аналитики малвари и разумеется яркие представители с «другой стороны».

Когда в одном месте собираются столь крутые профи, обязательно начинается потеха под названием у кого длиннее «кто круче»:

─── Binary Golf Grand Prix 1──────────────────────//──

Welcome to the Binary Golf Grand Prix! This is a challenge for people who like to craft tiny binaries.

Так появился на свет этот замечательный конкурс:

The goal of the Binary Golf Grand Prix is to challenge programmers to make the smallest possible binary that fits within certain constraints.

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

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

The binary you will craft will be the same executable when flipped backwards.

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

$ xxd some.random.exe | head -n 1
00000000: 4d5a 9000 0345 0000 0400 0000 ffff 0000 MZ...E..........

The entire binary will be reversed, and the first byte, will be the last byte:

4d5a 9000 0345 .... <-- reversed --> .... 4503 0090 5a4d

Критериев оценки в столь замечательном конкурсе два длина и толщина:

размер запускаемого бинарника и «процент реиспользования» байт при запуске в обратном порядке.

В оригинале:

Scores will be calculated based on both the size of the executable, as well asthe percentage of bytes executed when it is run.

Так что у джентельменов все серьезно.

Исходный код всех работ с описанием выложен на Github, также тут можно посмотреть разбор победителей этого замечательного конкурса за 2020й год и дальше не читать. Но если вы все же решили продолжить, то ниже будет описан реальный запуск каждой работы этого удивительного конкурса.

BootNoodle: A Palindromic Bootloader for BGGP

Автор: xcellerator

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

Запускается в эмуляторе QEMU:

qemu-system-x86_64 bin/bootnoodle.bin

В работе:

Вот так выглядит сборка и создание «зеркальной» копии:

mkdir bin 2>/dev/null
nasm -f bin -o bin/bootnoodle.bin src/bootnoodle.asm
dd if=bin/bootnoodle.bin of=bin/tmp.bin bs=1 count=256
rm bin/bootnoodle.bin
perl -0777pe '$_=reverse $_' bin/tmp.bin > bin/tmp2.bin
cat bin/tmp.bin bin/tmp2.bin > bin/bootnoodle.bin
rm bin/tmp*

512 байт на все, если вы вдруг не заметили. И это у джентельменов считается «слишком много».

ns.bggp : Palindromic 64 bit ELF binary

Автор: netspooky

Статья с описанием: https://n0.lol/elf-palindrome-original/

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

Он же в упакованном виде:

base64 -d <<< \
f0VMRgUP/zFIPLCQkJDrNAIAPgABAAAABAAAAAEAAAAcAAAAAAAAAAAAAAAAAAAAAQAAAEAAOAAB\
AAIA6wsAAAAAAADrCwAAAAAAADzrwDFIUFVQUFlTUFlQU1lQUFVQ6xiQkJCQkAUPlbZAIObBSMaJ\
D7LHiQAAAAG4AQAAAInHsg+JxkjB5iBAtpUPBZCQkJCQGOtQVVBQWVNQWVBTWVBQVVBIMcDrPAAA\
AAAAAAvrAAAAAAAAC+sAAgABADgAQAAAAAEAAAAAAAAAAAAAAAAAAAAcAAAAAQAAAAQAAAABAD4A\
AjTrkJCQsDxIMf8PBUZMRX8= > ns.bggp

Теперь о печальном:

This was tested and built on Ubuntu 20.04 with kernel 5.4.0-42-generic.

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

Вот так это должно было выглядеть:

$ ./build.sh
Executing initial binary...
PUPPYSPYPSYPPUP
00000000: 7f45 4c46 050f ff31 483c b090 9090 eb34 .ELF...1H&lt;.....4
00000010: 0200 3e00 0100 0000 0400 0000 0100 0000 ..>.............
00000020: 1c00 0000 0000 0000 0000 0000 0000 0000 ................
00000030: 0100 0000 4000 3800 0100 0200 eb0b 0000 ....@.8.........
00000040: 0000 0000 eb0b 0000 0000 0000 3ceb c031 ............&lt;..1
00000050: 4850 5550 5059 5350 5950 5359 5050 5550 HPUPPYSPYPSYPPUP
00000060: eb18 9090 9090 9005 0f95 b640 20e6 c148 ...........@ ..H
00000070: c689 0fb2 c789 0000 0001 b801 0000 0089 ................
00000080: c7b2 0f89 c648 c1e6 2040 b695 0f05 9090 .....H.. @......
00000090: 9090 9018 eb50 5550 5059 5350 5950 5359 .....PUPPYSPYPSY
000000a0: 5050 5550 4831 c0eb 3c00 0000 0000 000b PPUPH1..&lt;.......
000000b0: eb00 0000 0000 000b eb00 0200 0100 3800 ..............8.
000000c0: 4000 0000 0100 0000 0000 0000 0000 0000 @...............
000000d0: 0000 0000 1c00 0000 0100 0000 0400 0000 ................
000000e0: 0100 3e00 0234 eb90 9090 b03c 4831 ff0f ..>..4.....&lt;H1..
000000f0: 0546 4c45 7f .FLE.

Reversing...
Executing binary in reverse...
PUPPYSPYPSYPPUP
00000000: 7f45 4c46 050f ff31 483c b090 9090 eb34 .ELF...1H&lt;.....4
00000010: 0200 3e00 0100 0000 0400 0000 0100 0000 ..>.............
00000020: 1c00 0000 0000 0000 0000 0000 0000 0000 ................
00000030: 0100 0000 4000 3800 0100 0200 eb0b 0000 ....@.8.........
00000040: 0000 0000 eb0b 0000 0000 0000 3ceb c031 ............&lt;..1
00000050: 4850 5550 5059 5350 5950 5359 5050 5550 HPUPPYSPYPSYPPUP
00000060: eb18 9090 9090 9005 0f95 b640 20e6 c148 ...........@ ..H
00000070: c689 0fb2 c789 0000 0001 b801 0000 0089 ................
00000080: c7b2 0f89 c648 c1e6 2040 b695 0f05 9090 .....H.. @......
00000090: 9090 9018 eb50 5550 5059 5350 5950 5359 .....PUPPYSPYPSY
000000a0: 5050 5550 4831 c0eb 3c00 0000 0000 000b PPUPH1..&lt;.......
000000b0: eb00 0000 0000 000b eb00 0200 0100 3800 ..............8.
000000c0: 4000 0000 0100 0000 0000 0000 0000 0000 @...............
000000d0: 0000 0000 1c00 0000 0100 0000 0400 0000 ................
000000e0: 0100 3e00 0234 eb90 9090 b03c 4831 ff0f ..>..4.....&lt;H1..
000000f0: 0546 4c45 7f .FLE.

Comparing hashes...
c082d226c96b7251649c48526dd9766071fa5e59 ns.bggp
c082d226c96b7251649c48526dd9766071fa5e59 ns.bggp.R

Текст «PUPPYSPYPSYPPUP» это вывод приложения при запуске.

Viznut/PWP

Автор: viznut

Статья с описанием: http://viznut.fi/demos/vic20/vizpalapziv.html

Работа:

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

Entry name: VIZPALAPZIV Executable format: Commodore 8-bit PRG format on Commodore VIC-20 Number of bytes: 20 (including the 2-byte start address) Executed bytes: 18 (every byte except the start address, i.e. 90%) Bytes: 7c 00 8f 0f 90 25 48 48 73 a9 a9 73 48 48 25 90 0f 8f 00 7c

Да, это программа для Commodore, вот такой игрушечной машинки из 80х:

Которую мало кто из читателей имел возможность видеть в живую.

BGGP.COM

Автор: Boo Khan Ming

Работа:

Как гласит известная поговорка:

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

И код выше — лучшая тому иллюстрация.

Простой малазийский паренек сорвал куш — его код стал победителем этой специальной олимпиады в 2020 м году:

Hi! Nice to meet you everyone there! I am Wu (Boo Khan-Ming) from Malaysia. I am not a scene/demo coder. Just join this ASMCOMPO3 for fun.

Исходник на ассемблере под DOS:

JMP 103  EB01
RET  C3
MOV AX, B800  B800B8
MOV ES, AX  8EC0
MOV DI, 07D0  BFD007  
MOV AX, 9090  B89090
MOV ES:[DI], AX  268905
ADD AX, 2689  058926
NOP  90
NOP  90
MOV AX, D007  B807D0
MOV DI, 8EC0  BFC08E
MOV AX, B800  B800B8
RET  C3

Вот так это выглядит в работе:

При работе синий блок еще и мигает.

При работе синий блок еще и мигает.

Эпилог

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

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

Статья была опубликована на Хабре, оригинальная статья в нашем блоге.

Показать полностью 8
1

Реклама через попу Mail.ru

Привет Mail.ru, у вас реклама через жопу сделана. Она появляется поверх контента с задержкой, я постоянно на нее случайно кликаю. Это плохой UX и вы лишние деньги списываете с рекламодателей. Мне их продукт неинтересен, просто то как вы реализовали рекламу приводит к ежедневным случайным кликам по ней от меня.

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

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

Отличная работа, все прочитано!

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества