From 94d1c0ea39d2b27b15e75fb729e3361d813a956c Mon Sep 17 00:00:00 2001 From: mahlzeit Date: Mon, 17 Mar 2003 22:33:38 +0000 Subject: [PATCH] added (experimental) driver support git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2937 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/servers/midi/DeviceWatcher.cpp | 143 ++++++++++++ src/servers/midi/DeviceWatcher.h | 47 ++++ src/servers/midi/Jamfile | 3 + src/servers/midi/MidiServerApp.cpp | 2 + src/servers/midi/MidiServerApp.h | 5 + src/servers/midi/PortDrivers.cpp | 334 +++++++++++++++-------------- src/servers/midi/PortDrivers.h | 48 +++-- src/servers/midi/midi_server.rdef | 55 +++++ 8 files changed, 464 insertions(+), 173 deletions(-) create mode 100644 src/servers/midi/DeviceWatcher.cpp create mode 100644 src/servers/midi/DeviceWatcher.h diff --git a/src/servers/midi/DeviceWatcher.cpp b/src/servers/midi/DeviceWatcher.cpp new file mode 100644 index 0000000000..9fca4f1063 --- /dev/null +++ b/src/servers/midi/DeviceWatcher.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * Copyright (c) 2003 Jerome Leveque + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DeviceWatcher.h" +#include "PortDrivers.h" +#include "debug.h" + +//------------------------------------------------------------------------------ + +DeviceWatcher::DeviceWatcher() +{ + largeIcon = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); + miniIcon = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); + + app_info info; + be_app->GetAppInfo(&info); + BFile file(&info.ref, B_READ_ONLY); + + BResources res; + if (res.SetTo(&file) == B_OK) + { + size_t size; + const void* bits; + + bits = res.LoadResource('ICON', 10, &size); + largeIcon->SetBits(bits, size, 0, B_CMAP8); + + bits = res.LoadResource('MICN', 11, &size); + miniIcon->SetBits(bits, size, 0, B_CMAP8); + } +} + +//------------------------------------------------------------------------------ + +DeviceWatcher::~DeviceWatcher() +{ + delete largeIcon; + delete miniIcon; +} + +//------------------------------------------------------------------------------ + +void DeviceWatcher::Start() +{ + // We need to do this from a separate thread, otherwise we will deadlock. + // The reason is that we instantiate a BMidiRoster object, which sends a + // message to the midi_server to register itself, and blocks until it gets + // a response. But since we _are_ the midi_server we will never be able to + // send that response if our main thread is already blocking. + + resume_thread(spawn_thread( + SpawnThread, "DeviceWatcher", B_NORMAL_PRIORITY, this)); +} + +//------------------------------------------------------------------------------ + +int32 DeviceWatcher::SpawnThread(void* data) +{ + ((DeviceWatcher*) data)->ScanDevices("/dev/midi"); + return 0; +} + +//------------------------------------------------------------------------------ + +void DeviceWatcher::ScanDevices(const char* path) +{ + BDirectory dir(path); + if (dir.InitCheck() == B_OK) + { + BEntry entry; + while (dir.GetNextEntry(&entry) == B_OK) + { + BPath name; + entry.GetPath(&name); + if (entry.IsDirectory()) + { + ScanDevices(name.Path()); + } + else + { + int fd = open(name.Path(), O_RDWR | O_EXCL); + if (fd >= 0) + { + BMidiEndpoint* endp; + + endp = new MidiPortConsumer(fd, name.Path()); + SetIcons(endp); + endp->Register(); + + endp = new MidiPortProducer(fd, name.Path()); + SetIcons(endp); + endp->Register(); + } + } + } + } +} + +//------------------------------------------------------------------------------ + +void DeviceWatcher::SetIcons(BMidiEndpoint* endp) +{ + BMessage msg; + + msg.AddData( + "be:large_icon", 'ICON', largeIcon->Bits(), largeIcon->BitsLength()); + + msg.AddData( + "be:mini_icon", 'MICN', miniIcon->Bits(), miniIcon->BitsLength()); + + endp->SetProperties(&msg); +} + +//------------------------------------------------------------------------------ diff --git a/src/servers/midi/DeviceWatcher.h b/src/servers/midi/DeviceWatcher.h new file mode 100644 index 0000000000..9ca4ebcc60 --- /dev/null +++ b/src/servers/midi/DeviceWatcher.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * Copyright (c) 2003 Jerome Leveque + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef DEVICE_WATCHER_H +#define DEVICE_WATCHER_H + +class BBitmap; +class BMidiEndpoint; + +class DeviceWatcher +{ +public: + DeviceWatcher(); + ~DeviceWatcher(); + + void Start(); + +private: + static int32 SpawnThread(void* data); + void ScanDevices(const char* path); + void SetIcons(BMidiEndpoint* endp); + + BBitmap* largeIcon; + BBitmap* miniIcon; +}; + +#endif // DEVICE_WATCHER_H diff --git a/src/servers/midi/Jamfile b/src/servers/midi/Jamfile index 2687bb8a61..13df9ceeb1 100644 --- a/src/servers/midi/Jamfile +++ b/src/servers/midi/Jamfile @@ -8,8 +8,11 @@ AddResources midi_server : Server midi_server : MidiServerApp.cpp + DeviceWatcher.cpp + PortDrivers.cpp ; LinkSharedOSLibs midi_server : be + midi2 ; diff --git a/src/servers/midi/MidiServerApp.cpp b/src/servers/midi/MidiServerApp.cpp index fe94f24007..6e41b39727 100644 --- a/src/servers/midi/MidiServerApp.cpp +++ b/src/servers/midi/MidiServerApp.cpp @@ -24,6 +24,7 @@ #include "debug.h" #include "MidiServerApp.h" +#include "PortDrivers.h" #include "ServerDefs.h" #include "protocol.h" @@ -35,6 +36,7 @@ MidiServerApp::MidiServerApp() TRACE(("Running OpenBeOS MIDI server")) nextId = 1; + devWatcher.Start(); } //------------------------------------------------------------------------------ diff --git a/src/servers/midi/MidiServerApp.h b/src/servers/midi/MidiServerApp.h index 9454f2aa7b..bc431af803 100644 --- a/src/servers/midi/MidiServerApp.h +++ b/src/servers/midi/MidiServerApp.h @@ -26,6 +26,8 @@ #include #include +#include "DeviceWatcher.h" + struct app_t; struct endpoint_t; @@ -137,6 +139,9 @@ private: // The ID we will assign to the next new endpoint. int32 nextId; + // Creates endpoints for /dev/midi drivers. + DeviceWatcher devWatcher; + #ifdef DEBUG void DumpApps(); void DumpEndpoints(); diff --git a/src/servers/midi/PortDrivers.cpp b/src/servers/midi/PortDrivers.cpp index e2236a8ba1..74df5b4244 100644 --- a/src/servers/midi/PortDrivers.cpp +++ b/src/servers/midi/PortDrivers.cpp @@ -1,179 +1,199 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * Copyright (c) 2003 Jerome Leveque + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + #include +#include #include #include "PortDrivers.h" -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- +//------------------------------------------------------------------------------ -MidiPortConsumer::MidiPortConsumer(int filedescriptor, const char *name = NULL) - : BMidiLocalConsumer(name), - fd(filedescriptor) +MidiPortConsumer::MidiPortConsumer(int fd_, const char* name = NULL) + : BMidiLocalConsumer(name) { -}; - -//---------------------------- + fd = fd_; +} -void MidiPortConsumer::Data(uchar *data, size_t length, bool atomic, bigtime_t time) +//------------------------------------------------------------------------------ + +void MidiPortConsumer::Data( + uchar* data, size_t length, bool atomic, bigtime_t time) { snooze_until(time - Latency(), B_SYSTEM_TIMEBASE); + if (write(fd, data, length) == -1) - perror("Error while sending data to Hardware "); -}; - -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- - -MidiPortProducer::MidiPortProducer(int filedescriptor, const char *name = NULL) - : BMidiLocalProducer(name), - fd(filedescriptor), - KeepRunning(true) -{ - thread_id thread = spawn_thread(SpawnThread, "MidiDriversProducer", B_URGENT_PRIORITY, this); - if (thread >= 0) - resume_thread(thread); -}; - -//---------------------------- - -MidiPortProducer::~MidiPortProducer(void) -{ - KeepRunning = false; -} - -//---------------------------- - -int32 MidiPortProducer::SpawnThread(MidiPortProducer *producer) -{ - return producer->GetData(); -} - -//---------------------------- - -int32 MidiPortProducer::GetData(void) -{ -uchar newdata[3]; -uchar *message; - while (KeepRunning) { - if (read(fd, newdata, 1) != 1) + perror("Error sending data to driver"); + } +} + +//------------------------------------------------------------------------------ + +MidiPortProducer::MidiPortProducer(int fd_, const char *name = NULL) + : BMidiLocalProducer(name) +{ + fd = fd_; + keepRunning = true; + + thread_id thread = spawn_thread( + SpawnThread, "MidiDriversProducer", B_URGENT_PRIORITY, this); + + resume_thread(thread); +} + +//------------------------------------------------------------------------------ + +MidiPortProducer::~MidiPortProducer() +{ + keepRunning = false; +} + +//------------------------------------------------------------------------------ + +int32 MidiPortProducer::SpawnThread(void* data) +{ + return ((MidiPortProducer*) data)->GetData(); +} + +//------------------------------------------------------------------------------ + +int32 MidiPortProducer::GetData() +{ + uchar data[3]; + + while (keepRunning) + { + if (read(fd, data, 1) != 1) { - perror("Error while getting data "); + perror("Error reading data from driver"); return B_ERROR; } - switch(newdata[0] & 0xF0) + + switch (data[0] & 0xF0) { - case 0x80 : read(fd, newdata + 1, 2); - printf("Note OFF\n"); - SprayData(newdata, 3); - break; - case 0x90 : read(fd, newdata + 1, 2); - printf("Note ON\n"); - SprayData(newdata, 3); - break; - case 0xA0 : read(fd, newdata + 1, 2); - printf("AfterTouch\n"); - SprayData(newdata, 3); - break; - case 0xB0 : read(fd, newdata + 1, 2); - printf("Controler\n"); - SprayData(newdata, 3); - break; - case 0xC0 : read(fd, newdata + 1, 1); - printf("Program Change\n"); - SprayData(newdata, 2); - break; - case 0xD0 : read(fd, newdata + 1, 1); - printf("Channel Pressure\n"); - SprayData(newdata, 2); - break; - case 0xE0 : read(fd, newdata + 1, 2); - printf("Pitch Wheel\n"); - SprayData(newdata, 3); - break; + case B_NOTE_OFF: + read(fd, data + 1, 2); + SprayNoteOff(data[0] & 0x0F, data[1], data[2]); + break; + + case B_NOTE_ON: + read(fd, data + 1, 2); + SprayNoteOn(data[0] & 0x0F, data[1], data[2]); + break; + + case B_KEY_PRESSURE: + read(fd, data + 1, 2); + SprayKeyPressure(data[0] & 0x0F, data[1], data[2]); + break; + + case B_CONTROL_CHANGE: + read(fd, data + 1, 2); + SprayControlChange(data[0] & 0x0F, data[1], data[2]); + break; + + case B_PROGRAM_CHANGE: + read(fd, data + 1, 1); + SprayProgramChange(data[0] & 0x0F, data[1]); + break; + + case B_CHANNEL_PRESSURE: + read(fd, data + 1, 1); + SprayChannelPressure(data[0] & 0x0F, data[1]); + break; + + case B_PITCH_BEND: + read(fd, data + 1, 2); + SprayPitchBend(data[0] & 0x0F, data[1], data[2]); + break; } - switch (newdata[0]) + + switch (data[0]) { - case 0xF0 : { - int32 index = 0; - message = new uchar[1000]; - read(fd, newdata, 1); - while (newdata[0] != 0xF7) - { - message[index++] = newdata[0]; - read(fd, newdata, 1); - } - printf("System Exclusive\n"); - SpraySystemExclusive(message, index); - //Must we delete message after passing it to SpraySystemExclusive?? - delete message; + case B_SYS_EX_START: + { + size_t msg_size = 4096; + uchar* msg = (uchar*) malloc(msg_size); + size_t count = 0; + + while (read(fd, msg + count, 1) == 1) + { + // Realtime events may interleave System Exclusives. Any + // non-realtime status byte (not just 0xF7) ends a sysex. + + if (msg[count] >= 0xF8) + { + SpraySystemRealTime(msg[count]); + } + else if (msg[count] >= 0xF0) + { + SpraySystemExclusive(msg, count - 1); + break; + } + else // a normal data byte + { + ++count; + if (count == msg_size) + { + msg_size *= 2; + msg = (uchar*) realloc(msg, msg_size); } - break; - case 0xF1 : read(fd, newdata + 1, 1); //TimeCode Value - printf("Quarter Frame Message\n"); - SprayData(newdata, 2); - break; - case 0xF2 : read(fd, newdata + 1, 2); //LSB, MSB - printf("Song Position Pointer\n"); - SprayData(newdata, 3); - break; - case 0xF3 : read(fd, newdata + 1, 1); //Number - printf("Song Selection\n"); - SprayData(newdata, 2); - break; - case 0xF6 : printf("Tune Request\n"); - SprayData(newdata, 1); - break; - case 0xF8 : printf("Timing Clock\n"); - SprayData(newdata, 1); - break; - case 0xFA : printf("Start\n"); - SprayData(newdata, 1); - break; - case 0xFB : printf("Continue\n"); - SprayData(newdata, 1); - break; - case 0xFC : printf("Stop\n"); - SprayData(newdata, 1); - break; - case 0xFE : printf("Active Sensing\n"); - SprayData(newdata, 1); - break; - case 0xFF : read(fd, newdata + 1, 2); //Type, Size - message = new uchar[newdata[2] + 3]; - message[0] = 0xFF; - message[1] = newdata[1]; - message[2] = newdata[2]; - read(fd, message + 3, message[2]); - printf("System Message\n"); - SprayData(message, message[1] + 3); - //Must we delete message after passing it to SprayData?? - delete message; - break; + } + } + + free(msg); + break; + } + + case B_TUNE_REQUEST: + case B_SYS_EX_END: + SpraySystemCommon(data[0], 0, 0); + break; + + case B_MIDI_TIME_CODE: + case B_SONG_SELECT: + case B_CABLE_MESSAGE: + read(fd, data + 1, 1); + SpraySystemCommon(data[0], data[1], 0); + break; + + case B_SONG_POSITION: + read(fd, data + 1, 2); + SpraySystemCommon(data[0], data[1], data[2]); + break; + + case B_TIMING_CLOCK: + case B_START: + case B_CONTINUE: + case B_STOP: + case B_ACTIVE_SENSING: + case B_SYSTEM_RESET: + SpraySystemRealTime(data[0]); + break; } } -return B_OK; + + return B_OK; } - -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- -//---------------------------------------------------------- + +//------------------------------------------------------------------------------ diff --git a/src/servers/midi/PortDrivers.h b/src/servers/midi/PortDrivers.h index 7b3f06e5f7..ba14af97ec 100644 --- a/src/servers/midi/PortDrivers.h +++ b/src/servers/midi/PortDrivers.h @@ -1,40 +1,56 @@ -#ifndef _PORT_DRIVER_H -#define _PORT_DRIVER_H +/* + * Copyright (c) 2003 Matthijs Hollemans + * Copyright (c) 2003 Jerome Leveque + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef _PORT_DRIVERS_H +#define _PORT_DRIVERS_H #include #include -//---------------------------------------------------------- - class MidiPortConsumer : public BMidiLocalConsumer { - public: - MidiPortConsumer(int filedescriptor, const char *name); + MidiPortConsumer(int fd, const char* name); - void Data(uchar *data, size_t length, bool atomic, bigtime_t time); + virtual void Data(uchar* data, size_t length, bool atomic, bigtime_t time); private: int fd; - }; -//---------------------------------------------------------- - class MidiPortProducer : public BMidiLocalProducer { public: - MidiPortProducer(int filedescriptor, const char *name); + MidiPortProducer(int fd, const char* name); ~MidiPortProducer(void); - static int32 SpawnThread(MidiPortProducer *producer); int32 GetData(void); private: + static int32 SpawnThread(void* data); + int fd; - bool KeepRunning; + bool keepRunning; }; -//---------------------------------------------------------- - -#endif +#endif // _PORT_DRIVERS_H diff --git a/src/servers/midi/midi_server.rdef b/src/servers/midi/midi_server.rdef index 8ce5a02ab5..8597ce7fa2 100644 --- a/src/servers/midi/midi_server.rdef +++ b/src/servers/midi/midi_server.rdef @@ -3,3 +3,58 @@ resource app_signature "application/x-vnd.OpenBeOS.midi-server"; resource app_flags 0x00000004; // BACKGROUND_APP | SINGLE_LAUNCH +resource(10, "bus_large") #'ICON' array +{ + $"FFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFF0000000000000000000000000000FFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFF00000000000A0A0A0A0A0A0A0A0000000000FFFFFFFFFFFFFF" + $"FFFFFFFFFFFF000000000A0A0A0A0A0A0A0A0A0A0A0A00000000FFFFFFFFFFFF" + $"FFFFFFFFFF0000000A0A0A0A0A0A0A00000A0A0A0A0A0A0A000000FFFFFFFFFF" + $"FFFFFFFF0000000A0A0A0A0A0A0A000000000A0A0A0A0A0A0A000000FFFFFFFF" + $"FFFFFF0000000C0C0C0C0C0C0C0C000000000C0C0C0C0C0C0C0C000000FFFFFF" + $"FFFF0000000C0C0C00000C0C0C0C0C00000C0C0C0C0C00000C0C0C000000FFFF" + $"FFFF00000C0C0C000000000C0C0C0C0C0C0C0C0C0C000000000C0C0C0000FFFF" + $"FF0000000C0C0C000000000C0C0C0C0C0C0C0C0C0C000000000C0C0C000000FF" + $"FF00000E0E0E0E0E00000E0E0E0E0E0E0E0E0E0E0E0E00000E0E0E0E0E0000FF" + $"FF00000E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0000FF" + $"00000E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0000" + $"00000E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0000" + $"0000101010000010101010101010101010101010101010101000001010100000" + $"0000101000000000101010101010101010101010101010100000000010100000" + $"0000101000000000101010101010101010101010101010100000000010100000" + $"0000101010000010101010101010101010101010101010101000001010100000" + $"0000121212121212121212121212121212121212121212121212121212120000" + $"0000121212121212121212121212121212121212121212121212121212120000" + $"FF000012121212121212121212121212121212121212121212121212120000FF" + $"FF000012121212121212121212121212121212121212121212121212120000FF" + $"FF000000141414141414141414141414141414141414141414141414000000FF" + $"FFFF00001414141414141414141414141414141414141414141414140000FFFF" + $"FFFF00000014141414141414141414141414141414141414141414000000FFFF" + $"FFFFFF0000001414141414141414141414141414141414141414000000FFFFFF" + $"FFFFFFFF000000161616161616161616161616161616161616000000FFFFFFFF" + $"FFFFFFFFFF00000016161616161616161616161616161616000000FFFFFFFFFF" + $"FFFFFFFFFFFF0000000016161616161616161616161600000000FFFFFFFFFFFF" + $"FFFFFFFFFFFFFF000000000016161616161616160000000000FFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFF0000000000000000000000000000FFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFF" +}; + +resource(11, "bus_small") #'MICN' array +{ + $"FFFFFFFFFFFF00000000FFFFFFFFFFFF" + $"FFFFFFFF00000A0A0A0A0000FFFFFFFF" + $"FFFFFF000A0A0A00000A0A0A00FFFFFF" + $"FFFF000C0C0C0C00000C0C0C0C00FFFF" + $"FF000C00000C0C0C0C0C0C00000C00FF" + $"FF000E00000E0E0E0E0E0E00000E00FF" + $"000E0E0E0E0E0E0E0E0E0E0E0E0E0E00" + $"00100000101010101010101000001000" + $"00100000101010101010101000001000" + $"00121212121212121212121212121200" + $"FF0012121212121212121212121200FF" + $"FF0014141414141414141414141400FF" + $"FFFF001414141414141414141400FFFF" + $"FFFFFF00161616161616161600FFFFFF" + $"FFFFFFFF0000161616160000FFFFFFFF" + $"FFFFFFFFFFFF00000000FFFFFFFFFFFF" +};