summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2019-12-22 15:06:47 +1100
committerDamien Zammit <damien@zamaudio.com>2019-12-22 15:06:47 +1100
commitc7c085f57f7a65d411d755c1e180d6efa91f51b1 (patch)
treeff2b74e374e3f0ac3d6df39aa63c68d2c0a4c5f4
parent96cbf6c89dd2c132ccf2a32f619ce887c706cc19 (diff)
utils/aurez: Add AuRez for cross-compiling AUs
-rw-r--r--utils/aurez/AuRez.cc223
-rw-r--r--utils/aurez/AuRsrc.cc62
-rw-r--r--utils/aurez/AuRsrc.h60
-rw-r--r--utils/aurez/LICENSE23
-rw-r--r--utils/aurez/MacRsrc.cc260
-rw-r--r--utils/aurez/MacRsrc.h55
-rw-r--r--utils/aurez/Makefile26
-rw-r--r--utils/aurez/README.md5
8 files changed, 714 insertions, 0 deletions
diff --git a/utils/aurez/AuRez.cc b/utils/aurez/AuRez.cc
new file mode 100644
index 00000000..f4ef0f95
--- /dev/null
+++ b/utils/aurez/AuRez.cc
@@ -0,0 +1,223 @@
+// Copyright Jean Pierre Cimalando 2018.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "MacRsrc.h"
+#include "AuRsrc.h"
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+void show_help()
+{
+ fprintf(stderr,
+ "Usage: AuRez <type-id> <subtype-id> <manufacturer-id>\n"
+ " [-o|--output <file.rsrc>]\n"
+ " [-A|--arch <arch>]* [-M|--manufacturer <name>] [-P|--product <name>] [-V|--version <x.y.z>]\n"
+ " [-e|--entry <function>] [-v|--view-entry <function>]\n"
+ " [-n|--no-view]"
+ "\n"
+ "Acceptable values for <type-id>:\n"
+ " `auou` Output `aumu` MusicDevice `aumf` MusicEffect\n"
+ " `aufc` FormatConverter `aufx` Effect `aumx` Mixer\n"
+ " `aupn` Panner `auol` OfflineEffect `augn` Generator\n");
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ show_help();
+ return 0;
+ }
+
+ unsigned archs = 0;
+ const char *manufacturer = nullptr;
+ const char *product = nullptr;
+ const char *version_string = nullptr;
+ const char *plugin_entry = nullptr;
+ const char *view_entry = nullptr;
+ const char *output_file = nullptr;
+ bool view_enabled = true;
+
+ static const option cmd_opts[] = {
+ {"output", 1, nullptr, 'o'},
+ {"arch", 1, nullptr, 'A'},
+ {"manufacturer", 1, nullptr, 'M'},
+ {"product", 1, nullptr, 'P'},
+ {"version", 1, nullptr, 'V'},
+ {"entry", 1, nullptr, 'e'},
+ {"view-entry", 1, nullptr, 'v'},
+ {"no-view", 0, nullptr, 'n'},
+ {}
+ };
+
+ for (int c; (c = getopt_long(argc, argv, "o:A:M:P:V:e:v:n", cmd_opts, nullptr)) != -1;) {
+ switch (c) {
+ case 'o':
+ output_file = optarg;
+ break;
+ case 'A': {
+ const char *arch = optarg;
+ if (!strcmp(arch, "i386"))
+ archs |= (1 << ArchIA32NativeEntryPoint);
+ else if (!strcmp(arch, "x86_64"))
+ archs |= (1 << ArchX86_64NativeEntryPoint);
+ else if (!strcmp(arch, "ppc"))
+ archs |= (1 << ArchPowerPCNativeEntryPoint);
+ else if (!strcmp(arch, "ppc64"))
+ archs |= (1 << ArchPowerPC64NativeEntryPoint);
+ else {
+ fprintf(stderr, "Unrecognized architecture `%s`\n", arch);
+ return 1;
+ }
+ break;
+ }
+ case 'M': {
+ manufacturer = optarg;
+ break;
+ }
+ case 'P': {
+ product = optarg;
+ break;
+ }
+ case 'V': {
+ version_string = optarg;
+ break;
+ }
+ case 'e': {
+ plugin_entry = optarg;
+ break;
+ }
+ case 'v': {
+ view_entry = optarg;
+ break;
+ }
+ case 'n': {
+ view_enabled = false;
+ break;
+ }
+ default:
+ show_help();
+ return 1;
+ }
+ }
+
+ if (optind != argc - 3) {
+ fprintf(stderr, "AuRez requires 3 positional arguments.\n");
+ show_help();
+ return 1;
+ }
+
+ const char *arg_type = argv[optind];
+ const char *arg_subtype = argv[optind + 1];
+ const char *arg_manuf = argv[optind + 2];
+
+ if (strlen(arg_type) > 4) {
+ fprintf(stderr, "The type-id cannot be longer than 4 characters.\n");
+ return 1;
+ }
+ if (strlen(arg_subtype) > 4) {
+ fprintf(stderr, "The subtype-id cannot be longer than 4 characters.\n");
+ return 1;
+ }
+ if (strlen(arg_manuf) > 4) {
+ fprintf(stderr, "The manufacturer-id cannot be longer than 4 characters.\n");
+ return 1;
+ }
+
+ char type[4] = {' ', ' ', ' ', ' '};
+ char subtype[4] = {' ', ' ', ' ', ' '};
+ char manuf[4] = {' ', ' ', ' ', ' '};
+ memcpy(type, arg_type, strlen(arg_type));
+ memcpy(subtype, arg_subtype, strlen(arg_subtype));
+ memcpy(manuf, arg_manuf, strlen(arg_manuf));
+
+ if (archs == 0) {
+ archs = (1 << ArchIA32NativeEntryPoint) | (1 << ArchX86_64NativeEntryPoint);
+ fprintf(stderr, "Architectures not set, defaulting to i386 and x86_64\n");
+ }
+ if (!manufacturer) {
+ manufacturer = "ACME";
+ fprintf(stderr, "Manufacturer not set, defaulting to `%s`\n", manufacturer);
+ }
+ if (!product) {
+ product = "Biniou";
+ fprintf(stderr, "Product not set, defaulting to `%s`\n", product);
+ }
+ if (!version_string) {
+ version_string = "1.0.0";
+ fprintf(stderr, "Version not set, defaulting to `%s`\n", version_string);
+ }
+ if (!plugin_entry) {
+ plugin_entry = "AUEntry";
+ fprintf(stderr, "Plugin entry not set, defaulting to `%s`\n", plugin_entry);
+ }
+ if (view_enabled && !view_entry) {
+ view_entry = "AUViewEntry";
+ fprintf(stderr, "View entry not set, defaulting to `%s`\n", view_entry);
+ }
+
+ uint16_t id_plugin = 1000;
+ uint16_t id_plugin_info = id_plugin + 1;
+ uint16_t id_view = 2000;
+ uint16_t id_view_info = id_view + 1;
+
+ unsigned versionX, versionY, versionZ;
+ if (sscanf(version_string, "%u.%u.%u", &versionX, &versionY, &versionZ) != 3) {
+ fprintf(stderr, "The version string is not in `x.y.z` format.\n");
+ return 1;
+ }
+
+ if (versionX > 255 || versionY > 255 || versionZ > 255) {
+ fprintf(stderr, "The version components cannot be greater than 255.\n");
+ return 1;
+ }
+
+ uint32_t version = (versionX << 16) | (versionY << 8) | versionZ;
+
+ MacRsrc rsrc;
+
+ rsrc.AddResource(
+ id_plugin, ResPurgeable, "STR", "", MakeSTR(std::string(manufacturer) + ": " + product));
+ rsrc.AddResource(
+ id_plugin_info, ResPurgeable, "STR", "", MakeSTR(product));
+ rsrc.AddResource(
+ id_plugin, 0, "dlle", "", MakeCSTR(plugin_entry));
+ rsrc.AddResource(
+ id_plugin, 0, "thng", std::string(manufacturer) + ": " + product,
+ MakeThng(archs, id_plugin, id_plugin_info, version, type, subtype, manuf));
+
+ if (view_enabled) {
+ rsrc.AddResource(
+ id_view, ResPurgeable, "STR", "", MakeSTR(std::string(manufacturer) + ": " + product + " View"));
+ rsrc.AddResource(
+ id_view_info, ResPurgeable, "STR", "", MakeSTR(std::string(manufacturer) + ": " + product + " View"));
+ rsrc.AddResource(
+ id_view, 0, "dlle", "", MakeCSTR(view_entry));
+ rsrc.AddResource(
+ id_view, 0, "thng", std::string(manufacturer) + ": " + product + " View",
+ MakeThng(archs, id_view, id_view_info, version, "auvw", subtype, manuf));
+ }
+
+ FILE *fh = output_file ? fopen(output_file, "wb") : stdout;
+ if (!fh) {
+ fprintf(stderr, "Cannot open output file.");
+ return 1;
+ }
+
+ if (isatty(fileno(fh)))
+ rsrc.DisplayAsText(fh);
+ else {
+ if (rsrc.Write(fh) == -1) {
+ fprintf(stderr, "Cannot write resource output.");
+ return 1;
+ }
+ }
+
+ if (fh != stdout)
+ fclose(fh);
+
+ return 0;
+}
diff --git a/utils/aurez/AuRsrc.cc b/utils/aurez/AuRsrc.cc
new file mode 100644
index 00000000..ab7933e8
--- /dev/null
+++ b/utils/aurez/AuRsrc.cc
@@ -0,0 +1,62 @@
+// Copyright Jean Pierre Cimalando 2018.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "AuRsrc.h"
+#include <string.h>
+#include <arpa/inet.h>
+
+std::vector<uint8_t> MakeSTR(const std::string &string)
+{
+ size_t size = string.size();
+ if (size > 255)
+ size = 255;
+
+ std::vector<uint8_t> pstr;
+ pstr.reserve(size + 1);
+ pstr.push_back((uint8_t)size);
+ pstr.insert(pstr.end(), &string[0], &string[size]);
+
+ return pstr;
+}
+
+std::vector<uint8_t> MakeCSTR(const std::string &string)
+{
+ const char *cstr = string.c_str();
+ return std::vector<uint8_t>(cstr, cstr + string.size() + 1);
+}
+
+std::vector<uint8_t> MakeThng(
+ unsigned archs, uint16_t id_plugin, uint16_t id_info, uint32_t version,
+ const char type[4], const char subtype[4], const char manuf[4])
+{
+ unsigned num_archs = 0;
+ for (unsigned a = ArchsBegin; a < ArchsEnd; ++a)
+ num_archs += (archs & (1 << a)) != 0;
+
+ std::vector<uint8_t> thng_buf(sizeof(CarbonThng) + num_archs * sizeof(CarbonThng::PlatformInfo));
+ CarbonThng *thng = (CarbonThng *)thng_buf.data();
+ memcpy(thng->Type, type, 4);
+ memcpy(thng->Subtype, subtype, 4);
+ memcpy(thng->Manufacturer, manuf, 4);
+ memcpy(thng->NameType, "STR ", 4);
+ thng->NameId = htons(id_plugin);
+ memcpy(thng->InfoType, "STR ", 4);
+ thng->InfoId = htons(id_info);
+ thng->ComponentVersion = htonl(version);
+ thng->RegistrationFlags = htonl(9); // componentHasMultiplePlatforms | componentDoAutoVersion
+ thng->PlatformInfoCount = htonl(num_archs);
+
+ for (unsigned a = ArchsBegin, i = 0; a < ArchsEnd; ++a) {
+ if ((archs & (1 << a)) == 0) continue;
+ CarbonThng::PlatformInfo &pl = thng->PlatformInfos[i];
+ pl.ComponentFlags = htonl(0x10000000); // cmpThreadSafeOnMac
+ memcpy(pl.CodeType, "dlle", 4);
+ pl.CodeId = htons(id_plugin);
+ pl.PlatformType = htons(a);
+ ++i;
+ }
+
+ return thng_buf;
+}
diff --git a/utils/aurez/AuRsrc.h b/utils/aurez/AuRsrc.h
new file mode 100644
index 00000000..d55c8b9f
--- /dev/null
+++ b/utils/aurez/AuRsrc.h
@@ -0,0 +1,60 @@
+// Copyright Jean Pierre Cimalando 2018.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#pragma once
+#include <string>
+#include <vector>
+#include <stdint.h>
+
+std::vector<uint8_t> MakeSTR(const std::string &string);
+std::vector<uint8_t> MakeCSTR(const std::string &string);
+
+enum CarbonArchitecture
+{
+ ArchsBegin = 1,
+ Arch68k = ArchsBegin,
+ ArchPowerPC,
+ ArchInterpreted,
+ ArchWin32,
+ ArchPowerPCNativeEntryPoint,
+ ArchIA32NativeEntryPoint,
+ ArchPowerPC64NativeEntryPoint,
+ ArchX86_64NativeEntryPoint,
+ ArchsEnd
+};
+
+#pragma pack(push, 1)
+struct CarbonThng
+{
+ char Type[4];
+ char Subtype[4];
+ char Manufacturer[4];
+ uint32_t ComponentFlags;
+ uint32_t ComponentFlagsMask;
+ char CodeType[4];
+ uint16_t CodeId;
+ char NameType[4];
+ uint16_t NameId;
+ char InfoType[4];
+ uint16_t InfoId;
+ char IconType[4];
+ uint16_t IconId;
+ uint32_t ComponentVersion;
+ uint32_t RegistrationFlags;
+ uint16_t IconFamilyId;
+ struct PlatformInfo {
+ uint32_t ComponentFlags;
+ char CodeType[4];
+ uint16_t CodeId;
+ uint16_t PlatformType;
+ };
+ uint32_t PlatformInfoCount;
+ PlatformInfo PlatformInfos[];
+};
+#pragma pack(pop)
+
+std::vector<uint8_t> MakeThng(
+ unsigned archs, uint16_t id_plugin, uint16_t id_info, uint32_t version,
+ const char type[4], const char subtype[4], const char manuf[4]);
diff --git a/utils/aurez/LICENSE b/utils/aurez/LICENSE
new file mode 100644
index 00000000..36b7cd93
--- /dev/null
+++ b/utils/aurez/LICENSE
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/utils/aurez/MacRsrc.cc b/utils/aurez/MacRsrc.cc
new file mode 100644
index 00000000..4d4fc858
--- /dev/null
+++ b/utils/aurez/MacRsrc.cc
@@ -0,0 +1,260 @@
+// Copyright Jean Pierre Cimalando 2018.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "MacRsrc.h"
+#include <algorithm>
+#include <stdio.h>
+#include <string.h>
+
+MacRsrc::MacRsrc()
+{
+}
+
+MacRsrc::~MacRsrc()
+{
+}
+
+size_t MacRsrc::GetResourceCount() const
+{
+ return fResources.size();
+}
+
+const MacRsrc::Resource &MacRsrc::GetResource(size_t index)
+{
+ return fResources[index];
+}
+
+uint16_t MacRsrc::FileAttributes() const
+{
+ return fFileAttributes;
+}
+
+void MacRsrc::SetFileAttributes(uint16_t attr)
+{
+ fFileAttributes = attr;
+}
+
+int MacRsrc::AddResource(uint32_t id, uint8_t attr, const char *type, std::string name, const std::vector<uint8_t> &data)
+{
+ Resource res;
+ res.Id = id;
+ res.Attr = attr;
+
+ size_t TypeLen = strlen(type);
+ if (TypeLen > 4)
+ return -1;
+
+ for (size_t i = 0; i < TypeLen; ++i)
+ res.Type[i] = type[i];
+ for (size_t i = TypeLen; i < 4; ++i)
+ res.Type[i] = ' ';
+
+ res.Name = std::move(name);
+ res.DataOffset = fData.size();
+ res.DataSize = data.size();
+ fData.insert(fData.end(), data.begin(), data.end());
+
+ fResources.push_back(std::move(res));
+ return 0;
+}
+
+static int WriteU16(uint32_t value, FILE *stream)
+{
+ for (unsigned i = 0; i < 2; ++i) {
+ if (fputc((value >> (8 * (1 - i))) & 0xff, stream) == EOF)
+ return -1;
+ }
+ return 0;
+}
+
+static int WriteU24(uint32_t value, FILE *stream)
+{
+ for (unsigned i = 0; i < 3; ++i) {
+ if (fputc((value >> (8 * (2 - i))) & 0xff, stream) == EOF)
+ return -1;
+ }
+ return 0;
+}
+
+static int WriteU32(uint32_t value, FILE *stream)
+{
+ for (unsigned i = 0; i < 4; ++i) {
+ if (fputc((value >> (8 * (3 - i))) & 0xff, stream) == EOF)
+ return -1;
+ }
+ return 0;
+}
+
+int MacRsrc::Write(FILE *stream) const
+{
+ const std::vector<Resource> &Resources = fResources;
+ size_t NumResources = Resources.size();
+
+ // Header (fill later)
+ off_t HeaderPos = ftell(stream);
+ for (unsigned i = 0; i < 256; ++i)
+ fputc(0, stream);
+
+ // Resource data
+ off_t ResourceDataPos = ftell(stream);
+ for (size_t i = 0; i < NumResources; ++i) {
+ const Resource &Res = Resources[i];
+ WriteU32(Res.DataSize, stream);
+ fwrite(&fData[Res.DataOffset], 1, Res.DataSize, stream);
+ }
+
+ // Resource map (fill later)
+ off_t ResourceMapPos = ftell(stream);
+ for (unsigned i = 0; i < 22; ++i)
+ fputc(0, stream);
+ WriteU16(fFileAttributes, stream);
+ off_t ResourceMapOffsetsPos = ftell(stream);
+ WriteU16(0, stream); // fill later
+ WriteU16(0, stream); // fill later
+
+ // Type list (fill later)
+ struct TypeInfo
+ {
+ std::array<char, 4> Type;
+ std::vector<size_t> Resources;
+ off_t ReferenceListOffsetPos = 0;
+ };
+
+ std::vector<TypeInfo> TypeList;
+ for (size_t R_i = 0; R_i < NumResources; ++R_i) {
+ std::array<char, 4> Type = Resources[R_i].Type;
+ auto Position = std::find_if(
+ TypeList.begin(), TypeList.end(),
+ [Type](const TypeInfo &x) -> bool { return x.Type == Type; });
+ if (Position == TypeList.end()) {
+ TypeInfo Info;
+ Info.Type = Type;
+ TypeList.push_back(Info);
+ Position = TypeList.end() - 1;
+ }
+ Position->Resources.push_back(R_i);
+ }
+
+ off_t TypeListPos = ftell(stream);
+ size_t NumTypes = TypeList.size();
+
+ WriteU16((uint16_t)NumTypes - 1, stream);
+ for (size_t i = 0; i < NumTypes; ++i) {
+ fwrite(TypeList[i].Type.data(), 1, 4, stream);
+ WriteU16((uint16_t)TypeList[i].Resources.size() - 1, stream);
+ TypeList[i].ReferenceListOffsetPos = ftell(stream);
+ WriteU16(0, stream); // fill later
+ }
+
+ // Reference list (fill later)
+ std::vector<off_t> ReferenceNamePos(NumResources, -1);
+ std::vector<size_t> ReferenceListOffset(NumTypes);
+ for (size_t T_i = 0; T_i < NumTypes; ++T_i) {
+ const TypeInfo &Type = TypeList[T_i];
+ ReferenceListOffset[T_i] = ftell(stream) - TypeListPos;
+ for (size_t R_i : Type.Resources) {
+ const Resource &Res = Resources[R_i];
+ WriteU16(Res.Id, stream);
+ ReferenceNamePos[R_i] = ftell(stream);
+ WriteU16((uint16_t)-1, stream); // fill later
+ fputc(Res.Attr, stream);
+ WriteU24(Res.DataOffset + R_i * sizeof(uint32_t), stream);
+ WriteU32(0, stream);
+ }
+ }
+
+ // Resource name list
+ off_t NameListPos = ftell(stream);
+ std::vector<size_t> NameOffset(NumResources);
+ for (size_t R_i = 0; R_i < NumResources; ++R_i) {
+ const Resource &Res = Resources[R_i];
+ if (Res.Name.empty())
+ NameOffset[R_i] = (size_t)-1;
+ else {
+ NameOffset[R_i] = ftell(stream) - NameListPos;
+ fputc(Res.Name.size(), stream);
+ fwrite(Res.Name.data(), 1, Res.Name.size(), stream);
+ }
+ }
+
+ off_t EndFilePos = ftell(stream);
+
+ // Fill reference list offsets
+ for (size_t R_i = 0; R_i < NumResources; ++R_i) {
+ fseek(stream, ReferenceNamePos[R_i], SEEK_SET);
+ WriteU16((uint16_t)NameOffset[R_i], stream);
+ }
+
+ // Fill type list offsets
+ for (size_t T_i = 0; T_i < NumTypes; ++T_i) {
+ fseek(stream, TypeList[T_i].ReferenceListOffsetPos, SEEK_SET);
+ WriteU16(ReferenceListOffset[T_i], stream);
+ }
+
+ // Fill resource map offsets
+ fseek(stream, ResourceMapOffsetsPos, SEEK_SET);
+ WriteU16(TypeListPos - ResourceMapPos, stream);
+ WriteU16(NameListPos - ResourceMapPos, stream);
+
+ // Fill header
+ fseek(stream, HeaderPos, SEEK_SET);
+ WriteU32(ResourceDataPos - HeaderPos, stream);
+ WriteU32(ResourceMapPos - HeaderPos, stream);
+ WriteU32(ResourceMapPos - ResourceDataPos, stream);
+ WriteU32(EndFilePos - ResourceMapPos, stream);
+
+ if (fflush(stream) != 0)
+ return -1;
+
+ return 0;
+}
+
+static void HexDump(FILE *stream, const uint8_t *data, size_t size)
+{
+ size_t addr = 0;
+
+ while (addr < size) {
+ fprintf(stream, "%08lx|", addr);
+
+ for (size_t a = addr; a < addr + 16; ++a) {
+ if (a < size)
+ fprintf(stream, " %02X", data[a]);
+ else
+ fprintf(stream, " ");
+ }
+
+ fprintf(stream, " | ");
+
+ for (size_t a = addr; a < addr + 16; ++a) {
+ if (a < size) {
+ uint8_t ch = data[a];
+ bool printable = ch >= 0x20 && ch < 0x7f;
+ fprintf(stream, "%c", printable ? ch : '.');
+ }
+ else
+ fprintf(stream, " ");
+ }
+
+ fprintf(stream, "\n");
+ addr += 16;
+ }
+}
+
+void MacRsrc::DisplayAsText(FILE *stream) const
+{
+ const std::vector<Resource> &Resources = fResources;
+ size_t NumResources = Resources.size();
+
+ for (size_t i = 0; i < NumResources; ++i) {
+ const Resource &Res = Resources[i];
+
+ if (i > 0)
+ fprintf(stream, "\n");
+
+ fprintf(stream, "Resource %u `%.4s` Attributes:$%02x Name:`%s`\n",
+ Res.Id, Res.Type.data(), Res.Attr, Res.Name.c_str());
+ HexDump(stream, &fData[Res.DataOffset], Res.DataSize);
+ }
+}
diff --git a/utils/aurez/MacRsrc.h b/utils/aurez/MacRsrc.h
new file mode 100644
index 00000000..3a947524
--- /dev/null
+++ b/utils/aurez/MacRsrc.h
@@ -0,0 +1,55 @@
+// Copyright Jean Pierre Cimalando 2018.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#pragma once
+#include <string>
+#include <vector>
+#include <array>
+#include <stdint.h>
+
+class MacRsrc
+{
+public:
+ MacRsrc();
+ ~MacRsrc();
+
+ struct Resource;
+
+ size_t GetResourceCount() const;
+ const Resource &GetResource(size_t index);
+
+ uint16_t FileAttributes() const;
+ void SetFileAttributes(uint16_t attr);
+
+ int AddResource(uint32_t id, uint8_t attr, const char *type, std::string name, const std::vector<uint8_t> &data);
+
+ int Write(FILE *stream) const;
+ void DisplayAsText(FILE *stream) const;
+
+ struct Resource
+ {
+ uint32_t Id = 0;
+ uint8_t Attr = 0;
+ std::array<char, 4> Type;
+ std::string Name;
+ size_t DataOffset = 0;
+ size_t DataSize = 0;
+ };
+
+private:
+ uint16_t fFileAttributes = 0;
+ std::vector<Resource> fResources;
+ std::vector<uint8_t> fData;
+};
+
+enum MacResourceAttribute
+{
+ ResSysHeap = 64,
+ ResPurgeable = 32,
+ ResLocked = 16,
+ ResProtected = 8,
+ ResPreload = 4,
+ ResChanged = 2
+};
diff --git a/utils/aurez/Makefile b/utils/aurez/Makefile
new file mode 100644
index 00000000..122a2615
--- /dev/null
+++ b/utils/aurez/Makefile
@@ -0,0 +1,26 @@
+PREFIX ?= /usr/local
+CXX ?= g++
+CXXFLAGS ?= -O2 -g -Wall -std=c++11
+LDFLAGS ?=
+
+all: bin/AuRez
+
+clean:
+ rm -rf bin obj
+
+install: all
+ install -D -m 755 bin/AuRez $(DESTDIR)$(PREFIX)/bin/AuRez
+
+.PHONY: all clean install
+
+obj/%.o: %.cc
+ @mkdir -p obj
+ $(CXX) $(CXXFLAGS) -c -o $@ $<
+
+bin/AuRez: obj/AuRez.o obj/AuRsrc.o obj/MacRsrc.o
+ @mkdir -p bin
+ $(CXX) $(LDFLAGS) -o $@ $^
+
+AuRez.cc: AuRsrc.h MacRsrc.h
+AuRsrc.cc: AuRsrc.h
+MacRsrc.cc: MacRsrc.h
diff --git a/utils/aurez/README.md b/utils/aurez/README.md
new file mode 100644
index 00000000..4f7ea048
--- /dev/null
+++ b/utils/aurez/README.md
@@ -0,0 +1,5 @@
+# AuRez
+Portable compiler of AudioUnit resource forks
+
+This creates the `rsrc` file required by AudioUnit, without depending on the `Rez` utility from Xcode.
+It facilitates cross-compilation of AudioUnits.