├── LICENSE ├── Makefile ├── README.md ├── demo ├── Fixed.h ├── quickcg.cc ├── quickcg.h └── raycaster.cc ├── polf.asm ├── titlescreen.inc └── titlescreen.pe /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 David Given 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: polf.prg 2 | 3 | polf.prg: polf.asm titlescreen.inc 4 | 64tass --cbm-prg -o $@ -L polf.lst $< 5 | 6 | demo/demo: demo/quickcg.cc demo/quickcg.h demo/raycaster.cc demo/Fixed.h 7 | g++ --std=c++17 -g -o $@ demo/quickcg.cc demo/raycaster.cc -lSDL 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | POLF 0.1 2 | ======== 3 | 4 | © 2021 David Given 5 | 6 | Portable Object reLocation Force: the game of objects, holes, and dubious physics 7 | 8 | What? 9 | ----- 10 | 11 | POLF is a game of the Commodore PET. You are placed in a maze; your goal is to 12 | search the maze for the Portable Object, and use your Impulse Pusher (space 13 | bar) to move the Object into the Hole. There is no plot; please feel free to 14 | make up your own. 15 | 16 | It should run on any 40-column PET with 16kB of RAM or more. (If anyone knows 17 | how to switch an 8032 from 80-column to 40-column mode, please get in touch.) 18 | It supports both Business and Graphics keyboards. 19 | 20 | How? 21 | ---- 22 | 23 | POLF is written with the [64tass](http://tass64.sourceforge.net/) assembler. 24 | Simply assemble the `polf.asm` file with the `--cbm-prg` option and you should 25 | have a working `.prg` file. Transfer this to your pet by whichever means makes 26 | you happy. There is a Makefile which will build it for you. 27 | 28 | Load the program, then `RUN` it. POLF should start. 29 | 30 | Instructions: use `WASD` to move; use `,` and `.` to turn left or right (or `,` 31 | and `;` on a graphical keyboard). Press `SPACE` to push the Portable Object if 32 | you're close enough. The radar instruments at the bottom of the screen will 33 | tell you how close you are to both Object and Hole. 34 | 35 | There's a secret feature! You can use `A` and `D` on the level overview screen 36 | to select any level (there are 100). 37 | 38 | Why? 39 | ---- 40 | 41 | I wanted to write a proper 3D game for the Commodore PET. Given that the PET 42 | cannot actually display graphics, this seemed like a pleasantly futile task. 43 | 44 | I originally wanted to use block graphics, which would have allowed a 80x50 45 | graphics resolution, but it actually turns out that given the usual 46 | small-computer angle representation of 256 b-radians in a circle, then a 47 | comfortable field of view of 56° corresponds to 40 b-radians. This controls the 48 | highest possible resolution, because the program can't represent smaller angles 49 | than that. This is probably a good thing, as the rather feeble 1MHz 6502 50 | processor produces a small enough frame rate as it is. 51 | 52 | To draw the maze, raycasting is used, following [Lode's Computer Graphics 53 | Tutorial](https://lodev.org/cgtutor/raycasting.html). The `demo` directory 54 | contains a considerably hacked version of Lode's demo program, ported to use 55 | 8-bit fixed point graphics. POLF's number representation is 3.5 fixed-point, 56 | which allows numbers to be represented from 0 to 7 31/32 at 1/32 intervals. 57 | This is why the map is only 8x8, which is uncomfortably small. 58 | 59 | In fact, this isn't really enough precision, even switching to 16-bit precision 60 | for a few operations. I've managed to special-case most of the graphical 61 | glitches but there are still some. Entertain yourself by finding them! 62 | 63 | It should be easily portable to any other 6502 platform with a 40x25 text-mode 64 | screen and 16kB of RAM. 65 | 66 | There's about 2.5kB of actual code, and 6kB of lookup tables. 67 | 68 | License? 69 | -------- 70 | 71 | POLF is © 2021 David Given, and is distributable under the terms of the MIT 72 | public license. See the `LICENSE` file for the full text. 73 | 74 | Who? 75 | ---- 76 | 77 | POLF was written by me, David Given. You may contact me at dg@cowlark.com, or 78 | visit my website at http://www.cowlark.com. There may or may not be anything 79 | interesting there. 80 | 81 | -------------------------------------------------------------------------------- /demo/Fixed.h: -------------------------------------------------------------------------------- 1 | // From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/Fixed.h 2 | // See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math 3 | /* 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2015 Evan Teran 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #ifndef FIXED_H_ 28 | #define FIXED_H_ 29 | 30 | #if __cplusplus >= 201402L 31 | #define CONSTEXPR14 constexpr 32 | #else 33 | #define CONSTEXPR14 34 | #endif 35 | 36 | 37 | #include 38 | #include 39 | #include // for size_t 40 | #include 41 | #include 42 | 43 | namespace numeric { 44 | 45 | template 46 | class Fixed; 47 | 48 | namespace detail { 49 | 50 | // helper templates to make magic with types :) 51 | // these allow us to determine resonable types from 52 | // a desired size, they also let us infer the next largest type 53 | // from a type which is nice for the division op 54 | template 55 | struct type_from_size { 56 | static constexpr bool is_specialized = false; 57 | }; 58 | 59 | #if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) 60 | template <> 61 | struct type_from_size<128> { 62 | static constexpr bool is_specialized = true; 63 | static constexpr size_t size = 128; 64 | 65 | using value_type = __int128; 66 | using unsigned_type = unsigned __int128; 67 | using signed_type = __int128; 68 | using next_size = type_from_size<256>; 69 | }; 70 | #endif 71 | 72 | template <> 73 | struct type_from_size<64> { 74 | static constexpr bool is_specialized = true; 75 | static constexpr size_t size = 64; 76 | 77 | using value_type = int64_t; 78 | using unsigned_type = std::make_unsigned::type; 79 | using signed_type = std::make_signed::type; 80 | using next_size = type_from_size<128>; 81 | }; 82 | 83 | template <> 84 | struct type_from_size<32> { 85 | static constexpr bool is_specialized = true; 86 | static constexpr size_t size = 32; 87 | 88 | using value_type = int32_t; 89 | using unsigned_type = std::make_unsigned::type; 90 | using signed_type = std::make_signed::type; 91 | using next_size = type_from_size<64>; 92 | }; 93 | 94 | template <> 95 | struct type_from_size<16> { 96 | static constexpr bool is_specialized = true; 97 | static constexpr size_t size = 16; 98 | 99 | using value_type = int16_t; 100 | using unsigned_type = std::make_unsigned::type; 101 | using signed_type = std::make_signed::type; 102 | using next_size = type_from_size<32>; 103 | }; 104 | 105 | template <> 106 | struct type_from_size<8> { 107 | static constexpr bool is_specialized = true; 108 | static constexpr size_t size = 8; 109 | 110 | using value_type = int8_t; 111 | using unsigned_type = std::make_unsigned::type; 112 | using signed_type = std::make_signed::type; 113 | using next_size = type_from_size<16>; 114 | }; 115 | 116 | // this is to assist in adding support for non-native base 117 | // types (for adding big-int support), this should be fine 118 | // unless your bit-int class doesn't nicely support casting 119 | template 120 | constexpr B next_to_base(N rhs) { 121 | return static_cast(rhs); 122 | } 123 | 124 | struct divide_by_zero : std::exception { 125 | }; 126 | 127 | template 128 | CONSTEXPR14 Fixed divide(Fixed numerator, Fixed denominator, Fixed &remainder, typename std::enable_if::next_size::is_specialized>::type* = nullptr) { 129 | 130 | using next_type = typename Fixed::next_type; 131 | using base_type = typename Fixed::base_type; 132 | constexpr size_t fractional_bits = Fixed::fractional_bits; 133 | 134 | next_type t(numerator.to_raw()); 135 | t <<= fractional_bits; 136 | 137 | Fixed quotient; 138 | 139 | quotient = Fixed::from_base(next_to_base(t / denominator.to_raw())); 140 | remainder = Fixed::from_base(next_to_base(t % denominator.to_raw())); 141 | 142 | return quotient; 143 | } 144 | 145 | template 146 | CONSTEXPR14 Fixed divide(Fixed numerator, Fixed denominator, Fixed &remainder, typename std::enable_if::next_size::is_specialized>::type* = nullptr) { 147 | 148 | // NOTE(eteran): division is broken for large types :-( 149 | // especially when dealing with negative quantities 150 | 151 | using base_type = typename Fixed::base_type; 152 | using unsigned_type = typename Fixed::unsigned_type; 153 | 154 | constexpr int bits = Fixed::total_bits; 155 | 156 | if(denominator == 0) { 157 | throw divide_by_zero(); 158 | } else { 159 | 160 | int sign = 0; 161 | 162 | Fixed quotient; 163 | 164 | if(numerator < 0) { 165 | sign ^= 1; 166 | numerator = -numerator; 167 | } 168 | 169 | if(denominator < 0) { 170 | sign ^= 1; 171 | denominator = -denominator; 172 | } 173 | 174 | base_type n = numerator.to_raw(); 175 | base_type d = denominator.to_raw(); 176 | base_type x = 1; 177 | base_type answer = 0; 178 | 179 | // egyptian division algorithm 180 | while((n >= d) && (((d >> (bits - 1)) & 1) == 0)) { 181 | x <<= 1; 182 | d <<= 1; 183 | } 184 | 185 | while(x != 0) { 186 | if(n >= d) { 187 | n -= d; 188 | answer += x; 189 | } 190 | 191 | x >>= 1; 192 | d >>= 1; 193 | } 194 | 195 | unsigned_type l1 = n; 196 | unsigned_type l2 = denominator.to_raw(); 197 | 198 | // calculate the lower bits (needs to be unsigned) 199 | // unfortunately for many fractions this overflows the type still :-/ 200 | const unsigned_type lo = (static_cast(n) << F) / denominator.to_raw(); 201 | 202 | quotient = Fixed::from_base((answer << F) | lo); 203 | remainder = n; 204 | 205 | if(sign) { 206 | quotient = -quotient; 207 | } 208 | 209 | return quotient; 210 | } 211 | } 212 | 213 | // this is the usual implementation of multiplication 214 | template 215 | CONSTEXPR14 Fixed multiply(Fixed lhs, Fixed rhs, typename std::enable_if::next_size::is_specialized>::type* = nullptr) { 216 | 217 | using next_type = typename Fixed::next_type; 218 | using base_type = typename Fixed::base_type; 219 | 220 | constexpr size_t fractional_bits = Fixed::fractional_bits; 221 | 222 | next_type t (static_cast(lhs.to_raw()) * static_cast(rhs.to_raw())); 223 | t >>= fractional_bits; 224 | 225 | return Fixed::from_base(next_to_base(t)); 226 | } 227 | 228 | // this is the fall back version we use when we don't have a next size 229 | // it is slightly slower, but is more robust since it doesn't 230 | // require and upgraded type 231 | template 232 | CONSTEXPR14 Fixed multiply(Fixed lhs, Fixed rhs, typename std::enable_if::next_size::is_specialized>::type* = nullptr) { 233 | 234 | using base_type = typename Fixed::base_type; 235 | 236 | constexpr size_t fractional_bits = Fixed::fractional_bits; 237 | constexpr base_type integer_mask = Fixed::integer_mask; 238 | constexpr base_type fractional_mask = Fixed::fractional_mask; 239 | 240 | // more costly but doesn't need a larger type 241 | constexpr base_type a_hi = (lhs.to_raw() & integer_mask) >> fractional_bits; 242 | constexpr base_type b_hi = (rhs.to_raw() & integer_mask) >> fractional_bits; 243 | constexpr base_type a_lo = (lhs.to_raw() & fractional_mask); 244 | constexpr base_type b_lo = (rhs.to_raw() & fractional_mask); 245 | 246 | constexpr base_type x1 = a_hi * b_hi; 247 | constexpr base_type x2 = a_hi * b_lo; 248 | constexpr base_type x3 = a_lo * b_hi; 249 | constexpr base_type x4 = a_lo * b_lo; 250 | 251 | return Fixed::from_base((x1 << fractional_bits) + (x3 + x2) + (x4 >> fractional_bits)); 252 | } 253 | } 254 | 255 | template 256 | class Fixed { 257 | static_assert(detail::type_from_size::is_specialized, "invalid combination of sizes"); 258 | 259 | public: 260 | static constexpr size_t fractional_bits = F; 261 | static constexpr size_t integer_bits = I; 262 | static constexpr size_t total_bits = I + F; 263 | 264 | using base_type_info = detail::type_from_size; 265 | 266 | using base_type = typename base_type_info::value_type; 267 | using next_type = typename base_type_info::next_size::value_type; 268 | using unsigned_type = typename base_type_info::unsigned_type; 269 | 270 | public: 271 | #ifdef __GNUC__ 272 | #pragma GCC diagnostic push 273 | #pragma GCC diagnostic ignored "-Woverflow" 274 | #endif 275 | static constexpr base_type fractional_mask = ~(static_cast(~base_type(0)) << fractional_bits); 276 | static constexpr base_type integer_mask = ~fractional_mask; 277 | #ifdef __GNUC__ 278 | #pragma GCC diagnostic push 279 | #endif 280 | 281 | public: 282 | static constexpr base_type one = base_type(1) << fractional_bits; 283 | 284 | public: // constructors 285 | Fixed() = default; 286 | Fixed(const Fixed &) = default; 287 | Fixed& operator=(const Fixed &) = default; 288 | 289 | template 290 | constexpr Fixed(Number n, typename std::enable_if::value>::type* = nullptr) : data_(static_cast(n * one)) { 291 | } 292 | 293 | public: // conversion 294 | template 295 | CONSTEXPR14 explicit Fixed(Fixed other) { 296 | static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types"); 297 | using T = Fixed; 298 | 299 | const base_type fractional = (other.data_ & T::fractional_mask); 300 | const base_type integer = (other.data_ & T::integer_mask) >> T::fractional_bits; 301 | data_ = (integer << fractional_bits) | (fractional << (fractional_bits - T::fractional_bits)); 302 | } 303 | 304 | private: 305 | // this makes it simpler to create a fixed point object from 306 | // a native type without scaling 307 | // use "Fixed::from_base" in order to perform this. 308 | struct NoScale {}; 309 | 310 | constexpr Fixed(base_type n, const NoScale &) : data_(n) { 311 | } 312 | 313 | public: 314 | constexpr static Fixed from_base(base_type n) { 315 | return Fixed(n, NoScale()); 316 | } 317 | 318 | public: // comparison operators 319 | constexpr bool operator==(Fixed rhs) const { 320 | return data_ == rhs.data_; 321 | } 322 | 323 | constexpr bool operator!=(Fixed rhs) const { 324 | return data_ != rhs.data_; 325 | } 326 | 327 | constexpr bool operator<(Fixed rhs) const { 328 | return data_ < rhs.data_; 329 | } 330 | 331 | constexpr bool operator>(Fixed rhs) const { 332 | return data_ > rhs.data_; 333 | } 334 | 335 | constexpr bool operator<=(Fixed rhs) const { 336 | return data_ <= rhs.data_; 337 | } 338 | 339 | constexpr bool operator>=(Fixed rhs) const { 340 | return data_ >= rhs.data_; 341 | } 342 | 343 | public: // unary operators 344 | constexpr bool operator!() const { 345 | return !data_; 346 | } 347 | 348 | constexpr Fixed operator~() const { 349 | // NOTE(eteran): this will often appear to "just negate" the value 350 | // that is not an error, it is because -x == (~x+1) 351 | // and that "+1" is adding an infinitesimally small fraction to the 352 | // complimented value 353 | return Fixed::from_base(~data_); 354 | } 355 | 356 | constexpr Fixed operator-() const { 357 | return Fixed::from_base(-data_); 358 | } 359 | 360 | constexpr Fixed operator+() const { 361 | return Fixed::from_base(+data_); 362 | } 363 | 364 | CONSTEXPR14 Fixed &operator++() { 365 | data_ += one; 366 | return *this; 367 | } 368 | 369 | CONSTEXPR14 Fixed &operator--() { 370 | data_ -= one; 371 | return *this; 372 | } 373 | 374 | CONSTEXPR14 Fixed operator++(int) { 375 | Fixed tmp(*this); 376 | data_ += one; 377 | return tmp; 378 | } 379 | 380 | CONSTEXPR14 Fixed operator--(int) { 381 | Fixed tmp(*this); 382 | data_ -= one; 383 | return tmp; 384 | } 385 | 386 | public: // basic math operators 387 | CONSTEXPR14 Fixed& operator+=(Fixed n) { 388 | data_ += n.data_; 389 | return *this; 390 | } 391 | 392 | CONSTEXPR14 Fixed& operator-=(Fixed n) { 393 | data_ -= n.data_; 394 | return *this; 395 | } 396 | 397 | CONSTEXPR14 Fixed& operator*=(Fixed n) { 398 | return assign(detail::multiply(*this, n)); 399 | } 400 | 401 | CONSTEXPR14 Fixed& operator/=(Fixed n) { 402 | Fixed temp; 403 | return assign(detail::divide(*this, n, temp)); 404 | } 405 | 406 | private: 407 | CONSTEXPR14 Fixed& assign(Fixed rhs) { 408 | data_ = rhs.data_; 409 | return *this; 410 | } 411 | 412 | public: // binary math operators, effects underlying bit pattern since these 413 | // don't really typically make sense for non-integer values 414 | CONSTEXPR14 Fixed& operator&=(Fixed n) { 415 | data_ &= n.data_; 416 | return *this; 417 | } 418 | 419 | CONSTEXPR14 Fixed& operator|=(Fixed n) { 420 | data_ |= n.data_; 421 | return *this; 422 | } 423 | 424 | CONSTEXPR14 Fixed& operator^=(Fixed n) { 425 | data_ ^= n.data_; 426 | return *this; 427 | } 428 | 429 | template ::value>::type> 430 | CONSTEXPR14 Fixed& operator>>=(Integer n) { 431 | data_ >>= n; 432 | return *this; 433 | } 434 | 435 | template ::value>::type> 436 | CONSTEXPR14 Fixed& operator<<=(Integer n) { 437 | data_ <<= n; 438 | return *this; 439 | } 440 | 441 | public: // conversion to basic types 442 | constexpr int to_int() const { 443 | return (data_ & integer_mask) >> fractional_bits; 444 | } 445 | 446 | constexpr unsigned int to_uint() const { 447 | return (data_ & integer_mask) >> fractional_bits; 448 | } 449 | 450 | constexpr float to_float() const { 451 | return static_cast(data_) / Fixed::one; 452 | } 453 | 454 | constexpr double to_double() const { 455 | return static_cast(data_) / Fixed::one; 456 | } 457 | 458 | constexpr base_type to_raw() const { 459 | return data_; 460 | } 461 | 462 | constexpr operator int() const { 463 | return this->to_int(); 464 | } 465 | 466 | constexpr operator double() const { 467 | return this->to_double(); 468 | } 469 | 470 | public: 471 | CONSTEXPR14 void swap(Fixed &rhs) { 472 | using std::swap; 473 | swap(data_, rhs.data_); 474 | } 475 | 476 | public: 477 | base_type data_ = 0; 478 | }; 479 | 480 | // if we have the same fractional portion, but differing integer portions, we trivially upgrade the smaller type 481 | template 482 | CONSTEXPR14 typename std::conditional= I2, Fixed, Fixed>::type operator+(Fixed lhs, Fixed rhs) { 483 | 484 | using T = typename std::conditional< 485 | I1 >= I2, 486 | Fixed, 487 | Fixed 488 | >::type; 489 | 490 | const T l = T::from_base(lhs.to_raw()); 491 | const T r = T::from_base(rhs.to_raw()); 492 | return l + r; 493 | } 494 | 495 | template 496 | CONSTEXPR14 typename std::conditional= I2, Fixed, Fixed>::type operator-(Fixed lhs, Fixed rhs) { 497 | 498 | using T = typename std::conditional< 499 | I1 >= I2, 500 | Fixed, 501 | Fixed 502 | >::type; 503 | 504 | const T l = T::from_base(lhs.to_raw()); 505 | const T r = T::from_base(rhs.to_raw()); 506 | return l - r; 507 | } 508 | 509 | template 510 | CONSTEXPR14 typename std::conditional= I2, Fixed, Fixed>::type operator*(Fixed lhs, Fixed rhs) { 511 | 512 | using T = typename std::conditional< 513 | I1 >= I2, 514 | Fixed, 515 | Fixed 516 | >::type; 517 | 518 | const T l = T::from_base(lhs.to_raw()); 519 | const T r = T::from_base(rhs.to_raw()); 520 | return l * r; 521 | } 522 | 523 | template 524 | CONSTEXPR14 typename std::conditional= I2, Fixed, Fixed>::type operator/(Fixed lhs, Fixed rhs) { 525 | 526 | using T = typename std::conditional< 527 | I1 >= I2, 528 | Fixed, 529 | Fixed 530 | >::type; 531 | 532 | const T l = T::from_base(lhs.to_raw()); 533 | const T r = T::from_base(rhs.to_raw()); 534 | return l / r; 535 | } 536 | 537 | template 538 | std::ostream &operator<<(std::ostream &os, Fixed f) { 539 | os << f.to_double(); 540 | return os; 541 | } 542 | 543 | // basic math operators 544 | template CONSTEXPR14 Fixed operator+(Fixed lhs, Fixed rhs) { lhs += rhs; return lhs; } 545 | template CONSTEXPR14 Fixed operator-(Fixed lhs, Fixed rhs) { lhs -= rhs; return lhs; } 546 | template CONSTEXPR14 Fixed operator*(Fixed lhs, Fixed rhs) { lhs *= rhs; return lhs; } 547 | template CONSTEXPR14 Fixed operator/(Fixed lhs, Fixed rhs) { lhs /= rhs; return lhs; } 548 | 549 | template ::value>::type> CONSTEXPR14 Fixed operator+(Fixed lhs, Number rhs) { lhs += Fixed(rhs); return lhs; } 550 | template ::value>::type> CONSTEXPR14 Fixed operator-(Fixed lhs, Number rhs) { lhs -= Fixed(rhs); return lhs; } 551 | template ::value>::type> CONSTEXPR14 Fixed operator*(Fixed lhs, Number rhs) { lhs *= Fixed(rhs); return lhs; } 552 | template ::value>::type> CONSTEXPR14 Fixed operator/(Fixed lhs, Number rhs) { lhs /= Fixed(rhs); return lhs; } 553 | 554 | template ::value>::type> CONSTEXPR14 Fixed operator+(Number lhs, Fixed rhs) { Fixed tmp(lhs); tmp += rhs; return tmp; } 555 | template ::value>::type> CONSTEXPR14 Fixed operator-(Number lhs, Fixed rhs) { Fixed tmp(lhs); tmp -= rhs; return tmp; } 556 | template ::value>::type> CONSTEXPR14 Fixed operator*(Number lhs, Fixed rhs) { Fixed tmp(lhs); tmp *= rhs; return tmp; } 557 | template ::value>::type> CONSTEXPR14 Fixed operator/(Number lhs, Fixed rhs) { Fixed tmp(lhs); tmp /= rhs; return tmp; } 558 | 559 | // shift operators 560 | template ::value>::type> CONSTEXPR14 Fixed operator<<(Fixed lhs, Integer rhs) { lhs <<= rhs; return lhs; } 561 | template ::value>::type> CONSTEXPR14 Fixed operator>>(Fixed lhs, Integer rhs) { lhs >>= rhs; return lhs; } 562 | 563 | // comparison operators 564 | template ::value>::type> constexpr bool operator>(Fixed lhs, Number rhs) { return lhs > Fixed(rhs); } 565 | template ::value>::type> constexpr bool operator<(Fixed lhs, Number rhs) { return lhs < Fixed(rhs); } 566 | template ::value>::type> constexpr bool operator>=(Fixed lhs, Number rhs) { return lhs >= Fixed(rhs); } 567 | template ::value>::type> constexpr bool operator<=(Fixed lhs, Number rhs) { return lhs <= Fixed(rhs); } 568 | template ::value>::type> constexpr bool operator==(Fixed lhs, Number rhs) { return lhs == Fixed(rhs); } 569 | template ::value>::type> constexpr bool operator!=(Fixed lhs, Number rhs) { return lhs != Fixed(rhs); } 570 | 571 | template ::value>::type> constexpr bool operator>(Number lhs, Fixed rhs) { return Fixed(lhs) > rhs; } 572 | template ::value>::type> constexpr bool operator<(Number lhs, Fixed rhs) { return Fixed(lhs) < rhs; } 573 | template ::value>::type> constexpr bool operator>=(Number lhs, Fixed rhs) { return Fixed(lhs) >= rhs; } 574 | template ::value>::type> constexpr bool operator<=(Number lhs, Fixed rhs) { return Fixed(lhs) <= rhs; } 575 | template ::value>::type> constexpr bool operator==(Number lhs, Fixed rhs) { return Fixed(lhs) == rhs; } 576 | template ::value>::type> constexpr bool operator!=(Number lhs, Fixed rhs) { return Fixed(lhs) != rhs; } 577 | } 578 | 579 | #undef CONSTEXPR14 580 | 581 | #endif 582 | -------------------------------------------------------------------------------- /demo/quickcg.cc: -------------------------------------------------------------------------------- 1 | /* 2 | QuickCG 20191227 3 | 4 | Copyright (c) 2004-2019, Lode Vandevenne 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 14 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 15 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 16 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 22 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /* 27 | QuickCG is an SDL codebase that wraps some of the SDL functionality. 28 | It' used by Lode's Computer Graphics Tutorial to work with simple C++ calls 29 | to demonstrate graphical programs. 30 | 31 | QuickCG can handle some things that standard C++ doesn't but that are commonly useful, such as: 32 | -drawing graphics 33 | -a bitmap font 34 | -simplified saving and loading of files 35 | -reading keyboard and mouse input 36 | -playing sound 37 | -color models 38 | -loading images 39 | */ 40 | 41 | #include "quickcg.h" 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | namespace QuickCG 52 | { 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | //VARIABLES///////////////////////////////////////////////////////////////////// 56 | //////////////////////////////////////////////////////////////////////////////// 57 | 58 | int w; //width of the screen 59 | int h; //height of the screen 60 | 61 | std::map keypressed; //for the "keyPressed" function to detect a keypress only once 62 | SDL_Surface* scr; //the single SDL surface used 63 | Uint8* inkeys = 0; 64 | SDL_Event event = {0}; 65 | 66 | //////////////////////////////////////////////////////////////////////////////// 67 | //KEYBOARD FUNCTIONS//////////////////////////////////////////////////////////// 68 | //////////////////////////////////////////////////////////////////////////////// 69 | 70 | bool keyDown(int key) //this checks if the key is held down, returns true all the time until the key is up 71 | { 72 | if(!inkeys) return false; 73 | return (inkeys[key] != 0); 74 | } 75 | 76 | bool keyPressed(int key) //this checks if the key is *just* pressed, returns true only once until the key is up again 77 | { 78 | if(!inkeys) return false; 79 | if(keypressed.find(key) == keypressed.end()) keypressed[key] = false; 80 | if(inkeys[key]) 81 | { 82 | if(keypressed[key] == false) 83 | { 84 | keypressed[key] = true; 85 | return true; 86 | } 87 | } 88 | else keypressed[key] = false; 89 | 90 | return false; 91 | } 92 | 93 | //////////////////////////////////////////////////////////////////////////////// 94 | //BASIC SCREEN FUNCTIONS//////////////////////////////////////////////////////// 95 | //////////////////////////////////////////////////////////////////////////////// 96 | 97 | //The screen function: sets up the window for 32-bit color graphics. 98 | //Creates a graphical screen of width*height pixels in 32-bit color. 99 | //Set fullscreen to 0 for a window, or to 1 for fullscreen output 100 | //text is the caption or title of the window 101 | //also inits SDL 102 | void screen(int width, int height, bool fullscreen, const std::string& text) 103 | { 104 | int colorDepth = 32; 105 | w = width; 106 | h = height; 107 | 108 | if(SDL_Init(SDL_INIT_EVERYTHING) < 0) 109 | { 110 | printf("Unable to init SDL: %s\n", SDL_GetError()); 111 | SDL_Quit(); 112 | std::exit(1); 113 | } 114 | std::atexit(SDL_Quit); 115 | if(fullscreen) 116 | { 117 | scr = SDL_SetVideoMode(width, height, colorDepth, SDL_SWSURFACE | SDL_FULLSCREEN); 118 | lock(); 119 | } 120 | else 121 | { 122 | // TODO: use OpenGL to check if the blitting goes potentially faster with it 123 | scr = SDL_SetVideoMode(width, height, colorDepth, SDL_HWSURFACE | SDL_HWPALETTE); 124 | } 125 | if(scr == NULL) 126 | { 127 | printf("Unable to set video: %s\n", SDL_GetError()); 128 | SDL_Quit(); 129 | std::exit(1); 130 | } 131 | SDL_WM_SetCaption(text.c_str(), NULL); 132 | 133 | SDL_EnableUNICODE(1); //for the text input things 134 | } 135 | 136 | //Locks the screen 137 | void lock() 138 | { 139 | if(SDL_MUSTLOCK(scr)) 140 | if(SDL_LockSurface(scr) < 0) 141 | return; 142 | } 143 | 144 | //Unlocks the screen 145 | void unlock() 146 | { 147 | if(SDL_MUSTLOCK(scr)) 148 | SDL_UnlockSurface(scr); 149 | } 150 | 151 | //Updates the screen. Has to be called to view new pixels, but use only after 152 | //drawing the whole screen because it's slow. 153 | void redraw() 154 | { 155 | SDL_UpdateRect(scr, 0, 0, 0, 0); 156 | //SDL_Flip(scr); // this could potentially be faster than SDL_UpdateRect if double buffering is used 157 | } 158 | 159 | //Clears the screen to black 160 | void cls(const ColorRGB& color) 161 | { 162 | SDL_FillRect(scr, NULL, 65536 * color.r + 256 * color.g + color.b); 163 | } 164 | 165 | //Puts an RGB color pixel at position x,y 166 | void pset(int x, int y, const ColorRGB& color) 167 | { 168 | if(x < 0 || y < 0 || x >= w || y >= h) return; 169 | Uint32 colorSDL = SDL_MapRGB(scr->format, color.r, color.g, color.b); 170 | Uint32* bufp; 171 | bufp = (Uint32*)scr->pixels + y * scr->pitch / 4 + x; 172 | *bufp = colorSDL; 173 | } 174 | 175 | //Gets RGB color of pixel at position x,y 176 | ColorRGB pget(int x, int y) 177 | { 178 | if(x < 0 || y < 0 || x >= w || y >= h) return RGB_Black; 179 | Uint32* bufp; 180 | bufp = (Uint32*)scr->pixels + y * scr->pitch / 4 + x; 181 | Uint32 colorSDL = *bufp; 182 | ColorRGB8bit colorRGB; 183 | SDL_GetRGB(colorSDL, scr->format, &colorRGB.r, &colorRGB.g, &colorRGB.b); 184 | return ColorRGB(colorRGB); 185 | } 186 | 187 | //Draws a buffer of pixels to the screen 188 | void drawBuffer(Uint32* buffer) 189 | { 190 | Uint32* bufp; 191 | bufp = (Uint32*)scr->pixels; 192 | 193 | for(int y = 0; y < h; y++) 194 | { 195 | for(int x = 0; x < w; x++) 196 | { 197 | *bufp = buffer[y * w + x]; 198 | bufp++; 199 | } 200 | bufp += scr->pitch / 4; 201 | bufp -= w; 202 | } 203 | } 204 | 205 | void getScreenBuffer(std::vector& buffer) 206 | { 207 | Uint32* bufp; 208 | bufp = (Uint32*)scr->pixels; 209 | 210 | buffer.resize(w * h); 211 | 212 | for(int y = 0; y < h; y++) 213 | { 214 | for(int x = 0; x < w; x++) 215 | { 216 | buffer[y * w + x] = *bufp; 217 | bufp++; 218 | } 219 | bufp += scr->pitch / 4; 220 | bufp -= w; 221 | } 222 | } 223 | 224 | bool onScreen(int x, int y) 225 | { 226 | return (x >= 0 && y >= 0 && x < w && y < h); 227 | } 228 | 229 | 230 | 231 | //////////////////////////////////////////////////////////////////////////////// 232 | //NON GRAPHICAL FUNCTIONS/////////////////////////////////////////////////////// 233 | //////////////////////////////////////////////////////////////////////////////// 234 | 235 | //Waits until you press a key. First the key has to be loose, this means, if you put two sleep functions in a row, the second will only work after you first released the key. 236 | void sleep() 237 | { 238 | int done = 0; 239 | SDL_PollEvent(&event); 240 | while(done == 0) 241 | { 242 | while(SDL_PollEvent(&event)) 243 | { 244 | if(event.type == SDL_QUIT) end(); 245 | if(event.type == SDL_KEYDOWN) done = 1; 246 | } 247 | SDL_Delay(5); //so it consumes less processing power 248 | } 249 | } 250 | 251 | void sleep(double seconds) 252 | { 253 | SDL_Delay(seconds * 1000); 254 | } 255 | 256 | void waitFrame(double oldTime, double frameDuration) //in seconds 257 | { 258 | float time = getTime(); 259 | while(time - oldTime < frameDuration) 260 | { 261 | time = getTime(); 262 | SDL_PollEvent(&event); 263 | if(event.type == SDL_QUIT) end(); 264 | inkeys = SDL_GetKeyState(NULL); 265 | if(inkeys[SDLK_ESCAPE]) end(); 266 | SDL_Delay(5); //so it consumes less processing power 267 | } 268 | } 269 | 270 | //Returns 1 if you close the window or press the escape key. Also handles everything that's needed per frame. 271 | //Never put key input code right before done() or SDL may see the key as SDL_QUIT 272 | bool done(bool quit_if_esc, bool delay) //delay makes CPU have some free time, use once per frame to avoid 100% usage of a CPU core 273 | { 274 | if(delay) SDL_Delay(5); //so it consumes less processing power 275 | while(SDL_PollEvent(&event)) { 276 | if(event.type == SDL_QUIT) return true; 277 | } 278 | readKeys(); 279 | if(inkeys[SDLK_ESCAPE]) return true; 280 | return false; 281 | } 282 | 283 | //Ends the program 284 | void end() 285 | { 286 | SDL_Quit(); 287 | std::exit(1); 288 | } 289 | 290 | //Gives value of pressed key to inkeys. 291 | //the variable inkeys can then be used anywhere to check for input 292 | //Normally you have to use readkeys every time you want to use inkeys, but the done() function also uses inkeys so it's not needed to use readkeys if you use done(). 293 | void readKeys() 294 | { 295 | SDL_PumpEvents(); 296 | inkeys = SDL_GetKeyState(NULL); 297 | } 298 | 299 | void getMouseState(int& mouseX, int& mouseY) 300 | { 301 | SDL_GetMouseState(&mouseX, &mouseY); 302 | } 303 | 304 | void getMouseState(int& mouseX, int& mouseY, bool& LMB, bool& RMB) 305 | { 306 | Uint8 mouseState = SDL_GetMouseState(&mouseX, &mouseY); 307 | 308 | if(mouseState & 1) LMB = true; 309 | else LMB = false; 310 | if(mouseState & 4) RMB = true; 311 | else RMB = false; 312 | } 313 | 314 | //Returns the time in milliseconds since the program started 315 | unsigned long getTicks() 316 | { 317 | return SDL_GetTicks(); 318 | } 319 | 320 | 321 | //////////////////////////////////////////////////////////////////////////////// 322 | //2D SHAPES///////////////////////////////////////////////////////////////////// 323 | //////////////////////////////////////////////////////////////////////////////// 324 | 325 | 326 | //Fast horizontal line from (x1,y) to (x2,y), with rgb color 327 | bool horLine(int y, int x1, int x2, const ColorRGB& color) 328 | { 329 | if(x2 < x1) {x1 += x2; x2 = x1 - x2; x1 -= x2;} //swap x1 and x2, x1 must be the leftmost endpoint 330 | if(x2 < 0 || x1 >= w || y < 0 || y >= h) return 0; //no single point of the line is on screen 331 | if(x1 < 0) x1 = 0; //clip 332 | if(x2 >= w) x2 = w - 1; //clip 333 | 334 | Uint32 colorSDL = SDL_MapRGB(scr->format, color.r, color.g, color.b); 335 | Uint32* bufp; 336 | bufp = (Uint32*)scr->pixels + y * scr->pitch / 4 + x1; 337 | for(int x = x1; x <= x2; x++) 338 | { 339 | *bufp = colorSDL; 340 | bufp++; 341 | } 342 | return 1; 343 | } 344 | 345 | 346 | //Fast vertical line from (x,y1) to (x,y2), with rgb color 347 | bool verLine(int x, int y1, int y2, const ColorRGB& color) 348 | { 349 | if(y2 < y1) {y1 += y2; y2 = y1 - y2; y1 -= y2;} //swap y1 and y2 350 | if(y2 < 0 || y1 >= h || x < 0 || x >= w) return 0; //no single point of the line is on screen 351 | if(y1 < 0) y1 = 0; //clip 352 | if(y2 >= w) y2 = h - 1; //clip 353 | 354 | Uint32 colorSDL = SDL_MapRGB(scr->format, color.r, color.g, color.b); 355 | Uint32* bufp; 356 | 357 | bufp = (Uint32*)scr->pixels + y1 * scr->pitch / 4 + x; 358 | unsigned add = scr->pitch / 4; 359 | for(int y = y1; y <= y2; y++) 360 | { 361 | *bufp = colorSDL; 362 | bufp += add; 363 | } 364 | return 1; 365 | } 366 | 367 | 368 | //Bresenham line from (x1,y1) to (x2,y2) with rgb color 369 | bool drawLine(int x1, int y1, int x2, int y2, const ColorRGB& color) 370 | { 371 | if(x1 < 0 || x1 > w - 1 || x2 < 0 || x2 > w - 1 || y1 < 0 || y1 > h - 1 || y2 < 0 || y2 > h - 1) return 0; 372 | 373 | int deltax = std::abs(x2 - x1); //The difference between the x's 374 | int deltay = std::abs(y2 - y1); //The difference between the y's 375 | int x = x1; //Start x off at the first pixel 376 | int y = y1; //Start y off at the first pixel 377 | int xinc1, xinc2, yinc1, yinc2, den, num, numadd, numpixels, curpixel; 378 | 379 | if(x2 >= x1) //The x-values are increasing 380 | { 381 | xinc1 = 1; 382 | xinc2 = 1; 383 | } 384 | else //The x-values are decreasing 385 | { 386 | xinc1 = -1; 387 | xinc2 = -1; 388 | } 389 | if(y2 >= y1) //The y-values are increasing 390 | { 391 | yinc1 = 1; 392 | yinc2 = 1; 393 | } 394 | else //The y-values are decreasing 395 | { 396 | yinc1 = -1; 397 | yinc2 = -1; 398 | } 399 | if (deltax >= deltay) //There is at least one x-value for every y-value 400 | { 401 | xinc1 = 0; //Don't change the x when numerator >= denominator 402 | yinc2 = 0; //Don't change the y for every iteration 403 | den = deltax; 404 | num = deltax / 2; 405 | numadd = deltay; 406 | numpixels = deltax; //There are more x-values than y-values 407 | } 408 | else //There is at least one y-value for every x-value 409 | { 410 | xinc2 = 0; //Don't change the x for every iteration 411 | yinc1 = 0; //Don't change the y when numerator >= denominator 412 | den = deltay; 413 | num = deltay / 2; 414 | numadd = deltax; 415 | numpixels = deltay; //There are more y-values than x-values 416 | } 417 | for (curpixel = 0; curpixel <= numpixels; curpixel++) 418 | { 419 | pset(x % w, y % h, color); //Draw the current pixel 420 | num += numadd; //Increase the numerator by the top of the fraction 421 | if (num >= den) //Check if numerator >= denominator 422 | { 423 | num -= den; //Calculate the new numerator value 424 | x += xinc1; //Change the x as appropriate 425 | y += yinc1; //Change the y as appropriate 426 | } 427 | x += xinc2; //Change the x as appropriate 428 | y += yinc2; //Change the y as appropriate 429 | } 430 | 431 | return 1; 432 | } 433 | 434 | 435 | //Bresenham circle with center at (xc,yc) with radius and red green blue color 436 | bool drawCircle(int xc, int yc, int radius, const ColorRGB& color) 437 | { 438 | if(xc - radius < 0 || xc + radius >= w || yc - radius < 0 || yc + radius >= h) return 0; 439 | int x = 0; 440 | int y = radius; 441 | int p = 3 - (radius << 1); 442 | int a, b, c, d, e, f, g, h; 443 | while (x <= y) 444 | { 445 | a = xc + x; //8 pixels can be calculated at once thanks to the symmetry 446 | b = yc + y; 447 | c = xc - x; 448 | d = yc - y; 449 | e = xc + y; 450 | f = yc + x; 451 | g = xc - y; 452 | h = yc - x; 453 | pset(a, b, color); 454 | pset(c, d, color); 455 | pset(e, f, color); 456 | pset(g, f, color); 457 | if(x > 0) //avoid drawing pixels at same position as the other ones 458 | { 459 | pset(a, d, color); 460 | pset(c, b, color); 461 | pset(e, h, color); 462 | pset(g, h, color); 463 | } 464 | if(p < 0) p += (x++ << 2) + 6; 465 | else p += ((x++ - y--) << 2) + 10; 466 | } 467 | 468 | return 1; 469 | } 470 | 471 | 472 | //Filled bresenham circle with center at (xc,yc) with radius and red green blue color 473 | bool drawDisk(int xc, int yc, int radius, const ColorRGB& color) 474 | { 475 | if(xc + radius < 0 || xc - radius >= w || yc + radius < 0 || yc - radius >= h) return 0; //every single pixel outside screen, so don't waste time on it 476 | int x = 0; 477 | int y = radius; 478 | int p = 3 - (radius << 1); 479 | int a, b, c, d, e, f, g, h; 480 | int pb = yc + radius + 1, pd = yc + radius + 1; //previous values: to avoid drawing horizontal lines multiple times (ensure initial value is outside the range) 481 | while (x <= y) 482 | { 483 | // write data 484 | a = xc + x; 485 | b = yc + y; 486 | c = xc - x; 487 | d = yc - y; 488 | e = xc + y; 489 | f = yc + x; 490 | g = xc - y; 491 | h = yc - x; 492 | if(b != pb) horLine(b, a, c, color); 493 | if(d != pd) horLine(d, a, c, color); 494 | if(f != b) horLine(f, e, g, color); 495 | if(h != d && h != f) horLine(h, e, g, color); 496 | pb = b; 497 | pd = d; 498 | if(p < 0) p += (x++ << 2) + 6; 499 | else p += ((x++ - y--) << 2) + 10; 500 | } 501 | 502 | return 1; 503 | } 504 | 505 | //Rectangle with corners (x1,y1) and (x2,y2) and rgb color 506 | bool drawRect(int x1, int y1, int x2, int y2, const ColorRGB& color) 507 | { 508 | if(x1 < 0 || x1 > w - 1 || x2 < 0 || x2 > w - 1 || y1 < 0 || y1 > h - 1 || y2 < 0 || y2 > h - 1) return 0; 509 | SDL_Rect rec; 510 | rec.x = x1; 511 | rec.y = y1; 512 | rec.w = x2 - x1 + 1; 513 | rec.h = y2 - y1 + 1; 514 | Uint32 colorSDL = SDL_MapRGB(scr->format, color.r, color.g, color.b); 515 | SDL_FillRect(scr, &rec, colorSDL); //SDL's ability to draw a hardware rectangle is used for now 516 | return 1; 517 | } 518 | 519 | //Functions for clipping a 2D line to the screen, which is the rectangle (0,0)-(w,h) 520 | //This is the Cohen-Sutherland Clipping Algorithm 521 | //Each of 9 regions gets an outcode, based on if it's at the top, bottom, left or right of the screen 522 | // 1001 1000 1010 9 8 10 523 | // 0001 0000 0010 1 0 2 524 | // 0101 0100 0110 5 4 6 525 | //int findregion returns which of the 9 regions a point is in, void clipline does the actual clipping 526 | int findRegion(int x, int y) 527 | { 528 | int code=0; 529 | if(y >= h) 530 | code |= 1; //top 531 | else if( y < 0) 532 | code |= 2; //bottom 533 | if(x >= w) 534 | code |= 4; //right 535 | else if ( x < 0) 536 | code |= 8; //left 537 | return(code); 538 | } 539 | bool clipLine(int x1, int y1, int x2, int y2, int & x3, int & y3, int & x4, int & y4) 540 | { 541 | int code1, code2, codeout; 542 | bool accept = 0, done=0; 543 | code1 = findRegion(x1, y1); //the region outcodes for the endpoints 544 | code2 = findRegion(x2, y2); 545 | do //In theory, this can never end up in an infinite loop, it'll always come in one of the trivial cases eventually 546 | { 547 | if(!(code1 | code2)) accept = done = 1; //accept because both endpoints are in screen or on the border, trivial accept 548 | else if(code1 & code2) done = 1; //the line isn't visible on screen, trivial reject 549 | else //if no trivial reject or accept, continue the loop 550 | { 551 | int x, y; 552 | codeout = code1 ? code1 : code2; 553 | if(codeout & 1) //top 554 | { 555 | x = x1 + (x2 - x1) * (h - y1) / (y2 - y1); 556 | y = h - 1; 557 | } 558 | else if(codeout & 2) //bottom 559 | { 560 | x = x1 + (x2 - x1) * -y1 / (y2 - y1); 561 | y = 0; 562 | } 563 | else if(codeout & 4) //right 564 | { 565 | y = y1 + (y2 - y1) * (w - x1) / (x2 - x1); 566 | x = w - 1; 567 | } 568 | else //left 569 | { 570 | y = y1 + (y2 - y1) * -x1 / (x2 - x1); 571 | x = 0; 572 | } 573 | if(codeout == code1) //first endpoint was clipped 574 | { 575 | x1 = x; y1 = y; 576 | code1 = findRegion(x1, y1); 577 | } 578 | else //second endpoint was clipped 579 | { 580 | x2 = x; y2 = y; 581 | code2 = findRegion(x2, y2); 582 | } 583 | } 584 | } 585 | while(done == 0); 586 | 587 | if(accept) 588 | { 589 | x3 = x1; 590 | x4 = x2; 591 | y3 = y1; 592 | y4 = y2; 593 | return 1; 594 | } 595 | else 596 | { 597 | x3 = x4 = y3 = y4 = 0; 598 | return 0; 599 | } 600 | } 601 | 602 | 603 | //////////////////////////////////////////////////////////////////////////////// 604 | //COLOR STRUCTS///////////////////////////////////////////////////////////////// 605 | //////////////////////////////////////////////////////////////////////////////// 606 | ColorRGB::ColorRGB(Uint8 r, Uint8 g, Uint8 b) 607 | { 608 | this->r = r; 609 | this->g = g; 610 | this->b = b; 611 | } 612 | ColorRGB::ColorRGB(const ColorRGB8bit& color) 613 | { 614 | this->r = color.r; 615 | this->g = color.g; 616 | this->b = color.b; 617 | } 618 | ColorRGB::ColorRGB() 619 | { 620 | this->r = 0; 621 | this->g = 0; 622 | this->b = 0; 623 | } 624 | ColorRGB8bit::ColorRGB8bit(Uint8 r, Uint8 g, Uint8 b) 625 | { 626 | this->r = r; 627 | this->g = g; 628 | this->b = b; 629 | } 630 | ColorRGB8bit::ColorRGB8bit(const ColorRGB& color) 631 | { 632 | this->r = color.r; 633 | this->g = color.g; 634 | this->b = color.b; 635 | } 636 | ColorRGB8bit::ColorRGB8bit() 637 | { 638 | this->r = 0; 639 | this->g = 0; 640 | this->b = 0; 641 | } 642 | 643 | //Add two colors 644 | ColorRGB operator+(const ColorRGB& color, const ColorRGB& color2) 645 | { 646 | ColorRGB c; 647 | c.r = color.r + color2.r; 648 | c.g = color.g + color2.g; 649 | c.b = color.b + color2.b; 650 | return c; 651 | } 652 | 653 | //Subtract two colors 654 | ColorRGB operator-(const ColorRGB& color, const ColorRGB& color2) 655 | { 656 | ColorRGB c; 657 | c.r = color.r - color2.r; 658 | c.g = color.g - color2.g; 659 | c.b = color.b - color2.b; 660 | return c; 661 | } 662 | 663 | //Multiplies a color with an integer 664 | ColorRGB operator*(const ColorRGB& color, int a) 665 | { 666 | ColorRGB c; 667 | c.r = color.r * a; 668 | c.g = color.g * a; 669 | c.b = color.b * a; 670 | return c; 671 | } 672 | 673 | //Multiplies a color with an integer 674 | ColorRGB operator*(int a, const ColorRGB& color) 675 | { 676 | ColorRGB c; 677 | c.r = color.r * a; 678 | c.g = color.g * a; 679 | c.b = color.b * a; 680 | return c; 681 | } 682 | 683 | //Divides a color through an integer 684 | ColorRGB operator/(const ColorRGB& color, int a) 685 | { 686 | if(a == 0) return color; 687 | ColorRGB c; 688 | c.r = color.r / a; 689 | c.g = color.g / a; 690 | c.b = color.b / a; 691 | return c; 692 | } 693 | 694 | //Are both colors equal? 695 | bool operator==(const ColorRGB& color, const ColorRGB& color2) 696 | { 697 | return(color.r == color2.r && color.g == color2.g && color.b == color2.b); 698 | } 699 | 700 | //Are both colors not equal? 701 | bool operator!=(const ColorRGB& color, const ColorRGB& color2) 702 | { 703 | return(!(color.r == color2.r && color.g == color2.g && color.b == color2.b)); 704 | } 705 | 706 | ColorHSL::ColorHSL(Uint8 h, Uint8 s, Uint8 l) 707 | { 708 | this->h = h; 709 | this->s = s; 710 | this->l = l; 711 | } 712 | ColorHSL::ColorHSL() 713 | { 714 | this->h = 0; 715 | this->s = 0; 716 | this->l = 0; 717 | } 718 | ColorHSV::ColorHSV(Uint8 h, Uint8 s, Uint8 v) 719 | { 720 | this->h = h; 721 | this->s = s; 722 | this->v = v; 723 | } 724 | ColorHSV::ColorHSV() 725 | { 726 | this->h = 0; 727 | this->s = 0; 728 | this->v = 0; 729 | } 730 | 731 | 732 | //////////////////////////////////////////////////////////////////////////////// 733 | //COLOR CONVERSIONS///////////////////////////////////////////////////////////// 734 | //////////////////////////////////////////////////////////////////////////////// 735 | 736 | /* 737 | Convert colors from one type to another 738 | r=red g=green b=blue h=hue s=saturation l=lightness v=value 739 | Color components from the color structs are Uint8's between 0 and 255 740 | color components used in the calculations are normalized between 0.0-1.0 741 | */ 742 | 743 | //Converts an RGB color to HSL color 744 | ColorHSL RGBtoHSL(const ColorRGB& colorRGB) 745 | { 746 | float r, g, b, h = 0, s = 0, l; //this function works with floats between 0 and 1 747 | r = colorRGB.r / 256.0; 748 | g = colorRGB.g / 256.0; 749 | b = colorRGB.b / 256.0; 750 | 751 | float maxColor = std::max(r, std::max(g, b)); 752 | float minColor = std::min(r, std::min(g, b)); 753 | 754 | if(minColor == maxColor) //R = G = B, so it's a shade of grey 755 | { 756 | h = 0; //it doesn't matter what value it has 757 | s = 0; 758 | l = r; //doesn't matter if you pick r, g, or b 759 | } 760 | else 761 | { 762 | l = (minColor + maxColor) / 2; 763 | 764 | if(l < 0.5) s = (maxColor - minColor) / (maxColor + minColor); 765 | if(l >= 0.5) s = (maxColor - minColor) / (2.0 - maxColor - minColor); 766 | 767 | if(r == maxColor) h = (g - b) / (maxColor - minColor); 768 | if(g == maxColor) h = 2.0 + (b - r) / (maxColor - minColor); 769 | if(b == maxColor) h = 4.0 + (r - g) / (maxColor - minColor); 770 | 771 | h /= 6; //to bring it to a number between 0 and 1 772 | if(h < 0) h += 1; 773 | } 774 | 775 | ColorHSL colorHSL; 776 | colorHSL.h = int(h * 255.0); 777 | colorHSL.s = int(s * 255.0); 778 | colorHSL.l = int(l * 255.0); 779 | return colorHSL; 780 | } 781 | 782 | //Converts an HSL color to RGB color 783 | ColorRGB HSLtoRGB(const ColorHSL& colorHSL) 784 | { 785 | float r, g, b, h, s, l; //this function works with floats between 0 and 1 786 | float temp1, temp2, tempr, tempg, tempb; 787 | h = colorHSL.h / 256.0; 788 | s = colorHSL.s / 256.0; 789 | l = colorHSL.l / 256.0; 790 | 791 | //If saturation is 0, the color is a shade of grey 792 | if(s == 0) r = g = b = l; 793 | //If saturation > 0, more complex calculations are needed 794 | else 795 | { 796 | //set the temporary values 797 | if(l < 0.5) temp2 = l * (1 + s); 798 | else temp2 = (l + s) - (l * s); 799 | temp1 = 2 * l - temp2; 800 | tempr=h + 1.0 / 3.0; 801 | if(tempr > 1.0) tempr--; 802 | tempg=h; 803 | tempb=h-1.0 / 3.0; 804 | if(tempb < 0.0) tempb++; 805 | 806 | //red 807 | if(tempr < 1.0 / 6.0) r = temp1 + (temp2 - temp1) * 6.0 * tempr; 808 | else if(tempr < 0.5) r = temp2; 809 | else if(tempr < 2.0 / 3.0) r = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempr) * 6.0; 810 | else r = temp1; 811 | 812 | //green 813 | if(tempg < 1.0 / 6.0) g = temp1 + (temp2 - temp1) * 6.0 * tempg; 814 | else if(tempg < 0.5) g=temp2; 815 | else if(tempg < 2.0 / 3.0) g = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempg) * 6.0; 816 | else g = temp1; 817 | 818 | //blue 819 | if(tempb < 1.0 / 6.0) b = temp1 + (temp2 - temp1) * 6.0 * tempb; 820 | else if(tempb < 0.5) b = temp2; 821 | else if(tempb < 2.0 / 3.0) b = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempb) * 6.0; 822 | else b = temp1; 823 | } 824 | 825 | ColorRGB colorRGB; 826 | colorRGB.r = int(r * 255.0); 827 | colorRGB.g = int(g * 255.0); 828 | colorRGB.b = int(b * 255.0); 829 | return colorRGB; 830 | } 831 | 832 | //Converts an RGB color to HSV color 833 | ColorHSV RGBtoHSV(const ColorRGB& colorRGB) 834 | { 835 | float r, g, b, h = 0.0, s = 0.0, v; //this function works with floats between 0 and 1 836 | r = colorRGB.r / 256.0; 837 | g = colorRGB.g / 256.0; 838 | b = colorRGB.b / 256.0; 839 | 840 | float maxColor = std::max(r, std::max(g, b)); 841 | float minColor = std::min(r, std::min(g, b)); 842 | 843 | v = maxColor; 844 | 845 | if(maxColor != 0.0) //avoid division by zero when the color is black 846 | { 847 | s = (maxColor - minColor) / maxColor; 848 | } 849 | 850 | if(s == 0.0) 851 | { 852 | h = 0.0; //it doesn't matter what value it has 853 | } 854 | else 855 | { 856 | if(r == maxColor) h = (g - b) / (maxColor - minColor); 857 | if(g == maxColor) h = 2.0 + (b - r) / (maxColor - minColor); 858 | if(b == maxColor) h = 4.0 + (r - g) / (maxColor - minColor); 859 | 860 | h /= 6.0; //to bring it to a number between 0 and 1 861 | if(h < 0.0) h++; 862 | } 863 | 864 | ColorHSV colorHSV; 865 | colorHSV.h = int(h * 255.0); 866 | colorHSV.s = int(s * 255.0); 867 | colorHSV.v = int(v * 255.0); 868 | return colorHSV; 869 | } 870 | 871 | //Converts an HSV color to RGB color 872 | ColorRGB HSVtoRGB(const ColorHSV& colorHSV) 873 | { 874 | float r, g, b, h, s, v; //this function works with floats between 0 and 1 875 | h = colorHSV.h / 256.0; 876 | s = colorHSV.s / 256.0; 877 | v = colorHSV.v / 256.0; 878 | 879 | //if saturation is 0, the color is a shade of grey 880 | if(s == 0.0) r = g = b = v; 881 | //if saturation > 0, more complex calculations are needed 882 | else 883 | { 884 | float f, p, q, t; 885 | int i; 886 | h *= 6.0; //to bring hue to a number between 0 and 6, better for the calculations 887 | i = int(floor(h)); //e.g. 2.7 becomes 2 and 3.01 becomes 3 or 4.9999 becomes 4 888 | f = h - i;//the fractional part of h 889 | 890 | p = v * (1.0 - s); 891 | q = v * (1.0 - (s * f)); 892 | t = v * (1.0 - (s * (1.0 - f))); 893 | 894 | switch(i) 895 | { 896 | case 0: r=v; g=t; b=p; break; 897 | case 1: r=q; g=v; b=p; break; 898 | case 2: r=p; g=v; b=t; break; 899 | case 3: r=p; g=q; b=v; break; 900 | case 4: r=t; g=p; b=v; break; 901 | case 5: r=v; g=p; b=q; break; 902 | default: r = g = b = 0; break; 903 | } 904 | } 905 | ColorRGB colorRGB; 906 | colorRGB.r = int(r * 255.0); 907 | colorRGB.g = int(g * 255.0); 908 | colorRGB.b = int(b * 255.0); 909 | return colorRGB; 910 | } 911 | 912 | Uint32 RGBtoINT(const ColorRGB& colorRGB) 913 | { 914 | return 65536 * colorRGB.r + 256 * colorRGB.g + colorRGB.b; 915 | } 916 | 917 | ColorRGB INTtoRGB(Uint32 colorINT) 918 | { 919 | ColorRGB colorRGB; 920 | colorRGB.r = (colorINT / 65536) % 256; 921 | colorRGB.g = (colorINT / 256) % 256; 922 | colorRGB.b = colorINT % 256; 923 | return colorRGB; 924 | } 925 | 926 | //////////////////////////////////////////////////////////////////////////////// 927 | //FILE FUNCTIONS//////////////////////////////////////////////////////////////// 928 | //////////////////////////////////////////////////////////////////////////////// 929 | 930 | void loadFile(std::vector& buffer, const std::string& filename) //designed for loading files from hard disk in an std::vector 931 | { 932 | std::ifstream file(filename.c_str(), std::ios::in|std::ios::binary|std::ios::ate); 933 | 934 | //get filesize 935 | std::streamsize size = 0; 936 | if(file.seekg(0, std::ios::end).good()) size = file.tellg(); 937 | if(file.seekg(0, std::ios::beg).good()) size -= file.tellg(); 938 | 939 | //read contents of the file into the vector 940 | buffer.resize(size_t(size)); 941 | if(size > 0) file.read((char*)(&buffer[0]), size); 942 | } 943 | 944 | //write given buffer to the file, overwriting the file, it doesn't append to it. 945 | void saveFile(const std::vector& buffer, const std::string& filename) 946 | { 947 | std::ofstream file(filename.c_str(), std::ios::out|std::ios::binary); 948 | file.write(buffer.size() ? (char*)&buffer[0] : 0, std::streamsize(buffer.size())); 949 | } 950 | 951 | //////////////////////////////////////////////////////////////////////////////// 952 | //IMAGE FUNCTIONS/////////////////////////////////////////////////////////////// 953 | //////////////////////////////////////////////////////////////////////////////// 954 | 955 | int loadImage(std::vector& out, unsigned long& w, unsigned long& h, const std::string& filename) 956 | { 957 | std::vector file, image; 958 | loadFile(file, filename); 959 | if(decodePNG(image, w, h, file)) return 1; 960 | 961 | out.resize(image.size() / 4); 962 | 963 | for(size_t i = 0; i < out.size(); i++) 964 | { 965 | out[i].r = image[i * 4 + 0]; 966 | out[i].g = image[i * 4 + 1]; 967 | out[i].b = image[i * 4 + 2]; 968 | //out[i].a = image[i * 4 + 3]; 969 | } 970 | 971 | return 0; 972 | } 973 | 974 | int loadImage(std::vector& out, unsigned long& w, unsigned long& h, const std::string& filename) 975 | { 976 | std::vector file, image; 977 | loadFile(file, filename); 978 | if(decodePNG(image, w, h, file)) return 1; 979 | 980 | out.resize(image.size() / 4); 981 | 982 | for(size_t i = 0; i < out.size(); i++) 983 | { 984 | out[i] = 0x1000000 * image[i * 4 + 3] + 0x10000 * image[i * 4 + 0] + 0x100 * image[i * 4 + 1] + image[i * 4 + 2]; 985 | } 986 | 987 | return 0; 988 | } 989 | 990 | //////////////////////////////////////////////////////////////////////////////// 991 | //TEXT FUNCTIONS//////////////////////////////////////////////////////////////// 992 | //////////////////////////////////////////////////////////////////////////////// 993 | 994 | //Draws character n at position x,y with color RGB and, if enabled, background color 995 | //This function is used by the text printing functions below, and uses the font data 996 | //defined below to draw the letter pixel by pixel 997 | void drawLetter(unsigned char n, int x, int y, const ColorRGB& color, bool bg, const ColorRGB& color2) 998 | { 999 | int u,v; 1000 | 1001 | for (v = 0; v < 8; v++) 1002 | for (u = 0; u < 8; u++) 1003 | { 1004 | if(font[n][u][v]) pset(x + u, y + v, color); 1005 | else if(bg) pset(x + u, y + v, color2); 1006 | } 1007 | } 1008 | 1009 | //Draws a string of text 1010 | int printString(const std::string& text, int x, int y, const ColorRGB& color, bool bg, const ColorRGB& color2, int forceLength) 1011 | { 1012 | int amount = 0; 1013 | for(size_t i = 0; i < text.size(); i++) 1014 | { 1015 | amount++; 1016 | drawLetter(text[i], x, y, color, bg, color2); 1017 | x += 8; 1018 | if(x > w - 8) {x %= 8; y += 8;} 1019 | if(y > h - 8) {y %= 8;} 1020 | } 1021 | while(amount < forceLength) 1022 | { 1023 | amount++; 1024 | drawLetter(' ', x, y, color, bg, color2); 1025 | x += 8; 1026 | if(x > w - 8) {x %= 8; y += 8;} 1027 | if(y > h - 8) {y %= 8;} 1028 | } 1029 | return h * x + y; 1030 | } 1031 | 1032 | //////////////////////////////////////////////////////////////////////////////// 1033 | //TEXT INPUT FUNCTIONS////////////////////////////////////////////////////////// 1034 | //////////////////////////////////////////////////////////////////////////////// 1035 | 1036 | const int ASCII_ENTER = 13; 1037 | const int ASCII_BACKSPACE = 8; 1038 | const int ASCII_SPACE = 32; //smallest printable ascii char 1039 | 1040 | Uint8 getInputCharacter() 1041 | { 1042 | int ascii = 0; 1043 | static int previouschar = 0; 1044 | 1045 | if ((event.key.keysym.unicode & 0xFF80) == 0) 1046 | { 1047 | if(event.type == SDL_KEYDOWN) 1048 | { 1049 | ascii = event.key.keysym.unicode & 0x7F; 1050 | } 1051 | } 1052 | 1053 | if(ascii < ASCII_SPACE && ascii != ASCII_ENTER && ascii != ASCII_BACKSPACE) ascii = 0; //<32 ones, except enter and backspace 1054 | 1055 | if(ascii != previouschar) previouschar = ascii; 1056 | else ascii = 0; 1057 | 1058 | return ascii; 1059 | } 1060 | //returns a string, length is the maximum length of the given string array 1061 | void getInputString(std::string& text, const std::string& message, bool clear, int x, int y, const ColorRGB& color, bool bg, const ColorRGB& color2) 1062 | { 1063 | std::vector screenBuffer; 1064 | getScreenBuffer(screenBuffer); 1065 | 1066 | bool enter = 0; 1067 | bool change = 1; 1068 | text.clear(); 1069 | 1070 | while(enter == 0) 1071 | { 1072 | if(done()) end(); 1073 | Uint8 temp = getInputCharacter(); 1074 | if(temp >= ASCII_SPACE) 1075 | { 1076 | text.push_back(temp); 1077 | change = 1; 1078 | } 1079 | if(temp == ASCII_BACKSPACE && text.size() > 0) {text.resize(text.size() - 1); change = 1; } 1080 | 1081 | if(change) 1082 | { 1083 | drawBuffer(&screenBuffer[0]); 1084 | int pos = print(message, x, y, color, bg, color2); 1085 | int x2 = pos / h, y2 = pos % h; 1086 | print(text, x2, y2, color, bg, color2); 1087 | redraw(); 1088 | } 1089 | if(temp == ASCII_ENTER) {enter = 1;} 1090 | } 1091 | 1092 | //remove the input stuff from the screen again so there is room for possible next input 1093 | if(clear) 1094 | { 1095 | drawBuffer(&screenBuffer[0]); 1096 | redraw(); 1097 | } 1098 | } 1099 | 1100 | void encodeBase64(const std::vector& in, std::string& out) 1101 | { 1102 | const std::string characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 1103 | unsigned long bit24 = 0; 1104 | unsigned long sextet[4] = {0, 0, 0, 0}; 1105 | unsigned long octet[3] = {0, 0, 0}; 1106 | 1107 | out.clear(); 1108 | out.reserve((4 * in.size()) / 3); 1109 | 1110 | for(size_t pos = 0; pos < in.size(); pos += 3) 1111 | { 1112 | octet[0] = in[pos + 0]; 1113 | octet[1] = (pos + 1 < in.size()) ? in[pos + 1] : 0; 1114 | octet[2] = (pos + 2 < in.size()) ? in[pos + 2] : 0; 1115 | 1116 | bit24 = 256 * 256 * octet[0]; 1117 | bit24 += 256 * octet[1]; 1118 | bit24 += octet[2]; 1119 | 1120 | sextet[0] = (bit24 / (64 * 64 * 64)) % 64; 1121 | sextet[1] = (bit24 / ( 64 * 64)) % 64; 1122 | sextet[2] = (bit24 / ( 64)) % 64; 1123 | sextet[3] = (bit24 / ( 1)) % 64; 1124 | 1125 | for(size_t i = 0; i < 4; i++) 1126 | { 1127 | if(pos + i - 1 < in.size()) out.push_back(characters[sextet[i]]); 1128 | else out.push_back('='); 1129 | } 1130 | 1131 | if(pos % 57 == 0 && pos != 0) out.push_back(10); //newline char every 76 chars (57 = 3/4th of 76) 1132 | } 1133 | } 1134 | 1135 | void decodeBase64(std::vector& out, const std::string& in) 1136 | { 1137 | unsigned long bit24 = 0; 1138 | unsigned long sextet[4] = {0, 0, 0, 0}; 1139 | unsigned long octet[3] = {0, 0, 0}; 1140 | 1141 | out.clear(); 1142 | out.reserve((3 * in.size()) / 4); 1143 | 1144 | for(size_t pos = 0; pos < in.size() - 3; pos += 4) 1145 | { 1146 | for(size_t i = 0; i < 4; i++) 1147 | { 1148 | unsigned long c = in[pos + i]; 1149 | if(c >= 65 && c <= 90) sextet[i] = c - 65; 1150 | else if(c >= 97 && c <= 122) sextet[i] = c - 71; 1151 | else if(c >= 48 && c <= 57) sextet[i] = c + 4; 1152 | else if(c == '+') sextet[i] = 62; 1153 | else if(c == '/') sextet[i] = 63; 1154 | else if(c == '=') sextet[i] = 0; //value doesn't matter 1155 | else //unknown char, can be whitespace, newline, ... 1156 | { 1157 | pos++; 1158 | if(pos > in.size() - 3) return; 1159 | i--; 1160 | } 1161 | } 1162 | 1163 | bit24 = 64 * 64 * 64 * sextet[0]; 1164 | bit24 += 64 * 64 * sextet[1]; 1165 | bit24 += 64 * sextet[2]; 1166 | bit24 += sextet[3]; 1167 | 1168 | octet[0] = (bit24 / (256 * 256)) % 256; 1169 | octet[1] = (bit24 / ( 256)) % 256; 1170 | octet[2] = (bit24 / ( 1)) % 256; 1171 | 1172 | for(size_t i = 0; i < 3; i++) 1173 | { 1174 | if(in[pos + 1 + i] != '=') out.push_back(octet[i]); 1175 | } 1176 | } 1177 | } 1178 | 1179 | //////////////////////////////////////////////////////////////////////////////// 1180 | // PNG // 1181 | //////////////////////////////////////////////////////////////////////////////// 1182 | 1183 | int decodePNG(std::vector& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32) 1184 | { 1185 | // picoPNG version 20101224 1186 | // Copyright (c) 2005-2010 Lode Vandevenne 1187 | // 1188 | // This software is provided 'as-is', without any express or implied 1189 | // warranty. In no event will the authors be held liable for any damages 1190 | // arising from the use of this software. 1191 | // 1192 | // Permission is granted to anyone to use this software for any purpose, 1193 | // including commercial applications, and to alter it and redistribute it 1194 | // freely, subject to the following restrictions: 1195 | // 1196 | // 1. The origin of this software must not be misrepresented; you must not 1197 | // claim that you wrote the original software. If you use this software 1198 | // in a product, an acknowledgment in the product documentation would be 1199 | // appreciated but is not required. 1200 | // 2. Altered source versions must be plainly marked as such, and must not be 1201 | // misrepresented as being the original software. 1202 | // 3. This notice may not be removed or altered from any source distribution. 1203 | 1204 | static const unsigned long LENBASE[29] = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258}; 1205 | static const unsigned long LENEXTRA[29] = {0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; 1206 | static const unsigned long DISTBASE[30] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; 1207 | static const unsigned long DISTEXTRA[30] = {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; 1208 | static const unsigned long CLCL[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; //code length code lengths 1209 | struct Zlib //nested functions for zlib decompression 1210 | { 1211 | static unsigned long readBitFromStream(size_t& bitp, const unsigned char* bits) { unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1; bitp++; return result;} 1212 | static unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits) 1213 | { 1214 | unsigned long result = 0; 1215 | for(size_t i = 0; i < nbits; i++) result += (readBitFromStream(bitp, bits)) << i; 1216 | return result; 1217 | } 1218 | struct HuffmanTree 1219 | { 1220 | int makeFromLengths(const std::vector& bitlen, unsigned long maxbitlen) 1221 | { //make tree given the lengths 1222 | unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0; 1223 | std::vector tree1d(numcodes), blcount(maxbitlen + 1, 0), nextcode(maxbitlen + 1, 0); 1224 | for(unsigned long bits = 0; bits < numcodes; bits++) blcount[bitlen[bits]]++; //count number of instances of each code length 1225 | for(unsigned long bits = 1; bits <= maxbitlen; bits++) nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; 1226 | for(unsigned long n = 0; n < numcodes; n++) if(bitlen[n] != 0) tree1d[n] = nextcode[bitlen[n]]++; //generate all the codes 1227 | tree2d.clear(); tree2d.resize(numcodes * 2, 32767); //32767 here means the tree2d isn't filled there yet 1228 | for(unsigned long n = 0; n < numcodes; n++) //the codes 1229 | for(unsigned long i = 0; i < bitlen[n]; i++) //the bits for this code 1230 | { 1231 | unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1; 1232 | if(treepos > numcodes - 2) return 55; 1233 | if(tree2d[2 * treepos + bit] == 32767) //not yet filled in 1234 | { 1235 | if(i + 1 == bitlen[n]) { tree2d[2 * treepos + bit] = n; treepos = 0; } //last bit 1236 | else { tree2d[2 * treepos + bit] = ++nodefilled + numcodes; treepos = nodefilled; } //addresses are encoded as values > numcodes 1237 | } 1238 | else treepos = tree2d[2 * treepos + bit] - numcodes; //subtract numcodes from address to get address value 1239 | } 1240 | return 0; 1241 | } 1242 | int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const 1243 | { //Decodes a symbol from the tree 1244 | unsigned long numcodes = (unsigned long)tree2d.size() / 2; 1245 | if(treepos >= numcodes) return 11; //error: you appeared outside the codetree 1246 | result = tree2d[2 * treepos + bit]; 1247 | decoded = (result < numcodes); 1248 | treepos = decoded ? 0 : result - numcodes; 1249 | return 0; 1250 | } 1251 | std::vector tree2d; //2D representation of a huffman tree: The one dimension is "0" or "1", the other contains all nodes and leaves of the tree. 1252 | }; 1253 | struct Inflator 1254 | { 1255 | int error; 1256 | void inflate(std::vector& out, const std::vector& in, size_t inpos = 0) 1257 | { 1258 | size_t bp = 0, pos = 0; //bit pointer and byte pointer 1259 | error = 0; 1260 | unsigned long BFINAL = 0; 1261 | while(!BFINAL && !error) 1262 | { 1263 | if(bp >> 3 >= in.size()) { error = 52; return; } //error, bit pointer will jump past memory 1264 | BFINAL = readBitFromStream(bp, &in[inpos]); 1265 | unsigned long BTYPE = readBitFromStream(bp, &in[inpos]); BTYPE += 2 * readBitFromStream(bp, &in[inpos]); 1266 | if(BTYPE == 3) { error = 20; return; } //error: invalid BTYPE 1267 | else if(BTYPE == 0) inflateNoCompression(out, &in[inpos], bp, pos, in.size()); 1268 | else inflateHuffmanBlock(out, &in[inpos], bp, pos, in.size(), BTYPE); 1269 | } 1270 | if(!error) out.resize(pos); //Only now we know the true size of out, resize it to that 1271 | } 1272 | void generateFixedTrees(HuffmanTree& tree, HuffmanTree& treeD) //get the tree of a deflated block with fixed tree 1273 | { 1274 | std::vector bitlen(288, 8), bitlenD(32, 5);; 1275 | for(size_t i = 144; i <= 255; i++) bitlen[i] = 9; 1276 | for(size_t i = 256; i <= 279; i++) bitlen[i] = 7; 1277 | tree.makeFromLengths(bitlen, 15); 1278 | treeD.makeFromLengths(bitlenD, 15); 1279 | } 1280 | HuffmanTree codetree, codetreeD, codelengthcodetree; //the code tree for Huffman codes, dist codes, and code length codes 1281 | unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& codetree, size_t inlength) 1282 | { //decode a single symbol from given list of bits with given code tree. return value is the symbol 1283 | bool decoded; unsigned long ct; 1284 | for(size_t treepos = 0;;) 1285 | { 1286 | if((bp & 0x07) == 0 && (bp >> 3) > inlength) { error = 10; return 0; } //error: end reached without endcode 1287 | error = codetree.decode(decoded, ct, treepos, readBitFromStream(bp, in)); if(error) return 0; //stop, an error happened 1288 | if(decoded) return ct; 1289 | } 1290 | } 1291 | void getTreeInflateDynamic(HuffmanTree& tree, HuffmanTree& treeD, const unsigned char* in, size_t& bp, size_t inlength) 1292 | { //get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree 1293 | std::vector bitlen(288, 0), bitlenD(32, 0); 1294 | if(bp >> 3 >= inlength - 2) { error = 49; return; } //the bit pointer is or will go past the memory 1295 | size_t HLIT = readBitsFromStream(bp, in, 5) + 257; //number of literal/length codes + 257 1296 | size_t HDIST = readBitsFromStream(bp, in, 5) + 1; //number of dist codes + 1 1297 | size_t HCLEN = readBitsFromStream(bp, in, 4) + 4; //number of code length codes + 4 1298 | std::vector codelengthcode(19); //lengths of tree to decode the lengths of the dynamic tree 1299 | for(size_t i = 0; i < 19; i++) codelengthcode[CLCL[i]] = (i < HCLEN) ? readBitsFromStream(bp, in, 3) : 0; 1300 | error = codelengthcodetree.makeFromLengths(codelengthcode, 7); if(error) return; 1301 | size_t i = 0, replength; 1302 | while(i < HLIT + HDIST) 1303 | { 1304 | unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength); if(error) return; 1305 | if(code <= 15) { if(i < HLIT) bitlen[i++] = code; else bitlenD[i++ - HLIT] = code; } //a length code 1306 | else if(code == 16) //repeat previous 1307 | { 1308 | if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory 1309 | replength = 3 + readBitsFromStream(bp, in, 2); 1310 | unsigned long value; //set value to the previous code 1311 | if((i - 1) < HLIT) value = bitlen[i - 1]; 1312 | else value = bitlenD[i - HLIT - 1]; 1313 | for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths 1314 | { 1315 | if(i >= HLIT + HDIST) { error = 13; return; } //error: i is larger than the amount of codes 1316 | if(i < HLIT) bitlen[i++] = value; else bitlenD[i++ - HLIT] = value; 1317 | } 1318 | } 1319 | else if(code == 17) //repeat "0" 3-10 times 1320 | { 1321 | if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory 1322 | replength = 3 + readBitsFromStream(bp, in, 3); 1323 | for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths 1324 | { 1325 | if(i >= HLIT + HDIST) { error = 14; return; } //error: i is larger than the amount of codes 1326 | if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0; 1327 | } 1328 | } 1329 | else if(code == 18) //repeat "0" 11-138 times 1330 | { 1331 | if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory 1332 | replength = 11 + readBitsFromStream(bp, in, 7); 1333 | for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths 1334 | { 1335 | if(i >= HLIT + HDIST) { error = 15; return; } //error: i is larger than the amount of codes 1336 | if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0; 1337 | } 1338 | } 1339 | else { error = 16; return; } //error: somehow an unexisting code appeared. This can never happen. 1340 | } 1341 | if(bitlen[256] == 0) { error = 64; return; } //the length of the end code 256 must be larger than 0 1342 | error = tree.makeFromLengths(bitlen, 15); if(error) return; //now we've finally got HLIT and HDIST, so generate the code trees, and the function is done 1343 | error = treeD.makeFromLengths(bitlenD, 15); if(error) return; 1344 | } 1345 | void inflateHuffmanBlock(std::vector& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype) 1346 | { 1347 | if(btype == 1) { generateFixedTrees(codetree, codetreeD); } 1348 | else if(btype == 2) { getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength); if(error) return; } 1349 | for(;;) 1350 | { 1351 | unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength); if(error) return; 1352 | if(code == 256) return; //end code 1353 | else if(code <= 255) //literal symbol 1354 | { 1355 | if(pos >= out.size()) out.resize((pos + 1) * 2); //reserve more room 1356 | out[pos++] = (unsigned char)(code); 1357 | } 1358 | else if(code >= 257 && code <= 285) //length code 1359 | { 1360 | size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257]; 1361 | if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory 1362 | length += readBitsFromStream(bp, in, numextrabits); 1363 | unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength); if(error) return; 1364 | if(codeD > 29) { error = 18; return; } //error: invalid dist code (30-31 are never used) 1365 | unsigned long dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD]; 1366 | if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory 1367 | dist += readBitsFromStream(bp, in, numextrabitsD); 1368 | size_t start = pos, back = start - dist; //backwards 1369 | if(pos + length >= out.size()) out.resize((pos + length) * 2); //reserve more room 1370 | for(size_t i = 0; i < length; i++) { out[pos++] = out[back++]; if(back >= start) back = start - dist; } 1371 | } 1372 | } 1373 | } 1374 | void inflateNoCompression(std::vector& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength) 1375 | { 1376 | while((bp & 0x7) != 0) bp++; //go to first boundary of byte 1377 | size_t p = bp / 8; 1378 | if(p >= inlength - 4) { error = 52; return; } //error, bit pointer will jump past memory 1379 | unsigned long LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3]; p += 4; 1380 | if(LEN + NLEN != 65535) { error = 21; return; } //error: NLEN is not one's complement of LEN 1381 | if(pos + LEN >= out.size()) out.resize(pos + LEN); 1382 | if(p + LEN > inlength) { error = 23; return; } //error: reading outside of in buffer 1383 | for(unsigned long n = 0; n < LEN; n++) out[pos++] = in[p++]; //read LEN bytes of literal data 1384 | bp = p * 8; 1385 | } 1386 | }; 1387 | int decompress(std::vector& out, const std::vector& in) //returns error value 1388 | { 1389 | Inflator inflator; 1390 | if(in.size() < 2) { return 53; } //error, size of zlib data too small 1391 | if((in[0] * 256 + in[1]) % 31 != 0) { return 24; } //error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way 1392 | unsigned long CM = in[0] & 15, CINFO = (in[0] >> 4) & 15, FDICT = (in[1] >> 5) & 1; 1393 | if(CM != 8 || CINFO > 7) { return 25; } //error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec 1394 | if(FDICT != 0) { return 26; } //error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary." 1395 | inflator.inflate(out, in, 2); 1396 | return inflator.error; //note: adler32 checksum was skipped and ignored 1397 | } 1398 | }; 1399 | struct PNG //nested functions for PNG decoding 1400 | { 1401 | struct Info 1402 | { 1403 | unsigned long width, height, colorType, bitDepth, compressionMethod, filterMethod, interlaceMethod, key_r, key_g, key_b; 1404 | bool key_defined; //is a transparent color key given? 1405 | std::vector palette; 1406 | } info; 1407 | int error; 1408 | void decode(std::vector& out, const unsigned char* in, size_t size, bool convert_to_rgba32) 1409 | { 1410 | error = 0; 1411 | if(size == 0 || in == 0) { error = 48; return; } //the given data is empty 1412 | readPngHeader(&in[0], size); if(error) return; 1413 | size_t pos = 33; //first byte of the first chunk after the header 1414 | std::vector idat; //the data from idat chunks 1415 | bool IEND = false, known_type = true; 1416 | info.key_defined = false; 1417 | while(!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer 1418 | { 1419 | if(pos + 8 >= size) { error = 30; return; } //error: size of the in buffer too small to contain next chunk 1420 | size_t chunkLength = read32bitInt(&in[pos]); pos += 4; 1421 | if(chunkLength > 2147483647) { error = 63; return; } 1422 | if(pos + chunkLength >= size) { error = 35; return; } //error: size of the in buffer too small to contain next chunk 1423 | if(in[pos + 0] == 'I' && in[pos + 1] == 'D' && in[pos + 2] == 'A' && in[pos + 3] == 'T') //IDAT chunk, containing compressed image data 1424 | { 1425 | idat.insert(idat.end(), &in[pos + 4], &in[pos + 4 + chunkLength]); 1426 | pos += (4 + chunkLength); 1427 | } 1428 | else if(in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D') { pos += 4; IEND = true; } 1429 | else if(in[pos + 0] == 'P' && in[pos + 1] == 'L' && in[pos + 2] == 'T' && in[pos + 3] == 'E') //palette chunk (PLTE) 1430 | { 1431 | pos += 4; //go after the 4 letters 1432 | info.palette.resize(4 * (chunkLength / 3)); 1433 | if(info.palette.size() > (4 * 256)) { error = 38; return; } //error: palette too big 1434 | for(size_t i = 0; i < info.palette.size(); i += 4) 1435 | { 1436 | for(size_t j = 0; j < 3; j++) info.palette[i + j] = in[pos++]; //RGB 1437 | info.palette[i + 3] = 255; //alpha 1438 | } 1439 | } 1440 | else if(in[pos + 0] == 't' && in[pos + 1] == 'R' && in[pos + 2] == 'N' && in[pos + 3] == 'S') //palette transparency chunk (tRNS) 1441 | { 1442 | pos += 4; //go after the 4 letters 1443 | if(info.colorType == 3) 1444 | { 1445 | if(4 * chunkLength > info.palette.size()) { error = 39; return; } //error: more alpha values given than there are palette entries 1446 | for(size_t i = 0; i < chunkLength; i++) info.palette[4 * i + 3] = in[pos++]; 1447 | } 1448 | else if(info.colorType == 0) 1449 | { 1450 | if(chunkLength != 2) { error = 40; return; } //error: this chunk must be 2 bytes for greyscale image 1451 | info.key_defined = 1; info.key_r = info.key_g = info.key_b = 256 * in[pos] + in[pos + 1]; pos += 2; 1452 | } 1453 | else if(info.colorType == 2) 1454 | { 1455 | if(chunkLength != 6) { error = 41; return; } //error: this chunk must be 6 bytes for RGB image 1456 | info.key_defined = 1; 1457 | info.key_r = 256 * in[pos] + in[pos + 1]; pos += 2; 1458 | info.key_g = 256 * in[pos] + in[pos + 1]; pos += 2; 1459 | info.key_b = 256 * in[pos] + in[pos + 1]; pos += 2; 1460 | } 1461 | else { error = 42; return; } //error: tRNS chunk not allowed for other color models 1462 | } 1463 | else //it's not an implemented chunk type, so ignore it: skip over the data 1464 | { 1465 | if(!(in[pos + 0] & 32)) { error = 69; return; } //error: unknown critical chunk (5th bit of first byte of chunk type is 0) 1466 | pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk 1467 | known_type = false; 1468 | } 1469 | pos += 4; //step over CRC (which is ignored) 1470 | } 1471 | unsigned long bpp = getBpp(info); 1472 | std::vector scanlines(((info.width * (info.height * bpp + 7)) / 8) + info.height); //now the out buffer will be filled 1473 | Zlib zlib; //decompress with the Zlib decompressor 1474 | error = zlib.decompress(scanlines, idat); if(error) return; //stop if the zlib decompressor returned an error 1475 | size_t bytewidth = (bpp + 7) / 8, outlength = (info.height * info.width * bpp + 7) / 8; 1476 | out.resize(outlength); //time to fill the out buffer 1477 | unsigned char* out_ = outlength ? &out[0] : 0; //use a regular pointer to the std::vector for faster code if compiled without optimization 1478 | if(info.interlaceMethod == 0) //no interlace, just filter 1479 | { 1480 | size_t linestart = 0, linelength = (info.width * bpp + 7) / 8; //length in bytes of a scanline, excluding the filtertype byte 1481 | if(bpp >= 8) //byte per byte 1482 | for(unsigned long y = 0; y < info.height; y++) 1483 | { 1484 | unsigned long filterType = scanlines[linestart]; 1485 | const unsigned char* prevline = (y == 0) ? 0 : &out_[(y - 1) * info.width * bytewidth]; 1486 | unFilterScanline(&out_[linestart - y], &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); if(error) return; 1487 | linestart += (1 + linelength); //go to start of next scanline 1488 | } 1489 | else //less than 8 bits per pixel, so fill it up bit per bit 1490 | { 1491 | std::vector templine((info.width * bpp + 7) >> 3); //only used if bpp < 8 1492 | for(size_t y = 0, obp = 0; y < info.height; y++) 1493 | { 1494 | unsigned long filterType = scanlines[linestart]; 1495 | const unsigned char* prevline = (y == 0) ? 0 : &out_[(y - 1) * info.width * bytewidth]; 1496 | unFilterScanline(&templine[0], &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); if(error) return; 1497 | for(size_t bp = 0; bp < info.width * bpp;) setBitOfReversedStream(obp, out_, readBitFromReversedStream(bp, &templine[0])); 1498 | linestart += (1 + linelength); //go to start of next scanline 1499 | } 1500 | } 1501 | } 1502 | else //interlaceMethod is 1 (Adam7) 1503 | { 1504 | size_t passw[7] = { (info.width + 7) / 8, (info.width + 3) / 8, (info.width + 3) / 4, (info.width + 1) / 4, (info.width + 1) / 2, (info.width + 0) / 2, (info.width + 0) / 1 }; 1505 | size_t passh[7] = { (info.height + 7) / 8, (info.height + 7) / 8, (info.height + 3) / 8, (info.height + 3) / 4, (info.height + 1) / 4, (info.height + 1) / 2, (info.height + 0) / 2 }; 1506 | size_t passstart[7] = {0}; 1507 | size_t pattern[28] = {0,4,0,2,0,1,0,0,0,4,0,2,0,1,8,8,4,4,2,2,1,8,8,8,4,4,2,2}; //values for the adam7 passes 1508 | for(int i = 0; i < 6; i++) passstart[i + 1] = passstart[i] + passh[i] * ((passw[i] ? 1 : 0) + (passw[i] * bpp + 7) / 8); 1509 | std::vector scanlineo((info.width * bpp + 7) / 8), scanlinen((info.width * bpp + 7) / 8); //"old" and "new" scanline 1510 | for(int i = 0; i < 7; i++) 1511 | adam7Pass(&out_[0], &scanlinen[0], &scanlineo[0], &scanlines[passstart[i]], info.width, pattern[i], pattern[i + 7], pattern[i + 14], pattern[i + 21], passw[i], passh[i], bpp); 1512 | } 1513 | if(convert_to_rgba32 && (info.colorType != 6 || info.bitDepth != 8)) //conversion needed 1514 | { 1515 | std::vector data = out; 1516 | error = convert(out, &data[0], info, info.width, info.height); 1517 | } 1518 | } 1519 | void readPngHeader(const unsigned char* in, size_t inlength) //read the information from the header and store it in the Info 1520 | { 1521 | if(inlength < 29) { error = 27; return; } //error: the data length is smaller than the length of the header 1522 | if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { error = 28; return; } //no PNG signature 1523 | if(in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') { error = 29; return; } //error: it doesn't start with a IHDR chunk! 1524 | info.width = read32bitInt(&in[16]); info.height = read32bitInt(&in[20]); 1525 | info.bitDepth = in[24]; info.colorType = in[25]; 1526 | info.compressionMethod = in[26]; if(in[26] != 0) { error = 32; return; } //error: only compression method 0 is allowed in the specification 1527 | info.filterMethod = in[27]; if(in[27] != 0) { error = 33; return; } //error: only filter method 0 is allowed in the specification 1528 | info.interlaceMethod = in[28]; if(in[28] > 1) { error = 34; return; } //error: only interlace methods 0 and 1 exist in the specification 1529 | error = checkColorValidity(info.colorType, info.bitDepth); 1530 | } 1531 | void unFilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned long filterType, size_t length) 1532 | { 1533 | switch(filterType) 1534 | { 1535 | case 0: for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; break; 1536 | case 1: 1537 | for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; 1538 | for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; 1539 | break; 1540 | case 2: 1541 | if(precon) for(size_t i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; 1542 | else for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; 1543 | break; 1544 | case 3: 1545 | if(precon) 1546 | { 1547 | for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; 1548 | for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); 1549 | } 1550 | else 1551 | { 1552 | for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; 1553 | for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; 1554 | } 1555 | break; 1556 | case 4: 1557 | if(precon) 1558 | { 1559 | for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + paethPredictor(0, precon[i], 0); 1560 | for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]); 1561 | } 1562 | else 1563 | { 1564 | for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; 1565 | for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0); 1566 | } 1567 | break; 1568 | default: error = 36; return; //error: unexisting filter type given 1569 | } 1570 | } 1571 | void adam7Pass(unsigned char* out, unsigned char* linen, unsigned char* lineo, const unsigned char* in, unsigned long w, size_t passleft, size_t passtop, size_t spacex, size_t spacey, size_t passw, size_t passh, unsigned long bpp) 1572 | { //filter and reposition the pixels into the output when the image is Adam7 interlaced. This function can only do it after the full image is already decoded. The out buffer must have the correct allocated memory size already. 1573 | if(passw == 0) return; 1574 | size_t bytewidth = (bpp + 7) / 8, linelength = 1 + ((bpp * passw + 7) / 8); 1575 | for(unsigned long y = 0; y < passh; y++) 1576 | { 1577 | unsigned char filterType = in[y * linelength], *prevline = (y == 0) ? 0 : lineo; 1578 | unFilterScanline(linen, &in[y * linelength + 1], prevline, bytewidth, filterType, (w * bpp + 7) / 8); if(error) return; 1579 | if(bpp >= 8) for(size_t i = 0; i < passw; i++) for(size_t b = 0; b < bytewidth; b++) //b = current byte of this pixel 1580 | out[bytewidth * w * (passtop + spacey * y) + bytewidth * (passleft + spacex * i) + b] = linen[bytewidth * i + b]; 1581 | else for(size_t i = 0; i < passw; i++) 1582 | { 1583 | size_t obp = bpp * w * (passtop + spacey * y) + bpp * (passleft + spacex * i), bp = i * bpp; 1584 | for(size_t b = 0; b < bpp; b++) setBitOfReversedStream(obp, out, readBitFromReversedStream(bp, &linen[0])); 1585 | } 1586 | unsigned char* temp = linen; linen = lineo; lineo = temp; //swap the two buffer pointers "line old" and "line new" 1587 | } 1588 | } 1589 | static unsigned long readBitFromReversedStream(size_t& bitp, const unsigned char* bits) { unsigned long result = (bits[bitp >> 3] >> (7 - (bitp & 0x7))) & 1; bitp++; return result;} 1590 | static unsigned long readBitsFromReversedStream(size_t& bitp, const unsigned char* bits, unsigned long nbits) 1591 | { 1592 | unsigned long result = 0; 1593 | for(size_t i = nbits - 1; i < nbits; i--) result += ((readBitFromReversedStream(bitp, bits)) << i); 1594 | return result; 1595 | } 1596 | void setBitOfReversedStream(size_t& bitp, unsigned char* bits, unsigned long bit) { bits[bitp >> 3] |= (bit << (7 - (bitp & 0x7))); bitp++; } 1597 | unsigned long read32bitInt(const unsigned char* buffer) { return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; } 1598 | int checkColorValidity(unsigned long colorType, unsigned long bd) //return type is a LodePNG error code 1599 | { 1600 | if((colorType == 2 || colorType == 4 || colorType == 6)) { if(!(bd == 8 || bd == 16)) return 37; else return 0; } 1601 | else if(colorType == 0) { if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; else return 0; } 1602 | else if(colorType == 3) { if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; else return 0; } 1603 | else return 31; //unexisting color type 1604 | } 1605 | unsigned long getBpp(const Info& info) 1606 | { 1607 | if(info.colorType == 2) return (3 * info.bitDepth); 1608 | else if(info.colorType >= 4) return (info.colorType - 2) * info.bitDepth; 1609 | else return info.bitDepth; 1610 | } 1611 | int convert(std::vector& out, const unsigned char* in, Info& infoIn, unsigned long w, unsigned long h) 1612 | { //converts from any color type to 32-bit. return value = LodePNG error code 1613 | size_t numpixels = w * h, bp = 0; 1614 | out.resize(numpixels * 4); 1615 | unsigned char* out_ = out.empty() ? 0 : &out[0]; //faster if compiled without optimization 1616 | if(infoIn.bitDepth == 8 && infoIn.colorType == 0) //greyscale 1617 | for(size_t i = 0; i < numpixels; i++) 1618 | { 1619 | out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = in[i]; 1620 | out_[4 * i + 3] = (infoIn.key_defined && in[i] == infoIn.key_r) ? 0 : 255; 1621 | } 1622 | else if(infoIn.bitDepth == 8 && infoIn.colorType == 2) //RGB color 1623 | for(size_t i = 0; i < numpixels; i++) 1624 | { 1625 | for(size_t c = 0; c < 3; c++) out_[4 * i + c] = in[3 * i + c]; 1626 | out_[4 * i + 3] = (infoIn.key_defined == 1 && in[3 * i + 0] == infoIn.key_r && in[3 * i + 1] == infoIn.key_g && in[3 * i + 2] == infoIn.key_b) ? 0 : 255; 1627 | } 1628 | else if(infoIn.bitDepth == 8 && infoIn.colorType == 3) //indexed color (palette) 1629 | for(size_t i = 0; i < numpixels; i++) 1630 | { 1631 | if(4U * in[i] >= infoIn.palette.size()) return 46; 1632 | for(size_t c = 0; c < 4; c++) out_[4 * i + c] = infoIn.palette[4 * in[i] + c]; //get rgb colors from the palette 1633 | } 1634 | else if(infoIn.bitDepth == 8 && infoIn.colorType == 4) //greyscale with alpha 1635 | for(size_t i = 0; i < numpixels; i++) 1636 | { 1637 | out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = in[2 * i + 0]; 1638 | out_[4 * i + 3] = in[2 * i + 1]; 1639 | } 1640 | else if(infoIn.bitDepth == 8 && infoIn.colorType == 6) for(size_t i = 0; i < numpixels; i++) for(size_t c = 0; c < 4; c++) out_[4 * i + c] = in[4 * i + c]; //RGB with alpha 1641 | else if(infoIn.bitDepth == 16 && infoIn.colorType == 0) //greyscale 1642 | for(size_t i = 0; i < numpixels; i++) 1643 | { 1644 | out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = in[2 * i]; 1645 | out_[4 * i + 3] = (infoIn.key_defined && 256U * in[i] + in[i + 1] == infoIn.key_r) ? 0 : 255; 1646 | } 1647 | else if(infoIn.bitDepth == 16 && infoIn.colorType == 2) //RGB color 1648 | for(size_t i = 0; i < numpixels; i++) 1649 | { 1650 | for(size_t c = 0; c < 3; c++) out_[4 * i + c] = in[6 * i + 2 * c]; 1651 | out_[4 * i + 3] = (infoIn.key_defined && 256U*in[6*i+0]+in[6*i+1] == infoIn.key_r && 256U*in[6*i+2]+in[6*i+3] == infoIn.key_g && 256U*in[6*i+4]+in[6*i+5] == infoIn.key_b) ? 0 : 255; 1652 | } 1653 | else if(infoIn.bitDepth == 16 && infoIn.colorType == 4) //greyscale with alpha 1654 | for(size_t i = 0; i < numpixels; i++) 1655 | { 1656 | out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = in[4 * i]; //most significant byte 1657 | out_[4 * i + 3] = in[4 * i + 2]; 1658 | } 1659 | else if(infoIn.bitDepth == 16 && infoIn.colorType == 6) for(size_t i = 0; i < numpixels; i++) for(size_t c = 0; c < 4; c++) out_[4 * i + c] = in[8 * i + 2 * c]; //RGB with alpha 1660 | else if(infoIn.bitDepth < 8 && infoIn.colorType == 0) //greyscale 1661 | for(size_t i = 0; i < numpixels; i++) 1662 | { 1663 | unsigned long value = (readBitsFromReversedStream(bp, in, infoIn.bitDepth) * 255) / ((1 << infoIn.bitDepth) - 1); //scale value from 0 to 255 1664 | out_[4 * i + 0] = out_[4 * i + 1] = out_[4 * i + 2] = (unsigned char)(value); 1665 | out_[4 * i + 3] = (infoIn.key_defined && value && ((1U << infoIn.bitDepth) - 1U) == infoIn.key_r && ((1U << infoIn.bitDepth) - 1U)) ? 0 : 255; 1666 | } 1667 | else if(infoIn.bitDepth < 8 && infoIn.colorType == 3) //palette 1668 | for(size_t i = 0; i < numpixels; i++) 1669 | { 1670 | unsigned long value = readBitsFromReversedStream(bp, in, infoIn.bitDepth); 1671 | if(4 * value >= infoIn.palette.size()) return 47; 1672 | for(size_t c = 0; c < 4; c++) out_[4 * i + c] = infoIn.palette[4 * value + c]; //get rgb colors from the palette 1673 | } 1674 | return 0; 1675 | } 1676 | unsigned char paethPredictor(short a, short b, short c) //Paeth predicter, used by PNG filter type 4 1677 | { 1678 | short p = a + b - c, pa = p > a ? (p - a) : (a - p), pb = p > b ? (p - b) : (b - p), pc = p > c ? (p - c) : (c - p); 1679 | return (unsigned char)((pa <= pb && pa <= pc) ? a : pb <= pc ? b : c); 1680 | } 1681 | }; 1682 | PNG decoder; decoder.decode(out_image, in_png, in_size, convert_to_rgba32); 1683 | image_width = decoder.info.width; image_height = decoder.info.height; 1684 | return decoder.error; 1685 | } 1686 | 1687 | int decodePNG(std::vector& out_image_32bit, unsigned long& image_width, unsigned long& image_height, const std::vector& in_png) 1688 | { 1689 | return decodePNG(out_image_32bit, image_width, image_height, in_png.size() ? &in_png[0] : 0, in_png.size()); 1690 | } 1691 | 1692 | //////////////////////////////////////////////////////////////////////////////// 1693 | //DATA////////////////////////////////////////////////////////////////////////// 1694 | //////////////////////////////////////////////////////////////////////////////// 1695 | 1696 | 1697 | bool font[256][8][8]; 1698 | struct GenerateFont 1699 | { 1700 | GenerateFont() 1701 | { 1702 | /* 1703 | The full extended ASCII character set, in the form 256 bitmap symbols of 8x8 1704 | pixels in one 128x128 PNG, base64-encoded. 1705 | The background color is black, not transparent. 1706 | */ 1707 | const std::string font8x8string = "\ 1708 | iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQAAAADrRVxmAAAEjklEQVR4AY2TBYhsyRWG/9hqPcEP\n\ 1709 | TLOO24n1u8Bdxd3iWK1VrICOdQ6kcmeDRXGJJ0gch6CxgoGKHV7Qx8A8aWxdOjZ7gaLuVt2RdfnL\n\ 1710 | mq//I9eAYfDnu/OYMJ3r7939IfCNqXShgn7vevfBhz8F/PKghK6jJ9wPz7l7d/vZUUro9x7409cd\n\ 1711 | P/wE8Ke9EEppQPsZ/PCJrrsUWojS6tNzyPmqllSH7eOfamWBrmtlhTbxh3js3eR2BxARjrQ527uD\n\ 1712 | PfQ9EeMumjDeM7gDqYFkbHT9hFLIfU1bSClxGobqILej1TA0x9TXHAN2ZOipJy53TVSr9G7ne0Or\n\ 1713 | ciICRkw4FXvP6CxZNF0HiPchecvsGIBpoETtoqWej+IZXmgNS1NGBjdQDF/GsWPO5EenaDlW4DiH\n\ 1714 | cFqjVWHwYzjViV9YZJG7LAIQrEC3qn20qjqHKC6zMX6MRpXZZjLI3HVqRpLAHGEIz7I1xSirAaqD\n\ 1715 | oaxa/VaJVwwGJGcxIlZWzATLr2sDgXOfS+lVbnl8m1KHJM75hx5yysbZ5x/3uKouus98JlbgbTlM\n\ 1716 | FYToRGJmI/bS1Qry7LiorP6hdDEhasthXQXOpeQh2qps71GRx0ur8jplqUM1JS0S5Tzg1OnvZ6DW\n\ 1717 | UjeDDVfg9QeWyQNBQ1fBpU7YEiXAXpZFBcXzQ9YQKhCvrPJ84mSva2C7eFwkc0pS5N2C12kCY4MF\n\ 1718 | OmyYNquGGBYGvu7H948kiBG79itx/OwogHEadKtrJs9FFQguarbqmIQvNaBONdrgmiM1sP6jrGVr\n\ 1719 | Hpf9xyWp4FgjsMKpzmPWgjbro5+MWUx8ucPl9X4I8w3KFcDDqY/HN6hdQ2pA5huk3BwJIVjzIZXi\n\ 1720 | wyNHwJrtSGv2yeRaBQXbW17uZJZgMeiGUTcNG2WoYg/gtiGBGRHSi0io26pbCaCq1xljolNlYhWI\n\ 1721 | 5ntCCHHIuQEFaftTg42xgpxhBrl3GIbrWqJeVCGY1eNEC9wjaDgpdbo9C8CrburoQF73d5B3FbOC\n\ 1722 | Do/IPsGaIiKqmjRu8/8GyK7OasCyVoceKdeQx/NVqjkuisgwmE4os+7jRCNAwNkVFh2A3PcMpHTc\n\ 1723 | 2mKEdwBU6YMKWQFQ7xjY34dzMCw6PntPb3O+57+kH0TgVhOI0Tz8aaoObeDyMGg0F//V1xyDyKAK\n\ 1724 | CPCDn/d4lW59DwG446NXiGgJLJd1VvKt33311WB2jEuMh8tyuBxfdtB1y+uuO3GM42GpjsNSxvHE\n\ 1725 | saxzCYCa49XgdQ6qE41gHm8Kbl1euPfF6cL04nTqYAZ4vqJpavPCvfXv2YHZQ3OCtr0D0Ca1RZiw\n\ 1726 | PTe9Aajz1lvr8SoH1eMETNN076231jbH2s72HABattUu4h2DqgIgd90C1/UCSCkObs3eM4xVrDV5\n\ 1727 | HtxlSYmGISouj9GTOtKSwsFBVvxNo+cGkteDg2OH00DivQ5DyxGjL2KJu+cFtlcgAm3LR0cVWzpL\n\ 1728 | jA44JwIgM/MO8Rc9YLxBI2R3CD+ZQdcfOYgyOqrA9kc5iPBFED3rQ3MAoH3+CQAPNEfOuYJXf6q0\n\ 1729 | QtOibS8BEX/cffjo+7IAAAAASUVORK5CYII="; 1730 | 1731 | std::vector png, image; 1732 | decodeBase64(png, font8x8string); 1733 | unsigned long w, h; 1734 | decodePNG(image, w, h, &png[0], png.size()); 1735 | for(size_t c = 0; c < 256; c++) 1736 | for(size_t y = 0; y < 8; y++) 1737 | for(size_t x = 0; x < 8; x++) 1738 | { 1739 | font[c][x][y] = image[4 * 128 * (8 * (c / 16) + y) + 4 * (8 * (c % 16) + x)] != 0; 1740 | } 1741 | } 1742 | }; 1743 | GenerateFont generateFont; 1744 | 1745 | //////////////////////////////////////////////////////////////////////////////// 1746 | //Multithreading helper functions/////////////////////////////////////////////// 1747 | //////////////////////////////////////////////////////////////////////////////// 1748 | 1749 | //SDL's C functions don't have destructors and such so therefore this here is needed 1750 | 1751 | //currently only needed for audio, therefor it's not in a different cpp file. 1752 | 1753 | //this creates SDL mutexes and makes sure that they're destroyed at the end. MutexFactory does the deletion! 1754 | struct MutexFactory 1755 | { 1756 | SDL_mutex* createMutex() 1757 | { 1758 | mutexes.push_back(SDL_CreateMutex()); 1759 | return mutexes.back(); 1760 | } 1761 | 1762 | ~MutexFactory() 1763 | { 1764 | for(size_t i = 0; i < mutexes.size(); i++) 1765 | SDL_DestroyMutex(mutexes[i]); 1766 | } 1767 | 1768 | private: 1769 | 1770 | std::vector mutexes; 1771 | }; 1772 | 1773 | MutexFactory mutexFactory; 1774 | 1775 | //this does SDL_mutexP in the ctor and SDL_mutexV in the dtor so no matter where you leave a function, SDL_mutexV is called 1776 | struct Mutex 1777 | { 1778 | SDL_mutex** m; 1779 | 1780 | Mutex(SDL_mutex*& mutex) 1781 | { 1782 | m = &mutex; 1783 | SDL_mutexP(*m); 1784 | } 1785 | 1786 | ~Mutex() 1787 | { 1788 | SDL_mutexV(*m); 1789 | } 1790 | }; 1791 | 1792 | //////////////////////////////////////////////////////////////////////////////// 1793 | //Soundcard functions/////////////////////////////////////////////////////////// 1794 | //////////////////////////////////////////////////////////////////////////////// 1795 | 1796 | size_t audio_min_samples = 4096; //safety buffer to avoid clicks 1797 | size_t audio_max_samples = 8192; //avoid too long queue 1798 | 1799 | double audio_volume = 1.0; 1800 | int audio_mode = 2; //0=off, 1=full (volume ignored), 2=volume-controlled 1801 | 1802 | void audioSetBufferSamplesRange(size_t min_samples, size_t max_samples) 1803 | { 1804 | audio_min_samples = min_samples; 1805 | audio_max_samples = max_samples; 1806 | } 1807 | 1808 | void audioSetMode(int mode) //0: silent, 1: full (no volume calculations ==> faster), 2: volume-controlled (= default value) 1809 | { 1810 | audio_mode = mode; 1811 | } 1812 | 1813 | void audioSetVolume(double volume) //multiplier used if mode is 2 (volume-controlled). Default value is 1.0. 1814 | { 1815 | audio_volume = volume; 1816 | } 1817 | 1818 | /* 1819 | Avoid the callback function and pushSamples function to be called at the same time, 1820 | or the std::vector can be invalid as two threads at the same time change it. 1821 | This SDL_mutex helps eliminate that problem. 1822 | */ 1823 | SDL_mutex* audio_lock = mutexFactory.createMutex(); 1824 | 1825 | std::vector audio_data(audio_min_samples, 0); 1826 | 1827 | SDL_AudioSpec audiospec_wanted, audiospec_obtained; 1828 | 1829 | size_t audioSamplesShortage() //returns value > 0 if the soundcard is consuming more samples than you're producing 1830 | { 1831 | if(audio_data.size() < audio_min_samples) return audio_min_samples - audio_data.size(); 1832 | else return 0; 1833 | } 1834 | 1835 | size_t audioSamplesOverflow() //returns value > 0 if you're producing more samples than the soundard is consuming - so take it easy a bit 1836 | { 1837 | if(audio_data.size() > audio_max_samples) return audio_data.size() - audio_max_samples; 1838 | else return 0; 1839 | } 1840 | 1841 | void audioCallback(void* /*userdata*/, Uint8* stream, int len) 1842 | { 1843 | Mutex mutex(audio_lock); 1844 | 1845 | int dataLengthLeft = audio_data.size(); 1846 | 1847 | //only play if we have data left 1848 | if(dataLengthLeft <= 0) return; 1849 | 1850 | int nsamples = len / 2; //always 16-bit, so always 2 bytes per sample, hence the amount of samples being len / 2 1851 | int fill_len = (nsamples < dataLengthLeft ? nsamples : dataLengthLeft); 1852 | 1853 | for(int i = 0; i < nsamples; i++) 1854 | { 1855 | if(i < fill_len) 1856 | { 1857 | int s = int(audio_data[i] * 32768); 1858 | if(s < -32768) s = -32768; 1859 | if(s > 32767) s = 32767; 1860 | 1861 | stream[i * 2 + 0] = Uint8(s % 256); 1862 | stream[i * 2 + 1] = Uint8(s / 256); 1863 | } 1864 | else stream[i * 2 + 0] = stream[i * 2 + 1] = 0; 1865 | } 1866 | 1867 | audio_data.erase(audio_data.begin(), audio_data.begin() + fill_len); 1868 | } 1869 | 1870 | int audioOpen(int samplerate, int framesize) //always 16-bit mono sound for now 1871 | { 1872 | //set the audio format 1873 | audiospec_wanted.freq = samplerate; 1874 | audiospec_wanted.format = AUDIO_S16; 1875 | audiospec_wanted.channels = 1; //1 = mono, 2 = stereo 1876 | audiospec_wanted.samples = framesize; 1877 | audiospec_wanted.callback = audioCallback; 1878 | audiospec_wanted.userdata = NULL; 1879 | 1880 | /* 1881 | when using alsa and 44100 samples/second, then the framesize (samples) 1882 | will be 940 instead of 1024. Resampled to 48000Hz, this gives back 1024. 1883 | */ 1884 | 1885 | //open the audio device, forcing the wanted format 1886 | if(SDL_OpenAudio(&audiospec_wanted, &audiospec_obtained) < 0) 1887 | { 1888 | return 1; 1889 | } 1890 | 1891 | SDL_PauseAudio(0); 1892 | 1893 | return 0; 1894 | } 1895 | 1896 | void audioClose() 1897 | { 1898 | SDL_CloseAudio(); 1899 | } 1900 | 1901 | int audioReOpen() //closes and opens again with same parameters 1902 | { 1903 | SDL_PauseAudio(1); 1904 | SDL_CloseAudio(); 1905 | if ( SDL_OpenAudio(&audiospec_wanted, &audiospec_obtained) < 0 ) 1906 | { 1907 | return 1; 1908 | } 1909 | SDL_PauseAudio(0); 1910 | 1911 | return 0; 1912 | } 1913 | 1914 | 1915 | //only works correct for 16 bit audio currently 1916 | void audioPushSamples(const std::vector& samples, size_t pos, size_t end) 1917 | { 1918 | if(audio_mode == 0) return; 1919 | 1920 | Mutex mutex(audio_lock); 1921 | 1922 | if(audio_mode == 1) 1923 | { 1924 | audio_data.insert(audio_data.end(), samples.begin() + pos, samples.begin() + end); 1925 | } 1926 | else if(audio_mode == 2) 1927 | { 1928 | size_t j = audio_data.size(); 1929 | audio_data.resize(j + samples.size()); 1930 | for(size_t i = 0; i < samples.size(); i++) 1931 | { 1932 | audio_data[j + i] = samples[i] * audio_volume; 1933 | } 1934 | } 1935 | } 1936 | 1937 | void audioPlay(const std::vector& samples) 1938 | { 1939 | if(audio_mode == 0) return; 1940 | 1941 | Mutex mutex(audio_lock); 1942 | 1943 | //the *current* time is at the first sample of audio_data, the rest has been played through soundcard already 1944 | 1945 | if(samples.size() > audio_data.size()) audio_data.resize(samples.size(), 0.0); 1946 | 1947 | if(audio_mode == 1) for(size_t i = 0; i < samples.size(); i++) audio_data[i] += samples[i]; 1948 | else if(audio_mode == 2) for(size_t i = 0; i < samples.size(); i++) audio_data[i] += samples[i] * audio_volume; 1949 | } 1950 | 1951 | } 1952 | -------------------------------------------------------------------------------- /demo/quickcg.h: -------------------------------------------------------------------------------- 1 | /* 2 | QuickCG 20191227 3 | 4 | Copyright (c) 2004-2019, Lode Vandevenne 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 14 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 15 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 16 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 22 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /* 27 | QuickCG is an SDL 1.2 codebase that wraps some of the SDL 1.2 functionality. 28 | It's used by Lode's Computer Graphics Tutorial to work with simple function calls 29 | to demonstrate graphical programs. It may or may not be of industrial strength 30 | for games, though I've actually used it for some. 31 | 32 | QuickCG can handle some things that standard C++ does not but that are useful, such as: 33 | -drawing graphics 34 | -a bitmap font 35 | -simplified saving and loading of files 36 | -reading keyboard and mouse input 37 | -playing sound 38 | -color models 39 | -loading images 40 | 41 | Contact info: 42 | My email address is (puzzle the account and domain together with an @ symbol): 43 | Domain: gmail dot com. 44 | Account: lode dot vandevenne. 45 | */ 46 | 47 | #ifndef _quickcg_h_included 48 | #define _quickcg_h_included 49 | 50 | #include 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include //std::min and std::max 57 | 58 | namespace QuickCG 59 | { 60 | 61 | //////////////////////////////////////////////////////////////////////////////// 62 | //useful templates////////////////////////////////////////////////////////////// 63 | //////////////////////////////////////////////////////////////////////////////// 64 | 65 | //don't know why, but the standard C++ abs sometimes gives cryptic errors? if so use this :D 66 | template 67 | const T template_abs(const T &a) 68 | { 69 | return (a < 0) ? -a : a; 70 | } 71 | 72 | //usage: std::string str = valtostr(25454.91654654f); 73 | template 74 | std::string valtostr(const T& val) 75 | { 76 | std::ostringstream sstream; 77 | sstream << val; 78 | return sstream.str(); 79 | } 80 | 81 | //usage: double val = strtoval("465498.654"); 82 | template 83 | T strtoval(const std::string& s) 84 | { 85 | std::istringstream sstream(s); 86 | T val; 87 | sstream >> val; 88 | return val; 89 | } 90 | 91 | //length is decimal precision of the floating point number 92 | template 93 | std::string valtostr(const T& val, int length, bool fixed = true) 94 | { 95 | std::ostringstream sstream; 96 | if(fixed) sstream << std::fixed; 97 | sstream << std::setprecision(length) << val; 98 | return sstream.str(); 99 | } 100 | 101 | //////////////////////////////////////////////////////////////////////////////// 102 | //COLOR STRUCTS///////////////////////////////////////////////////////////////// 103 | //////////////////////////////////////////////////////////////////////////////// 104 | 105 | struct ColorRGB8bit; 106 | //a color with 3 components: r, g and b 107 | struct ColorRGB 108 | { 109 | int r; 110 | int g; 111 | int b; 112 | 113 | ColorRGB(Uint8 r, Uint8 g, Uint8 b); 114 | ColorRGB(const ColorRGB8bit& color); 115 | ColorRGB(); 116 | }; 117 | 118 | ColorRGB operator+(const ColorRGB& color, const ColorRGB& color2); 119 | ColorRGB operator-(const ColorRGB& color, const ColorRGB& color2); 120 | ColorRGB operator*(const ColorRGB& color, int a); 121 | ColorRGB operator*(int a, const ColorRGB& color); 122 | ColorRGB operator/(const ColorRGB& color, int a); 123 | bool operator==(const ColorRGB& color, const ColorRGB& color2); 124 | bool operator!=(const ColorRGB& color, const ColorRGB& color2); 125 | 126 | static const ColorRGB RGB_Black ( 0, 0, 0); 127 | static const ColorRGB RGB_Red (255, 0, 0); 128 | static const ColorRGB RGB_Green ( 0, 255, 0); 129 | static const ColorRGB RGB_Blue ( 0, 0, 255); 130 | static const ColorRGB RGB_Cyan ( 0, 255, 255); 131 | static const ColorRGB RGB_Magenta (255, 0, 255); 132 | static const ColorRGB RGB_Yellow (255, 255, 0); 133 | static const ColorRGB RGB_White (255, 255, 255); 134 | static const ColorRGB RGB_Gray (128, 128, 128); 135 | static const ColorRGB RGB_Grey (192, 192, 192); 136 | static const ColorRGB RGB_Maroon (128, 0, 0); 137 | static const ColorRGB RGB_Darkgreen( 0, 128, 0); 138 | static const ColorRGB RGB_Navy ( 0, 0, 128); 139 | static const ColorRGB RGB_Teal ( 0, 128, 128); 140 | static const ColorRGB RGB_Purple (128, 0, 128); 141 | static const ColorRGB RGB_Olive (128, 128, 0); 142 | 143 | //a color with 3 components: r, g and b 144 | struct ColorRGB8bit 145 | { 146 | Uint8 r; 147 | Uint8 g; 148 | Uint8 b; 149 | 150 | ColorRGB8bit(Uint8 r, Uint8 g, Uint8 b); 151 | ColorRGB8bit(const ColorRGB& color); 152 | ColorRGB8bit(); 153 | }; 154 | 155 | //a color with 3 components: h, s and l 156 | struct ColorHSL 157 | { 158 | int h; 159 | int s; 160 | int l; 161 | 162 | ColorHSL(Uint8 h, Uint8 s, Uint8 l); 163 | ColorHSL(); 164 | }; 165 | 166 | //a color with 3 components: h, s and v 167 | struct ColorHSV 168 | { 169 | int h; 170 | int s; 171 | int v; 172 | 173 | ColorHSV(Uint8 h, Uint8 s, Uint8 v); 174 | ColorHSV(); 175 | }; 176 | 177 | //////////////////////////////////////////////////////////////////////////////// 178 | //GLOBAL VARIABLES////////////////////////////////////////////////////////////// 179 | //////////////////////////////////////////////////////////////////////////////// 180 | 181 | extern int w; 182 | extern int h; 183 | 184 | //////////////////////////////////////////////////////////////////////////////// 185 | //KEYBOARD FUNCTIONS//////////////////////////////////////////////////////////// 186 | //////////////////////////////////////////////////////////////////////////////// 187 | 188 | bool keyDown(int key); //this checks if the key is held down, returns true all the time until the key is up 189 | bool keyPressed(int key); //this checks if the key is *just* pressed, returns true only once until the key is up again 190 | 191 | //////////////////////////////////////////////////////////////////////////////// 192 | //BASIC SCREEN FUNCTIONS//////////////////////////////////////////////////////// 193 | //////////////////////////////////////////////////////////////////////////////// 194 | 195 | void screen(int width = 640, int height = 400, bool fullscreen = 0, const std::string& text = " "); 196 | void lock(); 197 | void unlock(); 198 | void redraw(); 199 | void cls(const ColorRGB& color = RGB_Black); 200 | void pset(int x, int y, const ColorRGB& color); 201 | ColorRGB pget(int x, int y); 202 | void drawBuffer(Uint32* buffer); 203 | bool onScreen(int x, int y); 204 | 205 | //////////////////////////////////////////////////////////////////////////////// 206 | //NON GRAPHICAL FUNCTIONS/////////////////////////////////////////////////////// 207 | //////////////////////////////////////////////////////////////////////////////// 208 | 209 | void sleep(); 210 | void sleep(double seconds); 211 | void waitFrame(double oldTime, double frameDuration); //in seconds 212 | bool done(bool quit_if_esc = true, bool delay = true); 213 | void end(); 214 | void readKeys(); 215 | void getMouseState(int& mouseX, int& mouseY); 216 | void getMouseState(int& mouseX, int& mouseY, bool& LMB, bool& RMB); 217 | unsigned long getTicks(); //ticks in milliseconds 218 | inline double getTime() { return getTicks() / 1000.0; } //time in seconds 219 | 220 | //////////////////////////////////////////////////////////////////////////////// 221 | //2D SHAPES///////////////////////////////////////////////////////////////////// 222 | //////////////////////////////////////////////////////////////////////////////// 223 | 224 | bool horLine(int y, int x1, int x2, const ColorRGB& color); 225 | bool verLine(int x, int y1, int y2, const ColorRGB& color); 226 | bool drawLine(int x1, int y1, int x2, int y2, const ColorRGB& color); 227 | bool drawCircle(int xc, int yc, int radius, const ColorRGB& color); 228 | bool drawDisk(int xc, int yc, int radius, const ColorRGB& color); 229 | bool drawRect(int x1, int y1, int x2, int y2, const ColorRGB& color); 230 | bool clipLine(int x1,int y1,int x2, int y2, int & x3, int & y3, int & x4, int & y4); 231 | 232 | //////////////////////////////////////////////////////////////////////////////// 233 | //COLOR CONVERSIONS///////////////////////////////////////////////////////////// 234 | //////////////////////////////////////////////////////////////////////////////// 235 | ColorHSL RGBtoHSL(const ColorRGB& colorRGB); 236 | ColorRGB HSLtoRGB(const ColorHSL& colorHSL); 237 | ColorHSV RGBtoHSV(const ColorRGB& colorRGB); 238 | ColorRGB HSVtoRGB(const ColorHSV& colorHSV); 239 | Uint32 RGBtoINT(const ColorRGB& colorRGB); 240 | ColorRGB INTtoRGB(Uint32 colorINT); 241 | 242 | //////////////////////////////////////////////////////////////////////////////// 243 | //FILE FUNCTIONS//////////////////////////////////////////////////////////////// 244 | //////////////////////////////////////////////////////////////////////////////// 245 | 246 | void loadFile(std::vector& buffer, const std::string& filename); 247 | void saveFile(const std::vector& buffer, const std::string& filename); 248 | 249 | //////////////////////////////////////////////////////////////////////////////// 250 | //IMAGE FUNCTIONS/////////////////////////////////////////////////////////////// 251 | //////////////////////////////////////////////////////////////////////////////// 252 | 253 | int loadImage(std::vector& out, unsigned long& w, unsigned long& h, const std::string& filename); 254 | int loadImage(std::vector& out, unsigned long& w, unsigned long& h, const std::string& filename); 255 | int decodePNG(std::vector& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32 = true); 256 | int decodePNG(std::vector& out_image_32bit, unsigned long& image_width, unsigned long& image_height, const std::vector& in_png); 257 | 258 | //////////////////////////////////////////////////////////////////////////////// 259 | //TEXT FUNCTIONS//////////////////////////////////////////////////////////////// 260 | //////////////////////////////////////////////////////////////////////////////// 261 | extern bool font[256][8][8]; 262 | void drawLetter(unsigned char n, int x, int y, const ColorRGB& color = RGB_White, bool bg = 0, const ColorRGB& color2 = RGB_Black); 263 | int printString(const std::string& text, int x = 0, int y = 0, const ColorRGB& color = RGB_White, bool bg = 0, const ColorRGB& color2 = RGB_Black, int forceLength = 0); 264 | 265 | //print something (string, int, float, ...) 266 | template 267 | int print(const T& val, int x = 0, int y = 0, const ColorRGB& color = RGB_White, bool bg = 0, const ColorRGB& color2 = RGB_Black, int forceLength = 0) 268 | { 269 | std::string text = valtostr(val); 270 | return printString(text, x, y, color, bg, color2, forceLength); 271 | } 272 | 273 | //print some floating point number, this one allows printing floating point numbers with limited length 274 | template 275 | int fprint(const T& val, int length, int x = 0, int y = 0, const ColorRGB& color = RGB_White, bool bg = 0, const ColorRGB& color2 = RGB_Black, int forceLength = 0) 276 | { 277 | std::string text = valtostr(val, length, true); 278 | return printString(text, x, y, color, bg, color2, forceLength); 279 | } 280 | 281 | //////////////////////////////////////////////////////////////////////////////// 282 | //TEXT INPUT FUNCTIONS////////////////////////////////////////////////////////// 283 | //////////////////////////////////////////////////////////////////////////////// 284 | Uint8 getInputCharacter(); 285 | void getInputString(std::string& text, const std::string& message = "", bool clear = false, int x = 0, int y = 0, const ColorRGB& color = RGB_White, bool bg = 0, const ColorRGB& color2 = RGB_Black); 286 | 287 | template 288 | T getInput(const std::string& message = "", bool clear = false, int x = 0, int y = 0, const ColorRGB& color = RGB_White, bool bg = 0, const ColorRGB& color2 = RGB_Black) 289 | { 290 | std::string text; 291 | getInputString(text, message, clear, x, y, color, bg, color2); 292 | return strtoval(text); 293 | } 294 | 295 | //////////////////////////////////////////////////////////////////////////////// 296 | //SOUNDCARD FUNCTIONS/////////////////////////////////////////////////////////// 297 | //////////////////////////////////////////////////////////////////////////////// 298 | 299 | int audioOpen(int samplerate, int framesize); //always 16-bit mono sound for now; returns 0 if no error happened 300 | void audioClose(); 301 | int audioReOpen(); //closes and opens again with same parameters 302 | 303 | /* 304 | push samples to the soundcard, making sure not to cause shortage or overflow 305 | pos and end are the range in the samples vector that you want to push to the audio card 306 | */ 307 | void audioPushSamples(const std::vector& samples, size_t pos, size_t end); 308 | 309 | size_t audioSamplesShortage(); //returns value > 0 if the soundcard is consuming more samples than you're producing 310 | size_t audioSamplesOverflow(); //returns value > 0 if you're producing more samples than the soundard is consuming - so take it easy a bit 311 | void audioSetBufferSamplesRange(size_t min_samples, size_t max_samples); //set shortage and overflow values. E.g. 4096 and 8192. 312 | 313 | /* 314 | This plays the sound starting at this time, until it's done 315 | The difference with audioPushSamples is: 316 | audioPlay allows playing multiple sounds at the same time: it doesn't push at the end, 317 | but elementwise-adds or pushes back samples if needed. 318 | The duration depends on samplerate, make sure the samples in the vector have the correct samplerate. 319 | */ 320 | void audioPlay(const std::vector& samples); 321 | 322 | void audioSetMode(int mode); //0: silent, 1: full (no volume calculations ==> faster), 2: volume-controlled (= default value) 323 | void audioSetVolume(double volume); //multiplier used if mode is 2 (volume-controlled). Default value is 1.0. 324 | 325 | } //end of namespace QuickCG 326 | 327 | #endif 328 | 329 | 330 | -------------------------------------------------------------------------------- /demo/raycaster.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2004-2019, Lode Vandevenne 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 12 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 13 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 14 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 15 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "Fixed.h" 31 | 32 | #include "quickcg.h" 33 | using namespace QuickCG; 34 | 35 | /* 36 | g++ *.cpp -lSDL -O3 -W -Wall -ansi -pedantic 37 | g++ *.cpp -lSDL 38 | */ 39 | 40 | //place the example code below here: 41 | 42 | #define screenWidth 40 43 | #define screenHeight 24 44 | #define mapWidth 16 45 | #define mapHeight 16 46 | 47 | int worldMap[mapWidth][mapHeight]= 48 | { 49 | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 50 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 51 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 52 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 53 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 54 | {1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1}, 55 | {1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1}, 56 | {1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1}, 57 | {1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1}, 58 | {1,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1}, 59 | {1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1}, 60 | {1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1}, 61 | {1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1}, 62 | {1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1}, 63 | {1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1}, 64 | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 65 | }; 66 | 67 | typedef numeric::Fixed<12,4> number_t; 68 | 69 | number_t abs(number_t n) 70 | { 71 | return (number_t) abs(n.to_double()); 72 | } 73 | 74 | int main(int /*argc*/, char */*argv*/[]) 75 | { 76 | number_t posX = number_t::from_base(0x9c); 77 | number_t posY = number_t::from_base(0xa2); 78 | uint8_t dir = 0x2e; 79 | 80 | double time = 0; //time of current frame 81 | double oldTime = 0; //time of previous frame 82 | 83 | auto torad = [](uint8_t bgrad) { return (double)bgrad * (M_PI*2/256); }; 84 | 85 | int8_t sincos_table[256+64]; 86 | for (int i=0; i<256+64; i++) 87 | sincos_table[i] = 127.0*sin(torad(i)); 88 | 89 | int8_t inv_sincos_table[256*64]; 90 | for (int i=0; i<256+64; i++) 91 | inv_sincos_table[i] = 16.0 * std::clamp(1.0 / sin(torad(i)), -7.9, 7.9); 92 | 93 | auto tsin = [&](uint8_t t) { return sincos_table[t] / 128.0; }; 94 | auto tcos = [&](uint8_t t) { return sincos_table[t+0x40] / 128.0; }; 95 | auto tinvsin = [&](uint8_t t) { return (number_t)(inv_sincos_table[t] / 16.0); }; 96 | auto tinvcos = [&](uint8_t t) { return (number_t)(inv_sincos_table[t+0x40] / 16.0); }; 97 | 98 | int8_t deltadistx_table[256]; 99 | int8_t deltadisty_table[256]; 100 | for (int i=0; i<256; i++) 101 | { 102 | double deltaDistX = abs(1 / sin(torad(i))); 103 | deltadistx_table[i] = std::clamp(deltaDistX, -7.9, 7.9) * 16.0; 104 | double deltaDistY = abs(1 / cos(torad(i))); 105 | deltadisty_table[i] = std::clamp(deltaDistY, -7.9, 7.9) * 16.0; 106 | } 107 | 108 | screen(screenWidth, screenHeight, 0, "Raycaster"); 109 | while(!done()) 110 | { 111 | usleep(10*1000); 112 | 113 | number_t dirX = tsin(dir); 114 | number_t dirY = tcos(dir); 115 | 116 | for(int x = w-1; x >= 0; x--) 117 | { 118 | //calculate ray position and direction 119 | uint8_t q = dir - 20 + x; 120 | number_t rayDirX = tsin(q); 121 | number_t rayDirY = tcos(q); 122 | //which box of the map we're in 123 | int mapX = int(posX); 124 | int mapY = int(posY); 125 | 126 | //length of ray from current position to next x or y-side 127 | number_t sideDistX; 128 | number_t sideDistY; 129 | 130 | //length of ray from one x or y-side to next x or y-side 131 | number_t deltaDistX = deltadistx_table[q] / 16.0; 132 | number_t deltaDistY = deltadisty_table[q] / 16.0; 133 | number_t perpWallDist; 134 | 135 | //what direction to step in x or y-direction (either +1 or -1) 136 | int stepX; 137 | int stepY; 138 | 139 | int hit = 0; //was there a wall hit? 140 | int side; //was a NS or a EW wall hit? 141 | //calculate step and initial sideDist 142 | if(rayDirX < 0) 143 | { 144 | stepX = -1; 145 | sideDistX = (posX - mapX) * deltaDistX; 146 | } 147 | else 148 | { 149 | stepX = 1; 150 | sideDistX = (1.0 - (posX - mapX)) * deltaDistX; 151 | } 152 | if(rayDirY < 0) 153 | { 154 | stepY = -1; 155 | sideDistY = (posY - mapY) * deltaDistY; 156 | } 157 | else 158 | { 159 | stepY = 1; 160 | sideDistY = (1.0 - (posY - mapY)) * deltaDistY; 161 | } 162 | //perform DDA 163 | 164 | while (hit == 0) 165 | { 166 | //jump to next map square, OR in x-direction, OR in y-direction 167 | if(sideDistX <= sideDistY) 168 | { 169 | sideDistX += deltaDistX; 170 | mapX += stepX; 171 | side = 0; 172 | } 173 | else 174 | { 175 | sideDistY += deltaDistY; 176 | mapY += stepY; 177 | side = 1; 178 | } 179 | 180 | //Check if ray has hit a wall 181 | if (worldMap[mapX][mapY]) 182 | hit = 1; 183 | } 184 | //Calculate distance projected on camera direction (Euclidean distance will give fisheye effect!) 185 | number_t sum; 186 | if (side == 0) 187 | { 188 | sum = ((1 - stepX)/2.0 - (posX - mapX)); 189 | perpWallDist = sum * tinvsin(q); 190 | } 191 | else 192 | { 193 | sum = ((1 - stepY)/2.0 - (posY - mapY)); 194 | perpWallDist = sum * tinvcos(q); 195 | } 196 | 197 | //Calculate height of line to draw on screen 198 | int lineHeight = (perpWallDist > 0.01) ? (int)((number_t)h / perpWallDist) : 1; 199 | 200 | //calculate lowest and highest pixel to fill in current stripe 201 | int drawStart = -lineHeight / 2 + h / 2; 202 | if(drawStart < 0)drawStart = 0; 203 | int drawEnd = lineHeight / 2 + h / 2; 204 | if(drawEnd >= h)drawEnd = h - 1; 205 | 206 | //choose wall color 207 | ColorRGB color; 208 | switch(worldMap[mapX][mapY]) 209 | { 210 | case 1: color = RGB_Red; break; //red 211 | case 2: color = RGB_Green; break; //green 212 | case 3: color = RGB_Blue; break; //blue 213 | case 4: color = RGB_White; break; //white 214 | default: color = RGB_Yellow; break; //yellow 215 | } 216 | 217 | //give x and y sides different brightness 218 | if(side == 1) {color = color / 2;} 219 | 220 | //draw the pixels of the stripe as a vertical line 221 | if (drawEnd > drawStart) 222 | { 223 | verLine(x, drawStart, drawEnd, color); 224 | } 225 | } 226 | //timing for input and FPS counter 227 | oldTime = time; 228 | time = getTicks(); 229 | number_t frameTime = (time - oldTime) / 1000.0; //frameTime is the time this frame has taken, in seconds 230 | redraw(); 231 | cls(); 232 | 233 | //speed modifiers 234 | number_t moveSpeed = frameTime * 5.0; //the constant value is in squares/second 235 | number_t rotSpeed = frameTime * 3.0; //the constant value is in radians/second 236 | moveSpeed = 0.2; 237 | rotSpeed = 1.0; 238 | readKeys(); 239 | //move forward if no wall in front of you 240 | if(keyDown(SDLK_UP)) 241 | { 242 | if(worldMap[int(posX + dirX * moveSpeed)][int(posY)] == false) posX += dirX * moveSpeed; 243 | if(worldMap[int(posX)][int(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed; 244 | } 245 | //move backwards if no wall behind you 246 | if(keyDown(SDLK_DOWN)) 247 | { 248 | if(worldMap[int(posX - dirX * moveSpeed)][int(posY)] == false) posX -= dirX * moveSpeed; 249 | if(worldMap[int(posX)][int(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed; 250 | } 251 | //rotate to the right 252 | if(keyDown(SDLK_RIGHT)) 253 | { 254 | dir++; 255 | } 256 | //rotate to the left 257 | if(keyDown(SDLK_LEFT)) 258 | { 259 | dir--; 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /polf.asm: -------------------------------------------------------------------------------- 1 | ; POLF: the game of balls and holes for the Commodore PET 2 | ; © 2021 David Given 3 | ; This program is distributable under the terms of the MIT license. See the 4 | ; LICENSE file in the distribution directory for the full text. 5 | 6 | .cpu "6502" 7 | 8 | PIA1 = $e810 9 | PIA1_PA = PIA1 + 0 10 | PIA1_PB = PIA1 + 2 11 | 12 | PIA2 = $e820 13 | PIA2_PA = PIA2 + 0 14 | PIA2_PB = PIA2 + 2 15 | 16 | VIA = $e840 17 | VIA_PB = VIA + 0 18 | VIA_PCR = VIA + 12 19 | 20 | CRTC = $e880 21 | CRTC_ADDR = CRTC+0 22 | CRTC_STATUS = CRTC+1 23 | CRTC_DATA = CRTC+1 24 | 25 | FACTOR_BITS = 5 26 | FACTOR = 1.0<draw_address 1676 | sta ptr+1 1677 | lda #<(map_table-1) 1678 | sta mptr+0 1679 | lda #>(map_table-1) 1680 | sta mptr+1 1681 | 1682 | ldx #MAP_ROW_SIZE 1683 | stx row 1684 | outer_loop: 1685 | ldy #MAP_ROW_SIZE 1686 | inner_loop: 1687 | lda (mptr), y 1688 | tax 1689 | lda map_char_table, x 1690 | sta (ptr), y 1691 | dey 1692 | bne inner_loop 1693 | 1694 | clc 1695 | lda ptr+0 1696 | adc #40 1697 | sta ptr+0 1698 | bcc + 1699 | inc ptr+1 1700 | + 1701 | 1702 | clc 1703 | lda mptr+0 1704 | adc #MAP_ROW_SIZE 1705 | sta mptr+0 1706 | bcc + 1707 | inc mptr+1 1708 | + 1709 | 1710 | dec row 1711 | bne outer_loop 1712 | rts 1713 | 1714 | map_char_table: 1715 | .byte 32 ; space 1716 | .byte 102 ; $5f ; fill 1717 | 1718 | .section zp 1719 | row: .byte ? 1720 | mptr: .word ? 1721 | .send 1722 | .bend 1723 | 1724 | draw_level_banner: 1725 | .block 1726 | title_address = backbuffer + (2*40) + 20 - (world_string_size/2) 1727 | level_address = title_address + (1*40) + world_string_size - 3 1728 | 1729 | ldx #world_string_size-1 1730 | - 1731 | lda #128+32 1732 | sta title_address+0*40, x 1733 | sta title_address+2*40, x 1734 | lda world_string, x 1735 | sta title_address+1*40, x 1736 | dex 1737 | bpl - 1738 | 1739 | lda #level_address 1742 | sta ptr+1 1743 | lda level 1744 | jsr bintobcd 1745 | ldy #0 1746 | jsr drawbyte 1747 | rts 1748 | .bend 1749 | 1750 | ; Waits for A, D or SPACE, and adjusts the level number based on A. 1751 | ; Returns with the carry clear if SPACE was pressed. 1752 | 1753 | select_level: 1754 | .block 1755 | - 1756 | lda a_row 1757 | sta PIA1_PA 1758 | lda PIA1_PB 1759 | and a_col 1760 | beq change_level_down 1761 | 1762 | lda d_row 1763 | sta PIA1_PA 1764 | lda PIA1_PB 1765 | and d_col 1766 | beq change_level_up 1767 | 1768 | lda space_row 1769 | sta PIA1_PA 1770 | lda PIA1_PB 1771 | and space_col 1772 | bne - 1773 | 1774 | jsr wait_for_release 1775 | clc 1776 | rts 1777 | 1778 | change_level_up: 1779 | jsr next_level 1780 | jsr wait_for_release 1781 | sec 1782 | rts 1783 | 1784 | change_level_down: 1785 | jsr prev_level 1786 | jsr wait_for_release 1787 | sec 1788 | rts 1789 | .bend 1790 | 1791 | prev_level: 1792 | dec level 1793 | bpl + 1794 | lda #99 1795 | sta level 1796 | + 1797 | rts 1798 | 1799 | next_level: 1800 | lda level 1801 | clc 1802 | adc #1 1803 | cmp #100 1804 | bne + 1805 | lda #0 1806 | + 1807 | sta level 1808 | rts 1809 | 1810 | ; --- Maths ----------------------------------------------------------------- 1811 | 1812 | ; Fast multiplication routines from 1813 | ; https://codebase64.org/doku.php?id=base:seriously_fast_multiplication. 1814 | 1815 | ; Computes AY = A*X, unsigned; preserves X. Note that the result is 16 bits 1816 | ; wide. 1817 | mul_8x8_16: 1818 | sta sm1+1 1819 | sta sm3+1 1820 | eor #$ff 1821 | sta sm2+1 1822 | sta sm4+1 1823 | ; falls through 1824 | ; Computes AX = A * lhs of previous multiply. 1825 | mul_8x8_again: 1826 | sec 1827 | sm1 lda square1_lo, x 1828 | sm2 sbc square2_lo, x 1829 | tay 1830 | sm3 lda square1_hi, x 1831 | sm4 sbc square2_hi, x 1832 | rts 1833 | 1834 | ; Computes AXY = AX*Y, unsigned. 1835 | ; P1 P0 + 1836 | ; Q0 1837 | ; ----- 1838 | ; P0*Q0 + 1839 | ; P1*Q0 + 1840 | mul_8x16_24: 1841 | .block 1842 | sta p+1 1843 | stx p+0 1844 | 1845 | tya 1846 | ldx p+0 1847 | jsr mul_8x8_16 1848 | sta r+1 1849 | sty r+0 1850 | 1851 | ldx p+1 1852 | jsr mul_8x8_again 1853 | sta r+2 1854 | tya 1855 | clc 1856 | adc r+1 1857 | tax 1858 | lda r+2 1859 | adc #0 1860 | ldy r+0 1861 | rts 1862 | 1863 | .section zp 1864 | p: .word ? 1865 | r: .fill 3, ? 1866 | .send 1867 | .bend 1868 | 1869 | ; Computes AY = A, unsigned. 1870 | square: 1871 | .block 1872 | tax ; set flags 1873 | bpl + 1874 | eor #$ff ; approximate abs 1875 | + 1876 | tax 1877 | jmp mul_8x8_16 1878 | .bend 1879 | 1880 | ; Computes A = A*X unsigned, where all numbers are unsigned fixeds. 1881 | mul_8x8_8f: 1882 | .block 1883 | jsr mul_8x8_16 1884 | sta hi 1885 | tya 1886 | .rept FACTOR_BITS 1887 | lsr hi 1888 | ror 1889 | .next 1890 | rts 1891 | 1892 | .section zp 1893 | hi: .byte ? 1894 | .send 1895 | .bend 1896 | 1897 | ; Computes A = A*X signed, where all numbers are signed fixeds. 1898 | mul_8x8_8fs: 1899 | .block 1900 | sta lhs 1901 | stx rhs 1902 | jsr mul_8x8_16 1903 | sta hi 1904 | sty lo 1905 | 1906 | bit lhs 1907 | bpl + 1908 | sec 1909 | lda hi 1910 | sbc rhs 1911 | sta hi 1912 | + 1913 | 1914 | bit rhs 1915 | bpl + 1916 | sec 1917 | lda hi 1918 | sbc lhs 1919 | sta hi 1920 | + 1921 | 1922 | lda lo 1923 | .rept FACTOR_BITS 1924 | lsr hi 1925 | ror 1926 | .next 1927 | rts 1928 | 1929 | .section zp 1930 | lhs: .byte ? 1931 | rhs: .byte ? 1932 | lo: .byte ? 1933 | hi: .byte ? 1934 | .send 1935 | .bend 1936 | 1937 | ; 8x8 unsigned divide. 1938 | ; Computes A = A/X. 1939 | 1940 | div_8x8_8: 1941 | .block 1942 | lda #$00 1943 | ldx #$07 1944 | clc 1945 | - 1946 | rol num 1947 | rol 1948 | cmp denom 1949 | bcc + 1950 | sbc denom 1951 | + 1952 | dex 1953 | bpl - 1954 | lda num 1955 | rol 1956 | rts 1957 | 1958 | .section zp 1959 | num: .byte ? 1960 | denom: .byte ? 1961 | .send 1962 | .bend 1963 | 1964 | ; 8x8 signed divide. 1965 | ; Computs A = A/X. 1966 | 1967 | div_8x8_8s: 1968 | .block 1969 | sta lhs 1970 | stx rhs 1971 | eor rhs ; discover sign of result 1972 | 1973 | bit lhs 1974 | bpl + 1975 | sec 1976 | lda #0 1977 | sbc lhs 1978 | sta lhs 1979 | + 1980 | 1981 | bit rhs 1982 | bpl + 1983 | sec 1984 | lda #0 1985 | sbc rhs 1986 | sta rhs 1987 | + 1988 | 1989 | lda lhs 1990 | ldx rhs 1991 | jsr div_8x8_8 1992 | 1993 | bit rsign 1994 | bpl + 1995 | sta rsign 1996 | sec 1997 | lda #0 1998 | sbc rsign 1999 | + 2000 | rts 2001 | 2002 | .section zp 2003 | lhs: .byte ? 2004 | rhs: .byte ? 2005 | rsign: .byte ? 2006 | .send 2007 | .bend 2008 | ; Super-fast arctan2 code without division (!), taken from here: 2009 | ; https://codebase64.org/doku.php?id=base:8bit_atan2_8-bit_angle 2010 | ; 2011 | ; Computes A = arctan2(Y-player_y, A-player_x) 2012 | arctan2: 2013 | .block 2014 | sec 2015 | tax 2016 | lda player_x 2017 | sbc identity_table, x 2018 | sta x1 2019 | bcs + 2020 | eor #$ff 2021 | + 2022 | tax 2023 | rol octant 2024 | 2025 | sec 2026 | lda player_y 2027 | sbc identity_table, y 2028 | sta y1 2029 | bcs + 2030 | eor #$ff 2031 | + 2032 | tay 2033 | rol octant 2034 | 2035 | sec 2036 | lda log2_table, x 2037 | sbc log2_table, y 2038 | bcc + 2039 | eor #$ff 2040 | + 2041 | tax 2042 | 2043 | lda octant 2044 | rol 2045 | and #%111 2046 | tay 2047 | 2048 | lda atan_table, x 2049 | eor octant_adjust_table, y 2050 | rts 2051 | 2052 | octant_adjust_table: 2053 | .byte %00111111 ; x+,y+,|x|>|y| 2054 | .byte %00000000 ; x+,y+,|x|<|y| 2055 | .byte %11000000 ; x+,y-,|x|>|y| 2056 | .byte %11111111 ; x+,y-,|x|<|y| 2057 | .byte %01000000 ; x-,y+,|x|>|y| 2058 | .byte %01111111 ; x-,y+,|x|<|y| 2059 | .byte %10111111 ; x-,y-,|x|>|y| 2060 | .byte %10000000 ; x-,y-,|x|<|y| 2061 | 2062 | .section zp 2063 | octant: .byte ? 2064 | .send 2065 | .bend 2066 | 2067 | ; Medium fast 16-bit square root, taken from here: 2068 | ; https://codebase64.org/doku.php?id=base:16bit_and_24bit_sqrt 2069 | ; 2070 | ; Computes A = sqrt(AX). 2071 | 2072 | sqrt16: 2073 | .block 2074 | sta hi 2075 | stx lo 2076 | 2077 | ldy #1 2078 | sty t1 2079 | dey 2080 | sty t2 2081 | again: 2082 | sec 2083 | lda lo 2084 | tax 2085 | sbc t1 2086 | sta lo 2087 | 2088 | lda hi 2089 | sbc t2 2090 | sta hi 2091 | 2092 | bcs continue 2093 | tya 2094 | rts 2095 | 2096 | continue: 2097 | iny 2098 | lda t1 2099 | adc #1 2100 | sta t1 2101 | bcc again 2102 | inc t2 2103 | jmp again 2104 | 2105 | .section zp 2106 | lo: .byte ? 2107 | hi: .byte ? 2108 | t1: .byte ? 2109 | t2: .byte ? 2110 | .send 2111 | .bend 2112 | 2113 | ; Produce a shuffled number: if you call this 256 times, you get all 256 values. 2114 | ; Returns the value in A; preserves X and Y. 2115 | 2116 | shuffled: 2117 | .block 2118 | lda seed 2119 | beq do_eor 2120 | asl 2121 | beq no_eor 2122 | bcc no_eor 2123 | do_eor: 2124 | eor #$1d 2125 | no_eor: 2126 | sta seed 2127 | rts 2128 | .bend 2129 | 2130 | .section zp 2131 | seed: .byte ? 2132 | .send 2133 | 2134 | ; As above, but produces a number less than A. This is not good code. 2135 | 2136 | shuffled_limited: 2137 | .block 2138 | clc 2139 | adc #1 2140 | cmp #1 2141 | beq shuffled 2142 | sta max 2143 | - 2144 | jsr shuffled 2145 | cmp max 2146 | bcs - 2147 | rts 2148 | 2149 | .section zp 2150 | max: .byte ? 2151 | .send 2152 | .bend 2153 | 2154 | ; Slow and horrible routine to convert binary to BCD. 2155 | 2156 | bintobcd: 2157 | tax 2158 | lda #$99 2159 | sed 2160 | - 2161 | clc 2162 | adc #1 2163 | dex 2164 | bpl - 2165 | cld 2166 | rts 2167 | 2168 | hex_table: 2169 | .text "0123456789ABCDEF" 2170 | 2171 | torad .function i 2172 | .endf i * (PI * 2 / 256) 2173 | 2174 | clamp .function n, lo, hi 2175 | .if n < lo 2176 | n := lo 2177 | .endif 2178 | .if n > hi 2179 | n := hi 2180 | .endif 2181 | .endf n 2182 | 2183 | div .function x, y 2184 | .if y == 0 2185 | n := 9999 2186 | .else 2187 | n := x / y 2188 | .endif 2189 | .endf n 2190 | 2191 | nround .function x 2192 | .endf trunc(x + 0.5) 2193 | 2194 | ; backbuffer row addresses. 2195 | 2196 | row_table_lo: 2197 | .for i := 0, i < 25, i += 1 2198 | .byte <(backbuffer + i*40) 2199 | .next 2200 | 2201 | row_table_hi: 2202 | .for i := 0, i < 25, i += 1 2203 | .byte >(backbuffer + i*40) 2204 | .next 2205 | 2206 | ; square multiplication tables. 2207 | 2208 | .align $100 ; required or it won't work 2209 | square1_lo: 2210 | .for i := 0, i < 512, i += 1 2211 | .byte <((i*i)/4) 2212 | .next 2213 | 2214 | square1_hi: 2215 | .for i := 0, i < 512, i += 1 2216 | .byte >((i*i)/4) 2217 | .next 2218 | 2219 | square2_lo: 2220 | .for i := 0, i < 512, i += 1 2221 | .byte <(((i-255)*(i-255))/4) 2222 | .next 2223 | 2224 | square2_hi: 2225 | .for i := 0, i < 512, i += 1 2226 | .byte >(((i-255)*(i-255))/4) 2227 | .next 2228 | 2229 | identity_table: 2230 | .for i := 0, i < 256, i += 1 2231 | .byte i 2232 | .next 2233 | 2234 | height_table: 2235 | .for i := 0, i < 256, i += 1 2236 | f := i / FACTOR 2237 | h := $ff 2238 | .if i != 0 2239 | h := 7.9 * 1.0/f 2240 | .endif 2241 | .if h < 1 2242 | h := 1 2243 | .endif 2244 | .byte h 2245 | .next 2246 | 2247 | inverse_table: 2248 | .byte $ff 2249 | .for i := 1, i < 256, i += 1 2250 | f := i / FACTOR 2251 | .byte clamp(nround(FACTOR / f), 0, 255.0) 2252 | .next 2253 | 2254 | atan_table: 2255 | ; This is supposed to be atan(2^(x/32))*128/pi, but I can't make that 2256 | ; reproduce the numbers here, so it's hard-coded. 2257 | 2258 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2259 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2260 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2261 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2262 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2263 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2264 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2265 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2266 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2267 | .byte $00,$00,$00,$00,$00,$00,$00,$00 2268 | .byte $00,$00,$00,$00,$00,$01,$01,$01 2269 | .byte $01,$01,$01,$01,$01,$01,$01,$01 2270 | .byte $01,$01,$01,$01,$01,$01,$01,$01 2271 | .byte $01,$01,$01,$01,$01,$01,$01,$01 2272 | .byte $01,$01,$01,$01,$01,$02,$02,$02 2273 | .byte $02,$02,$02,$02,$02,$02,$02,$02 2274 | .byte $02,$02,$02,$02,$02,$02,$02,$02 2275 | .byte $03,$03,$03,$03,$03,$03,$03,$03 2276 | .byte $03,$03,$03,$03,$03,$04,$04,$04 2277 | .byte $04,$04,$04,$04,$04,$04,$04,$04 2278 | .byte $05,$05,$05,$05,$05,$05,$05,$05 2279 | .byte $06,$06,$06,$06,$06,$06,$06,$06 2280 | .byte $07,$07,$07,$07,$07,$07,$08,$08 2281 | .byte $08,$08,$08,$08,$09,$09,$09,$09 2282 | .byte $09,$0a,$0a,$0a,$0a,$0b,$0b,$0b 2283 | .byte $0b,$0c,$0c,$0c,$0c,$0d,$0d,$0d 2284 | .byte $0d,$0e,$0e,$0e,$0e,$0f,$0f,$0f 2285 | .byte $10,$10,$10,$11,$11,$11,$12,$12 2286 | .byte $12,$13,$13,$13,$14,$14,$15,$15 2287 | .byte $15,$16,$16,$17,$17,$17,$18,$18 2288 | .byte $19,$19,$19,$1a,$1a,$1b,$1b,$1c 2289 | .byte $1c,$1c,$1d,$1d,$1e,$1e,$1f,$1f 2290 | 2291 | log2_table: 2292 | .byte 0 2293 | .for i := 1, i < 256, i += 1 2294 | .byte clamp(nround(log(i)*32.0 / log(2)), 0, 255) 2295 | .next 2296 | 2297 | inv_sincos_table_fn .function i 2298 | .endf clamp(256.0 * div(1.0, abs(sin(torad(i)))), 0, 65535) 2299 | 2300 | inv_sincos_table_lo: 2301 | .for i := 0, i < 256, i += 1 2302 | .byte inv_sincos_table_fn(i) 2308 | .next 2309 | 2310 | compass_table: 2311 | .text 'e ese se sse ' 2312 | .text 's ssw sw wsw ' 2313 | .text 'w wnw nw nnw ' 2314 | .text 'n nne ne nne ' 2315 | 2316 | ball_string: 2317 | .byte 'b' | $80 2318 | .byte 'a' | $80 2319 | .byte 'l' | $80 2320 | .byte 'l' | $80 2321 | .byte ' ' | $80 2322 | 2323 | hole_string: 2324 | .byte ' ' | $80 2325 | .byte 'h' | $80 2326 | .byte 'o' | $80 2327 | .byte 'l' | $80 2328 | .byte 'e' | $80 2329 | 2330 | world_string: 2331 | .byte ' ' | $80 2332 | .byte 'w' | $80 2333 | .byte 'o' | $80 2334 | .byte 'r' | $80 2335 | .byte 'l' | $80 2336 | .byte 'd' | $80 2337 | .byte ' ' | $80 2338 | .byte $80 2339 | .byte $80 2340 | .byte ' ' | $80 2341 | world_string_size = * - world_string 2342 | 2343 | spacebar_string: 2344 | .byte ' ' | $80 2345 | .byte 'p' | $80 2346 | .byte 'R' | $80 2347 | .byte 'E' | $80 2348 | .byte 'S' | $80 2349 | .byte 'S' | $80 2350 | .byte ' ' | $80 2351 | .byte 's' | $80 2352 | .byte 'p' | $80 2353 | .byte 'a' | $80 2354 | .byte 'c' | $80 2355 | .byte 'e' | $80 2356 | .byte ' ' | $80 2357 | .byte 'T' | $80 2358 | .byte 'O' | $80 2359 | .byte ' ' | $80 2360 | .byte 'S' | $80 2361 | .byte 'T' | $80 2362 | .byte 'A' | $80 2363 | .byte 'R' | $80 2364 | .byte 'T' | $80 2365 | .byte ' ' | $80 2366 | spacebar_string_size = * - spacebar_string 2367 | 2368 | scale_table: 2369 | .byte $60, $65, $74, $75, $61, $f6, $ea, $e7, $e0 2370 | 2371 | kbdata_b: 2372 | .byte 8 ; space_row 2373 | .byte 1<<2 ; space_col 2374 | .byte 4 ; w_row 2375 | .byte 1<<1 ; w_col 2376 | .byte 3 ; a_row 2377 | .byte 1<<0 ; a_col 2378 | .byte 2 ; s_row 2379 | .byte 1<<1 ; s_col 2380 | .byte 3 ; d_row 2381 | .byte 1<<1 ; d_col 2382 | .byte 6 ; dot_row 2383 | .byte 1<<3 ; dot_col 2384 | .byte 7 ; comma_row 2385 | .byte 1<<3 ; comma_col 2386 | .if (* - kbdata_b) != kbdata_size 2387 | .error Bad keyboard data 2388 | .endif 2389 | 2390 | kbdata_g: 2391 | .byte 9 ; space_row 2392 | .byte 1<<2 ; space_col 2393 | .byte 3 ; w_row 2394 | .byte 1<<0 ; w_col 2395 | .byte 4 ; a_row 2396 | .byte 1<<0 ; a_col 2397 | .byte 5 ; s_row 2398 | .byte 1<<0 ; s_col 2399 | .byte 4 ; d_row 2400 | .byte 1<<1 ; d_col 2401 | .byte 6 ; dot_row 2402 | .byte 1<<4 ; dot_col 2403 | .byte 7 ; comma_row 2404 | .byte 1<<3 ; comma_col 2405 | .if (* - kbdata_g) != kbdata_size 2406 | .error Bad keyboard data 2407 | .endif 2408 | 2409 | sin_table: 2410 | cos_table = sin_table + 64 2411 | .for i := 0, i < 256+64, i += 1 2412 | .char clamp(nround(FACTOR * sin(torad(i))), -127, 127) 2413 | .next 2414 | 2415 | dirx_table = cos_table 2416 | diry_table = sin_table 2417 | 2418 | deltadisty_table: 2419 | deltadistx_table = deltadisty_table + 64 2420 | .for i := 0, i < 256+64, i += 1 2421 | .byte clamp(nround(FACTOR * abs(div(1, sin(torad(i))))), 0, 255) 2422 | .next 2423 | 2424 | titlescreen_table: 2425 | .include "titlescreen.inc" 2426 | 2427 | .section zp 2428 | zbuffer: 2429 | .fill 40, ? 2430 | .send 2431 | 2432 | .align $100 2433 | backbuffer: 2434 | .fill 1024, ? 2435 | 2436 | * = 0 2437 | .dsection zp 2438 | .cerror * > $ff 2439 | 2440 | ; vim: sw=4 ts=4 et 2441 | 2442 | -------------------------------------------------------------------------------- /titlescreen.inc: -------------------------------------------------------------------------------- 1 | .byte 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 96, 32, 96, 96, 96, 32 2 | .byte 32, 32, 32, 98, 98, 98, 98, 98, 98, 98, 32, 32, 96, 32, 96, 96,112, 64, 64, 80, 15, 18, 20, 1, 2, 12, 5, 64, 64, 64, 64, 64, 64,110, 32, 32, 32, 32, 96, 96 3 | .byte 32, 32, 32,224,224,224,224,224,224,224, 32, 32, 96, 32, 96, 32, 93, 32, 32, 32, 79, 2, 10, 5, 3, 20, 32, 32, 32, 32, 32, 32, 32, 93, 32, 96, 32, 96, 96, 96 4 | .byte 32, 32, 32, 32, 32, 32,224, 32, 32,224, 32, 32, 32, 32, 96, 32, 93, 32, 96, 32, 32, 18, 5, 76, 15, 3, 1, 20, 9, 15, 14, 32, 32, 93, 32, 96, 32, 32, 96, 96 5 | .byte 32, 32, 32, 32, 32, 32,224, 32, 32,224, 32, 32, 32, 32, 96, 32,109, 64, 64, 64, 64, 64, 70, 15, 18, 3, 5, 64, 64, 64, 64, 64, 64,125, 32, 96, 32, 32, 96, 96 6 | .byte 32, 32, 32, 32, 32, 32,224,224,224,224, 32, 32, 32, 32, 32, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 32, 32, 32, 96, 96 7 | .byte 32, 32, 96, 32, 32, 96, 96,226,226, 96, 32, 32, 96, 32, 32, 32, 32, 32, 32,108, 98,123, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 8 | .byte 32, 32, 96, 96, 98, 98, 98, 98, 98, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,225,215, 97, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 9 | .byte 32, 32, 96,160,160,160,160,160,160,160, 32, 32, 32, 32, 96, 32, 32, 32, 32,124,226,126, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 10 | .byte 32, 32, 96,160, 32, 32, 32, 32, 32,160, 32, 32, 32, 96, 96, 32,108, 98,123,108, 98,123,108, 98,123, 32, 32, 32,108, 98,123,108, 98,123, 32, 32, 32, 32, 32, 32 11 | .byte 32, 32, 96,160, 32, 32, 32, 32, 32,160, 32, 32, 32, 96, 32, 32,225,193, 97,225,211, 97,225,196, 97, 32, 96, 32,225,188, 97,225,190, 97, 32, 96, 32, 32, 96, 32 12 | .byte 32, 32, 32,160,160,160,160,160,160,160, 32, 32, 32, 96, 32, 32,124,226,126,124,226,126,124,226,126, 96, 96, 32,124,226,126,124,226,126, 96, 96, 96, 32, 96, 32 13 | .byte 32, 32, 32, 32,226,226,226,226,226, 32, 32, 32, 32, 32, 32, 32, 84, 18, 1, 14, 19, 12, 1, 20, 5, 96, 96, 32, 82, 15, 20, 1, 20, 5, 96, 96, 32, 32, 96, 32 14 | .byte 32, 32, 96, 98, 98, 98, 98, 98, 98, 98, 32, 32, 32, 32, 32, 96, 96, 96, 96, 96, 32, 32,108, 98, 98, 98, 98, 98,123, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 32 15 | .byte 32, 32, 32,160,160,160,160,160,160,160, 32, 32, 32, 32, 96, 96, 96, 96, 96, 96, 32, 32,225,211,208,193,195,197, 97, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 16 | .byte 32, 32, 32,160, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 96, 96, 96, 96, 96, 32, 32,124,226,226,226,226,226,126, 32, 32, 32, 32, 32, 32, 96, 32, 96, 32, 32 17 | .byte 32, 32, 32,160, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 96, 96, 96, 96, 96, 96, 73, 13, 16, 21, 12, 19, 5, 32, 32, 32, 32, 32, 96, 32, 32, 32, 32, 32 18 | .byte 32, 32, 32,160, 32, 32, 32, 32, 32, 32, 32, 32, 96, 32, 96, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 32, 32, 32, 32, 32 19 | .byte 32, 32, 32, 96, 32, 32, 32, 32, 32, 32, 32, 96, 96, 32, 32,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 32, 32, 32, 32, 32 20 | .byte 32, 32, 32,160,160,160,160,160,160,160, 32, 96, 96, 96,103,208,146,133,147,147,160,211,208,193,195,197,160,148,143,160,147,148,129,146,148,101, 96, 96, 96, 96 21 | .byte 32, 32, 32,226,226,226,160,226,226,160, 32, 32, 96, 96, 32, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 96, 96, 96, 96, 96 22 | .byte 32, 32, 32, 96, 96, 96,160, 96, 96,160, 96, 96, 96, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 96, 96, 96, 96, 96, 96, 32, 32, 32, 32, 32, 32, 32, 32, 96, 96 23 | .byte 32, 32, 96, 96, 96, 96,226, 96, 96,160, 96, 96, 96, 96, 40, 67, 41, 32, 50, 48, 50, 49, 32, 68, 1, 22, 9, 4, 32, 71, 9, 22, 5, 14, 32, 32, 32, 32, 96, 96 24 | .byte 32, 32, 32, 32, 32, 32, 32, 32, 32,226, 32, 32, 32, 96, 8, 20, 20, 16, 58, 47, 47, 3, 15, 23, 12, 1, 18, 11, 46, 3, 3, 13, 47, 16, 15, 12, 6, 32, 32, 96 25 | .byte 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 26 | 27 | -------------------------------------------------------------------------------- /titlescreen.pe: -------------------------------------------------------------------------------- 1 | {"app":"PETSCII Editor","version":"3.0","data":"{\"app\":\"PETSCII Editor\",\"urlą\"http://petscii.krissz.hu/Ė\"mĦaąĀnaĹĜNew projectķauthĔNĿeĜķeđērVersionĜ3.0ķfileFĔmatŜŞŠŢĆŤŦėcreůeTiŀ:16Ƃ940896648ėlastSavŽſŕƁƃ7395774581}ėoptųsļ\"paŪğƘ\"colodĔŕŸrtFũtŝĜnšƺ\"ŨŪœƀ\"nŃ-ņňŊŌėsƔeCounǀĕ:5ŸšcůƓƕǗǙǛĜyeƫėŎēǔƕǨǪōŏoǯeIǚŝvƯą10ėkeyboardLayǘŌĆuǫdžiŞtKȂƸwŴ\"drawŧȒƑClickĜțȝķēƵƩpƫĆǩȐcęsĔŬlƶȞȰDzƥƳȣpȄȆdȯĀĨźenĐĒĔą[ĀchȆDůĻ:[[Ǿƍ9ƍ123],[22Ǟ21Ǟ97ɠɘ24,ɣ6,ɝ6]ɠƳƵĔɓtɕɗ1ɮɾɲ4ɫʀʄʂɡʅʁɵ}ɶɐȆsĦɊŚąfƯʏǓņĒeʑɌ:ʔlʏȽʍŞĦȯɎNjŔĜCƋ uăŝǡʏķmƷƲşngŪķbgǗƶǜɱƳɑrʽʜʀĸulƩ˄r1ǽėmˈˊɸr2ą2ėbĒŮȮɍ[6ǿǾ˗1ǾɲˤƉ,ɛ,˟,0ɫɭ˪ˠ0ˢ2ɱˡɲ˲˷˗˭ʇ˯˶˾˽˸˿ɮ˻˞˱˗˧̈˵˸˫̄ɝ˱ɜ́̑ˢƇɲ2ǿ̍˴˨ɱ˧̞̎̚ɳˬʃ̙̉̕ǿ̤˧̄˫˶̜ˤ˾̗̋ʃ̒˹̶̡̊̓̕ɡ˫˯̻ɮ̼˰̸[3ˠ˳̕ͅə,5ɱ̍̑ɜ̖˥˳̧̛̳͕̟̙͓̏̄9˥͚ɝ7˷͞9͚͡˨͚͋ˢ1͍˵̙ˣ̆͒ɫ̷̢̪̲̮̌ɿͱ˯͔̀Ͱ̷Ͳ̿ʀ̘Ͷͼ˽͎͇˶̩ͭ˧˫̵̘̿ɱ̾Ώ̱̽̃ͼͱ̲ʹʇΕΔ˗̺ΒɡͣΟ͜0͛ͤ̀͞͝ͽΜ;̲̳ͦ˶Ω̾΍˪ͅ˯ƌ͗͊ͯǿεκƍλΌ̢ɲμ͍ɮε˩ɤ˺θ͆̈́ɝͅΗ[ǿ˯̪̙ΐɯʆώɲɱεΤΤϙηɡ̗ˬϠϟ̰ɡϓ˯ϟϦΓέ˸Ϣϡϩ˶ɤǞϯ5ϱΫˮɮ6̇ɱΊ̕Ὰ˩˿ϔ,ϙ˸7ϣ̳̅˫͉˷3ЈЌ̩γ̃ϡϭ˼Ёνε΃̀Зɿϊ˗δƍ̺̄ͭϳΪ˺̀ϏϽ̠ΰЦВϧϽƌɫϬ̠ϬРЬϠΰа,ЌͪЕɚϝЇ˾̭ͨϪΛІ̾Њϓвψῄτͪоͻβ2ƍ΋ύ˵ɮ͂έ2͞ϺΧ̣ͪϷβήІѐ̜̀ͳІ̠ЀϥΝпΖͭѢͺѯ̋ΛЈІЭВϦЫЧѸЮ8ʃѽ̞КʁѺ̴ϠвГɘϋИл˽ϋ·ЈАжѬϬϰɯУϢɫƍђ˪˳ΣњɯѓχϤϽѫЁɭиϧУҕҘϞжҪҗЬиҕҫедҔүǞІЖρЂҼλοϋӁМωҨж̼ǿчΑ϶ҦɜǞ͞ҬɢϽӉϏѹѨ9ˢӗɲә1ӛӝНҸʃәɣɿϋчɜɮ͞ɟɡЌΣ̽ƍЊˣН˽ӗˮҸӚӘӸӷӺӜ2ӵɥУЌԁйԃӪϖϑю̠҅ύгҮӿҹɫ5ЛҞԓҜҠ,Пɫ͹Ԛ͕ӅǿΣɥҚӊʇƝɯ31҅Ί̓ΎԦӚ5иԇуԨхШ̲̾ѕβќԹ˪ҙԖϸ̕ћНƍԘңҦ̽ӶΐӡӹνӼӺҿՄҥՈҭǿкͧӯɮԑ̀ҕΤ6ԁԧɧ,өɲԝҵՙƆϔЧըէժɭըգϏՇӠϤҰԌѶդҩԮԣӹՌջӞӻӴϤ0̽ց͈ԧ5ԧ̖ւ՗օ1ɫԂ֎ԄйծɯփֈքքӾҖՆНКӔɿӛђ֍֐֏ԁӋ̾ԬԬՑϖбϱ֬ɲոӑՅйԧԬӐҔКԡՅ֒շҖ֯Ϭ֧ևҤϕϥӶֵѸՇׁׁ֥ԡָ̼ՉվպսռӽϤ̻ו̽זɯטӣɫ͞םՠןמɪղԎնՙհҹҵֺּ׫ϰ֣֡ԃҳϳ֒լիզլʃ֭՟՟г׉Յ׋ұңթ״״׼؁׵թ׺׹֯Ӝ֬Ќɾϱơ֮͜5؎Ԥ׭ϤԬ،֮؎2ؓ؎ؔ؛ּʃؙؠɧ؎ؤ؟اևײԣ؍ؒدɧ؞ԭآʇ3ؐԟةءɾΣضشɬؓـɲؽرذ؝ؾɝفل1ك5ىٍهԤئذْٕؑؖجɘإٚ؜زٕؠؗɘؖ֨٣և٤س٠ɣɥ4Ќɭ٬٫ϔ؎4Σͣ٠ٕٲقؐاطءٵُيپڀىنٽ˵3ͪӧӍ˵Ӱчٕ٘3Ҟ͜ن؏غٟڍّٛڔعؚٵبغؤَ͚ٵڗښژڛ4ث؋ڝڎ՞كٷْڢخيؖ4ٝٗ؋ڃ٦ɞ٥ڻԥڨ׸ڤۀغڶڿڙۅڛ֨ڍڋڊі͍ɜچ֮Ύۉښ٢՞ڣټےۆ۔ڽۛٵڃڴٰۜ΢مڡږɥ̖ۢۨ֓͞۩ۢڛ٘٭ԥ۬ؼևۭƞװۥ۠۰ۺٯ۰ۮ֯ҳؙّڸڼںڽ֘ɞڡۭқɝҚۧƜؾױפܑ׬ձɢڹڹՇۈ؋ڥڕܒקۄْˠؠܢع٨܁؟͜ڳ͜ܘܛظ۹֨۵؎8ԎӡڱّͣɧˢإӢۯ۟ۺܚܔܞۿۼܰ۲۫ۜۼۯ܎׀ٮݍۡ۩ۯУٖרؚگܕ܀ڼوۜ݀٨ק݁ݛ۴עɢݞݚܓܑ܇ݞݘۜݑӿ֛ڡ݄ݡدڰغٔڏؘܴۙݙٴܗ՞ڃ۸گܾۼ۵ڑۤ؋ۖɭڡҟވۂݸӑݯևɤغқދޔڍڒپ֨ށڵލݕٚܫ՞ڥۃ˼زܿ܄ܬܵݷީݳٞؕٮޛܐܙڨ׬ݜݬ޳޲ݪ܅ۨ϶܄ރ݇޹ڧްפݥݒݾ݂ѿ٬ޙޮܯ՞݉ކخޔݰ޶߁ݟֻݧң۩ҟӜ؜ۍיٳ٬ۯݼݠߢںݨפߕݗׄԍݟݝרկ݂݁ߥߔߪ٨ۧߵ݆۪߸߶϶ݎۻ߼ۼ߿ԅݦԀޠӚڡ٤ˮڹފ׶ЗܟݹۛӜֲؓ߫٘՜˪кࠗࠖ࠙ѣ՝ʁؕމ۩߀ɭҚ5דݣ֛ԡސٳ͜ٯԬ՜Ӆࠕ࠰࠘࠱ӅφҖӟ࠷࠶࠶֘ݕڒ࠽ބ٧޵ࠂߦԍˮ0ݽߜ܌Ӛ٬ࠊݐܛڠذځࡐ9߱ҳ֜ߝݠψ΃ܩϱ܂ݯ˰࠻࡝ۆڒޢܖݙژ޹܁ލߐݯ࡬ϔ࡭࡫϶ߘߞ̕ɜ9ࡋࠠࡍࠈަԧϧݼͯ࠘߹ћࢀߺࠎࡨࡻߣԦࡔࠤࡊق۬۴ھҭࡉՌӣ׵ࠣӟׂݩ࢙ޜ؊֮࢝؉֮ࢊࡃׅࡂࠓࡪ࢚ࢥͺ࠲ࢪ࠙࠳ɡֆ֖֕֕ࢯࢯࢱցӵ࠸࠵ࢹࢋࢺ࠷ࢡ׫ࢳ۴։иֳן՟࠭؜ݢࢼ࠹࣊࣌ࠤࡺߣךࡾףࢨ؃ࠋխࢅࠉחՆ߆࣓ܓמࢆ܆ࣝࢧҴ̄ࢤ࢔࣒ࣧࡥߢߧࢦ߯ࡽࡻ࣏ࣩ࣠ࣟ࠱ࢬࢫࢭֳࣺ́ࠒֿࣼ֌ң׿ࠢϔँַः࠴ࣣܕҷ߯ߖݣऋߩхࢻऑࢸऒ҈ࣦݒ࢜ɧ϶؂ࣖࣔߠࢇࣟࠍ֭ٙ࢞खܛण࢝ࣕछ˭]ƥĽʨĆʪ4 ȹʯƐDžʳdʵiʷʹ˘ʼ˓ąˀʢˋǽɮˇˉiˋˍƁˏˑॉ˓˕:˗\"˙t˛ʥѭͧˠ˦̛ѓյǿϻԾήҢϖ̤ɝ΁љғϠ˫͹ԋԸԕΨ।ϟѐ̠Ήյʀϐֱ֛Ҷ॰ΖѴ१и०ѦԱдѹӉЁ८͊९Թд०̝̔΅ϫиইϓ८ѯߙߙۢͣॼ१ԱͽॼॴѮ҄ঝͽঝ̉࠯ΚԕϺӅত˸͹ঠϹ̿џЫ΃Ύׁ҃জΙЈॲ҆঺Ωॼܸ܊Հ͈४Сѻѐহ঩ॢ͐বͫॺϙ͘ψһҽ৔ύӁҼঝτ࢖ѱωঢ়ϋ८ϐԪֱ׍࢑Ϙ͍ԔϜ҄մեৣБѻάটשΘ̷՚˸৵̷ࣥϷ঱ϻঝϨΞ̐̈́О̳Ѕড়ѰӰ0؎̋ЎͯґմοਂνӢ҂ӂґεдѰҕৈ঴ШϒϽय़е̾ЯՒ҆͘؀֪ਡਟикਖнਅ́с਀ѵ৬˯ঔਝяСьਮщ̹ਹқєѣ҃ј˿ћβαѥѠੀਯ঱ভѧѝ΋Ҋষ৾ख़ਲ਼ΨѣѲΛॿ̯҄ӕЁਠ੝ਢѾʇҀ˧ਔ৅҅ৢӐӱЁয়ґ੫ҏыੑҒ৪߲ԳФԉ঺΀ঞঃ̂ੋ̞঎фѝ͑থ̥͌੏੿਼̦͖ͪɬ͖ѥઊઈ੾̬੘ξ৳੻੐ϵ਽৭ֱύ੃ӃӁ̔Њৰএ΄ӂઃ঒Ξ઎પζϾ͜ӜԿ͟ΦΠΧਰͩԉ̭ઙ੔઄੎੺থ͸ફੰ঺ѰͿ͵੻Лધ।ѤϺॠਲ਼αՐਪખ઻Ա৖ૂঢ੓ળুԔˣͥ͢Ϯਜ਼ৡ঄૞੎ϓαҌσ਻৑ՏҤׇֹչՍ৘սՎְ૏૭[ࣀ֓։֖ࢴ֔ࢶࢮޠ٬փ˶ࢯگଁ˲תӐऩ؇ଊऩ׳࢚֘৫ਠ৫ڢאଔ׏ӷࡅૹ૷ࢵ֊֗ӫ֢ଟԅԌଛૺࣿɢଁؠଃ৶ଧЌ଩ࠥׯ֣׽˯ࣽ֩Ԍ࢟؉଱їׅࣻЭऄ૵ଐ߰ਤՓࣻ֩׃ࢢੴݦ׈૪׾ः৭׎גଖ୏Ӽˮך୔ࣚיࣚלנ୚ס߱ଐࢾ਎ୁ׆ࣜɘԃ͍̊ज़঱ॼЭपէ׸ध׻ਧѫ׿ଈजଉ؅ӑ୬״ଶ֬ިٶؐ֬ޯ߬׫ڦܩۓߟۿगىڪإܚ߱ޝࡏ۾܇ޚࢌެࢌஂ՚ۖݚڲ࣭ϔݺ߈ࢇ஗ݒࡶۙߊًଥ՚ࡑ஦ۆܝ܇݀ࠐߢ஑ܾࣝҪஓ࡫ԭ࠻னڮڭܮஂய࣠࣠றऍஃإ܋܋ϙڌஈ߂஋ہ஡࡜ڱޡஜ՚ோقޠـஎணகߚ࡮࡮எ஋ࡏ்ஔپܪ௔ߓ܅ݥߡɭ࢐ூநہٖஇஎܜؕɥ஍࣢֬یࡴ஖ࡆ௥ग௲ࠏއࡁ௼தً۠௉ஃ۞ட݇߃ߎ޾߶ఌٳࡪ݄݆ப۪ɧաݳ߽۹ࠀࡀఅ௦ࡡிࠇࣝ܏ఔ௅܍۩ఢఀऎ఩ࣱ௨ޱه௱झ࢛عܤ҆ࡧञ௡࡛௛ఓ௴ٙ஼ߑఔإِܳܶ࡜ؓܺؕӟ݃ݯఈथܐా఍߸߉ܐ݋ఙ߼݅ߓݓ҆௿௑٦௧܄ߨ఩ߒ޹΢ࢾఊࢥࢾݦާ࣢ݮߋఉُޫإݵܮౘܦݺࠆࢇఊ୽ٟށ଀ԟ࠿౯ߊɣࠠ஁ܽޏיڱޓவޖஊ׀࡭౳ݿޞ௤௙ࡤச౺ోబఋಒ௏௽౿ۂ஥ܻ௑Ҫ޴௳޷నݫܒڹౣң޼౽޾ޙ௪ܓ౥౥ऌɾో߿ొࠏߍ౹ಀాಡಯݞ࣬ܐࡩޒٝవک૿ڞೇګూڔஐಟه஻஫ٱؼޜޗஊ஀ۣهೕذٌ௟ಏܨʁಚఛܠڥޗௌ۸ట౷ࡤ٩౔೬ػࠅమ೒ٸࠝೲ೮ݖࡎࡒ೸ي߅׸څڇʁډېഁٽاڐࠫ̕ೡಜ௙ௐ೏೉஌௕೸ഉయڦ௪ப௙ؙ೐೶಺ذ௣ಎڷ౶࢈஠ۘഊ௭ಓഒഋ޹ۉۋഁɾࡈۏɧۑܛۓಣسகഃުಗ̢࡯౎ಬ౸ࣘڼ࣯ࡘ̹ࢃ݆ࣵࢁఫ஠௨ࢉրଙ֋૸ൎࣀଘ̳ଃٟଅ˗൓ࠁईపʇघ൜ࢠ௵ੵ҈߮రୀ௫൙׫̩ࣷ൩ࠖԐ്ଣଚૼ࢘ओ൲࣋ऒ౧У൯ࣂ૾ْ̳֕ଭൎئࢷ൴൳࣍ࠥ೨ୗඇ೨൶٪୹Ɔേۜךडࢤࣰࣳൟౌऐऋ࣑ඔ࣪࣠ೀ௠ࣤഠඎ܅ඓു˞൪ࣵࣸࣽඩֿࣻ϶ऄථୌंࠢइౠऌऊరඊ൧क՘ࢹ۟೵ڭ೟ඞӶ୻൞୸ଋ׶଎ാۛठୣස՟थ୮සଉଌब,मNJĈTʬʮrʰषʴĜʶʸDžʻॄ:ǿˁȆ෥Ǟे˒ʾोԧ෬ॏʾ॑॓ॕॗ˝қ3Өɮ8ɱ7ɱ3˗ј৹йϺԈƊ˪ฆѝїฌ̿෺йЛѬ෹Ϸด˪ญқ̘Փ฀ญฏമ͎૥ต̝Ƌถ੨̢Ϻม͎รสฒƍฏร7ѓปԖ̩ชจԊีоқՂԗ฼ҚՃɘσЂโϷӯяϺƍ7ͧ˗้˪ัͺตหฤรઌึম̦ॠุͺढ़ԗ๋Ө๘ฉ৬ۏจ๣฀শซ๠ึϐ๐ฒї๙Ơฅɯоঝ๤ϼՠΛ๎ҏ๣ϷϏ๨ѣ˗฻຀฼ิ๨๙๣ѣ๙ۏۏз̹๙9๗Јๆ๠ຊ๭ຄ̀ฝทກ฿ЩโଲΛЛਸйขຢມ຤উїˢɱμɯড়แອકϖ฾Ђ๿฽ԗব฀ҝ຤Ƃੴୠ[ນВ฿ຉ๥ն֍ໂ̠พໂۏ̄อιϏ̐̿฿̦̐ǿɜ฀่Һ่๋˗ε๯6ӯ฿෼ϗໃɡ໠຺໥ຩโ̄ก໡ກ຺໪໊າԽՁ໰໐ິԾ฻଑຿฼Ƃգ҇଻਩ਫ਼Ҳโ໔ລѬΊ෾ຏ˩ຆָ̀զກԾ๹Ύ਺ฤ๔๟໲ΛોЙγบ҅แภઆ̽ํู͈อພ๵๴৖̙จ໤˵༬ড়༧๟༊༐๨НโઢӅПϠ༸ॼ༺ິ໻੣мҁָૄਤ҇໽Χੲ੭཈Ҏψจϊঊ຺୪ߪӐັোԔਿ।໦໡ཙཛɴୡඟ੟ճਧൢ୞ୡֻІ໪ཨຣཀྵ຤กɫຮ཯Ђ׳˽໧੢ຽິїਧ໬Ύ੝̘ࡈత௸࢒ޓޖѓວ໫ຜբʇຫ༫༄ࡴ࠻ཿཱྀཾۍ࠻ɲྔ՞ྕԯৢѩৎ༘དබ̄՘Ϛঘৃฺ๏๒๫ྦถ֒Ќ໠ཱུݚจຓЁ๢ͪࡓҭ༆ຕ༱Լқ4͚ͧྻິࣥຫ༴࿂ɯྐྵҡરԾྤ໯ກҕ຀ʃ˫8ˠ࿏࿑໓࿓ԗ࿊ິນবԕ8ւ֓צϱҟࠕؤן؎ଇक୵࿨ඌྜྷභ࣓ି࿫ऊ̢ྑྀྏతʃ਄ు1࿷ϱ࿺ɲ࿺ʃྕကྖଥଵ࿼࿹໓జරࢻࡖ࢔ջ֠ଞଯԄԼ໬Ǟ࿍ཥ୯୼ɡ຾୰໸Ч࿙ဖ࣬֒՟ပྲྀ࿋ཡ࿫࿌฽ဒິंဣ୑בုӸ୓ୖࣧ୕ඈҧɡס୛נଢ଼໢൥୆໾බಳခ၃٠ଢ࿩୭൛ဗ৲ဤါਗ࿧࿪။ිज෍ۿފɦπ՞6͜7މࡋࡪ֨યͨ࠿ၢπ͜ƈഌיސەɥၗɣڬಯၖەၲӚၪࡗڜɥયၮၩၻɦܫ೔ܩၳఄၴႃഽؿႄ9ڬ࡜ႁႈܐၱႄƟၥၼފڍၧၤ႖ڒ႕႙ޅ୓ࡸϔႝɭ႟႓݃ثႤҖႥͨࠪه႙Σ8ٱܲܲႬႛԣႋႴႄ೻ʇ႕إ6ϱႻ႑႖ၧٵႹΣƛၘπϱႚುևၡ჈ၤၽದأ՞჌܁Ⴕ౴ɯ჋ၦၦ჊މႲɘؙგბႰ႑ޫ႕ႌۮܲసڢٮ႟ٷႠ႞ხჁიႾ჌ფޜჳڡၹၹ౩ɘჶϗႼڱჀ႔ჱ჎޹თტۯၭᄈࠩჯწწಊҖߟ܄ɞႶნڛᄒɣࡋᄘיಛ޵Ӝސ߈ݯݐث௯حႦᄥɤႥᄧೢலࡲზცᄋࡸࡔఢᄒࡉᄗԥნౌාߗᄯჯׇࡹϤჸჷޅ࿬ɢᅀആზ౾ᅅ٨წޑɦၬࠠ௎ᄼఅక֮٪దჷႬࡪۭႭπ౼ჅႨӚᄾݣ׀ᅖൣݝᅣᄶᅨ܈ᄼၰᄟᄻჭᅪᄵᅊ۩ًࡗ௚ಁᄭᅑஒႝᅶᄌࡍ௻පᅽᄶ౤߅౦ඕݩჺױᄐჯᄲؕഔّ႙ჾ֮Ⴑᄪӑద჊ᅕᄻᅶݿᄀ߼ۭქႷ١ჇۤࡷᄏႾ೪ࢴ݄ၸ౾ࢴᆬഞႄؽᄩɤსாᄇߘᆍ಑჌ಓ႘ۤᅧᅰఢధಘᆹᆢᆦޜᆏჱᆚᆊᆴဈᆘୈᅾඞᄍܒᆁ܈޻ݏݲ౓௩ᅿᅆ՚ᆄಳాಶ౬಴ೋჍಕᅩಡᄷࢧൢऌᅯߙ௘ཿᅷᆚۿᆿࠆᇳᅩᇲᆂᆆרᄹှ߳෈ூᇩ൙මҴᇪᄖሄᄙᅱህᄚӵᄨᆬላልම٣ᄠ႞ࡱᅮᆬై൤ᅯᇒ܏ಢ೙૚ྡྷરྡӏ̢ٳᄞᅩᄍᄢ֙ӵՆᆊᅯᆌႶݢ̓ምሱሟњҲဉՆሷ֙ሹɤ࢘ᅐச࠾ᅆுᄸߩߴࡇ࿳ࡵᆤᅯږႵᄝႇႴሁऑᆽٲฦტჹ܄ᄅɳࡠᇃႿᇇ೪ᅋᅳاΎᅴመᅡᆲᆥብቧڕߟቪሓᇯᇭቇၶᇐቬᇒٷЧძݢّ7ɨ࿻ቺ࿽ቼ࿹݃ቶᅮታࣉհ֞؎7٥௳ލ̓ࡵӲΑဝ൴ሼᅥडၔ၊ङᇸඹࢣ൦ᇪኛׄሢሳሲሠԿҙ࿻ဇ࿽ኦဆԗእሩሺሶክሸሻම࿸စЅࣃԃԟࠒࠕ͝ඁ඄ංංቲኁࣕቶሁ၇׷ᄺባᄋᇿಠ߯዁ቒኘࢤ඗ࣔࠋዃ዇ኃᇼዋ߲ወႠዀᄻውݢටඦࠗ֍ණዤֲࣾතධअනዪਣਨቂኋୢᅃዯୱኮሻኯዷࣥࢧၔचෆዊሗዖჭᇎܕንᇺጄዅඍුූƲෘळowवʱˏෟĆ෡ऽ॔िʾą෧ृीǝ्ैॊˎ෱ˋ෴˘˚Ăक़ณՠ෼෾ືйਠЊ๻༡Ƣྥรә༉๛әঠ༰Ϸྜˮຫጴ਄จ਄ጴጻ๠Ԋጲ̘πཚཱིཚоϟፁԞӈҚ࡟̹ตጷ๎๴໯ѻ࿋ໟਥใโໝ༢ͯ๐໘࿖˷ไ৬຾ັজтჄ፯Ӭ਋ਤፘྷፏ५༰ีਠ፴˩әหӅፑՠፓ࿁Ӆ፴๑รর๒๼ঝ̍༭তᎍ͍҄༧༇͈໴፸ຒ๦ঠӎ፱ՠବ੧ྯ๦ྲ᎑ชፄፔ˟্Ϙྱ˳ຠཫᎬઁдร໪຺ສૉι፠แ८ັ྾သਨ໭ຸᎱ፶࿮ဪ໷༹ຖ່ঊ୷ແ҅໇๰້Լј׶໎࿏ЫϿ0໓໡3໖Й፦์໛፩ፅ཮໺Ꮗਧྋཙສ໩ຨ࿘Ꮧ।μ໲࿈Ꮼຂи฻໶ຂົᏄ໔໼ਦ၀Ѽༀҭ໚Ꮱྌ༲਄ຎ˨ፙІ༌ᏖᏲᎴ๠̈́ε๓૩ЇཌྷԖ༗ຯЛ࿞Ꮛ༝ቓᐎᎵ๷༢ՙ༤༓ϐ፹ઇ༪᏿ཙ༲༊༯༮ี༴՗ᏀᏴᏖਠ༼฻༾เཀ੥ག੧ཅᏹཇॺཉᐽཋચᐑӁཏᐮས࿧๰๩຅๲ଲญ˫ທ๵ࣥอྦྷᎰӮંᏌผດ̝༞ᐚྨઌፈᐠॠྦྷᑑฐฤะຒᑕ຃๴ืຕᑣ፫ำѿᎶ፡ๅ๚༡์੫์።จ̩ᑓྦྷ๔΋ᐃᐃ᎒ᑴ˩8๝ᎁᑬӋᎡ๴ྰ੺ᑪ๪ྨคᒊ෾ດᐏ๳ชঝᑸ๸ཌว̿ᐓѕຳᑮᑩᑊ๽ᑴᒋໂ຋˞ຍຏήຑ᎟ྱ๙໩ᑘԖກ૎ຜ྅ᐎо຾ဨՂ࿎࿔ϗᒾƂᓀ࿒ᏂᏩᒼോ൏൭൰ତ൑ଂೇർ˸ൗ࿦Գၐၒࣗਨඝ౟ץလਡྃቆᓞྐྍʇኴከᓣኪ࿿ဂ၃ဃеኩኳဇۄൾർନ՞ପϕတ֎ᓄӍ࿘အ൜ᓸ࢝ᏺነဣିඵᓫနဩမᏩཟ଍ᔅᓆ၌฻ာᔆ١କီଔဲံဳဵכး်္ኄဿዴ࣮။଒ኔຫ።์࿐̞ᎇ׳ጇ̍൝୷ᒺ၎ᓓዽၑᔫኖၕߘბၙၛၝಆᇙᆴၫπٳᅡቌᕃኺ۶႖ᕆࠕ௠ݕფბშݑቪᅠ჆ჲ࡜ቤෂᇆቀქቝ߈ᅤ఺ᇴᅰಥӜᅘᕒᕡᆥ،ӡႁᕅᆓቛ్ᇎޙ዁Ⴂሊ׬݉ልᕀࠅᕧႄ8Ⴌኇ֬ბቤᕮኂႡᕕொႬˢᒅπᖅైஎᕩჲᄂᕠᕗᕫቁෂᖋᅓƂᆷᄝௗᕢͨᕔቦؔᖊ؟ሣቍᔽ௘ᕙӿتುᇴᕝᇵࠠᄣᇻᖐᆐᕑᕊჼᄃ఻ರᐃͨᖈᖇ֓ಾ߂ჀቕᅇᄆᇋᖌᕣɩᕥƝஎݾᄑٜೋሇሆለᖑႊᄚሥᄡᖜఋᆱሌᄦଃᆧᇌފቢጀዎᇻᅰᄳޓᄖᇀ቏൚ᆕᖀ߫ቱᄿᅁᖿዯˮᅅݥၡڃᅉܽߘၸᅎϔᅐ቉ᅒᕆழᅤɦᕢߴᕺᅜఉƣᕵᕤᅦᆋᅂᇺᆱწᇶᅰሦႍᅭዚᘏᔻᅲᅸቭቈᗝᖂᆙணዚᓙᄸሙྴᗠ߄ᘌඑኛᆈүራᘖᗑቌუၦᆑᖔჴᕬۜ෎ዖᇰ୽ᆜ۹ᆞݽᄔᕗቧ႓ყᆔᘎ߈ᆪᇚᗗ۝ႀೲᗖᆳٴᆵᘮქპቜၿᄆᆽᘐᗥᄤᖐᖏᘴᘰᆦᕐᄩᇊࡔᇐᕲጂᗁᗼᙥᘋᇔౢಬ౐׀ᖻఆᅂಲጃᇞ಻ఈᇡᙞቧ݄ᘌᙙᘥࡃࡂࣱ቗ᆻᗂڜၵၺᄕၵఓᙐᚇᕙၳၺᅷၷᗮᄉၼၡᆰᄎᕃᙒᗐᕃᆠசᚍ೴᚞ᘯᚋၗᕃ႐Ⴭᄈᕂჼசᖲᚋᗞᖁ్ᕴᗖᕽ೯ჰᘈᅛᇡͨᆳᄀކቋᚠᆟᄔႹ؎ႽƂቚ჈ᚴᖔჄͨბᛄᄷၠᇇᛅᖾᚅᗂകᚽಯᛍᛒᘇᄃ᚜ᙓႾპᚹᘿቚᖤᕥᙝږᄮᘕ዁ᄁᛗᛎᕎ؋ᖲᅉჺᖮ͜ᛂᛂీᚿᄂᛐ቗ᚺ୓ᚆᚧᗩᙊᖜᗉᄓᘵᛦҒዔ[ቸቾቹ˨ቻᜊችዜᜃᛦ൬ࢰᓊ൸૸ᜑ଄२ൗൿଆዏሀ၉ን୻ම୷ዾᗯ᜜ౠ̘ኡሞኢኻမእኪኧᜮᓬካղኰድ᜵ሷኲᜭᓭϩᓱବᓳНଫଝࠦኾኽࢼᜎታትᄯዄ࿪ᝆძᜣಥࣞᖀᗦኙᝊᝑዕኁᝎᙧቃኃᝌხዞࡿࣶᝠዠዣዦඪࣾየያදᝨआਤዳ၅ዱᘍ൦ଈဉᚹᕻഛᙰࢤዻ؆ᓕዲᚭן ᜞࢜ጅ൝᝻׷बɶɆeɈʥጊŁŃ SŹញŢጒह෠ऻ෢ķşzeXąզ\"រeY˖෫ƴʾBĔहǜጛ˓BgጚǟʾC˂ą෫ːጠ˓෯ጟ෭Ĕጥ\"extឮጡौឿេៃॐ˖ėៀែጘĔ3ąЌហʘǀMॎោ॓s។e៖ីෳुʗĮǀsǸឨȇǁĆhidहន៓២ǪVįibʹ:trueŸ˂aŋŝSĦ៑៼ɒɔ˝Ꭽཬཪ᠊᠉᠌໪˧᠈᠐᠋᠑᠍᠋̤᠎ફ཭ӫ᠕ɚ᠜ᐄ᠞፼ᐟ᠗຤०য়ตᔧ׺܍ྔ̈́Ǟᎆᑼ˥Փ᠒঱Ɖ֍᠋ဴᔗᠷඇཫ᠏঱᠎ԁ᠋ၜຫˤǞ଀᠔ᡅᠽᠣጮ͔ᠴᡆຣ࢔ཫᡎᠱ̞ᡇᠢཫтӎڊԃ۴˨࢞྅ᡓᠼຣধ᠚ᡌ຤ᡐᡤຝᡅᠻ˷͚ᠭྨ਄ᠨԃᠬᠮ๑ɝᡃᡟᡈ̚ᡋ᠓໪ᠶᔖᡣᠢԛઑᢀᢂફ᠖᠕Ԝᡢᡔᡠ͖ɰי͖ᠺጮ᡺̓᠝ɝᠾ᡾ᡑᢑᡌ᡹ᠲጹ᠝ᠠᢖᢟ೫ᅍ˨өᢗᢥᢟ᠑ᢚ̟̪᢫࿕ᓁᒿ᢯᠓ᡩ᠈৽ྛᢧᢦᢘᎭ̜ᢩᑎᢦ᢮᠈ᢅ˷ᢓᄳ̔Ͽᣃᣂᢕᡕᣄ͍ਉᣇᢶᢺ༬ᠰᡣᢾᢆ᠌೫௚ᗄᢡˣᢣᔼӜ̛ᢤᡒӲჇɚᗷӚ̦ᣟᢲᡟᢩᎿ᢭᢬᢯᢮ᣓ᠌ᣨ๵ᢌྚחԈᢴԈᣀ᣸ԉו᣹᠘ᣩᢈᠵϒᤃཻᤅᢶ຤࿜ፋߌӚ͆࿞ɨᢎ๿ܺ࿞ᡚᤏᣠᡷ᠙́ᢊ᠟ᢝᤜ᠞ᢘᢃ᠌͇ᢞᤣᣋᤇᢿືᣪຨᣬᤫᣮᢼᡨᢄ᣿ᡦၸ۴ቯݕᗄᗄᢸᢷମ᠋ᣰ᤹᤯ᢁᤱᎱ᣽ᢍ᥄ᢌᣴ̙᤾ᡩᣨᤩᢰ᤺ᢟᤠ̤ᛈᛁზ͚͆ᡵᡣᣲᢹᤁູ᤮᤾ᢉᤖᥞ᥎᥉ᢐᠱ᥌ᥙᢦᣀᎱᏕ˷˱ᥭᥬ᥯Ǿ᥮ᥱᥰᥫᥳ᥶᥵᥸ᥲ᥹᥯ᢑ᥌ᣯᣭ᥿᤬᡿ઋਈ֓ӧڅೱ೵̺ᣚ̖ᤵܷΣ̪ϙٯᣯڮ৐ࠫ௷Ǿԧԛ᥌᥆ᤄ᢯ᦝᣐᣱᤐલ૜ᦤᦦ૘ᦥᦨᦧᦩᦩᤰᡸᥛᦃᢪઐᢻᦳᦣᥡᦢ᥀ᦹᦷᦃᣏᦹᢌ०६᦮զ6͞߀໪5ιӯι͚໪።ސᡛ྅ኈᡛɣ͈ੂᡑᡡᤙᤦᡍ᥈ᣔᠩ΄ɱጴٷٷ؟ԥᤍтᣚ4฀ᡙྫᦐ࢞Ѵᢏ᥌ᦻᥢ᧳᧚᧵ᢹʌ˓ɺɼᐵʈ᧽ɿ᧿҃᧾ᨁᨀᨂᨅᨄᨇᨃᨉᨆᨊᨈɾᑰᨋᨐᨍᨒ᧕Ǟؐᨌᨗʀᨕᨔᨛᨖᨑᨏᨓᨑᨘᨈ᨝ᨠᨥᨢᨉᨤᨢ᨟ᨧᨡᨡᨩᨭᨦᨋᨯᨒᨫᨰᨬᨓᨚᨹ᧕ᨮ᨜ᨽᨰᨵᨱᩁᨷᩃᨶᩅᨥᩀᩄᩂᩆᩉᩌᩆᩈᩋᩐᩊᩒᩍᨴ༿ᩑᩔᩘᩗᩚϕᩛᩙᩓᩝᩑᩏ᩟ᩣᩞᩥᨿᩖᩤ᩠ᩩᩫᨷᩢᩦᩬᩪᩯᩮᩱᩴᩰᩒᩳ᩶ᩯ᩺ᩡᩨ᩻᩹᩵ᩞ᩸᩾᪃᪀ᨆ᪂᪅᩿᪉ᨌ᪇᩹ᨺ᪎ᨽ᪏᪑ᨻᩇ᩽᪀᪒᪗᪐᪙᪏᪆᪕᪊ʈ᪘᪠᪚ᨛ᨞᪝᪄᪍᪢ᨳᩔ᪌᪦᪬᪈᪫᪈᪭᪉᪯᪞᪴᪦ɵ១ʙʥɶǙƸSɻȥɍɶź᪽ᪿȦɖឆ᪸ǀ᠁ħɍឌĆłń៚៰ ʏǒ៯᪹ᫍĘ៫ĜjnUwxAʲጓហភ጖ឦĔឮ០ɷʾS៛ॅុෲĔो॓ិូ˔᫫ៀƮndឝʝʕ៻ំ᫻dឣ᫿ʟᬁ෶Ă᧺˝࿮ЫᓨᬏᓒᏵбᓧᓩྗྉࡼွି՞ᬔᬏᬖ୤ྖຼᬚྈᬟྔбᬣᓨᬡդဂᬔˠᬥ҆ᬜᬮᓿ҆ᬞᬠᬕଥᬝᜢᬪ᬴ᬮᬵˠᬰᬘᬲϟᬛᬗᬱခᬰᬩ৫ᭃᬤᬝᭊᬨᔡץ᭍ᬶᬻᬽར᭐Χ᭒᭑ᬭᭈ᭐኶ᭌᬣ᭚ᬙᓚ᭟ᬳᬸ኶ཤᓗွᭀ၃Ѩ᭢࿯ਧ᭪᭎̍᭭ᬑਘВ᭰᭴᭸࢑᭶᭶᭹᭨᭛᭞᭮ᭁ᭕ᬭ᭥ဟᮃᓚ࿚ᮅ᭾᭏Ꮑୡ᭱᭸᫈Ę᫼o᪾៾᫆[᫂Řᮔ᫅᫁ʋ}"} --------------------------------------------------------------------------------