Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64c5abd9b9 | ||
|
|
d9dc0b7176 | ||
|
|
a567084398 | ||
|
|
789061b71f |
No files matched your search
+1
-6
@@ -1,6 +1 @@
|
||||
*.bmp
|
||||
*.png
|
||||
/.propane*
|
||||
/.rscons*
|
||||
/build/
|
||||
/fart
|
||||
/target/
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "fart"
|
||||
version = "0.1.0"
|
||||
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "fart"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
@@ -1,7 +0,0 @@
|
||||
.PHONY: all
|
||||
all:
|
||||
@./rscons
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@./rscons clean
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
configure do
|
||||
check_cxx_compiler "clang++"
|
||||
check_cfg package: "libpng"
|
||||
end
|
||||
|
||||
class Propane < Builder
|
||||
def run(options)
|
||||
@sources += ["propane"] unless @sources.include?("propane")
|
||||
if @command
|
||||
finalize_command
|
||||
else
|
||||
command = %W[./propane -w --log #{@target}.log #{@sources.first} #{@target}]
|
||||
standard_command("Propane <target>#{@target}<reset>", command, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
env do |env|
|
||||
env.add_builder(Propane)
|
||||
env["CCFLAGS"] += %w[-Wall -O2 -std=c++23 -include fart/common.h]
|
||||
env["CPPPATH"] += %W[src #{env.build_root}/src]
|
||||
|
||||
env.Propane("^/src/fart/propane.cpp", "src/fart.propane")
|
||||
env.barrier
|
||||
sources = glob("src/**/*.cpp") + ["#{env.build_root}/src/fart/propane.cpp"]
|
||||
env.Program("fart", sources)
|
||||
end
|
||||
@@ -1,27 +0,0 @@
|
||||
" Vim syntax file
|
||||
|
||||
" Setup
|
||||
if version >= 600
|
||||
" Quit when a syntax file was already loaded
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
endif
|
||||
|
||||
syn case match
|
||||
|
||||
syn keyword fartKeywords ambient ambient_occlusion color define diffuse exposure height jitter look_at material max_depth multisample ngon offset polygon position radius reflectance refraction rotate scale shininess size specular texture translate transparency union up vfov width
|
||||
syn keyword fartObjects box camera cyl extrude intersect light options plane scene shape sphere subtract union
|
||||
syn keyword fartControl for while if else elsif local
|
||||
syn match fartNumber "\(^\|\W\)\@<=[+-]\=\(\d\+\)\=\.\=\d\+\([eE][+-]\=\d\+\)\="
|
||||
syn match fartComment "#.*"
|
||||
syn match fartIdentifier "\$[a-zA-Z_][a-zA-Z_0-9]*"
|
||||
|
||||
hi def link fartKeywords Operator
|
||||
hi def link fartObjects Type
|
||||
hi def link fartNumber Number
|
||||
hi def link fartComment Comment
|
||||
hi def link fartControl Special
|
||||
hi def link fartIdentifier Identifier
|
||||
|
||||
let b:current_syntax = "fart"
|
||||
@@ -1,3 +0,0 @@
|
||||
if has("autocmd")
|
||||
autocmd BufRead *.fart set syntax=fart
|
||||
endif " has("autocmd")
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2010-2026 Josh Holtrop
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
script = File.join(File.dirname(__FILE__), ".propane-5.2.0-58c963344c9bc893689cbae5e4c49b1b.rb")
|
||||
unless File.exist?(script)
|
||||
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/
|
||||
require "zlib"
|
||||
require "base64"
|
||||
encoded_compressed = $1
|
||||
unescaped_compressed = Base64.decode64(encoded_compressed)
|
||||
inflated = Zlib::Inflate.inflate(unescaped_compressed)
|
||||
File.open(script, "wb") do |fh|
|
||||
fh.write(inflated)
|
||||
end
|
||||
else
|
||||
raise "Could not decompress."
|
||||
end
|
||||
end
|
||||
load script
|
||||
if __FILE__ == $0
|
||||
Propane::CLI.new.run(ARGV)
|
||||
end
|
||||
#==>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 truncated
|
||||
@@ -1,538 +0,0 @@
|
||||
#!/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.4.2-b1e276fdbe6b603498fe893d904fe368.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) != "b1e276fdbe6b603498fe893d904fe368"
|
||||
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
|
||||
#==>
|
||||
#eJztvWt7GzmOKPy9f0WlkulIji0nPTu776NO2k5spzt7nMuxnT6Zo3g0slSya6Lb
|
||||
#Vkl2fBztbz8AeANIVkl2knl79zmZ6UTFCwiCIAiCIHj/3vaiLLbP8sl2NrlMisXZ
|
||||
#9Q9F9h+LvMiSdJLNty/m81nqkgb5eVbO0x/G08FilCVHZX86KX9IEv39fFLm8JUk
|
||||
#/VGvLJOnT5MyGw0pJUmODo4PTpJnSfox6zwepzp1kA2TqyKfZ418uplsjLOy7J1n
|
||||
#TZ0L+dNufzqaFlBRtdbqzWajvN+b59NJC3J70KYqYuvkQ1utNclHOzZDgNN1d6Bh
|
||||
#11w2Gdjf08Uc0U1jcBnI/qIossncoYn95E1mJdBuUJmvu9zKev0LgJ98GX9hudTq
|
||||
#uJWX3d5O43he5JPzpshOklE+yUoAPG6VQJl5I/04STeTrSfRctRM9yqfX3TzySD7
|
||||
#TC1izmaSf/EqUNuYl9wTdBD5svtQUPQ3UkcR9tE6BX3SrqrBRy9oDjsS5MZqQKfy
|
||||
#5KkmV5n/nyzZSp6s2Xl/cAMsqgus4qO1Ooljv0Yn/ZRsVPq06ffKLBl7iVcX2SRp
|
||||
#wxD8zFN9DqcZ/vOfn4x9VFT18yLLJj+vrP5TRfXrbDSaXv28qvqfK6qfjRYZx76i
|
||||
#+r9UVB/DVJ3Mez+vqP6Xiur9695kjdb/taL61QVIytWk+7eqvk9HAz54sepPqgcO
|
||||
#q/PBq6heNXBYnQ9eRfXKgYPqfPAqqlcNHFbng1dRvWrgsDofvIrqVQOH1fngVVSv
|
||||
#G7hVbFM1akVWZvManonJmUA+iG/5tZ4YrBB/HJQnhL56XdRNjtfsCf+dT1tKKQEY
|
||||
#BqzLnxX5ZW9ucEUNJqpLAIJH71/8tfvu8PnJy7dHr5Nn/5lsjwHRqy/j8rrcZng0
|
||||
#Dt783klPDo5ep6fJMxjPz/OsGKfN5Mcfkz9ddYb5cJr0L3pFrw/px7Osn/dGp618
|
||||
#0h8tBhm22irnvXlrOL+eMd3JIyiUms+vdyIdNr/Uv/g3/me1PuCfUOVTKt5zp4wR
|
||||
#hN58XnSLrDfIiqQN2OaXWfeyV+S9ybx0BXr9PgwusEr7bJGPBt1BXsQyQ+3OKzDp
|
||||
#zi+wsTJsu+wX+Wweq1TmI8Wrk2F+viiyWJnLrDibauLh+IKulM+B5qAMaJLt+mBg
|
||||
#Js2Lhc22HYN0Gtyj4723b467L96/Otzv7r86goH+8iVJqZyZuFCwNchGGTBeWN6M
|
||||
#667tNYB+P89HUAW5BdSVriQIlNW0754X08UMK3RO2Uirzs175adu9jnrL3AYu7ML
|
||||
#WPkNl+zGMgMIxWLSKIgxiOqbBBJm5mTQnQHTjsvNpLyYXnUpeTPpjUbmZzbpnY1A
|
||||
#GBkeMZ3cg0mfAYMDW0/6WScd9vIRyazxGKCWOEtMVzSSvDkcCy/JFPUbhKJ+0g92
|
||||
#+oaFUQ/GSXnPh+8mY2pZIhWyoI9d6kba9/tq6tOs6g5681562kn9iukpm9qxbsXb
|
||||
#q5n9yNQ0flD5mH60JtmVowYM4QJ4ejTtDbpserGKLczjnMA3VoiHj4ZrWsHXLGJr
|
||||
#UZICpXIalncc6CKbL4pJ8jiAiTvFayqtB6kx7IFIbNbxtpzG+TBgpFY2nnEpCkTu
|
||||
#A/xiirIDyH2ZF9PJOOOkBignAKVFoDopzJjeYjQXA4j5LKeloCrcstiYcdEeomiW
|
||||
#TOrhpDeG3Z3K+uK3aUus0ab99TiQAPHhXUxGQJRk16YbduSkCXnX321mn2ElgYG5
|
||||
#14jwOaypf2t8fPRlq7nNFYDIhAgqm/3yZsprlri8DnNZ92ZZA9vRW5JB6isyjyO+
|
||||
#02htNB9sx0oP8hL/3UyMIMehAnwePNlMHvwkaoR4d3ilUyvpDFCi85YgtFSO0FJg
|
||||
#0MXeRbuFEzdo2UlDndJpEw6+imZzNUkRyep+aCCnklCjEiU1ssgawEkA3EEhDFlY
|
||||
#CgpXVs+imNjlE4YkG65qRgNJEm+Ni6wMwSoYrxm18ehMYeZBe51WdB+U8wEou5u0
|
||||
#u99M0pcEMtG1ksb9mzx5lDxZbt+/8VtDE8my2U439V5jk6wQbqg16NZsAVOJFBZd
|
||||
#tTufdsuG/vDVbE6s/ijrTQx9kSYBbXgmyNniPJsbac+lIaa77qvSRTaegpaqMhvq
|
||||
#H4f8S+gr4ly2inF36GWzUSdQoO5l/fm0yDPbOKiT8+TmS28zOfuSnClz0tNnvyQ9
|
||||
#RTaHnKl7XYGfzW/YXw7LSfZ5bqQtYmwxud6JFYcJ09jPixasUoSrK5JsJZ2HrYeb
|
||||
#ycNW6+Fp01/skgSrFWOowCvBsPcXWTLJR7XLhuoP8VswxCCT5usPs1+whSnGIiiG
|
||||
#rBg2nC7eDDmLZqvcC8QUB5zqbtRr1rr1lDnb4IBpAbgWaj2qt5hPsQwjvI9j9f6N
|
||||
#96xGL7GLsy4DUzqqvRy4xM4pX+ou+UIAny0NqEKAxpuJi1HREa/jFm9HRNaKS5SQ
|
||||
#kYDITib77QyV28aur6eeZef5hPXLjIiFC/KKq57E+GpTfFAUaFD5JcnEsAFvwlZS
|
||||
#qp5Ut5eDuhkbznD7s+6WIKbl/BCgIpk5pPhiQi2PzGy+hcgl0F8ncQNZln0G2bAT
|
||||
#lKPlJD1CWPnkPLl/owosU5xIfAOPf+4sxlWH/rtJ8YB2tm6cfN9P7msubahNynSG
|
||||
#DK4UbtNRFIsxSxL+uXfPz2oNQeWBoTK6aiJ1R9zB4w+jzot9FLYkDSYh5UR+dO66
|
||||
#PuhfrQErqdMsOoRNmMtUVgNlmM37F4223iNuJnwWs5qfsutILUjdpKZcDb+ro14J
|
||||
#VDJ0PMXTWw2kRlQYGBpfq+93eVurNxL5MDJOHBSXm2q7H2j3tSzo9P4Y9kSBxgYI
|
||||
#hXIz+fFsNO1/YtyHycBWRTmvMDfrPRmVK0d5P7vXeBzKFscXDiCaAO3W0jfWuQFo
|
||||
#jTOQVzRH2omaKWac2knnlOFKuLvFDL+smSw2frRJMLAaQdcrJ55avFJRHeb5ZDKd
|
||||
#wxIKv0a4e8DsfJL0IPUiK2RjCrk0INPqSbh+28lkagmF3c0n2SDS4jwrupfnODKj
|
||||
#3vhs0CMeVam6suBUkaPWAuBWH+nS2YppcD1KQmMSDmyDZnIPK0evAoiQvXJv62HU
|
||||
#CfE+ZfPdrWFV2/zqGSfpKBuCXaPZv/s79zV23uEodTqn0oTNT2Oc4luhBQWs5XVa
|
||||
#pX9hmCgw/ekCbTZSYnDqqQIV5FtHVgmDs27umTVq4h99IIbHfW8YXxt7xBCUTyNk
|
||||
#CT1u2cmHPuokZsV4GPjoRgAqQaw8Kn8zWP2X1VYjNTuZRkx6duDXU7954SvGHUeu
|
||||
#cqTq+Pw6z0aDRDa8Ctc1bNRK3UJ7a9k2lCPr6ylpllF7Lf4UYgcTWoNMtQSrAq4d
|
||||
#tiVft/tTJ4EhLGFuTObDRvqnrZ/+rUwVULU0L0lhlhCXnB8o07cn0zc3KEshAQtb
|
||||
#d45KLBoWt+7fuOJLeTYN3aE82Dd8ymBNLc49WWIhITM+45Baixm6pngA/QNrSwSf
|
||||
#DH9BMqT3b0wDy5RIobCposUq2WS2pPXr1m052PDNx8nvfFrHZ2ayA5P2YTxv+TBN
|
||||
#2kmaLtucaHeaIAanxAkI0whLsNorItXQX02NRJXoqJ9loUlmOoEEuy0XhzorD0MY
|
||||
#HNc7PYoSLh722YpkMXfHe95sCRoMZguItNEiCxZWyoM54x/IKHgdB8Zfcf0KZTbv
|
||||
#soYaFQhIK3zE9ysuw9P3k0+T6dVEgcIz50RMTLM20EqEWGkRowVAs2bCrs8HlYeQ
|
||||
#eluAuyLSIevP7bzSd7baaTi0vaANedgubL1K2JD/6apjbZzKgOnsLP4W3dqtpENG
|
||||
#3AXjRa/M+8xIZ0nlfBYasX01iiNgGEgD8XKc0WFvQ8Fut/cPXj5/f3jSRR+Ek6P3
|
||||
#eyev3r7p/v786NXzF4cHxwYEjUJIq3umPVHOTRG1soNMKO8F49s5bQDBPBw7uKkN
|
||||
#ip4Dt0N+pHwryJJNPGvQfhgmQ6whUvNGMQkEnw2aQaXfYCRT6DOfZ8gRULjIhg31
|
||||
#zyakzoseJmpF3gBVKUIvtCV9h2aNQN0eAPFTG0gHJqaIqc0PLsbkKJJNLtvkv02b
|
||||
#Tdho4t+nDMVWvFcMjCQEsGKv7ILguJgOjOePPZVqC2CWcDNEvzsc9c7LBv3NNqf0
|
||||
#rc5R73knqDoLpEU2GjUePAl348UlP9W9mhZ04HaM5elDnw2LNstP+cw7Q1RlY+dt
|
||||
#mCMO2/CgFACwwYnAwz9oBoxuEBSTyW0q0IsYmS8qxSXuuNC/iDnJ8IxHjsP9Ni5g
|
||||
#EEbZ6jZoQzEibkxo4zEKbWXIJiMARUTq0PmhXME0AYSZPuJeiFDwEBI3+BUNKdJ0
|
||||
#CFP4e3S6Wn0j0IgaefVs9Yr+RbBjqsVet5nu7b08fP7rMeiVHQ2GqHUaL324v7q0
|
||||
#pECMSurUW2GPE+D+jRF56d67dyC93x0dvHz1AZTbwLdAjXBHl3v15gBxecD6xmEj
|
||||
#ZfRZ/jcgjoH0tT0mrMrrsphO598CLQNq7XGrqfFVY/fqzd5aA/fu+clvNaPmQT18
|
||||
#9eLw1Zv/sRoyFKxjhhDs/qujtaCuwBfHczyZbvWvz69ycYEhMl5YRVA8MkJemaAt
|
||||
#aGUwvSpjLa0PZaZ8Lb8vukjzrf/V2wyoa8A8PzZgHjxhDk110EbV0BxSa0ObVUMD
|
||||
#Tl0bHHowF3NaRXca6ZbYLqwmaxWcR7eEs+bwZLEKavIEpSP2guIy0BCZonNPah1G
|
||||
#l6lUhUi3i2XQR9BSfXGly8x6eUFLv9GTpRoDSqFQsfW5yPOi6F03aanGZZoncpEI
|
||||
#tZWe7ashkq6uXJW2EtsfDhZjo2Ip1bd70SsvkmdWE55PuxdhAdqlkTGue3bd+LGN
|
||||
#nklNYQkRlsVuOS+UNdgcCV2Pz6ajZrKj0tROOCGd2daLelvhnZJNA5J5UKEfAVlU
|
||||
#DIakudk9NnOwilFh3Y2ZMguv74SMhaXHij6ONLsY/KeTdk1q6s0sZlnhWHEg8buY
|
||||
#0JI94VyUwhBCDNYQuWzk4DOwt0CawNcDWI+rqfxtzBnV7fA2cjNrBZUpiVFY70Fd
|
||||
#yXDXk5ttrISk0hgoLiNYhShAJZcEPEpi4FwRYRyDtJKOUoXNkwtCXmjdswO15/O8
|
||||
#Cimx2x8P5L6akgFtq9Mc/3ZweCjWDMQkKIFLQ9pEC3xDg0DD4HiQosVzu0+Wzq2+
|
||||
#gNOhgpsEr3It0VecsTHQMOfZuEu1Ij1/9bY1m8LANDobtm+bxl3ztKmOMKdMbE9b
|
||||
#qKyskhnVRhp7YrH6sMIdHSh74+1MoiT3U306QabDtjxJOFWLoam7ki2M+czeZxrm
|
||||
#o2yBzkCVF9lfoHEiK8qfNQhtaVOpBDp2w11ZetDEMuvNL4K5gtnCl6FczLLCqEXt
|
||||
#tEkuGBUjJC43KfelML13VlZlldNF0c8iF6WgTmUeOe6Eydnk0iWyS1MwWSMN54Os
|
||||
#mw2HsGrV3AADXp9ZTgwNl8y6j91z/i0dTQt7FciSwFyOgoTpaDHPaEwau9Lza1d3
|
||||
#HQp7y4jOsL4Vu45OqEzon61xb0Z8rb4dG0eaVkVCEbpr3Ots25Rge4SBGZ5xz6BL
|
||||
#m6WFrs3jFrpdTng0bWXuOg+f9owjSa8jxm7ZVKFbZbgSlI0NWAMLWONnblzQ/dNk
|
||||
#axpvJn45oe8WMNOAgo0NjqcV1yzNCQ+W6ijNErvKhkiun7GxRyRVkQar1WQuUVii
|
||||
#yM7zEp0NWJlGrBVWT9AahUKkeI3oxftzHqdrJ5eTi7xMtMl0vCjJwWV6mRVFPhhk
|
||||
#kwSda0CMnNGgpREi55N5F6B39fk7rFao4rJzTrtmiKFUFc+UvJMAyCC8CozonKGn
|
||||
#LlGDQ9wB0NwW4HlO/XAzllRqt7R61aCAWlH1N64iN1ChHUBYBlN0LTrueoS86uXz
|
||||
#7nBaNPZUOp2v2I4qOno4NkPqoT4+6BWDO1PPoIU3AtUvM8esNAuEHio2u3xVUG5Z
|
||||
#yo9zMcN7G6CkZjtuqu+6jqlqm8RIbopwJ2+u8+hDV634zC6mk2vtGLzjC2vVG8Jh
|
||||
#/GmQF91Zw3jJoriyxXl5z+83gMg19XUYddendd0ubJhPaBWzAGPjc7th2DXexBpV
|
||||
#mqJrD4N3rqhdAA2foq+eswao9ZCnrXUYqUQG15S8Fd0IFSoPa4T5Ns6X+lN4Iu6K
|
||||
#SkAq8e0X0gdY/NMvQtBZGec2KZZGmLAb7hzLx4Mm9Mau7IBxIt2NdOQWBITVOnma
|
||||
#/Ab7/koadtV6m036OY5zdBlFBbMhsRfVDAlEYoUCEShynFRPn5qBtW2jGq/TWlYr
|
||||
#8w6lqgrBEnrGWIi35PTFEnTMcQ/IMjln4GBuoGLReKz2QEVWkrd0w0wRDbdsOhVC
|
||||
#lUke2TztzXfz5exLcu9Mm3jM0bjZG8CvHqzD0GN/wfAPqfFAzy6mIDtxudcXJqAj
|
||||
#OWzeKJPtcaEQJSHZsU/k1O4uVLheuE6gmuU42jgsM631UdKIcEDHjQt6EqN5rCdg
|
||||
#tnqj0Y6625AJ56R7FX1wdz+hAu6RWR09RLUlBN/JonUmAOUjp+nGtqKSXkChjaCQ
|
||||
#zgyo5zbPQ5dD/r6/iIBQLs9z/o7aNE2Eh/qliNxK+BzwekqKmEHmsTK6VoxH3H16
|
||||
#D/1o6EiVmKuHSig5aplWW8m7aVnmmNLPi/5i1CsSyznX5ORz/0ZzJxCr8+RU85y5
|
||||
#h7PeZlyFlNseD/6SRnfoNu0f5XSSrghK8vvB0fGrt2/Q//DPrX9p/ZTWSNo9u7l9
|
||||
#99vbN3/tqoMsrNqmBO0sV7XR965BAd+aFBJxyp7q4ifEZEMQVMQlhI4xKpwDEkYX
|
||||
#Jt3nH9N80ogExrNXHjeTtKUDMiCANOYOIzxoqFjc4YY7zjgvMlZDWeojOkbl9cxh
|
||||
#w/WruQ4VEFCXXLzLxbhLlY19anc0nX4C7dTksp2C0HfsPSQ7z7QSSpep1GUzoVgy
|
||||
#FL1Op7AVK4HeZJPSzMeHhyx0rjoMxlWq1qXhhZN6wwu6G9349+O3b1p4gNFQ4Jt1
|
||||
#yiVXw/XEZzsBlNwYXeUyvinQiwXJdzRh3JBwj22aUeya9U2rhKqxmlt+K2wwvuBT
|
||||
#9AH+0WZW+92wgFzhuv1CpHgSu0kYLSc3k4PsbHFOiyIP3rP3fO+3g+7+wYv3v6b+
|
||||
#iYHyi6Vp305OVPftnURQJbNBCexmhDLs4/u9BfoM4qUBWJ1REBN6yXSCl6E/1fs3
|
||||
#c7/DH+LF+G/joKy5Vg9hetq66JVI6Z2GJTqnyddQRNCDMettiIO3hoBESB3FJQO1
|
||||
#OIGIgOVnTKKOFqJ8XnMhIUaqCHFuzVIBNTuWiOgsqqWQCujliaY/GgsCG8BnNsEg
|
||||
#Y5NzILNmQlD6cUE7y6g8VJwnaKP+p/CmpKYSbIqY+6QytNuv9//Susg+Kw3CmFbM
|
||||
#EekfjI9tJA34Bk1tahhZ0V6T/SuYWE2Prhbs9cTEQqnW+GMAukNc9mwMKZWo1gnY
|
||||
#BfS/JIN+J6UyAMUFxpHGuCLvz6niaThzFJbPIm1+r9nwFQPH+6LxQZF0nl9mE6V2
|
||||
#QyF0pJ4OE7G3tnIdBBWslFhsVmSX+XRRxsp//aSSWZrWDePaTXb4ZMscBDRC4jcj
|
||||
#N8X/IKMAy0AP/gO8vzHNuCAypxUUKemSTmFtGpPY8YnDa9dPP1uyag7aAsFEtDmr
|
||||
#Z6MefoZWCCWcdH8ESannBmK5ZYNBia0n6HLlN5KbfBokjwIKMS0XPpVq+iUq0Lp6
|
||||
#zOvXfVZQD9l3UwDuOAIenTE2RIDzsor4Xz0Hb9EDkMGLGa6kWICiVqwik2soagj3
|
||||
#jOq33lStuz26zd5IfVv+Yd4r3nrrbGan6I8fVWZjGqcMtOdfBev3Rmj4AewsB4dA
|
||||
#+Pj5G8uI3bBik8nZobWY5P/hDCuBNJW7RI0RHeixdqzOiK5066mMm6K6mchY3xsH
|
||||
#UZBEORaq63UicKN6ajLpipt+rmg+IkaasoaMTzgQCLoFB2E1wrVNLGzNP2IX9EU5
|
||||
#gnTvnmV7c3/Ola2zxZsgRbqWszfRJwaYvCcDH4X75TIboWOndY2Nz+2OxZcWAQkU
|
||||
#+Rso3PixTebS0CBnDjdxasQlzaxXzJVL8PxC+w9tdz5+/Lh9ui2KKGdedyeJ3Uai
|
||||
#WEPKe29ednIVddrJgnJxhsChDWdm3FBlH28m6mJFYJThNhYNIH4Wa3oYKWRIzmIw
|
||||
#wYTXBU9pjILRj6wkUddgFtbp1iwg8AnYYNDVtdh5TNcCvTUH1AXICnkycp4QAVYT
|
||||
#eSreRw3WK20g+1E3YiJZBAHCaOjVi5Io6lnkHyWsDB5Q2YBEMgiuLVPDA75tmblf
|
||||
#kfWVAhgTH6P/mrD8mmhX1vlEGC70ISGe2LpuoJt3VhQtodVc9Qo8smyDKuPALyEZ
|
||||
#NKppUSxm8xaGqZtjdLz2x8n9G9NAEHrDon4TuqwEbEKnEi7WUGzI40VinuBeyfUM
|
||||
#7/4KwG3+pGpGNSJeStDRB3f/hgjZXEsVDFDuYGVStiM6j8NBnsjUFFy3S30kVlw7
|
||||
#cWyozg7G0wF0Oy3OUseMVmQL0kQwM9SB1oDvyuVXUQl2ktwFoN6jYY95HQnPUH66
|
||||
#Kd1O/RoseP+8N1/EvEyVb5fwTPXyeDAz6U17YT2jPT8Lq/lrVG/pYbUbHCybDIeu
|
||||
#8PuxqadewSCmXODNFor9xcQKhUvjE8Mb3kk67PM0aTtLhAbq1zJYsJrWmY7V1lnm
|
||||
#HvmGaX8z2dArgHF707HGga4bznNpg7fuQvzTIGGQDPrRIkvQ1Okr1GZjg7UdPzyr
|
||||
#cwa3aXhs9+dVZ82Gu21A0RgHyfCiu3OY1yp08gyaLYQv4+7VtPikX4xI79/UHeou
|
||||
#t12AEiMAQu3KAmy66XxOq42JIg7N2ELLbQUSlsDzVFSwWiCdZkogeKJ5Zu85rA4U
|
||||
#uuIlh7ol51mw8kZIFH3cA//k5HL8D3TI1Z3XgV15KtNDY5e1UjPYaC9J7eUtDoHd
|
||||
#4Gq1WiIMtre5FlGxBWQND+q7pT7m76LeDHCMcBqEa4oGaroD8SrIcZzN5yp8KF3M
|
||||
#AISJKvRElG62Rf4AVZHB6/V1Fay2XJDwlzx8oSLZBmzqouGtw4qro7wgm6j216mp
|
||||
#yE+1KsZG34fhwCriourA1FZUNDb6/b4hguEdNSDpHpbFgcCj0L3EVKHxMFXiztMA
|
||||
#ky6dRLVXVwWLad+mrUgsDQ3Ifxij34eqf7rqnMMPEJaT89AOR0WwrnVw6/fZZhqk
|
||||
#JSdBv1/pdquRcKqxL2lhMLTayi9C1BR9+tTBYwhDIdoWAeY7yWNY9p5YnHSQFbaH
|
||||
#Js4xPuOglvWdnQLFoMtJyQ9rusBI+zTy2QDVNCAM7bwxZFkTNNuIf7jmk8+f78Ap
|
||||
#jx798XgFcUJeefTobtzCCfGV/PL58y04hgr/l+KZAeeYwVocs38rfhmsxy+Dlfwy
|
||||
#CPllYPgFfowG/Z/wr5BhqBRWtwwz8BmGUWGwgl0GtewyWJ9ZBopVBjWsMrgDqwxu
|
||||
#yyqDtcXL8Dx6d4Hsh/1PKiip3ZnoJGeWi+tSnLEMlIdMq1JJTHV4KJhORXwAXelc
|
||||
#Bc9zzaukdZt/KBQ5rFndpB0nrUPpCrYlh006+3S+pZZ/p8TpWmQRQI7U+x90um3o
|
||||
#qtwUWhGFj362E1cXwz7q6suHiR3qWj2LImyK/STtttD+AbNqa6uvok5sbY3yM7sJ
|
||||
#c/vcjiWVvu9gxtwLY2zI3d1M1M4dA0uBskaPv5C81jdjvLtT+dCW5zFwy/m0QBtK
|
||||
#gXqhBl05Y+1cUpC8kjFd64IMAg31T7c6Ir0uP5tpE3kH1M5T9K93Nzh5CeNb/8O6
|
||||
#s2EvUShw3hRIVfCn26R5G7vhORK71U/1dq3KAxWk0tbB25eMB+9r/3vcKjIURBDP
|
||||
#fIIuLiBH8AewA4hRfKsy2cCPS+i4d25k3mv7maW6sx/XvhtKfc/VgUkP9+koKH1w
|
||||
#s7e3TJ3cS7vHb98f7R0cq+wqMrDyJ8+Pfj04qSk+FcX3D969fHV4UFN+PLQVlnYa
|
||||
#aN5/EuMgt1/TCTz8NBqI/AiPYn3BBVJVY+SEei5OFAVaUYfTumT80oGb3nhCKOLz
|
||||
#NYjQe6/3l6mKBuggdG8/uyvnd0VvzMTXAS5ubM9wEGTHloFA8HuZJGdF1vsUocBX
|
||||
#iA7QOv8wwgM06O8kPkDH/e8oQD58uLUEQUr8PxliC64vQz58+H9CpHI/lo9n02Le
|
||||
#MD/q5Icp010hRUS5W8uS/UTV5pLEYXdHMTK4vRDRWNy/MY0v+dS34uP7yon9Wysa
|
||||
#gz+KkBBcwDwaefJtBYao7IuN/e6r1+/eHp10feEhat1JhOz/k9SQqv4FckT21UkT
|
||||
#2dX/f2QK7N4a8F+dJIHsFTJEl7i19IB6Ra+45sKDcKmQGxS0PWYrcds/usnc1Qfr
|
||||
#VXPOFneCiFWskj010qdGwsRljHRl48DYmPLToHU61v+uHVtT8/qK/tpfdKtdb9x0
|
||||
#EsbGT0CpSvosvBhagRe9c+ZuHeUQU4z7QlMTNUW9693rzLzKlWHEHBVVDFBM7QCn
|
||||
#n1atFnzUbrNAZJ+zdSW+nrOMmirhtlJeV/PluwnpyyS7LnknmX64/8+R6WFvAmlu
|
||||
#eubkuOnYt5HgtZYtjgXjo7Dlu68I2nDXsAa8qCf7aoOttnTewnqqV5l1rJ+2U1Sn
|
||||
#whAe2L9plMJ+17FNvPvyDVhz4kxXtFPtzUTee9p7XNnQ0YTOTK6GbMoDkHHxW/Qt
|
||||
#afV7M5Cj2Z8bGy5iz3yQq7NkGZIqnzBPW4nMwed8riEjQuoXekjmHBVTRw2oQqwm
|
||||
#F9B1uZ14P9zzJ+SFdlAUk2m7ffDm7cGbE1c3RU0f/nvy07/VvUGoeF8NKgqA+KCo
|
||||
#UloMqw8k3TSInOYK2hCp0l3RxiENwxzzSJQROGGQg7oe2TdBvmmXTKzWO/UpH0Zh
|
||||
#Ub+iQZwXWWUY5yo4OqyzDHifrVUzqFjrH8JPA+i44htSWUew9aIVRQo8fVoRZrte
|
||||
#MAfHd8K1T2aR4p2qMx1dmR/v8Zo8XVVzx0H1C1A0OLX25AFtxENpKdx4xEWkDG/K
|
||||
#4kubp8GQ6wnBX56gNS5W+TSmSEilWb0EhV2uuBblohsQYeTFJ0Ez8YCDfwnq3orO
|
||||
#SUVnhG+AC+R2cEUcwBLWvs5Go+lVLc0JAJGcj2U1vacTauS04mla6WfWCKoJ97Qa
|
||||
#uO+KaZ9DDcv1e9E4xTrMsqUGg1FxvrnHfasS5S74M67iyqMa6TXPJnjxD5Rk4Yy4
|
||||
#XMu1zL+5sIaX0Xc4WMMN13ChNl74DlXcdIWvmWwkP61vwHKK9p/+Vwe9QZbJVj/Z
|
||||
#IlpFLE7xZOtJQcseCtN0b4+mKsDTWXfQzWtVX7X8098RPZtHj4xd7FrH9ee7HG/M
|
||||
#M5DywE1P59cAGR1LT35h2Sc0xid/vBH+/Dkyxh8+/OEHud5d5/uZnvuAfmsMGwzf
|
||||
#8Izj+/V25077HLvTRlemU+6tRLR2aOLLp4lOtYlXFyAOEQBrzuOSwZ24RLzw7Phk
|
||||
#X8mCQX/poYDYsxrw2Z2e/aNcDIf5Z6h59P7FX7vvDp+fvHx79JpejxnDWnX1ZVxe
|
||||
#l9sYOL8FpSl0fmuartGXYRTr+zei3eUaXVvDFML6L4baEWPTT29vTZUtBzBNvdzj
|
||||
#3+rzD/fra9fni4eZVKHDrcNIKfYqlCk28ovt7x+8+/XgjdLU0i28L/zswY05nlim
|
||||
#p5tRLnd+5jWj/kU9+LQtWiRCd5BE717pd3rojkC6tVVko6nyVd/Cy1CjZ7O8n1ap
|
||||
#YhbS2xf/fvz+JXaS4Aj2iN0d/daGp3VkX+I9EbbGxie+ifEfSkFkZPAFd1lQxGEu
|
||||
#1fJVFWAgeCNF5PBbmQFo/MOehKnSnOsc9lc43qtnaeSOeK0qHd1tr278ypB/AQig
|
||||
#6XfEVgg3xubAsF3zoBOrvwObTBB86Ho3BK5HSte/wGoFUfr8SM3dXqFnLqTsvVa2
|
||||
#aYD34Ob50TLBv2kq4U9taKaf2iy9PLV11YzTtYt+aXOOQ48rSJRNHZtlRrTy/FjJ
|
||||
#EPxtX7hbYro6gldl9Fth6sMhG2CITbbVvVWv5W/YrH79S2CAyUyyLunTABLSVGVx
|
||||
#nJkQxUF+/Xo/2Xr9MmGC1JV1I9BxieYlNTMGBu2UlbCGa1WAUHP5WgZqFFrHBrZW
|
||||
#7dPzvnGMS8nVyxEXBjw2rHt7cfruOfLuMeruuV971cNLTQfDSxh8h+a/7TAbjNYZ
|
||||
#ZvMEnRjmaJpnLJHpYvXeT12emMZECVt6aT4R6JLBs/zDG+kKVsA7+e+O3gKdjrs0
|
||||
#KAeJTDRj4o/WGiMU8oLHsn2boXcq6fmjR6lLk0z74UOcaz98uB3ffPiwHuNS+yHn
|
||||
#EhrfB4dvzL0GqbXYV2PkscuHD/6Q9ZNWfzZLcMeZtPZMwX0jcwZW5giF2CWKMd1X
|
||||
#ggh/QGFvLduPk1S4Z1C56jHcjwofbPVbNflth2x//QHT32x4UudAlO5HBnP/1fHz
|
||||
#49dy+oMqvf/+9Tvqk8quISYrYDHcGuRlryyzMd5b2NpSJ+G2xrs9bA73kd6Y/wb/
|
||||
#H3Lab023vmacdUPnazQ0LK8n897nrelkdP01TXoTY2AzAvchOwLuNWA1LV6Z4dKu
|
||||
#B5N8ZBMik4dO1V3/DvcVA4Zs/G2Z8jDGTIcHuhPDUfY5dYmSvSCBwEO6xsiXnAFd
|
||||
#bVmvvcgWOHV5wcZ3xDLDgXDH8TyFT6WerX/4+2ujKtNFRpnh9ZfSosuULl7NUA5e
|
||||
#ILAs2BUwv/G4M+gesVSOINjIUlzLFJUOG3MMgM7yvHkzNUBB4fiV5YnNVEquMmhJ
|
||||
#MoCOfzNqrti/YLIYEUz4FsouwOG/a/QGjUIwiBqT74QGmpC+5eBb4FxnVw2gS6Hd
|
||||
#9rrifmld2BUwOp7w0FcZ/pB9I1WPIK0/bHGFz6Dz/XD5DmPHtTmjtvoDsm8n0D6f
|
||||
#QExvM0oSz/SGympwuuIdFSqoXTc0cT1Otf4tm/72I7HvjQNvy9pDeXlrI8Uq4TQL
|
||||
#NAVhOzbqQMoyvQFTWoOu5ZGssvuQUUkAyqsjwaFPA9vIVnnRK7IBK1qzynvt+Os8
|
||||
#ZbMsuX7Q2jHKz0Tp6tVmgJEp8dyitMrX8av/rX338Y0enioJDAlEFfi3hqNNrlxX
|
||||
#//rcLGpnOT6Ow5KlnoEpCJhy1tWsXGGrvSvSL1cHUKKASe4LHyi+mhaDyIO7KooS
|
||||
#O3ZJngYnMYRP7B0eLzgYvmCUT84xnJWoasLJAIAuxmayFuJdVrLsnl2b0EDMiCyK
|
||||
#8FBFLMqADn+YO2P2bj4gi/JjkfLoGXs2ajf3TfwyCnFDPJ8o0QAqmEhn+Ccfklut
|
||||
#F8Eo2rmOKXiqfHFt+Zivqn4dSBNlwj1ocu8p46rmqCnXiDwWEFVqMDH/xqLHReK5
|
||||
#DbozEPzqmJjfwYg+P5z1L6YVwLvFdDqP1Zl0VTSySNuWKF4cMPMsoHzWMLcPvKkH
|
||||
#BQUX0fvbkzk6MCHjxV556vXn+WXW1SVLP36vTnfHxzqh07bPOxFxvUv7+Eg8YKtf
|
||||
#84r7FZ31ShtFjAqHT5/t6uyOLbuZbPBOnWrP2S3uq+oov04YtPs3u+I2qWAw6Adv
|
||||
#zj6WxjiwPsRCbyIE01U+v0gUyRNLcvMor34OgmZF1A3J71tdx4KZ6Pwa3cjgaPsR
|
||||
#0eTh2ToRrn6QpEjfqeBjtl+uLHwNpyDO+8DrFPZLksd/7s28Phm84K2mjoh8Z2Re
|
||||
#NuhG36h0JWsetWbPc8LyI2S1pryOhSoA8mcbXCp7LtBL7F5Mp58oHYQMBj3cTGbT
|
||||
#co6/vLFwL3Z/phvQ5gVI9iCKOeN78f7V4f7B0bG7riEeGqXJw2/0+M+hmicpW8hS
|
||||
#8y4GDQ4BNCsBgEyLPwpo4leqYvdvQqDiHndvMDCdlO3XRyLZRRnMDp75sTNm8XPn
|
||||
#igzlvBiZUpdZcTYVi46ILqrrinx69TeC8K4V+/HZK1cFyy/0UmniVIHdyuWJMxtb
|
||||
#FKxCgGnsmN+9WYt/uONKRA/S64yT31i5g0mntwDhwutVxOubVKx06ywpdavJLSRh
|
||||
#ECq1NVjMdB5hyP2lKYEcpdvOfdWUAuHSaZOgPxvhC7tW5TA/lSw4pfvmqs6zKjhe
|
||||
#oQn8CEupZ3nwu2mLx9zvVYaOz22xasqJUyF05AQiJXXX6kAYFv7KBG6l8dhJlArH
|
||||
#47vaMqCGyis5fqNt8mpxg3gp1R16P9lAY6yiO25efnU99JbSrChDiakUDf3FhSZq
|
||||
#vhEJVS+bqJJyW99FlxF8JSGCoeWSVb1QLBPoO5SKoauyU69HlKUeka7sDJVp/OiX
|
||||
#r9VCTIuwdt2ySaxy63YlRY2C0jV0wxW70XYSdpNJ228r8lhw5btJvMRum7jsq1z6
|
||||
#QBKiyjid8G6IV8SFNMS1OJp9PT6bjgJhKYsKJ6wKHUH/Q3MvfNH6OMdLKy9MtGs1
|
||||
#l7wOiPUyikckBj+bsh2z/QwfcY9S1HBZ+CA8nzb0ZHj0Ffc4z9ZAwylRDQ73aKo8
|
||||
#PX5ir2ArWimvwk3ZNzb02ed50TNBOyTVXJabP7x0CrsdlxA8r+xukZv4xuyzdV4u
|
||||
#zhrpx4/oAL2dRhFqAa/vJD/+KCvio2fzLm59dsip2u1hlttsn4NXhFi1Jd4YIvfb
|
||||
#6M7MBzQVPYN+0iXRce9TBhvrUY82uRQ5v7qZZsV7027QZn2zucjRuDIQISh8jGb9
|
||||
#GixsZd0mb2lWTFm4YLkfa9W+Xqxr0i0Z2G/JDYzJJJ5EMXXVy1E+cddOW8jE1qZC
|
||||
#WFi9OR8HqHkQi8rtjleijEFxKi7XcKXiK3ZloM0OFv0MNDtab6Cbi5F7nh2LuCVH
|
||||
#ZSaPeG6LwYZRwdgF0MFgKsgbvFcXeHFI4mGMAShwzZ5UJq6gZsycYKogeiwZ4xbE
|
||||
#G0e3dpmlNGQV7lRlG+R42ircYmW9cYw99ehhGZh0fAdr8wKKz7qrYfMHwdfj3qAK
|
||||
#R4UvziWPXO5NQP72dxDNc2U3xuU58nNQTJmx/Ct6vi0mCHwf4yBs4tEzDJP+Hvbj
|
||||
#f9cyyLyXAJTQTyZ0QfglWy//jtfgLvPsih5OVC3Y+xJ0Za6YjuXLo+rd2eksU0ah
|
||||
#WIj5KrsYIOdvyOT+D6WbNrJ4q74/6rzeOJtfTAfdcV6WMNwN9akji/LgMFbnt9c6
|
||||
#VVHSe9g1IkJgUy9jpQoqAeBgpOZ622jL0q1gz21ccwPdqGahi1EmsLTfe8WxfAZP
|
||||
#m1DeT7LP+E4PDQQMfrEg7Skxqi4+b4mX9RWs4E0fPgr2oUCAiFcO/Kf3dA8TE4JD
|
||||
#fzsDsErgSrxKsdpBSzeBdxdUgurdC39vlBgcVDGh4reGsDjOMydv3bMnTuljQ3VK
|
||||
#zMRAKyTaZuQCdGEfaQaT5fXVWzdymm0KAXHZpi0AT0Syt704I4F8DsSXtCyibkjL
|
||||
#EEut2WsZqBgM29bNCq+UXi/xjMiwoC6oxyTU1l/0iiLnoyTtnB1T3z5dy7Ruf7Bi
|
||||
#ahoZcGsmvLLSmucDYYq5Z1PdtFWZVXxlWVwXU/+EEppZaeUbuMjmi4GIcLAYVHPx
|
||||
#Ak9coIBox1tLFKMvBr6sq3+5U85Ru7Ww2HbMEIiQAqIz95Ib7EsMOmCzDKrYbTsn
|
||||
#QN1r9gAFbV0hUs5MsRhwckXQBwZexBdeq3wRE/aGaLM0rOFxBueisDjGky4yOj4u
|
||||
#Yb223OTs9/KpVZ0t6thCdZDu9kZrfKRXHWOI8feDxAskHVY8mUtxCnzusqrZXUKG
|
||||
#7ohq3iTwtTuvajUleMGmYJ/1KAIsxUHUMJe308KtBBM//HDIoGFl361mrbdluQRx
|
||||
#m4WmRrFbcXadkm3toUjHiV9GGqvM0BMJti/RPuAfdae4dt7Fz3B6iRsErfzN5VPK
|
||||
#nhbCaWyfRGZoNdhvSzBxLgfjGSF1UM3foAfv0a4hdb23MQNxVd0j9861eO/Z6I3O
|
||||
#lKPUx6hBZ33Mpov5bDG3tpm4EWmF+Wi3y6YTQ9+IxVCwmI1NfUUn/DmWTM1Ei30X
|
||||
#WBl2IF1p3WzpONesniOgVLJWYY+HCQxMwN13lq7rC6HIKMYoEqDJ+UsJFFWZG5iq
|
||||
#nj+VMfOY9U+/b4GGuWgEJsoxGpAM7ZdUYlEZTEe9JLw4a/ypuPnbR/hfY+dZBx8U
|
||||
#xrB3da4KTVaL12HWtGaF9TEm1LU8hn/sOsb7on8DZxXZUJUKZcmsyCdzq5sXi4mJ
|
||||
#omN4dzOhM17gA/UGHHTIvvJuz9gw0gI/GVMhDvzD43xoKlbFyXJRmvytfBneIVeD
|
||||
#LiGGIX0cyMhZtgq3I3u3BqSgToRjFAnk+XgNhCSGSjCv86GB4ZSv6bw36sasiOpT
|
||||
#HnTML/KSFUZMiAOGjfRPsMT5sMjqssRIIGZ75DLZ7MKAhBkdnKQdgCLbQBN1AHh5
|
||||
#6nbxschOG3r8R9MCMOiOe8WnxQwt2qYtDFVh4j2lTflWXmyuGA4fTou7r5hyx1i1
|
||||
#ap6pnWZMPareDYuQBaC+zJjjg7VVGN1FhUzRC5bOVIFQbr6UXxJQimHS6GOI1iC1
|
||||
#u6GB70JBq5WUE6nvxu1iUjpPDFPfbs0kZKf9RWNme4D0Ei+OHKKBrwMElC7M02xZ
|
||||
#xT+jrDfpCh8SJeXl92YSba7a+aQj4sQ/q0CCbfIUR3Qtq+lF4jybdPkKp4vTqGh7
|
||||
#RUPWtQHx9KBbQvvWK3qyXnCIzxTRosrj2DgrEhFxbFhyYNTi1WWcfRe9lKXjDIQh
|
||||
#UVBk3O/SqUOswcrxR0G33hA5CH07BCRLBDwUVKxdvRC39Dq8bA1y9po4b7dap2Ra
|
||||
#ZVjeqZIGLREyEpngnZMBpoyhkdg6xrDBydGvMd1QA8YqJblsM/ExquJlIbv8YHFV
|
||||
#dj+99zZOPPU656bcL8V3Q8iDbHmR8poWOutOLvY3oO5UeLjc1aoYrpPEFtVIsmBl
|
||||
#qEXpczy7KUZXi4aLz0laBalNtkVVw0kCOgmaZ2Ou4sZPMI3NM25mWV0LOMw0xmpR
|
||||
#NyLphPqJez7ctqLP7DqmTszwqqvveSqcwLLqJDeOJsF7ETQTBOOxJxU4w84noI0M
|
||||
#nHMnbemW1oavF2ZsCU8tTKuRk4uqEznCSoYgCs+6oUPpS3V8NZ9qM4VDSU3MpX0P
|
||||
#mUCKgEmmJJ9XbCVxqZyFvMNBaxpQ9gBWaZMuUJGzL50nJCxP+rhR7wQS2k0vYjSR
|
||||
#BLvr7PwKjy5jqnW5FMUz9IyrGlp34l1k4+llRmzqVxfsdwfWo5DAyHtaKvicV2Eb
|
||||
#DNAyrGtV36gowL1FLzD1bdJ0K4W1TxW3C54PnviUimgOCAt4UVJV6fDsKY6nhhoZ
|
||||
#q0rPjcjZ00oTq+/PoCQbep6gEgR/5eUFLHIqucGnAp79UKpyLMIT6YbnafEscT7V
|
||||
#3OVCeLmww6LatknENEMZU+cyI7ahcynIYzykijTZpCXomVUJmOkXlUJDFq+aiQ1t
|
||||
#vQFQ7zNrhvrXKXiWZnrEfWD5MISnxceeb1+I9yqoziuEsG1cPD28Mma3lWRVFYUA
|
||||
#WukkQW8qB5B8u4cY8BWLSjBmwfKi/ngW92qHE8ZCkr+fMvZey20ncAhh7LGybngB
|
||||
#y3CUoRoUO40YjURHq71iVvgPaWnN9c/KqhbXqiNWFDn5SJtX8K6Rmx/Ggwwfp+Eu
|
||||
#ZHpebCbTs380AycyJ/oi89IdkEfHNOq6xadf6O9AZTuPT1tSScY9BlGgEe9m5IpO
|
||||
#Sweu197n2tGeZzbDqXcHJeLurugW2XX0CP+MUX5FeCiAsdbM4wNUuRCsiDccXWQm
|
||||
#04l/scUkhQKbbh3aYL4qlVPunhagvVF+me3EeshVJzI9IOqcgzdEY2taC+PLEiOi
|
||||
#WYOqlpGgXI0TScW6JlFUf3N3fHZbO4vE3lRe7cdkVSY4KuHX0fSsN9ovjdO+dwtX
|
||||
#WaFZP1QCWg0dJD4AbsvvbmUmt7u6Sft5QKuxMUMvqYJHfVXGqGimBDCYXk1GUxiy
|
||||
#RQH7kEFWVjwOye73gJik+BfdUT7O9cHYX/zdRlA6E4GRgSUopHb2GTZGOw1st4ke
|
||||
#4u5BhIveT3/513Ix5rWo3n5+DqXb7ePfnv/Uusg+D+i7QeDO8gnxH8Fr4oWoVQAj
|
||||
#q3GdJBH7qiJHu93RK6Qd09zKrFuWI/QGKkbSy/1iPp+V7W3u3g6ryAzwy8SNEDRZ
|
||||
#gh4Bw1bAnHIHIanTI1SvIU2Qg5skpKdykrzJoNRvJyfvFE6AdN66mOJ44y/18KbG
|
||||
#vW1+qIUQ0ZZy2YVDVxxT9UCc10MEhAsXtY1eGRj2GH6rqniJ2K9M8dPVcQdmN4Ns
|
||||
#TQdYhTHfy848Ra56XPVzQ8dEdvJLxcgUXPesu9BtZxEqFPdvkCZL3FdmLXfqUrU3
|
||||
#M+TREtGO0pGeN+x+jK5RORG96NCVOBvQCdUDDFBVHijbR6QnKW8/NPmoldXKEdOf
|
||||
#TmoiaqennmDRqoZEv13dry18osvJEW0XWceIoIVRFY2P1dszoa/t+sNcPbARyVMh
|
||||
#4ZJ7zzQfO3m2GiXuDqzvfSTTSS1eUvRnk8v4ndroOqQfhTaX9h10mNdebPsqqPGL
|
||||
#1PNe+Uk7n2MogdlFr8y4lSJryZsv3mhHuzbr4Ut0ymmENpGboP5/ykq8D0rMaA6N
|
||||
#HXIngEa7/Q4rUh9uUdlve35Bb0lMJyL6yMGb3zupetuwVUL31ZIFTUJS9/jg3fOj
|
||||
#5ydvj2pgguJM10VDxw665NjRC6rnn0FB0iRKNW3oG6kVTXhwAHJVozVNICC8mivb
|
||||
#QHVXJ1b5O5h8XCpNUbogUUdHyS1sDBiUKK4FcWpDHXmKGwOJzjPIBCR48FjSLlYm
|
||||
#6D2VUCJuWlz77jt8w94hbLgaA9XwYFVc8JNymtVOt4bpZsL6FUIyuNARZvXbm54S
|
||||
#l4LowdApijpi1ZAE8wpGYTOEWWVkZt/IoJxyFOL4NwjTtJWiqNVfAe3386I1u2K7
|
||||
#DnqgJVXttEGageKFIt6ORvJQYfwwVQ6yukHfFADK/nU5z8bs6cX+BZRrI6Sq1g6z
|
||||
#3uWtGnNrGrS5epl4Ka7utJPUkdC9L7l6sSgvbK+CLYGMa8XcjMgDld10Ydi6qtaG
|
||||
#p0w3W0/izAbdmueTBQuyYOMUmCzfcqnA4q79Gd59AI7QiWg/qRAyju9c2SrmXysq
|
||||
#iI/KLyxSl+WGNRy4IqoXVY2gWa0P+XuCgF+DqAsZJQQql9GX51CrVwyiGjPbt6Aq
|
||||
#fNbrf5oXvX7W+enU1459eqnxFJ31HJ/oyVDUFDdNO+x5WHFhrvKYMriEpuCs1ivl
|
||||
#zED9pUrjoXGtLiAB6chPWvOIb8JjLBerVttCN5vgfbHBTsMkSNeR6kbqa8YbOy+m
|
||||
#i9ktFM016smGcHNtI5U11mhBdcJWqStaB9th0bEg2n32aHe7fzGeBt/dI5ECWwr/
|
||||
#W5aYiY/uqJDf/JNOO0YjljKaiI9u6X2yG2zt8SdYZvzvLm9+fMk+YAVlX8VYfHSH
|
||||
#8rPwPmW2bHc+XfQvzDczFauLfs5CoN7L7KpkfWNTWRI002xsfLri7MONC6gU4Ows
|
||||
#nf+5uB4a1q5bI82/ylZ4Mp0dZpcZWguTp1HL4UzFQlM+YvwjsCC2eC55z7nP6Gzo
|
||||
#LeZT3KU1zI8QpMnBO6v6Zy1Lo2MgG58BaHt+mt0X8nLqXaZa/lxMvLRgvL0QZbFB
|
||||
#V5TjIx+ONwlhVpw8fe1Y33aEzUuiWfUQB8bhTRfxrjtlDsEqmF1gQOaFSRtxn7WD
|
||||
#pbbp9jHDzSCLPWYZZA5q6g3Pw7TuBUWnjDZSkWWcGIOMUX4WpJmX378hcwhPL05V
|
||||
#XxCYwJJy61elBfQX5Xw6VhE4GlYj8fnKPJmr9BijID2CDQt/z95EFBLoVb1z7kUQ
|
||||
#DeSDly8mvORT3cAuEw/sIIsJNdKWKGaci7eU4XOjg6ntAc23lMSE9+4fWVBgH3JE
|
||||
#fgL68LxXzPNhD694Nc4Wc7pjJiJMNn0gBsm28rpo8rbj9itEJaoUa1QNrSvQtVNe
|
||||
#Bz0gMq/AysslqxRs+TrKPJXOCuBXehp2e1EW22gvHcEXEh3SjvH2vBKO6sFiVTxp
|
||||
#aLnaTlytZtpkDy6rDlkxvQ793e4TNz+C9F9NeYtIDfV1RytQfaVyDdXR54JFD40Q
|
||||
#KfWJYdeZihbem/w1R3Z1r22LaywqOA/JeM5tLRGYJUjmydwZYeUMVUsI5s51SGmy
|
||||
#fNCBmDL34NOW0NnizNJnjVnd8OAa29GT0LvYItaILXX9AZ2K8bXTthlfHF0ajiLU
|
||||
#RSMtizJrg8zKgi0VVoy5aaoE9totoMJ6+TnrNySITR8kTdkumYPDyGvhkXP1yTJ2
|
||||
#gWkSMuz4reJi52rvSSEOyk+lFxHb8D5LWHMfnCKKaIUip8mH6sLvdDEZpJVbYwmY
|
||||
#0Fm5Ytroo1hczle8LgQ0N+HcWDfCrbdraJc+697bNEQnK78uEQ+y7Qf+Dq6ZiWx7
|
||||
#PMAz3ZrL3dg81fBWRxQm7rXgg12qDKnSWW7XAiMCSvzwug67Tfcsqb5GJ9QM0189
|
||||
#vyIZXLuopWCc7CJVLZcV6XoqRtQYaQgJIqPHtZtdLy60C0QmCeXO6CODsaswlrHO
|
||||
#GLIiQ9OQt4XzLh6UF0RJPrxuRPvEBTDIDtUg30tSCjn6EXodV8LGClEm5RmbFpXC
|
||||
#wQsCQHXQUwEFhoO8fBhcMPSIEaAhL+tSLCpTT38O5JihW5G3f73VyWVlUePWFXlE
|
||||
#gHWGc4xbadz9c7fa0KrlMk51M7KT0udMMdezhK11dXhHFAN5LyLWV48g0rdq5RKa
|
||||
#JCqlo5ZvPrQxg3lM5YmRQbOiGXHmuxalVoRpbB3LNkHJuunEz+5ZjN2INYVJEnuo
|
||||
#b9LCtzyiUFG48DuUQtZYoJi8NkC6oiZgMsH2yMSuYbCpQuTANgpfiRAH3oOmsk+b
|
||||
#LNAMpsjbVUwK2SVeih68b4e3ES1LNVxpzWrKN3yl86b0n1TPmXgOC9Vvu6hR1nKa
|
||||
#rs9O+1LvNzt1qXZhIGLfZ6FWYeKNSIzrJ1CUSl1Gpa6gUj5cYy1IOH3NyHS5crGG
|
||||
#QI/Uq9eN9TdaZGuVY3Hj3D8kxyO5msfStxnF1Un0fyYYXqKx0/541W7uUISJZdUY
|
||||
#qTgW3Jtvew0fG3lDPkCXuTuEIRRW+AxE8FuJTuUd737d3W7yI8UrwH199XdjY3sD
|
||||
#b/iyeT7IvXvr1LQ58de5HdMQHYU+etJstU6DQcHTQoYGXjQ2kJYpxQTZ/giNP9je
|
||||
#TOTVbLt4cKcE342CW/7xmKKhUbvtqZ8fbSE4PHTRJ4gN/9Zobew0G087f/vl9NEv
|
||||
#TfhqPhAMSXYL3HYObLQeoMWDJ5vJg5/gvz+zsnRpFAuyNHLeS58q7/9f5H0TLAqw
|
||||
#2mNAZiKcJHUtdUm6olb/ujcJq9BRa0UNyqugZ5J0ZFc3fEqqvjdP9fF+Fwh58+Xy
|
||||
#S3JJ+lBaOUE7mtx12ocZu7qDdnNajxFwbpIv/S9Jv5Mm6Sm+xwY6bl9fdX+IdruH
|
||||
#H+HPQ9B10VujnfSTpfOpiDY9QFV5jEZq/1EP4hjyTzo63nv75rj75uS3o4Pn+8fo
|
||||
#qkQM9HHwSPKMdr188AQPM/JIt8MDf2KdozNlg2m3oaGXr37tpOgH3J2W6ak/0Nuj
|
||||
#fLL4vC2GWTfrjEvpNq522/3ZIp8MpyAVyn5v0tj+m14Dp8XHcqO93aRJHzQwLq/y
|
||||
#yZe4lCaa/P1qnPcTAI73wpM3i/FZVrwdHk7PQZEdvTNNlMkWrTV/J2JVlnpGVJRt
|
||||
#VBLSJ6ZBedArAOcQUcC+Px8lW5Pk4qo1AYwVMl/XZOilHB3oJ1F2U0FyhtNi3NOh
|
||||
#HkgglsEiVDo/GU8g68DRVbMOlwVXapk0HpkEAriVPFk2Yx5eEk+6EaPUdTyab8ij
|
||||
#UG366sCKe4q3OdXHx49p7EYBPhq4owCQLVsvBgb0jgebjYVnXqvR18iPEURpoYwF
|
||||
#t/Ps9AG4ezw29fqLz1t6aZyjw7wNDMII5qqqtxV8R3ndRaEk+J1cb+GrD1FyiwAl
|
||||
#fsDgeCgvoxoaTaI65ggdow0cr90ixk6N6sFaB+2jCkc2RqY/MQ5jsFdSPYm+EEIj
|
||||
#oKZzNurNSrw/lo+zhv5wGF0AEvS4n85JtpPGvz5ONpJ/fdxseuuFLrH1TNfCQvSX
|
||||
#GwNYBVToUwYPIFUCMhUElBKUzgk9cqVLtvoZu/NivSjZNRcgq8LJe11PB/CnEaMC
|
||||
#y4sk7kjKIWCIWoNZDUBdZDmuACkK604tmR+rcM2UYxePDxT6QnMVNlrGlTJ3nngx
|
||||
#pTHAsqva+lg2Ph5vNH+OruMPnlTwon2DRTUDmkOAslXHSRv3J4SqTphcPXoQbbxO
|
||||
#5lYIoKorb/ZRWMSMHku0WaZw1Q1fne8HwlWpPBQU7HxSKVp1IWKG7aqQJZxl0PmY
|
||||
#uqBrNitDLSbJO90TsrzSvskTQE11rE1ZInRdQMpW6WLrSXpGniEJ2LF2282En95Q
|
||||
#f7xqtmmHsxTdSbv3bx48WcJfP8noLv65FBZUEnf99Xgl38zwmjOPD4qaQmM89IOS
|
||||
#6mDgzKZDR+QL9MJ5+NAmOvXXgqBVpgsac0bjiT88lqI8sx1swt6h3PDmhWkLOOUh
|
||||
#/O+RNz19SgXFsQVfebD4Y8Odv7VPH30EefDoYxP1ctiWtnUaJjY/BqoqEeTpU4lJ
|
||||
#oiNCCtitjXYS7GzxD5BIk9UDoqED+rqIVqCgMzQxGj+2S9CSZnLSyblVMUDVCyu2
|
||||
#E2UR8hUI3W+jN5nqLPR3s9F7qxY78m7FD3jpkLTelh09mzVQ3TWn6toSp/pO50Pp
|
||||
#OdK152HrvR6riqfhGbY4fVe+6fcasabWuRNYeZoc3ID3noljoVwiwXR0/KE3U1ME
|
||||
#vVQoHgCAjOsR/7HIyMT6P/FfcXfXtmKjyAdX/E90BJzsKjBJe3t+B5CME95MEo/a
|
||||
#MLxas0V5EYSE8btQTWYNJOKt6Jlg3Lsztq67hCFWwNhFq2ZLLF1dfJbGXvCRa6KD
|
||||
#mrZAs9efy5SFlaGbFLjN9CsFneDh+7TwWGdv5pIZV5WsNGA36o3PBr2oSsB2uErP
|
||||
#i+5wa29s1Rh/lL2c7DHw32B6VXK7DDdSVNxXU730uIV3zX208C4tWqNEYXQ4d/tq
|
||||
#HEb9VQme1XAb6cgemm/xHCxlXMYfqVeaUhFj/DeOLJGh0dAleefwSR+vKP5xRfFC
|
||||
#jC29xMFLb1sHGPPWdc56c9AZiWkE03Vk4E/zp8poEC7ANSIh1NSig+Cw5CPBMEBi
|
||||
#1yFddVJW742lHlui4p4XiY6O4y7f7I6vuzbRpPWnPZNoFUZ9h9ZF0PE8ND5llofx
|
||||
#qF+DdYesLN+12oFUewYsTuoNCm6xMCme8mkLxlsKWiO/oGzWBc2qYepSRtR6FkM0
|
||||
#YIZa6wYnE+6TAdofmVa634IyX93vzukzbIlcwnzGM4MCWUG9GJqrCcbjFMXpZEVq
|
||||
#jE4riFR/dm1nCep0zuGzd89hr/W9+LNoOhNd9vEGaO9eM0TfeMWVkXeBz+ldYFMS
|
||||
#rYf2I0oSN/vtpJC4u+7aM2vhc0I3KqfzCwrn5cQK67IKxJX4L1mL3MoXjg3ym2z0
|
||||
#AMmmrK2pTmhI4QRSr1fqqxOJer58M2kT1rYPMvi5+mdT6yviqW06ZlL5OoVOTtQZ
|
||||
#N9dGVBm2JX7w8abR+dvyFDaiy8pDUqjFH8WtOifVDvPPPLwNFI63dYGjvFC7h6p4
|
||||
#wcEHZcSURqgZJQYniJm7jCgyQLGmi8aj4pKyw0idUn65vPxiqqiE2RdaY5f3by4v
|
||||
#2Vu2y2XwpF7A6RXgVQhwhOLDrNEE2KhvJuqlaXO/QV2QEEGP1u57VVcJ52rUwl6K
|
||||
#Tvm16zScysDHnyagKla8zzi/nmX6gUakLU3wZdKglzQplXY6JgotXqe+f+NzmM1u
|
||||
#rmkbE7OQl4wwnyprDZKgnUWefjFPpEzmFdyeBQ83KjZYNfyhvMBz2x/izat/OhsM
|
||||
#g9MoPvEgwWsOE1LDDJNNCEYgtrzBztMozfZcwy4cZjGxK7yV2NpArapII3W4Agtt
|
||||
#BraeeUEVjP6wENZHETaiVq3BAoEC6LnbVes30cMPqaoQ+XQJpFOLJ/GiaoHt9uaN
|
||||
#DR6EkKIfGiaFD9dNXDiF9ue17wfdZ4uvCUpqNCZvCXBDtJiUF/lwHgxifIcQo4Al
|
||||
#KgYU5SsmfNcsl5CLK3r19MUC2msFBLYbumay5JUwFIeoo5kOdBIZ9/QTsFGT85Dh
|
||||
#DckZzaBETAXk09D1c7292v7By+fvD0+6L96/Otw/ODrG3RZVM/FG1WWttg4nab9m
|
||||
#1/rnvrFT2O8SJnU2humuU17xa8jmy692mH02v/KzomfT357hlTH94Z6hcAnaE8Ym
|
||||
#sFut7eOLHgyABKjSBNhj2JTqn3/t9fv4E7lcbWOZl37yNDlaTPBQmD5/1qQNHSq1
|
||||
#Em7s0940CHMSkQhD8JwZt7nxkhdrdYP7pcx91G+ETxL5mFnsFSLjOEnrSrjTrngv
|
||||
#ppHOMQSOe8np8f+HVzALfBzsSffx48f4X6v1L+Yn/tdUR+bl9TjSyryroqh0y4vM
|
||||
#3vbbpQ+8i2TJ4ptlyV6oigmbX3BLOW7RTUsVvC5FO8+rt2hpzSaNjr7QnuL7Yokp
|
||||
#oo8782kYXjGf0iFWi05ZvFzfyuO5H6k/8gwjrCa/jI/b8W8Hh4fpKZmiRAJGb0op
|
||||
#2dGnw0tA37b63hnBigLqpErNphat+bT7S4WzcNqscRdWpn+HUFpepDFEAJWqrIj2
|
||||
#2Un74wGd5AaF67XNikZiSzLnVWN01xZ3++iWny7Y9isppwacBSy+oRUb13Cq9fr4
|
||||
#r8fby5XcDmAcj6fZ5DLVPI7/Mja/QSaXTL0kQ6gpE0wABc0fxjVYv/YYBD1NOuG9
|
||||
#Jp/SzxrczhOMg2/rqVof9bcJw001dNrr/HM+MRYNnbafzfAMweKr3bpISJso8OTx
|
||||
#gCeyoqQ9Hd/Vb2rJkeZeKxRWvJN29w/evXx1eJB6VkNz7EsRkuJH87UQJPV36cl2
|
||||
#/52VXfNA066Nb0VHzItJ/h+QCOPOIYpXX+5mTeYv5Mgri7BvyAp8s2lkoNx61NQs
|
||||
#dPNSafDBYMZuTphB1nUGjU/89Vf1h5JcDIx29hn2b0CxPUx/TWahtY5XNba8mocF
|
||||
#KEOXue2xHUKbLN8xDwdbXB4Oz30T1wBd2nG3SOsR9+z/IWD+aD3+UZG57Rk3M9pZ
|
||||
#BJz9lBeWq/Cue5OwKOfM22gDOdR7cJCD6bSVpQJWPJoqlRFxZfT+bkCRYF+cT2YL
|
||||
#vN/JNsMelqseaWLSDFSj2aiHD337C6qa4G11iOhLYNlTHRBYw1y5ssrK8VoS2/CN
|
||||
#0WcelKAERj7HuCCo68Pax0mxyyd8AIlnxoML6vfWIkyIZIu9UbKu/A6IZaTsyfOj
|
||||
#Xw9OKC7qbvB8uyt2/Pb90d7BsSoXLgIk2LFJa5mNMDG+mqlk+vH7ly9ffVim/pPO
|
||||
#vEUr/j0GirAQDsg9jb28qwUMjHi4p4OX26lvVUwUrv4L2nYVET3bZPPdBxRhSDI1
|
||||
#akhLDNHMQEXOngcxSoiXRtUAxGnrvUhqTSUVvs+GlAis8olIZzGKXhEzfzr1UMRD
|
||||
#k+K2WaCAxWgY95+OkSy0QWpSKkVBh7ZriEC3ob4h+8eezHG8pBMbgbgzQlmAIJ6B
|
||||
#1QHtLeUqFWmVhBTxNUMBttw+zCf4Kkli7nfdvyFtK3IpxMzk5lJf7ELjs7lM5hg3
|
||||
#eusrMlKrMLsLRnXmeCCsDkRqx8OG4IoFUq31Mv6mqrZS2vadw6FUX7pTyifPR/Mg
|
||||
#TTeQKUpMkEtMZMZ3YMq/ffHvTpTC9+GrF+b7NMKIwRO7BDZ4SXnXe0oZ/1g9wAgV
|
||||
#/y1dzbnBM7obphe+0Ihcl4iwlCbUkL9ni0hbSrLdg6f9m1soBgGlwmiy2Me6Q7Lr
|
||||
#nkTecOV/lMDTL8P6WG4m8ReJQ+6r6uf6W87vycbaYhnoJ160WU2xtRQU8053O9lV
|
||||
#fZa7C58DRoPK9e7+zYg+l8j8EaGLftcDdXnUI7Zp2anpKiFiJhNvcVnxr99IV7XC
|
||||
#1dC2bd/N8jaC4R+xC2X0CBglllbvlqWxUY8mgqZUQRTCV8eY87Gs0EzupDxq2scK
|
||||
#2hGlkh4OtcswZ4e91/sVHBGsFqldKivXPLuSGIFSxbnfeFZqy4ISI8lT7xXeyolY
|
||||
#+QTZ7dvWpyrrtx0XAb4AcFTygmGsw1NrbkdW8UoVi/THA1JYkmemJSgJetrxXmp3
|
||||
#fQiDz51oMHymxRGI45P9t+9PPHOjexKGooNWa/YOQhRrOTtDJr9/Y/q1XI/Rg5Fd
|
||||
#xdCRO1i34bPZdZTJmFXGi8YqTTK7OuIiezKVe1KZuwv6cM8/va+bTEShbl7qZyas
|
||||
#ZtQaZZNzUEZ+SZ40gS+iEh3/kCeztosKQ8ratXz9AfhE4SJx2zFzJWnLVzwCAKRh
|
||||
#oBLnP94gAMY0P/f4ddEP9D6HcwLZXtfs3Te8N1n0VSARYDh1Xfrlm9fd/bcnr5+f
|
||||
#7P3WBG10hKE+6U2xkETMEX4YUaoYjoszLBZCgAytR4o7Brp89FqBLFj0m03/0p0k
|
||||
#bUcDO1UbITVk6O2s21658Y/owQ520Q/gehc1AcN6L6ZqIeya4dx6avktIhH0Syyg
|
||||
#2rjtnzwcNEAFB6m3wSKeKnqrDlsSIPIAdtQ7+rE5dbCfEAVCM76LZiexqduiqtfB
|
||||
#v+822TxUg0+Q6sbdJlVE+qwiZqAaBtpYjXGDXkTcNLFy24GsrI+Rg5UrCvRnDTuK
|
||||
#m3TSUWbFZYaU8Z+vX+fMpmqI10KdN9DwBWNEmhoh6SU3oTTinrSjT3qbf9WqZWIT
|
||||
#P6X16+c7a1nW1+SfuATaNm+xFkau/gRri3+YRufvMUqzGtXvu6jH99rJ390Me5j0
|
||||
#Rni2dp0QwFJfaaGwlD0XzDqtfhPGu3rp3VIJZmq6B83N5TtNdSqUN6G9mWlNE2tN
|
||||
#x7UOJj125Nxkf38Nc1p/qX/2NuArNfz9V8fPj193qxT9UEl2Xf0mptOaXSQp++0k
|
||||
#mAvfbtd4mH3+77hrOzz4cIsB/TWbZIWau6PsM4hEHXiG3K6/+ZofMTh/w/FUToLB
|
||||
#mBpfAGW6a7cDq7O3hARjr94gCZKNfQNGImqxfivsXustHmvwWL1p0F9avg0Xhtao
|
||||
#VVz4/Gh9HqThQg6ErHneT0Z6IHtF/wL2GF9tdvp2LKaGdE0O+6qWnJ/sP0tGYW23
|
||||
#nyFftPJLUq5wucBFxB2uaE8L6bE26/erT2P3lt5JM15xwQr/mWyPBv0g2sas3+2P
|
||||
#6ZY6VH63h5KuDeX8nVy4NYzWPA9rfkMbbswBoHbeaBzXtMpyFrnDieFXH/5Zcxbu
|
||||
#XBe43bmNPcv5fa+cTtKJ6yvZP+768a3mwt6HD6tmw6xL80EV9rgP0DrPJjZ3/+Dd
|
||||
#rwdv6qMBcXh7deD2YtDknlMz8bujg3dHb4GNj7t7e8TK1Eh9SQU9VZcfsNnvpP5E
|
||||
#HF/QSGGdR27hUCNIsdIY/u5dtVq1vuNCdBrrifBdvBG+q8qlTz5vr3L5BeQRatxL
|
||||
#Eyc+2hZtOJ6N8bQQJhQe4dga+uz5gSLMabOTtqqOGHQRdL21acANpyFHAdv/6tjJ
|
||||
#Fsahr/HE5Q/fU9G494p2Ka/vbCAJfA/Meln+T1d7w5gwkWNUKcUkrurg0hU93I9f
|
||||
#Mbw9G4tLRn8UZgYt2F7RXsGMx78dvnoBsnglN643QYRzoEWjWT9lngVJGP7wXoNC
|
||||
#3rU2tps7jc7ftk8fNR8s0d708cn9Gwt6+fHjT5XPJ/1TpzVR8rvNa35x7b/o7OZd
|
||||
#uPscP/5t7VkOI/KN5/ntN3F1kzj7PC96vjNS2i2pqbrYznfuRD6ejTKdEfSi6sGu
|
||||
#Fa9AoTUXtRDGCLHntKqYxlRHrtE/o4OLertnoZfvzJkXZ3d9hL6eav/nv5px9vjV
|
||||
#/z5Y34iiOvjdtMd/ikUWr+7+dzTJ/vX53t7dbLJ09+uPZ5RVY9lumzE00tK9z6ZB
|
||||
#tlHaPz/WfZf3VFyeXiyxiHJD1VluIQ68pKHEc3sQAbpDiJFYbP9AeNVhtLdXjRHl
|
||||
#xTHaMwjdjQ64Gle3q3Pv0HJtTz98qOkqZVa0yC0s3GHU5H4FFepwMtl3wYrq3p5G
|
||||
#+zWjsl89Jvs1mCh7693IU4eOzr09QlhxPcr8YOUsVTw8/P3182MtVX9IYki5Ig6v
|
||||
#HxKOmS5h8cPcbyVp/ij4mlfVy+tJ3/jJWNMtLKq0xISWW+11OMpJ/JsYI+r1nb2j
|
||||
#V+9OMM7In646qgI1nrDfreJM7X3M8g1rjvVEwt9QG/9hMVvkzfbscz4nZXI+BXX3
|
||||
#MhsJCOrGefKWevyOFsl2+9XkEpb+wVt9Q/GXJHehf7XXRGu2gL1NPm35fk8ifyGy
|
||||
#CJOfouthGDNHXdbGKMr6QmNDPZjMkWe53L/w6gJN+UQUeh2Gv8GAUeYho/NYP3e0
|
||||
#tYXhyHeajZ32MwwE19yRoeDiT93ZUHBBUZWNV4s9PyqG64on7NzQtspR3s/uNR4z
|
||||
#hUIqX2ewQ6l7xk+0K+gtaVx2exNzc7Thk5hnrktnHQBcZWIgI5HDIdIjzJoOK0dc
|
||||
#9ouDCTpXzfJsovGndkEAXClieC6F6DATS88mGMhroEIkqqfQ7TUHPqF09OzkCwgJ
|
||||
#disbv1rTSSPdek5RNrbQW9h78Z03XeGgwwGdKUCkNyf7r47SJrsVDqKQ+z9GYsfb
|
||||
#cvYuMffNjTeZqSYtDX4/1o2aFPlWX0Ax83NFM8OEjgCaoaeuHM5YCDoO56VCF8d6
|
||||
#a9jLRxhb3qN5LKg+soYuzptWsVkfr2hTBVHZushGM7+tiJRcG+w/Evt2myKLfett
|
||||
#xTDbcsj+5nf1426uxUJ1hJ7SS16/3TfjQQn44IxwgNbvB5oslqMe95sOh/KULYLq
|
||||
#YApTvMy7qknf09dAgk1bP7s1LE9Ax+2AvPsnqvs0H/2BFMJjjZl6qadNVpxNy3VY
|
||||
#UJdcDzrCLaFWlN1SBT3RZZL7N0YT+/3g6PjV2zex2NgxZkQP+GkBq/29VUvGGgsN
|
||||
#LMxsJtvWIm9tsmJ8+y8Uj3SvN0EvTWRt6CCrEuvck1jnREBHIWNiOpy72zcMIulr
|
||||
#vId1ZgEXOTFCBK9r8mn0+UXG9UV8D0JH9mmlFRAOp9NP2QBfjMCYG9HuqPclgUf5
|
||||
#4a5HLoe71Dgj3IuKK+vZZsAkm2wGbbplbzNYMjzFlYekA0XVDVm1o2/Wsg7xcbdd
|
||||
#v39cq+Cy+unTg7cvf3iPKUjHB4+XSQfvnfRGxiB8mnS0ekP/uOQWPkn7w6+icBvA
|
||||
#bj3fTEgRoCaOgSRIC0WtpAGqzATGG8UAninBZiRh+5WymeQTpXiNgOFayXsop4cF
|
||||
#iPSPxURFIMWqxDVbJ6hbLeh56h+w8bOEQh8iCrT8P6NPhUo2VyoB80xu6DOJtspp
|
||||
#KiAZqAAIwQzYs9+PCcIBDSQ+rDjIS/ppSrSgBrpV99Q+b6vMkCfmwKHYj2Q6NCX1
|
||||
#aKhnGJJXQ+qFeoWAOLBUfUu3UsrRtRC0bnJAVnKUCVMTOpuSLbDhAqhNrQqAj1LE
|
||||
#W8OdJLCNofdw8P0ISCBIVqWZX/TmQNfMwE7Ors3hDf6cTkbXKgvNkpYa+GFAYftQ
|
||||
#z5HnLcbRvsoxkq3qsQKMUbwdcUoiYZmc58glxmDbm1y7AigzNHBE0BCllZwAVA3N
|
||||
#AGdQf1DzbZzha6coN9B6SisvYjMrpnQMlJf2WR4ASayglDaqjryIH4gh391SuZfI
|
||||
#L1wlc9yPCdCkMQyPpueq+VmRXebThQ42RI75tL1sDKaZ7ugUhplyCZpeMFSDF9gg
|
||||
#6mOuISWdEkyshyYhgRKGsCZai3r2xs6WCT0OiyQ12lZjNO3DdLezxiy8ceVsqVso
|
||||
#kr23h2+PsBXSXJ7Rp21GaTOoYCUN0KhAdi7m082EVCIN4QTrkghx3bVigjpLMhjf
|
||||
#DC5Z37HLpdfn5IVlZoJF3KykE82UHhdIxGSK35Qw2np+gstTKcUacBLyN8GDH9fJ
|
||||
#Re8y8yDhc6xTzS2XJF2UOkSVjhaTRH+PrpGZkRV7izIzrIaN0mULN8dVtwxbLUqa
|
||||
#p0ok4K0xc5GeGjTaVMAsRoNak19wtXDriTkCAMWn28Wp0e3ipZkHqGkZhWxvlOPm
|
||||
#kZbP50e//t6kGv8X0VCbvw==
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
macro_rules! options {
|
||||
($($name:ident $sflag:literal $lflag:literal $hasarg:literal),* $(,)?) => {
|
||||
enum Options {
|
||||
$($name,)*
|
||||
}
|
||||
|
||||
const SHORT_FLAGS: &[&str] = &[
|
||||
$($sflag,)*
|
||||
];
|
||||
|
||||
const LONG_FLAGS: &[&str] = &[
|
||||
$($lflag,)*
|
||||
];
|
||||
|
||||
const HAS_ARG: &[bool] = &[
|
||||
$($hasarg,)*
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
options! {
|
||||
Width "w" "width" true,
|
||||
Height "h" "height" true,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Cli {
|
||||
width: Option<usize>,
|
||||
height: Option<usize>,
|
||||
input_file: String,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new() -> Self {
|
||||
Cli::default()
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Option<()> {
|
||||
let mut args = std::env::args();
|
||||
/* Skip program name. */
|
||||
args.next();
|
||||
loop {
|
||||
if let Some(arg) = args.next() {
|
||||
if arg.starts_with("--") {
|
||||
let flag = &arg[2..];
|
||||
} else if arg.starts_with("-") {
|
||||
let flag = &arg[1..2];
|
||||
} else {
|
||||
self.input_file = arg;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
tree;
|
||||
tree_prefix P;
|
||||
ptype double;
|
||||
|
||||
<<header
|
||||
/* User terminate codes. */
|
||||
#define PARSER_TERMINATE_NUMBER_RANGE 1u
|
||||
>>
|
||||
|
||||
<<
|
||||
#include <charconv>
|
||||
|
||||
/* Store the numeric value of the matched text in *out. Returns false if the
|
||||
* value cannot be represented. */
|
||||
static bool parse_number(uint8_t const * match_text, size_t match_length, double * out)
|
||||
{
|
||||
const char * first = (const char *)match_text;
|
||||
const char * last = first + match_length;
|
||||
std::from_chars_result r = std::from_chars(first, last, *out);
|
||||
return (r.ec == std::errc()) && (r.ptr == last);
|
||||
}
|
||||
>>
|
||||
|
||||
drop /\s+/;
|
||||
drop /#.*/;
|
||||
|
||||
token background;
|
||||
token box;
|
||||
token camera;
|
||||
token color;
|
||||
token comma /,/;
|
||||
token cyl;
|
||||
token gt />/;
|
||||
token height;
|
||||
token intersect;
|
||||
token lcurly /\{/;
|
||||
token lt /</;
|
||||
token material;
|
||||
token options;
|
||||
token plane;
|
||||
token position;
|
||||
token rcurly /\}/;
|
||||
token rotate;
|
||||
token scale;
|
||||
token size;
|
||||
token sphere;
|
||||
token subtract;
|
||||
token target;
|
||||
token translate;
|
||||
token union;
|
||||
token up;
|
||||
token width;
|
||||
|
||||
token uint /\d+/ <<
|
||||
if (!parse_number(match_text, match_length, &$$))
|
||||
{
|
||||
$terminate(PARSER_TERMINATE_NUMBER_RANGE);
|
||||
}
|
||||
>>
|
||||
token number /-?\d+(\.\d+)?/ <<
|
||||
if (!parse_number(match_text, match_length, &$$))
|
||||
{
|
||||
$terminate(PARSER_TERMINATE_NUMBER_RANGE);
|
||||
}
|
||||
>>
|
||||
|
||||
start Scene;
|
||||
|
||||
Background -> background Vector;
|
||||
|
||||
Color -> color Vector;
|
||||
|
||||
Height -> height uint;
|
||||
|
||||
Number -> uint;
|
||||
Number -> number;
|
||||
|
||||
Options -> options lcurly OptionsItems rcurly;
|
||||
|
||||
OptionsItems -> ;
|
||||
OptionsItems -> OptionsItem OptionsItems;
|
||||
|
||||
OptionsItem -> Background;
|
||||
OptionsItem -> Height;
|
||||
OptionsItem -> Width;
|
||||
|
||||
Position -> position Vector;
|
||||
|
||||
Rotate -> rotate Number comma Vector;
|
||||
Rotate -> rotate Number comma Vector lcurly SceneItems rcurly;
|
||||
|
||||
Scale -> scale Number;
|
||||
Scale -> scale Number lcurly SceneItems rcurly;
|
||||
Scale -> scale Vector;
|
||||
Scale -> scale Vector lcurly SceneItems rcurly;
|
||||
|
||||
SceneItemBlock -> SceneItemName lcurly SceneItems rcurly;
|
||||
|
||||
Scene -> Options? SceneItems;
|
||||
|
||||
SceneItems -> ;
|
||||
SceneItems -> SceneItem SceneItems;
|
||||
|
||||
SceneItem -> Color;
|
||||
SceneItem -> Position;
|
||||
SceneItem -> Rotate;
|
||||
SceneItem -> Scale;
|
||||
SceneItem -> SceneItemBlock;
|
||||
SceneItem -> Size;
|
||||
SceneItem -> Target;
|
||||
SceneItem -> Translate;
|
||||
SceneItem -> Up;
|
||||
|
||||
SceneItemName -> box;
|
||||
SceneItemName -> camera;
|
||||
SceneItemName -> cyl;
|
||||
SceneItemName -> material;
|
||||
SceneItemName -> plane;
|
||||
SceneItemName -> sphere;
|
||||
|
||||
Size -> size Number;
|
||||
Size -> size Vector;
|
||||
|
||||
Target -> target Vector;
|
||||
|
||||
Translate -> translate Vector;
|
||||
Translate -> translate Vector lcurly SceneItems rcurly;
|
||||
|
||||
Up -> up Vector;
|
||||
|
||||
Vector -> lt Number comma Number comma Number gt;
|
||||
Vector -> lt Number comma Number comma Number comma Number gt;
|
||||
|
||||
Width -> width uint;
|
||||
@@ -1,226 +0,0 @@
|
||||
#include <fart/Cli.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define FLAGS \
|
||||
FLAG(height, "h", true) \
|
||||
FLAG(height_l, "height", true) \
|
||||
FLAG(help, "help", false) \
|
||||
FLAG(output, "o", true) \
|
||||
FLAG(output_l, "output", true) \
|
||||
FLAG(threads, "t", true) \
|
||||
FLAG(threads_l, "threads", true) \
|
||||
FLAG(version, "version", false) \
|
||||
FLAG(width, "w", true) \
|
||||
FLAG(width_l, "width", true) \
|
||||
|
||||
#define FLAG(name, flag, arg) flag_##name,
|
||||
typedef enum
|
||||
{
|
||||
FLAGS
|
||||
flag__count
|
||||
} flag_id_t;
|
||||
#undef FLAG
|
||||
|
||||
#define FLAG(name, flag, arg) static const char flag_##name##_str[] = flag;
|
||||
FLAGS
|
||||
#undef FLAG
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char * flag;
|
||||
size_t flag_length;
|
||||
bool arg;
|
||||
} flag_def_t;
|
||||
|
||||
#define FLAG(name, flag, arg) { \
|
||||
flag_##name##_str, \
|
||||
sizeof(flag_##name##_str) - 1u, \
|
||||
arg},
|
||||
static const flag_def_t flag_defs[] = {
|
||||
FLAGS
|
||||
};
|
||||
#undef FLAG
|
||||
|
||||
int Cli::run(int argc, char * argv[])
|
||||
{
|
||||
for (size_t i = 1; i < argc; i++)
|
||||
{
|
||||
size_t dashes = 0u;
|
||||
if (strncmp(argv[i], "--", 2) == 0)
|
||||
{
|
||||
dashes = 2u;
|
||||
}
|
||||
else if (strncmp(argv[i], "-", 1) == 0)
|
||||
{
|
||||
dashes = 1u;
|
||||
}
|
||||
if (dashes > 0u)
|
||||
{
|
||||
const char * flag = &argv[i][dashes];
|
||||
size_t flag_idx;
|
||||
for (flag_idx = 0u; flag_idx < flag__count; flag_idx++)
|
||||
{
|
||||
size_t flag_length = flag_defs[flag_idx].flag_length;
|
||||
size_t flag_dashes = flag_length == 1u ? 1u : 2u;
|
||||
if (flag_dashes != dashes)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (strncmp(flag, flag_defs[flag_idx].flag, flag_length) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (dashes == 2 && flag[flag_length] != '\0' && flag[flag_length] != '=')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool arg = flag_defs[flag_idx].arg;
|
||||
const char * arg_value = nullptr;
|
||||
if (arg)
|
||||
{
|
||||
if (flag[flag_length] == '\0')
|
||||
{
|
||||
if (i >= (argc - 1))
|
||||
{
|
||||
fprintf(stderr, "Error: option %s requires an argument\n", flag_defs[flag_idx].flag);
|
||||
return 2;
|
||||
}
|
||||
i++;
|
||||
arg_value = argv[i];
|
||||
}
|
||||
else if (dashes == 1u)
|
||||
{
|
||||
arg_value = &flag[flag_length];
|
||||
}
|
||||
else if (flag[flag_length + 1] != '\0')
|
||||
{
|
||||
arg_value = &flag[flag_length + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: option %s requires an argument\n", flag_defs[flag_idx].flag);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else if (dashes == 2u && flag[flag_length] == '=')
|
||||
{
|
||||
fprintf(stderr, "Error: option %s does not take an argument\n", flag_defs[flag_idx].flag);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: support combining short flags, e.g. -cofoo */
|
||||
}
|
||||
int r = handle_option(flag_idx, arg_value);
|
||||
if (r != 0)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (flag_idx == flag__count)
|
||||
{
|
||||
fprintf(stderr, "Unknown flag: %s\n", flag);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!this->input_file.empty())
|
||||
{
|
||||
fprintf(stderr, "Extraneous argument: %s\n", argv[i]);
|
||||
return 2;
|
||||
}
|
||||
this->input_file = argv[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool parse_size(const char * flag, const char * value, opt<size_t> * out)
|
||||
{
|
||||
bool valid = value[0] != '\0';
|
||||
for (const char * c = value; *c != '\0'; c++)
|
||||
{
|
||||
if (*c < '0' || *c > '9')
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
errno = 0;
|
||||
unsigned long long v = strtoull(value, nullptr, 10);
|
||||
if (errno == 0 && v <= SIZE_MAX)
|
||||
{
|
||||
*out = (size_t)v;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: invalid value for option %s: '%s'\n", flag, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
int Cli::handle_option(size_t flag_idx, const char * arg_value)
|
||||
{
|
||||
switch (flag_idx)
|
||||
{
|
||||
case flag_height:
|
||||
case flag_height_l:
|
||||
if (!parse_size(flag_defs[flag_idx].flag, arg_value, &this->options.height))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case flag_help:
|
||||
printf("Usage: fart [options] input-file\n");
|
||||
printf("\n");
|
||||
printf(" Options:\n");
|
||||
printf("\n");
|
||||
printf(" -h, --height H Set render height to H\n");
|
||||
printf(" --help Show this help and exit\n");
|
||||
printf(" -o, --output F Write rendered image to file F\n");
|
||||
printf(" -t, --threads N Use N render threads (default: number of CPUs)\n");
|
||||
printf(" -w, --width W Set render width to W\n");
|
||||
printf(" --version Show fart version and exit\n");
|
||||
return 1;
|
||||
|
||||
case flag_output:
|
||||
case flag_output_l:
|
||||
this->output_file = arg_value;
|
||||
return 0;
|
||||
|
||||
case flag_threads:
|
||||
case flag_threads_l:
|
||||
if (!parse_size(flag_defs[flag_idx].flag, arg_value, &this->options.threads))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if (*this->options.threads == 0u)
|
||||
{
|
||||
fprintf(stderr, "Error: option %s must be at least 1\n", flag_defs[flag_idx].flag);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case flag_width:
|
||||
case flag_width_l:
|
||||
if (!parse_size(flag_defs[flag_idx].flag, arg_value, &this->options.width))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case flag_version:
|
||||
printf("fart, version 0.0.0.0.0.1\n");
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr, "Internal error: unhandled flag_idx=%zu\n", flag_idx);
|
||||
return 2;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fart/Options.h>
|
||||
|
||||
class Cli
|
||||
{
|
||||
public:
|
||||
Options options;
|
||||
string_view input_file;
|
||||
opt<string_view> output_file;
|
||||
|
||||
int run(int argc, char * argv[]);
|
||||
|
||||
private:
|
||||
int handle_option(size_t option_idx, const char * arg_value);
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
struct Color
|
||||
{
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
double a;
|
||||
|
||||
Color(double r = 0.0, double g = 0.0, double b = 0.0, double a = 1.0)
|
||||
: r(r), g(g), b(b), a(a)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -1,84 +0,0 @@
|
||||
#include <fart/Engine.h>
|
||||
#include <fart/Parser.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
Engine::Engine(const Options & options)
|
||||
: options(options)
|
||||
{
|
||||
}
|
||||
|
||||
Engine::~Engine()
|
||||
{
|
||||
delete scene;
|
||||
}
|
||||
|
||||
int Engine::run(string_view input_file, opt<string_view> output_file)
|
||||
{
|
||||
string output_path;
|
||||
if (output_file)
|
||||
{
|
||||
output_path = *output_file;
|
||||
}
|
||||
else
|
||||
{
|
||||
string_view base = input_file;
|
||||
if (base.ends_with(".fart"))
|
||||
{
|
||||
base.remove_suffix(5u);
|
||||
}
|
||||
output_path = string(base) + ".png";
|
||||
}
|
||||
|
||||
image_writer.reset(ImageWriter::build(output_path));
|
||||
if (!image_writer)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Verify the output file can be written before rendering. */
|
||||
FILE * fh = fopen(output_path.c_str(), "wb");
|
||||
if (fh == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Error opening output file '%s': %s\n", output_path.c_str(), strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (fclose(fh) != 0)
|
||||
{
|
||||
fprintf(stderr, "Error closing output file '%s': %s\n", output_path.c_str(), strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (remove(output_path.c_str()) != 0)
|
||||
{
|
||||
fprintf(stderr, "Error removing output file '%s': %s\n", output_path.c_str(), strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
delete scene;
|
||||
scene = Parser::load_scene(input_file);
|
||||
if (!scene)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* CLI options take precedence over scene options; defaults fill the rest. */
|
||||
options.merge(scene->options);
|
||||
options.apply_defaults();
|
||||
|
||||
Image image(*options.width, *options.height);
|
||||
for (size_t y = 0u; y < *options.height; y++)
|
||||
{
|
||||
for (size_t x = 0u; x < *options.width; x++)
|
||||
{
|
||||
image[x, y] = *options.background;
|
||||
}
|
||||
}
|
||||
|
||||
if (!image_writer->write(image))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fart/ImageWriter.h>
|
||||
#include <fart/Options.h>
|
||||
#include <fart/Scene.h>
|
||||
#include <memory>
|
||||
|
||||
class Engine
|
||||
{
|
||||
public:
|
||||
Engine(const Options & options);
|
||||
~Engine();
|
||||
int run(string_view input_file, opt<string_view> output_file);
|
||||
|
||||
protected:
|
||||
Options options;
|
||||
std::unique_ptr<ImageWriter> image_writer;
|
||||
Scene * scene = nullptr;
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <fart/Image.h>
|
||||
|
||||
Image::Image(size_t width, size_t height)
|
||||
: width(width), height(height), pixels(width * height)
|
||||
{
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fart/Color.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct Image
|
||||
{
|
||||
size_t width;
|
||||
size_t height;
|
||||
vector<Color> pixels;
|
||||
Image(size_t width, size_t height);
|
||||
|
||||
Color & operator[](size_t x, size_t y)
|
||||
{
|
||||
return this->pixels[this->width * y + x];
|
||||
}
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
#include <fart/ImageWriter.h>
|
||||
#include <fart/PngImageWriter.h>
|
||||
#include <stdio.h>
|
||||
#include <filesystem>
|
||||
|
||||
ImageWriter * ImageWriter::build(const string & fname)
|
||||
{
|
||||
string ext = std::filesystem::path(fname).extension().string();
|
||||
if (ext == ".png")
|
||||
{
|
||||
return new PngImageWriter(fname);
|
||||
}
|
||||
fprintf(stderr, "Unsupported output image format: '%s'\n", fname.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fart/Image.h>
|
||||
|
||||
class ImageWriter
|
||||
{
|
||||
public:
|
||||
static ImageWriter * build(const string & fname);
|
||||
virtual ~ImageWriter() = default;
|
||||
virtual bool write(const Image & image) = 0;
|
||||
|
||||
protected:
|
||||
ImageWriter(const string & fname) : fname(fname) {}
|
||||
string fname;
|
||||
};
|
||||
@@ -1,44 +0,0 @@
|
||||
#include <fart/Options.h>
|
||||
#include <thread>
|
||||
|
||||
void Options::merge(const Options & other)
|
||||
{
|
||||
if (!width)
|
||||
{
|
||||
width = other.width;
|
||||
}
|
||||
if (!height)
|
||||
{
|
||||
height = other.height;
|
||||
}
|
||||
if (!threads)
|
||||
{
|
||||
threads = other.threads;
|
||||
}
|
||||
if (!background)
|
||||
{
|
||||
background = other.background;
|
||||
}
|
||||
}
|
||||
|
||||
void Options::apply_defaults()
|
||||
{
|
||||
if (!width)
|
||||
{
|
||||
width = 800u;
|
||||
}
|
||||
if (!height)
|
||||
{
|
||||
height = 600u;
|
||||
}
|
||||
if (!threads)
|
||||
{
|
||||
/* hardware_concurrency() may return 0 if it cannot be determined. */
|
||||
size_t n = std::thread::hardware_concurrency();
|
||||
threads = n > 0u ? n : 1u;
|
||||
}
|
||||
if (!background)
|
||||
{
|
||||
background = Color{0.0, 0.0, 0.0, 1.0};
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fart/Color.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct Options
|
||||
{
|
||||
opt<size_t> width;
|
||||
opt<size_t> height;
|
||||
opt<size_t> threads;
|
||||
opt<Color> background;
|
||||
|
||||
/* Set each field that is not already set to the value from other. */
|
||||
void merge(const Options & other);
|
||||
|
||||
/* Fill in any unset fields with default values. */
|
||||
void apply_defaults();
|
||||
};
|
||||
@@ -1,147 +0,0 @@
|
||||
#include <fart/Parser.h>
|
||||
#include <fart/propane.h>
|
||||
#include <fart/read_file.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
Scene * Parser::load_scene(const string_view & input_file)
|
||||
{
|
||||
opt<vector<uint8_t>> input = read_file(input_file);
|
||||
if (!input)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p_context_t * context = p_context_new(input->data(), input->size());
|
||||
size_t result = p_parse(context);
|
||||
p_position_t pos = p_position(context);
|
||||
int fname_len = (int)input_file.size();
|
||||
const char * fname = input_file.data();
|
||||
bool success = false;
|
||||
switch (result)
|
||||
{
|
||||
case P_SUCCESS:
|
||||
success = true;
|
||||
break;
|
||||
|
||||
case P_DECODE_ERROR:
|
||||
fprintf(stderr, "%.*s:%u:%u: error: invalid UTF-8 encoding\n",
|
||||
fname_len, fname, pos.row, pos.col);
|
||||
break;
|
||||
|
||||
case P_UNEXPECTED_INPUT:
|
||||
fprintf(stderr, "%.*s:%u:%u: error: unexpected input\n",
|
||||
fname_len, fname, pos.row, pos.col);
|
||||
break;
|
||||
|
||||
case P_UNEXPECTED_TOKEN:
|
||||
{
|
||||
p_token_t token = p_token(context);
|
||||
if (token == TOKEN___EOF)
|
||||
{
|
||||
fprintf(stderr, "%.*s:%u:%u: error: unexpected end of input\n",
|
||||
fname_len, fname, pos.row, pos.col);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%.*s:%u:%u: error: unexpected token '%s'\n",
|
||||
fname_len, fname, pos.row, pos.col, p_token_names[token]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case P_USER_TERMINATED:
|
||||
if (p_user_terminate_code(context) == PARSER_TERMINATE_NUMBER_RANGE)
|
||||
{
|
||||
fprintf(stderr, "%.*s:%u:%u: error: number out of range\n",
|
||||
fname_len, fname, pos.row, pos.col);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%.*s:%u:%u: internal error: unknown terminate code %zu\n",
|
||||
fname_len, fname, pos.row, pos.col, p_user_terminate_code(context));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "%.*s:%u:%u: internal error: unknown parser result %zu\n",
|
||||
fname_len, fname, pos.row, pos.col, result);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
p_context_delete(context);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Scene * scene = build_scene(input_file, p_result(context));
|
||||
p_context_delete(context);
|
||||
return scene;
|
||||
}
|
||||
|
||||
/* Convert a uint token value to a size_t, reporting an error if it does not
|
||||
* fit. */
|
||||
static bool token_to_size(const string_view & input_file, PToken token, opt<size_t> * out)
|
||||
{
|
||||
/* SIZE_MAX + 1, computed so that it is exactly representable as a double. */
|
||||
const double size_limit = (double)(SIZE_MAX / 2u + 1u) * 2.0;
|
||||
double value = token.pvalue();
|
||||
if (value >= size_limit)
|
||||
{
|
||||
p_position_t pos = token.position();
|
||||
fprintf(stderr, "%.*s:%u:%u: error: value out of range\n",
|
||||
(int)input_file.size(), input_file.data(), pos.row, pos.col);
|
||||
return false;
|
||||
}
|
||||
*out = (size_t)value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Color vector_to_color(PVector vector)
|
||||
{
|
||||
Color color{
|
||||
vector.pNumber2().pToken().pvalue(),
|
||||
vector.pNumber4().pToken().pvalue(),
|
||||
vector.pNumber6().pToken().pvalue(),
|
||||
};
|
||||
if (PNumber a = vector.pNumber8(); p_node_valid(a))
|
||||
{
|
||||
color.a = a.pToken().pvalue();
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
Scene * Parser::build_scene(const string_view & input_file, PScene pscene)
|
||||
{
|
||||
Scene * scene = new Scene();
|
||||
|
||||
/* If the options block is absent, pOptions() returns a null node and so
|
||||
* does pOptionsItems() on it, so the loop body is skipped. */
|
||||
for (POptionsItems items = pscene.pOptions().pOptionsItems();
|
||||
p_node_valid(items);
|
||||
items = items.pOptionsItems())
|
||||
{
|
||||
POptionsItem item = items.pOptionsItem();
|
||||
bool ok = true;
|
||||
if (PBackground background = item.pBackground(); p_node_valid(background))
|
||||
{
|
||||
scene->options.background = vector_to_color(background.pVector());
|
||||
}
|
||||
else if (PHeight height = item.pHeight(); p_node_valid(height))
|
||||
{
|
||||
ok = token_to_size(input_file, height.pToken2(), &scene->options.height);
|
||||
}
|
||||
else if (PWidth width = item.pWidth(); p_node_valid(width))
|
||||
{
|
||||
ok = token_to_size(input_file, width.pToken2(), &scene->options.width);
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
delete scene;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return scene;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fart/Scene.h>
|
||||
|
||||
struct PScene;
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
static Scene * load_scene(const string_view & input_file);
|
||||
|
||||
private:
|
||||
static Scene * build_scene(const string_view & input_file, PScene pscene);
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
#include <fart/PngImageWriter.h>
|
||||
#include <png.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
static uint8_t to_byte(double v)
|
||||
{
|
||||
return (uint8_t)std::lround(std::clamp(v, 0.0, 1.0) * 255.0);
|
||||
}
|
||||
|
||||
bool PngImageWriter::write(const Image & image)
|
||||
{
|
||||
vector<uint8_t> buffer(image.width * image.height * 4u);
|
||||
for (size_t i = 0u; i < image.pixels.size(); i++)
|
||||
{
|
||||
const Color & c = image.pixels[i];
|
||||
buffer[i * 4u + 0u] = to_byte(c.r);
|
||||
buffer[i * 4u + 1u] = to_byte(c.g);
|
||||
buffer[i * 4u + 2u] = to_byte(c.b);
|
||||
buffer[i * 4u + 3u] = to_byte(c.a);
|
||||
}
|
||||
|
||||
png_image png = {};
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
png.width = image.width;
|
||||
png.height = image.height;
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
|
||||
if (!png_image_write_to_file(&png, fname.c_str(), 0, buffer.data(), 0, nullptr))
|
||||
{
|
||||
fprintf(stderr, "Error writing PNG file '%s': %s\n", fname.c_str(), png.message);
|
||||
png_image_free(&png);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fart/ImageWriter.h>
|
||||
|
||||
class PngImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
PngImageWriter(const string & fname) : ImageWriter(fname) {}
|
||||
bool write(const Image & image) override;
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fart/Options.h>
|
||||
|
||||
class Scene
|
||||
{
|
||||
public:
|
||||
Options options;
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
using string = std::string;
|
||||
using string_view = std::string_view;
|
||||
template <typename T> using opt = std::optional<T>;
|
||||
template <typename T> using vector = std::vector<T>;
|
||||
@@ -1,20 +0,0 @@
|
||||
#include <fart/Cli.h>
|
||||
#include <fart/Engine.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
Cli cli;
|
||||
int rc = cli.run(argc, argv);
|
||||
if (rc > 0)
|
||||
{
|
||||
return rc - 1;
|
||||
}
|
||||
if (cli.input_file.empty())
|
||||
{
|
||||
fprintf(stderr, "Error: no input file specified\n");
|
||||
return 1;
|
||||
}
|
||||
Engine engine(cli.options);
|
||||
return engine.run(cli.input_file, cli.output_file);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#include <fart/read_file.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
opt<vector<uint8_t>> read_file(string_view fname)
|
||||
{
|
||||
string path(fname);
|
||||
FILE * fh = fopen(path.c_str(), "rb");
|
||||
if (fh == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Error opening input file '%s': %s\n", path.c_str(), strerror(errno));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> buffer;
|
||||
uint8_t chunk[4096];
|
||||
size_t n;
|
||||
while ((n = fread(chunk, 1u, sizeof(chunk), fh)) > 0u)
|
||||
{
|
||||
buffer.insert(buffer.end(), chunk, chunk + n);
|
||||
}
|
||||
if (ferror(fh))
|
||||
{
|
||||
fprintf(stderr, "Error reading input file '%s': %s\n", path.c_str(), strerror(errno));
|
||||
fclose(fh);
|
||||
return std::nullopt;
|
||||
}
|
||||
fclose(fh);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
opt<vector<uint8_t>> read_file(string_view fname);
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
mod cli;
|
||||
|
||||
use crate::cli::Cli;
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
Some(_) => ExitCode::SUCCESS,
|
||||
None => ExitCode::FAILURE,
|
||||
}
|
||||
}
|
||||
|
||||
fn run() -> Option<()> {
|
||||
let mut cli = Cli::new();
|
||||
cli.run()
|
||||
}
|
||||
Reference in new issue
Block a user