From 39f645b50ae98ba197828d0c348b180852fcffe8 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Tue, 7 Oct 2025 17:48:15 -0400 Subject: [PATCH] Add dlog.c/dlog.h --- .gitignore | 3 + README.md | 3 + Rsconscript | 4 + rscons | 510 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dlog.c | 34 ++++ src/dlog.h | 9 + 6 files changed, 563 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 Rsconscript create mode 100755 rscons create mode 100644 src/dlog.c create mode 100644 src/dlog.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab65f1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.rscons* +/build/ +*.a diff --git a/README.md b/README.md new file mode 100644 index 0000000..db78919 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +dlog is a small debugging log wrapper around `snprintf()` to accumulate a text log in a RAM buffer for examining at a later time with a debugger. + +It is useful in an environment that has a C library, some RAM to buffer the output, and the ability to attach a debugger, but might not have any communication channel (e.g. UART) to actually output a debug log. diff --git a/Rsconscript b/Rsconscript new file mode 100644 index 0000000..cca36f0 --- /dev/null +++ b/Rsconscript @@ -0,0 +1,4 @@ +env "dlog" do |env| + sources = glob("src/**/*.c") + env.Library("dlog.a", sources) +end diff --git a/rscons b/rscons new file mode 100755 index 0000000..507c681 --- /dev/null +++ b/rscons @@ -0,0 +1,510 @@ +#!/usr/bin/env ruby + +# Copyright (c) 2013-2025 Josh Holtrop +# +# MIT License +# +# 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. + + +BASE64CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + +def base64_decode(s) + out = "" + v = 0 + bits = 0 + s.each_char do |c| + if cv = BASE64CHARS.index(c) + v = (v << 6) | cv + bits += 6 + elsif c == "=" + break + end + if bits >= 8 + out += (v >> (bits - 8)).chr + v &= 0xFFFFFFFF >> (32 - (bits - 8)) + bits -= 8 + end + end + out +end + +script = File.join(File.dirname(__FILE__), ".rscons-3.3.0-135042e93e2fe1ba463a6c5bcec8f8e4.rb") +unless File.exist?(script) + if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/m + require "zlib" + encoded_compressed = $1 + compressed = base64_decode(encoded_compressed) + if ENV["rscons_dist_specs"] + require "digest/md5" + if Digest::MD5.hexdigest(compressed) != "135042e93e2fe1ba463a6c5bcec8f8e4" + raise "Hash mismatch when decompressing rscons executable" + end + end + inflated = Zlib::Inflate.inflate(compressed) + File.open(script, "wb") do |fh| + fh.write(inflated) + end + else + raise "Error expanding rscons executable" + end +end +load script +if __FILE__ == $0 + Rscons::Cli.new.run(ARGV) +end +#==> +#eJztff13GzmO4O/5KyqVdEdybDnpudl9Tx237dhOt/ecOGc7vZlTNBpZKtk1kaq0 +#VZI/1tH97UeAXwDJKsmJ09e77zJv2ip+gCAIgiAIgk8eb87LYvM8zTaT7Coq5ue3 +#j4rkP+ZpkURxlsw2L2ezaWyThulFUs7iR5N8OB8n0Uk5yLPyURSp792sTMVXFA3G +#/bKMXr2KymQ8wpQoOjk4PTiLtqL4U9J5MYlV6jAZRddFOksaab4erU2SsuxfJE2V +#K/Lz3iAf54WoKFtr9afTcTroz9I8a4ncvmhTFjF10pGp1srS8bbJYOBU3W3RsG0u +#yYbmdz6fAbpxCC4BOZgXRZLNLJrQT9pkUgraDSvzVZdbSX9wKeBHXyZfSC62Omml +#Za+/3TidFWl20WTZUTROs6QUgCetUlBm1og/ZfF6tPEyWA6b6V2ns8temg2TG2wR +#ctaj9ItTAduGvOgxowPL590XBVl/A3UkYZ+vUtAl7bIadPS85qAjXm6ohuhUGr1S +#5CrT/0yijejlip13B9fDorrAMj5aqZMw9it00k1JxqVLm0G/TKKJk3h9mWRRWwzB +#zzTV5XCc4T//5eXERUVWvyiSJPt5afWfKqrfJuNxfv3zsup/qah+Pp4nFPuK6v+j +#ovpETNVs1v95SfW/VlQf3PazFVr/l4rq15dCUi4n3b9W9T0fD+nghaq/rB44qE4H +#r6J61cBBdTp4FdUrB05Up4NXUb1q4KA6HbyK6lUDB9Xp4FVUrxo4qE4Hr6J63cAt +#Y5uqUSuSMpnV8ExIznjygX3zr9XEYIX4o6AcIfTN66JqcrJiT+jvNG9JpUTA0GBt +#/rRIr/ozjStoMEFdQiB48uH133rvj3bP3hyfvI22/k+0ORGIXn+ZlLflJsGjcfDu +#9058dnDyNu5GW2I8b2ZJMYmb0Y8/Rj9cd0bpKI8Gl/2iPxDpp9NkkPbH3VaaDcbz +#YQKttspZf9YazW6nRHdyCCpKzWa324EO61/yL/wX/m+0PsE/vsonVbxdq4whhP5s +#VvSKpD9MiqgtsE2vkt5Vv0j72ay0BfqDgRhcwSrt83k6HvaGaRHK9LU7p0DWm11C +#Y6Xfdjko0uksVKlMx5JXs1F6MS+SUJmrpDjPFfFgfIWulM4EzYUyoEi244IRM2lW +#zE226ZhIx8E9Od07fnfae/3h8Gi/t394Igb6y5coxnJ64oqCrWEyTgTj+eX1uO6Y +#XgvQH2bpWFQBbhHqSo8TRJRVtO9dFPl8ChU6XTLSsnOzfvm5l9wkgzkMY296KVZ+ +#zSU7oUwPQjHPGgUyBlJ9HUGKmZkNe1PBtJNyPSov8+seJq9H/fFY/0yy/vlYCCPN +#I7qTe2LSJ4LBBVtng6QTj/rpGGXWZCKgljBLdFcUkrQ5GAsnSRd1GxRF3aRHZvr6 +#hUEPhkn52IVvJ2NsWCJmsmAAXeoF2nf7quvjrOoN+7N+3O3EbsW4S6Z2qFvh9mpm +#PzA1jp+ofIo/WllybakhhnAueHqc94c9Mr1IxRbkUU6gGyvAw0XDNi3hKxYxtTBJ +#gpI5DcM7FnSRzOZFFr3wYMJO8RZLq0FqjPpCJDbreJtP43TkMVIrmUypFBVEHgj4 +#RQ6yQ5D7Ki3ybJJQUgsoZwJKC0F1YjFj+vPxjA0g5JOcloQqcUtCY0ZFu4+iXjKx +#h1l/InZ3MuuL26YpsUKb5tcLTwKEh3eejQVRoh2TrtmRksbnXXe3mdyIlUQMzONG +#gM/Fmvr3xqfnXzaam1QBCEwIr7LeL6/HtGYJy+so5XXvFjWwLb05Gbi+wvMo4tuN +#1lrz6Wao9DAt4e96pAU5DJXA5+nL9ejpT6yGj3eHVuoaSaeBIp03GKG5cgSWAo0u +#9C7YLZi4XstWGqqUThtxcFU0k6tICkhW90MB6XJCjUuQ1MAiKwBHAfAVCqHPwlxQ +#2LJqFoXELp0wKNlgVdMaSBQ5a1xgZfBWwXDNoI1HZTIzD9jrlKL7tJwNhbK7jrv7 +#9Sh+gyAjVStqPLlLo+fRy8Xmkzu3NTCRLJrteF3tNdbRCmGHWoFuTediKqHCoqr2 +#ZnmvbKgPV82mxBqMk36m6Qs08WhDM4WcLS6SmZb2VBpCuu2+LF0kk1xoqTKzIf9Y +#5N+IvgLOZauY9EZONhl1BCXUvWQwy4s0MY0LdXIW3X3pr0fnX6JzaU56tfVL1Jdk +#s8jpurcV+Jn8hvllscySm5mWtoCxweR2O1RcTJjGflq0xCqFuNoi0UbUedZ6th49 +#a7WedZvuYhdFUK2YiAq0khj2wTyJsnRcu2zI/iC/eUMsZNJs9WF2C7YgRVsE2ZAV +#o4bVxZs+Z+Fs5XuBkOIAU92Oes1at5oyZxocEi0A1kKlR/XnsxzKEMK7OFbv32jP +#avQSszirMmJKB7WXA5vY6dKl7oouBOKzpQBVCNBwM2ExyjridNzgbYlIWrGJHDIQ +#ENhJZx9PQblt7Lh66nlykWakX3pEDFwhr6jqiYwvN8UHRQEGlV+ihA2b4E2xleSq +#J9btp0LdDA2nv/1ZdUsQ0nIeeahwZvYpPs+w5bGezfcQuQj62ySuJ8uSGyEbtr1y +#uJzEJwArzS6iJ3eywCKGiUQ38PDvq8W47NB/Nynu0c7UDZPv+8l9xaUNuUnJp8Dg +#UuHWHQWxGLIkwb/Hj92s1kioPGKotK4acd0RdvDwQ6vzbB8FLXGDiU85lh+cu7YP +#6ldrSEqqNIMOYuPnEpVVQxkls8Flo632iOsRncWk5ufkNlBLpK5jU7aG29VxvxRU +#0nTswumtAlIjKjQMha/R93u0reUbiXQUGCcKispNud33tPtaFrR6fwh7pEBjTQiF +#cj368XycDz4T7oNkwVZFOaswN6s9GZYrx+kgedx44csWyxcWIJgAzdbSNdbZAWhN +#EiGvcI60IzlT9Di1o06X4Iq428UMvoyZLDR+uEnQsBpe1ysnnly8YlZdzPMsy2di +#CRW/xrB7gOw0i/oi9TIpeGMSudgj0/JJuHrbUZYbQkF30ywZBlqcJUXv6gJGZtyf +#nA/7yKMyVVVmnMpy5FoguNVFurS2Yhxch5KiMQ5HbIOmfA/LR68CCJO9fG/rYNTx +#8e6S+W7XsKptfvWM43TkDYldo96/uzv3FXbe/ih1Ol1uwqanMVbxrdCCPNZyOi3T +#vxBMJJhBPgebDZcYlHqyQAX5VpFVzOCsmtsyRk34pw7E4LjvHeFrbY8YCeVTC1lE +#j1p20pGLOopZNh4aPrgRCJUgVB6Uv6lY/RfVViM5O4lGjHq259dTv3mhK8ZXjlzl +#SNXx+W2ajIcRb3gZrivYqKW6BfbWsq0ph9bXLmqWQXst/GRiBxJaw0S2JFYFWDtM +#S65u90MnEkNYirmRzUaN+IeNn/61jCVQuTQvUGHmEBeUHzDTtSfjNzUocyEhFrbe +#DJRYMCxuPLmzxRf8bFp0B/PEvuFzItbU4sKRJQYSMOMWhdSaT8E1xQHoHlgbIrhk +#+CuQIX5ypxtYxEgKiU0VLZbJJr0lrV+37svBmm8+Zb/TaR2emdG2mLTPwnmLZ3HU +#juJ40aZE+6oJonGKrIDQjZAEo70CUg311VRIVImO+lnmm2TyTCSYbTk71Fl6GELg +#2N6pUeRw4bDPVESLuT3ec2aL16A3W4RIG88Tb2HFPDFn3AMZCa9jwbgrrluhTGY9 +#0lCjAgFuhQ/4foVlePwh+5zl15kEBWfOEZuYem3AlQiwUiJGCYBmzYRdnQ8qDyHV +#tgB2RahD1p/bOaW/2mqn4OD2Ajfkfrti61WKDfkP1x1j45QGTGtncbfoxm7FHTLC +#Lhiv+2U6IEY6Qyrrs9AI7atBHAmGEWlCvJwmeNjbkLDb7f2DN7sfjs564INwdvJh +#7+zw+F3v992Tw93XRwenGgSOgk+rx7o9Vs5OEbmyC5lQPvbGt9NtCII5OHZgU+sV +#vRDcLvID5VteFm9iq4H7YTEZQg2hmjcOSSDx2cAZVLoNBjKZPnMzBY4QhYtk1JB/ +#1kXqrOhDolLkNVCZwvRCU9J1aFYI1O0BAD+5gbRgQoqY3PzAYoyOIkl21Ub/bdxs +#io0m/LdLUGyFe0XAcEIIVuyXPSE4LvOh9vwxp1JtBswQbgro90bj/kXZwP+SzSl+ +#y3PUx84JqsoS0iIZjxtPX/q78eKKnupe5wUeuJ1CefxQZ8OszfJzOnXOEGXZ0Hkb +#5LDDNjgoFQDI4ATgwT8wAwY3CJLJ+DZV0AsZmS4qxRXsuMC/iDjJ0IznlsPdNi7F +#IIyT5W3ghmKM3BjhxmPs28qATcYCFBKpg+eHfAVTBGBm+oB7IUCBQ0jY4Fc0JEnT +#QUzFf8fd5eobggbU0Ktno18MLr0dUy32qs14b+/N0e6vp0Kv7CgwSK1uuPTR/vLS +#nAIhKslTb4k9TIAnd1rkxXvv3wvp/f7k4M3hR6Hcer4FcoQ7qtzhuwPA5SnpG4UN +#lFFn+Q9AHA3pW3uMWJW3ZZHns4dAS4NaedxqanzT2B2+21tp4N7vnv1WM2oO1KPD +#10eH7/7ncsiiYB0z+GD3D09WgroEXxjPSZZvDG4vrlN2gSEwXlCFUTwwQk4Zry3R +#yjC/LkMtrQ5lKn0tvy+6QPONf++ve9TVYHZPNZinL4lDUx20cTU0i9TK0KbV0ASn +#rgwOPJiLGa6i2414g20XlpO1Cs7ze8JZcXiSUAU5ebzSAXtBceVpiETRecy1Dq3L +#VKpCqNuFMvDDa6m+uNRlpv20wKVf68lcjRFKIVOx1bnIblH0b5u4VMMyTROpSBS1 +#pZ7tqiGcrrZclbYS2h8O5xOtYknVt3fZLy+jLaMJz/LepV8Ad2lojOud3zZ+bINn +#UpNZQphlsVfOCmkN1kdCt5PzfNyMtmWa3AlHqDObekFvK7hTsq5BEg8q8CNAi4rG +#EDU3s8cmDlYhKqy6MZNm4dWdkKEw91hRx5F6FwN/OnFPp8bOzCKWFYoVBRK+iyla +#Miec85IZQpDBGiyXjJz49OwtIo3h6wCsx1VXfhhzRnU7tI1Uz1pGZUwiFFZ7UFvS +#3/WkehvLIck0AorKCFIhCFDKJQYPkwg4W4QZx0RaiUepzOZJBSEttOrZgdzzOV6F +#mNgbTIZ8X43JAm2j05z+dnB0xNYMwMQrAUtD3AQLfEOBAMPgZBiDxXNzgJbOjQGD +#08GC6wivci1RV5yhMaFhzpJJD2sFen543JrmYmAanTXTt3XtrtltyiPMnIjtvAXK +#yjKZUW2kMScWyw8r7NGBtDfezySKcj9WpxNoOmzzk4SuXAx13aVsoc1n5j7TKB0n +#c3AGqrzI/hqME0lR/qxAKEubTEXQoRvu0tIDJpZpf3bpzRXIZr4M5XyaFFotasdN +#dMGoGCHngpJ0YArllPm8EL9DWeiHE8pIsqtQMkzYYBPpMOklo5FYiGoudQn2nRrm +#8m2RxGAPXbEuKx3VOXO7R/WIllBJpoj2UDMFMMFkQ2yDLepcc2WylNwyedTItUM7 +#CtahxN6IoTOHDCqqRsgbLZPK1JMEhGnZWBPLSCGWyamlA3hQ6mxFlPXILcdUxkIw +#q6BCY43iaSQeSbPzj6TaOUcSe9IMh96TiJH8bpAixIMIChTJRVrC2Twp0ghBJPUY +#XWEOBYrXSCq4buZwkfIJObtMy0hZGCfzEv1B8qukKNLhMMki8EURs+4cBygOEDTN +#Zj0BvaeOq4VwB42QHAsaEcuGTVY8l+KBA0D76TIwrHOanqpEDQ5hfzntXE/z7Gpt +#pxBqoHYlcqqJAnIBUt8gdO9EhbYHYeGMVLQaHXccQl7301lvlBeNPZmOxxGmo5KO +#Do5Nn3qgvg77xfCrqafRggt08peeT9VSCPSAHSp3pReTdHucT+Gag9Dpkm07rXds +#x2S1dWQkO0WoTzRVEdQZpdITppd5dqv8aA10qnQoHCafh2nRmza0UymIJlOclnfc +#ZD2IVLFdhVF3XFrXbVpGaYYrhAEYGp/7DcOOdr5VqOIUXXkYnGM45TGn+RRc2+zm +#We5CaNpKZ3dSZFDFwlkttVDB8mI90N/aV1F9Mse9HVZJkIp9u4XUeQ/9dIsgdFLG +#ehmyZVBM2DV77OPigRN6bYd3QPtc7gQ6cg8CipU5ehX9JrbJlTTsybU1yQYpjHNw +#yQR9rMGxZ9U0CVhihbLgKUmUVK9e6YE1bYPWq9KUN71zgBMsIRbPc8I8tA2rhZW9 +#Ipn0BUGyCwJLzApQHxov5GahSEp0K27oyaHglk2rKMgy0XOTR70cQ1MaNjJwnGXW +#RiEKYfVW1wUEdqnYumAm2eGJQpgEVARE0aXbXiewqFnMQEOyDKrddbWweB41AqPZ +#sWQGJ1qwDPUZwFZ/PN6Wbv0J88t5XNEBe+1RVIDtIamjiF5bgvEQL1q3+5XuYYpo +#ZBfGiSXIs+YVUpke6egmmQ129AuLhWTzHL/noDlPBzeoX1bQo4JytdNTVKo0Mi+k +#vbFiPMKew3vgQoKnichZfVAo0UdJt9qK3udlmULKIC0G83G/iAzn3KJ/y5M7xALN +#hkixhetYXLH5lCHUNifDv8bBHalJ+2eZZ/GSIBy/H5ycHh6/A3+7v7T+0noR14jK +#PbP7e//b8bu/9eTBDVRtY4JyDqva2DrXfgSz6hQUU9J+aOMFhKSBF0TDJviOIDJ8 +#ARBGFUbl5Z95mjUCgeDMFb/1KG6pAAQAIA65fzCPESwWdjChjiLWa4rUkJbpgJJQ +#eR1x1LD9aq5CBQDUQ5fmcj7pYWVtj9kZ5/lnoV7qXKLqM4XF3Lsxk0tpkXh5SF6u +#YpohQdHpdCz2UqWgN9pgFPPR4UGLlK0uBuM6lsvL6NKKutEl3gVu/Nvp8bsWGOwb +#EnyzTjukerSa7USVB3EN0USuwlq90s5ktWX303piaJXFz3x7IqtOEff18BWusyEn +#kP3ZMDmfX7hmae1IjBYTc+NNaF7JsBSDq+We2PYO+nPwSAOXdLEAgqzDdqM8g6u2 +#n+u9Z6lX26NwMfpbu78qHlFkjruty34JxNtuGDrS/i7p7X37CldMRI+hs3Ich1Kc +#i/klBPYE5QSK7nRW470e6nmgr/cbd48yHUMQcCtU81eGfnIm9R/GJ2KsxGeSQZyp +#7EIQT3GKUGRBxp8nWF5UnEVgpvxDGIiTSc51SaV9XEXb7bf7f21dJjdyUdXmAn1K +#9n2ZzcRGEN9CAck1t0lSKip+A6dJHu4pzbaeNlAoVopsCEBvBILdRAWSia1Jfxrd +#CeV28CUaDjoxlhFQbKgTbi8q0sEMK3Z9DpdYbgXa/L6MC+NAUVPgQQxcpFdJJpVD +#UQg8XfNRxHZzRjQK4SAWBCg2LZKrNJ+XofLfzvI8S5GuoX1v0cobbWgzc8OnZTNw +#lff7EFVI0r74v0DjgUlAZ702bWNkmis89TJpWu6FeZpWrZ8ZpmTV9DAFvDlicpZP +#FDWUBC0fij8fvoNMUmwLjW6YQDps7yJUkPKBJBTlULHHdjtM9CzxKXWpL0HR0VND +#WL8SkoJqBL7zquiQDa7Jeygsqmj5jdPD/gpaIR2L5ndTiOW3GQNyeu6sDtZw0QV/ +#4BX1o8gJ9OVeRRn0x7D7niV1+hAloC8Mgmo8nmYQEEa5ALeb1XSLdVZdMy7Ud2jG +#CqIkgkJGrgTMSxHDDetJblMV191c1nxg2jR5DR7LbMgQtPISYDWWyOXmn7EL6lIN +#Qnr82LCovmtjy7onV3SC6YAmqpbdq+MnBKN7zIOk+LufMhmDE5hxowvPrY7BF4Ue +#ByowAgo3fmyjjck3ZuiTHXA4CM/0ab+YSffB2aXyNdjsfPr0abO7yYpIxz97f4Hc +#XMC4JNLTZ1Z2Uhmh1s7bcn4OwEUb1kSzJsu+WI+kE7a3caZbYQUgfBClexgopElO +#4rWICa8KdnGMvNGvEbXsnJ6EgLk3CzB8PDYY9lQtYr3uGaD35oC6YDo+TwYMsAFg +#NVFqwn1UYJ3SGrJ7Qz8kklnAEIicXL2AsKKONfN5RMqARd8EL+EBM02ZGh5w7XLE +#zwQtVxjsFPkYPKuY1UxHxjEn72yHq3xG4bjKdgNcQpOikFHy4n/vF3Bc0xY6h4W7 +#iK5B18iLYj6dtSCW1QxCaLU/ZU/uNGTvfr7B+c4/qPf4A025NiBJaKzDRULuok7J +#1ayVruinhlIPRAdyUa8PKAq2Jjcr1xSsbqLK4mCHX9o7J/kwaUdxcR5bJoipp0n9 +#ceYecTlg4azpcQjLGLg1iJvWrD+bh0JjS8cO5vTl5NHAPyxfXnkITBHrqqFQvad7 +#xY53EqUzLLrs0N+kdp2CXvwlz5XFF3vzzMyNK30gThvejjrksxu17UZSAXVraSxI +#TeNJQ2qrLH3nck23vx6tKQmofV5UXF5B1zXrtrBGW7fhsHGQ4EI5/mjhLj636zW2 +#2VgjbYcN73WOkyYNTP5/WXZOpbnbBN8LcRAPxbczE/NLhhmdimYL5si0c50Xn1V0 +#9fjJXd2B0GKz593Y97ULA5AIggsUujrirmjGFFpsSpBiCbiIWQWjBeFJCAcCpyHn +#xid4eVC9JVHP6yTvlrfyBEgUDIQP/1L0LfwneOOpzqsgiDSV6GGhiw2xHmzYUMfm +#ogOFQG47tFotFjLW2QiyCLIMsoIn6tsVL7SJlvG1LSN0vdAmwaAmX0G8CnKcJrOZ +#DLWHTswCYaQKPqeimm3hWWJVFN16fVUGdiznKPw5D1/KqI8em9rIUauw4vKICMAm +#sv1VakryY62KsVG+4xRYRQxBFcTViIrG2mAw0ETQvCMHJN6DsjAQcBK0F+kqOB66 +#SthzUsBEB+2g9marQDHlDLERuHeuALlB5AcDUfWH686F+CGEZWZsV7YmFoG6xh1m +#MCCbSSEtKQkGg0qfO4WE1RBdSSsGQ2lv1OO5puirVxYeQVgUwm2BwHw7eiGWvZcG +#JxWQgOwhkXO0w2hbwLP7dBCDNidGx418DlGpceSTIWjJgjC484TwPs1FMw44hyo+ +#ubn5Ck55/vzPxyuAE/DK8+dfxy2UEN/ILzc39+AYLPxfimeGlGOGK3HM/r34Zbga +#vwyX8svQ55eh5hfxYzwc/AT/8RkGS0F1wzBDl2EIFYZL2GVYyy7D1ZllKFllWMMq +#w69gleF9WWW4sngZXQQdl9F+NvgsA/iZnYlKsmapsC5FGUtDeUa0KplEVIdnjOnk +#7WihK13IQFO2eZm0avPPmCIHNaubNOOkdChVwbRksYmnny825PJvlThVC58LAI5U +#+x/w0muoqtQUWBGxCn+2I1sXQqSp6otnkRnqWj0Lo9Gx/STutsDsIGbVxsZA3tDe +#2Bin52YTZve5HUMq5eysx9wJ+anJ3VuP5M4dgrAIZQ0fSkB5rdzinYsT6ciUp/Ei +#y1leJD28QNnQoCtnrJlLEpJTMqRrXaJBoCH/9KqjN6vy06kyEXeE2tkFh1x7VYuW +#0M64j1adDXuRRIHyJkOqgj/tJs3Z2I0ugNitQay2a1Xea0IqbRwcvyE8+EQ57MJW +#kaDAAt6lGXgbCDkCPwQ7CDEK77pFa/BxJTrunJvot41+Jqn27MO2b4dSXWizYOKj +#fTwKiZ/e7e0tYiv34t7p8YeTvYNTmV1FBlL+bPfk14OzmuI5K75/8P7N4dFBTfnJ +#yFRYmGmgeP9liIPsfk0l0FCtYCByo6Gx9QUWSFmNkFPUszFVMCiBPBxVJcNeynZ6 +#wwkZi2XVQELvvd1fxDJyloXQu//srpzfFb3RE19dBr8zPYNB4B1beALB7WUUnRdJ +#/3OAAt8gOoTW+acRHkKD/k7iQ+i4/x0FyMeP95YgQIn/L0NMwdVlyMeP/1+IVO7H +#0sk0L2YN/aNOfugyvSVShJW7tyzZj2RtKkksdl8pRob3FyIKiyd3uvEFnfpGfHxf +#ObF/b0Vj+GcREowLiAcbTb6vwGCVXbGx3zt8+/745KznCg9W66tEyP4fpIZU9c+T +#I7yvVprwrv6/kSli99YQ/6+TJCJ7iQxRJe4tPUS9ol/cUuGBuFTIDQxwHLKV2O0f +#Xn3Ewyf/8MrMOVPcCiJSsUr21EifGgkTljHclYsCI2NKT4NW6djgu3ZsRc3rG/pr +#fuEdWLVxU0kQRzoSSlU0IKF4wAo8718Q99ogh+hi1DEWm6gp6twHXWXmVa4MY+Ko +#J+PlQWpHcHq3arWgo3afBSK5SVaV+GrOEmrKhPtKeVXNle86/CWR7KrkV8n0o/0/ +#Rqb7vfGkue6ZleO6Yw8jwWstWxQLwkd+y1+/IijDXcMY8IKe1MsNtsrSeQ/rqVpl +#VrF+mk5hnQpDuGf/xlHy+13HNuHu8/cS9YkzXu+MlTcTOrEp72lpQwcTOjG5arJJ +#DzjCxcfgW9Ia9KdCjiZ/aazZcB2zYSrPknk8mjQjnqYcmYObdKYgA0LyF3gIphQV +#XUcOqESsJlega3M74X7YpwLQG+ygKLK83T54d3zw7szWjUHTF/9/+dO/1r3XJXlf +#DioIgPCgyFJKDMsPIF3uhU2yBU04Qe61Z2L2+SFBadS2ABz/gnRdj0z8/Aftko5r +#+FV9SkdBWNivYMDTeVIZ8rQKjgqByoNDJyvV9CrW+ofQ0wA8rnhAKqtoj060kkCB +#V68qQtLWC2bv+I659vEsVLxjeaajKtPjPVqTpstq9jiofgEKBnJVnjxCG3FQWjA3 +#HnZpJoFLi/AqXdcbcjUhaJR2XONClbshRYIrzfLVFOhyxRUee1cbCcMv6TCasWDn +#7oWdx0s6xxWdMbyXy5DbxvfHxRLWvk3G4/y6luYIAElOx7Ka3nmGjXQrnnHkfmYN +#rxpzT6uB+77IBxSqX27QD8b0VCFJDTUIjIrzzT3qW6Wegf8ZVnF5YQ3oNUsyuJws +#lGTmjLhYybXM9dxfwcvoOxyswYZrNJcbL3izJWy6gsj/a9FPqxuwrKL9w793wBtk +#EW0Mog2kVcDiFE42nhS47IEwjff2cKoKeCrrK3TzWtVXLv/434CeveQ57ZVcf77L +#8cYsEVJecNOr2a2ADI6lZ7+Q7DMc47M/3wjf3ATG+OPHP/0g17vrfD/T80Cg35qI +#DYZreIbx/Xa7c6d9Ad1pgytTl3orIa1pLBe4witTTeL1pRCHAIA053DJ8Ku4hL2G +#avlkX8qC4WDhoADYkxris5ef/7Ocj0bpjah58uH133rvj3bP3hyfvMWXFiZirbr+ +#Milvy00IMt0SpTHMdCuPV+jLKIj1kzvW7mKFrq1gCiH9Z0NtibHuprc3cmnLEZjG +#Tu7Rfm0ufYREFjraOAqUIi+g6GJjt9j+/sH7Xw/eSU0r3oD7rltP7/TxwiLurge5 +#VHW4Ex+//rfTD2+gCfTUZ8QN3Tx8aLPNKpIjch6jWWHbEN4CuCH5ARl+t93eOGMh +#TEsp/KuuknvR+FkOvdPngcZBtI8PVOmdde7uS9zW5QMIfD+5UpWO6rZTN3zhxr0+ +#I6CpF2uWiIYv8gUdeItlmg56+ukQUn9bbNGE2ADHtdH7wz2gdP1bf2Yax7sncub0 +#CzVvRMreW2nZFfCe3u2eLCL4Lz6cAj+VmRZ/KqPuomvqyvdVVO1iUJqcU99fSSTy +#pk61kGat7J7KGQy/zVtKC0iXB9iyjHqVRn5YZD0Mocm2vPzotPyAzap3ZhgGkEzk +#2gI/NSAmy2QWxZmIMBjkt2/3o423byIixmxZOwIdm6jf7NFjoNGOSQlj9pUFEDWb +#r2SgQqF1qmErxTi+GGi3shgdpSxxxYCHhnVvL0zfPUvePULdPftrr3p4sWlveBGD +#79D8ww6zxmiVYdaPHbFhDqY5pgaeztbO/djmsWmMlDClF/oTgC4IPMM/tJEeYwW4 +#0f3+5FjQ6bSHg3IQ8UQ9Ju5orTBCPi84LDswGUrPjy+eP49tGmfajx/DXPvx4/34 +#5uPH1RgX2/c5F9H4Pjg8MPdqpFZiX4WRwy4fP7pDNohag+k0gv1a1NrTBfe1zBka +#mcPUTZvIxnRfCiL4IQo7a9l+mKTMuQHLVY/hflD4QKsP1eTDDtn+6gOmvsnwxNb9 +#Jt4PDOb+4enu6Vs+/YUqvf/h7Xvsk8yuISYpYDDcGKZlvyyTCXj9b2zIc2RTw2Ge +#ocnwHFQMlvZtRsk6h7pL6nA7S8cmIcBgeG5r+3e0LwfJH+qHHbijEMGPDlQnRuPk +#JraJfAhEAoIX6QojV7p4I2HKOu0FNmmxzfO2ZmOS6Q+EPfClKZTd+qb+0e9vtTqJ +#V+V4htNfTAuKclW8mgUtPG9SG7BLYD7wuBPoDrFkDiPY2FBczTuZLjavEJ6X5Dnz +#JtdAxaL8K8ljG44YnTHAVqEBnf6mVUGm40MyGxFIeAiFUMChv2vWVoWCN4gKk++E +#xulvDzv4BjjVa2UD4LRmtoa2uFtaFbYFtB7EfMBlhjtkD6QOIaTVhy2sFGl0vh8u +#32HsqMajVTt3QAJ6DSQ6Y7Efnj61KoSoVUfosOaimvradh6eiPsOCffr2N9bwSHJ +#ISUs2G7/KtEWGZWIY14d6kcu7qaRjfKyXyRDUrRmYXXacZdWzCZZXGSjuB6n56x0 +#tYAfQrg9MEaXRt85PfzfyiEbXmqgqZywIgGpIv7WsJ3O5UvZ33b1OnKewmsJJJkv +#7ZACgDFnVWXGFjZKpST9YnlUHIyCY7/ghUZ8k7wqNA6xpUevPPM64hN6mMGJ+ATv +#WKTZBcQoYlV1jBABoAcBd4zhcoeULHvntzreC7FtsiI0/gy5Oq5iuqUkDmc6REPn +#C5byfIs8HrKTupZnHtq0wR7E4mgIKujwVfAvHaGvpBOWJti5ji7YlQ6WNYZw81yE +#IkpG3SJS5y3HquawKdsIt1azKjWY6L+hkGDBFxiTwWVeUaMH79aH6mQ9GTcqEPXL +#9NSJ2KRfb+KvT6Xm4R757hNjDXxVNJuBqwlwU+gtj/5gll4lPVWydCONqnT2HjMk +#dNrmEQ8ZNpFfr4anbwW26mWgsAfIeb808Z6wsP+qzY7K7piy69Ea7VRX+TjSF8x3 +#LOVXCVj15G6H3ftjXCOfbDbNmXdwCFvVX4bvZ0zawEPpkSR5ZEiu305UMdSR1YMO +#I27f6jrmTS/rgWZHBkbbjV212tvUNBbRI06K+L0ME2X6ZcuKr1EuZPRA8DoGaOLk +#cR/10Y+EeY+YyqnDYpRpQZYMe8GnxGzJmndGyStqYk1hAlhRXgVvZABpfHSbSl6C +#chJ7l3n+GdOnRQLh6dajaV7O4JczFvYR1Ru8q6pf9iKPAujzpNcfDo/2D05OrWM9 +#ew8OJw+9e+G+Wqcf4W0BS816EN7UB9CsBCBkWvi9Jx1pUBZ7cucDZTdu+8Oh7iRv +#vz5mxA7IYP7atKWdyKJnnBUZ0s0sMKWukuI8ZysJiwOp6rJ8fJwxgPCOEfvh2ctX +#BcMv+KxcZNd3Iu7N+g1p5LDYPhoI/6jzQEBtUSuIlcxQuQNJ3XuAsCHOKmKmZRVr +#2CqLRd06cQ8Z54WrbA3nU5WHGFKfVUxAZ9W2dSHUpYTYwPeL5avd61ZD0D/lLO/i +#nV9ZZ6sKjlMoEz/8UvKVCvhumuIhF2iZoWIEG6yafEpUiBM+NVCn3DHaDYSmvtbB +#M3E8tiOpcdEYm6aM0Br5tQi30Tb6RthBvOKKDD5gqaERVlEd18/12R46i2RSlL4s +#lCqE+qLiEBTVgOyplzpYSboO49PWEKk9gKHhkmW9kCzjaTKYCuGDkq7TI8ySr3hW +#dgbLNH50y9fqF7pFsSrds0mocu92OUW16tHTdIO1uNG2snOdyNGHFXkkwO3XSbzI +#7HKo7Ktc1IQkBGUwz2g32DOuTBrCKhvMvp2c52NPWPKizJWnYvVXf3DuyeWp3daK +#Qbt9msLFgdc64rCcS04H2EoYxCMQB5xM2Y7eLfqv6AYpqrnMf5GXTht8uTX4jG6Y +#Z2ugwZSoBge7L1keH2Aw12AlraRv2jrvGxn65GZW9HXgBE41m2XnDy0di32MTfCe +#x7Q3eXWMWfLZuijn54340ydwQt2Mgwi1BK9vRz/+yCuKiVrMerCp2UbHVrs7WWyS +#HQxc0yDVFnBrA10gg3suF1DOeib6iRf1Jv3Pidgyj/u4fYWsRnUzzYr3QvVL9DkJ +#pMr3P63aNyFVTbw/IPY3fMOgM5FTQHjAY9YRd9szhXTUYSwEheXbvWGAijOgKN9e +#OCXKEBSrUhKN0lE02S6o4uliUqT6zWLIbdW9jWwZlN9tvL6EKxUcD735BjGo94A8 +#cQk1Q9t3XQXQI8lwozvcODj88iypt8pAkDJbI0fTluEWKuuMY+g9MgdLz4TiXgTW +#byO4rLscNn1mdTXu9apQVOiSWdKYzs4EpC+qenEOl3ZjUl4AP3vFpNnIvbzk2j68 +#kOAhDoImnm9BAOkPYv/7DyWfdCR5QQkVTL4nRFK08eYfcEHoKk2u8Qkx2YLxJMfL +#REU+4c/jybcO82kijTCh4NtVdiiBnLtN4rsykG7KqBEygZAhpPUmyewyH/YmaVmK +#4W7ITxVzkYbNMJq4ufAmi6I2Qi5YqNez1RPi8rq9ACdGaqY2c6Ys3pd0XIIVN+Bd +#UxLUFWQCSfu9D0/X+4Es4w9ZcgMPeeBAiMEv5qjTRFoBhYfe4BqzhOU9+kFHQfYE +#rN03U3Andx/Q0o+k6+AE6tsaXGUCVa1lilmz1Wv06JcuE2TvXrs7lkjjIIsxxbs1 +#EgvnLLHy1j4IYVUxMlRdZCYCWiLR1iPnoSt2d3owSR6unm1nmq0zAXHVRsWcJgLZ +#204EBk8+e+KLW/JAY8NliKTW7IA0VAgTbOomhVNKrZf8oIXZCc2r9+5DG5HzzS+Q +#gtWzZtZK06Z+HUzME/sKoJ17MrOKOQyfqmLyjydmw+zMZi+1fvJHHIGd50N2x3s+ +#rObWOZxkiAIMFWfNkFjMh65Mo1ZYQ3N2RTqQvxU1AqnPI/Km4jxL/0PrJziu/REY +#2zThHbrTMfKLQzDaIsFjylIsaYaA1qTM3wlU2ayOKVQHadkDg9XSqd6KzgjrRpNm +#CFkMaDIVahgh2WZVcwWHLFBn1RxecZUdp6rqNU2lTa3YeyEOKIQae4azyQAtmkxa +#eg7RrJ1vfCtq1XJ4Hz7xjVxMI7cWhZJsKkUR3R9CALNaY3R0g3EQU/gnrxPWECCq +#OBToR5bUSruZ8YdCnWWWUtK8xknQapDfhlrsoAeO8u13cOPuPbtYKTqqcbKvmLLH +#QrVqY20AUsMJWgLy+Ww6n5mtetimsMSagIbUnhhnoYL2uNGppULAklYsenSV/Wpp +#sPpEokgEeu/RWU4Lud2nQZ2q3rDjgZ+I+UQFaQfLRjCMCOboRYzHp4oqsaiMCCGf +#g5yfN34o7v7+Sfyvsb3VgVchIXZT3Sluk9SidYiBpFlhvgkJISVVxB8jY2lf1G/B +#A0UykqX8hW1apNnMqFFil6dDQWguW4/w+EuMuXzISHTIPJVrDingujA9WpD3dN1z +#tXSkK1YFe7GhRtxdV+lf5ZSDziH6cSksyMAxn4wZwXu3AiSvToBjJAn40WENhCiE +#iid/05GGYZWAfNYf90IGH/nJLcWzy7QkhQET5IBRI/5BCGsXFm6QF3CdXXEDsf6Q +#2QVRtRK0PMcdAYW3ATY+D/CiazdcofAka2r8x3khMOhN+sXn+RRMgrotuG+tg5bE +#Tf7gU2iuaA4f5UVwGXZVfB+AmEapfVlvte1IVSyQqs2L0pj0+WC95F3na6I/t/Gd +#zuRmRgjP+4wsYPY7bAUUgqDi8MznBFwzqhtD6aBMiUZngTOYhg2ehLMFxYFhC1nD +#6kNojJolEyq6w0bUjvoKq7bLa4nh1I2x3a7oRiAdUT+zbzuaVpTZsKPrhLaNqvqe +#I5oYllXG5DCaCO+114x319sYS4pkkF9kYpYNrT8HKhULY0ZQgSagJTCc6FYDxpMq +#oyBixW+4++Z20aH4jbSgzXKlSFqU1JPz5rE6BMnu4+uSdFZY/iGplIUc+6TzODyp +#tI4+zujfgyYNqoDyw2/sHUNCnd8H1NoHOrB18zBMUt2xtzWVq3eLYVDdCoxpvoJh +#MMoacIyayy6/1BzfMLQ0wxmRHZzAsNL1vf3TOk6Skm2hZHFzXO+CR+7CImrc/AJO +#4ClZ2jdahfFUUL3RqTnyCRitlm5Q3YMQ87QpRsUU/0nLS7GwyOQGZWCwN6nnT+Gc +#EEzZDeeIZiuyzk/0rIYdjxEDVW3bKBiavmSoO2tjStGMi98QD8kitpF5htATswyT +#/bRQEgxZnGo63J45RsAIwErSy792D25opp8Od4ClIx+eUj/3XG033CuvOq3gwzbB +#UtTw8jCIxvJeVZEtIEtPV/CZOg+Sq4WzAV+yFHhj5i0K8p9jyag+qSIsxPn7FWHv +#lc77vJMkwh5L6/qe0pqjNNVEsW5gC8M6Wn2ctuTgUUlrqvNVVjW4+jZbpUELkZOO +#lbIPTsF2fuijZ4j3Tc+e1bxYj/Lzfza902cr+gLzssrSXXngS+eef0qCZTsvui2u +#zYoM2f1GuI8BR9qWCgSqPMmU0xzNZHLf83FZbdlfeeF3TamBIa9RFmomCiVppdxe +#EnEtuCZkeea6leokX76iN78JZyZTKZ0eK3nXH6dXyXaoh1TTwb0noE4Zbo01tuJW +#M7yKECLqJaNK6nvlas6ZKpYhP0wSd4YjV5tC8ZOkT9kpmiQQjkz4dZyf98f7pXaZ +#q32PHNRhTIj0c9geY9ldsb3tEN3vSgRueQVajbUpnIYWNHKXuU4GljG8kWGVBVW4 +#SjqpfPe4Q6a2wCKrPKY219ZiOuMMaPQn2KzaJFk3m61oPy1a2AVVs1lptIyi96on +#aClAk6RjOWziaX+GWW7kL74etkprpeL0HObX2TgXHDwvxN5nmJQVrwURZ2NBRrzg +#2hunk1QZlP9K1C5+FUKXTlikPEFgjLGY3IjN2HYD2m2Cu5qNkHvZ/+mv/1LOJ11G +#E1FvP70Qpdvt0992f2pdJjdD/G4guPM0w+mI8Jrgnb0MYECXcEcvrFuInhYp2DFP +#DoF2RO8sk15ZjuFwtBhzl7vL2Wxatjepr51gjanAL2HuqXAqL7QgwSCFEDHWqBhb +#FpO9jrY4OagZhDtoRdG7RJT67ezsvcRJIJ22LnMYb/glX2JSuLf1DzlRAG3OmjY+ +#puSYqhdDnB4CIFh5sW2YsRDJT/yWVeGuklsZA2pK0yFkN71sRQd4BVfkO9mJo4ZW +#j6uKP3+KZEd3HLjVSjXnuntjZhbJR+yBJgvYFScta8Gs2llq8qgFwozSiZo3xFlX +#1aiciE7Aw0qcNegI6wkMQNEfSntLoCdM5vlmJqloGDmi+9OJxUou/Z+6jmBRuhJH +#v13drw14s8HKEWWLqdJ92BSVwqiKxqcyGLnvYrT6MFcPbEDyVEi46PGW4mMrz5aj +#RL2glLtrlGe1eHHRn2RX4Qs+wWVZvRKo7wZa6GJeO8FOq6CG72vN+uVn5XMHNxan +#l/0yoTaWpMUdfp3RDnZt2oenSeRRKW6B18Xm5XNSwuUUZEZ9AGOROxNotNvvoSL2 +#4R6V3bZnlxhcOM/YzeWDd793YvnYTasU3ZdLlmhSJPVOD97vnuyeHZ/UwBS7Bry7 +#4h+S4o2LjlpQnbNODOrAUappQ12PqWjCgSMgVzVa0wQAgntCvA3Q/lVi1dmhzoel +#UhdFv9A6OnJuIWNAoARxLZBTsTuOo2Sk8jQyHgmevuC0C5Xxeo8lpIjLi1v3KJy9 +#mY3YUDVGVANPG3bbgMtpUjveGMWo9Op++ZA0Lui/UP0Yk6PExUL0wA1tSR22anCC +#OQWDsAnCpDIws2sikQfcEnH4rxCmcSsGUau+PNqDEj69JpswjNgdy3baQpoJxQtE +#vBkNeBgdMH4WS0co1SDVHuTZV3lbzpIJeYtncCnKtQFSVWtHSf/qXo3ZNU20uXyZ +#eMM8lttRbEloHxxavliUl6ZX3paAx8QgR/bol0QcfAm2tqqxQErD08bLMLOJbs3S +#bE5ufJpLkzrLtbtKsGDEEJrRS+AIlQgGoAohY/nOlq1i/pUuH7uo/EKcTw03rOAM +#EVC9sGoAzWp9yN0TePzqXQFNMMFTubS+PBO1+sUwqDGTfQuowuf9wedZ0R8knZ+6 +#rnbs0kuOJ+us40SAb0iBpriu2yHvhbF7ApVHo57vvYSzXK/kMwP0lyqNB8e1ugAH +#pAJMNKqf/A4qZsFqtS30kgzc5IfbDZ3AgwVUN1JfM9zYRZHPp/dQNFeoxxuCzbUJ +#iNJYoQXZCVOlrmgdbItFx4BoD8grju3B5ST3vnsnLEVsKdxvXmLKPnrjgn/TTzyr +#GY9JyjhjH73S+SSO++3JZ7HMuN892vzkinyIFZR8FRP20Rvxz8L55Nm83Vk+H1zq +#b2Inl/cbrIVAPqDUk8nqooq0JCimWVv7fE3ZhxoXQCmA2Vlar0t2K8avXbdG6r/S +#dHqWT4+SqwSMp9GroCF1KkOu9KSCRT48g2qL5qI7tf0Mzob+fJbDLq2hf/ggdQ5c +#1VE/a1kajIxkfIZC23PTzL6QlpNPDdTy5zxz0rzxdiKhhAZdUo6OvD/eKIRJcTSY +#mrG+7wjrp6WS6iH2bOXrNrBOLyfOdTJmjmdPp4VRG7GftYMlt+nmdZt1L4u8buRl +#DmvqjS78tN4lBsEKNlKRZZ59dzPgSWc3TT8F+oDMwbzLKFVdQaDjV/GtX5UWMJiX +#s3wiLx43jEbi8pV+Q03qMVpBei42LPSBUx3egKFX9fClE6jMkw9OPpvwnE9VAztE +#PJBzPSLUUFvCADY2+EMC708Nc9MDnG8xignnIRm0oIh9yAl6Oaij/34xS0d9cPxv +#nM9nePOABbJqukA0km3pM9KkbYftV4BKUClWqGpaV6Brpry664lkXoKVk4tWKbHl +#60jzVDwtBL/iW2Gb87LYBHvpWHwB0UXaKVwalMJRvmAni0cNJVfbka3VjJvkBT7Z +#ISOmV6G/3X3C5oeR/pspbxCpob7qaAWqhzJXUx08RkiQsgCRYpcYZp2paOGDzl9x +#ZJf32rS4wqIiXxfuc7tbAGYpJHM2s0ZYPkPlEgK5MxWOEi0feCAmzT1C+0xEZ4tz +#Q58VZnXDgattRy99f2SDWCO01A2GeCpG107TZnhxtGkwiqIuGGlJMDsTy44XbMkY +#J8Q1VCaQ588EKqSXN8mgwUGsuyBxyvbQHOyHgfFP4KsP2qELRJPgIUvvFVMzlXtP +#vBRafi6daJqa90nCivvgGFAEKxS6fD6L7MuvlVtjDhjRWbpimlBoUJzPV3DSFzTX +#sWVIN/ytt21oBz/rnpDSREcrvyoRjuXppPtXNli2OR6gmXbNpU54jmp4ryMKHV6T +#8cEOVo7cF493DDAkIMdPZNObKVtR9ZUUpmbo/qr5Fcig2kUtBcNkZ6lyuaxIV1Mx +#oMZwQ4gXgDWs3ew44SftPWROKHtGHxiMHYkxD/FCkGUZioa0LZh34QiBQpSko9tG +#sE9UAAvZIRuke0lMQTdFRK9jS+iGlUl5SqZFzau67Goo1gFPBRAYFvLimXdZxyGG +#hwa/+IYhOHQ99TnkYwZeVs7+9V4nl5VFtZdbIAAx6QzlGLvS2AuldrXBVctmdFUz +#vJPcBU8y11ZE1ro6vAOKAb+LEeqrQxDuarZ0CY0imdKRyzcd2pDBPKTyhMigWFGP +#OHHlC1IrwDSmjmEbr2TddGLvftuAfwFrCpEk5lBfp/lxwINQQbgQiFzWGKCQvDJA +#DDXBYBLB9lzHIyCwsULgwDYIX4oQC96BJrO7TRJQAFL4jS4ihcwSz0UP3Iee9QhL +#NWxpxWrSs32pLyt3J5Wh0B2Hheq48HKUlZzGMAL5gOv9eqfO1S6Iiuj6LNQqTLQR +#jnH9BApSqUeo1GNUSkcrrAURpa8emR5VLlYQ6IF69bqx+gaLbK1y3D8v8zEEZ4I9 +#iHtIDkdydU8DE4rLk+j/E8FV7cZ2+9N1u7mNt7UXVWMk74RTb77NFXxs3Auu3hmT +#vfCL2P690VrbbjZedf7+S/f5L03x1XzK8MbtLexOhiakgCD205fr0dOfxP//Qsri +#fUYoSNLQxyt+Jb3cf+GXKqCogNWeCGQy5kunasmgAxW1Brf9zK+CJ3IVNTCvYoJE +#UYd3dc2lpOx7s6tOgXuCkHdfrr5EV7hsxpXj2FHkrluk9NjVncfqQ13w372Lvgy+ +#RINOHMVdePJDqEID5az7DMw7zz6Jf8+ESgSH+u1oEC3s0Xuw6SFoVBOwZbohppFj +#0I3l5BTepO29O/vt5GB3/xQ8WpCBPg2fc55RHnpPX4LNOw102z8XRtY5OZdb9XZb +#NPTm8NdODO6ivbyMu+5Ab47TbH6zyYZZNWttEPEmCMXNwXSeZqM8brbKQT9rbP5d +#icq8+FSutTebeEzuNTApr9PsS3gyI03+cT1JB5EADheVo3fzyXlSHI+O8guh74zf +#6ybKaANF0j+QWJWltpCKvI1KQrrE1CgP+4V6bpgjKrAfzMbRRhZdXrcygbFE5tua +#9J1ZgwP9MshuMi7BKC8m/RlKVun3xON3YYp1p3BEowqrWDXr4GK/LbWIGs91AgLc +#iF4umiFHII4n3iORWh2c4Db4iZmykHSEYO7ClUX58elTHHI8h3dptiUANHkqJyEN +#etuBTcbCscLULOvo7iZEaSH3lPdzAHQB2NsvJvX2i8tbysEKX0jHYnBFhRDMVpXx +#gF1/atVF5knmdnI1rwjFTMm4Py3hzk86SRrqwzZ6KRYUfOhE5USbUeNfXkRr0b+8 +#aDYdaaVKbGypWlAI/2OKCOEwlzG3CDwBqRKQrsCglIlQSTDgvyrZGiTEMd+4ehFf +#fEF4iZPz0oi59SG4HwssLqOwtxuFAAHsNWY1AFWRxaQCJCusOrUgznbMf4yPXSBW +#r6do1SpihBeVivXputlGZWbB+CruPbl7+nIh/vMTjzHgP1wv5UV1JAJ/6i3l0Snc +#3KMhoEAoNCYjGczIdkdFyyNaPh6azOFc9tkzk2hXOgMC90E9sTgmOHPhh3PNCPO0 +#5tcUakK59pQvArotMZLPxP+ei8WgjlJecWjBlRMGf2i48/d29/mnxqfT55+asAQL +#DbSt0iCx+clblZAgQjl/yl3ZZLwdBru11o48JRb+CRIpsjpAFHSBviqiZKXoDF6W +#avzYLoVAnHLR5d70CA5QtV8ctBNkETw98h2ygr7tdTabr7PaOCKCHIK0wiZ/NJvX +#WzeC1noN1Tq+V9fmONV3Oh3xs8SesZCu9myRLB77pxrsPEZ6Kz5uhJpa5ZZI5fmC +#d0XUecWAhCYIBIdQ8TTe5boInFvihVkBMiy0/2Oe4Kb7f8FfdpvLtGJiRXp3YM9U +#RIfk2jNSOOq9BYj7EGcmsejOBK/WdF5eeiEO3C5Uk1kBCfivOLstG4DZ3W0J0rBb +#kSHX+2aLLV09iM9sXL75PUkLNW5tij2b/FzEJEwC+taCRulW8jpBQ0Ap4bGKGmaT +#CVeVpLTAbtyfnA/7wWuiRJnFNTiszNb68Nfs86QFBbde6kVLugWj+5GKGwyylw63 +#0K7ZjxbcroKNJysMLohWhYZhVF+V4EkNqzMH1GVzN0PtTSQsCGYmf8ROaUwFjOFv +#GFkkQ6OhStLOQWxrpyj8s0XBRdqUXuCj0PetIxjz3nXO+7O4KZmGMV0Hb1t4kKr2 +#B/4CXCMSfE0tOAgWSzoSBAMgdh3SVbbT+vN5GXUcizvniirgg3XH3pnc9kyiThvk +#fZ1oFEZ1q8oGhXDO7D4nhofh8EeBtWZ3km9b7dAXHdnZjUbBLhY6xVE+TcFwS15r +#eFKcTHtCs2roupgR3CiHEPWYgfyuf5AMAnYIaH9mWql+M8p8c7873S1oCZ0EXMbT +#gyKyvHohNJcTjAbyCNPJiNQQnZYQqf40w8wS0OmsC1D/scVe6Xvh9wFUJjhxwp2g +#/uOmj772kygDz1Zd4LNVumRTbJ3MR5AkdvabScFxt901pxjsFBLv2OSzS4xQY8UK +#6bKMLRO5D62x3MoHuDTy62T0BJJNXltRHdHgwklIvX6pnGkj+breetRGrE0feAha +#+Wdd6SvsJTi0KMt8lYJGUhmElWojsgzZEj/9dNfo/H3RFRvRReV5iKhF32yqOhJR +#LpRbDt4aCsXbOEVgnq/di6rg8uqC0mJKIdQMEoMSRM9dQhQeJlPRReFRcW3NYiQP +#JL5cXX3RVWTC9AuusYsnd1dX5KmlxcJ7W8Lj9ArwGKwVWvBg1mgCZNTXI/kQmvZ4 +#lS6zLAzGyn2v6iriXI2a30vWKbd2nYZTGX7zcyZUxYqHSma300S9VAK0xQm+iBr4 +#pAym4k5HR1WEC3ZP7lwOM9nNFW1jbBbSkgHmk2VNkJokmwUCa+sA1NmsgtsT7wUT +#yQbLht+XF3BE8yjcvPzTWSMYdIP4hINerjhMQA09TCbBG4HQ8iZ2nlppNkZks3Do +#xcSs8EZiq6BFsgoPXOSvwEybEVvPtMAKWn+YM+sju0hcq9ZAAU8BdBwwqvWboKWZ +#qypIPlUC6NSiSbSoXGB7/VljjUbpwvBgmknFh+0mLJwd/vY5A+oGbiaLrw6ypzUm +#ZwmwQzTPyst0NPMGMbxDCFHAEBUC5NEVU3zXLJcilzyIG5i+UEAdUAuBbYeuGS1o +#JbiczeoophM6CY/j91mwUZPykOYNzhlNr0RIBaTT0PZztb2a+4w27LawWltFWFvX +#X9Nb9XNfWybMdymmcTIRE1ylHNKraPrLrXaU3Ohf6XnRN+nH53BtQH28LxJ1qm0S +#yEWm9ullX1CY15dpDMqp2HWqn3/rDwbwE9hY7lOJY2b0KjqZZ3DEhp8/K9r5PjRK +#y9YGaIfP/ZyIJQoa79LHr4l1khZr9bwrRcRjyG2EzgL+FgQP4s6eR6IPqLp7RHml +#vVdeJubqxQ5+gGO4Qdi1iKKpThZj5jbvyljYmBqXMpJQDCaWw2MwciZZo6NuF8b4 +#2rkuoqLPpbkf6yrN8fyohQccTq5rYHEO+eU/fnzgV+Nf2pPk9LeDo6O4i1YglgCh +#NGJMtvTp0BKibxsDxzy/pIA8JJJ8Th6vj5nnVtys8d2SVneLUFxexiFEBCpVWQHF +#rxMPJkO8i+UVrlf0KhoJrYbM+VXZu5Wx27w74KYztv1GyskBJ8E073CxhOUTa709 +#/dvp5mIptwswlsfjJLuKFY/DX8Lmd8DknKkXaIPUZbwJIKG5w7gC69eeQMCJesd3 +#MncpvdWgJhZvHFwzyxJXRhWhX5oOVNrb9CbNtDFBpe0nUzDfG3yVnwuKTx1QGD0O +#4TCUlTQH0zvq6T4+0jQ0Dwap7cS9/YP3bw6PDmLHYKdPXDFcRfhUvBYCp/4OPhvo +#Btrf0S8r7ZhgI3i6C0+2iUQx7hQiC/v/dYZc+kQCvz8iVPakgCc3xhrKvUdNzkI7 +#L6Xy7A1myI1VD7KqM2x8pg9ByX+YZC8kt5MbsXUSFNuD9LdokVnpZFNhS6s5WAg1 +#5So1PTZDaJL5q3z+YLObXP6Ra2QbQA9qe6WnHnHH9O4Dpm8uwj8ZNdYcLxN7mUHA +#mi5pYb4K66mkHwk2wV/XgENbfMdJwXTa0kggVjycKpXhCY2+RWtz8ca3pGk2ncNl +#G7IPdbBc9koHkWZCNZqKjXgSbbEmwTCME7wtz+9cCcx7qqIzKphLV1ZeOVyLY+s/ +#nrTlQPFKQBhauKQN0QvE2kdJsUMnvAeJZoYjPanncgJMCGQLhbtfVX57xNJS9mz3 +#5NeDMwxSt+O9mWiLnR5/ONk7OJXl/EUABTs0aYyiASaOn94pmX764c2bw4+LmL8q +#x1s04t9hoAALwYA8Vth7L88DHvZNNPb6vGkSyriP6ZlVhPVsncx3F1CAIeWb9epl +#AYiXSUAFjn352KrVTQXHabBQef4iybEhTwZYAqjEhjdHtSRhILCj5qnHJev6smnN +#InT5s26xeZRmEOY90q7/6rHsgL+wZr/mQvn8g7FS3zOw1A5eCAgM0TLMvgajOvOt +#IKwKZWbGwwTxCIViq/WAfVD9UGoa+9ZBzXnBMsd8+Q5m8GlJ7BzyNrpQBERAR8iA +#49f/Zue/+D46fK2/uwFGNCtQ9TvYEXnymqyjGhV3nsqSy/gCsa9+vXMn/HynXilV +#Ryre4AzxxR840MpE5C07TkQ3RdiV1p2Gedx7R7IJVxpd8o6HlUvEk7sxfi6APQJi +#CTxZh/LmjTtiqmWrfcmEgPWDPdhhBKR600/W8pcJ07Z5mMPR7/1/bHNB6JH5xf20 +#ekcXhY18VkksgBVEQXxVHBcXS2/B+QadQNE+VNCMKJZ0cKhdqCg77L3dr+AIT57G +#ZjGpXBWMrF2P6jn3gWelDuolO/3KeV2vciKGp6E7CS2mzqXPVcZ1RU1v2XhVDdNg +#MsRlNdrSLYmSQps43YuNQg0wKP8Gg74SXQNBnJ7tH384cyw5NvQ5RsHqVkobCyGI +#NZ8hPqM9udP9WqzGbN7ILmOqwGsj9+Gz6W2QyciG14k6xne7OyqyEHnYjPqHaI9s +#dYDhnknWMLSkUC8tVThlo0O1xkl2MbuE4LVNwRdBqQr/0D9TmZzYHnXlWu4T94JP +#JC4ct209V6I2j1btAQDVvgdaihukmAEMqTb2Ycli4Ck2FudIZDtdM6+8wNWbYrDY +#XFvbXBMMJ+97vXn3trd/fPZ292zvt2ZLjByEtMK3M3wSEffekbezyYYEx/k5FPMh +#iAzlYsk8p1X5oLM0L1gMmk33eRlO2o4C1pXquhwy8OFUbS/dUwUUPQu7GHhw5Vsv +#/TKRD6wLDOt9M6qFsG2GcmvX8FvQIIAxuYV6YTcp/NxFA2UcJN/ACJy/qw3lfAqu +#70Ox79tWj6rIw8sIKeBbSG3UFo5N3UZKvuH5fTdzOiA7fWncbKVYRKsqYnrq2X22 +#4Pjyz7qOCdf2ZCVv3kaBhZC1snJFgcG0YUZxHY3IZVJcJUAZ92nYVczhVUO8Euq0 +#gYYrGAPSVAtJJ7kpSgPuUTv48Kb+K1ctHYPvFa5fP3+1lmXO0//AJdC0eY+1MHCh +#wVtb3HMKPNoMUZrUqI5jLh+ZaUf/sDPsWdQfw7HFbYQAS+Woj+GX+jZoY1wd+9y5 +#UOb43nszNd4Tzc34ewR1KpQzoZ2ZabbjK03Hlc58HHak3GR+fwtzGp+QP3ob8I0a +#/v7h6e7p216Vou8rybarD2Lgq9nJobLfjry58HA7t6Pk5r/jru3o4OM9BvTXJEsK +#OXfHyY0QibLBCJ1JH3zND5hFH3A8pWeUN6b6mFWaz9ptzzbqLCHe2MtY216ytjGI +#kQjaVY+Z7Wm1xWMFHqs3z7lLy8NwoW8RWsaFuyer8+Br/SYwPCedDqKxGsh+MbgU +#e4xvNv08HIvJIV2Rw+osAKDsz0FDvI8JwPoHLsWAuxR8I8eFDyId5gJQdjuEXkLl +#l6hcchgOZpuPH+0ZgjoF595E02lvAMYmWdjZtAm0LpLM5O4fvP/14F19WAgKb68O +#3F4IGlfT1Wx5f3Lw/uRYzJfT3t4ezhlspL6khB5LL1ho9jutGIFjWNjXmaPMexzv +#MlIstR++f1+9Eq1+IukLDDsRvssx43ddpdSBzf1XKbcAP/kJ+wzBxAdzjInLsDbJ +#C7brVOYAbhsxJldJmG6zE7eqrLKqCDiCmTTBDV2fowTb/2rZyRSGoa/xC6NvYmLR +#8LG0cnCs76wnCVx/oPojgj9cU/CDAwROf7gU47jK8xZb9Gg/fNfk/mzMnNH/LMws +#FAdzV28JM57+dnT4Wsjipdy42gRhrioGjWb9lNnyklrl/PxxA2MftdY2m9uNzt83 +#u8+bTxewRf/08smdAb349Omnysjqf+i0Rkp+t3lNLzj8F53dtAtfP8dPf1t5losR +#eeB5fn+9t24SJzezot9zwkHEvRKbqovn99WdSCfTcaIyvF5UxfJfEiAeDGCghRBG +#CEXar2IaXR24Rv2s3Ck6Rk3+BIV+jGrHRejbqfaf/9XsWaeH//tg9X2n7OB30x7/ +#ECMWXPH672jF+tvu3t7XmbHwJsKfz44lx7Ld1mOopaV9usE+tvv0bvdU9Z17Tds8 +#tVhCEemtprLsQuy5P4oSu8Z2K3QHHyO22P6J8KrDaG+vGiPMC2O0pxH6OjrAalzd +#rsr9ipZre/rxY01XMbOiRWphoX5uOvcbqFCHk87+Gqyw7v1ptF8zKvvVY7Jfg8n+ +#3teTpw4dlXt/hKDiapR5ZOQsVjw6+v3t7qmSqo+iEFK2iMXrUUQxUyUMfpD7UJLm +#z4KvfnCxvM0G2rXAmG7FoopLjG+5VY5a4xTFv75sLiOu750cvj+DC+c/XHdkBWw8 +#Ir9bxbnc++jlW6w5xnkDfova8Idc3uf3LJObdIbK5CwX6u5VMmYQ1OPbx9jj97hI +#ttuH2ZVY+ofH6r7ML1FqY0Cqg+YWPhie5i3XVYTlz1kWYvJTcD30gyfIq4MQTlNd +#r2nIt9Qo8iSXumRdX8IFJySKeibdRj6GcMMio/NChbjf2IC4tNvNxnZ7CyICNbd5 +#TKDwKxgmJpBXVGbDRTfH9YTguuR1Czu0+h37F0Sh4MrXudih1L3wwdpl9OY0Lnsy +#7CUS2iUxzVyVzioSrMyEiBYsh0LE99kUHZaOOO8XBeN1rprlyUSjr3AJAXAtieF4 +#YYGPQShdPWBu3vSmkbzphFJhVKMvQkiQO4Lw1cqzRryxi3e+N8DB0nkMkjZd4dNA +#AZ1LQPIdzv3Dk7hJ7igKUUhdxgJBhE05c7ONujOGm0xkk4YGv5+qRnUKC/LjU+zK +#fTk93MwowiOApu/cyIczFIuIwnkj0YWx3hj10zEEGV7+ACeyhipOm5ZB+l4saVNe +#6d+4TMZTt62AlFwZ7D8j816HJIt532PJMJtywP76d/WDHrbFQnYEn0+J3h7v6/HA +#hB68CMoC+Mk3Y3QWyZEPuuSjET9lC733mospXqY92aTrHKkhiU3bILk3LEdAh+2A +#tPtnsvs4H92BZMJjhZl6paZNUpzn5SosqEquBh3glvDebojdYgk9UmWiJ3daE/v9 +#4OT08PhdKEhqiBnBaTgvxGr/eNmSscJCIxZmMpNNa9RkrRzhSDG6/WeKR7zXz8Cx +#DVhbdJBUCXXuZahzLLIXkzEhHc5eSRp5IZUV3qM6s4ANoRUggtM1/moiPCFNdcdZ +#rl+va8UVEI7y/HMyhNDhcAM82B35ppDgUXq465DL4s41zgD3guJKerbuMck6mUHr +#dtlb95YMR3GloYuEomqHrNo3MmkZH+Kwp6PbP6pVUFn96tXB8ZtHHyAF6Pj0xSLq +#gKt+f6wNwt2oo9Qb/GOTW61W99GjX1nhtgC7sbseoSKATZwKkgAtJLWihlBlMjHe +#IAbgTElsRuhrq2UzSjOpeI0Fw7WiD6KcGhZBpH/OMxmKDqoi12ycgW41x5frHkHj +#5xHGwAIUcPnfwk+JSjLznve2r4djTlMCSYQKABD0gG39fooQDnAg4TGdYVriT12i +#JWqAJ2pf7vM2ygR4At7ohH5E+UiXVKMh43FHhyPshQxHjRxYyr7FGzHmqFoAWjUp +#H/gGmZDrGKqYbICN5oLa2CoD+DwGvBXcLBLbGHwYAQKJiwSEZFSa2WV/JuiaaNjR +#+a0+vIGfeTa+lVlgljTUgA8NCtoX9Sx5jiGg6nUKIQ1ljyVgCOdqiVMiCcvoIgUu +#0QbbfnZrC4DMUMABQU2UVnQmoCpoGjiB+kjOt0kCL1yB3ADrKa685Pl6aN0+pC5Z +#QSptWB14ET4AQ7q7xXJvgF+oSma5HxKSoTEMj/ML2fy0SK7SfK5CX6AvM24vG8M8 +#UR3NxTBjLkJTC4Zs8BIaBH3MNiSlUwSJ9dA4JKGEAaxMaVFb78xsyfBBMCCp1rYa +#43wgpruZNXrhDStnC9VCEe0dHx2fQCuouWzhp2lGajOgYEUNoVGt4+vy6xGqRArC +#GdS171tjd42YwM6al3dL0nfocun0OXptmBlhITdL6YQzpU8FEjKZ5DcpjDZ2z2B5 +#KrlYE5wE/I3wxI/b6LJ/lTiQ4AmuXHHLFUoXqQ5hpZN5Fqnv8S0wM7Bif14mmtWg +#UfRPt3Ncdkuz1bzEeSpFAly00fd/sUGtTXnMojWoFfkFVgu7nugjAKH49HowNXo9 +#uGfwFDQtrZDtjVPYPOLyuXvy6+9NrPF/AYwxfo8= + diff --git a/src/dlog.c b/src/dlog.c new file mode 100644 index 0000000..2dc2eea --- /dev/null +++ b/src/dlog.c @@ -0,0 +1,34 @@ +#include "dlog.h" + +#include + +#define DLOG_SIZE 1024 + +char dlog[DLOG_SIZE]; +static size_t dlog_index; + +void dlog_printf(const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + dlog_vprintf(fmt, args); + va_end(args); +} + +void dlog_vprintf(const char * fmt, va_list args) +{ + size_t size = DLOG_SIZE - dlog_index; + if (size > 0u) + { + int written = vsnprintf(&dlog[dlog_index], size, fmt, args); + if (written > 0) + { + dlog_index += (size_t)written; + if (dlog_index >= DLOG_SIZE) + { + dlog_index = DLOG_SIZE; + dlog[DLOG_SIZE - 1u] = '\0'; + } + } + } +} diff --git a/src/dlog.h b/src/dlog.h new file mode 100644 index 0000000..9eaf521 --- /dev/null +++ b/src/dlog.h @@ -0,0 +1,9 @@ +#ifndef DLOG_H +#define DLOG_H + +#include + +void dlog_printf(const char * fmt, ...); +void dlog_vprintf(const char * fmt, va_list args); + +#endif