primeiro commit
This commit is contained in:
3
src/3rdparty/README.licensing
vendored
Normal file
3
src/3rdparty/README.licensing
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
The files in this directory are not licensed under the same terms as the
|
||||
rest of OpenTTD. Licensing details can be found in OpenTTD's readme.txt
|
||||
and in this directory or subdirectories as well.
|
||||
325
src/3rdparty/md5/md5.cpp
vendored
Normal file
325
src/3rdparty/md5/md5.cpp
vendored
Normal file
@@ -0,0 +1,325 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file md5.cpp Creating MD5 checksums of files. */
|
||||
|
||||
/*
|
||||
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.c is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2007-12-24 Changed to C++ and adapted to OpenTTD source
|
||||
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
|
||||
either statically or dynamically; added missing #include <string.h>
|
||||
in library.
|
||||
2002-03-11 lpd Corrected argument list for main(), and added int return
|
||||
type, in test program and T value program.
|
||||
2002-02-21 lpd Added missing #include <stdio.h> in test program.
|
||||
2000-07-03 lpd Patched to eliminate warnings about "constant is
|
||||
unsigned in ANSI C, signed in traditional"; made test program
|
||||
self-checking.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../core/endian_func.hpp"
|
||||
#include "md5.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
#define T_MASK ((uint32)~0)
|
||||
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
|
||||
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
|
||||
#define T3 0x242070db
|
||||
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
|
||||
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
|
||||
#define T6 0x4787c62a
|
||||
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
|
||||
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
|
||||
#define T9 0x698098d8
|
||||
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
|
||||
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
|
||||
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
|
||||
#define T13 0x6b901122
|
||||
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
|
||||
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
|
||||
#define T16 0x49b40821
|
||||
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
|
||||
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
|
||||
#define T19 0x265e5a51
|
||||
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
|
||||
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
|
||||
#define T22 0x02441453
|
||||
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
|
||||
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
|
||||
#define T25 0x21e1cde6
|
||||
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
|
||||
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
|
||||
#define T28 0x455a14ed
|
||||
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
|
||||
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
|
||||
#define T31 0x676f02d9
|
||||
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
|
||||
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
|
||||
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
|
||||
#define T35 0x6d9d6122
|
||||
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
|
||||
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
|
||||
#define T38 0x4bdecfa9
|
||||
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
|
||||
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
|
||||
#define T41 0x289b7ec6
|
||||
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
|
||||
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
|
||||
#define T44 0x04881d05
|
||||
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
|
||||
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
|
||||
#define T47 0x1fa27cf8
|
||||
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
|
||||
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
|
||||
#define T50 0x432aff97
|
||||
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
|
||||
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
|
||||
#define T53 0x655b59c3
|
||||
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
|
||||
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
|
||||
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
|
||||
#define T57 0x6fa87e4f
|
||||
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
|
||||
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
|
||||
#define T60 0x4e0811a1
|
||||
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
|
||||
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
|
||||
#define T63 0x2ad7d2bb
|
||||
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
|
||||
|
||||
static inline void Md5Set1(const uint32 *X, uint32 *a, const uint32 *b, const uint32 *c, const uint32 *d, const uint8 k, const uint8 s, const uint32 Ti)
|
||||
{
|
||||
uint32 t = (*b & *c) | (~*b & *d);
|
||||
t += *a + X[k] + Ti;
|
||||
*a = ROL(t, s) + *b;
|
||||
}
|
||||
|
||||
static inline void Md5Set2(const uint32 *X, uint32 *a, const uint32 *b, const uint32 *c, const uint32 *d, const uint8 k, const uint8 s, const uint32 Ti)
|
||||
{
|
||||
uint32 t = (*b & *d) | (*c & ~*d);
|
||||
t += *a + X[k] + Ti;
|
||||
*a = ROL(t, s) + *b;
|
||||
}
|
||||
|
||||
|
||||
static inline void Md5Set3(const uint32 *X, uint32 *a, const uint32 *b, const uint32 *c, const uint32 *d, const uint8 k, const uint8 s, const uint32 Ti)
|
||||
{
|
||||
uint32 t = *b ^ *c ^ *d;
|
||||
t += *a + X[k] + Ti;
|
||||
*a = ROL(t, s) + *b;
|
||||
}
|
||||
|
||||
static inline void Md5Set4(const uint32 *X, uint32 *a, const uint32 *b, const uint32 *c, const uint32 *d, const uint8 k, const uint8 s, const uint32 Ti)
|
||||
{
|
||||
uint32 t = *c ^ (*b | ~*d);
|
||||
t += *a + X[k] + Ti;
|
||||
*a = ROL(t, s) + *b;
|
||||
}
|
||||
|
||||
Md5::Md5()
|
||||
{
|
||||
count[0] = 0;
|
||||
count[1] = 0;
|
||||
abcd[0] = 0x67452301;
|
||||
abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
|
||||
abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
|
||||
abcd[3] = 0x10325476;
|
||||
}
|
||||
|
||||
void Md5::Process(const uint8 *data /*[64]*/)
|
||||
{
|
||||
uint32 a = this->abcd[0];
|
||||
uint32 b = this->abcd[1];
|
||||
uint32 c = this->abcd[2];
|
||||
uint32 d = this->abcd[3];
|
||||
|
||||
uint32 X[16];
|
||||
|
||||
/* Convert the uint8 data to uint32 LE */
|
||||
const uint32 *px = (const uint32 *)data;
|
||||
for (uint i = 0; i < 16; i++) {
|
||||
X[i] = TO_LE32(*px);
|
||||
px++;
|
||||
}
|
||||
|
||||
/* Round 1. */
|
||||
Md5Set1(X, &a, &b, &c, &d, 0, 7, T1);
|
||||
Md5Set1(X, &d, &a, &b, &c, 1, 12, T2);
|
||||
Md5Set1(X, &c, &d, &a, &b, 2, 17, T3);
|
||||
Md5Set1(X, &b, &c, &d, &a, 3, 22, T4);
|
||||
Md5Set1(X, &a, &b, &c, &d, 4, 7, T5);
|
||||
Md5Set1(X, &d, &a, &b, &c, 5, 12, T6);
|
||||
Md5Set1(X, &c, &d, &a, &b, 6, 17, T7);
|
||||
Md5Set1(X, &b, &c, &d, &a, 7, 22, T8);
|
||||
Md5Set1(X, &a, &b, &c, &d, 8, 7, T9);
|
||||
Md5Set1(X, &d, &a, &b, &c, 9, 12, T10);
|
||||
Md5Set1(X, &c, &d, &a, &b, 10, 17, T11);
|
||||
Md5Set1(X, &b, &c, &d, &a, 11, 22, T12);
|
||||
Md5Set1(X, &a, &b, &c, &d, 12, 7, T13);
|
||||
Md5Set1(X, &d, &a, &b, &c, 13, 12, T14);
|
||||
Md5Set1(X, &c, &d, &a, &b, 14, 17, T15);
|
||||
Md5Set1(X, &b, &c, &d, &a, 15, 22, T16);
|
||||
|
||||
/* Round 2. */
|
||||
Md5Set2(X, &a, &b, &c, &d, 1, 5, T17);
|
||||
Md5Set2(X, &d, &a, &b, &c, 6, 9, T18);
|
||||
Md5Set2(X, &c, &d, &a, &b, 11, 14, T19);
|
||||
Md5Set2(X, &b, &c, &d, &a, 0, 20, T20);
|
||||
Md5Set2(X, &a, &b, &c, &d, 5, 5, T21);
|
||||
Md5Set2(X, &d, &a, &b, &c, 10, 9, T22);
|
||||
Md5Set2(X, &c, &d, &a, &b, 15, 14, T23);
|
||||
Md5Set2(X, &b, &c, &d, &a, 4, 20, T24);
|
||||
Md5Set2(X, &a, &b, &c, &d, 9, 5, T25);
|
||||
Md5Set2(X, &d, &a, &b, &c, 14, 9, T26);
|
||||
Md5Set2(X, &c, &d, &a, &b, 3, 14, T27);
|
||||
Md5Set2(X, &b, &c, &d, &a, 8, 20, T28);
|
||||
Md5Set2(X, &a, &b, &c, &d, 13, 5, T29);
|
||||
Md5Set2(X, &d, &a, &b, &c, 2, 9, T30);
|
||||
Md5Set2(X, &c, &d, &a, &b, 7, 14, T31);
|
||||
Md5Set2(X, &b, &c, &d, &a, 12, 20, T32);
|
||||
|
||||
/* Round 3. */
|
||||
Md5Set3(X, &a, &b, &c, &d, 5, 4, T33);
|
||||
Md5Set3(X, &d, &a, &b, &c, 8, 11, T34);
|
||||
Md5Set3(X, &c, &d, &a, &b, 11, 16, T35);
|
||||
Md5Set3(X, &b, &c, &d, &a, 14, 23, T36);
|
||||
Md5Set3(X, &a, &b, &c, &d, 1, 4, T37);
|
||||
Md5Set3(X, &d, &a, &b, &c, 4, 11, T38);
|
||||
Md5Set3(X, &c, &d, &a, &b, 7, 16, T39);
|
||||
Md5Set3(X, &b, &c, &d, &a, 10, 23, T40);
|
||||
Md5Set3(X, &a, &b, &c, &d, 13, 4, T41);
|
||||
Md5Set3(X, &d, &a, &b, &c, 0, 11, T42);
|
||||
Md5Set3(X, &c, &d, &a, &b, 3, 16, T43);
|
||||
Md5Set3(X, &b, &c, &d, &a, 6, 23, T44);
|
||||
Md5Set3(X, &a, &b, &c, &d, 9, 4, T45);
|
||||
Md5Set3(X, &d, &a, &b, &c, 12, 11, T46);
|
||||
Md5Set3(X, &c, &d, &a, &b, 15, 16, T47);
|
||||
Md5Set3(X, &b, &c, &d, &a, 2, 23, T48);
|
||||
|
||||
/* Round 4. */
|
||||
Md5Set4(X, &a, &b, &c, &d, 0, 6, T49);
|
||||
Md5Set4(X, &d, &a, &b, &c, 7, 10, T50);
|
||||
Md5Set4(X, &c, &d, &a, &b, 14, 15, T51);
|
||||
Md5Set4(X, &b, &c, &d, &a, 5, 21, T52);
|
||||
Md5Set4(X, &a, &b, &c, &d, 12, 6, T53);
|
||||
Md5Set4(X, &d, &a, &b, &c, 3, 10, T54);
|
||||
Md5Set4(X, &c, &d, &a, &b, 10, 15, T55);
|
||||
Md5Set4(X, &b, &c, &d, &a, 1, 21, T56);
|
||||
Md5Set4(X, &a, &b, &c, &d, 8, 6, T57);
|
||||
Md5Set4(X, &d, &a, &b, &c, 15, 10, T58);
|
||||
Md5Set4(X, &c, &d, &a, &b, 6, 15, T59);
|
||||
Md5Set4(X, &b, &c, &d, &a, 13, 21, T60);
|
||||
Md5Set4(X, &a, &b, &c, &d, 4, 6, T61);
|
||||
Md5Set4(X, &d, &a, &b, &c, 11, 10, T62);
|
||||
Md5Set4(X, &c, &d, &a, &b, 2, 15, T63);
|
||||
Md5Set4(X, &b, &c, &d, &a, 9, 21, T64);
|
||||
|
||||
/* Then perform the following additions. (That is increment each
|
||||
* of the four registers by the value it had before this block
|
||||
* was started.) */
|
||||
this->abcd[0] += a;
|
||||
this->abcd[1] += b;
|
||||
this->abcd[2] += c;
|
||||
this->abcd[3] += d;
|
||||
}
|
||||
|
||||
void Md5::Append(const void *data, const size_t nbytes)
|
||||
{
|
||||
const uint8 *p = (const uint8 *)data;
|
||||
size_t left = nbytes;
|
||||
const size_t offset = (this->count[0] >> 3) & 63;
|
||||
const uint32 nbits = (uint32)(nbytes << 3);
|
||||
|
||||
if (nbytes <= 0) return;
|
||||
|
||||
/* Update the message length. */
|
||||
this->count[1] += (uint32)(nbytes >> 29);
|
||||
this->count[0] += nbits;
|
||||
|
||||
if (this->count[0] < nbits) this->count[1]++;
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
|
||||
memcpy(this->buf + offset, p, copy);
|
||||
|
||||
if (offset + copy < 64) return;
|
||||
|
||||
p += copy;
|
||||
left -= copy;
|
||||
this->Process(this->buf);
|
||||
}
|
||||
|
||||
/* Process full blocks. */
|
||||
for (; left >= 64; p += 64, left -= 64) this->Process(p);
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left) memcpy(this->buf, p, left);
|
||||
}
|
||||
|
||||
void Md5::Finish(uint8 digest[16])
|
||||
{
|
||||
static const uint8 pad[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
uint8 data[8];
|
||||
|
||||
/* Save the length before padding. */
|
||||
for (uint i = 0; i < 8; ++i) {
|
||||
data[i] = (uint8)(this->count[i >> 2] >> ((i & 3) << 3));
|
||||
}
|
||||
|
||||
/* Pad to 56 bytes mod 64. */
|
||||
this->Append(pad, ((55 - (this->count[0] >> 3)) & 63) + 1);
|
||||
/* Append the length. */
|
||||
this->Append(data, 8);
|
||||
|
||||
for (uint i = 0; i < 16; ++i) {
|
||||
digest[i] = (uint8)(this->abcd[i >> 2] >> ((i & 3) << 3));
|
||||
}
|
||||
}
|
||||
72
src/3rdparty/md5/md5.h
vendored
Normal file
72
src/3rdparty/md5/md5.h
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file md5.h Functions to create MD5 checksums. */
|
||||
|
||||
/*
|
||||
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.h is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2007-12-24 Changed to C++ and adapted to OpenTTD source
|
||||
2002-04-13 lpd Removed support for non-ANSI compilers; removed
|
||||
references to Ghostscript; clarified derivation from RFC 1321;
|
||||
now handles byte order either statically or dynamically.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
|
||||
added conditionalization for C++ compilation from Martin
|
||||
Purschke <purschke@bnl.gov>.
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#ifndef MD5_INCLUDED
|
||||
#define MD5_INCLUDED
|
||||
|
||||
struct Md5 {
|
||||
private:
|
||||
uint32 count[2]; ///< message length in bits, lsw first
|
||||
uint32 abcd[4]; ///< digest buffer
|
||||
uint8 buf[64]; ///< accumulate block
|
||||
|
||||
void Process(const uint8 *data);
|
||||
|
||||
public:
|
||||
Md5();
|
||||
void Append(const void *data, const size_t nbytes);
|
||||
void Finish(uint8 digest[16]);
|
||||
};
|
||||
|
||||
#endif /* MD5_INCLUDED */
|
||||
299
src/3rdparty/os2/getaddrinfo.c
vendored
Normal file
299
src/3rdparty/os2/getaddrinfo.c
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* This file is part of libESMTP, a library for submission of RFC 2822
|
||||
* formatted electronic mail messages using the SMTP protocol described
|
||||
* in RFC 2821.
|
||||
*
|
||||
* Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* An emulation of the RFC 2553 / Posix getaddrinfo resolver interface.
|
||||
*/
|
||||
|
||||
#if !HAVE_GETADDRINFO
|
||||
|
||||
/* Need to turn off Posix features in glibc to build this */
|
||||
#undef _POSIX_C_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
|
||||
#include "getaddrinfo.h"
|
||||
//#include "compat/inet_pton.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
static struct addrinfo *
|
||||
dup_addrinfo (struct addrinfo *info, void *addr, size_t addrlen) {
|
||||
struct addrinfo *ret;
|
||||
|
||||
ret = malloc (sizeof (struct addrinfo));
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
memcpy (ret, info, sizeof (struct addrinfo));
|
||||
ret->ai_addr = malloc (addrlen);
|
||||
if (ret->ai_addr == NULL) {
|
||||
free (ret);
|
||||
return NULL;
|
||||
}
|
||||
memcpy (ret->ai_addr, addr, addrlen);
|
||||
ret->ai_addrlen = addrlen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
getaddrinfo (const char *nodename, const char *servname,
|
||||
const struct addrinfo *hints, struct addrinfo **res)
|
||||
{
|
||||
struct hostent *hp;
|
||||
struct servent *servent;
|
||||
const char *socktype;
|
||||
int port;
|
||||
struct addrinfo hint, result;
|
||||
struct addrinfo *ai, *sai, *eai;
|
||||
char **addrs;
|
||||
|
||||
if (servname == NULL && nodename == NULL)
|
||||
return EAI_NONAME;
|
||||
|
||||
memset (&result, 0, sizeof result);
|
||||
|
||||
/* default for hints */
|
||||
if (hints == NULL) {
|
||||
memset (&hint, 0, sizeof hint);
|
||||
hint.ai_family = PF_UNSPEC;
|
||||
hints = &hint;
|
||||
}
|
||||
|
||||
if (servname == NULL)
|
||||
port = 0;
|
||||
else {
|
||||
/* check for tcp or udp sockets only */
|
||||
if (hints->ai_socktype == SOCK_STREAM)
|
||||
socktype = "tcp";
|
||||
else if (hints->ai_socktype == SOCK_DGRAM)
|
||||
socktype = "udp";
|
||||
else
|
||||
return EAI_SERVICE;
|
||||
result.ai_socktype = hints->ai_socktype;
|
||||
|
||||
/* Note: maintain port in host byte order to make debugging easier */
|
||||
if (isdigit (*servname))
|
||||
port = strtol (servname, NULL, 10);
|
||||
else if ((servent = getservbyname (servname, socktype)) != NULL)
|
||||
port = ntohs (servent->s_port);
|
||||
else
|
||||
return EAI_NONAME;
|
||||
}
|
||||
|
||||
/* if nodename == NULL refer to the local host for a client or any
|
||||
for a server */
|
||||
if (nodename == NULL) {
|
||||
struct sockaddr_in sin;
|
||||
|
||||
/* check protocol family is PF_UNSPEC or PF_INET - could try harder
|
||||
for IPv6 but that's more code than I'm prepared to write */
|
||||
if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
|
||||
result.ai_family = AF_INET;
|
||||
else
|
||||
return EAI_FAMILY;
|
||||
|
||||
sin.sin_family = result.ai_family;
|
||||
sin.sin_port = htons (port);
|
||||
if (hints->ai_flags & AI_PASSIVE)
|
||||
sin.sin_addr.s_addr = htonl (INADDR_ANY);
|
||||
else
|
||||
sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
|
||||
/* Duplicate result and addr and return */
|
||||
*res = dup_addrinfo (&result, &sin, sizeof sin);
|
||||
return (*res == NULL) ? EAI_MEMORY : 0;
|
||||
}
|
||||
|
||||
/* If AI_NUMERIC is specified, use inet_pton to translate numbers and
|
||||
dots notation. */
|
||||
if (hints->ai_flags & AI_NUMERICHOST) {
|
||||
struct sockaddr_in sin;
|
||||
|
||||
/* check protocol family is PF_UNSPEC or PF_INET */
|
||||
if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
|
||||
result.ai_family = AF_INET;
|
||||
else
|
||||
return EAI_FAMILY;
|
||||
|
||||
sin.sin_family = result.ai_family;
|
||||
sin.sin_port = htons (port);
|
||||
if (inet_pton(result.ai_family, nodename, &sin.sin_addr)==0)
|
||||
return EAI_NONAME;
|
||||
sin.sin_addr.s_addr = inet_addr (nodename);
|
||||
/* Duplicate result and addr and return */
|
||||
*res = dup_addrinfo (&result, &sin, sizeof sin);
|
||||
return (*res == NULL) ? EAI_MEMORY : 0;
|
||||
}
|
||||
|
||||
#if HAVE_H_ERRNO
|
||||
h_errno = 0;
|
||||
#endif
|
||||
errno = 0;
|
||||
hp = gethostbyname(nodename);
|
||||
if (hp == NULL) {
|
||||
#ifdef EAI_SYSTEM
|
||||
if (errno != 0) {
|
||||
return EAI_SYSTEM;
|
||||
}
|
||||
#endif
|
||||
switch (h_errno) {
|
||||
case HOST_NOT_FOUND:
|
||||
return EAI_NODATA;
|
||||
case NO_DATA:
|
||||
return EAI_NODATA;
|
||||
#if defined(NO_ADDRESS) && NO_ADDRESS != NO_DATA
|
||||
case NO_ADDRESS:
|
||||
return EAI_NODATA;
|
||||
#endif
|
||||
case NO_RECOVERY:
|
||||
return EAI_FAIL;
|
||||
case TRY_AGAIN:
|
||||
return EAI_AGAIN;
|
||||
default:
|
||||
return EAI_FAIL;
|
||||
}
|
||||
return EAI_FAIL;
|
||||
}
|
||||
|
||||
/* Check that the address family is acceptable.
|
||||
*/
|
||||
switch (hp->h_addrtype) {
|
||||
case AF_INET:
|
||||
if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET))
|
||||
return EAI_FAMILY;
|
||||
break;
|
||||
#ifndef __OS2__
|
||||
case AF_INET6:
|
||||
if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET6))
|
||||
return EAI_FAMILY;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return EAI_FAMILY;
|
||||
}
|
||||
|
||||
/* For each element pointed to by hp, create an element in the
|
||||
result linked list. */
|
||||
sai = eai = NULL;
|
||||
for (addrs = hp->h_addr_list; *addrs != NULL; addrs++) {
|
||||
struct sockaddr sa;
|
||||
size_t addrlen;
|
||||
|
||||
if (hp->h_length < 1)
|
||||
continue;
|
||||
sa.sa_family = hp->h_addrtype;
|
||||
switch (hp->h_addrtype) {
|
||||
case AF_INET:
|
||||
((struct sockaddr_in *) &sa)->sin_port = htons (port);
|
||||
memcpy (&((struct sockaddr_in *) &sa)->sin_addr,
|
||||
*addrs, hp->h_length);
|
||||
addrlen = sizeof (struct sockaddr_in);
|
||||
break;
|
||||
#ifndef __OS2__
|
||||
case AF_INET6:
|
||||
#if SIN6_LEN
|
||||
((struct sockaddr_in6 *) &sa)->sin6_len = hp->h_length;
|
||||
#endif
|
||||
((struct sockaddr_in6 *) &sa)->sin6_port = htons (port);
|
||||
memcpy (&((struct sockaddr_in6 *) &sa)->sin6_addr,
|
||||
*addrs, hp->h_length);
|
||||
addrlen = sizeof (struct sockaddr_in6);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
result.ai_family = hp->h_addrtype;
|
||||
ai = dup_addrinfo (&result, &sa, addrlen);
|
||||
if (ai == NULL) {
|
||||
freeaddrinfo (sai);
|
||||
return EAI_MEMORY;
|
||||
}
|
||||
if (sai == NULL)
|
||||
sai = ai;
|
||||
else
|
||||
eai->ai_next = ai;
|
||||
eai = ai;
|
||||
}
|
||||
|
||||
if (sai == NULL) {
|
||||
return EAI_NODATA;
|
||||
}
|
||||
|
||||
if (hints->ai_flags & AI_CANONNAME) {
|
||||
sai->ai_canonname = malloc (strlen (hp->h_name) + 1);
|
||||
if (sai->ai_canonname == NULL) {
|
||||
freeaddrinfo (sai);
|
||||
return EAI_MEMORY;
|
||||
}
|
||||
strcpy (sai->ai_canonname, hp->h_name);
|
||||
}
|
||||
|
||||
*res = sai;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
freeaddrinfo (struct addrinfo *ai)
|
||||
{
|
||||
struct addrinfo *next;
|
||||
|
||||
while (ai != NULL) {
|
||||
next = ai->ai_next;
|
||||
if (ai->ai_canonname != NULL)
|
||||
free (ai->ai_canonname);
|
||||
if (ai->ai_addr != NULL)
|
||||
free (ai->ai_addr);
|
||||
free (ai);
|
||||
ai = next;
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
gai_strerror (int ecode)
|
||||
{
|
||||
static const char *eai_descr[] = {
|
||||
"no error",
|
||||
"address family for nodename not supported", /* EAI_ADDRFAMILY */
|
||||
"temporary failure in name resolution", /* EAI_AGAIN */
|
||||
"invalid value for ai_flags", /* EAI_BADFLAGS */
|
||||
"non-recoverable failure in name resolution", /* EAI_FAIL */
|
||||
"ai_family not supported", /* EAI_FAMILY */
|
||||
"memory allocation failure", /* EAI_MEMORY */
|
||||
"no address associated with nodename", /* EAI_NODATA */
|
||||
"nodename nor servname provided, or not known", /* EAI_NONAME */
|
||||
"servname not supported for ai_socktype", /* EAI_SERVICE */
|
||||
"ai_socktype not supported", /* EAI_SOCKTYPE */
|
||||
"system error returned in errno", /* EAI_SYSTEM */
|
||||
"argument buffer overflow", /* EAI_OVERFLOW */
|
||||
};
|
||||
|
||||
if (ecode < 0 || ecode > (int) (sizeof eai_descr/ sizeof eai_descr[0]))
|
||||
return "unknown error";
|
||||
return eai_descr[ecode];
|
||||
}
|
||||
|
||||
#endif /* HAVE_GETADDRINFO */
|
||||
101
src/3rdparty/os2/getaddrinfo.h
vendored
Normal file
101
src/3rdparty/os2/getaddrinfo.h
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
#ifndef _getaddrinfo_h
|
||||
#define _getaddrinfo_h
|
||||
|
||||
/*
|
||||
* Shamelessly duplicated from the fetchmail public sources
|
||||
* for use by the Squid Project under GNU Public License.
|
||||
*
|
||||
* Update/Maintenance History:
|
||||
*
|
||||
* 15-Aug-2007 : Copied from fetchmail 6.3.8
|
||||
* - added protection around libray headers
|
||||
*
|
||||
* 16-Aug-2007 : Altered configure checks
|
||||
* Un-hacked slightly to use system gethostbyname()
|
||||
*
|
||||
* Original License and code follows.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of libESMTP, a library for submission of RFC 2822
|
||||
* formatted electronic mail messages using the SMTP protocol described
|
||||
* in RFC 2821.
|
||||
*
|
||||
* Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* Structure and prototypes taken from RFC 2553 */
|
||||
|
||||
/* SG 23/09/2007:
|
||||
On Windows the following definitions are already available, may be that
|
||||
this could be needed on some other platform */
|
||||
typedef int socklen_t;
|
||||
|
||||
struct addrinfo {
|
||||
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
|
||||
int ai_family; /* PF_xxx */
|
||||
int ai_socktype; /* SOCK_xxx */
|
||||
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
|
||||
socklen_t ai_addrlen; /* length of ai_addr */
|
||||
char *ai_canonname; /* canonical name for nodename */
|
||||
struct sockaddr *ai_addr; /* binary address */
|
||||
struct addrinfo *ai_next; /* next structure in linked list */
|
||||
};
|
||||
|
||||
/* Supposed to be defined in <netdb.h> */
|
||||
#define AI_ADDRCONFIG 0
|
||||
#define AI_PASSIVE 1 /* Socket address is intended for `bind'. */
|
||||
#define AI_CANONNAME 2 /* Request for canonical name. */
|
||||
#define AI_NUMERICHOST 4 /* Don't use name resolution. */
|
||||
|
||||
/* Supposed to be defined in <netdb.h> */
|
||||
#define EAI_ADDRFAMILY 1 /* address family for nodename not supported */
|
||||
#define EAI_AGAIN 2 /* temporary failure in name resolution */
|
||||
#define EAI_BADFLAGS 3 /* invalid value for ai_flags */
|
||||
#define EAI_FAIL 4 /* non-recoverable failure in name resolution */
|
||||
#define EAI_FAMILY 5 /* ai_family not supported */
|
||||
#define EAI_MEMORY 6 /* memory allocation failure */
|
||||
#define EAI_NODATA 7 /* no address associated with nodename */
|
||||
#define EAI_NONAME 8 /* nodename nor servname provided, or not known */
|
||||
#define EAI_SERVICE 9 /* servname not supported for ai_socktype */
|
||||
#define EAI_SOCKTYPE 10 /* ai_socktype not supported */
|
||||
|
||||
#ifndef EAI_SYSTEM
|
||||
/* Not defined on mingw32. */
|
||||
#define EAI_SYSTEM 11 /* System error returned in `errno'. */
|
||||
#endif
|
||||
#ifndef EAI_OVERFLOW
|
||||
/* Not defined on mingw32. */
|
||||
#define EAI_OVERFLOW 12 /* Argument buffer overflow. */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* RFC 2553 / Posix resolver */
|
||||
int getaddrinfo (const char *nodename, const char *servname,
|
||||
const struct addrinfo *hints, struct addrinfo **res);
|
||||
/* Free addrinfo structure and associated storage */
|
||||
void freeaddrinfo (struct addrinfo *ai);
|
||||
|
||||
/* Convert error return from getaddrinfo() to string */
|
||||
const char *gai_strerror (int code);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _getaddrinfo_h */
|
||||
367
src/3rdparty/os2/getnameinfo.c
vendored
Normal file
367
src/3rdparty/os2/getnameinfo.c
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Issues to be discussed:
|
||||
* - RFC2553 says that we should raise error on short buffer. X/Open says
|
||||
* we need to truncate the result. We obey RFC2553 (and X/Open should be
|
||||
* modified). ipngwg rough consensus seems to follow RFC2553. RFC3493 says
|
||||
* nothing about it, but defines a new error code EAI_OVERFLOW which seems
|
||||
* to be intended the code for this case.
|
||||
* - What is "local" in NI_NOFQDN? (see comments in the code)
|
||||
* - NI_NAMEREQD and NI_NUMERICHOST conflict with each other.
|
||||
* - (KAME extension) always attach textual scopeid (fe80::1%lo0), if
|
||||
* sin6_scope_id is filled - standardization status?
|
||||
* - what should we do if we should do getservbyport("sctp")?
|
||||
*/
|
||||
|
||||
/*
|
||||
* Considerations about thread-safeness
|
||||
* The code in this file is thread-safe, and so the thread-safeness of
|
||||
* getnameinfo() depends on the property of backend functions.
|
||||
* - getservbyport() is not thread safe for most systems we are targeting.
|
||||
* - getipnodebyaddr() is thread safe. However, many resolver libraries
|
||||
* used in the function are not thread safe.
|
||||
* - gethostbyaddr() is usually not thread safe.
|
||||
*/
|
||||
|
||||
#if !HAVE_GETNAMEINFO
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <netdb.h>
|
||||
#include <resolv.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include "getaddrinfo.h"
|
||||
#include "getnameinfo.h"
|
||||
|
||||
static const struct afd {
|
||||
int a_af;
|
||||
int a_addrlen;
|
||||
int a_socklen;
|
||||
int a_off;
|
||||
int a_portoff;
|
||||
} afdl [] = {
|
||||
#if INET6
|
||||
{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
|
||||
offsetof(struct sockaddr_in6, sin6_addr),
|
||||
offsetof(struct sockaddr_in6, sin6_port)},
|
||||
#endif
|
||||
{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
|
||||
offsetof(struct sockaddr_in, sin_addr),
|
||||
offsetof(struct sockaddr_in, sin_port)},
|
||||
{0, 0, 0, 0, 0},
|
||||
};
|
||||
|
||||
#if INET6
|
||||
static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
|
||||
size_t, int));
|
||||
static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int));
|
||||
#endif
|
||||
|
||||
int
|
||||
getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
|
||||
const struct sockaddr *sa;
|
||||
socklen_t salen;
|
||||
char *host;
|
||||
size_t hostlen;
|
||||
char *serv;
|
||||
size_t servlen;
|
||||
int flags;
|
||||
{
|
||||
const struct afd *afd;
|
||||
struct servent *sp;
|
||||
struct hostent *hp;
|
||||
unsigned short port;
|
||||
int family, i;
|
||||
const char *addr;
|
||||
uint32_t v4a;
|
||||
char numserv[512];
|
||||
|
||||
if (sa == NULL)
|
||||
return EAI_FAIL;
|
||||
|
||||
#if HAVE_SA_LEN /*XXX*/
|
||||
if (sa->sa_len != salen)
|
||||
return EAI_FAIL;
|
||||
#endif
|
||||
|
||||
family = sa->sa_family;
|
||||
for (i = 0; afdl[i].a_af; i++)
|
||||
if (afdl[i].a_af == family) {
|
||||
afd = &afdl[i];
|
||||
goto found;
|
||||
}
|
||||
return EAI_FAMILY;
|
||||
|
||||
found:
|
||||
if (salen != afd->a_socklen)
|
||||
return EAI_FAIL;
|
||||
|
||||
/* network byte order */
|
||||
memcpy(&port, (const char *)sa + afd->a_portoff, sizeof(port));
|
||||
addr = (const char *)sa + afd->a_off;
|
||||
|
||||
if (serv == NULL || servlen == 0) {
|
||||
/*
|
||||
* do nothing in this case.
|
||||
* in case you are wondering if "&&" is more correct than
|
||||
* "||" here: RFC3493 says that serv == NULL OR servlen == 0
|
||||
* means that the caller does not want the result.
|
||||
*/
|
||||
} else {
|
||||
if (flags & NI_NUMERICSERV)
|
||||
sp = NULL;
|
||||
else {
|
||||
sp = getservbyport(port,
|
||||
(flags & NI_DGRAM) ? "udp" : "tcp");
|
||||
}
|
||||
if (sp) {
|
||||
if (strlen(sp->s_name) + 1 > servlen)
|
||||
return EAI_OVERFLOW;
|
||||
strncpy(serv, sp->s_name, servlen);
|
||||
} else {
|
||||
snprintf(numserv, sizeof(numserv), "%u", ntohs(port));
|
||||
if (strlen(numserv) + 1 > servlen)
|
||||
return EAI_OVERFLOW;
|
||||
strncpy(serv, numserv, servlen);
|
||||
}
|
||||
}
|
||||
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
v4a = (uint32_t)
|
||||
ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr);
|
||||
if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
|
||||
flags |= NI_NUMERICHOST;
|
||||
v4a >>= IN_CLASSA_NSHIFT;
|
||||
if (v4a == 0)
|
||||
flags |= NI_NUMERICHOST;
|
||||
break;
|
||||
#if INET6
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *sin6;
|
||||
sin6 = (const struct sockaddr_in6 *)sa;
|
||||
switch (sin6->sin6_addr.s6_addr[0]) {
|
||||
case 0x00:
|
||||
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
|
||||
;
|
||||
else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
|
||||
;
|
||||
else
|
||||
flags |= NI_NUMERICHOST;
|
||||
break;
|
||||
default:
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
|
||||
flags |= NI_NUMERICHOST;
|
||||
else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
|
||||
flags |= NI_NUMERICHOST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
if (host == NULL || hostlen == 0) {
|
||||
/*
|
||||
* do nothing in this case.
|
||||
* in case you are wondering if "&&" is more correct than
|
||||
* "||" here: RFC3493 says that host == NULL or hostlen == 0
|
||||
* means that the caller does not want the result.
|
||||
*/
|
||||
} else if (flags & NI_NUMERICHOST) {
|
||||
/* NUMERICHOST and NAMEREQD conflicts with each other */
|
||||
if (flags & NI_NAMEREQD)
|
||||
return EAI_NONAME;
|
||||
|
||||
goto numeric;
|
||||
} else {
|
||||
#if USE_GETIPNODEBY
|
||||
int h_error = 0;
|
||||
hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
|
||||
#else
|
||||
hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
|
||||
#if 0 // getnameinfo.c:161:9: error: variable 'h_error' set but not used
|
||||
#if HAVE_H_ERRNO
|
||||
h_error = h_errno;
|
||||
#else
|
||||
h_error = EINVAL;
|
||||
#endif
|
||||
#endif /* 0 */
|
||||
#endif
|
||||
|
||||
if (hp) {
|
||||
#if 0
|
||||
if (flags & NI_NOFQDN) {
|
||||
/*
|
||||
* According to RFC3493 section 6.2, NI_NOFQDN
|
||||
* means "node name portion of the FQDN shall
|
||||
* be returned for local hosts." The following
|
||||
* code tries to implement it by returning the
|
||||
* first label (the part before the first
|
||||
* period) of the FQDN. However, it is not
|
||||
* clear if this always makes sense, since the
|
||||
* given address may be outside of "local
|
||||
* hosts." Due to the unclear description, we
|
||||
* disable the code in this implementation.
|
||||
*/
|
||||
char *p;
|
||||
p = strchr(hp->h_name, '.');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
}
|
||||
#endif
|
||||
if (strlen(hp->h_name) + 1 > hostlen) {
|
||||
#if USE_GETIPNODEBY
|
||||
freehostent(hp);
|
||||
#endif
|
||||
return EAI_OVERFLOW;
|
||||
}
|
||||
strncpy(host, hp->h_name, hostlen);
|
||||
#if USE_GETIPNODEBY
|
||||
freehostent(hp);
|
||||
#endif
|
||||
} else {
|
||||
if (flags & NI_NAMEREQD)
|
||||
return EAI_NONAME;
|
||||
|
||||
numeric:
|
||||
switch (afd->a_af) {
|
||||
#if INET6
|
||||
case AF_INET6: {
|
||||
int error;
|
||||
|
||||
if ((error = ip6_parsenumeric(sa, addr, host,
|
||||
hostlen,
|
||||
flags)) != 0)
|
||||
return(error);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
if (inet_ntop(afd->a_af, addr, host,
|
||||
hostlen) == NULL)
|
||||
return EAI_SYSTEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
#if INET6
|
||||
static int
|
||||
ip6_parsenumeric(sa, addr, host, hostlen, flags)
|
||||
const struct sockaddr *sa;
|
||||
const char *addr;
|
||||
char *host;
|
||||
size_t hostlen;
|
||||
int flags;
|
||||
{
|
||||
int numaddrlen;
|
||||
char numaddr[512];
|
||||
|
||||
if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL)
|
||||
return EAI_SYSTEM;
|
||||
|
||||
numaddrlen = strlen(numaddr);
|
||||
if (numaddrlen + 1 > hostlen) /* don't forget terminator */
|
||||
return EAI_OVERFLOW;
|
||||
strncpy(host, numaddr, hostlen);
|
||||
|
||||
if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
|
||||
char zonebuf[SQUIDHOSTNAMELEN];
|
||||
int zonelen;
|
||||
|
||||
zonelen = ip6_sa2str(
|
||||
(const struct sockaddr_in6 *)(const void *)sa,
|
||||
zonebuf, sizeof(zonebuf), flags);
|
||||
if (zonelen < 0)
|
||||
return EAI_OVERFLOW;
|
||||
if (zonelen + 1 + numaddrlen + 1 > hostlen)
|
||||
return EAI_OVERFLOW;
|
||||
|
||||
/* construct <numeric-addr><delim><zoneid> */
|
||||
memcpy(host + numaddrlen + 1, zonebuf,
|
||||
(size_t)zonelen);
|
||||
host[numaddrlen] = SCOPE_DELIMITER;
|
||||
host[numaddrlen + 1 + zonelen] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
ip6_sa2str(sa6, buf, bufsiz, flags)
|
||||
const struct sockaddr_in6 *sa6;
|
||||
char *buf;
|
||||
size_t bufsiz;
|
||||
int flags;
|
||||
{
|
||||
unsigned int ifindex;
|
||||
const struct in6_addr *a6;
|
||||
int n;
|
||||
|
||||
ifindex = (unsigned int)sa6->sin6_scope_id;
|
||||
a6 = &sa6->sin6_addr;
|
||||
|
||||
#if NI_NUMERICSCOPE
|
||||
if ((flags & NI_NUMERICSCOPE) != 0) {
|
||||
n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
|
||||
if (n < 0 || n >= bufsiz)
|
||||
return -1;
|
||||
else
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if_indextoname() does not take buffer size. not a good api... */
|
||||
if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) ||
|
||||
IN6_IS_ADDR_MC_NODELOCAL(a6)) && bufsiz >= IF_NAMESIZE) {
|
||||
char *p = if_indextoname(ifindex, buf);
|
||||
if (p)
|
||||
return (strlen(p));
|
||||
}
|
||||
|
||||
/* last resort */
|
||||
n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
|
||||
if (n < 0 || n >= bufsiz)
|
||||
return -1;
|
||||
else
|
||||
return n;
|
||||
}
|
||||
#endif /* INET6 */
|
||||
#endif
|
||||
29
src/3rdparty/os2/getnameinfo.h
vendored
Normal file
29
src/3rdparty/os2/getnameinfo.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef _getnameinfo_h
|
||||
#define _getnameinfo_h
|
||||
/*
|
||||
* Reconstructed from KAME getnameinfo.c (in lib/)
|
||||
*/
|
||||
|
||||
/* getnameinfo flags */
|
||||
#define NI_NOFQDN 0x0001
|
||||
#define NI_NUMERICHOST 0x0002 /* return numeric form of address */
|
||||
#define NI_NAMEREQD 0x0004 /* request DNS name */
|
||||
#define NI_NUMERICSERV 0x0008
|
||||
#define NI_DGRAM 0x0010
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* RFC 2553 / Posix resolver */
|
||||
int getnameinfo(const struct sockaddr *sa,
|
||||
socklen_t salen,
|
||||
char *host,
|
||||
size_t hostlen,
|
||||
char *serv,
|
||||
size_t servlen,
|
||||
int flags );
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _getnameinfo_h */
|
||||
29
src/3rdparty/squirrel/COPYRIGHT
vendored
Normal file
29
src/3rdparty/squirrel/COPYRIGHT
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
Copyright (c) 2003-2011 Alberto Demichelis
|
||||
|
||||
This software is provided 'as-is', without any
|
||||
express or implied warranty. In no event will the
|
||||
authors be held liable for any damages arising from
|
||||
the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software
|
||||
for any purpose, including commercial applications,
|
||||
and to alter it and redistribute it freely, subject
|
||||
to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that
|
||||
you wrote the original software. If you
|
||||
use this software in a product, an
|
||||
acknowledgment in the product
|
||||
documentation would be appreciated but is
|
||||
not required.
|
||||
|
||||
2. Altered source versions must be plainly
|
||||
marked as such, and must not be
|
||||
misrepresented as being the original
|
||||
software.
|
||||
|
||||
3. This notice may not be removed or
|
||||
altered from any source distribution.
|
||||
-----------------------------------------------------
|
||||
END OF COPYRIGHT
|
||||
4
src/3rdparty/squirrel/README.OpenTTD
vendored
Normal file
4
src/3rdparty/squirrel/README.OpenTTD
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
This folder contains a modified version of Squirrel that is tailored to meet
|
||||
the needs of OpenTTD.
|
||||
We have based this modification on the version as described in:
|
||||
include/squirrel.h
|
||||
8
src/3rdparty/squirrel/include/sqstdaux.h
vendored
Normal file
8
src/3rdparty/squirrel/include/sqstdaux.h
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQSTD_AUXLIB_H_
|
||||
#define _SQSTD_AUXLIB_H_
|
||||
|
||||
void sqstd_seterrorhandlers(HSQUIRRELVM v);
|
||||
void sqstd_printcallstack(HSQUIRRELVM v);
|
||||
|
||||
#endif /* _SQSTD_AUXLIB_H_ */
|
||||
7
src/3rdparty/squirrel/include/sqstdmath.h
vendored
Normal file
7
src/3rdparty/squirrel/include/sqstdmath.h
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQSTD_MATH_H_
|
||||
#define _SQSTD_MATH_H_
|
||||
|
||||
SQRESULT sqstd_register_mathlib(HSQUIRRELVM v);
|
||||
|
||||
#endif /*_SQSTD_MATH_H_*/
|
||||
25
src/3rdparty/squirrel/include/sqstdstring.h
vendored
Normal file
25
src/3rdparty/squirrel/include/sqstdstring.h
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQSTD_STRING_H_
|
||||
#define _SQSTD_STRING_H_
|
||||
|
||||
typedef unsigned int SQRexBool;
|
||||
typedef struct SQRex SQRex;
|
||||
|
||||
typedef struct {
|
||||
const SQChar *begin;
|
||||
SQInteger len;
|
||||
} SQRexMatch;
|
||||
|
||||
SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error);
|
||||
void sqstd_rex_free(SQRex *exp);
|
||||
SQBool sqstd_rex_match(SQRex* exp,const SQChar* text);
|
||||
SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end);
|
||||
SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end);
|
||||
SQInteger sqstd_rex_getsubexpcount(SQRex* exp);
|
||||
SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp);
|
||||
|
||||
SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output);
|
||||
|
||||
SQRESULT sqstd_register_stringlib(HSQUIRRELVM v);
|
||||
|
||||
#endif /*_SQSTD_STRING_H_*/
|
||||
371
src/3rdparty/squirrel/include/squirrel.h
vendored
Normal file
371
src/3rdparty/squirrel/include/squirrel.h
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2011 Alberto Demichelis
|
||||
*
|
||||
* This software is provided 'as-is', without any
|
||||
* express or implied warranty. In no event will the
|
||||
* authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software
|
||||
* for any purpose, including commercial applications,
|
||||
* and to alter it and redistribute it freely, subject
|
||||
* to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be
|
||||
* misrepresented; you must not claim that
|
||||
* you wrote the original software. If you
|
||||
* use this software in a product, an
|
||||
* acknowledgment in the product
|
||||
* documentation would be appreciated but is
|
||||
* not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly
|
||||
* marked as such, and must not be
|
||||
* misrepresented as being the original
|
||||
* software.
|
||||
*
|
||||
* 3. This notice may not be removed or
|
||||
* altered from any source distribution.
|
||||
*/
|
||||
#ifndef _SQUIRREL_H_
|
||||
#define _SQUIRREL_H_
|
||||
|
||||
#include "../../../string_type.h"
|
||||
|
||||
typedef __int64 SQInteger;
|
||||
typedef unsigned __int64 SQUnsignedInteger;
|
||||
typedef unsigned __int64 SQHash; /*should be the same size of a pointer*/
|
||||
typedef int SQInt32;
|
||||
|
||||
|
||||
#ifdef SQUSEDOUBLE
|
||||
typedef double SQFloat;
|
||||
#else
|
||||
typedef float SQFloat;
|
||||
#endif
|
||||
|
||||
typedef __int64 SQRawObjectVal; //must be 64bits
|
||||
#define SQ_OBJECT_RAWINIT() { _unVal.raw = 0; }
|
||||
|
||||
typedef void* SQUserPointer;
|
||||
typedef SQUnsignedInteger SQBool;
|
||||
typedef SQInteger SQRESULT;
|
||||
|
||||
#define SQTrue (1)
|
||||
#define SQFalse (0)
|
||||
|
||||
struct SQVM;
|
||||
struct SQTable;
|
||||
struct SQArray;
|
||||
struct SQString;
|
||||
struct SQClosure;
|
||||
struct SQGenerator;
|
||||
struct SQNativeClosure;
|
||||
struct SQUserData;
|
||||
struct SQFunctionProto;
|
||||
struct SQRefCounted;
|
||||
struct SQClass;
|
||||
struct SQInstance;
|
||||
struct SQDelegable;
|
||||
|
||||
typedef char SQChar;
|
||||
#define MAX_CHAR 0xFFFF
|
||||
|
||||
#define SQUIRREL_VERSION "Squirrel 2.2.5 stable - With custom OpenTTD modifications"
|
||||
#define SQUIRREL_COPYRIGHT "Copyright (C) 2003-2010 Alberto Demichelis"
|
||||
#define SQUIRREL_AUTHOR "Alberto Demichelis"
|
||||
#define SQUIRREL_VERSION_NUMBER 225
|
||||
|
||||
#define SQ_VMSTATE_IDLE 0
|
||||
#define SQ_VMSTATE_RUNNING 1
|
||||
#define SQ_VMSTATE_SUSPENDED 2
|
||||
|
||||
#define SQUIRREL_EOB 0
|
||||
#define SQ_BYTECODE_STREAM_TAG 0xFAFA
|
||||
|
||||
#define SQOBJECT_REF_COUNTED 0x08000000
|
||||
#define SQOBJECT_NUMERIC 0x04000000
|
||||
#define SQOBJECT_DELEGABLE 0x02000000
|
||||
#define SQOBJECT_CANBEFALSE 0x01000000
|
||||
|
||||
#define SQ_MATCHTYPEMASKSTRING (-99999)
|
||||
|
||||
#define _RT_MASK 0x00FFFFFF
|
||||
#define _RAW_TYPE(type) (type&_RT_MASK)
|
||||
|
||||
#define _RT_NULL 0x00000001
|
||||
#define _RT_INTEGER 0x00000002
|
||||
#define _RT_FLOAT 0x00000004
|
||||
#define _RT_BOOL 0x00000008
|
||||
#define _RT_STRING 0x00000010
|
||||
#define _RT_TABLE 0x00000020
|
||||
#define _RT_ARRAY 0x00000040
|
||||
#define _RT_USERDATA 0x00000080
|
||||
#define _RT_CLOSURE 0x00000100
|
||||
#define _RT_NATIVECLOSURE 0x00000200
|
||||
#define _RT_GENERATOR 0x00000400
|
||||
#define _RT_USERPOINTER 0x00000800
|
||||
#define _RT_THREAD 0x00001000
|
||||
#define _RT_FUNCPROTO 0x00002000
|
||||
#define _RT_CLASS 0x00004000
|
||||
#define _RT_INSTANCE 0x00008000
|
||||
#define _RT_WEAKREF 0x00010000
|
||||
|
||||
typedef enum tagSQObjectType{
|
||||
OT_NULL = (_RT_NULL|SQOBJECT_CANBEFALSE),
|
||||
OT_INTEGER = (_RT_INTEGER|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE),
|
||||
OT_FLOAT = (_RT_FLOAT|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE),
|
||||
OT_BOOL = (_RT_BOOL|SQOBJECT_CANBEFALSE),
|
||||
OT_STRING = (_RT_STRING|SQOBJECT_REF_COUNTED),
|
||||
OT_TABLE = (_RT_TABLE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
|
||||
OT_ARRAY = (_RT_ARRAY|SQOBJECT_REF_COUNTED),
|
||||
OT_USERDATA = (_RT_USERDATA|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
|
||||
OT_CLOSURE = (_RT_CLOSURE|SQOBJECT_REF_COUNTED),
|
||||
OT_NATIVECLOSURE = (_RT_NATIVECLOSURE|SQOBJECT_REF_COUNTED),
|
||||
OT_GENERATOR = (_RT_GENERATOR|SQOBJECT_REF_COUNTED),
|
||||
OT_USERPOINTER = _RT_USERPOINTER,
|
||||
OT_THREAD = (_RT_THREAD|SQOBJECT_REF_COUNTED) ,
|
||||
OT_FUNCPROTO = (_RT_FUNCPROTO|SQOBJECT_REF_COUNTED), //internal usage only
|
||||
OT_CLASS = (_RT_CLASS|SQOBJECT_REF_COUNTED),
|
||||
OT_INSTANCE = (_RT_INSTANCE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
|
||||
OT_WEAKREF = (_RT_WEAKREF|SQOBJECT_REF_COUNTED)
|
||||
}SQObjectType;
|
||||
|
||||
#define ISREFCOUNTED(t) (t&SQOBJECT_REF_COUNTED)
|
||||
|
||||
|
||||
typedef union tagSQObjectValue
|
||||
{
|
||||
struct SQTable *pTable;
|
||||
struct SQArray *pArray;
|
||||
struct SQClosure *pClosure;
|
||||
struct SQGenerator *pGenerator;
|
||||
struct SQNativeClosure *pNativeClosure;
|
||||
struct SQString *pString;
|
||||
struct SQUserData *pUserData;
|
||||
SQInteger nInteger;
|
||||
SQFloat fFloat;
|
||||
SQUserPointer pUserPointer;
|
||||
struct SQFunctionProto *pFunctionProto;
|
||||
struct SQRefCounted *pRefCounted;
|
||||
struct SQDelegable *pDelegable;
|
||||
struct SQVM *pThread;
|
||||
struct SQClass *pClass;
|
||||
struct SQInstance *pInstance;
|
||||
struct SQWeakRef *pWeakRef;
|
||||
SQRawObjectVal raw;
|
||||
}SQObjectValue;
|
||||
|
||||
|
||||
typedef struct tagSQObject
|
||||
{
|
||||
SQObjectType _type;
|
||||
SQObjectValue _unVal;
|
||||
}SQObject;
|
||||
|
||||
typedef struct tagSQStackInfos{
|
||||
const SQChar* funcname;
|
||||
const SQChar* source;
|
||||
SQInteger line;
|
||||
}SQStackInfos;
|
||||
|
||||
typedef struct SQVM* HSQUIRRELVM;
|
||||
typedef SQObject HSQOBJECT;
|
||||
typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM);
|
||||
typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size);
|
||||
typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,const SQChar * /*desc*/,const SQChar * /*source*/,SQInteger /*line*/,SQInteger /*column*/);
|
||||
typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,const SQChar * ,...);
|
||||
|
||||
typedef SQInteger (*SQWRITEFUNC)(SQUserPointer,SQUserPointer,SQInteger);
|
||||
typedef SQInteger (*SQREADFUNC)(SQUserPointer,SQUserPointer,SQInteger);
|
||||
|
||||
typedef WChar (*SQLEXREADFUNC)(SQUserPointer);
|
||||
|
||||
typedef struct tagSQRegFunction{
|
||||
const SQChar *name;
|
||||
SQFUNCTION f;
|
||||
SQInteger nparamscheck;
|
||||
const SQChar *typemask;
|
||||
}SQRegFunction;
|
||||
|
||||
typedef struct tagSQFunctionInfo {
|
||||
SQUserPointer funcid;
|
||||
const SQChar *name;
|
||||
const SQChar *source;
|
||||
}SQFunctionInfo;
|
||||
|
||||
|
||||
/*vm*/
|
||||
bool sq_can_suspend(HSQUIRRELVM v);
|
||||
HSQUIRRELVM sq_open(SQInteger initialstacksize);
|
||||
HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize);
|
||||
void sq_seterrorhandler(HSQUIRRELVM v);
|
||||
void sq_close(HSQUIRRELVM v);
|
||||
void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p);
|
||||
SQUserPointer sq_getforeignptr(HSQUIRRELVM v);
|
||||
void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc);
|
||||
SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v);
|
||||
SQRESULT sq_suspendvm(HSQUIRRELVM v);
|
||||
bool sq_resumecatch(HSQUIRRELVM v, int suspend = -1);
|
||||
bool sq_resumeerror(HSQUIRRELVM v);
|
||||
SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool resumedret,SQBool retval,SQBool raiseerror,SQBool throwerror);
|
||||
SQInteger sq_getvmstate(HSQUIRRELVM v);
|
||||
void sq_decreaseops(HSQUIRRELVM v, int amount);
|
||||
|
||||
/*compiler*/
|
||||
SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror);
|
||||
SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror);
|
||||
void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable);
|
||||
void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable);
|
||||
void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f);
|
||||
|
||||
/*stack operations*/
|
||||
void sq_push(HSQUIRRELVM v,SQInteger idx);
|
||||
void sq_pop(HSQUIRRELVM v,SQInteger nelemstopop);
|
||||
void sq_poptop(HSQUIRRELVM v);
|
||||
void sq_remove(HSQUIRRELVM v,SQInteger idx);
|
||||
SQInteger sq_gettop(HSQUIRRELVM v);
|
||||
void sq_settop(HSQUIRRELVM v,SQInteger newtop);
|
||||
void sq_reservestack(HSQUIRRELVM v,SQInteger nsize);
|
||||
SQInteger sq_cmp(HSQUIRRELVM v);
|
||||
void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx);
|
||||
|
||||
/*object creation handling*/
|
||||
SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size);
|
||||
void sq_newtable(HSQUIRRELVM v);
|
||||
void sq_newarray(HSQUIRRELVM v,SQInteger size);
|
||||
void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars);
|
||||
SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask);
|
||||
SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx);
|
||||
void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len);
|
||||
void sq_pushfloat(HSQUIRRELVM v,SQFloat f);
|
||||
void sq_pushinteger(HSQUIRRELVM v,SQInteger n);
|
||||
void sq_pushbool(HSQUIRRELVM v,SQBool b);
|
||||
void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p);
|
||||
void sq_pushnull(HSQUIRRELVM v);
|
||||
SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx);
|
||||
SQInteger sq_getsize(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx);
|
||||
SQBool sq_instanceof(HSQUIRRELVM v);
|
||||
void sq_tostring(HSQUIRRELVM v,SQInteger idx);
|
||||
void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b);
|
||||
SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c);
|
||||
SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i);
|
||||
SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f);
|
||||
SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b);
|
||||
SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread);
|
||||
SQRESULT sq_getuserpointer(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p);
|
||||
SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag);
|
||||
SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag);
|
||||
SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag);
|
||||
void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook);
|
||||
SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize);
|
||||
SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger idx,SQFunctionInfo *fi);
|
||||
SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars);
|
||||
SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name);
|
||||
SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p);
|
||||
SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag);
|
||||
SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize);
|
||||
SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase);
|
||||
SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx);
|
||||
void sq_weakref(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t);
|
||||
|
||||
/*object manipulation*/
|
||||
void sq_pushroottable(HSQUIRRELVM v);
|
||||
void sq_pushregistrytable(HSQUIRRELVM v);
|
||||
void sq_pushconsttable(HSQUIRRELVM v);
|
||||
SQRESULT sq_setroottable(HSQUIRRELVM v);
|
||||
SQRESULT sq_setconsttable(HSQUIRRELVM v);
|
||||
SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic);
|
||||
SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval);
|
||||
SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval);
|
||||
SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval);
|
||||
SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize);
|
||||
SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_arrayremove(HSQUIRRELVM v,SQInteger idx,SQInteger itemidx);
|
||||
SQRESULT sq_arrayinsert(HSQUIRRELVM v,SQInteger idx,SQInteger destpos);
|
||||
SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
|
||||
SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx);
|
||||
SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx);
|
||||
|
||||
/*calls*/
|
||||
SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror, int suspend = -1);
|
||||
SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror);
|
||||
const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx);
|
||||
const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
|
||||
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err);
|
||||
void sq_reseterror(HSQUIRRELVM v);
|
||||
void sq_getlasterror(HSQUIRRELVM v);
|
||||
|
||||
/*raw object handling*/
|
||||
SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po);
|
||||
void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj);
|
||||
void sq_addref(HSQUIRRELVM v,HSQOBJECT *po);
|
||||
SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po);
|
||||
void sq_resetobject(HSQOBJECT *po);
|
||||
const SQChar *sq_objtostring(HSQOBJECT *o);
|
||||
SQBool sq_objtobool(HSQOBJECT *o);
|
||||
SQInteger sq_objtointeger(HSQOBJECT *o);
|
||||
SQFloat sq_objtofloat(HSQOBJECT *o);
|
||||
SQRESULT sq_getobjtypetag(HSQOBJECT *o,SQUserPointer * typetag);
|
||||
|
||||
/*GC*/
|
||||
SQInteger sq_collectgarbage(HSQUIRRELVM v);
|
||||
|
||||
/*serialization*/
|
||||
SQRESULT sq_writeclosure(HSQUIRRELVM vm,SQWRITEFUNC writef,SQUserPointer up);
|
||||
SQRESULT sq_readclosure(HSQUIRRELVM vm,SQREADFUNC readf,SQUserPointer up);
|
||||
|
||||
/*mem allocation*/
|
||||
void *sq_malloc(SQUnsignedInteger size);
|
||||
void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize);
|
||||
void sq_free(void *p,SQUnsignedInteger size);
|
||||
|
||||
/*debug*/
|
||||
SQRESULT sq_stackinfos(HSQUIRRELVM v,SQInteger level,SQStackInfos *si);
|
||||
void sq_setdebughook(HSQUIRRELVM v);
|
||||
|
||||
/*UTILITY MACRO*/
|
||||
#define sq_isnumeric(o) ((o)._type&SQOBJECT_NUMERIC)
|
||||
#define sq_istable(o) ((o)._type==OT_TABLE)
|
||||
#define sq_isarray(o) ((o)._type==OT_ARRAY)
|
||||
#define sq_isfunction(o) ((o)._type==OT_FUNCPROTO)
|
||||
#define sq_isclosure(o) ((o)._type==OT_CLOSURE)
|
||||
#define sq_isgenerator(o) ((o)._type==OT_GENERATOR)
|
||||
#define sq_isnativeclosure(o) ((o)._type==OT_NATIVECLOSURE)
|
||||
#define sq_isstring(o) ((o)._type==OT_STRING)
|
||||
#define sq_isinteger(o) ((o)._type==OT_INTEGER)
|
||||
#define sq_isfloat(o) ((o)._type==OT_FLOAT)
|
||||
#define sq_isuserpointer(o) ((o)._type==OT_USERPOINTER)
|
||||
#define sq_isuserdata(o) ((o)._type==OT_USERDATA)
|
||||
#define sq_isthread(o) ((o)._type==OT_THREAD)
|
||||
#define sq_isnull(o) ((o)._type==OT_NULL)
|
||||
#define sq_isclass(o) ((o)._type==OT_CLASS)
|
||||
#define sq_isinstance(o) ((o)._type==OT_INSTANCE)
|
||||
#define sq_isbool(o) ((o)._type==OT_BOOL)
|
||||
#define sq_isweakref(o) ((o)._type==OT_WEAKREF)
|
||||
#define sq_type(o) ((o)._type)
|
||||
|
||||
/* deprecated */
|
||||
#define sq_createslot(v,n) sq_newslot(v,n,SQFalse)
|
||||
|
||||
#define SQ_OK (0)
|
||||
#define SQ_ERROR (-1)
|
||||
|
||||
#define SQ_FAILED(res) (res<0)
|
||||
#define SQ_SUCCEEDED(res) (res>=0)
|
||||
|
||||
#endif /*_SQUIRREL_H_*/
|
||||
146
src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp
vendored
Normal file
146
src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include <squirrel.h>
|
||||
#include <sqstdaux.h>
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
void sqstd_printcallstack(HSQUIRRELVM v)
|
||||
{
|
||||
SQPRINTFUNCTION pf = sq_getprintfunc(v);
|
||||
if(pf) {
|
||||
SQStackInfos si;
|
||||
SQInteger i;
|
||||
SQBool b;
|
||||
SQFloat f;
|
||||
const SQChar *s;
|
||||
SQInteger level=1; //1 is to skip this function that is level 0
|
||||
const SQChar *name=0;
|
||||
SQInteger seq=0;
|
||||
pf(v,"\nCALLSTACK\n");
|
||||
while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si)))
|
||||
{
|
||||
const SQChar *fn="unknown";
|
||||
const SQChar *src="unknown";
|
||||
if(si.funcname)fn=si.funcname;
|
||||
if(si.source) {
|
||||
/* We don't want to bother users with absolute paths to all AI files.
|
||||
* Since the path only reaches NoAI code in a formatted string we have
|
||||
* to strip it here. Let's hope nobody installs openttd in a subdirectory
|
||||
* of a directory named /ai/. */
|
||||
src = strstr(si.source, "\\ai\\");
|
||||
if (!src) src = strstr(si.source, "/ai/");
|
||||
if (src) {
|
||||
src += 4;
|
||||
} else {
|
||||
src = si.source;
|
||||
}
|
||||
}
|
||||
pf(v,"*FUNCTION [%s()] %s line [%d]\n",fn,src,si.line);
|
||||
level++;
|
||||
}
|
||||
level=0;
|
||||
pf(v,"\nLOCALS\n");
|
||||
|
||||
for(level=0;level<10;level++){
|
||||
seq=0;
|
||||
while((name = sq_getlocal(v,level,seq)))
|
||||
{
|
||||
seq++;
|
||||
switch(sq_gettype(v,-1))
|
||||
{
|
||||
case OT_NULL:
|
||||
pf(v,"[%s] NULL\n",name);
|
||||
break;
|
||||
case OT_INTEGER:
|
||||
sq_getinteger(v,-1,&i);
|
||||
pf(v,"[%s] %d\n",name,i);
|
||||
break;
|
||||
case OT_FLOAT:
|
||||
sq_getfloat(v,-1,&f);
|
||||
pf(v,"[%s] %.14g\n",name,f);
|
||||
break;
|
||||
case OT_USERPOINTER:
|
||||
pf(v,"[%s] USERPOINTER\n",name);
|
||||
break;
|
||||
case OT_STRING:
|
||||
sq_getstring(v,-1,&s);
|
||||
pf(v,"[%s] \"%s\"\n",name,s);
|
||||
break;
|
||||
case OT_TABLE:
|
||||
pf(v,"[%s] TABLE\n",name);
|
||||
break;
|
||||
case OT_ARRAY:
|
||||
pf(v,"[%s] ARRAY\n",name);
|
||||
break;
|
||||
case OT_CLOSURE:
|
||||
pf(v,"[%s] CLOSURE\n",name);
|
||||
break;
|
||||
case OT_NATIVECLOSURE:
|
||||
pf(v,"[%s] NATIVECLOSURE\n",name);
|
||||
break;
|
||||
case OT_GENERATOR:
|
||||
pf(v,"[%s] GENERATOR\n",name);
|
||||
break;
|
||||
case OT_USERDATA:
|
||||
pf(v,"[%s] USERDATA\n",name);
|
||||
break;
|
||||
case OT_THREAD:
|
||||
pf(v,"[%s] THREAD\n",name);
|
||||
break;
|
||||
case OT_CLASS:
|
||||
pf(v,"[%s] CLASS\n",name);
|
||||
break;
|
||||
case OT_INSTANCE:
|
||||
pf(v,"[%s] INSTANCE\n",name);
|
||||
break;
|
||||
case OT_WEAKREF:
|
||||
pf(v,"[%s] WEAKREF\n",name);
|
||||
break;
|
||||
case OT_BOOL:{
|
||||
sq_getbool(v,-1,&b);
|
||||
pf(v,"[%s] %s\n",name,b?"true":"false");
|
||||
}
|
||||
break;
|
||||
default: assert(0); break;
|
||||
}
|
||||
sq_pop(v,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v)
|
||||
{
|
||||
SQPRINTFUNCTION pf = sq_getprintfunc(v);
|
||||
if(pf) {
|
||||
const SQChar *sErr = 0;
|
||||
if(sq_gettop(v)>=1) {
|
||||
if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) {
|
||||
pf(v,"\nAN ERROR HAS OCCURED [%s]\n",sErr);
|
||||
}
|
||||
else{
|
||||
pf(v,"\nAN ERROR HAS OCCURED [unknown]\n");
|
||||
}
|
||||
sqstd_printcallstack(v);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSource,SQInteger line,SQInteger column)
|
||||
{
|
||||
SQPRINTFUNCTION pf = sq_getprintfunc(v);
|
||||
if(pf) {
|
||||
pf(v,"%s line = (%d) column = (%d) : error %s\n",sSource,line,column,sErr);
|
||||
}
|
||||
}
|
||||
|
||||
void sqstd_seterrorhandlers(HSQUIRRELVM v)
|
||||
{
|
||||
sq_setcompilererrorhandler(v,_sqstd_compiler_error);
|
||||
sq_newclosure(v,_sqstd_aux_printerror,0);
|
||||
sq_seterrorhandler(v);
|
||||
}
|
||||
118
src/3rdparty/squirrel/sqstdlib/sqstdmath.cpp
vendored
Normal file
118
src/3rdparty/squirrel/sqstdlib/sqstdmath.cpp
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include <squirrel.h>
|
||||
#include <math.h>
|
||||
#include <sqstdmath.h>
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
#define SINGLE_ARG_FUNC(_funcname, num_ops) static SQInteger math_##_funcname(HSQUIRRELVM v){ \
|
||||
SQFloat f; \
|
||||
sq_decreaseops(v,num_ops); \
|
||||
sq_getfloat(v,2,&f); \
|
||||
sq_pushfloat(v,(SQFloat)_funcname(f)); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
#define TWO_ARGS_FUNC(_funcname, num_ops) static SQInteger math_##_funcname(HSQUIRRELVM v){ \
|
||||
SQFloat p1,p2; \
|
||||
sq_decreaseops(v,num_ops); \
|
||||
sq_getfloat(v,2,&p1); \
|
||||
sq_getfloat(v,3,&p2); \
|
||||
sq_pushfloat(v,(SQFloat)_funcname(p1,p2)); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
static SQInteger math_srand(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger i;
|
||||
if(SQ_FAILED(sq_getinteger(v,2,&i)))
|
||||
return sq_throwerror(v,"invalid param");
|
||||
srand((unsigned int)i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger math_rand(HSQUIRRELVM v)
|
||||
{
|
||||
sq_pushinteger(v,rand());
|
||||
return 1;
|
||||
}
|
||||
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
|
||||
|
||||
static SQInteger math_abs(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger n;
|
||||
sq_getinteger(v,2,&n);
|
||||
sq_pushinteger(v,(SQInteger)abs((int)n));
|
||||
return 1;
|
||||
}
|
||||
|
||||
SINGLE_ARG_FUNC(sqrt, 100)
|
||||
SINGLE_ARG_FUNC(fabs, 1)
|
||||
SINGLE_ARG_FUNC(sin, 100)
|
||||
SINGLE_ARG_FUNC(cos, 100)
|
||||
SINGLE_ARG_FUNC(asin, 100)
|
||||
SINGLE_ARG_FUNC(acos, 100)
|
||||
SINGLE_ARG_FUNC(log, 100)
|
||||
SINGLE_ARG_FUNC(log10, 100)
|
||||
SINGLE_ARG_FUNC(tan, 100)
|
||||
SINGLE_ARG_FUNC(atan, 100)
|
||||
TWO_ARGS_FUNC(atan2, 100)
|
||||
TWO_ARGS_FUNC(pow, 100)
|
||||
SINGLE_ARG_FUNC(floor, 1)
|
||||
SINGLE_ARG_FUNC(ceil, 1)
|
||||
SINGLE_ARG_FUNC(exp, 100)
|
||||
|
||||
#define _DECL_FUNC(name,nparams,tycheck) {#name,math_##name,nparams,tycheck}
|
||||
static SQRegFunction mathlib_funcs[] = {
|
||||
_DECL_FUNC(sqrt,2,".n"),
|
||||
_DECL_FUNC(sin,2,".n"),
|
||||
_DECL_FUNC(cos,2,".n"),
|
||||
_DECL_FUNC(asin,2,".n"),
|
||||
_DECL_FUNC(acos,2,".n"),
|
||||
_DECL_FUNC(log,2,".n"),
|
||||
_DECL_FUNC(log10,2,".n"),
|
||||
_DECL_FUNC(tan,2,".n"),
|
||||
_DECL_FUNC(atan,2,".n"),
|
||||
_DECL_FUNC(atan2,3,".nn"),
|
||||
_DECL_FUNC(pow,3,".nn"),
|
||||
_DECL_FUNC(floor,2,".n"),
|
||||
_DECL_FUNC(ceil,2,".n"),
|
||||
_DECL_FUNC(exp,2,".n"),
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
_DECL_FUNC(srand,2,".n"),
|
||||
_DECL_FUNC(rand,1,NULL),
|
||||
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
|
||||
_DECL_FUNC(fabs,2,".n"),
|
||||
_DECL_FUNC(abs,2,".n"),
|
||||
{0,0,0,0},
|
||||
};
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
SQRESULT sqstd_register_mathlib(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger i=0;
|
||||
while(mathlib_funcs[i].name!=0) {
|
||||
sq_pushstring(v,mathlib_funcs[i].name,-1);
|
||||
sq_newclosure(v,mathlib_funcs[i].f,0);
|
||||
sq_setparamscheck(v,mathlib_funcs[i].nparamscheck,mathlib_funcs[i].typemask);
|
||||
sq_setnativeclosurename(v,-1,mathlib_funcs[i].name);
|
||||
sq_createslot(v,-3);
|
||||
i++;
|
||||
}
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
sq_pushstring(v,"RAND_MAX",-1);
|
||||
sq_pushinteger(v,RAND_MAX);
|
||||
sq_createslot(v,-3);
|
||||
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
|
||||
sq_pushstring(v,"PI",-1);
|
||||
sq_pushfloat(v,(SQFloat)M_PI);
|
||||
sq_createslot(v,-3);
|
||||
return SQ_OK;
|
||||
}
|
||||
632
src/3rdparty/squirrel/sqstdlib/sqstdrex.cpp
vendored
Normal file
632
src/3rdparty/squirrel/sqstdlib/sqstdrex.cpp
vendored
Normal file
@@ -0,0 +1,632 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#include <squirrel.h>
|
||||
#include <exception>
|
||||
#include "sqstdstring.h"
|
||||
|
||||
#ifdef _UNICODE
|
||||
#define scisprint iswprint
|
||||
#else
|
||||
#define scisprint isprint
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
static const SQChar *g_nnames[] =
|
||||
{
|
||||
"NONE","OP_GREEDY", "OP_OR",
|
||||
"OP_EXPR","OP_NOCAPEXPR","OP_DOT", "OP_CLASS",
|
||||
"OP_CCLASS","OP_NCLASS","OP_RANGE","OP_CHAR",
|
||||
"OP_EOL","OP_BOL","OP_WB"
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
|
||||
#define OP_OR (MAX_CHAR+2)
|
||||
#define OP_EXPR (MAX_CHAR+3) //parentesis ()
|
||||
#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
|
||||
#define OP_DOT (MAX_CHAR+5)
|
||||
#define OP_CLASS (MAX_CHAR+6)
|
||||
#define OP_CCLASS (MAX_CHAR+7)
|
||||
#define OP_NCLASS (MAX_CHAR+8) //negates class the [^
|
||||
#define OP_RANGE (MAX_CHAR+9)
|
||||
#define OP_CHAR (MAX_CHAR+10)
|
||||
#define OP_EOL (MAX_CHAR+11)
|
||||
#define OP_BOL (MAX_CHAR+12)
|
||||
#define OP_WB (MAX_CHAR+13)
|
||||
|
||||
#define SQREX_SYMBOL_ANY_CHAR ('.')
|
||||
#define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
|
||||
#define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
|
||||
#define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
|
||||
#define SQREX_SYMBOL_BRANCH ('|')
|
||||
#define SQREX_SYMBOL_END_OF_STRING ('$')
|
||||
#define SQREX_SYMBOL_BEGINNING_OF_STRING ('^')
|
||||
#define SQREX_SYMBOL_ESCAPE_CHAR ('\\')
|
||||
|
||||
|
||||
typedef int SQRexNodeType;
|
||||
|
||||
typedef struct tagSQRexNode{
|
||||
SQRexNodeType type;
|
||||
SQInteger left;
|
||||
SQInteger right;
|
||||
SQInteger next;
|
||||
}SQRexNode;
|
||||
|
||||
struct SQRex{
|
||||
const SQChar *_eol;
|
||||
const SQChar *_bol;
|
||||
const SQChar *_p;
|
||||
SQInteger _first;
|
||||
SQInteger _op;
|
||||
SQRexNode *_nodes;
|
||||
SQInteger _nallocated;
|
||||
SQInteger _nsize;
|
||||
SQInteger _nsubexpr;
|
||||
SQRexMatch *_matches;
|
||||
SQInteger _currsubexp;
|
||||
const SQChar **_error;
|
||||
};
|
||||
|
||||
static SQInteger sqstd_rex_list(SQRex *exp);
|
||||
|
||||
static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type)
|
||||
{
|
||||
SQRexNode n;
|
||||
n.type = type;
|
||||
n.next = n.right = n.left = -1;
|
||||
if(type == OP_EXPR)
|
||||
n.right = exp->_nsubexpr++;
|
||||
if(exp->_nallocated < (exp->_nsize + 1)) {
|
||||
SQInteger oldsize = exp->_nallocated;
|
||||
exp->_nallocated *= 2;
|
||||
exp->_nodes = (SQRexNode *)sq_realloc(exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode));
|
||||
}
|
||||
exp->_nodes[exp->_nsize++] = n;
|
||||
SQInteger newid = exp->_nsize - 1;
|
||||
return (SQInteger)newid;
|
||||
}
|
||||
|
||||
static void sqstd_rex_error(SQRex *exp,const SQChar *error)
|
||||
{
|
||||
if(exp->_error) *exp->_error = error;
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
static void sqstd_rex_expect(SQRex *exp, SQChar n){
|
||||
if((*exp->_p) != n)
|
||||
sqstd_rex_error(exp, "expected paren");
|
||||
exp->_p++;
|
||||
}
|
||||
|
||||
static SQChar sqstd_rex_escapechar(SQRex *exp)
|
||||
{
|
||||
if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){
|
||||
exp->_p++;
|
||||
switch(*exp->_p) {
|
||||
case 'v': exp->_p++; return '\v';
|
||||
case 'n': exp->_p++; return '\n';
|
||||
case 't': exp->_p++; return '\t';
|
||||
case 'r': exp->_p++; return '\r';
|
||||
case 'f': exp->_p++; return '\f';
|
||||
default: return (*exp->_p++);
|
||||
}
|
||||
} else if(!scisprint(*exp->_p)) sqstd_rex_error(exp,"letter expected");
|
||||
return (*exp->_p++);
|
||||
}
|
||||
|
||||
static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid)
|
||||
{
|
||||
SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS);
|
||||
exp->_nodes[n].left = classid;
|
||||
return n;
|
||||
}
|
||||
|
||||
static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass)
|
||||
{
|
||||
SQChar t;
|
||||
if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) {
|
||||
exp->_p++;
|
||||
switch(*exp->_p) {
|
||||
case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n');
|
||||
case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t');
|
||||
case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r');
|
||||
case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f');
|
||||
case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v');
|
||||
case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
|
||||
case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
|
||||
case 'p': case 'P': case 'l': case 'u':
|
||||
{
|
||||
t = *exp->_p; exp->_p++;
|
||||
return sqstd_rex_charclass(exp,t);
|
||||
}
|
||||
case 'b':
|
||||
case 'B':
|
||||
if(!isclass) {
|
||||
SQInteger node = sqstd_rex_newnode(exp,OP_WB);
|
||||
exp->_nodes[node].left = *exp->_p;
|
||||
exp->_p++;
|
||||
return node;
|
||||
} //else default
|
||||
default:
|
||||
t = *exp->_p; exp->_p++;
|
||||
return sqstd_rex_newnode(exp,t);
|
||||
}
|
||||
}
|
||||
else if(!scisprint(*exp->_p)) {
|
||||
|
||||
sqstd_rex_error(exp,"letter expected");
|
||||
}
|
||||
t = *exp->_p; exp->_p++;
|
||||
return sqstd_rex_newnode(exp,t);
|
||||
}
|
||||
static SQInteger sqstd_rex_class(SQRex *exp)
|
||||
{
|
||||
SQInteger ret = -1;
|
||||
SQInteger first = -1,chain;
|
||||
if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){
|
||||
ret = sqstd_rex_newnode(exp,OP_NCLASS);
|
||||
exp->_p++;
|
||||
}else ret = sqstd_rex_newnode(exp,OP_CLASS);
|
||||
|
||||
if(*exp->_p == ']') sqstd_rex_error(exp,"empty class");
|
||||
chain = ret;
|
||||
while(*exp->_p != ']' && exp->_p != exp->_eol) {
|
||||
if(*exp->_p == '-' && first != -1){
|
||||
SQInteger r;
|
||||
if(*exp->_p++ == ']') sqstd_rex_error(exp,"unfinished range");
|
||||
r = sqstd_rex_newnode(exp,OP_RANGE);
|
||||
if(exp->_nodes[first].type>*exp->_p) sqstd_rex_error(exp,"invalid range");
|
||||
if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,"cannot use character classes in ranges");
|
||||
exp->_nodes[r].left = exp->_nodes[first].type;
|
||||
SQInteger t = sqstd_rex_escapechar(exp);
|
||||
exp->_nodes[r].right = t;
|
||||
exp->_nodes[chain].next = r;
|
||||
chain = r;
|
||||
first = -1;
|
||||
}
|
||||
else{
|
||||
if(first!=-1){
|
||||
SQInteger c = first;
|
||||
exp->_nodes[chain].next = c;
|
||||
chain = c;
|
||||
first = sqstd_rex_charnode(exp,SQTrue);
|
||||
}
|
||||
else{
|
||||
first = sqstd_rex_charnode(exp,SQTrue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(first!=-1){
|
||||
SQInteger c = first;
|
||||
exp->_nodes[chain].next = c;
|
||||
chain = c;
|
||||
first = -1;
|
||||
}
|
||||
/* hack? */
|
||||
exp->_nodes[ret].left = exp->_nodes[ret].next;
|
||||
exp->_nodes[ret].next = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SQInteger sqstd_rex_parsenumber(SQRex *exp)
|
||||
{
|
||||
SQInteger ret = *exp->_p-'0';
|
||||
SQInteger positions = 10;
|
||||
exp->_p++;
|
||||
while(isdigit(*exp->_p)) {
|
||||
ret = ret*10+(*exp->_p++-'0');
|
||||
if(positions==1000000000) sqstd_rex_error(exp,"overflow in numeric constant");
|
||||
positions *= 10;
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SQInteger sqstd_rex_element(SQRex *exp)
|
||||
{
|
||||
SQInteger ret = -1;
|
||||
switch(*exp->_p)
|
||||
{
|
||||
case '(': {
|
||||
SQInteger expr;
|
||||
exp->_p++;
|
||||
|
||||
|
||||
if(*exp->_p =='?') {
|
||||
exp->_p++;
|
||||
sqstd_rex_expect(exp,':');
|
||||
expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR);
|
||||
}
|
||||
else
|
||||
expr = sqstd_rex_newnode(exp,OP_EXPR);
|
||||
SQInteger newn = sqstd_rex_list(exp);
|
||||
exp->_nodes[expr].left = newn;
|
||||
ret = expr;
|
||||
sqstd_rex_expect(exp,')');
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
exp->_p++;
|
||||
ret = sqstd_rex_class(exp);
|
||||
sqstd_rex_expect(exp,']');
|
||||
break;
|
||||
case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break;
|
||||
case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break;
|
||||
default:
|
||||
ret = sqstd_rex_charnode(exp,SQFalse);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
SQInteger op;
|
||||
SQBool isgreedy = SQFalse;
|
||||
unsigned short p0 = 0, p1 = 0;
|
||||
switch(*exp->_p){
|
||||
case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;
|
||||
case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;
|
||||
case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break;
|
||||
case '{':
|
||||
exp->_p++;
|
||||
if(!isdigit(*exp->_p)) sqstd_rex_error(exp,"number expected");
|
||||
p0 = (unsigned short)sqstd_rex_parsenumber(exp);
|
||||
/*******************************/
|
||||
switch(*exp->_p) {
|
||||
case '}':
|
||||
p1 = p0; exp->_p++;
|
||||
break;
|
||||
case ',':
|
||||
exp->_p++;
|
||||
p1 = 0xFFFF;
|
||||
if(isdigit(*exp->_p)){
|
||||
p1 = (unsigned short)sqstd_rex_parsenumber(exp);
|
||||
}
|
||||
sqstd_rex_expect(exp,'}');
|
||||
break;
|
||||
default:
|
||||
sqstd_rex_error(exp,", or } expected");
|
||||
}
|
||||
/*******************************/
|
||||
isgreedy = SQTrue;
|
||||
break;
|
||||
|
||||
}
|
||||
if(isgreedy) {
|
||||
SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY);
|
||||
op = OP_GREEDY;
|
||||
exp->_nodes[nnode].left = ret;
|
||||
exp->_nodes[nnode].right = ((p0)<<16)|p1;
|
||||
ret = nnode;
|
||||
}
|
||||
|
||||
if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
|
||||
SQInteger nnode = sqstd_rex_element(exp);
|
||||
exp->_nodes[ret].next = nnode;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SQInteger sqstd_rex_list(SQRex *exp)
|
||||
{
|
||||
SQInteger ret=-1,e;
|
||||
if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) {
|
||||
exp->_p++;
|
||||
ret = sqstd_rex_newnode(exp,OP_BOL);
|
||||
}
|
||||
e = sqstd_rex_element(exp);
|
||||
if(ret != -1) {
|
||||
exp->_nodes[ret].next = e;
|
||||
}
|
||||
else ret = e;
|
||||
|
||||
if(*exp->_p == SQREX_SYMBOL_BRANCH) {
|
||||
SQInteger temp,tright;
|
||||
exp->_p++;
|
||||
temp = sqstd_rex_newnode(exp,OP_OR);
|
||||
exp->_nodes[temp].left = ret;
|
||||
tright = sqstd_rex_list(exp);
|
||||
exp->_nodes[temp].right = tright;
|
||||
ret = temp;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SQBool sqstd_rex_matchcclass(SQInteger cclass,SQChar c)
|
||||
{
|
||||
switch(cclass) {
|
||||
case 'a': return isalpha(c)?SQTrue:SQFalse;
|
||||
case 'A': return !isalpha(c)?SQTrue:SQFalse;
|
||||
case 'w': return (isalnum(c) || c == '_')?SQTrue:SQFalse;
|
||||
case 'W': return (!isalnum(c) && c != '_')?SQTrue:SQFalse;
|
||||
case 's': return isspace(c)?SQTrue:SQFalse;
|
||||
case 'S': return !isspace(c)?SQTrue:SQFalse;
|
||||
case 'd': return isdigit(c)?SQTrue:SQFalse;
|
||||
case 'D': return !isdigit(c)?SQTrue:SQFalse;
|
||||
case 'x': return isxdigit(c)?SQTrue:SQFalse;
|
||||
case 'X': return !isxdigit(c)?SQTrue:SQFalse;
|
||||
case 'c': return iscntrl(c)?SQTrue:SQFalse;
|
||||
case 'C': return !iscntrl(c)?SQTrue:SQFalse;
|
||||
case 'p': return ispunct(c)?SQTrue:SQFalse;
|
||||
case 'P': return !ispunct(c)?SQTrue:SQFalse;
|
||||
case 'l': return islower(c)?SQTrue:SQFalse;
|
||||
case 'u': return isupper(c)?SQTrue:SQFalse;
|
||||
}
|
||||
return SQFalse; /*cannot happen*/
|
||||
}
|
||||
|
||||
static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,SQInteger c)
|
||||
{
|
||||
do {
|
||||
switch(node->type) {
|
||||
case OP_RANGE:
|
||||
if(c >= node->left && c <= node->right) return SQTrue;
|
||||
break;
|
||||
case OP_CCLASS:
|
||||
if(sqstd_rex_matchcclass(node->left,c)) return SQTrue;
|
||||
break;
|
||||
default:
|
||||
if(c == node->type)return SQTrue;
|
||||
}
|
||||
} while((node->next != -1) && (node = &exp->_nodes[node->next]));
|
||||
return SQFalse;
|
||||
}
|
||||
|
||||
static const SQChar *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const SQChar *str,SQRexNode *next)
|
||||
{
|
||||
|
||||
SQRexNodeType type = node->type;
|
||||
switch(type) {
|
||||
case OP_GREEDY: {
|
||||
//SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
|
||||
SQRexNode *greedystop = NULL;
|
||||
SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
|
||||
const SQChar *s=str, *good = str;
|
||||
|
||||
if(node->next != -1) {
|
||||
greedystop = &exp->_nodes[node->next];
|
||||
}
|
||||
else {
|
||||
greedystop = next;
|
||||
}
|
||||
|
||||
while((nmaches == 0xFFFF || nmaches < p1)) {
|
||||
|
||||
const SQChar *stop;
|
||||
if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
|
||||
break;
|
||||
nmaches++;
|
||||
good=s;
|
||||
if(greedystop) {
|
||||
//checks that 0 matches satisfy the expression(if so skips)
|
||||
//if not would always stop(for instance if is a '?')
|
||||
if(greedystop->type != OP_GREEDY ||
|
||||
(greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
|
||||
{
|
||||
SQRexNode *gnext = NULL;
|
||||
if(greedystop->next != -1) {
|
||||
gnext = &exp->_nodes[greedystop->next];
|
||||
}else if(next && next->next != -1){
|
||||
gnext = &exp->_nodes[next->next];
|
||||
}
|
||||
stop = sqstd_rex_matchnode(exp,greedystop,s,gnext);
|
||||
if(stop) {
|
||||
//if satisfied stop it
|
||||
if(p0 == p1 && p0 == nmaches) break;
|
||||
else if(nmaches >= p0 && p1 == 0xFFFF) break;
|
||||
else if(nmaches >= p0 && nmaches <= p1) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(s >= exp->_eol)
|
||||
break;
|
||||
}
|
||||
if(p0 == p1 && p0 == nmaches) return good;
|
||||
else if(nmaches >= p0 && p1 == 0xFFFF) return good;
|
||||
else if(nmaches >= p0 && nmaches <= p1) return good;
|
||||
return NULL;
|
||||
}
|
||||
case OP_OR: {
|
||||
const SQChar *asd = str;
|
||||
SQRexNode *temp=&exp->_nodes[node->left];
|
||||
while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) {
|
||||
if(temp->next != -1)
|
||||
temp = &exp->_nodes[temp->next];
|
||||
else
|
||||
return asd;
|
||||
}
|
||||
asd = str;
|
||||
temp = &exp->_nodes[node->right];
|
||||
while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) {
|
||||
if(temp->next != -1)
|
||||
temp = &exp->_nodes[temp->next];
|
||||
else
|
||||
return asd;
|
||||
}
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case OP_EXPR:
|
||||
case OP_NOCAPEXPR:{
|
||||
SQRexNode *n = &exp->_nodes[node->left];
|
||||
const SQChar *cur = str;
|
||||
SQInteger capture = -1;
|
||||
if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
|
||||
capture = exp->_currsubexp;
|
||||
exp->_matches[capture].begin = cur;
|
||||
exp->_currsubexp++;
|
||||
}
|
||||
|
||||
do {
|
||||
SQRexNode *subnext = NULL;
|
||||
if(n->next != -1) {
|
||||
subnext = &exp->_nodes[n->next];
|
||||
}else {
|
||||
subnext = next;
|
||||
}
|
||||
if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) {
|
||||
if(capture != -1){
|
||||
exp->_matches[capture].begin = 0;
|
||||
exp->_matches[capture].len = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
} while((n->next != -1) && (n = &exp->_nodes[n->next]));
|
||||
|
||||
if(capture != -1)
|
||||
exp->_matches[capture].len = cur - exp->_matches[capture].begin;
|
||||
return cur;
|
||||
}
|
||||
case OP_WB:
|
||||
if((str == exp->_bol && !isspace(*str))
|
||||
|| (str == exp->_eol && !isspace(*(str-1)))
|
||||
|| (!isspace(*str) && isspace(*(str+1)))
|
||||
|| (isspace(*str) && !isspace(*(str+1))) ) {
|
||||
return (node->left == 'b')?str:NULL;
|
||||
}
|
||||
return (node->left == 'b')?NULL:str;
|
||||
case OP_BOL:
|
||||
if(str == exp->_bol) return str;
|
||||
return NULL;
|
||||
case OP_EOL:
|
||||
if(str == exp->_eol) return str;
|
||||
return NULL;
|
||||
case OP_DOT:{
|
||||
*str++;
|
||||
}
|
||||
return str;
|
||||
case OP_NCLASS:
|
||||
case OP_CLASS:
|
||||
if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) {
|
||||
*str++;
|
||||
return str;
|
||||
}
|
||||
return NULL;
|
||||
case OP_CCLASS:
|
||||
if(sqstd_rex_matchcclass(node->left,*str)) {
|
||||
*str++;
|
||||
return str;
|
||||
}
|
||||
return NULL;
|
||||
default: /* char */
|
||||
if(*str != (SQChar)node->type) return NULL;
|
||||
*str++;
|
||||
return str;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* public api */
|
||||
SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error)
|
||||
{
|
||||
SQRex *exp = (SQRex *)sq_malloc(sizeof(SQRex));
|
||||
exp->_eol = exp->_bol = NULL;
|
||||
exp->_p = pattern;
|
||||
exp->_nallocated = (SQInteger)strlen(pattern) * sizeof(SQChar);
|
||||
exp->_nodes = (SQRexNode *)sq_malloc(exp->_nallocated * sizeof(SQRexNode));
|
||||
exp->_nsize = 0;
|
||||
exp->_matches = 0;
|
||||
exp->_nsubexpr = 0;
|
||||
exp->_first = sqstd_rex_newnode(exp,OP_EXPR);
|
||||
exp->_error = error;
|
||||
try {
|
||||
SQInteger res = sqstd_rex_list(exp);
|
||||
exp->_nodes[exp->_first].left = res;
|
||||
if(*exp->_p!='\0')
|
||||
sqstd_rex_error(exp,"unexpected character");
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
SQInteger nsize,i;
|
||||
SQRexNode *t;
|
||||
nsize = exp->_nsize;
|
||||
t = &exp->_nodes[0];
|
||||
printf("\n");
|
||||
/* XXX -- The (int) casts are needed to silent warnings on 64bit systems (SQInteger is 64bit, %d assumes 32bit, (int) is 32bit) */
|
||||
for(i = 0;i < nsize; i++) {
|
||||
if(exp->_nodes[i].type>MAX_CHAR)
|
||||
printf("[%02d] %10s ",(int)i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
|
||||
else
|
||||
printf("[%02d] %10c ",(int)i,exp->_nodes[i].type);
|
||||
printf("left %02d right %02d next %02d\n",(int)exp->_nodes[i].left,(int)exp->_nodes[i].right,(int)exp->_nodes[i].next);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
exp->_matches = (SQRexMatch *) sq_malloc(exp->_nsubexpr * sizeof(SQRexMatch));
|
||||
memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch));
|
||||
}
|
||||
catch (...) {
|
||||
sqstd_rex_free(exp);
|
||||
return NULL;
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
void sqstd_rex_free(SQRex *exp)
|
||||
{
|
||||
if(exp) {
|
||||
if(exp->_nodes) sq_free(exp->_nodes,exp->_nallocated * sizeof(SQRexNode));
|
||||
if(exp->_matches) sq_free(exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch));
|
||||
sq_free(exp,sizeof(SQRex));
|
||||
}
|
||||
}
|
||||
|
||||
SQBool sqstd_rex_match(SQRex* exp,const SQChar* text)
|
||||
{
|
||||
const SQChar* res = NULL;
|
||||
exp->_bol = text;
|
||||
exp->_eol = text + strlen(text);
|
||||
exp->_currsubexp = 0;
|
||||
res = sqstd_rex_matchnode(exp,exp->_nodes,text,NULL);
|
||||
if(res == NULL || res != exp->_eol)
|
||||
return SQFalse;
|
||||
return SQTrue;
|
||||
}
|
||||
|
||||
SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end)
|
||||
{
|
||||
const SQChar *cur = NULL;
|
||||
SQInteger node = exp->_first;
|
||||
if(text_begin >= text_end) return SQFalse;
|
||||
exp->_bol = text_begin;
|
||||
exp->_eol = text_end;
|
||||
do {
|
||||
cur = text_begin;
|
||||
while(node != -1) {
|
||||
exp->_currsubexp = 0;
|
||||
cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,NULL);
|
||||
if(!cur)
|
||||
break;
|
||||
node = exp->_nodes[node].next;
|
||||
}
|
||||
*text_begin++;
|
||||
} while(cur == NULL && text_begin != text_end);
|
||||
|
||||
if(cur == NULL)
|
||||
return SQFalse;
|
||||
|
||||
--text_begin;
|
||||
|
||||
if(out_begin) *out_begin = text_begin;
|
||||
if(out_end) *out_end = cur;
|
||||
return SQTrue;
|
||||
}
|
||||
|
||||
SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end)
|
||||
{
|
||||
return sqstd_rex_searchrange(exp,text,text + strlen(text),out_begin,out_end);
|
||||
}
|
||||
|
||||
SQInteger sqstd_rex_getsubexpcount(SQRex* exp)
|
||||
{
|
||||
return exp->_nsubexpr;
|
||||
}
|
||||
|
||||
SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp)
|
||||
{
|
||||
if( n<0 || n >= exp->_nsubexpr) return SQFalse;
|
||||
*subexp = exp->_matches[n];
|
||||
return SQTrue;
|
||||
}
|
||||
|
||||
366
src/3rdparty/squirrel/sqstdlib/sqstdstring.cpp
vendored
Normal file
366
src/3rdparty/squirrel/sqstdlib/sqstdstring.cpp
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#include <squirrel.h>
|
||||
#include <sqstdstring.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define scstrchr strchr
|
||||
#define scatoi atoi
|
||||
#define scstrtok strtok
|
||||
#define MAX_FORMAT_LEN 20
|
||||
#define MAX_WFORMAT_LEN 3
|
||||
#define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar))
|
||||
|
||||
static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width)
|
||||
{
|
||||
SQChar swidth[MAX_WFORMAT_LEN];
|
||||
SQInteger wc = 0;
|
||||
SQInteger start = n;
|
||||
fmt[0] = '%';
|
||||
while (scstrchr("-+ #0", src[n])) n++;
|
||||
while (isdigit(src[n])) {
|
||||
swidth[wc] = src[n];
|
||||
n++;
|
||||
wc++;
|
||||
if(wc>=MAX_WFORMAT_LEN)
|
||||
return sq_throwerror(v,"width format too long");
|
||||
}
|
||||
swidth[wc] = '\0';
|
||||
if(wc > 0) {
|
||||
width = atoi(swidth);
|
||||
}
|
||||
else
|
||||
width = 0;
|
||||
if (src[n] == '.') {
|
||||
n++;
|
||||
|
||||
wc = 0;
|
||||
while (isdigit(src[n])) {
|
||||
swidth[wc] = src[n];
|
||||
n++;
|
||||
wc++;
|
||||
if(wc>=MAX_WFORMAT_LEN)
|
||||
return sq_throwerror(v,"precision format too long");
|
||||
}
|
||||
swidth[wc] = '\0';
|
||||
if(wc > 0) {
|
||||
width += atoi(swidth);
|
||||
}
|
||||
}
|
||||
if (n-start > MAX_FORMAT_LEN )
|
||||
return sq_throwerror(v,"format too long");
|
||||
memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar));
|
||||
fmt[(n-start)+2] = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Little hack to remove the "format not a string literal, argument types not checked" warning.
|
||||
* This check has been added to OpenTTD to make sure that nobody passes wrong string literals,
|
||||
* but three lines in Squirrel have a little problem with those. Therefor we use this hack
|
||||
* which basically uses vsnprintf instead of sprintf as vsnprintf is not testing for the right
|
||||
* string literal at compile time.
|
||||
*/
|
||||
static void _append_string(SQInteger &i, SQChar *dest, SQInteger allocated, const SQChar *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
i += vsnprintf(&dest[i],allocated-i,fmt,va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output)
|
||||
{
|
||||
const SQChar *format;
|
||||
SQChar *dest;
|
||||
SQChar fmt[MAX_FORMAT_LEN];
|
||||
sq_getstring(v,nformatstringidx,&format);
|
||||
SQInteger allocated = (sq_getsize(v,nformatstringidx)+2)*sizeof(SQChar);
|
||||
dest = sq_getscratchpad(v,allocated);
|
||||
SQInteger n = 0,i = 0, nparam = nformatstringidx+1, w = 0;
|
||||
while(format[n] != '\0') {
|
||||
if(format[n] != '%') {
|
||||
assert(i < allocated);
|
||||
dest[i++] = format[n];
|
||||
n++;
|
||||
}
|
||||
else if(format[n+1] == '%') { //handles %%
|
||||
dest[i++] = '%';
|
||||
n += 2;
|
||||
}
|
||||
else {
|
||||
n++;
|
||||
if( nparam > sq_gettop(v) )
|
||||
return sq_throwerror(v,"not enough paramters for the given format string");
|
||||
n = validate_format(v,fmt,format,n,w);
|
||||
if(n < 0) return -1;
|
||||
SQInteger addlen = 0;
|
||||
SQInteger valtype = 0;
|
||||
const SQChar *ts;
|
||||
SQInteger ti;
|
||||
SQFloat tf;
|
||||
switch(format[n]) {
|
||||
case 's':
|
||||
if(SQ_FAILED(sq_getstring(v,nparam,&ts)))
|
||||
return sq_throwerror(v,"string expected for the specified format");
|
||||
addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar));
|
||||
valtype = 's';
|
||||
break;
|
||||
case 'i': case 'd': case 'c':case 'o': case 'u': case 'x': case 'X':
|
||||
if(SQ_FAILED(sq_getinteger(v,nparam,&ti)))
|
||||
return sq_throwerror(v,"integer expected for the specified format");
|
||||
addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
|
||||
valtype = 'i';
|
||||
break;
|
||||
case 'f': case 'g': case 'G': case 'e': case 'E':
|
||||
if(SQ_FAILED(sq_getfloat(v,nparam,&tf)))
|
||||
return sq_throwerror(v,"float expected for the specified format");
|
||||
addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
|
||||
valtype = 'f';
|
||||
break;
|
||||
default:
|
||||
return sq_throwerror(v,"invalid format");
|
||||
}
|
||||
n++;
|
||||
allocated += addlen + sizeof(SQChar);
|
||||
dest = sq_getscratchpad(v,allocated);
|
||||
switch(valtype) {
|
||||
case 's': _append_string(i,dest,allocated,fmt,ts); break;
|
||||
case 'i': _append_string(i,dest,allocated,fmt,ti); break;
|
||||
case 'f': _append_string(i,dest,allocated,fmt,tf); break;
|
||||
};
|
||||
nparam ++;
|
||||
}
|
||||
}
|
||||
*outlen = i;
|
||||
dest[i] = '\0';
|
||||
*output = dest;
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
static SQInteger _string_format(HSQUIRRELVM v)
|
||||
{
|
||||
SQChar *dest = NULL;
|
||||
SQInteger length = 0;
|
||||
if(SQ_FAILED(sqstd_format(v,2,&length,&dest)))
|
||||
return -1;
|
||||
sq_pushstring(v,dest,length);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __strip_l(const SQChar *str,const SQChar **start)
|
||||
{
|
||||
const SQChar *t = str;
|
||||
while(((*t) != '\0') && isspace(*t)){ t++; }
|
||||
*start = t;
|
||||
}
|
||||
|
||||
static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end)
|
||||
{
|
||||
if(len == 0) {
|
||||
*end = str;
|
||||
return;
|
||||
}
|
||||
const SQChar *t = &str[len-1];
|
||||
while(t != str && isspace(*t)) { t--; }
|
||||
*end = t+1;
|
||||
}
|
||||
|
||||
static SQInteger _string_strip(HSQUIRRELVM v)
|
||||
{
|
||||
const SQChar *str,*start,*end;
|
||||
sq_getstring(v,2,&str);
|
||||
SQInteger len = sq_getsize(v,2);
|
||||
__strip_l(str,&start);
|
||||
__strip_r(str,len,&end);
|
||||
sq_pushstring(v,start,end - start);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger _string_lstrip(HSQUIRRELVM v)
|
||||
{
|
||||
const SQChar *str,*start;
|
||||
sq_getstring(v,2,&str);
|
||||
__strip_l(str,&start);
|
||||
sq_pushstring(v,start,-1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger _string_rstrip(HSQUIRRELVM v)
|
||||
{
|
||||
const SQChar *str,*end;
|
||||
sq_getstring(v,2,&str);
|
||||
SQInteger len = sq_getsize(v,2);
|
||||
__strip_r(str,len,&end);
|
||||
sq_pushstring(v,str,end - str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger _string_split(HSQUIRRELVM v)
|
||||
{
|
||||
const SQChar *str,*seps;
|
||||
SQChar *stemp,*tok;
|
||||
sq_getstring(v,2,&str);
|
||||
sq_getstring(v,3,&seps);
|
||||
if(sq_getsize(v,3) == 0) return sq_throwerror(v,"empty separators string");
|
||||
SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar);
|
||||
stemp = sq_getscratchpad(v,memsize);
|
||||
memcpy(stemp,str,memsize);
|
||||
tok = scstrtok(stemp,seps);
|
||||
sq_newarray(v,0);
|
||||
while( tok != NULL ) {
|
||||
sq_pushstring(v,tok,-1);
|
||||
sq_arrayappend(v,-2);
|
||||
tok = scstrtok( NULL, seps );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define SETUP_REX(v) \
|
||||
SQRex *self = NULL; \
|
||||
sq_getinstanceup(v,1,(SQUserPointer *)&self,0);
|
||||
|
||||
static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger size)
|
||||
{
|
||||
SQRex *self = ((SQRex *)p);
|
||||
sqstd_rex_free(self);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger _regexp_match(HSQUIRRELVM v)
|
||||
{
|
||||
SETUP_REX(v);
|
||||
const SQChar *str;
|
||||
sq_getstring(v,2,&str);
|
||||
if(sqstd_rex_match(self,str) == SQTrue)
|
||||
{
|
||||
sq_pushbool(v,SQTrue);
|
||||
return 1;
|
||||
}
|
||||
sq_pushbool(v,SQFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end)
|
||||
{
|
||||
sq_newtable(v);
|
||||
sq_pushstring(v,"begin",-1);
|
||||
sq_pushinteger(v,begin - str);
|
||||
sq_rawset(v,-3);
|
||||
sq_pushstring(v,"end",-1);
|
||||
sq_pushinteger(v,end - str);
|
||||
sq_rawset(v,-3);
|
||||
}
|
||||
|
||||
static SQInteger _regexp_search(HSQUIRRELVM v)
|
||||
{
|
||||
SETUP_REX(v);
|
||||
const SQChar *str,*begin,*end;
|
||||
SQInteger start = 0;
|
||||
sq_getstring(v,2,&str);
|
||||
if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
|
||||
if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
|
||||
_addrexmatch(v,str,begin,end);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger _regexp_capture(HSQUIRRELVM v)
|
||||
{
|
||||
SETUP_REX(v);
|
||||
const SQChar *str,*begin,*end;
|
||||
SQInteger start = 0;
|
||||
sq_getstring(v,2,&str);
|
||||
if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
|
||||
if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
|
||||
SQInteger n = sqstd_rex_getsubexpcount(self);
|
||||
SQRexMatch match;
|
||||
sq_newarray(v,0);
|
||||
for(SQInteger i = 0;i < n; i++) {
|
||||
sqstd_rex_getsubexp(self,i,&match);
|
||||
if(match.len > 0)
|
||||
_addrexmatch(v,str,match.begin,match.begin+match.len);
|
||||
else
|
||||
_addrexmatch(v,str,str,str); //empty match
|
||||
sq_arrayappend(v,-2);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger _regexp_subexpcount(HSQUIRRELVM v)
|
||||
{
|
||||
SETUP_REX(v);
|
||||
sq_pushinteger(v,sqstd_rex_getsubexpcount(self));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger _regexp_constructor(HSQUIRRELVM v)
|
||||
{
|
||||
const SQChar *error,*pattern;
|
||||
sq_getstring(v,2,&pattern);
|
||||
SQRex *rex = sqstd_rex_compile(pattern,&error);
|
||||
if(!rex) return sq_throwerror(v,error);
|
||||
sq_setinstanceup(v,1,rex);
|
||||
sq_setreleasehook(v,1,_rexobj_releasehook);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger _regexp__typeof(HSQUIRRELVM v)
|
||||
{
|
||||
sq_pushstring(v,"regexp",-1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define _DECL_REX_FUNC(name,nparams,pmask) {#name,_regexp_##name,nparams,pmask}
|
||||
static SQRegFunction rexobj_funcs[]={
|
||||
_DECL_REX_FUNC(constructor,2,".s"),
|
||||
_DECL_REX_FUNC(search,-2,"xsn"),
|
||||
_DECL_REX_FUNC(match,2,"xs"),
|
||||
_DECL_REX_FUNC(capture,-2,"xsn"),
|
||||
_DECL_REX_FUNC(subexpcount,1,"x"),
|
||||
_DECL_REX_FUNC(_typeof,1,"x"),
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
#define _DECL_FUNC(name,nparams,pmask) {#name,_string_##name,nparams,pmask}
|
||||
static SQRegFunction stringlib_funcs[]={
|
||||
_DECL_FUNC(format,-2,".s"),
|
||||
_DECL_FUNC(strip,2,".s"),
|
||||
_DECL_FUNC(lstrip,2,".s"),
|
||||
_DECL_FUNC(rstrip,2,".s"),
|
||||
_DECL_FUNC(split,3,".ss"),
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
|
||||
SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
|
||||
{
|
||||
sq_pushstring(v,"regexp",-1);
|
||||
sq_newclass(v,SQFalse);
|
||||
SQInteger i = 0;
|
||||
while(rexobj_funcs[i].name != 0) {
|
||||
SQRegFunction &f = rexobj_funcs[i];
|
||||
sq_pushstring(v,f.name,-1);
|
||||
sq_newclosure(v,f.f,0);
|
||||
sq_setparamscheck(v,f.nparamscheck,f.typemask);
|
||||
sq_setnativeclosurename(v,-1,f.name);
|
||||
sq_createslot(v,-3);
|
||||
i++;
|
||||
}
|
||||
sq_createslot(v,-3);
|
||||
|
||||
i = 0;
|
||||
while(stringlib_funcs[i].name!=0)
|
||||
{
|
||||
sq_pushstring(v,stringlib_funcs[i].name,-1);
|
||||
sq_newclosure(v,stringlib_funcs[i].f,0);
|
||||
sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask);
|
||||
sq_setnativeclosurename(v,-1,stringlib_funcs[i].name);
|
||||
sq_createslot(v,-3);
|
||||
i++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
1325
src/3rdparty/squirrel/squirrel/sqapi.cpp
vendored
Normal file
1325
src/3rdparty/squirrel/squirrel/sqapi.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
87
src/3rdparty/squirrel/squirrel/sqarray.h
vendored
Normal file
87
src/3rdparty/squirrel/squirrel/sqarray.h
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQARRAY_H_
|
||||
#define _SQARRAY_H_
|
||||
|
||||
struct SQArray : public CHAINABLE_OBJ
|
||||
{
|
||||
private:
|
||||
SQArray(SQSharedState *ss,SQInteger nsize){_values.resize(nsize); INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
|
||||
~SQArray()
|
||||
{
|
||||
REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
|
||||
}
|
||||
public:
|
||||
static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){
|
||||
SQArray *newarray=(SQArray*)SQ_MALLOC(sizeof(SQArray));
|
||||
new (newarray) SQArray(ss,nInitialSize);
|
||||
return newarray;
|
||||
}
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable **chain);
|
||||
#endif
|
||||
void Finalize(){
|
||||
_values.resize(0);
|
||||
}
|
||||
bool Get(const SQInteger nidx,SQObjectPtr &val)
|
||||
{
|
||||
if(nidx>=0 && nidx<(SQInteger)_values.size()){
|
||||
SQObjectPtr &o = _values[nidx];
|
||||
val = _realval(o);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
bool Set(const SQInteger nidx,const SQObjectPtr &val)
|
||||
{
|
||||
if(nidx>=0 && nidx<(SQInteger)_values.size()){
|
||||
_values[nidx]=val;
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
SQInteger Next(const SQObjectPtr &refpos,SQObjectPtr &outkey,SQObjectPtr &outval)
|
||||
{
|
||||
SQUnsignedInteger idx=TranslateIndex(refpos);
|
||||
while(idx<_values.size()){
|
||||
//first found
|
||||
outkey=(SQInteger)idx;
|
||||
SQObjectPtr &o = _values[idx];
|
||||
outval = _realval(o);
|
||||
//return idx for the next iteration
|
||||
return ++idx;
|
||||
}
|
||||
//nothing to iterate anymore
|
||||
return -1;
|
||||
}
|
||||
SQArray *Clone(){SQArray *anew=Create(_opt_ss(this),Size()); anew->_values.copy(_values); return anew; }
|
||||
SQInteger Size() const {return _values.size();}
|
||||
void Resize(SQInteger size,SQObjectPtr &fill = _null_) { _values.resize(size,fill); ShrinkIfNeeded(); }
|
||||
void Reserve(SQInteger size) { _values.reserve(size); }
|
||||
void Append(const SQObject &o){_values.push_back(o);}
|
||||
void Extend(const SQArray *a);
|
||||
SQObjectPtr &Top(){return _values.top();}
|
||||
void Pop(){_values.pop_back(); ShrinkIfNeeded(); }
|
||||
bool Insert(SQInteger idx,const SQObject &val){
|
||||
if(idx < 0 || idx > (SQInteger)_values.size())
|
||||
return false;
|
||||
_values.insert(idx,val);
|
||||
return true;
|
||||
}
|
||||
void ShrinkIfNeeded() {
|
||||
if(_values.size() <= _values.capacity()>>2) //shrink the array
|
||||
_values.shrinktofit();
|
||||
}
|
||||
bool Remove(SQInteger idx){
|
||||
if(idx < 0 || idx >= (SQInteger)_values.size())
|
||||
return false;
|
||||
_values.remove(idx);
|
||||
ShrinkIfNeeded();
|
||||
return true;
|
||||
}
|
||||
void Release()
|
||||
{
|
||||
sq_delete(this,SQArray);
|
||||
}
|
||||
SQObjectPtrVec _values;
|
||||
};
|
||||
#endif //_SQARRAY_H_
|
||||
962
src/3rdparty/squirrel/squirrel/sqbaselib.cpp
vendored
Normal file
962
src/3rdparty/squirrel/squirrel/sqbaselib.cpp
vendored
Normal file
@@ -0,0 +1,962 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
/*
|
||||
* Needs to be first due to a squirrel header defining type() and type()
|
||||
* being used in some versions of the headers included by algorithm.
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "sqpcheader.h"
|
||||
#include "sqvm.h"
|
||||
#include "sqstring.h"
|
||||
#include "sqtable.h"
|
||||
#include "sqarray.h"
|
||||
#include "sqfuncproto.h"
|
||||
#include "sqclosure.h"
|
||||
#include "sqclass.h"
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
bool str2num(const SQChar *s,SQObjectPtr &res)
|
||||
{
|
||||
SQChar *end;
|
||||
if(strstr(s,".")){
|
||||
SQFloat r = SQFloat(strtod(s,&end));
|
||||
if(s == end) return false;
|
||||
res = r;
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
SQInteger r = SQInteger(strtol(s,&end,10));
|
||||
if(s == end) return false;
|
||||
res = r;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
static SQInteger base_dummy(HSQUIRRELVM v)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
static SQInteger base_collectgarbage(HSQUIRRELVM v)
|
||||
{
|
||||
sq_pushinteger(v, sq_collectgarbage(v));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SQInteger base_getroottable(HSQUIRRELVM v)
|
||||
{
|
||||
v->Push(v->_roottable);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger base_getconsttable(HSQUIRRELVM v)
|
||||
{
|
||||
v->Push(_ss(v)->_consts);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static SQInteger base_setroottable(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr &o=stack_get(v,2);
|
||||
if(SQ_FAILED(sq_setroottable(v))) return SQ_ERROR;
|
||||
v->Push(o);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger base_setconsttable(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr &o=stack_get(v,2);
|
||||
if(SQ_FAILED(sq_setconsttable(v))) return SQ_ERROR;
|
||||
v->Push(o);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger base_seterrorhandler(HSQUIRRELVM v)
|
||||
{
|
||||
sq_seterrorhandler(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger base_setdebughook(HSQUIRRELVM v)
|
||||
{
|
||||
sq_setdebughook(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger base_enabledebuginfo(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr &o=stack_get(v,2);
|
||||
sq_enabledebuginfo(v,(type(o) != OT_NULL)?1:0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger base_getstackinfos(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger level;
|
||||
SQStackInfos si;
|
||||
SQInteger seq = 0;
|
||||
const SQChar *name = NULL;
|
||||
sq_getinteger(v, -1, &level);
|
||||
if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si)))
|
||||
{
|
||||
const SQChar *fn = "unknown";
|
||||
const SQChar *src = "unknown";
|
||||
if(si.funcname)fn = si.funcname;
|
||||
if(si.source)src = si.source;
|
||||
sq_newtable(v);
|
||||
sq_pushstring(v, "func", -1);
|
||||
sq_pushstring(v, fn, -1);
|
||||
sq_createslot(v, -3);
|
||||
sq_pushstring(v, "src", -1);
|
||||
sq_pushstring(v, src, -1);
|
||||
sq_createslot(v, -3);
|
||||
sq_pushstring(v, "line", -1);
|
||||
sq_pushinteger(v, si.line);
|
||||
sq_createslot(v, -3);
|
||||
sq_pushstring(v, "locals", -1);
|
||||
sq_newtable(v);
|
||||
seq=0;
|
||||
while ((name = sq_getlocal(v, level, seq))) {
|
||||
sq_pushstring(v, name, -1);
|
||||
sq_push(v, -2);
|
||||
sq_createslot(v, -4);
|
||||
sq_pop(v, 1);
|
||||
seq++;
|
||||
}
|
||||
sq_createslot(v, -3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
|
||||
|
||||
static SQInteger base_assert(HSQUIRRELVM v)
|
||||
{
|
||||
if(v->IsFalse(stack_get(v,2))){
|
||||
return sq_throwerror(v,"assertion failed");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx,SQObjectPtr &o)
|
||||
{
|
||||
SQInteger top = sq_gettop(v);
|
||||
sidx=0;
|
||||
eidx=0;
|
||||
o=stack_get(v,1);
|
||||
SQObjectPtr &start=stack_get(v,2);
|
||||
if(type(start)!=OT_NULL && sq_isnumeric(start)){
|
||||
sidx=tointeger(start);
|
||||
}
|
||||
if(top>2){
|
||||
SQObjectPtr &end=stack_get(v,3);
|
||||
if(sq_isnumeric(end)){
|
||||
eidx=tointeger(end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
eidx = sq_getsize(v,1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger base_print(HSQUIRRELVM v)
|
||||
{
|
||||
const SQChar *str;
|
||||
sq_tostring(v,2);
|
||||
sq_getstring(v,-1,&str);
|
||||
if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,"%s",str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
static SQInteger base_compilestring(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger nargs=sq_gettop(v);
|
||||
const SQChar *src=NULL,*name="unnamedbuffer";
|
||||
SQInteger size;
|
||||
sq_getstring(v,2,&src);
|
||||
size=sq_getsize(v,2);
|
||||
if(nargs>2){
|
||||
sq_getstring(v,3,&name);
|
||||
}
|
||||
if(SQ_SUCCEEDED(sq_compilebuffer(v,src,size,name,SQFalse)))
|
||||
return 1;
|
||||
else
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
static SQInteger base_newthread(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr &func = stack_get(v,2);
|
||||
SQInteger stksize = (_funcproto(_closure(func)->_function)->_stacksize << 1) +2;
|
||||
HSQUIRRELVM newv = sq_newthread(v, (stksize < MIN_STACK_OVERHEAD + 2)? MIN_STACK_OVERHEAD + 2 : stksize);
|
||||
sq_move(newv,v,-2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger base_suspend(HSQUIRRELVM v)
|
||||
{
|
||||
return sq_suspendvm(v);
|
||||
}
|
||||
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
|
||||
|
||||
static SQInteger base_array(HSQUIRRELVM v)
|
||||
{
|
||||
SQArray *a;
|
||||
SQInteger nInitialSize = tointeger(stack_get(v,2));
|
||||
SQInteger ret = 1;
|
||||
if (nInitialSize < 0) {
|
||||
v->Raise_Error("can't create/resize array with/to size %d", nInitialSize);
|
||||
nInitialSize = 0;
|
||||
ret = -1;
|
||||
}
|
||||
if(sq_gettop(v) > 2) {
|
||||
a = SQArray::Create(_ss(v),0);
|
||||
a->Resize(nInitialSize,stack_get(v,3));
|
||||
}
|
||||
else {
|
||||
a = SQArray::Create(_ss(v),nInitialSize);
|
||||
}
|
||||
v->Push(a);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SQInteger base_type(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr &o = stack_get(v,2);
|
||||
v->Push(SQString::Create(_ss(v),GetTypeName(o),-1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQRegFunction base_funcs[]={
|
||||
//generic
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
{"seterrorhandler",base_seterrorhandler,2, NULL},
|
||||
{"setdebughook",base_setdebughook,2, NULL},
|
||||
{"enabledebuginfo",base_enabledebuginfo,2, NULL},
|
||||
{"getstackinfos",base_getstackinfos,2, ".n"},
|
||||
{"getroottable",base_getroottable,1, NULL},
|
||||
{"setroottable",base_setroottable,2, NULL},
|
||||
{"getconsttable",base_getconsttable,1, NULL},
|
||||
{"setconsttable",base_setconsttable,2, NULL},
|
||||
#endif
|
||||
{"assert",base_assert,2, NULL},
|
||||
{"print",base_print,2, NULL},
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
{"compilestring",base_compilestring,-2, ".ss"},
|
||||
{"newthread",base_newthread,2, ".c"},
|
||||
{"suspend",base_suspend,-1, NULL},
|
||||
#endif
|
||||
{"array",base_array,-2, ".n"},
|
||||
{"type",base_type,2, NULL},
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
{"dummy",base_dummy,0,NULL},
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
{"collectgarbage",base_collectgarbage,1, "t"},
|
||||
#endif
|
||||
#endif
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
void sq_base_register(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger i=0;
|
||||
sq_pushroottable(v);
|
||||
while(base_funcs[i].name!=0) {
|
||||
sq_pushstring(v,base_funcs[i].name,-1);
|
||||
sq_newclosure(v,base_funcs[i].f,0);
|
||||
sq_setnativeclosurename(v,-1,base_funcs[i].name);
|
||||
sq_setparamscheck(v,base_funcs[i].nparamscheck,base_funcs[i].typemask);
|
||||
sq_createslot(v,-3);
|
||||
i++;
|
||||
}
|
||||
sq_pushstring(v,"_version_",-1);
|
||||
sq_pushstring(v,SQUIRREL_VERSION,-1);
|
||||
sq_createslot(v,-3);
|
||||
sq_pushstring(v,"_charsize_",-1);
|
||||
sq_pushinteger(v,sizeof(SQChar));
|
||||
sq_createslot(v,-3);
|
||||
sq_pushstring(v,"_intsize_",-1);
|
||||
sq_pushinteger(v,sizeof(SQInteger));
|
||||
sq_createslot(v,-3);
|
||||
sq_pushstring(v,"_floatsize_",-1);
|
||||
sq_pushinteger(v,sizeof(SQFloat));
|
||||
sq_createslot(v,-3);
|
||||
sq_pop(v,1);
|
||||
}
|
||||
|
||||
static SQInteger default_delegate_len(HSQUIRRELVM v)
|
||||
{
|
||||
v->Push(SQInteger(sq_getsize(v,1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger default_delegate_tofloat(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr &o=stack_get(v,1);
|
||||
switch(type(o)){
|
||||
case OT_STRING:{
|
||||
SQObjectPtr res;
|
||||
if(str2num(_stringval(o),res)){
|
||||
v->Push(SQObjectPtr(tofloat(res)));
|
||||
break;
|
||||
}}
|
||||
return sq_throwerror(v, "cannot convert the string");
|
||||
break;
|
||||
case OT_INTEGER:case OT_FLOAT:
|
||||
v->Push(SQObjectPtr(tofloat(o)));
|
||||
break;
|
||||
case OT_BOOL:
|
||||
v->Push(SQObjectPtr((SQFloat)(_integer(o)?1:0)));
|
||||
break;
|
||||
default:
|
||||
v->Push(_null_);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger default_delegate_tointeger(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr &o=stack_get(v,1);
|
||||
switch(type(o)){
|
||||
case OT_STRING:{
|
||||
SQObjectPtr res;
|
||||
if(str2num(_stringval(o),res)){
|
||||
v->Push(SQObjectPtr(tointeger(res)));
|
||||
break;
|
||||
}}
|
||||
return sq_throwerror(v, "cannot convert the string");
|
||||
break;
|
||||
case OT_INTEGER:case OT_FLOAT:
|
||||
v->Push(SQObjectPtr(tointeger(o)));
|
||||
break;
|
||||
case OT_BOOL:
|
||||
v->Push(SQObjectPtr(_integer(o)?(SQInteger)1:(SQInteger)0));
|
||||
break;
|
||||
default:
|
||||
v->Push(_null_);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger default_delegate_tostring(HSQUIRRELVM v)
|
||||
{
|
||||
sq_tostring(v,1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger obj_delegate_weakref(HSQUIRRELVM v)
|
||||
{
|
||||
sq_weakref(v,1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger obj_clear(HSQUIRRELVM v)
|
||||
{
|
||||
return sq_clear(v,-1);
|
||||
}
|
||||
|
||||
|
||||
static SQInteger number_delegate_tochar(HSQUIRRELVM v)
|
||||
{
|
||||
SQObject &o=stack_get(v,1);
|
||||
SQChar c = (SQChar)tointeger(o);
|
||||
v->Push(SQString::Create(_ss(v),(const SQChar *)&c,1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
//TABLE DEFAULT DELEGATE
|
||||
|
||||
static SQInteger table_rawdelete(HSQUIRRELVM v)
|
||||
{
|
||||
if(SQ_FAILED(sq_rawdeleteslot(v,1,SQTrue)))
|
||||
return SQ_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static SQInteger container_rawexists(HSQUIRRELVM v)
|
||||
{
|
||||
if(SQ_SUCCEEDED(sq_rawget(v,-2))) {
|
||||
sq_pushbool(v,SQTrue);
|
||||
return 1;
|
||||
}
|
||||
sq_pushbool(v,SQFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger table_rawset(HSQUIRRELVM v)
|
||||
{
|
||||
return sq_rawset(v,-3);
|
||||
}
|
||||
|
||||
|
||||
static SQInteger table_rawget(HSQUIRRELVM v)
|
||||
{
|
||||
return SQ_SUCCEEDED(sq_rawget(v,-2))?1:SQ_ERROR;
|
||||
}
|
||||
|
||||
|
||||
SQRegFunction SQSharedState::_table_default_delegate_funcz[]={
|
||||
{"len",default_delegate_len,1, "t"},
|
||||
{"rawget",table_rawget,2, "t"},
|
||||
{"rawset",table_rawset,3, "t"},
|
||||
{"rawdelete",table_rawdelete,2, "t"},
|
||||
{"rawin",container_rawexists,2, "t"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"clear",obj_clear,1, "."},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
//ARRAY DEFAULT DELEGATE///////////////////////////////////////
|
||||
|
||||
static SQInteger array_append(HSQUIRRELVM v)
|
||||
{
|
||||
return sq_arrayappend(v,-2);
|
||||
}
|
||||
|
||||
static SQInteger array_extend(HSQUIRRELVM v)
|
||||
{
|
||||
_array(stack_get(v,1))->Extend(_array(stack_get(v,2)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger array_reverse(HSQUIRRELVM v)
|
||||
{
|
||||
return sq_arrayreverse(v,-1);
|
||||
}
|
||||
|
||||
static SQInteger array_pop(HSQUIRRELVM v)
|
||||
{
|
||||
return SQ_SUCCEEDED(sq_arraypop(v,1,SQTrue))?1:SQ_ERROR;
|
||||
}
|
||||
|
||||
static SQInteger array_top(HSQUIRRELVM v)
|
||||
{
|
||||
SQObject &o=stack_get(v,1);
|
||||
if(_array(o)->Size()>0){
|
||||
v->Push(_array(o)->Top());
|
||||
return 1;
|
||||
}
|
||||
else return sq_throwerror(v,"top() on a empty array");
|
||||
}
|
||||
|
||||
static SQInteger array_insert(HSQUIRRELVM v)
|
||||
{
|
||||
SQObject &o=stack_get(v,1);
|
||||
SQObject &idx=stack_get(v,2);
|
||||
SQObject &val=stack_get(v,3);
|
||||
if(!_array(o)->Insert(tointeger(idx),val))
|
||||
return sq_throwerror(v,"index out of range");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger array_remove(HSQUIRRELVM v)
|
||||
{
|
||||
SQObject &o = stack_get(v, 1);
|
||||
SQObject &idx = stack_get(v, 2);
|
||||
if(!sq_isnumeric(idx)) return sq_throwerror(v, "wrong type");
|
||||
SQObjectPtr val;
|
||||
if(_array(o)->Get(tointeger(idx), val)) {
|
||||
_array(o)->Remove(tointeger(idx));
|
||||
v->Push(val);
|
||||
return 1;
|
||||
}
|
||||
return sq_throwerror(v, "idx out of range");
|
||||
}
|
||||
|
||||
static SQInteger array_resize(HSQUIRRELVM v)
|
||||
{
|
||||
SQObject &o = stack_get(v, 1);
|
||||
SQObject &nsize = stack_get(v, 2);
|
||||
SQObjectPtr fill;
|
||||
if(sq_isnumeric(nsize)) {
|
||||
if(sq_gettop(v) > 2)
|
||||
fill = stack_get(v, 3);
|
||||
_array(o)->Resize(tointeger(nsize),fill);
|
||||
return 0;
|
||||
}
|
||||
return sq_throwerror(v, "size must be a number");
|
||||
}
|
||||
|
||||
|
||||
bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret)
|
||||
{
|
||||
if(func < 0) {
|
||||
if(!v->ObjCmp(a,b,ret)) return false;
|
||||
}
|
||||
else {
|
||||
SQInteger top = sq_gettop(v);
|
||||
sq_push(v, func);
|
||||
sq_pushroottable(v);
|
||||
v->Push(a);
|
||||
v->Push(b);
|
||||
if(SQ_FAILED(sq_call(v, 3, SQTrue, SQFalse))) {
|
||||
if(!sq_isstring( v->_lasterror))
|
||||
v->Raise_Error("compare func failed");
|
||||
return false;
|
||||
}
|
||||
if(SQ_FAILED(sq_getinteger(v, -1, &ret))) {
|
||||
v->Raise_Error("numeric value expected as return value of the compare function");
|
||||
return false;
|
||||
}
|
||||
sq_settop(v, top);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bottom, SQInteger func)
|
||||
{
|
||||
SQInteger maxChild;
|
||||
SQInteger done = 0;
|
||||
SQInteger ret;
|
||||
SQInteger root2;
|
||||
while (((root2 = root * 2) <= bottom) && (!done))
|
||||
{
|
||||
if (root2 == bottom) {
|
||||
maxChild = root2;
|
||||
}
|
||||
else {
|
||||
if(!_sort_compare(v,arr->_values[root2],arr->_values[root2 + 1],func,ret))
|
||||
return false;
|
||||
if (ret > 0) {
|
||||
maxChild = root2;
|
||||
}
|
||||
else {
|
||||
maxChild = root2 + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!_sort_compare(v,arr->_values[root],arr->_values[maxChild],func,ret))
|
||||
return false;
|
||||
if (ret < 0) {
|
||||
if (root == maxChild) {
|
||||
v->Raise_Error("inconsistent compare function");
|
||||
return false; // We'd be swapping ourselve. The compare function is incorrect
|
||||
}
|
||||
_Swap(arr->_values[root],arr->_values[maxChild]);
|
||||
root = maxChild;
|
||||
}
|
||||
else {
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _hsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger l, SQInteger r,SQInteger func)
|
||||
{
|
||||
SQArray *a = _array(arr);
|
||||
SQInteger i;
|
||||
SQInteger array_size = a->Size();
|
||||
for (i = (array_size / 2); i >= 0; i--) {
|
||||
if(!_hsort_sift_down(v,a, i, array_size - 1,func)) return false;
|
||||
}
|
||||
|
||||
for (i = array_size-1; i >= 1; i--)
|
||||
{
|
||||
_Swap(a->_values[0],a->_values[i]);
|
||||
if(!_hsort_sift_down(v,a, 0, i-1,func)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static SQInteger array_sort(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger func = -1;
|
||||
SQObjectPtr &o = stack_get(v,1);
|
||||
if(_array(o)->Size() > 1) {
|
||||
if(sq_gettop(v) == 2) func = 2;
|
||||
if(!_hsort(v, o, 0, _array(o)->Size()-1, func))
|
||||
return SQ_ERROR;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SQInteger array_slice(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger sidx,eidx;
|
||||
SQObjectPtr o;
|
||||
if(get_slice_params(v,sidx,eidx,o)==-1)return -1;
|
||||
SQInteger alen = _array(o)->Size();
|
||||
if(sidx < 0)sidx = alen + sidx;
|
||||
if(eidx < 0)eidx = alen + eidx;
|
||||
if(eidx < sidx)return sq_throwerror(v,"wrong indexes");
|
||||
if(eidx > alen)return sq_throwerror(v,"slice out of range");
|
||||
SQArray *arr=SQArray::Create(_ss(v),eidx-sidx);
|
||||
SQObjectPtr t;
|
||||
SQInteger count=0;
|
||||
for(SQInteger i=sidx;i<eidx;i++){
|
||||
_array(o)->Get(i,t);
|
||||
arr->Set(count++,t);
|
||||
}
|
||||
v->Push(arr);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_array_default_delegate_funcz[]={
|
||||
{"len",default_delegate_len,1, "a"},
|
||||
{"append",array_append,2, "a"},
|
||||
{"extend",array_extend,2, "aa"},
|
||||
{"push",array_append,2, "a"},
|
||||
{"pop",array_pop,1, "a"},
|
||||
{"top",array_top,1, "a"},
|
||||
{"insert",array_insert,3, "an"},
|
||||
{"remove",array_remove,2, "an"},
|
||||
{"resize",array_resize,-2, "an"},
|
||||
{"reverse",array_reverse,1, "a"},
|
||||
{"sort",array_sort,-1, "ac"},
|
||||
{"slice",array_slice,-1, "ann"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"clear",obj_clear,1, "."},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
//STRING DEFAULT DELEGATE//////////////////////////
|
||||
static SQInteger string_slice(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger sidx,eidx;
|
||||
SQObjectPtr o;
|
||||
if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1;
|
||||
SQInteger slen = _string(o)->_len;
|
||||
if(sidx < 0)sidx = slen + sidx;
|
||||
if(eidx < 0)eidx = slen + eidx;
|
||||
if(eidx < sidx) return sq_throwerror(v,"wrong indexes");
|
||||
if(eidx > slen) return sq_throwerror(v,"slice out of range");
|
||||
v->Push(SQString::Create(_ss(v),&_stringval(o)[sidx],eidx-sidx));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger string_find(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger top,start_idx=0;
|
||||
const SQChar *str,*substr,*ret;
|
||||
if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){
|
||||
if(top>2)sq_getinteger(v,3,&start_idx);
|
||||
if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){
|
||||
ret=strstr(&str[start_idx],substr);
|
||||
if(ret){
|
||||
sq_pushinteger(v,(SQInteger)(ret-str));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return sq_throwerror(v,"invalid param");
|
||||
}
|
||||
|
||||
#define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \
|
||||
{ \
|
||||
SQObject str=stack_get(v,1); \
|
||||
SQInteger len=_string(str)->_len; \
|
||||
const SQChar *sThis=_stringval(str); \
|
||||
SQChar *sNew=(_ss(v)->GetScratchPad(len)); \
|
||||
for(SQInteger i=0;i<len;i++) sNew[i]=func(sThis[i]); \
|
||||
v->Push(SQString::Create(_ss(v),sNew,len)); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
|
||||
STRING_TOFUNCZ(tolower)
|
||||
STRING_TOFUNCZ(toupper)
|
||||
|
||||
SQRegFunction SQSharedState::_string_default_delegate_funcz[]={
|
||||
{"len",default_delegate_len,1, "s"},
|
||||
{"tointeger",default_delegate_tointeger,1, "s"},
|
||||
{"tofloat",default_delegate_tofloat,1, "s"},
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"slice",string_slice,-1, " s n n"},
|
||||
{"find",string_find,-2, "s s n "},
|
||||
{"tolower",string_tolower,1, "s"},
|
||||
{"toupper",string_toupper,1, "s"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
//INTEGER DEFAULT DELEGATE//////////////////////////
|
||||
SQRegFunction SQSharedState::_number_default_delegate_funcz[]={
|
||||
{"tointeger",default_delegate_tointeger,1, "n|b"},
|
||||
{"tofloat",default_delegate_tofloat,1, "n|b"},
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"tochar",number_delegate_tochar,1, "n|b"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
//CLOSURE DEFAULT DELEGATE//////////////////////////
|
||||
static SQInteger closure_pcall(HSQUIRRELVM v)
|
||||
{
|
||||
return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQFalse))?1:SQ_ERROR;
|
||||
}
|
||||
|
||||
static SQInteger closure_call(HSQUIRRELVM v)
|
||||
{
|
||||
return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQTrue))?1:SQ_ERROR;
|
||||
}
|
||||
|
||||
static SQInteger _closure_acall(HSQUIRRELVM v,SQBool raiseerror)
|
||||
{
|
||||
SQArray *aparams=_array(stack_get(v,2));
|
||||
SQInteger nparams=aparams->Size();
|
||||
v->Push(stack_get(v,1));
|
||||
for(SQInteger i=0;i<nparams;i++)v->Push(aparams->_values[i]);
|
||||
return SQ_SUCCEEDED(sq_call(v,nparams,SQTrue,raiseerror))?1:SQ_ERROR;
|
||||
}
|
||||
|
||||
static SQInteger closure_acall(HSQUIRRELVM v)
|
||||
{
|
||||
return _closure_acall(v,SQTrue);
|
||||
}
|
||||
|
||||
static SQInteger closure_pacall(HSQUIRRELVM v)
|
||||
{
|
||||
return _closure_acall(v,SQFalse);
|
||||
}
|
||||
|
||||
static SQInteger closure_bindenv(HSQUIRRELVM v)
|
||||
{
|
||||
if(SQ_FAILED(sq_bindenv(v,1)))
|
||||
return SQ_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SQInteger closure_getinfos(HSQUIRRELVM v) {
|
||||
SQObject o = stack_get(v,1);
|
||||
SQTable *res = SQTable::Create(_ss(v),4);
|
||||
if(type(o) == OT_CLOSURE) {
|
||||
SQFunctionProto *f = _funcproto(_closure(o)->_function);
|
||||
SQInteger nparams = f->_nparameters + (f->_varparams?1:0);
|
||||
SQObjectPtr params = SQArray::Create(_ss(v),nparams);
|
||||
for(SQInteger n = 0; n<f->_nparameters; n++) {
|
||||
_array(params)->Set((SQInteger)n,f->_parameters[n]);
|
||||
}
|
||||
if(f->_varparams) {
|
||||
_array(params)->Set(nparams-1,SQString::Create(_ss(v),"...",-1));
|
||||
}
|
||||
res->NewSlot(SQString::Create(_ss(v),"native",-1),false);
|
||||
res->NewSlot(SQString::Create(_ss(v),"name",-1),f->_name);
|
||||
res->NewSlot(SQString::Create(_ss(v),"src",-1),f->_sourcename);
|
||||
res->NewSlot(SQString::Create(_ss(v),"parameters",-1),params);
|
||||
res->NewSlot(SQString::Create(_ss(v),"varargs",-1),f->_varparams);
|
||||
}
|
||||
else { //OT_NATIVECLOSURE
|
||||
SQNativeClosure *nc = _nativeclosure(o);
|
||||
res->NewSlot(SQString::Create(_ss(v),"native",-1),true);
|
||||
res->NewSlot(SQString::Create(_ss(v),"name",-1),nc->_name);
|
||||
res->NewSlot(SQString::Create(_ss(v),"paramscheck",-1),nc->_nparamscheck);
|
||||
SQObjectPtr typecheck;
|
||||
if(nc->_typecheck.size() > 0) {
|
||||
typecheck =
|
||||
SQArray::Create(_ss(v), nc->_typecheck.size());
|
||||
for(SQUnsignedInteger n = 0; n<nc->_typecheck.size(); n++) {
|
||||
_array(typecheck)->Set((SQInteger)n,nc->_typecheck[n]);
|
||||
}
|
||||
}
|
||||
res->NewSlot(SQString::Create(_ss(v),"typecheck",-1),typecheck);
|
||||
}
|
||||
v->Push(res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
SQRegFunction SQSharedState::_closure_default_delegate_funcz[]={
|
||||
{"call",closure_call,-1, "c"},
|
||||
{"pcall",closure_pcall,-1, "c"},
|
||||
{"acall",closure_acall,2, "ca"},
|
||||
{"pacall",closure_pacall,2, "ca"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"bindenv",closure_bindenv,2, "c x|y|t"},
|
||||
{"getinfos",closure_getinfos,1, "c"},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
//GENERATOR DEFAULT DELEGATE
|
||||
static SQInteger generator_getstatus(HSQUIRRELVM v)
|
||||
{
|
||||
SQObject &o=stack_get(v,1);
|
||||
switch(_generator(o)->_state){
|
||||
case SQGenerator::eSuspended:v->Push(SQString::Create(_ss(v),"suspended"));break;
|
||||
case SQGenerator::eRunning:v->Push(SQString::Create(_ss(v),"running"));break;
|
||||
case SQGenerator::eDead:v->Push(SQString::Create(_ss(v),"dead"));break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={
|
||||
{"getstatus",generator_getstatus,1, "g"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
//THREAD DEFAULT DELEGATE
|
||||
|
||||
static SQInteger thread_call(HSQUIRRELVM v)
|
||||
{
|
||||
|
||||
SQObjectPtr o = stack_get(v,1);
|
||||
if(type(o) == OT_THREAD) {
|
||||
SQInteger nparams = sq_gettop(v);
|
||||
_thread(o)->Push(_thread(o)->_roottable);
|
||||
for(SQInteger i = 2; i<(nparams+1); i++)
|
||||
sq_move(_thread(o),v,i);
|
||||
if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQFalse))) {
|
||||
sq_move(v,_thread(o),-1);
|
||||
sq_pop(_thread(o),1);
|
||||
return 1;
|
||||
}
|
||||
v->_lasterror = _thread(o)->_lasterror;
|
||||
return SQ_ERROR;
|
||||
}
|
||||
return sq_throwerror(v,"wrong parameter");
|
||||
}
|
||||
|
||||
static SQInteger thread_wakeup(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr o = stack_get(v,1);
|
||||
if(type(o) == OT_THREAD) {
|
||||
SQVM *thread = _thread(o);
|
||||
SQInteger state = sq_getvmstate(thread);
|
||||
if(state != SQ_VMSTATE_SUSPENDED) {
|
||||
switch(state) {
|
||||
case SQ_VMSTATE_IDLE:
|
||||
return sq_throwerror(v,"cannot wakeup a idle thread");
|
||||
break;
|
||||
case SQ_VMSTATE_RUNNING:
|
||||
return sq_throwerror(v,"cannot wakeup a running thread");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SQInteger wakeupret = sq_gettop(v)>1?1:0;
|
||||
if(wakeupret) {
|
||||
sq_move(thread,v,2);
|
||||
}
|
||||
if(SQ_SUCCEEDED(sq_wakeupvm(thread,wakeupret,SQTrue,SQTrue,SQFalse))) {
|
||||
sq_move(v,thread,-1);
|
||||
sq_pop(thread,1); //pop retval
|
||||
if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) {
|
||||
sq_settop(thread,1); //pop roottable
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
sq_settop(thread,1);
|
||||
v->_lasterror = thread->_lasterror;
|
||||
return SQ_ERROR;
|
||||
}
|
||||
return sq_throwerror(v,"wrong parameter");
|
||||
}
|
||||
|
||||
static SQInteger thread_getstatus(HSQUIRRELVM v)
|
||||
{
|
||||
SQObjectPtr &o = stack_get(v,1);
|
||||
switch(sq_getvmstate(_thread(o))) {
|
||||
case SQ_VMSTATE_IDLE:
|
||||
sq_pushstring(v,"idle",-1);
|
||||
break;
|
||||
case SQ_VMSTATE_RUNNING:
|
||||
sq_pushstring(v,"running",-1);
|
||||
break;
|
||||
case SQ_VMSTATE_SUSPENDED:
|
||||
sq_pushstring(v,"suspended",-1);
|
||||
break;
|
||||
default:
|
||||
return sq_throwerror(v,"internal VM error");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_thread_default_delegate_funcz[] = {
|
||||
{"call", thread_call, -1, "v"},
|
||||
{"wakeup", thread_wakeup, -1, "v"},
|
||||
{"getstatus", thread_getstatus, 1, "v"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{0,0,0,0},
|
||||
};
|
||||
|
||||
static SQInteger class_getattributes(HSQUIRRELVM v)
|
||||
{
|
||||
if(SQ_SUCCEEDED(sq_getattributes(v,-2)))
|
||||
return 1;
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
static SQInteger class_setattributes(HSQUIRRELVM v)
|
||||
{
|
||||
if(SQ_SUCCEEDED(sq_setattributes(v,-3)))
|
||||
return 1;
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
static SQInteger class_instance(HSQUIRRELVM v)
|
||||
{
|
||||
if(SQ_SUCCEEDED(sq_createinstance(v,-1)))
|
||||
return 1;
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_class_default_delegate_funcz[] = {
|
||||
{"getattributes", class_getattributes, 2, "y."},
|
||||
{"setattributes", class_setattributes, 3, "y.."},
|
||||
{"rawin",container_rawexists,2, "y"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"instance",class_instance,1, "y"},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
static SQInteger instance_getclass(HSQUIRRELVM v)
|
||||
{
|
||||
if(SQ_SUCCEEDED(sq_getclass(v,1)))
|
||||
return 1;
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_instance_default_delegate_funcz[] = {
|
||||
{"getclass", instance_getclass, 1, "x"},
|
||||
{"rawin",container_rawexists,2, "x"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
static SQInteger weakref_ref(HSQUIRRELVM v)
|
||||
{
|
||||
if(SQ_FAILED(sq_getweakrefval(v,1)))
|
||||
return SQ_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_weakref_default_delegate_funcz[] = {
|
||||
{"ref",weakref_ref,1, "r"},
|
||||
{"weakref",obj_delegate_weakref,1, NULL },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
|
||||
199
src/3rdparty/squirrel/squirrel/sqclass.cpp
vendored
Normal file
199
src/3rdparty/squirrel/squirrel/sqclass.cpp
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include "sqpcheader.h"
|
||||
#include "sqvm.h"
|
||||
#include "sqtable.h"
|
||||
#include "sqclass.h"
|
||||
#include "sqclosure.h"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
SQClass::SQClass(SQSharedState *ss,SQClass *base)
|
||||
{
|
||||
_base = base;
|
||||
_typetag = 0;
|
||||
_hook = NULL;
|
||||
_udsize = 0;
|
||||
_metamethods.resize(MT_LAST); //size it to max size
|
||||
if(_base) {
|
||||
_defaultvalues.copy(base->_defaultvalues);
|
||||
_methods.copy(base->_methods);
|
||||
_metamethods.copy(base->_metamethods);
|
||||
__ObjAddRef(_base);
|
||||
}
|
||||
_members = base?base->_members->Clone() : SQTable::Create(ss,0);
|
||||
__ObjAddRef(_members);
|
||||
_locked = false;
|
||||
INIT_CHAIN();
|
||||
ADD_TO_CHAIN(&_sharedstate->_gc_chain, this);
|
||||
}
|
||||
|
||||
void SQClass::Finalize() {
|
||||
_attributes = _null_;
|
||||
_defaultvalues.resize(0);
|
||||
_methods.resize(0);
|
||||
_metamethods.resize(0);
|
||||
__ObjRelease(_members);
|
||||
if(_base) {
|
||||
__ObjRelease(_base);
|
||||
}
|
||||
}
|
||||
|
||||
SQClass::~SQClass()
|
||||
{
|
||||
REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
|
||||
Finalize();
|
||||
}
|
||||
|
||||
bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic)
|
||||
{
|
||||
SQObjectPtr temp;
|
||||
if(_locked)
|
||||
return false; //the class already has an instance so cannot be modified
|
||||
if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value
|
||||
{
|
||||
_defaultvalues[_member_idx(temp)].val = val;
|
||||
return true;
|
||||
}
|
||||
if(type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE || bstatic) {
|
||||
SQInteger mmidx;
|
||||
if((type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE) &&
|
||||
(mmidx = ss->GetMetaMethodIdxByName(key)) != -1) {
|
||||
_metamethods[mmidx] = val;
|
||||
}
|
||||
else {
|
||||
if(type(temp) == OT_NULL) {
|
||||
SQClassMember m;
|
||||
m.val = val;
|
||||
_members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size())));
|
||||
_methods.push_back(m);
|
||||
}
|
||||
else {
|
||||
_methods[_member_idx(temp)].val = val;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
SQClassMember m;
|
||||
m.val = val;
|
||||
_members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size())));
|
||||
_defaultvalues.push_back(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
SQInstance *SQClass::CreateInstance()
|
||||
{
|
||||
if(!_locked) Lock();
|
||||
return SQInstance::Create(_opt_ss(this),this);
|
||||
}
|
||||
|
||||
SQInteger SQClass::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
|
||||
{
|
||||
SQObjectPtr oval;
|
||||
SQInteger idx = _members->Next(false,refpos,outkey,oval);
|
||||
if(idx != -1) {
|
||||
if(_ismethod(oval)) {
|
||||
outval = _methods[_member_idx(oval)].val;
|
||||
}
|
||||
else {
|
||||
SQObjectPtr &o = _defaultvalues[_member_idx(oval)].val;
|
||||
outval = _realval(o);
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
bool SQClass::SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val)
|
||||
{
|
||||
SQObjectPtr idx;
|
||||
if(_members->Get(key,idx)) {
|
||||
if(_isfield(idx))
|
||||
_defaultvalues[_member_idx(idx)].attrs = val;
|
||||
else
|
||||
_methods[_member_idx(idx)].attrs = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SQClass::GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval)
|
||||
{
|
||||
SQObjectPtr idx;
|
||||
if(_members->Get(key,idx)) {
|
||||
outval = (_isfield(idx)?_defaultvalues[_member_idx(idx)].attrs:_methods[_member_idx(idx)].attrs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
void SQInstance::Init(SQSharedState *ss)
|
||||
{
|
||||
_userpointer = NULL;
|
||||
_hook = NULL;
|
||||
__ObjAddRef(_class);
|
||||
_delegate = _class->_members;
|
||||
INIT_CHAIN();
|
||||
ADD_TO_CHAIN(&_sharedstate->_gc_chain, this);
|
||||
}
|
||||
|
||||
SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize)
|
||||
{
|
||||
_memsize = memsize;
|
||||
_class = c;
|
||||
SQUnsignedInteger nvalues = _class->_defaultvalues.size();
|
||||
for(SQUnsignedInteger n = 0; n < nvalues; n++) {
|
||||
new (&_values[n]) SQObjectPtr(_class->_defaultvalues[n].val);
|
||||
}
|
||||
Init(ss);
|
||||
}
|
||||
|
||||
SQInstance::SQInstance(SQSharedState *ss, SQInstance *i, SQInteger memsize)
|
||||
{
|
||||
_memsize = memsize;
|
||||
_class = i->_class;
|
||||
SQUnsignedInteger nvalues = _class->_defaultvalues.size();
|
||||
for(SQUnsignedInteger n = 0; n < nvalues; n++) {
|
||||
new (&_values[n]) SQObjectPtr(i->_values[n]);
|
||||
}
|
||||
Init(ss);
|
||||
}
|
||||
|
||||
void SQInstance::Finalize()
|
||||
{
|
||||
SQUnsignedInteger nvalues = _class->_defaultvalues.size();
|
||||
__ObjRelease(_class);
|
||||
for(SQUnsignedInteger i = 0; i < nvalues; i++) {
|
||||
_values[i] = _null_;
|
||||
}
|
||||
}
|
||||
|
||||
SQInstance::~SQInstance()
|
||||
{
|
||||
REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
|
||||
if(_class){ Finalize(); } //if _class is null it was already finalized by the GC
|
||||
}
|
||||
|
||||
bool SQInstance::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res)
|
||||
{
|
||||
if(type(_class->_metamethods[mm]) != OT_NULL) {
|
||||
res = _class->_metamethods[mm];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SQInstance::InstanceOf(SQClass *trg)
|
||||
{
|
||||
SQClass *parent = _class;
|
||||
while(parent != NULL) {
|
||||
if(parent == trg)
|
||||
return true;
|
||||
parent = parent->_base;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
162
src/3rdparty/squirrel/squirrel/sqclass.h
vendored
Normal file
162
src/3rdparty/squirrel/squirrel/sqclass.h
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQCLASS_H_
|
||||
#define _SQCLASS_H_
|
||||
|
||||
struct SQInstance;
|
||||
|
||||
struct SQClassMember {
|
||||
SQClassMember(){}
|
||||
SQClassMember(const SQClassMember &o) {
|
||||
val = o.val;
|
||||
attrs = o.attrs;
|
||||
}
|
||||
SQObjectPtr val;
|
||||
SQObjectPtr attrs;
|
||||
};
|
||||
|
||||
typedef sqvector<SQClassMember> SQClassMemberVec;
|
||||
|
||||
#define MEMBER_TYPE_METHOD 0x01000000
|
||||
#define MEMBER_TYPE_FIELD 0x02000000
|
||||
|
||||
#define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD)
|
||||
#define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD)
|
||||
#define _make_method_idx(i) ((SQInteger)(MEMBER_TYPE_METHOD|i))
|
||||
#define _make_field_idx(i) ((SQInteger)(MEMBER_TYPE_FIELD|i))
|
||||
#define _member_type(o) (_integer(o)&0xFF000000)
|
||||
#define _member_idx(o) (_integer(o)&0x00FFFFFF)
|
||||
|
||||
struct SQClass : public CHAINABLE_OBJ
|
||||
{
|
||||
SQClass(SQSharedState *ss,SQClass *base);
|
||||
public:
|
||||
static SQClass* Create(SQSharedState *ss,SQClass *base) {
|
||||
SQClass *newclass = (SQClass *)SQ_MALLOC(sizeof(SQClass));
|
||||
new (newclass) SQClass(ss, base);
|
||||
return newclass;
|
||||
}
|
||||
~SQClass();
|
||||
bool NewSlot(SQSharedState *ss, const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic);
|
||||
bool Get(const SQObjectPtr &key,SQObjectPtr &val) {
|
||||
if(_members->Get(key,val)) {
|
||||
if(_isfield(val)) {
|
||||
SQObjectPtr &o = _defaultvalues[_member_idx(val)].val;
|
||||
val = _realval(o);
|
||||
}
|
||||
else {
|
||||
val = _methods[_member_idx(val)].val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val);
|
||||
bool GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval);
|
||||
void Lock() { _locked = true; if(_base) _base->Lock(); }
|
||||
void Release() {
|
||||
if (_hook) { _hook(_typetag,0);}
|
||||
sq_delete(this, SQClass);
|
||||
}
|
||||
void Finalize();
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable ** );
|
||||
#endif
|
||||
SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
|
||||
SQInstance *CreateInstance();
|
||||
SQTable *_members;
|
||||
SQClass *_base;
|
||||
SQClassMemberVec _defaultvalues;
|
||||
SQClassMemberVec _methods;
|
||||
SQObjectPtrVec _metamethods;
|
||||
SQObjectPtr _attributes;
|
||||
SQUserPointer _typetag;
|
||||
SQRELEASEHOOK _hook;
|
||||
bool _locked;
|
||||
SQInteger _udsize;
|
||||
};
|
||||
|
||||
#define calcinstancesize(_theclass_) \
|
||||
(_theclass_->_udsize + sizeof(SQInstance) + (sizeof(SQObjectPtr)*(_theclass_->_defaultvalues.size()>0?_theclass_->_defaultvalues.size()-1:0)))
|
||||
|
||||
struct SQInstance : public SQDelegable
|
||||
{
|
||||
void Init(SQSharedState *ss);
|
||||
SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize);
|
||||
SQInstance(SQSharedState *ss, SQInstance *c, SQInteger memsize);
|
||||
public:
|
||||
static SQInstance* Create(SQSharedState *ss,SQClass *theclass) {
|
||||
|
||||
SQInteger size = calcinstancesize(theclass);
|
||||
SQInstance *newinst = (SQInstance *)SQ_MALLOC(size);
|
||||
new (newinst) SQInstance(ss, theclass,size);
|
||||
if(theclass->_udsize) {
|
||||
newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize);
|
||||
}
|
||||
return newinst;
|
||||
}
|
||||
SQInstance *Clone(SQSharedState *ss)
|
||||
{
|
||||
SQInteger size = calcinstancesize(_class);
|
||||
SQInstance *newinst = (SQInstance *)SQ_MALLOC(size);
|
||||
new (newinst) SQInstance(ss, this,size);
|
||||
if(_class->_udsize) {
|
||||
newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize);
|
||||
}
|
||||
return newinst;
|
||||
}
|
||||
~SQInstance();
|
||||
bool Get(const SQObjectPtr &key,SQObjectPtr &val) {
|
||||
if(_class->_members->Get(key,val)) {
|
||||
if(_isfield(val)) {
|
||||
SQObjectPtr &o = _values[_member_idx(val)];
|
||||
val = _realval(o);
|
||||
}
|
||||
else {
|
||||
val = _class->_methods[_member_idx(val)].val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Set(const SQObjectPtr &key,const SQObjectPtr &val) {
|
||||
SQObjectPtr idx;
|
||||
if(_class->_members->Get(key,idx) && _isfield(idx)) {
|
||||
_values[_member_idx(idx)] = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Release() {
|
||||
_uiRef++;
|
||||
try {
|
||||
if (_hook) { _hook(_userpointer,0);}
|
||||
} catch (...) {
|
||||
_uiRef--;
|
||||
if (_uiRef == 0) {
|
||||
SQInteger size = _memsize;
|
||||
this->~SQInstance();
|
||||
SQ_FREE(this, size);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
_uiRef--;
|
||||
if(_uiRef > 0) return;
|
||||
SQInteger size = _memsize;
|
||||
this->~SQInstance();
|
||||
SQ_FREE(this, size);
|
||||
}
|
||||
void Finalize();
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable ** );
|
||||
#endif
|
||||
bool InstanceOf(SQClass *trg);
|
||||
bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res);
|
||||
|
||||
SQClass *_class;
|
||||
SQUserPointer _userpointer;
|
||||
SQRELEASEHOOK _hook;
|
||||
SQInteger _memsize;
|
||||
SQObjectPtr _values[1];
|
||||
};
|
||||
|
||||
#endif //_SQCLASS_H_
|
||||
122
src/3rdparty/squirrel/squirrel/sqclosure.h
vendored
Normal file
122
src/3rdparty/squirrel/squirrel/sqclosure.h
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQCLOSURE_H_
|
||||
#define _SQCLOSURE_H_
|
||||
|
||||
struct SQFunctionProto;
|
||||
|
||||
struct SQClosure : public CHAINABLE_OBJ
|
||||
{
|
||||
private:
|
||||
SQClosure(SQSharedState *ss,SQFunctionProto *func){_function=func; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
|
||||
public:
|
||||
static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func){
|
||||
SQClosure *nc=(SQClosure*)SQ_MALLOC(sizeof(SQClosure));
|
||||
new (nc) SQClosure(ss,func);
|
||||
return nc;
|
||||
}
|
||||
void Release(){
|
||||
sq_delete(this,SQClosure);
|
||||
}
|
||||
SQClosure *Clone()
|
||||
{
|
||||
SQClosure * ret = SQClosure::Create(_opt_ss(this),_funcproto(_function));
|
||||
ret->_env = _env;
|
||||
ret->_outervalues.copy(_outervalues);
|
||||
ret->_defaultparams.copy(_defaultparams);
|
||||
return ret;
|
||||
}
|
||||
~SQClosure()
|
||||
{
|
||||
REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
|
||||
}
|
||||
bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
|
||||
static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable **chain);
|
||||
void Finalize(){_outervalues.resize(0); }
|
||||
#endif
|
||||
SQObjectPtr _env;
|
||||
SQObjectPtr _function;
|
||||
SQObjectPtrVec _outervalues;
|
||||
SQObjectPtrVec _defaultparams;
|
||||
};
|
||||
//////////////////////////////////////////////
|
||||
struct SQGenerator : public CHAINABLE_OBJ
|
||||
{
|
||||
enum SQGeneratorState{eRunning,eSuspended,eDead};
|
||||
private:
|
||||
SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=NULL;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
|
||||
public:
|
||||
static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){
|
||||
SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator));
|
||||
new (nc) SQGenerator(ss,closure);
|
||||
return nc;
|
||||
}
|
||||
~SQGenerator()
|
||||
{
|
||||
REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
|
||||
}
|
||||
void Kill(){
|
||||
_state=eDead;
|
||||
_stack.resize(0);
|
||||
_closure=_null_;}
|
||||
void Release(){
|
||||
sq_delete(this,SQGenerator);
|
||||
}
|
||||
bool Yield(SQVM *v);
|
||||
bool Resume(SQVM *v,SQInteger target);
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable **chain);
|
||||
void Finalize(){_stack.resize(0);_closure=_null_;}
|
||||
#endif
|
||||
SQObjectPtr _closure;
|
||||
SQObjectPtrVec _stack;
|
||||
SQObjectPtrVec _vargsstack;
|
||||
SQVM::CallInfo _ci;
|
||||
ExceptionsTraps _etraps;
|
||||
SQGeneratorState _state;
|
||||
};
|
||||
|
||||
struct SQNativeClosure : public CHAINABLE_OBJ
|
||||
{
|
||||
private:
|
||||
SQNativeClosure(SQSharedState *ss,SQFUNCTION func) : _nparamscheck(0) {_function=func;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); }
|
||||
public:
|
||||
static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func)
|
||||
{
|
||||
SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(sizeof(SQNativeClosure));
|
||||
new (nc) SQNativeClosure(ss,func);
|
||||
return nc;
|
||||
}
|
||||
SQNativeClosure *Clone()
|
||||
{
|
||||
SQNativeClosure * ret = SQNativeClosure::Create(_opt_ss(this),_function);
|
||||
ret->_env = _env;
|
||||
ret->_name = _name;
|
||||
ret->_outervalues.copy(_outervalues);
|
||||
ret->_typecheck.copy(_typecheck);
|
||||
ret->_nparamscheck = _nparamscheck;
|
||||
return ret;
|
||||
}
|
||||
~SQNativeClosure()
|
||||
{
|
||||
REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
|
||||
}
|
||||
void Release(){
|
||||
sq_delete(this,SQNativeClosure);
|
||||
}
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable **chain);
|
||||
void Finalize(){_outervalues.resize(0);}
|
||||
#endif
|
||||
SQInteger _nparamscheck;
|
||||
SQIntVec _typecheck;
|
||||
SQObjectPtrVec _outervalues;
|
||||
SQObjectPtr _env;
|
||||
SQFUNCTION _function;
|
||||
SQObjectPtr _name;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //_SQCLOSURE_H_
|
||||
1372
src/3rdparty/squirrel/squirrel/sqcompiler.cpp
vendored
Normal file
1372
src/3rdparty/squirrel/squirrel/sqcompiler.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
82
src/3rdparty/squirrel/squirrel/sqcompiler.h
vendored
Normal file
82
src/3rdparty/squirrel/squirrel/sqcompiler.h
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQCOMPILER_H_
|
||||
#define _SQCOMPILER_H_
|
||||
|
||||
struct SQVM;
|
||||
|
||||
#define TK_IDENTIFIER 258
|
||||
#define TK_STRING_LITERAL 259
|
||||
#define TK_INTEGER 260
|
||||
#define TK_FLOAT 261
|
||||
#define TK_DELEGATE 262
|
||||
#define TK_DELETE 263
|
||||
#define TK_EQ 264
|
||||
#define TK_NE 265
|
||||
#define TK_LE 266
|
||||
#define TK_GE 267
|
||||
#define TK_SWITCH 268
|
||||
#define TK_ARROW 269
|
||||
#define TK_AND 270
|
||||
#define TK_OR 271
|
||||
#define TK_IF 272
|
||||
#define TK_ELSE 273
|
||||
#define TK_WHILE 274
|
||||
#define TK_BREAK 275
|
||||
#define TK_FOR 276
|
||||
#define TK_DO 277
|
||||
#define TK_NULL 278
|
||||
#define TK_FOREACH 279
|
||||
#define TK_IN 280
|
||||
#define TK_NEWSLOT 281
|
||||
#define TK_MODULO 282
|
||||
#define TK_LOCAL 283
|
||||
#define TK_CLONE 284
|
||||
#define TK_FUNCTION 285
|
||||
#define TK_RETURN 286
|
||||
#define TK_TYPEOF 287
|
||||
#define TK_UMINUS 288
|
||||
#define TK_PLUSEQ 289
|
||||
#define TK_MINUSEQ 290
|
||||
#define TK_CONTINUE 291
|
||||
#define TK_YIELD 292
|
||||
#define TK_TRY 293
|
||||
#define TK_CATCH 294
|
||||
#define TK_THROW 295
|
||||
#define TK_SHIFTL 296
|
||||
#define TK_SHIFTR 297
|
||||
#define TK_RESUME 298
|
||||
#define TK_DOUBLE_COLON 299
|
||||
#define TK_CASE 300
|
||||
#define TK_DEFAULT 301
|
||||
#define TK_THIS 302
|
||||
#define TK_PLUSPLUS 303
|
||||
#define TK_MINUSMINUS 304
|
||||
#define TK_PARENT 305
|
||||
#define TK_USHIFTR 306
|
||||
#define TK_CLASS 307
|
||||
#define TK_EXTENDS 308
|
||||
#define TK_CONSTRUCTOR 310
|
||||
#define TK_INSTANCEOF 311
|
||||
#define TK_VARPARAMS 312
|
||||
#define TK_VARGC 313
|
||||
#define TK_VARGV 314
|
||||
#define TK_TRUE 315
|
||||
#define TK_FALSE 316
|
||||
#define TK_MULEQ 317
|
||||
#define TK_DIVEQ 318
|
||||
#define TK_MODEQ 319
|
||||
#define TK_ATTR_OPEN 320
|
||||
#define TK_ATTR_CLOSE 321
|
||||
#define TK_STATIC 322
|
||||
#define TK_ENUM 323
|
||||
#define TK_CONST 324
|
||||
|
||||
/* MSVC doesn't like NORETURN for function prototypes, but we kinda need it for GCC. */
|
||||
#if defined(_MSC_VER)
|
||||
typedef void(*CompilerErrorFunc)(void *ud, const SQChar *s);
|
||||
#else
|
||||
typedef NORETURN void(*CompilerErrorFunc)(void *ud, const SQChar *s);
|
||||
#endif
|
||||
|
||||
bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo);
|
||||
#endif //_SQCOMPILER_H_
|
||||
126
src/3rdparty/squirrel/squirrel/sqdebug.cpp
vendored
Normal file
126
src/3rdparty/squirrel/squirrel/sqdebug.cpp
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include <squirrel.h>
|
||||
#include "sqpcheader.h"
|
||||
#include "sqvm.h"
|
||||
#include "sqfuncproto.h"
|
||||
#include "sqclosure.h"
|
||||
#include "sqstring.h"
|
||||
|
||||
#include "../../../core/alloc_func.hpp"
|
||||
#include "../../../string_func.h"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi)
|
||||
{
|
||||
SQInteger cssize = v->_callsstacksize;
|
||||
if (cssize > level) {
|
||||
SQVM::CallInfo &ci = v->_callsstack[cssize-level-1];
|
||||
if(sq_isclosure(ci._closure)) {
|
||||
SQClosure *c = _closure(ci._closure);
|
||||
SQFunctionProto *proto = _funcproto(c->_function);
|
||||
fi->funcid = proto;
|
||||
fi->name = type(proto->_name) == OT_STRING?_stringval(proto->_name):"unknown";
|
||||
fi->source = type(proto->_name) == OT_STRING?_stringval(proto->_sourcename):"unknown";
|
||||
return SQ_OK;
|
||||
}
|
||||
}
|
||||
return sq_throwerror(v,"the object is not a closure");
|
||||
}
|
||||
|
||||
SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si)
|
||||
{
|
||||
SQInteger cssize = v->_callsstacksize;
|
||||
if (cssize > level) {
|
||||
memset(si, 0, sizeof(SQStackInfos));
|
||||
SQVM::CallInfo &ci = v->_callsstack[cssize-level-1];
|
||||
switch (type(ci._closure)) {
|
||||
case OT_CLOSURE:{
|
||||
SQFunctionProto *func = _funcproto(_closure(ci._closure)->_function);
|
||||
if (type(func->_name) == OT_STRING)
|
||||
si->funcname = _stringval(func->_name);
|
||||
if (type(func->_sourcename) == OT_STRING)
|
||||
si->source = _stringval(func->_sourcename);
|
||||
si->line = func->GetLine(ci._ip);
|
||||
}
|
||||
break;
|
||||
case OT_NATIVECLOSURE:
|
||||
si->source = "NATIVE";
|
||||
si->funcname = "unknown";
|
||||
if(type(_nativeclosure(ci._closure)->_name) == OT_STRING)
|
||||
si->funcname = _stringval(_nativeclosure(ci._closure)->_name);
|
||||
si->line = -1;
|
||||
break;
|
||||
default: break; //shutup compiler
|
||||
}
|
||||
return SQ_OK;
|
||||
}
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
void SQVM::Raise_Error(const SQChar *s, ...)
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl, s);
|
||||
size_t len = strlen(s)+(NUMBER_MAX_CHAR*2);
|
||||
char *buffer = MallocT<char>(len + 1);
|
||||
vseprintf(buffer, buffer + len, s, vl);
|
||||
va_end(vl);
|
||||
_lasterror = SQString::Create(_ss(this),buffer,-1);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void SQVM::Raise_Error(SQObjectPtr &desc)
|
||||
{
|
||||
_lasterror = desc;
|
||||
}
|
||||
|
||||
SQString *SQVM::PrintObjVal(const SQObject &o)
|
||||
{
|
||||
char buf[NUMBER_MAX_CHAR+1];
|
||||
switch(type(o)) {
|
||||
case OT_STRING: return _string(o);
|
||||
case OT_INTEGER:
|
||||
seprintf(buf, lastof(buf), OTTD_PRINTF64, _integer(o));
|
||||
return SQString::Create(_ss(this), buf);
|
||||
case OT_FLOAT:
|
||||
seprintf(buf, lastof(buf), "%.14g", _float(o));
|
||||
return SQString::Create(_ss(this), buf);
|
||||
default:
|
||||
return SQString::Create(_ss(this), GetTypeName(o));
|
||||
}
|
||||
}
|
||||
|
||||
void SQVM::Raise_IdxError(const SQObject &o)
|
||||
{
|
||||
SQObjectPtr oval = PrintObjVal(o);
|
||||
Raise_Error("the index '%.50s' does not exist", _stringval(oval));
|
||||
}
|
||||
|
||||
void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2)
|
||||
{
|
||||
SQObjectPtr oval1 = PrintObjVal(o1), oval2 = PrintObjVal(o2);
|
||||
Raise_Error("comparsion between '%.50s' and '%.50s'", _stringval(oval1), _stringval(oval2));
|
||||
}
|
||||
|
||||
|
||||
void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type)
|
||||
{
|
||||
SQObjectPtr exptypes = SQString::Create(_ss(this), "", -1);
|
||||
SQInteger found = 0;
|
||||
for(SQInteger i=0; i<16; i++)
|
||||
{
|
||||
SQInteger mask = 0x00000001 << i;
|
||||
if(typemask & (mask)) {
|
||||
if(found>0) StringCat(exptypes,SQString::Create(_ss(this), "|", -1), exptypes);
|
||||
found ++;
|
||||
StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes);
|
||||
}
|
||||
}
|
||||
Raise_Error("parameter %d has an invalid type '%s' ; expected: '%s'", nparam, IdType2Name((SQObjectType)type), _stringval(exptypes));
|
||||
}
|
||||
166
src/3rdparty/squirrel/squirrel/sqfuncproto.h
vendored
Normal file
166
src/3rdparty/squirrel/squirrel/sqfuncproto.h
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQFUNCTION_H_
|
||||
#define _SQFUNCTION_H_
|
||||
|
||||
#include "sqopcodes.h"
|
||||
|
||||
enum SQOuterType {
|
||||
otLOCAL = 0,
|
||||
otSYMBOL = 1,
|
||||
otOUTER = 2,
|
||||
};
|
||||
|
||||
struct SQOuterVar
|
||||
{
|
||||
|
||||
SQOuterVar() : _type(otLOCAL) {}
|
||||
SQOuterVar(const SQObjectPtr &name,const SQObjectPtr &src,SQOuterType t)
|
||||
{
|
||||
_name = name;
|
||||
_src=src;
|
||||
_type=t;
|
||||
}
|
||||
SQOuterVar(const SQOuterVar &ov)
|
||||
{
|
||||
_type=ov._type;
|
||||
_src=ov._src;
|
||||
_name=ov._name;
|
||||
}
|
||||
SQOuterType _type;
|
||||
SQObjectPtr _name;
|
||||
SQObjectPtr _src;
|
||||
};
|
||||
|
||||
struct SQLocalVarInfo
|
||||
{
|
||||
SQLocalVarInfo():_start_op(0),_end_op(0), _pos(0){}
|
||||
SQLocalVarInfo(const SQLocalVarInfo &lvi)
|
||||
{
|
||||
_name=lvi._name;
|
||||
_start_op=lvi._start_op;
|
||||
_end_op=lvi._end_op;
|
||||
_pos=lvi._pos;
|
||||
}
|
||||
SQObjectPtr _name;
|
||||
SQUnsignedInteger _start_op;
|
||||
SQUnsignedInteger _end_op;
|
||||
SQUnsignedInteger _pos;
|
||||
};
|
||||
|
||||
struct SQLineInfo { SQInteger _line;SQInteger _op; };
|
||||
|
||||
typedef sqvector<SQOuterVar> SQOuterVarVec;
|
||||
typedef sqvector<SQLocalVarInfo> SQLocalVarInfoVec;
|
||||
typedef sqvector<SQLineInfo> SQLineInfoVec;
|
||||
|
||||
#define _FUNC_SIZE(ni,nl,nparams,nfuncs,nouters,nlineinf,localinf,defparams) (sizeof(SQFunctionProto) \
|
||||
+((ni-1)*sizeof(SQInstruction))+(nl*sizeof(SQObjectPtr)) \
|
||||
+(nparams*sizeof(SQObjectPtr))+(nfuncs*sizeof(SQObjectPtr)) \
|
||||
+(nouters*sizeof(SQOuterVar))+(nlineinf*sizeof(SQLineInfo)) \
|
||||
+(localinf*sizeof(SQLocalVarInfo))+(defparams*sizeof(SQInteger)))
|
||||
|
||||
#define _CONSTRUCT_VECTOR(type,size,ptr) { \
|
||||
for(SQInteger n = 0; n < size; n++) { \
|
||||
new (&ptr[n]) type(); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define _DESTRUCT_VECTOR(type,size,ptr) { \
|
||||
for(SQInteger nl = 0; nl < size; nl++) { \
|
||||
ptr[nl].~type(); \
|
||||
} \
|
||||
}
|
||||
struct SQFunctionProto : public SQRefCounted
|
||||
{
|
||||
private:
|
||||
SQFunctionProto(SQInteger ninstructions,
|
||||
SQInteger nliterals,SQInteger nparameters,
|
||||
SQInteger nfunctions,SQInteger noutervalues,
|
||||
SQInteger nlineinfos,SQInteger nlocalvarinfos,SQInteger ndefaultparams)
|
||||
{
|
||||
_stacksize=0;
|
||||
_bgenerator=false;
|
||||
_varparams = false;
|
||||
_ninstructions = ninstructions;
|
||||
_literals = (SQObjectPtr*)&_instructions[ninstructions];
|
||||
_nliterals = nliterals;
|
||||
_parameters = (SQObjectPtr*)&_literals[nliterals];
|
||||
_nparameters = nparameters;
|
||||
_functions = (SQObjectPtr*)&_parameters[nparameters];
|
||||
_nfunctions = nfunctions;
|
||||
_outervalues = (SQOuterVar*)&_functions[nfunctions];
|
||||
_noutervalues = noutervalues;
|
||||
_lineinfos = (SQLineInfo *)&_outervalues[noutervalues];
|
||||
_nlineinfos = nlineinfos;
|
||||
_localvarinfos = (SQLocalVarInfo *)&_lineinfos[nlineinfos];
|
||||
_nlocalvarinfos = nlocalvarinfos;
|
||||
_defaultparams = (SQInteger *)&_localvarinfos[nlocalvarinfos];
|
||||
_ndefaultparams = ndefaultparams;
|
||||
|
||||
_CONSTRUCT_VECTOR(SQObjectPtr,_nliterals,_literals);
|
||||
_CONSTRUCT_VECTOR(SQObjectPtr,_nparameters,_parameters);
|
||||
_CONSTRUCT_VECTOR(SQObjectPtr,_nfunctions,_functions);
|
||||
_CONSTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues);
|
||||
//_CONSTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers
|
||||
_CONSTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos);
|
||||
}
|
||||
public:
|
||||
static SQFunctionProto *Create(SQInteger ninstructions,
|
||||
SQInteger nliterals,SQInteger nparameters,
|
||||
SQInteger nfunctions,SQInteger noutervalues,
|
||||
SQInteger nlineinfos,SQInteger nlocalvarinfos,SQInteger ndefaultparams)
|
||||
{
|
||||
SQFunctionProto *f;
|
||||
//I compact the whole class and members in a single memory allocation
|
||||
f = (SQFunctionProto *)sq_vm_malloc(_FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams));
|
||||
new (f) SQFunctionProto(ninstructions, nliterals, nparameters, nfunctions, noutervalues, nlineinfos, nlocalvarinfos, ndefaultparams);
|
||||
return f;
|
||||
}
|
||||
void Release(){
|
||||
_DESTRUCT_VECTOR(SQObjectPtr,_nliterals,_literals);
|
||||
_DESTRUCT_VECTOR(SQObjectPtr,_nparameters,_parameters);
|
||||
_DESTRUCT_VECTOR(SQObjectPtr,_nfunctions,_functions);
|
||||
_DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues);
|
||||
//_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers
|
||||
_DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos);
|
||||
SQInteger size = _FUNC_SIZE(_ninstructions,_nliterals,_nparameters,_nfunctions,_noutervalues,_nlineinfos,_nlocalvarinfos,_ndefaultparams);
|
||||
this->~SQFunctionProto();
|
||||
sq_vm_free(this,size);
|
||||
}
|
||||
const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop);
|
||||
SQInteger GetLine(SQInstruction *curr);
|
||||
bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
|
||||
static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);
|
||||
|
||||
SQObjectPtr _sourcename;
|
||||
SQObjectPtr _name;
|
||||
SQInteger _stacksize;
|
||||
bool _bgenerator;
|
||||
bool _varparams;
|
||||
|
||||
SQInteger _nlocalvarinfos;
|
||||
SQLocalVarInfo *_localvarinfos;
|
||||
|
||||
SQInteger _nlineinfos;
|
||||
SQLineInfo *_lineinfos;
|
||||
|
||||
SQInteger _nliterals;
|
||||
SQObjectPtr *_literals;
|
||||
|
||||
SQInteger _nparameters;
|
||||
SQObjectPtr *_parameters;
|
||||
|
||||
SQInteger _nfunctions;
|
||||
SQObjectPtr *_functions;
|
||||
|
||||
SQInteger _noutervalues;
|
||||
SQOuterVar *_outervalues;
|
||||
|
||||
SQInteger _ndefaultparams;
|
||||
SQInteger *_defaultparams;
|
||||
|
||||
SQInteger _ninstructions;
|
||||
SQInstruction _instructions[1];
|
||||
};
|
||||
|
||||
#endif //_SQFUNCTION_H_
|
||||
569
src/3rdparty/squirrel/squirrel/sqfuncstate.cpp
vendored
Normal file
569
src/3rdparty/squirrel/squirrel/sqfuncstate.cpp
vendored
Normal file
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include "sqpcheader.h"
|
||||
#include "sqcompiler.h"
|
||||
#include "sqfuncproto.h"
|
||||
#include "sqstring.h"
|
||||
#include "sqtable.h"
|
||||
#include "sqopcodes.h"
|
||||
#include "sqfuncstate.h"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
#ifdef _DEBUG_DUMP
|
||||
SQInstructionDesc g_InstrDesc[]={
|
||||
{"_OP_LINE"},
|
||||
{"_OP_LOAD"},
|
||||
{"_OP_LOADINT"},
|
||||
{"_OP_LOADFLOAT"},
|
||||
{"_OP_DLOAD"},
|
||||
{"_OP_TAILCALL"},
|
||||
{"_OP_CALL"},
|
||||
{"_OP_PREPCALL"},
|
||||
{"_OP_PREPCALLK"},
|
||||
{"_OP_GETK"},
|
||||
{"_OP_MOVE"},
|
||||
{"_OP_NEWSLOT"},
|
||||
{"_OP_DELETE"},
|
||||
{"_OP_SET"},
|
||||
{"_OP_GET"},
|
||||
{"_OP_EQ"},
|
||||
{"_OP_NE"},
|
||||
{"_OP_ARITH"},
|
||||
{"_OP_BITW"},
|
||||
{"_OP_RETURN"},
|
||||
{"_OP_LOADNULLS"},
|
||||
{"_OP_LOADROOTTABLE"},
|
||||
{"_OP_LOADBOOL"},
|
||||
{"_OP_DMOVE"},
|
||||
{"_OP_JMP"},
|
||||
{"_OP_JNZ"},
|
||||
{"_OP_JZ"},
|
||||
{"_OP_LOADFREEVAR"},
|
||||
{"_OP_VARGC"},
|
||||
{"_OP_GETVARGV"},
|
||||
{"_OP_NEWTABLE"},
|
||||
{"_OP_NEWARRAY"},
|
||||
{"_OP_APPENDARRAY"},
|
||||
{"_OP_GETPARENT"},
|
||||
{"_OP_COMPARITH"},
|
||||
{"_OP_COMPARITHL"},
|
||||
{"_OP_INC"},
|
||||
{"_OP_INCL"},
|
||||
{"_OP_PINC"},
|
||||
{"_OP_PINCL"},
|
||||
{"_OP_CMP"},
|
||||
{"_OP_EXISTS"},
|
||||
{"_OP_INSTANCEOF"},
|
||||
{"_OP_AND"},
|
||||
{"_OP_OR"},
|
||||
{"_OP_NEG"},
|
||||
{"_OP_NOT"},
|
||||
{"_OP_BWNOT"},
|
||||
{"_OP_CLOSURE"},
|
||||
{"_OP_YIELD"},
|
||||
{"_OP_RESUME"},
|
||||
{"_OP_FOREACH"},
|
||||
{"_OP_POSTFOREACH"},
|
||||
{"_OP_DELEGATE"},
|
||||
{"_OP_CLONE"},
|
||||
{"_OP_TYPEOF"},
|
||||
{"_OP_PUSHTRAP"},
|
||||
{"_OP_POPTRAP"},
|
||||
{"_OP_THROW"},
|
||||
{"_OP_CLASS"},
|
||||
{"_OP_NEWSLOTA"},
|
||||
{"_OP_SCOPE_END"}
|
||||
};
|
||||
#endif
|
||||
void DumpLiteral(SQObjectPtr &o)
|
||||
{
|
||||
switch(type(o)){
|
||||
case OT_STRING: printf("\"%s\"",_stringval(o));break;
|
||||
case OT_FLOAT: printf("{%f}",_float(o));break;
|
||||
case OT_INTEGER: printf("{" OTTD_PRINTF64 "}",_integer(o));break;
|
||||
case OT_BOOL: printf("%s",_integer(o)?"true":"false");break;
|
||||
default: printf("(%s %p)",GetTypeName(o),(void*)_rawval(o));break; break; //shut up compiler
|
||||
}
|
||||
}
|
||||
|
||||
SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed)
|
||||
{
|
||||
_nliterals = 0;
|
||||
_literals = SQTable::Create(ss,0);
|
||||
_strings = SQTable::Create(ss,0);
|
||||
_sharedstate = ss;
|
||||
_lastline = 0;
|
||||
_optimization = true;
|
||||
_parent = parent;
|
||||
_stacksize = 0;
|
||||
_traps = 0;
|
||||
_returnexp = 0;
|
||||
_varparams = false;
|
||||
_errfunc = efunc;
|
||||
_errtarget = ed;
|
||||
_bgenerator = false;
|
||||
|
||||
}
|
||||
|
||||
void SQFuncState::Error(const SQChar *err)
|
||||
{
|
||||
_errfunc(_errtarget,err);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG_DUMP
|
||||
void SQFuncState::Dump(SQFunctionProto *func)
|
||||
{
|
||||
SQUnsignedInteger n=0,i;
|
||||
SQInteger si;
|
||||
printf("SQInstruction sizeof %d\n",sizeof(SQInstruction));
|
||||
printf("SQObject sizeof %d\n",sizeof(SQObject));
|
||||
printf("--------------------------------------------------------------------\n");
|
||||
printf("*****FUNCTION [%s]\n",type(func->_name)==OT_STRING?_stringval(func->_name):"unknown");
|
||||
printf("-----LITERALS\n");
|
||||
SQObjectPtr refidx,key,val;
|
||||
SQInteger idx;
|
||||
SQObjectPtrVec templiterals;
|
||||
templiterals.resize(_nliterals);
|
||||
while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) {
|
||||
refidx=idx;
|
||||
templiterals[_integer(val)]=key;
|
||||
}
|
||||
for(i=0;i<templiterals.size();i++){
|
||||
printf("[%d] ",n);
|
||||
DumpLiteral(templiterals[i]);
|
||||
printf("\n");
|
||||
n++;
|
||||
}
|
||||
printf("-----PARAMS\n");
|
||||
if(_varparams)
|
||||
printf("<<VARPARAMS>>\n");
|
||||
n=0;
|
||||
for(i=0;i<_parameters.size();i++){
|
||||
printf("[%d] ",n);
|
||||
DumpLiteral(_parameters[i]);
|
||||
printf("\n");
|
||||
n++;
|
||||
}
|
||||
printf("-----LOCALS\n");
|
||||
for(si=0;si<func->_nlocalvarinfos;si++){
|
||||
SQLocalVarInfo lvi=func->_localvarinfos[si];
|
||||
printf("[%d] %s \t%d %d\n",lvi._pos,_stringval(lvi._name),lvi._start_op,lvi._end_op);
|
||||
n++;
|
||||
}
|
||||
printf("-----LINE INFO\n");
|
||||
for(i=0;i<_lineinfos.size();i++){
|
||||
SQLineInfo li=_lineinfos[i];
|
||||
printf("op [%d] line [%d] \n",li._op,li._line);
|
||||
n++;
|
||||
}
|
||||
printf("-----dump\n");
|
||||
n=0;
|
||||
for(i=0;i<_instructions.size();i++){
|
||||
SQInstruction &inst=_instructions[i];
|
||||
if(inst.op==_OP_LOAD || inst.op==_OP_DLOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){
|
||||
|
||||
SQInteger lidx = inst._arg1;
|
||||
printf("[%03d] %15s %d ",n,g_InstrDesc[inst.op].name,inst._arg0);
|
||||
if(lidx >= 0xFFFFFFFF)
|
||||
printf("null");
|
||||
else {
|
||||
SQInteger refidx;
|
||||
SQObjectPtr val,key,refo;
|
||||
while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) {
|
||||
refo = refidx;
|
||||
}
|
||||
DumpLiteral(key);
|
||||
}
|
||||
if(inst.op != _OP_DLOAD) {
|
||||
printf(" %d %d \n",inst._arg2,inst._arg3);
|
||||
}
|
||||
else {
|
||||
printf(" %d ",inst._arg2);
|
||||
lidx = inst._arg3;
|
||||
if(lidx >= 0xFFFFFFFF)
|
||||
printf("null");
|
||||
else {
|
||||
SQInteger refidx;
|
||||
SQObjectPtr val,key,refo;
|
||||
while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) {
|
||||
refo = refidx;
|
||||
}
|
||||
DumpLiteral(key);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(inst.op==_OP_LOADFLOAT) {
|
||||
printf("[%03d] %15s %d %f %d %d\n",n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3);
|
||||
}
|
||||
else if(inst.op==_OP_ARITH){
|
||||
printf("[%03d] %15s %d %d %d %c\n",n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);
|
||||
}
|
||||
else
|
||||
printf("[%03d] %15s %d %d %d %d\n",n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);
|
||||
n++;
|
||||
}
|
||||
printf("-----\n");
|
||||
printf("stack size[%d]\n",func->_stacksize);
|
||||
printf("--------------------------------------------------------------------\n\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
SQInteger SQFuncState::GetNumericConstant(const SQInteger cons)
|
||||
{
|
||||
return GetConstant(SQObjectPtr(cons));
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::GetNumericConstant(const SQFloat cons)
|
||||
{
|
||||
return GetConstant(SQObjectPtr(cons));
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::GetConstant(const SQObject &cons)
|
||||
{
|
||||
SQObjectPtr val;
|
||||
if(!_table(_literals)->Get(cons,val))
|
||||
{
|
||||
val = _nliterals;
|
||||
_table(_literals)->NewSlot(cons,val);
|
||||
_nliterals++;
|
||||
if(_nliterals > MAX_LITERALS) {
|
||||
val.Null();
|
||||
Error("internal compiler error: too many literals");
|
||||
}
|
||||
}
|
||||
return _integer(val);
|
||||
}
|
||||
|
||||
void SQFuncState::SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3)
|
||||
{
|
||||
_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0);
|
||||
_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1);
|
||||
_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2);
|
||||
_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3);
|
||||
}
|
||||
|
||||
void SQFuncState::SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val)
|
||||
{
|
||||
switch(arg){
|
||||
case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break;
|
||||
case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break;
|
||||
case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break;
|
||||
case 3:_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&val);break;
|
||||
};
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::AllocStackPos()
|
||||
{
|
||||
SQInteger npos=_vlocals.size();
|
||||
_vlocals.push_back(SQLocalVarInfo());
|
||||
if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) {
|
||||
if(_stacksize>MAX_FUNC_STACKSIZE) Error("internal compiler error: too many locals");
|
||||
_stacksize=_vlocals.size();
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::PushTarget(SQInteger n)
|
||||
{
|
||||
if(n!=-1){
|
||||
_targetstack.push_back(n);
|
||||
return n;
|
||||
}
|
||||
n=AllocStackPos();
|
||||
_targetstack.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::GetUpTarget(SQInteger n){
|
||||
return _targetstack[((_targetstack.size()-1)-n)];
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::TopTarget(){
|
||||
return _targetstack.back();
|
||||
}
|
||||
SQInteger SQFuncState::PopTarget()
|
||||
{
|
||||
SQInteger npos=_targetstack.back();
|
||||
SQLocalVarInfo t=_vlocals[_targetstack.back()];
|
||||
if(type(t._name)==OT_NULL){
|
||||
_vlocals.pop_back();
|
||||
}
|
||||
_targetstack.pop_back();
|
||||
return npos;
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::GetStackSize()
|
||||
{
|
||||
return _vlocals.size();
|
||||
}
|
||||
|
||||
void SQFuncState::SetStackSize(SQInteger n)
|
||||
{
|
||||
SQInteger size=_vlocals.size();
|
||||
while(size>n){
|
||||
size--;
|
||||
SQLocalVarInfo lvi=_vlocals.back();
|
||||
if(type(lvi._name)!=OT_NULL){
|
||||
lvi._end_op=GetCurrentPos();
|
||||
_localvarinfos.push_back(lvi);
|
||||
}
|
||||
_vlocals.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
bool SQFuncState::IsConstant(const SQObject &name,SQObject &e)
|
||||
{
|
||||
SQObjectPtr val;
|
||||
if(_table(_sharedstate->_consts)->Get(name,val)) {
|
||||
e = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SQFuncState::IsLocal(SQUnsignedInteger stkpos)
|
||||
{
|
||||
if(stkpos>=_vlocals.size())return false;
|
||||
else if(type(_vlocals[stkpos]._name)!=OT_NULL)return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::PushLocalVariable(const SQObject &name)
|
||||
{
|
||||
SQInteger pos=_vlocals.size();
|
||||
SQLocalVarInfo lvi;
|
||||
lvi._name=name;
|
||||
lvi._start_op=GetCurrentPos()+1;
|
||||
lvi._pos=_vlocals.size();
|
||||
_vlocals.push_back(lvi);
|
||||
if(_vlocals.size()>((SQUnsignedInteger)_stacksize))_stacksize=_vlocals.size();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::GetLocalVariable(const SQObject &name)
|
||||
{
|
||||
SQInteger locals=_vlocals.size();
|
||||
while(locals>=1){
|
||||
if(type(_vlocals[locals-1]._name)==OT_STRING && _string(_vlocals[locals-1]._name)==_string(name)){
|
||||
return locals-1;
|
||||
}
|
||||
locals--;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
SQInteger SQFuncState::GetOuterVariable(const SQObject &name)
|
||||
{
|
||||
SQInteger outers = _outervalues.size();
|
||||
for(SQInteger i = 0; i<outers; i++) {
|
||||
if(_string(_outervalues[i]._name) == _string(name))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SQFuncState::AddOuterValue(const SQObject &name)
|
||||
{
|
||||
SQInteger pos=-1;
|
||||
if(_parent) {
|
||||
pos = _parent->GetLocalVariable(name);
|
||||
if(pos == -1) {
|
||||
pos = _parent->GetOuterVariable(name);
|
||||
if(pos != -1) {
|
||||
_outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otOUTER)); //local
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otLOCAL)); //local
|
||||
return;
|
||||
}
|
||||
}
|
||||
_outervalues.push_back(SQOuterVar(name,name,otSYMBOL)); //global
|
||||
}
|
||||
|
||||
void SQFuncState::AddParameter(const SQObject &name)
|
||||
{
|
||||
PushLocalVariable(name);
|
||||
_parameters.push_back(name);
|
||||
}
|
||||
|
||||
void SQFuncState::AddLineInfos(SQInteger line,bool lineop,bool force)
|
||||
{
|
||||
if(_lastline!=line || force){
|
||||
SQLineInfo li;
|
||||
li._line=line;li._op=(GetCurrentPos()+1);
|
||||
if(lineop)AddInstruction(_OP_LINE,0,line);
|
||||
_lineinfos.push_back(li);
|
||||
_lastline=line;
|
||||
}
|
||||
}
|
||||
|
||||
void SQFuncState::AddInstruction(SQInstruction &i)
|
||||
{
|
||||
SQInteger size = _instructions.size();
|
||||
if(size > 0 && _optimization){ //simple optimizer
|
||||
SQInstruction &pi = _instructions[size-1];//previous instruction
|
||||
switch(i.op) {
|
||||
case _OP_RETURN:
|
||||
if( _parent && i._arg0 != MAX_FUNC_STACKSIZE && pi.op == _OP_CALL && _returnexp < size-1) {
|
||||
pi.op = _OP_TAILCALL;
|
||||
}
|
||||
break;
|
||||
case _OP_GET:
|
||||
if( pi.op == _OP_LOAD && pi._arg0 == i._arg2 && (!IsLocal(pi._arg0))){
|
||||
pi._arg2 = (unsigned char)i._arg1;
|
||||
pi.op = _OP_GETK;
|
||||
pi._arg0 = i._arg0;
|
||||
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case _OP_PREPCALL:
|
||||
if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){
|
||||
pi.op = _OP_PREPCALLK;
|
||||
pi._arg0 = i._arg0;
|
||||
pi._arg2 = i._arg2;
|
||||
pi._arg3 = i._arg3;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case _OP_APPENDARRAY:
|
||||
if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){
|
||||
pi.op = _OP_APPENDARRAY;
|
||||
pi._arg0 = i._arg0;
|
||||
pi._arg2 = MAX_FUNC_STACKSIZE;
|
||||
pi._arg3 = MAX_FUNC_STACKSIZE;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case _OP_MOVE:
|
||||
if((pi.op == _OP_GET || pi.op == _OP_ARITH || pi.op == _OP_BITW) && (pi._arg0 == i._arg1))
|
||||
{
|
||||
pi._arg0 = i._arg0;
|
||||
_optimization = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(pi.op == _OP_MOVE)
|
||||
{
|
||||
pi.op = _OP_DMOVE;
|
||||
pi._arg2 = i._arg0;
|
||||
pi._arg3 = (unsigned char)i._arg1;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case _OP_LOAD:
|
||||
if(pi.op == _OP_LOAD && i._arg1 < 256) {
|
||||
pi.op = _OP_DLOAD;
|
||||
pi._arg2 = i._arg0;
|
||||
pi._arg3 = (unsigned char)i._arg1;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case _OP_EQ:case _OP_NE:
|
||||
if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0) ))
|
||||
{
|
||||
pi.op = i.op;
|
||||
pi._arg0 = i._arg0;
|
||||
pi._arg2 = i._arg2;
|
||||
pi._arg3 = MAX_FUNC_STACKSIZE;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case _OP_LOADNULLS:
|
||||
if((pi.op == _OP_LOADNULLS && pi._arg0+pi._arg1 == i._arg0)) {
|
||||
|
||||
pi._arg1 = pi._arg1 + 1;
|
||||
pi.op = _OP_LOADNULLS;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case _OP_LINE:
|
||||
if(pi.op == _OP_LINE) {
|
||||
_instructions.pop_back();
|
||||
_lineinfos.pop_back();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
_optimization = true;
|
||||
_instructions.push_back(i);
|
||||
}
|
||||
|
||||
SQObject SQFuncState::CreateString(const SQChar *s,SQInteger len)
|
||||
{
|
||||
SQObjectPtr ns(SQString::Create(_sharedstate,s,len));
|
||||
_table(_strings)->NewSlot(ns,(SQInteger)1);
|
||||
return ns;
|
||||
}
|
||||
|
||||
SQObject SQFuncState::CreateTable()
|
||||
{
|
||||
SQObjectPtr nt(SQTable::Create(_sharedstate,0));
|
||||
_table(_strings)->NewSlot(nt,(SQInteger)1);
|
||||
return nt;
|
||||
}
|
||||
|
||||
SQFunctionProto *SQFuncState::BuildProto()
|
||||
{
|
||||
SQFunctionProto *f=SQFunctionProto::Create(_instructions.size(),
|
||||
_nliterals,_parameters.size(),_functions.size(),_outervalues.size(),
|
||||
_lineinfos.size(),_localvarinfos.size(),_defaultparams.size());
|
||||
|
||||
SQObjectPtr refidx,key,val;
|
||||
SQInteger idx;
|
||||
|
||||
f->_stacksize = _stacksize;
|
||||
f->_sourcename = _sourcename;
|
||||
f->_bgenerator = _bgenerator;
|
||||
f->_name = _name;
|
||||
|
||||
while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) {
|
||||
f->_literals[_integer(val)]=key;
|
||||
refidx=idx;
|
||||
}
|
||||
|
||||
for(SQUnsignedInteger nf = 0; nf < _functions.size(); nf++) f->_functions[nf] = _functions[nf];
|
||||
for(SQUnsignedInteger np = 0; np < _parameters.size(); np++) f->_parameters[np] = _parameters[np];
|
||||
for(SQUnsignedInteger no = 0; no < _outervalues.size(); no++) f->_outervalues[no] = _outervalues[no];
|
||||
for(SQUnsignedInteger no = 0; no < _localvarinfos.size(); no++) f->_localvarinfos[no] = _localvarinfos[no];
|
||||
for(SQUnsignedInteger no = 0; no < _lineinfos.size(); no++) f->_lineinfos[no] = _lineinfos[no];
|
||||
for(SQUnsignedInteger no = 0; no < _defaultparams.size(); no++) f->_defaultparams[no] = _defaultparams[no];
|
||||
|
||||
memcpy(f->_instructions,&_instructions[0],(size_t)_instructions.size()*sizeof(SQInstruction));
|
||||
|
||||
f->_varparams = _varparams;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
SQFuncState *SQFuncState::PushChildState(SQSharedState *ss)
|
||||
{
|
||||
SQFuncState *child = (SQFuncState *)sq_malloc(sizeof(SQFuncState));
|
||||
new (child) SQFuncState(ss,this,_errfunc,_errtarget);
|
||||
_childstates.push_back(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
void SQFuncState::PopChildState()
|
||||
{
|
||||
SQFuncState *child = _childstates.back();
|
||||
sq_delete(child,SQFuncState);
|
||||
_childstates.pop_back();
|
||||
}
|
||||
|
||||
SQFuncState::~SQFuncState()
|
||||
{
|
||||
while(_childstates.size() > 0)
|
||||
{
|
||||
PopChildState();
|
||||
}
|
||||
}
|
||||
85
src/3rdparty/squirrel/squirrel/sqfuncstate.h
vendored
Normal file
85
src/3rdparty/squirrel/squirrel/sqfuncstate.h
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQFUNCSTATE_H_
|
||||
#define _SQFUNCSTATE_H_
|
||||
///////////////////////////////////
|
||||
#include "squtils.h"
|
||||
|
||||
struct SQFuncState
|
||||
{
|
||||
SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed);
|
||||
~SQFuncState();
|
||||
#ifdef _DEBUG_DUMP
|
||||
void Dump(SQFunctionProto *func);
|
||||
#endif
|
||||
void Error(const SQChar *err);
|
||||
SQFuncState *PushChildState(SQSharedState *ss);
|
||||
void PopChildState();
|
||||
void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);}
|
||||
void AddInstruction(SQInstruction &i);
|
||||
void SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0);
|
||||
void SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val);
|
||||
SQInstruction &GetInstruction(SQInteger pos){return _instructions[pos];}
|
||||
void PopInstructions(SQInteger size){for(SQInteger i=0;i<size;i++)_instructions.pop_back();}
|
||||
void SetStackSize(SQInteger n);
|
||||
void SnoozeOpt(){_optimization=false;}
|
||||
void AddDefaultParam(SQInteger trg) { _defaultparams.push_back(trg); }
|
||||
SQInteger GetDefaultParamCount() { return _defaultparams.size(); }
|
||||
SQInteger GetCurrentPos(){return _instructions.size()-1;}
|
||||
SQInteger GetNumericConstant(const SQInteger cons);
|
||||
SQInteger GetNumericConstant(const SQFloat cons);
|
||||
SQInteger PushLocalVariable(const SQObject &name);
|
||||
void AddParameter(const SQObject &name);
|
||||
void AddOuterValue(const SQObject &name);
|
||||
SQInteger GetLocalVariable(const SQObject &name);
|
||||
SQInteger GetOuterVariable(const SQObject &name);
|
||||
SQInteger GenerateCode();
|
||||
SQInteger GetStackSize();
|
||||
SQInteger CalcStackFrameSize();
|
||||
void AddLineInfos(SQInteger line,bool lineop,bool force=false);
|
||||
SQFunctionProto *BuildProto();
|
||||
SQInteger AllocStackPos();
|
||||
SQInteger PushTarget(SQInteger n=-1);
|
||||
SQInteger PopTarget();
|
||||
SQInteger TopTarget();
|
||||
SQInteger GetUpTarget(SQInteger n);
|
||||
bool IsLocal(SQUnsignedInteger stkpos);
|
||||
SQObject CreateString(const SQChar *s,SQInteger len = -1);
|
||||
SQObject CreateTable();
|
||||
bool IsConstant(const SQObject &name,SQObject &e);
|
||||
SQInteger _returnexp;
|
||||
SQLocalVarInfoVec _vlocals;
|
||||
SQIntVec _targetstack;
|
||||
SQInteger _stacksize;
|
||||
bool _varparams;
|
||||
bool _bgenerator;
|
||||
SQIntVec _unresolvedbreaks;
|
||||
SQIntVec _unresolvedcontinues;
|
||||
SQObjectPtrVec _functions;
|
||||
SQObjectPtrVec _parameters;
|
||||
SQOuterVarVec _outervalues;
|
||||
SQInstructionVec _instructions;
|
||||
SQLocalVarInfoVec _localvarinfos;
|
||||
SQObjectPtr _literals;
|
||||
SQObjectPtr _strings;
|
||||
SQObjectPtr _name;
|
||||
SQObjectPtr _sourcename;
|
||||
SQInteger _nliterals;
|
||||
SQLineInfoVec _lineinfos;
|
||||
SQFuncState *_parent;
|
||||
SQIntVec _breaktargets;
|
||||
SQIntVec _continuetargets;
|
||||
SQIntVec _defaultparams;
|
||||
SQInteger _lastline;
|
||||
SQInteger _traps; //contains number of nested exception traps
|
||||
bool _optimization;
|
||||
SQSharedState *_sharedstate;
|
||||
sqvector<SQFuncState*> _childstates;
|
||||
SQInteger GetConstant(const SQObject &cons);
|
||||
private:
|
||||
CompilerErrorFunc _errfunc;
|
||||
void *_errtarget;
|
||||
};
|
||||
|
||||
|
||||
#endif //_SQFUNCSTATE_H_
|
||||
|
||||
491
src/3rdparty/squirrel/squirrel/sqlexer.cpp
vendored
Normal file
491
src/3rdparty/squirrel/squirrel/sqlexer.cpp
vendored
Normal file
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include "sqpcheader.h"
|
||||
#include <ctype.h>
|
||||
#include "sqtable.h"
|
||||
#include "sqstring.h"
|
||||
#include "sqcompiler.h"
|
||||
#include "sqlexer.h"
|
||||
|
||||
#include "../../../string_func.h"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
#define CUR_CHAR (_currdata)
|
||||
#define RETURN_TOKEN(t) { _prevtoken = _curtoken; _curtoken = t; return t;}
|
||||
#define IS_EOB() (CUR_CHAR <= SQUIRREL_EOB)
|
||||
#define NEXT() {Next();_currentcolumn++;}
|
||||
#define ADD_KEYWORD(key,id) _keywords->NewSlot( SQString::Create(ss, #key) ,SQInteger(id))
|
||||
|
||||
SQLexer::~SQLexer()
|
||||
{
|
||||
_keywords->Release();
|
||||
}
|
||||
|
||||
void SQLexer::APPEND_CHAR(WChar c)
|
||||
{
|
||||
char buf[4];
|
||||
size_t chars = Utf8Encode(buf, c);
|
||||
for (size_t i = 0; i < chars; i++) {
|
||||
_longstr.push_back(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
SQLexer::SQLexer(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up,CompilerErrorFunc efunc,void *ed)
|
||||
{
|
||||
_errfunc = efunc;
|
||||
_errtarget = ed;
|
||||
_sharedstate = ss;
|
||||
_keywords = SQTable::Create(ss, 26);
|
||||
ADD_KEYWORD(while, TK_WHILE);
|
||||
ADD_KEYWORD(do, TK_DO);
|
||||
ADD_KEYWORD(if, TK_IF);
|
||||
ADD_KEYWORD(else, TK_ELSE);
|
||||
ADD_KEYWORD(break, TK_BREAK);
|
||||
ADD_KEYWORD(continue, TK_CONTINUE);
|
||||
ADD_KEYWORD(return, TK_RETURN);
|
||||
ADD_KEYWORD(null, TK_NULL);
|
||||
ADD_KEYWORD(function, TK_FUNCTION);
|
||||
ADD_KEYWORD(local, TK_LOCAL);
|
||||
ADD_KEYWORD(for, TK_FOR);
|
||||
ADD_KEYWORD(foreach, TK_FOREACH);
|
||||
ADD_KEYWORD(in, TK_IN);
|
||||
ADD_KEYWORD(typeof, TK_TYPEOF);
|
||||
ADD_KEYWORD(delegate, TK_DELEGATE);
|
||||
ADD_KEYWORD(delete, TK_DELETE);
|
||||
ADD_KEYWORD(try, TK_TRY);
|
||||
ADD_KEYWORD(catch, TK_CATCH);
|
||||
ADD_KEYWORD(throw, TK_THROW);
|
||||
ADD_KEYWORD(clone, TK_CLONE);
|
||||
ADD_KEYWORD(yield, TK_YIELD);
|
||||
ADD_KEYWORD(resume, TK_RESUME);
|
||||
ADD_KEYWORD(switch, TK_SWITCH);
|
||||
ADD_KEYWORD(case, TK_CASE);
|
||||
ADD_KEYWORD(default, TK_DEFAULT);
|
||||
ADD_KEYWORD(this, TK_THIS);
|
||||
ADD_KEYWORD(parent,TK_PARENT);
|
||||
ADD_KEYWORD(class,TK_CLASS);
|
||||
ADD_KEYWORD(extends,TK_EXTENDS);
|
||||
ADD_KEYWORD(constructor,TK_CONSTRUCTOR);
|
||||
ADD_KEYWORD(instanceof,TK_INSTANCEOF);
|
||||
ADD_KEYWORD(vargc,TK_VARGC);
|
||||
ADD_KEYWORD(vargv,TK_VARGV);
|
||||
ADD_KEYWORD(true,TK_TRUE);
|
||||
ADD_KEYWORD(false,TK_FALSE);
|
||||
ADD_KEYWORD(static,TK_STATIC);
|
||||
ADD_KEYWORD(enum,TK_ENUM);
|
||||
ADD_KEYWORD(const,TK_CONST);
|
||||
|
||||
_readf = rg;
|
||||
_up = up;
|
||||
_lasttokenline = _currentline = 1;
|
||||
_currentcolumn = 0;
|
||||
_prevtoken = -1;
|
||||
_curtoken = -1;
|
||||
|
||||
_svalue = NULL;
|
||||
_nvalue = 0;
|
||||
_fvalue = 0;
|
||||
|
||||
Next();
|
||||
}
|
||||
|
||||
NORETURN void SQLexer::Error(const SQChar *err)
|
||||
{
|
||||
_errfunc(_errtarget,err);
|
||||
}
|
||||
|
||||
void SQLexer::Next()
|
||||
{
|
||||
WChar t = _readf(_up);
|
||||
if(t > MAX_CHAR) Error("Invalid character");
|
||||
if(t != 0) {
|
||||
_currdata = t;
|
||||
return;
|
||||
}
|
||||
_currdata = SQUIRREL_EOB;
|
||||
}
|
||||
|
||||
const SQChar *SQLexer::Tok2Str(SQInteger tok)
|
||||
{
|
||||
SQObjectPtr itr, key, val;
|
||||
SQInteger nitr;
|
||||
while((nitr = _keywords->Next(false,itr, key, val)) != -1) {
|
||||
itr = (SQInteger)nitr;
|
||||
if(((SQInteger)_integer(val)) == tok)
|
||||
return _stringval(key);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SQLexer::LexBlockComment()
|
||||
{
|
||||
bool done = false;
|
||||
while(!done) {
|
||||
switch(CUR_CHAR) {
|
||||
case '*': { NEXT(); if(CUR_CHAR == '/') { done = true; NEXT(); }}; continue;
|
||||
case '\n': _currentline++; NEXT(); continue;
|
||||
case SQUIRREL_EOB: Error("missing \"*/\" in comment");
|
||||
default: NEXT();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SQInteger SQLexer::Lex()
|
||||
{
|
||||
_lasttokenline = _currentline;
|
||||
while(CUR_CHAR != SQUIRREL_EOB) {
|
||||
switch(CUR_CHAR){
|
||||
case '\t': case '\r': case ' ': NEXT(); continue;
|
||||
case '\n':
|
||||
_currentline++;
|
||||
_prevtoken=_curtoken;
|
||||
_curtoken='\n';
|
||||
NEXT();
|
||||
_currentcolumn=1;
|
||||
continue;
|
||||
case '/':
|
||||
NEXT();
|
||||
switch(CUR_CHAR){
|
||||
case '*':
|
||||
NEXT();
|
||||
LexBlockComment();
|
||||
continue;
|
||||
case '/':
|
||||
do { NEXT(); } while (CUR_CHAR != '\n' && (!IS_EOB()));
|
||||
continue;
|
||||
case '=':
|
||||
NEXT();
|
||||
RETURN_TOKEN(TK_DIVEQ);
|
||||
case '>':
|
||||
NEXT();
|
||||
RETURN_TOKEN(TK_ATTR_CLOSE);
|
||||
default:
|
||||
RETURN_TOKEN('/');
|
||||
}
|
||||
case '=':
|
||||
NEXT();
|
||||
if (CUR_CHAR != '='){ RETURN_TOKEN('=') }
|
||||
else { NEXT(); RETURN_TOKEN(TK_EQ); }
|
||||
case '<':
|
||||
NEXT();
|
||||
if ( CUR_CHAR == '=' ) { NEXT(); RETURN_TOKEN(TK_LE) }
|
||||
else if ( CUR_CHAR == '-' ) { NEXT(); RETURN_TOKEN(TK_NEWSLOT); }
|
||||
else if ( CUR_CHAR == '<' ) { NEXT(); RETURN_TOKEN(TK_SHIFTL); }
|
||||
else if ( CUR_CHAR == '/' ) { NEXT(); RETURN_TOKEN(TK_ATTR_OPEN); }
|
||||
//else if ( CUR_CHAR == '[' ) { NEXT(); ReadMultilineString(); RETURN_TOKEN(TK_STRING_LITERAL); }
|
||||
else { RETURN_TOKEN('<') }
|
||||
case '>':
|
||||
NEXT();
|
||||
if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_GE);}
|
||||
else if(CUR_CHAR == '>'){
|
||||
NEXT();
|
||||
if(CUR_CHAR == '>'){
|
||||
NEXT();
|
||||
RETURN_TOKEN(TK_USHIFTR);
|
||||
}
|
||||
RETURN_TOKEN(TK_SHIFTR);
|
||||
}
|
||||
else { RETURN_TOKEN('>') }
|
||||
case '!':
|
||||
NEXT();
|
||||
if (CUR_CHAR != '='){ RETURN_TOKEN('!')}
|
||||
else { NEXT(); RETURN_TOKEN(TK_NE); }
|
||||
case '@': {
|
||||
SQInteger stype;
|
||||
NEXT();
|
||||
if(CUR_CHAR != '"')
|
||||
Error("string expected");
|
||||
if((stype=ReadString('"',true))!=-1) {
|
||||
RETURN_TOKEN(stype);
|
||||
}
|
||||
Error("error parsing the string");
|
||||
}
|
||||
case '"':
|
||||
case '\'': {
|
||||
SQInteger stype;
|
||||
if((stype=ReadString(CUR_CHAR,false))!=-1){
|
||||
RETURN_TOKEN(stype);
|
||||
}
|
||||
Error("error parsing the string");
|
||||
}
|
||||
case '{': case '}': case '(': case ')': case '[': case ']':
|
||||
case ';': case ',': case '?': case '^': case '~':
|
||||
{SQInteger ret = CUR_CHAR;
|
||||
NEXT(); RETURN_TOKEN(ret); }
|
||||
case '.':
|
||||
NEXT();
|
||||
if (CUR_CHAR != '.'){ RETURN_TOKEN('.') }
|
||||
NEXT();
|
||||
if (CUR_CHAR != '.'){ Error("invalid token '..'"); }
|
||||
NEXT();
|
||||
RETURN_TOKEN(TK_VARPARAMS);
|
||||
case '&':
|
||||
NEXT();
|
||||
if (CUR_CHAR != '&'){ RETURN_TOKEN('&') }
|
||||
else { NEXT(); RETURN_TOKEN(TK_AND); }
|
||||
case '|':
|
||||
NEXT();
|
||||
if (CUR_CHAR != '|'){ RETURN_TOKEN('|') }
|
||||
else { NEXT(); RETURN_TOKEN(TK_OR); }
|
||||
case ':':
|
||||
NEXT();
|
||||
if (CUR_CHAR != ':'){ RETURN_TOKEN(':') }
|
||||
else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); }
|
||||
case '*':
|
||||
NEXT();
|
||||
if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_MULEQ);}
|
||||
else RETURN_TOKEN('*');
|
||||
case '%':
|
||||
NEXT();
|
||||
if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_MODEQ);}
|
||||
else RETURN_TOKEN('%');
|
||||
case '-':
|
||||
NEXT();
|
||||
if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);}
|
||||
else if (CUR_CHAR == '-'){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);}
|
||||
else RETURN_TOKEN('-');
|
||||
case '+':
|
||||
NEXT();
|
||||
if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);}
|
||||
else if (CUR_CHAR == '+'){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);}
|
||||
else RETURN_TOKEN('+');
|
||||
case SQUIRREL_EOB:
|
||||
return 0;
|
||||
default:{
|
||||
if (isdigit(CUR_CHAR)) {
|
||||
SQInteger ret = ReadNumber();
|
||||
RETURN_TOKEN(ret);
|
||||
}
|
||||
else if (isalpha(CUR_CHAR) || CUR_CHAR == '_') {
|
||||
SQInteger t = ReadID();
|
||||
RETURN_TOKEN(t);
|
||||
}
|
||||
else {
|
||||
SQInteger c = CUR_CHAR;
|
||||
if (iscntrl((int)c)) Error("unexpected character(control)");
|
||||
NEXT();
|
||||
RETURN_TOKEN(c);
|
||||
}
|
||||
RETURN_TOKEN(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SQInteger SQLexer::GetIDType(SQChar *s)
|
||||
{
|
||||
SQObjectPtr t;
|
||||
if(_keywords->Get(SQString::Create(_sharedstate, s), t)) {
|
||||
return SQInteger(_integer(t));
|
||||
}
|
||||
return TK_IDENTIFIER;
|
||||
}
|
||||
|
||||
|
||||
SQInteger SQLexer::ReadString(WChar ndelim,bool verbatim)
|
||||
{
|
||||
INIT_TEMP_STRING();
|
||||
NEXT();
|
||||
if(IS_EOB()) return -1;
|
||||
for(;;) {
|
||||
while(CUR_CHAR != ndelim) {
|
||||
switch(CUR_CHAR) {
|
||||
case SQUIRREL_EOB:
|
||||
Error("unfinished string");
|
||||
return -1;
|
||||
case '\n':
|
||||
if(!verbatim) Error("newline in a constant");
|
||||
APPEND_CHAR(CUR_CHAR); NEXT();
|
||||
_currentline++;
|
||||
break;
|
||||
case '\\':
|
||||
if(verbatim) {
|
||||
APPEND_CHAR('\\'); NEXT();
|
||||
}
|
||||
else {
|
||||
NEXT();
|
||||
switch(CUR_CHAR) {
|
||||
case 'x': NEXT(); {
|
||||
if(!isxdigit(CUR_CHAR)) Error("hexadecimal number expected");
|
||||
const SQInteger maxdigits = 4;
|
||||
SQChar temp[maxdigits+1];
|
||||
SQInteger n = 0;
|
||||
while(isxdigit(CUR_CHAR) && n < maxdigits) {
|
||||
temp[n] = CUR_CHAR;
|
||||
n++;
|
||||
NEXT();
|
||||
}
|
||||
temp[n] = 0;
|
||||
SQChar *sTemp;
|
||||
APPEND_CHAR((SQChar)strtoul(temp,&sTemp,16));
|
||||
}
|
||||
break;
|
||||
case 't': APPEND_CHAR('\t'); NEXT(); break;
|
||||
case 'a': APPEND_CHAR('\a'); NEXT(); break;
|
||||
case 'b': APPEND_CHAR('\b'); NEXT(); break;
|
||||
case 'n': APPEND_CHAR('\n'); NEXT(); break;
|
||||
case 'r': APPEND_CHAR('\r'); NEXT(); break;
|
||||
case 'v': APPEND_CHAR('\v'); NEXT(); break;
|
||||
case 'f': APPEND_CHAR('\f'); NEXT(); break;
|
||||
case '0': APPEND_CHAR('\0'); NEXT(); break;
|
||||
case '\\': APPEND_CHAR('\\'); NEXT(); break;
|
||||
case '"': APPEND_CHAR('"'); NEXT(); break;
|
||||
case '\'': APPEND_CHAR('\''); NEXT(); break;
|
||||
default:
|
||||
Error("unrecognised escaper char");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
}
|
||||
NEXT();
|
||||
if(verbatim && CUR_CHAR == '"') { //double quotation
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TERMINATE_BUFFER();
|
||||
SQInteger len = _longstr.size()-1;
|
||||
if(ndelim == '\'') {
|
||||
if(len == 0) Error("empty constant");
|
||||
if(len > 1) Error("constant too long");
|
||||
_nvalue = _longstr[0];
|
||||
return TK_INTEGER;
|
||||
}
|
||||
_svalue = &_longstr[0];
|
||||
return TK_STRING_LITERAL;
|
||||
}
|
||||
|
||||
void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res)
|
||||
{
|
||||
*res = 0;
|
||||
while(*s != 0)
|
||||
{
|
||||
if(isdigit(*s)) *res = (*res)*16+((*s++)-'0');
|
||||
else if(isxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10);
|
||||
else { assert(0); }
|
||||
}
|
||||
}
|
||||
|
||||
void LexInteger(const SQChar *s,SQUnsignedInteger *res)
|
||||
{
|
||||
*res = 0;
|
||||
while(*s != 0)
|
||||
{
|
||||
*res = (*res)*10+((*s++)-'0');
|
||||
}
|
||||
}
|
||||
|
||||
SQInteger scisodigit(SQChar c) { return c >= '0' && c <= '7'; }
|
||||
|
||||
void LexOctal(const SQChar *s,SQUnsignedInteger *res)
|
||||
{
|
||||
*res = 0;
|
||||
while(*s != 0)
|
||||
{
|
||||
if(scisodigit(*s)) *res = (*res)*8+((*s++)-'0');
|
||||
else { assert(0); }
|
||||
}
|
||||
}
|
||||
|
||||
SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; }
|
||||
|
||||
|
||||
#define MAX_HEX_DIGITS (sizeof(SQInteger)*2)
|
||||
SQInteger SQLexer::ReadNumber()
|
||||
{
|
||||
#define TINT 1
|
||||
#define TFLOAT 2
|
||||
#define THEX 3
|
||||
#define TSCIENTIFIC 4
|
||||
#define TOCTAL 5
|
||||
SQInteger type = TINT, firstchar = CUR_CHAR;
|
||||
SQChar *sTemp;
|
||||
INIT_TEMP_STRING();
|
||||
NEXT();
|
||||
if(firstchar == '0' && (toupper(CUR_CHAR) == 'X' || scisodigit(CUR_CHAR)) ) {
|
||||
if(scisodigit(CUR_CHAR)) {
|
||||
type = TOCTAL;
|
||||
while(scisodigit(CUR_CHAR)) {
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
if(isdigit(CUR_CHAR)) Error("invalid octal number");
|
||||
}
|
||||
else {
|
||||
NEXT();
|
||||
type = THEX;
|
||||
while(isxdigit(CUR_CHAR)) {
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
if(_longstr.size() > MAX_HEX_DIGITS) Error("too many digits for an Hex number");
|
||||
}
|
||||
}
|
||||
else {
|
||||
APPEND_CHAR((int)firstchar);
|
||||
while (CUR_CHAR == '.' || isdigit(CUR_CHAR) || isexponent(CUR_CHAR)) {
|
||||
if(CUR_CHAR == '.' || isexponent(CUR_CHAR)) type = TFLOAT;
|
||||
if(isexponent(CUR_CHAR)) {
|
||||
if(type != TFLOAT) Error("invalid numeric format");
|
||||
type = TSCIENTIFIC;
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
if(CUR_CHAR == '+' || CUR_CHAR == '-'){
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
if(!isdigit(CUR_CHAR)) Error("exponent expected");
|
||||
}
|
||||
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
}
|
||||
TERMINATE_BUFFER();
|
||||
switch(type) {
|
||||
case TSCIENTIFIC:
|
||||
case TFLOAT:
|
||||
_fvalue = (SQFloat)strtod(&_longstr[0],&sTemp);
|
||||
return TK_FLOAT;
|
||||
case TINT:
|
||||
LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
|
||||
return TK_INTEGER;
|
||||
case THEX:
|
||||
LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
|
||||
return TK_INTEGER;
|
||||
case TOCTAL:
|
||||
LexOctal(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
|
||||
return TK_INTEGER;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SQInteger SQLexer::ReadID()
|
||||
{
|
||||
SQInteger res;
|
||||
INIT_TEMP_STRING();
|
||||
do {
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
} while(isalnum(CUR_CHAR) || CUR_CHAR == '_');
|
||||
TERMINATE_BUFFER();
|
||||
res = GetIDType(&_longstr[0]);
|
||||
if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) {
|
||||
_svalue = &_longstr[0];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
42
src/3rdparty/squirrel/squirrel/sqlexer.h
vendored
Normal file
42
src/3rdparty/squirrel/squirrel/sqlexer.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQLEXER_H_
|
||||
#define _SQLEXER_H_
|
||||
|
||||
struct SQLexer
|
||||
{
|
||||
~SQLexer();
|
||||
SQLexer(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up,CompilerErrorFunc efunc,void *ed);
|
||||
NORETURN void Error(const SQChar *err);
|
||||
SQInteger Lex();
|
||||
const SQChar *Tok2Str(SQInteger tok);
|
||||
private:
|
||||
SQInteger GetIDType(SQChar *s);
|
||||
SQInteger ReadString(WChar ndelim,bool verbatim);
|
||||
SQInteger ReadNumber();
|
||||
void LexBlockComment();
|
||||
SQInteger ReadID();
|
||||
void Next();
|
||||
SQInteger _curtoken;
|
||||
SQTable *_keywords;
|
||||
void INIT_TEMP_STRING() { _longstr.resize(0); }
|
||||
void APPEND_CHAR(WChar c);
|
||||
void TERMINATE_BUFFER() { _longstr.push_back('\0'); }
|
||||
|
||||
public:
|
||||
SQInteger _prevtoken;
|
||||
SQInteger _currentline;
|
||||
SQInteger _lasttokenline;
|
||||
SQInteger _currentcolumn;
|
||||
const SQChar *_svalue;
|
||||
SQInteger _nvalue;
|
||||
SQFloat _fvalue;
|
||||
SQLEXREADFUNC _readf;
|
||||
SQUserPointer _up;
|
||||
WChar _currdata;
|
||||
SQSharedState *_sharedstate;
|
||||
sqvector<SQChar> _longstr;
|
||||
CompilerErrorFunc _errfunc;
|
||||
void *_errtarget;
|
||||
};
|
||||
|
||||
#endif
|
||||
16
src/3rdparty/squirrel/squirrel/sqmem.cpp
vendored
Normal file
16
src/3rdparty/squirrel/squirrel/sqmem.cpp
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include "sqpcheader.h"
|
||||
|
||||
#include "../../../core/alloc_func.hpp"
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
void *sq_vm_malloc(SQUnsignedInteger size){ return MallocT<char>((size_t)size); }
|
||||
|
||||
void *sq_vm_realloc(void *p, SQUnsignedInteger oldsize, SQUnsignedInteger size){ return ReallocT<char>(static_cast<char*>(p), (size_t)size); }
|
||||
|
||||
void sq_vm_free(void *p, SQUnsignedInteger size){ free(p); }
|
||||
592
src/3rdparty/squirrel/squirrel/sqobject.cpp
vendored
Normal file
592
src/3rdparty/squirrel/squirrel/sqobject.cpp
vendored
Normal file
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include "sqpcheader.h"
|
||||
#include "sqvm.h"
|
||||
#include "sqstring.h"
|
||||
#include "sqarray.h"
|
||||
#include "sqtable.h"
|
||||
#include "squserdata.h"
|
||||
#include "sqfuncproto.h"
|
||||
#include "sqclass.h"
|
||||
#include "sqclosure.h"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
|
||||
const SQChar *IdType2Name(SQObjectType type)
|
||||
{
|
||||
switch(_RAW_TYPE(type))
|
||||
{
|
||||
case _RT_NULL:return "null";
|
||||
case _RT_INTEGER:return "integer";
|
||||
case _RT_FLOAT:return "float";
|
||||
case _RT_BOOL:return "bool";
|
||||
case _RT_STRING:return "string";
|
||||
case _RT_TABLE:return "table";
|
||||
case _RT_ARRAY:return "array";
|
||||
case _RT_GENERATOR:return "generator";
|
||||
case _RT_CLOSURE:
|
||||
case _RT_NATIVECLOSURE:
|
||||
return "function";
|
||||
case _RT_USERDATA:
|
||||
case _RT_USERPOINTER:
|
||||
return "userdata";
|
||||
case _RT_THREAD: return "thread";
|
||||
case _RT_FUNCPROTO: return "function";
|
||||
case _RT_CLASS: return "class";
|
||||
case _RT_INSTANCE: return "instance";
|
||||
case _RT_WEAKREF: return "weakref";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const SQChar *GetTypeName(const SQObjectPtr &obj1)
|
||||
{
|
||||
return IdType2Name(type(obj1));
|
||||
}
|
||||
|
||||
SQString *SQString::Create(SQSharedState *ss,const SQChar *s,SQInteger len)
|
||||
{
|
||||
SQString *str=ADD_STRING(ss,s,len);
|
||||
str->_sharedstate=ss;
|
||||
return str;
|
||||
}
|
||||
|
||||
void SQString::Release()
|
||||
{
|
||||
REMOVE_STRING(_sharedstate,this);
|
||||
}
|
||||
|
||||
SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
|
||||
{
|
||||
SQInteger idx = (SQInteger)TranslateIndex(refpos);
|
||||
while(idx < _len){
|
||||
outkey = (SQInteger)idx;
|
||||
outval = SQInteger(_val[idx]);
|
||||
//return idx for the next iteration
|
||||
return ++idx;
|
||||
}
|
||||
//nothing to iterate anymore
|
||||
return -1;
|
||||
}
|
||||
|
||||
SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx)
|
||||
{
|
||||
switch(type(idx)){
|
||||
case OT_NULL:
|
||||
return 0;
|
||||
case OT_INTEGER:
|
||||
return (SQUnsignedInteger)_integer(idx);
|
||||
default: assert(0); break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SQWeakRef *SQRefCounted::GetWeakRef(SQObjectType type)
|
||||
{
|
||||
if(!_weakref) {
|
||||
sq_new(_weakref,SQWeakRef);
|
||||
_weakref->_obj._type = type;
|
||||
_weakref->_obj._unVal.pRefCounted = this;
|
||||
}
|
||||
return _weakref;
|
||||
}
|
||||
|
||||
SQRefCounted::~SQRefCounted()
|
||||
{
|
||||
if(_weakref) {
|
||||
_weakref->_obj._type = OT_NULL;
|
||||
_weakref->_obj._unVal.pRefCounted = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SQWeakRef::Release() {
|
||||
if(ISREFCOUNTED(_obj._type)) {
|
||||
_obj._unVal.pRefCounted->_weakref = NULL;
|
||||
}
|
||||
sq_delete(this,SQWeakRef);
|
||||
}
|
||||
|
||||
bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) {
|
||||
if(_delegate) {
|
||||
return _delegate->Get((*_ss(v)->_metamethods)[mm],res);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SQDelegable::SetDelegate(SQTable *mt)
|
||||
{
|
||||
SQTable *temp = mt;
|
||||
if(temp == this) return false;
|
||||
while (temp) {
|
||||
if (temp->_delegate == this) return false; //cycle detected
|
||||
temp = temp->_delegate;
|
||||
}
|
||||
if (mt) __ObjAddRef(mt);
|
||||
__ObjRelease(_delegate);
|
||||
_delegate = mt;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQGenerator::Yield(SQVM *v)
|
||||
{
|
||||
if(_state==eSuspended) { v->Raise_Error("internal vm error, yielding dead generator"); return false;}
|
||||
if(_state==eDead) { v->Raise_Error("internal vm error, yielding a dead generator"); return false; }
|
||||
SQInteger size = v->_top-v->_stackbase;
|
||||
_ci=*v->ci;
|
||||
_stack.resize(size);
|
||||
for(SQInteger n =0; n<size; n++) {
|
||||
_stack._vals[n] = v->_stack[v->_stackbase+n];
|
||||
v->_stack[v->_stackbase+n] = _null_;
|
||||
}
|
||||
SQInteger nvargs = v->ci->_vargs.size;
|
||||
SQInteger vargsbase = v->ci->_vargs.base;
|
||||
for(SQInteger j = nvargs - 1; j >= 0; j--) {
|
||||
_vargsstack.push_back(v->_vargsstack[vargsbase+j]);
|
||||
}
|
||||
_ci._generator=NULL;
|
||||
for(SQInteger i=0;i<_ci._etraps;i++) {
|
||||
_etraps.push_back(v->_etraps.top());
|
||||
v->_etraps.pop_back();
|
||||
}
|
||||
_state=eSuspended;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQGenerator::Resume(SQVM *v,SQInteger target)
|
||||
{
|
||||
SQInteger size=_stack.size();
|
||||
if(_state==eDead){ v->Raise_Error("resuming dead generator"); return false; }
|
||||
if(_state==eRunning){ v->Raise_Error("resuming active generator"); return false; }
|
||||
SQInteger prevtop=v->_top-v->_stackbase;
|
||||
PUSH_CALLINFO(v,_ci);
|
||||
SQInteger oldstackbase=v->_stackbase;
|
||||
v->_stackbase = v->_top;
|
||||
v->ci->_target = (SQInt32)target;
|
||||
v->ci->_generator = this;
|
||||
v->ci->_vargs.size = (unsigned short)_vargsstack.size();
|
||||
|
||||
for(SQInteger i=0;i<_ci._etraps;i++) {
|
||||
v->_etraps.push_back(_etraps.top());
|
||||
_etraps.pop_back();
|
||||
}
|
||||
for(SQInteger n =0; n<size; n++) {
|
||||
v->_stack[v->_stackbase+n] = _stack._vals[n];
|
||||
_stack._vals[0] = _null_;
|
||||
}
|
||||
while(_vargsstack.size()) {
|
||||
v->_vargsstack.push_back(_vargsstack.back());
|
||||
_vargsstack.pop_back();
|
||||
}
|
||||
v->ci->_vargs.base = (unsigned short)(v->_vargsstack.size() - v->ci->_vargs.size);
|
||||
v->_top=v->_stackbase+size;
|
||||
v->ci->_prevtop = (SQInt32)prevtop;
|
||||
v->ci->_prevstkbase = (SQInt32)(v->_stackbase - oldstackbase);
|
||||
_state=eRunning;
|
||||
if (type(v->_debughook) != OT_NULL && _rawval(v->_debughook) != _rawval(v->ci->_closure))
|
||||
v->CallDebugHook('c');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SQArray::Extend(const SQArray *a){
|
||||
SQInteger xlen;
|
||||
if((xlen=a->Size()))
|
||||
for(SQInteger i=0;i<xlen;i++)
|
||||
Append(a->_values[i]);
|
||||
}
|
||||
|
||||
const SQChar* SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop)
|
||||
{
|
||||
SQUnsignedInteger nvars=_nlocalvarinfos;
|
||||
const SQChar *res=NULL;
|
||||
if(nvars>=nseq){
|
||||
for(SQUnsignedInteger i=0;i<nvars;i++){
|
||||
if(_localvarinfos[i]._start_op<=nop && _localvarinfos[i]._end_op>=nop)
|
||||
{
|
||||
if(nseq==0){
|
||||
vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]);
|
||||
res=_stringval(_localvarinfos[i]._name);
|
||||
break;
|
||||
}
|
||||
nseq--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SQInteger SQFunctionProto::GetLine(SQInstruction *curr)
|
||||
{
|
||||
SQInteger op = (SQInteger)(curr-_instructions);
|
||||
SQInteger line=_lineinfos[0]._line;
|
||||
for(SQInteger i=1;i<_nlineinfos;i++){
|
||||
if(_lineinfos[i]._op>=op)
|
||||
return line;
|
||||
line=_lineinfos[i]._line;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
#define _CHECK_IO(exp) { if(!exp)return false; }
|
||||
bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer dest,SQInteger size)
|
||||
{
|
||||
if(write(up,dest,size) != size) {
|
||||
v->Raise_Error("io error (write function failure)");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SafeRead(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size)
|
||||
{
|
||||
if(size && read(up,dest,size) != size) {
|
||||
v->Raise_Error("io error, read function failure, the origin stream could be corrupted/trucated");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQInteger tag)
|
||||
{
|
||||
return SafeWrite(v,write,up,&tag,sizeof(tag));
|
||||
}
|
||||
|
||||
bool CheckTag(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQInteger tag)
|
||||
{
|
||||
SQInteger t;
|
||||
_CHECK_IO(SafeRead(v,read,up,&t,sizeof(t)));
|
||||
if(t != tag){
|
||||
v->Raise_Error("invalid or corrupted closure stream");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o)
|
||||
{
|
||||
_CHECK_IO(SafeWrite(v,write,up,&type(o),sizeof(SQObjectType)));
|
||||
switch(type(o)){
|
||||
case OT_STRING:
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,_stringval(o),_string(o)->_len));
|
||||
break;
|
||||
case OT_INTEGER:
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break;
|
||||
case OT_FLOAT:
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_float(o),sizeof(SQFloat)));break;
|
||||
case OT_NULL:
|
||||
break;
|
||||
default:
|
||||
v->Raise_Error("cannot serialize a %s",GetTypeName(o));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o)
|
||||
{
|
||||
SQObjectType t;
|
||||
_CHECK_IO(SafeRead(v,read,up,&t,sizeof(SQObjectType)));
|
||||
switch(t){
|
||||
case OT_STRING:{
|
||||
SQInteger len;
|
||||
_CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger)));
|
||||
_CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(len),len));
|
||||
o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len);
|
||||
}
|
||||
break;
|
||||
case OT_INTEGER:{
|
||||
SQInteger i;
|
||||
_CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break;
|
||||
}
|
||||
case OT_FLOAT:{
|
||||
SQFloat f;
|
||||
_CHECK_IO(SafeRead(v,read,up,&f,sizeof(SQFloat))); o = f; break;
|
||||
}
|
||||
case OT_NULL:
|
||||
o=_null_;
|
||||
break;
|
||||
default:
|
||||
v->Raise_Error("cannot serialize a %s",IdType2Name(t));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)
|
||||
{
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD));
|
||||
_CHECK_IO(WriteTag(v,write,up,sizeof(SQChar)));
|
||||
_CHECK_IO(_funcproto(_function)->Save(v,up,write));
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret)
|
||||
{
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD));
|
||||
_CHECK_IO(CheckTag(v,read,up,sizeof(SQChar)));
|
||||
SQObjectPtr func;
|
||||
_CHECK_IO(SQFunctionProto::Load(v,up,read,func));
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL));
|
||||
ret = SQClosure::Create(_ss(v),_funcproto(func));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQFunctionProto::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)
|
||||
{
|
||||
SQInteger i,nliterals = _nliterals,nparameters = _nparameters;
|
||||
SQInteger noutervalues = _noutervalues,nlocalvarinfos = _nlocalvarinfos;
|
||||
SQInteger nlineinfos=_nlineinfos,ninstructions = _ninstructions,nfunctions=_nfunctions;
|
||||
SQInteger ndefaultparams = _ndefaultparams;
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(WriteObject(v,up,write,_sourcename));
|
||||
_CHECK_IO(WriteObject(v,up,write,_name));
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&nliterals,sizeof(nliterals)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&nparameters,sizeof(nparameters)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&noutervalues,sizeof(noutervalues)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&nlocalvarinfos,sizeof(nlocalvarinfos)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&nlineinfos,sizeof(nlineinfos)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&ndefaultparams,sizeof(ndefaultparams)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&ninstructions,sizeof(ninstructions)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&nfunctions,sizeof(nfunctions)));
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
for(i=0;i<nliterals;i++){
|
||||
_CHECK_IO(WriteObject(v,up,write,_literals[i]));
|
||||
}
|
||||
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
for(i=0;i<nparameters;i++){
|
||||
_CHECK_IO(WriteObject(v,up,write,_parameters[i]));
|
||||
}
|
||||
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
for(i=0;i<noutervalues;i++){
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_outervalues[i]._type,sizeof(SQUnsignedInteger)));
|
||||
_CHECK_IO(WriteObject(v,up,write,_outervalues[i]._src));
|
||||
_CHECK_IO(WriteObject(v,up,write,_outervalues[i]._name));
|
||||
}
|
||||
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
for(i=0;i<nlocalvarinfos;i++){
|
||||
SQLocalVarInfo &lvi=_localvarinfos[i];
|
||||
_CHECK_IO(WriteObject(v,up,write,lvi._name));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&lvi._pos,sizeof(SQUnsignedInteger)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&lvi._start_op,sizeof(SQUnsignedInteger)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&lvi._end_op,sizeof(SQUnsignedInteger)));
|
||||
}
|
||||
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(SafeWrite(v,write,up,_lineinfos,sizeof(SQLineInfo)*nlineinfos));
|
||||
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(SafeWrite(v,write,up,_defaultparams,sizeof(SQInteger)*ndefaultparams));
|
||||
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(SafeWrite(v,write,up,_instructions,sizeof(SQInstruction)*ninstructions));
|
||||
|
||||
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
|
||||
for(i=0;i<nfunctions;i++){
|
||||
_CHECK_IO(_funcproto(_functions[i])->Save(v,up,write));
|
||||
}
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_stacksize,sizeof(_stacksize)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_bgenerator,sizeof(_bgenerator)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_varparams,sizeof(_varparams)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret)
|
||||
{
|
||||
SQInteger i, nliterals,nparameters;
|
||||
SQInteger noutervalues ,nlocalvarinfos ;
|
||||
SQInteger nlineinfos,ninstructions ,nfunctions,ndefaultparams ;
|
||||
SQObjectPtr sourcename, name;
|
||||
SQObjectPtr o;
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(ReadObject(v, up, read, sourcename));
|
||||
_CHECK_IO(ReadObject(v, up, read, name));
|
||||
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(SafeRead(v,read,up, &nliterals, sizeof(nliterals)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &nparameters, sizeof(nparameters)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &noutervalues, sizeof(noutervalues)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &nlocalvarinfos, sizeof(nlocalvarinfos)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &nlineinfos, sizeof(nlineinfos)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &ndefaultparams, sizeof(ndefaultparams)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &ninstructions, sizeof(ninstructions)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &nfunctions, sizeof(nfunctions)));
|
||||
|
||||
|
||||
SQFunctionProto *f = SQFunctionProto::Create(ninstructions,nliterals,nparameters,
|
||||
nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams);
|
||||
SQObjectPtr proto = f; //gets a ref in case of failure
|
||||
f->_sourcename = sourcename;
|
||||
f->_name = name;
|
||||
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
|
||||
for(i = 0;i < nliterals; i++){
|
||||
_CHECK_IO(ReadObject(v, up, read, o));
|
||||
f->_literals[i] = o;
|
||||
}
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
|
||||
for(i = 0; i < nparameters; i++){
|
||||
_CHECK_IO(ReadObject(v, up, read, o));
|
||||
f->_parameters[i] = o;
|
||||
}
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
|
||||
for(i = 0; i < noutervalues; i++){
|
||||
SQUnsignedInteger type;
|
||||
SQObjectPtr name;
|
||||
_CHECK_IO(SafeRead(v,read,up, &type, sizeof(SQUnsignedInteger)));
|
||||
_CHECK_IO(ReadObject(v, up, read, o));
|
||||
_CHECK_IO(ReadObject(v, up, read, name));
|
||||
f->_outervalues[i] = SQOuterVar(name,o, (SQOuterType)type);
|
||||
}
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
|
||||
for(i = 0; i < nlocalvarinfos; i++){
|
||||
SQLocalVarInfo lvi;
|
||||
_CHECK_IO(ReadObject(v, up, read, lvi._name));
|
||||
_CHECK_IO(SafeRead(v,read,up, &lvi._pos, sizeof(SQUnsignedInteger)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &lvi._start_op, sizeof(SQUnsignedInteger)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &lvi._end_op, sizeof(SQUnsignedInteger)));
|
||||
f->_localvarinfos[i] = lvi;
|
||||
}
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(SafeRead(v,read,up, f->_lineinfos, sizeof(SQLineInfo)*nlineinfos));
|
||||
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(SafeRead(v,read,up, f->_defaultparams, sizeof(SQInteger)*ndefaultparams));
|
||||
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
_CHECK_IO(SafeRead(v,read,up, f->_instructions, sizeof(SQInstruction)*ninstructions));
|
||||
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
|
||||
for(i = 0; i < nfunctions; i++){
|
||||
_CHECK_IO(_funcproto(o)->Load(v, up, read, o));
|
||||
f->_functions[i] = o;
|
||||
}
|
||||
_CHECK_IO(SafeRead(v,read,up, &f->_stacksize, sizeof(f->_stacksize)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &f->_bgenerator, sizeof(f->_bgenerator)));
|
||||
_CHECK_IO(SafeRead(v,read,up, &f->_varparams, sizeof(f->_varparams)));
|
||||
|
||||
ret = f;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
|
||||
#define START_MARK() if(!(_uiRef&MARK_FLAG)){ \
|
||||
_uiRef|=MARK_FLAG;
|
||||
|
||||
#define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \
|
||||
AddToChain(chain, this); }
|
||||
|
||||
void SQVM::Mark(SQCollectable **chain)
|
||||
{
|
||||
START_MARK()
|
||||
SQSharedState::MarkObject(_lasterror,chain);
|
||||
SQSharedState::MarkObject(_errorhandler,chain);
|
||||
SQSharedState::MarkObject(_debughook,chain);
|
||||
SQSharedState::MarkObject(_roottable, chain);
|
||||
SQSharedState::MarkObject(temp_reg, chain);
|
||||
for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain);
|
||||
for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain);
|
||||
for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::MarkObject(_callsstack[k]._closure, chain);
|
||||
END_MARK()
|
||||
}
|
||||
|
||||
void SQArray::Mark(SQCollectable **chain)
|
||||
{
|
||||
START_MARK()
|
||||
SQInteger len = _values.size();
|
||||
for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain);
|
||||
END_MARK()
|
||||
}
|
||||
void SQTable::Mark(SQCollectable **chain)
|
||||
{
|
||||
START_MARK()
|
||||
if(_delegate) _delegate->Mark(chain);
|
||||
SQInteger len = _numofnodes;
|
||||
for(SQInteger i = 0; i < len; i++){
|
||||
SQSharedState::MarkObject(_nodes[i].key, chain);
|
||||
SQSharedState::MarkObject(_nodes[i].val, chain);
|
||||
}
|
||||
END_MARK()
|
||||
}
|
||||
|
||||
void SQClass::Mark(SQCollectable **chain)
|
||||
{
|
||||
START_MARK()
|
||||
_members->Mark(chain);
|
||||
if(_base) _base->Mark(chain);
|
||||
SQSharedState::MarkObject(_attributes, chain);
|
||||
for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) {
|
||||
SQSharedState::MarkObject(_defaultvalues[i].val, chain);
|
||||
SQSharedState::MarkObject(_defaultvalues[i].attrs, chain);
|
||||
}
|
||||
for(SQUnsignedInteger j =0; j< _methods.size(); j++) {
|
||||
SQSharedState::MarkObject(_methods[j].val, chain);
|
||||
SQSharedState::MarkObject(_methods[j].attrs, chain);
|
||||
}
|
||||
for(SQUnsignedInteger k =0; k< _metamethods.size(); k++) {
|
||||
SQSharedState::MarkObject(_metamethods[k], chain);
|
||||
}
|
||||
END_MARK()
|
||||
}
|
||||
|
||||
void SQInstance::Mark(SQCollectable **chain)
|
||||
{
|
||||
START_MARK()
|
||||
_class->Mark(chain);
|
||||
SQUnsignedInteger nvalues = _class->_defaultvalues.size();
|
||||
for(SQUnsignedInteger i =0; i< nvalues; i++) {
|
||||
SQSharedState::MarkObject(_values[i], chain);
|
||||
}
|
||||
END_MARK()
|
||||
}
|
||||
|
||||
void SQGenerator::Mark(SQCollectable **chain)
|
||||
{
|
||||
START_MARK()
|
||||
for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain);
|
||||
for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain);
|
||||
SQSharedState::MarkObject(_closure, chain);
|
||||
END_MARK()
|
||||
}
|
||||
|
||||
void SQClosure::Mark(SQCollectable **chain)
|
||||
{
|
||||
START_MARK()
|
||||
for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);
|
||||
for(SQUnsignedInteger i = 0; i < _defaultparams.size(); i++) SQSharedState::MarkObject(_defaultparams[i], chain);
|
||||
END_MARK()
|
||||
}
|
||||
|
||||
void SQNativeClosure::Mark(SQCollectable **chain)
|
||||
{
|
||||
START_MARK()
|
||||
for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);
|
||||
END_MARK()
|
||||
}
|
||||
|
||||
void SQUserData::Mark(SQCollectable **chain){
|
||||
START_MARK()
|
||||
if(_delegate) _delegate->Mark(chain);
|
||||
END_MARK()
|
||||
}
|
||||
|
||||
void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; }
|
||||
|
||||
#endif
|
||||
|
||||
381
src/3rdparty/squirrel/squirrel/sqobject.h
vendored
Normal file
381
src/3rdparty/squirrel/squirrel/sqobject.h
vendored
Normal file
@@ -0,0 +1,381 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQOBJECT_H_
|
||||
#define _SQOBJECT_H_
|
||||
|
||||
#include "squtils.h"
|
||||
|
||||
#define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R'))
|
||||
#define SQ_CLOSURESTREAM_PART (('P'<<24)|('A'<<16)|('R'<<8)|('T'))
|
||||
#define SQ_CLOSURESTREAM_TAIL (('T'<<24)|('A'<<16)|('I'<<8)|('L'))
|
||||
|
||||
struct SQSharedState;
|
||||
|
||||
enum SQMetaMethod{
|
||||
MT_ADD=0,
|
||||
MT_SUB=1,
|
||||
MT_MUL=2,
|
||||
MT_DIV=3,
|
||||
MT_UNM=4,
|
||||
MT_MODULO=5,
|
||||
MT_SET=6,
|
||||
MT_GET=7,
|
||||
MT_TYPEOF=8,
|
||||
MT_NEXTI=9,
|
||||
MT_CMP=10,
|
||||
MT_CALL=11,
|
||||
MT_CLONED=12,
|
||||
MT_NEWSLOT=13,
|
||||
MT_DELSLOT=14,
|
||||
MT_TOSTRING=15,
|
||||
MT_NEWMEMBER=16,
|
||||
MT_INHERITED=17,
|
||||
MT_LAST = 18
|
||||
};
|
||||
|
||||
#define MM_ADD "_add"
|
||||
#define MM_SUB "_sub"
|
||||
#define MM_MUL "_mul"
|
||||
#define MM_DIV "_div"
|
||||
#define MM_UNM "_unm"
|
||||
#define MM_MODULO "_modulo"
|
||||
#define MM_SET "_set"
|
||||
#define MM_GET "_get"
|
||||
#define MM_TYPEOF "_typeof"
|
||||
#define MM_NEXTI "_nexti"
|
||||
#define MM_CMP "_cmp"
|
||||
#define MM_CALL "_call"
|
||||
#define MM_CLONED "_cloned"
|
||||
#define MM_NEWSLOT "_newslot"
|
||||
#define MM_DELSLOT "_delslot"
|
||||
#define MM_TOSTRING "_tostring"
|
||||
#define MM_NEWMEMBER "_newmember"
|
||||
#define MM_INHERITED "_inherited"
|
||||
|
||||
#define MINPOWER2 4
|
||||
|
||||
struct SQRefCounted
|
||||
{
|
||||
SQRefCounted() { _uiRef = 0; _weakref = NULL; }
|
||||
virtual ~SQRefCounted();
|
||||
SQWeakRef *GetWeakRef(SQObjectType type);
|
||||
SQUnsignedInteger _uiRef;
|
||||
struct SQWeakRef *_weakref;
|
||||
virtual void Release()=0;
|
||||
};
|
||||
|
||||
struct SQWeakRef : SQRefCounted
|
||||
{
|
||||
void Release();
|
||||
SQObject _obj;
|
||||
};
|
||||
|
||||
#define _realval(o) (type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj)
|
||||
|
||||
struct SQObjectPtr;
|
||||
|
||||
#define __AddRef(type,unval) if(ISREFCOUNTED(type)) \
|
||||
{ \
|
||||
unval.pRefCounted->_uiRef++; \
|
||||
}
|
||||
|
||||
#define __Release(type,unval) if(ISREFCOUNTED(type) && ((--unval.pRefCounted->_uiRef)<=0)) \
|
||||
{ \
|
||||
unval.pRefCounted->Release(); \
|
||||
}
|
||||
|
||||
#define __ObjRelease(obj) { \
|
||||
if((obj)) { \
|
||||
(obj)->_uiRef--; \
|
||||
if((obj)->_uiRef == 0) \
|
||||
(obj)->Release(); \
|
||||
(obj) = NULL; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define __ObjAddRef(obj) { \
|
||||
(obj)->_uiRef++; \
|
||||
}
|
||||
|
||||
#define type(obj) ((obj)._type)
|
||||
#define is_delegable(t) (type(t)&SQOBJECT_DELEGABLE)
|
||||
#define raw_type(obj) _RAW_TYPE((obj)._type)
|
||||
|
||||
#define _integer(obj) ((obj)._unVal.nInteger)
|
||||
#define _float(obj) ((obj)._unVal.fFloat)
|
||||
#define _string(obj) ((obj)._unVal.pString)
|
||||
#define _table(obj) ((obj)._unVal.pTable)
|
||||
#define _array(obj) ((obj)._unVal.pArray)
|
||||
#define _closure(obj) ((obj)._unVal.pClosure)
|
||||
#define _generator(obj) ((obj)._unVal.pGenerator)
|
||||
#define _nativeclosure(obj) ((obj)._unVal.pNativeClosure)
|
||||
#define _userdata(obj) ((obj)._unVal.pUserData)
|
||||
#define _userpointer(obj) ((obj)._unVal.pUserPointer)
|
||||
#define _thread(obj) ((obj)._unVal.pThread)
|
||||
#define _funcproto(obj) ((obj)._unVal.pFunctionProto)
|
||||
#define _class(obj) ((obj)._unVal.pClass)
|
||||
#define _instance(obj) ((obj)._unVal.pInstance)
|
||||
#define _delegable(obj) ((SQDelegable *)(obj)._unVal.pDelegable)
|
||||
#define _weakref(obj) ((obj)._unVal.pWeakRef)
|
||||
#define _refcounted(obj) ((obj)._unVal.pRefCounted)
|
||||
#define _rawval(obj) ((obj)._unVal.raw)
|
||||
|
||||
#define _stringval(obj) (obj)._unVal.pString->_val
|
||||
#define _userdataval(obj) (obj)._unVal.pUserData->_val
|
||||
|
||||
#define tofloat(num) ((type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num))
|
||||
#define tointeger(num) ( (type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num))
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
struct SQObjectPtr : public SQObject
|
||||
{
|
||||
SQObjectPtr()
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_NULL;
|
||||
_unVal.pUserPointer=NULL;
|
||||
}
|
||||
SQObjectPtr(const SQObjectPtr &o)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=o._type;
|
||||
_unVal=o._unVal;
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(const SQObject &o)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=o._type;
|
||||
_unVal=o._unVal;
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQTable *pTable)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_TABLE;
|
||||
_unVal.pTable=pTable;
|
||||
assert(_unVal.pTable);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQClass *pClass)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_CLASS;
|
||||
_unVal.pClass=pClass;
|
||||
assert(_unVal.pClass);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQInstance *pInstance)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_INSTANCE;
|
||||
_unVal.pInstance=pInstance;
|
||||
assert(_unVal.pInstance);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQArray *pArray)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_ARRAY;
|
||||
_unVal.pArray=pArray;
|
||||
assert(_unVal.pArray);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQClosure *pClosure)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_CLOSURE;
|
||||
_unVal.pClosure=pClosure;
|
||||
assert(_unVal.pClosure);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQGenerator *pGenerator)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_GENERATOR;
|
||||
_unVal.pGenerator=pGenerator;
|
||||
assert(_unVal.pGenerator);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQNativeClosure *pNativeClosure)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_NATIVECLOSURE;
|
||||
_unVal.pNativeClosure=pNativeClosure;
|
||||
assert(_unVal.pNativeClosure);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQString *pString)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_STRING;
|
||||
_unVal.pString=pString;
|
||||
assert(_unVal.pString);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQUserData *pUserData)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_USERDATA;
|
||||
_unVal.pUserData=pUserData;
|
||||
assert(_unVal.pUserData);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQVM *pThread)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_THREAD;
|
||||
_unVal.pThread=pThread;
|
||||
assert(_unVal.pThread);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQWeakRef *pWeakRef)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_WEAKREF;
|
||||
_unVal.pWeakRef=pWeakRef;
|
||||
assert(_unVal.pWeakRef);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQFunctionProto *pFunctionProto)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_FUNCPROTO;
|
||||
_unVal.pFunctionProto=pFunctionProto;
|
||||
assert(_unVal.pFunctionProto);
|
||||
__AddRef(_type,_unVal);
|
||||
}
|
||||
SQObjectPtr(SQInteger nInteger)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_INTEGER;
|
||||
_unVal.nInteger=nInteger;
|
||||
}
|
||||
SQObjectPtr(SQFloat fFloat)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_FLOAT;
|
||||
_unVal.fFloat=fFloat;
|
||||
}
|
||||
SQObjectPtr(bool bBool)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type = OT_BOOL;
|
||||
_unVal.nInteger = bBool?1:0;
|
||||
}
|
||||
SQObjectPtr(SQUserPointer pUserPointer)
|
||||
{
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_type=OT_USERPOINTER;
|
||||
_unVal.pUserPointer=pUserPointer;
|
||||
}
|
||||
~SQObjectPtr()
|
||||
{
|
||||
__Release(_type,_unVal);
|
||||
}
|
||||
inline void Null()
|
||||
{
|
||||
SQObjectType tOldType;
|
||||
SQObjectValue unOldVal;
|
||||
tOldType = _type;
|
||||
unOldVal = _unVal;
|
||||
_type = OT_NULL;
|
||||
_unVal.pUserPointer = NULL;
|
||||
__Release(tOldType,unOldVal);
|
||||
}
|
||||
inline SQObjectPtr& operator=(SQInteger i)
|
||||
{
|
||||
__Release(_type,_unVal);
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_unVal.nInteger = i;
|
||||
_type = OT_INTEGER;
|
||||
return *this;
|
||||
}
|
||||
inline SQObjectPtr& operator=(SQFloat f)
|
||||
{
|
||||
__Release(_type,_unVal);
|
||||
SQ_OBJECT_RAWINIT()
|
||||
_unVal.fFloat = f;
|
||||
_type = OT_FLOAT;
|
||||
return *this;
|
||||
}
|
||||
inline SQObjectPtr& operator=(const SQObjectPtr& obj)
|
||||
{
|
||||
SQObjectType tOldType;
|
||||
SQObjectValue unOldVal;
|
||||
tOldType=_type;
|
||||
unOldVal=_unVal;
|
||||
_unVal = obj._unVal;
|
||||
_type = obj._type;
|
||||
__AddRef(_type,_unVal);
|
||||
__Release(tOldType,unOldVal);
|
||||
return *this;
|
||||
}
|
||||
inline SQObjectPtr& operator=(const SQObject& obj)
|
||||
{
|
||||
SQObjectType tOldType;
|
||||
SQObjectValue unOldVal;
|
||||
tOldType=_type;
|
||||
unOldVal=_unVal;
|
||||
_unVal = obj._unVal;
|
||||
_type = obj._type;
|
||||
__AddRef(_type,_unVal);
|
||||
__Release(tOldType,unOldVal);
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
SQObjectPtr(const SQChar *){} //safety
|
||||
};
|
||||
|
||||
inline void _Swap(SQObject &a,SQObject &b)
|
||||
{
|
||||
SQObjectType tOldType = a._type;
|
||||
SQObjectValue unOldVal = a._unVal;
|
||||
a._type = b._type;
|
||||
a._unVal = b._unVal;
|
||||
b._type = tOldType;
|
||||
b._unVal = unOldVal;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
#define MARK_FLAG 0x80000000
|
||||
struct SQCollectable : public SQRefCounted {
|
||||
SQCollectable *_next;
|
||||
SQCollectable *_prev;
|
||||
SQSharedState *_sharedstate;
|
||||
virtual void Release()=0;
|
||||
virtual void Mark(SQCollectable **chain)=0;
|
||||
void UnMark();
|
||||
virtual void Finalize()=0;
|
||||
static void AddToChain(SQCollectable **chain,SQCollectable *c);
|
||||
static void RemoveFromChain(SQCollectable **chain,SQCollectable *c);
|
||||
};
|
||||
|
||||
|
||||
#define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj)
|
||||
#define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);}
|
||||
#define CHAINABLE_OBJ SQCollectable
|
||||
#define INIT_CHAIN() {_next=NULL;_prev=NULL;_sharedstate=ss;}
|
||||
#else
|
||||
|
||||
#define ADD_TO_CHAIN(chain,obj) ((void)0)
|
||||
#define REMOVE_FROM_CHAIN(chain,obj) ((void)0)
|
||||
#define CHAINABLE_OBJ SQRefCounted
|
||||
#define INIT_CHAIN() ((void)0)
|
||||
#endif
|
||||
|
||||
struct SQDelegable : public CHAINABLE_OBJ {
|
||||
bool SetDelegate(SQTable *m);
|
||||
virtual bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res);
|
||||
SQTable *_delegate;
|
||||
};
|
||||
|
||||
SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx);
|
||||
typedef sqvector<SQObjectPtr> SQObjectPtrVec;
|
||||
typedef sqvector<SQInteger> SQIntVec;
|
||||
const SQChar *GetTypeName(const SQObjectPtr &obj1);
|
||||
const SQChar *IdType2Name(SQObjectType type);
|
||||
|
||||
|
||||
|
||||
#endif //_SQOBJECT_H_
|
||||
115
src/3rdparty/squirrel/squirrel/sqopcodes.h
vendored
Normal file
115
src/3rdparty/squirrel/squirrel/sqopcodes.h
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQOPCODES_H_
|
||||
#define _SQOPCODES_H_
|
||||
|
||||
#define MAX_FUNC_STACKSIZE 0xFF
|
||||
#define MAX_LITERALS ((SQInteger)0x7FFFFFFF)
|
||||
|
||||
enum BitWiseOP {
|
||||
BW_AND = 0,
|
||||
BW_OR = 2,
|
||||
BW_XOR = 3,
|
||||
BW_SHIFTL = 4,
|
||||
BW_SHIFTR = 5,
|
||||
BW_USHIFTR = 6
|
||||
};
|
||||
|
||||
enum CmpOP {
|
||||
CMP_G = 0,
|
||||
CMP_GE = 2,
|
||||
CMP_L = 3,
|
||||
CMP_LE = 4
|
||||
};
|
||||
enum SQOpcode
|
||||
{
|
||||
_OP_LINE= 0x00,
|
||||
_OP_LOAD= 0x01,
|
||||
_OP_LOADINT= 0x02,
|
||||
_OP_LOADFLOAT= 0x03,
|
||||
_OP_DLOAD= 0x04,
|
||||
_OP_TAILCALL= 0x05,
|
||||
_OP_CALL= 0x06,
|
||||
_OP_PREPCALL= 0x07,
|
||||
_OP_PREPCALLK= 0x08,
|
||||
_OP_GETK= 0x09,
|
||||
_OP_MOVE= 0x0A,
|
||||
_OP_NEWSLOT= 0x0B,
|
||||
_OP_DELETE= 0x0C,
|
||||
_OP_SET= 0x0D,
|
||||
_OP_GET= 0x0E,
|
||||
_OP_EQ= 0x0F,
|
||||
_OP_NE= 0x10,
|
||||
_OP_ARITH= 0x11,
|
||||
_OP_BITW= 0x12,
|
||||
_OP_RETURN= 0x13,
|
||||
_OP_LOADNULLS= 0x14,
|
||||
_OP_LOADROOTTABLE= 0x15,
|
||||
_OP_LOADBOOL= 0x16,
|
||||
_OP_DMOVE= 0x17,
|
||||
_OP_JMP= 0x18,
|
||||
_OP_JNZ= 0x19,
|
||||
_OP_JZ= 0x1A,
|
||||
_OP_LOADFREEVAR= 0x1B,
|
||||
_OP_VARGC= 0x1C,
|
||||
_OP_GETVARGV= 0x1D,
|
||||
_OP_NEWTABLE= 0x1E,
|
||||
_OP_NEWARRAY= 0x1F,
|
||||
_OP_APPENDARRAY= 0x20,
|
||||
_OP_GETPARENT= 0x21,
|
||||
_OP_COMPARITH= 0x22,
|
||||
_OP_COMPARITHL= 0x23,
|
||||
_OP_INC= 0x24,
|
||||
_OP_INCL= 0x25,
|
||||
_OP_PINC= 0x26,
|
||||
_OP_PINCL= 0x27,
|
||||
_OP_CMP= 0x28,
|
||||
_OP_EXISTS= 0x29,
|
||||
_OP_INSTANCEOF= 0x2A,
|
||||
_OP_AND= 0x2B,
|
||||
_OP_OR= 0x2C,
|
||||
_OP_NEG= 0x2D,
|
||||
_OP_NOT= 0x2E,
|
||||
_OP_BWNOT= 0x2F,
|
||||
_OP_CLOSURE= 0x30,
|
||||
_OP_YIELD= 0x31,
|
||||
_OP_RESUME= 0x32,
|
||||
_OP_FOREACH= 0x33,
|
||||
_OP_POSTFOREACH= 0x34,
|
||||
_OP_DELEGATE= 0x35,
|
||||
_OP_CLONE= 0x36,
|
||||
_OP_TYPEOF= 0x37,
|
||||
_OP_PUSHTRAP= 0x38,
|
||||
_OP_POPTRAP= 0x39,
|
||||
_OP_THROW= 0x3A,
|
||||
_OP_CLASS= 0x3B,
|
||||
_OP_NEWSLOTA= 0x3C,
|
||||
_OP_SCOPE_END= 0x3D,
|
||||
};
|
||||
|
||||
struct SQInstructionDesc {
|
||||
const SQChar *name;
|
||||
};
|
||||
|
||||
struct SQInstruction
|
||||
{
|
||||
SQInstruction(SQOpcode _op=_OP_SCOPE_END,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0)
|
||||
{ op = _op;
|
||||
_arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1;
|
||||
_arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3;
|
||||
}
|
||||
|
||||
|
||||
SQInt32 _arg1;
|
||||
unsigned char op;
|
||||
unsigned char _arg0;
|
||||
unsigned char _arg2;
|
||||
unsigned char _arg3;
|
||||
};
|
||||
|
||||
#include "squtils.h"
|
||||
typedef sqvector<SQInstruction> SQInstructionVec;
|
||||
|
||||
#define NEW_SLOT_ATTRIBUTES_FLAG 0x01
|
||||
#define NEW_SLOT_STATIC_FLAG 0x02
|
||||
|
||||
#endif // _SQOPCODES_H_
|
||||
15
src/3rdparty/squirrel/squirrel/sqpcheader.h
vendored
Normal file
15
src/3rdparty/squirrel/squirrel/sqpcheader.h
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQPCHEADER_H_
|
||||
#define _SQPCHEADER_H_
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
//squirrel stuff
|
||||
#include <squirrel.h>
|
||||
#include "sqobject.h"
|
||||
#include "sqstate.h"
|
||||
|
||||
#endif //_SQPCHEADER_H_
|
||||
585
src/3rdparty/squirrel/squirrel/sqstate.cpp
vendored
Normal file
585
src/3rdparty/squirrel/squirrel/sqstate.cpp
vendored
Normal file
@@ -0,0 +1,585 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include "sqpcheader.h"
|
||||
#include "sqopcodes.h"
|
||||
#include "sqvm.h"
|
||||
#include "sqfuncproto.h"
|
||||
#include "sqclosure.h"
|
||||
#include "sqstring.h"
|
||||
#include "sqtable.h"
|
||||
#include "sqarray.h"
|
||||
#include "squserdata.h"
|
||||
#include "sqclass.h"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
SQObjectPtr _null_;
|
||||
SQObjectPtr _true_(true);
|
||||
SQObjectPtr _false_(false);
|
||||
SQObjectPtr _one_((SQInteger)1);
|
||||
SQObjectPtr _minusone_((SQInteger)-1);
|
||||
|
||||
#define newsysstring(s) { \
|
||||
_systemstrings->push_back(SQString::Create(this,s)); \
|
||||
}
|
||||
|
||||
#define newmetamethod(s) { \
|
||||
_metamethods->push_back(SQString::Create(this,s)); \
|
||||
_table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \
|
||||
}
|
||||
|
||||
bool CompileTypemask(SQIntVec &res,const SQChar *typemask)
|
||||
{
|
||||
SQInteger i = 0;
|
||||
|
||||
SQInteger mask = 0;
|
||||
while(typemask[i] != 0) {
|
||||
|
||||
switch(typemask[i]){
|
||||
case 'o': mask |= _RT_NULL; break;
|
||||
case 'i': mask |= _RT_INTEGER; break;
|
||||
case 'f': mask |= _RT_FLOAT; break;
|
||||
case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break;
|
||||
case 's': mask |= _RT_STRING; break;
|
||||
case 't': mask |= _RT_TABLE; break;
|
||||
case 'a': mask |= _RT_ARRAY; break;
|
||||
case 'u': mask |= _RT_USERDATA; break;
|
||||
case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break;
|
||||
case 'b': mask |= _RT_BOOL; break;
|
||||
case 'g': mask |= _RT_GENERATOR; break;
|
||||
case 'p': mask |= _RT_USERPOINTER; break;
|
||||
case 'v': mask |= _RT_THREAD; break;
|
||||
case 'x': mask |= _RT_INSTANCE; break;
|
||||
case 'y': mask |= _RT_CLASS; break;
|
||||
case 'r': mask |= _RT_WEAKREF; break;
|
||||
case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue;
|
||||
case ' ': i++; continue; //ignores spaces
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
if(typemask[i] == '|') {
|
||||
i++;
|
||||
if(typemask[i] == 0)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
res.push_back(mask);
|
||||
mask = 0;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz)
|
||||
{
|
||||
SQInteger i=0;
|
||||
SQTable *t=SQTable::Create(ss,0);
|
||||
while(funcz[i].name!=0){
|
||||
SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f);
|
||||
nc->_nparamscheck = funcz[i].nparamscheck;
|
||||
nc->_name = SQString::Create(ss,funcz[i].name);
|
||||
if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask))
|
||||
return NULL;
|
||||
t->NewSlot(SQString::Create(ss,funcz[i].name),nc);
|
||||
i++;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
SQSharedState::SQSharedState()
|
||||
{
|
||||
_compilererrorhandler = NULL;
|
||||
_printfunc = NULL;
|
||||
_debuginfo = false;
|
||||
_notifyallexceptions = false;
|
||||
_scratchpad=NULL;
|
||||
_scratchpadsize=0;
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
_gc_chain=NULL;
|
||||
#endif
|
||||
sq_new(_stringtable,SQStringTable);
|
||||
sq_new(_metamethods,SQObjectPtrVec);
|
||||
sq_new(_systemstrings,SQObjectPtrVec);
|
||||
sq_new(_types,SQObjectPtrVec);
|
||||
_metamethodsmap = SQTable::Create(this,MT_LAST-1);
|
||||
//adding type strings to avoid memory trashing
|
||||
//types names
|
||||
newsysstring("null");
|
||||
newsysstring("table");
|
||||
newsysstring("array");
|
||||
newsysstring("closure");
|
||||
newsysstring("string");
|
||||
newsysstring("userdata");
|
||||
newsysstring("integer");
|
||||
newsysstring("float");
|
||||
newsysstring("userpointer");
|
||||
newsysstring("function");
|
||||
newsysstring("generator");
|
||||
newsysstring("thread");
|
||||
newsysstring("class");
|
||||
newsysstring("instance");
|
||||
newsysstring("bool");
|
||||
//meta methods
|
||||
newmetamethod(MM_ADD);
|
||||
newmetamethod(MM_SUB);
|
||||
newmetamethod(MM_MUL);
|
||||
newmetamethod(MM_DIV);
|
||||
newmetamethod(MM_UNM);
|
||||
newmetamethod(MM_MODULO);
|
||||
newmetamethod(MM_SET);
|
||||
newmetamethod(MM_GET);
|
||||
newmetamethod(MM_TYPEOF);
|
||||
newmetamethod(MM_NEXTI);
|
||||
newmetamethod(MM_CMP);
|
||||
newmetamethod(MM_CALL);
|
||||
newmetamethod(MM_CLONED);
|
||||
newmetamethod(MM_NEWSLOT);
|
||||
newmetamethod(MM_DELSLOT);
|
||||
newmetamethod(MM_TOSTRING);
|
||||
newmetamethod(MM_NEWMEMBER);
|
||||
newmetamethod(MM_INHERITED);
|
||||
|
||||
_constructoridx = SQString::Create(this,"constructor");
|
||||
_registry = SQTable::Create(this,0);
|
||||
_consts = SQTable::Create(this,0);
|
||||
_table_default_delegate = CreateDefaultDelegate(this,_table_default_delegate_funcz);
|
||||
_array_default_delegate = CreateDefaultDelegate(this,_array_default_delegate_funcz);
|
||||
_string_default_delegate = CreateDefaultDelegate(this,_string_default_delegate_funcz);
|
||||
_number_default_delegate = CreateDefaultDelegate(this,_number_default_delegate_funcz);
|
||||
_closure_default_delegate = CreateDefaultDelegate(this,_closure_default_delegate_funcz);
|
||||
_generator_default_delegate = CreateDefaultDelegate(this,_generator_default_delegate_funcz);
|
||||
_thread_default_delegate = CreateDefaultDelegate(this,_thread_default_delegate_funcz);
|
||||
_class_default_delegate = CreateDefaultDelegate(this,_class_default_delegate_funcz);
|
||||
_instance_default_delegate = CreateDefaultDelegate(this,_instance_default_delegate_funcz);
|
||||
_weakref_default_delegate = CreateDefaultDelegate(this,_weakref_default_delegate_funcz);
|
||||
|
||||
}
|
||||
|
||||
SQSharedState::~SQSharedState()
|
||||
{
|
||||
_constructoridx = _null_;
|
||||
_table(_registry)->Finalize();
|
||||
_table(_consts)->Finalize();
|
||||
_table(_metamethodsmap)->Finalize();
|
||||
_registry = _null_;
|
||||
_consts = _null_;
|
||||
_metamethodsmap = _null_;
|
||||
while(!_systemstrings->empty()) {
|
||||
_systemstrings->back()=_null_;
|
||||
_systemstrings->pop_back();
|
||||
}
|
||||
_thread(_root_vm)->Finalize();
|
||||
_root_vm = _null_;
|
||||
_table_default_delegate = _null_;
|
||||
_array_default_delegate = _null_;
|
||||
_string_default_delegate = _null_;
|
||||
_number_default_delegate = _null_;
|
||||
_closure_default_delegate = _null_;
|
||||
_generator_default_delegate = _null_;
|
||||
_thread_default_delegate = _null_;
|
||||
_class_default_delegate = _null_;
|
||||
_instance_default_delegate = _null_;
|
||||
_weakref_default_delegate = _null_;
|
||||
_refs_table.Finalize();
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
SQCollectable *t = _gc_chain;
|
||||
SQCollectable *nx = NULL;
|
||||
if(t) {
|
||||
t->_uiRef++;
|
||||
while(t) {
|
||||
t->Finalize();
|
||||
nx = t->_next;
|
||||
if(nx) nx->_uiRef++;
|
||||
if(--t->_uiRef == 0)
|
||||
t->Release();
|
||||
t = nx;
|
||||
}
|
||||
}
|
||||
// assert(_gc_chain==NULL); //just to proove a theory
|
||||
while(_gc_chain){
|
||||
_gc_chain->_uiRef--;
|
||||
_gc_chain->Release();
|
||||
}
|
||||
#endif
|
||||
|
||||
sq_delete(_types,SQObjectPtrVec);
|
||||
sq_delete(_systemstrings,SQObjectPtrVec);
|
||||
sq_delete(_metamethods,SQObjectPtrVec);
|
||||
sq_delete(_stringtable,SQStringTable);
|
||||
if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);
|
||||
}
|
||||
|
||||
|
||||
SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)
|
||||
{
|
||||
if(type(name) != OT_STRING)
|
||||
return -1;
|
||||
SQObjectPtr ret;
|
||||
if(_table(_metamethodsmap)->Get(name,ret)) {
|
||||
return _integer(ret);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
|
||||
void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)
|
||||
{
|
||||
switch(type(o)){
|
||||
case OT_TABLE:_table(o)->Mark(chain);break;
|
||||
case OT_ARRAY:_array(o)->Mark(chain);break;
|
||||
case OT_USERDATA:_userdata(o)->Mark(chain);break;
|
||||
case OT_CLOSURE:_closure(o)->Mark(chain);break;
|
||||
case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break;
|
||||
case OT_GENERATOR:_generator(o)->Mark(chain);break;
|
||||
case OT_THREAD:_thread(o)->Mark(chain);break;
|
||||
case OT_CLASS:_class(o)->Mark(chain);break;
|
||||
case OT_INSTANCE:_instance(o)->Mark(chain);break;
|
||||
default: break; //shutup compiler
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SQInteger SQSharedState::CollectGarbage(SQVM *vm)
|
||||
{
|
||||
SQInteger n=0;
|
||||
SQCollectable *tchain=NULL;
|
||||
SQVM *vms = _thread(_root_vm);
|
||||
|
||||
vms->Mark(&tchain);
|
||||
SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();
|
||||
_refs_table.Mark(&tchain);
|
||||
MarkObject(_registry,&tchain);
|
||||
MarkObject(_consts,&tchain);
|
||||
MarkObject(_metamethodsmap,&tchain);
|
||||
MarkObject(_table_default_delegate,&tchain);
|
||||
MarkObject(_array_default_delegate,&tchain);
|
||||
MarkObject(_string_default_delegate,&tchain);
|
||||
MarkObject(_number_default_delegate,&tchain);
|
||||
MarkObject(_generator_default_delegate,&tchain);
|
||||
MarkObject(_thread_default_delegate,&tchain);
|
||||
MarkObject(_closure_default_delegate,&tchain);
|
||||
MarkObject(_class_default_delegate,&tchain);
|
||||
MarkObject(_instance_default_delegate,&tchain);
|
||||
MarkObject(_weakref_default_delegate,&tchain);
|
||||
|
||||
SQCollectable *t = _gc_chain;
|
||||
SQCollectable *nx = NULL;
|
||||
if(t) {
|
||||
t->_uiRef++;
|
||||
while(t) {
|
||||
t->Finalize();
|
||||
nx = t->_next;
|
||||
if(nx) nx->_uiRef++;
|
||||
if(--t->_uiRef == 0)
|
||||
t->Release();
|
||||
t = nx;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
t = tchain;
|
||||
while(t) {
|
||||
t->UnMark();
|
||||
t = t->_next;
|
||||
}
|
||||
_gc_chain = tchain;
|
||||
SQInteger z = _table(_thread(_root_vm)->_roottable)->CountUsed();
|
||||
assert(z == x);
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c)
|
||||
{
|
||||
c->_prev = NULL;
|
||||
c->_next = *chain;
|
||||
if(*chain) (*chain)->_prev = c;
|
||||
*chain = c;
|
||||
}
|
||||
|
||||
void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)
|
||||
{
|
||||
if(c->_prev) c->_prev->_next = c->_next;
|
||||
else *chain = c->_next;
|
||||
if(c->_next)
|
||||
c->_next->_prev = c->_prev;
|
||||
c->_next = NULL;
|
||||
c->_prev = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
SQChar* SQSharedState::GetScratchPad(SQInteger size)
|
||||
{
|
||||
SQInteger newsize;
|
||||
if(size>0) {
|
||||
if(_scratchpadsize < size) {
|
||||
newsize = size + (size>>1);
|
||||
_scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
|
||||
_scratchpadsize = newsize;
|
||||
|
||||
}else if(_scratchpadsize >= (size<<5)) {
|
||||
newsize = _scratchpadsize >> 1;
|
||||
_scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
|
||||
_scratchpadsize = newsize;
|
||||
}
|
||||
}
|
||||
return _scratchpad;
|
||||
}
|
||||
|
||||
RefTable::RefTable()
|
||||
{
|
||||
AllocNodes(4);
|
||||
}
|
||||
|
||||
void RefTable::Finalize()
|
||||
{
|
||||
RefNode *nodes = _nodes;
|
||||
for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
|
||||
nodes->obj = _null_;
|
||||
nodes++;
|
||||
}
|
||||
}
|
||||
|
||||
RefTable::~RefTable()
|
||||
{
|
||||
SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode)));
|
||||
}
|
||||
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void RefTable::Mark(SQCollectable **chain)
|
||||
{
|
||||
RefNode *nodes = (RefNode *)_nodes;
|
||||
for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
|
||||
if(type(nodes->obj) != OT_NULL) {
|
||||
SQSharedState::MarkObject(nodes->obj,chain);
|
||||
}
|
||||
nodes++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void RefTable::AddRef(SQObject &obj)
|
||||
{
|
||||
SQHash mainpos;
|
||||
RefNode *prev;
|
||||
RefNode *ref = Get(obj,mainpos,&prev,true);
|
||||
ref->refs++;
|
||||
}
|
||||
|
||||
SQBool RefTable::Release(SQObject &obj)
|
||||
{
|
||||
SQHash mainpos;
|
||||
RefNode *prev;
|
||||
RefNode *ref = Get(obj,mainpos,&prev,false);
|
||||
if(ref) {
|
||||
if(--ref->refs == 0) {
|
||||
SQObjectPtr o = ref->obj;
|
||||
if(prev) {
|
||||
prev->next = ref->next;
|
||||
}
|
||||
else {
|
||||
_buckets[mainpos] = ref->next;
|
||||
}
|
||||
ref->next = _freelist;
|
||||
_freelist = ref;
|
||||
_slotused--;
|
||||
ref->obj = _null_;
|
||||
//<<FIXME>>test for shrink?
|
||||
return SQTrue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
}
|
||||
return SQFalse;
|
||||
}
|
||||
|
||||
void RefTable::Resize(SQUnsignedInteger size)
|
||||
{
|
||||
RefNode **oldbucks = _buckets;
|
||||
RefNode *t = _nodes;
|
||||
SQUnsignedInteger oldnumofslots = _numofslots;
|
||||
AllocNodes(size);
|
||||
//rehash
|
||||
SQUnsignedInteger nfound = 0;
|
||||
for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {
|
||||
if(type(t->obj) != OT_NULL) {
|
||||
//add back;
|
||||
assert(t->refs != 0);
|
||||
RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj);
|
||||
nn->refs = t->refs;
|
||||
t->obj = _null_;
|
||||
nfound++;
|
||||
}
|
||||
t++;
|
||||
}
|
||||
assert(nfound == oldnumofslots);
|
||||
SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode)));
|
||||
}
|
||||
|
||||
RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj)
|
||||
{
|
||||
RefNode *t = _buckets[mainpos];
|
||||
RefNode *newnode = _freelist;
|
||||
newnode->obj = obj;
|
||||
_buckets[mainpos] = newnode;
|
||||
_freelist = _freelist->next;
|
||||
newnode->next = t;
|
||||
assert(newnode->refs == 0);
|
||||
_slotused++;
|
||||
return newnode;
|
||||
}
|
||||
|
||||
RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add)
|
||||
{
|
||||
RefNode *ref;
|
||||
mainpos = ::HashObj(obj)&(_numofslots-1);
|
||||
*prev = NULL;
|
||||
for (ref = _buckets[mainpos]; ref; ) {
|
||||
if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj))
|
||||
break;
|
||||
*prev = ref;
|
||||
ref = ref->next;
|
||||
}
|
||||
if(ref == NULL && add) {
|
||||
if(_numofslots == _slotused) {
|
||||
assert(_freelist == 0);
|
||||
Resize(_numofslots*2);
|
||||
mainpos = ::HashObj(obj)&(_numofslots-1);
|
||||
}
|
||||
ref = Add(mainpos,obj);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
void RefTable::AllocNodes(SQUnsignedInteger size)
|
||||
{
|
||||
RefNode **bucks;
|
||||
RefNode *nodes;
|
||||
bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode)));
|
||||
nodes = (RefNode *)&bucks[size];
|
||||
RefNode *temp = nodes;
|
||||
SQUnsignedInteger n;
|
||||
for(n = 0; n < size - 1; n++) {
|
||||
bucks[n] = NULL;
|
||||
temp->refs = 0;
|
||||
new (&temp->obj) SQObjectPtr;
|
||||
temp->next = temp+1;
|
||||
temp++;
|
||||
}
|
||||
bucks[n] = NULL;
|
||||
temp->refs = 0;
|
||||
new (&temp->obj) SQObjectPtr;
|
||||
temp->next = NULL;
|
||||
_freelist = nodes;
|
||||
_nodes = nodes;
|
||||
_buckets = bucks;
|
||||
_slotused = 0;
|
||||
_numofslots = size;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//SQStringTable
|
||||
/*
|
||||
* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
|
||||
* http://www.lua.org/copyright.html#4
|
||||
* http://www.lua.org/source/4.0.1/src_lstring.c.html
|
||||
*/
|
||||
|
||||
SQStringTable::SQStringTable()
|
||||
{
|
||||
AllocNodes(4);
|
||||
_slotused = 0;
|
||||
}
|
||||
|
||||
SQStringTable::~SQStringTable()
|
||||
{
|
||||
SQ_FREE(_strings,sizeof(SQString*)*_numofslots);
|
||||
_strings = NULL;
|
||||
}
|
||||
|
||||
void SQStringTable::AllocNodes(SQInteger size)
|
||||
{
|
||||
_numofslots = size;
|
||||
_strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);
|
||||
memset(_strings,0,sizeof(SQString*)*(size_t)_numofslots);
|
||||
}
|
||||
|
||||
SQString *SQStringTable::Add(const SQChar *news,SQInteger len)
|
||||
{
|
||||
if(len<0)
|
||||
len = (SQInteger)strlen(news);
|
||||
SQHash h = ::_hashstr(news,(size_t)len)&(_numofslots-1);
|
||||
SQString *s;
|
||||
for (s = _strings[h]; s; s = s->_next){
|
||||
if(s->_len == len && (!memcmp(news,s->_val,(size_t)len)))
|
||||
return s; //found
|
||||
}
|
||||
|
||||
SQString *t=(SQString *)SQ_MALLOC(len+sizeof(SQString));
|
||||
new (t) SQString(news, len);
|
||||
t->_next = _strings[h];
|
||||
_strings[h] = t;
|
||||
_slotused++;
|
||||
if (_slotused > _numofslots) /* too crowded? */
|
||||
Resize(_numofslots*2);
|
||||
return t;
|
||||
}
|
||||
|
||||
SQString::SQString(const SQChar *news, SQInteger len)
|
||||
{
|
||||
memcpy(_val,news,(size_t)len);
|
||||
_val[len] = '\0';
|
||||
_len = len;
|
||||
_hash = ::_hashstr(news,(size_t)len);
|
||||
_next = NULL;
|
||||
_sharedstate = NULL;
|
||||
}
|
||||
|
||||
void SQStringTable::Resize(SQInteger size)
|
||||
{
|
||||
SQInteger oldsize=_numofslots;
|
||||
SQString **oldtable=_strings;
|
||||
AllocNodes(size);
|
||||
for (SQInteger i=0; i<oldsize; i++){
|
||||
SQString *p = oldtable[i];
|
||||
while(p){
|
||||
SQString *next = p->_next;
|
||||
SQHash h = p->_hash&(_numofslots-1);
|
||||
p->_next = _strings[h];
|
||||
_strings[h] = p;
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
SQ_FREE(oldtable,oldsize*sizeof(SQString*));
|
||||
}
|
||||
|
||||
void SQStringTable::Remove(SQString *bs)
|
||||
{
|
||||
SQString *s;
|
||||
SQString *prev=NULL;
|
||||
SQHash h = bs->_hash&(_numofslots - 1);
|
||||
|
||||
for (s = _strings[h]; s; ){
|
||||
if(s == bs){
|
||||
if(prev)
|
||||
prev->_next = s->_next;
|
||||
else
|
||||
_strings[h] = s->_next;
|
||||
_slotused--;
|
||||
SQInteger slen = s->_len;
|
||||
s->~SQString();
|
||||
SQ_FREE(s,sizeof(SQString) + slen);
|
||||
return;
|
||||
}
|
||||
prev = s;
|
||||
s = s->_next;
|
||||
}
|
||||
assert(0);//if this fail something is wrong
|
||||
}
|
||||
133
src/3rdparty/squirrel/squirrel/sqstate.h
vendored
Normal file
133
src/3rdparty/squirrel/squirrel/sqstate.h
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQSTATE_H_
|
||||
#define _SQSTATE_H_
|
||||
|
||||
#include "squtils.h"
|
||||
#include "sqobject.h"
|
||||
struct SQString;
|
||||
struct SQTable;
|
||||
//max number of character for a printed number
|
||||
#define NUMBER_MAX_CHAR 50
|
||||
|
||||
struct SQStringTable
|
||||
{
|
||||
SQStringTable();
|
||||
~SQStringTable();
|
||||
SQString *Add(const SQChar *,SQInteger len);
|
||||
void Remove(SQString *);
|
||||
private:
|
||||
void Resize(SQInteger size);
|
||||
void AllocNodes(SQInteger size);
|
||||
SQString **_strings;
|
||||
SQUnsignedInteger _numofslots;
|
||||
SQUnsignedInteger _slotused;
|
||||
};
|
||||
|
||||
struct RefTable {
|
||||
struct RefNode {
|
||||
SQObjectPtr obj;
|
||||
SQUnsignedInteger refs;
|
||||
struct RefNode *next;
|
||||
};
|
||||
RefTable();
|
||||
~RefTable();
|
||||
void AddRef(SQObject &obj);
|
||||
SQBool Release(SQObject &obj);
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable **chain);
|
||||
#endif
|
||||
void Finalize();
|
||||
private:
|
||||
RefNode *Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add);
|
||||
RefNode *Add(SQHash mainpos,SQObject &obj);
|
||||
void Resize(SQUnsignedInteger size);
|
||||
void AllocNodes(SQUnsignedInteger size);
|
||||
SQUnsignedInteger _numofslots;
|
||||
SQUnsignedInteger _slotused;
|
||||
RefNode *_nodes;
|
||||
RefNode *_freelist;
|
||||
RefNode **_buckets;
|
||||
};
|
||||
|
||||
#define ADD_STRING(ss,str,len) ss->_stringtable->Add(str,len)
|
||||
#define REMOVE_STRING(ss,bstr) ss->_stringtable->Remove(bstr)
|
||||
|
||||
struct SQObjectPtr;
|
||||
|
||||
struct SQSharedState
|
||||
{
|
||||
SQSharedState();
|
||||
~SQSharedState();
|
||||
public:
|
||||
SQChar* GetScratchPad(SQInteger size);
|
||||
SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name);
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
SQInteger CollectGarbage(SQVM *vm);
|
||||
static void MarkObject(SQObjectPtr &o,SQCollectable **chain);
|
||||
#endif
|
||||
SQObjectPtrVec *_metamethods;
|
||||
SQObjectPtr _metamethodsmap;
|
||||
SQObjectPtrVec *_systemstrings;
|
||||
SQObjectPtrVec *_types;
|
||||
SQStringTable *_stringtable;
|
||||
RefTable _refs_table;
|
||||
SQObjectPtr _registry;
|
||||
SQObjectPtr _consts;
|
||||
SQObjectPtr _constructoridx;
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
SQCollectable *_gc_chain;
|
||||
#endif
|
||||
SQObjectPtr _root_vm;
|
||||
SQObjectPtr _table_default_delegate;
|
||||
static SQRegFunction _table_default_delegate_funcz[];
|
||||
SQObjectPtr _array_default_delegate;
|
||||
static SQRegFunction _array_default_delegate_funcz[];
|
||||
SQObjectPtr _string_default_delegate;
|
||||
static SQRegFunction _string_default_delegate_funcz[];
|
||||
SQObjectPtr _number_default_delegate;
|
||||
static SQRegFunction _number_default_delegate_funcz[];
|
||||
SQObjectPtr _generator_default_delegate;
|
||||
static SQRegFunction _generator_default_delegate_funcz[];
|
||||
SQObjectPtr _closure_default_delegate;
|
||||
static SQRegFunction _closure_default_delegate_funcz[];
|
||||
SQObjectPtr _thread_default_delegate;
|
||||
static SQRegFunction _thread_default_delegate_funcz[];
|
||||
SQObjectPtr _class_default_delegate;
|
||||
static SQRegFunction _class_default_delegate_funcz[];
|
||||
SQObjectPtr _instance_default_delegate;
|
||||
static SQRegFunction _instance_default_delegate_funcz[];
|
||||
SQObjectPtr _weakref_default_delegate;
|
||||
static SQRegFunction _weakref_default_delegate_funcz[];
|
||||
|
||||
SQCOMPILERERROR _compilererrorhandler;
|
||||
SQPRINTFUNCTION _printfunc;
|
||||
bool _debuginfo;
|
||||
bool _notifyallexceptions;
|
||||
private:
|
||||
SQChar *_scratchpad;
|
||||
SQInteger _scratchpadsize;
|
||||
};
|
||||
|
||||
#define _sp(s) (_sharedstate->GetScratchPad(s))
|
||||
#define _spval (_sharedstate->GetScratchPad(-1))
|
||||
|
||||
#define _table_ddel _table(_sharedstate->_table_default_delegate)
|
||||
#define _array_ddel _table(_sharedstate->_array_default_delegate)
|
||||
#define _string_ddel _table(_sharedstate->_string_default_delegate)
|
||||
#define _number_ddel _table(_sharedstate->_number_default_delegate)
|
||||
#define _generator_ddel _table(_sharedstate->_generator_default_delegate)
|
||||
#define _closure_ddel _table(_sharedstate->_closure_default_delegate)
|
||||
#define _thread_ddel _table(_sharedstate->_thread_default_delegate)
|
||||
#define _class_ddel _table(_sharedstate->_class_default_delegate)
|
||||
#define _instance_ddel _table(_sharedstate->_instance_default_delegate)
|
||||
#define _weakref_ddel _table(_sharedstate->_weakref_default_delegate)
|
||||
|
||||
extern SQObjectPtr _null_;
|
||||
extern SQObjectPtr _true_;
|
||||
extern SQObjectPtr _false_;
|
||||
extern SQObjectPtr _one_;
|
||||
extern SQObjectPtr _minusone_;
|
||||
|
||||
bool CompileTypemask(SQIntVec &res,const SQChar *typemask);
|
||||
|
||||
#endif //_SQSTATE_H_
|
||||
31
src/3rdparty/squirrel/squirrel/sqstring.h
vendored
Normal file
31
src/3rdparty/squirrel/squirrel/sqstring.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQSTRING_H_
|
||||
#define _SQSTRING_H_
|
||||
|
||||
inline SQHash _hashstr (const SQChar *s, size_t l)
|
||||
{
|
||||
SQHash h = (SQHash)l; /* seed */
|
||||
size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */
|
||||
for (; l>=step; l-=step)
|
||||
h = h ^ ((h<<5)+(h>>2)+(unsigned short)*(s++));
|
||||
return h;
|
||||
}
|
||||
|
||||
struct SQString : public SQRefCounted
|
||||
{
|
||||
SQString(const SQChar *news, SQInteger len);
|
||||
~SQString(){}
|
||||
public:
|
||||
static SQString *Create(SQSharedState *ss, const SQChar *, SQInteger len = -1 );
|
||||
SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
|
||||
void Release();
|
||||
SQSharedState *_sharedstate;
|
||||
SQString *_next; //chain for the string table
|
||||
SQInteger _len;
|
||||
SQHash _hash;
|
||||
SQChar _val[1];
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //_SQSTRING_H_
|
||||
201
src/3rdparty/squirrel/squirrel/sqtable.cpp
vendored
Normal file
201
src/3rdparty/squirrel/squirrel/sqtable.cpp
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* see copyright notice in squirrel.h
|
||||
*/
|
||||
|
||||
#include "../../../stdafx.h"
|
||||
|
||||
#include "sqpcheader.h"
|
||||
#include "sqvm.h"
|
||||
#include "sqtable.h"
|
||||
#include "sqfuncproto.h"
|
||||
#include "sqclosure.h"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
SQTable::SQTable(SQSharedState *ss,SQInteger nInitialSize)
|
||||
{
|
||||
SQInteger pow2size=MINPOWER2;
|
||||
while(nInitialSize>pow2size)pow2size=pow2size<<1;
|
||||
AllocNodes(pow2size);
|
||||
_usednodes = 0;
|
||||
_delegate = NULL;
|
||||
INIT_CHAIN();
|
||||
ADD_TO_CHAIN(&_sharedstate->_gc_chain,this);
|
||||
}
|
||||
|
||||
void SQTable::Remove(const SQObjectPtr &key)
|
||||
{
|
||||
|
||||
_HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
|
||||
if (n) {
|
||||
n->val = n->key = _null_;
|
||||
_usednodes--;
|
||||
Rehash(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SQTable::AllocNodes(SQInteger nSize)
|
||||
{
|
||||
_HashNode *nodes=(_HashNode *)SQ_MALLOC(sizeof(_HashNode)*nSize);
|
||||
for(SQInteger i=0;i<nSize;i++){
|
||||
new (&nodes[i]) _HashNode;
|
||||
nodes[i].next=NULL;
|
||||
}
|
||||
_numofnodes=nSize;
|
||||
_nodes=nodes;
|
||||
_firstfree=&_nodes[_numofnodes-1];
|
||||
}
|
||||
|
||||
void SQTable::Rehash(bool force)
|
||||
{
|
||||
SQInteger oldsize=_numofnodes;
|
||||
//prevent problems with the integer division
|
||||
if(oldsize<4)oldsize=4;
|
||||
_HashNode *nold=_nodes;
|
||||
SQInteger nelems=CountUsed();
|
||||
if (nelems >= oldsize-oldsize/4) /* using more than 3/4? */
|
||||
AllocNodes(oldsize*2);
|
||||
else if (nelems <= oldsize/4 && /* less than 1/4? */
|
||||
oldsize > MINPOWER2)
|
||||
AllocNodes(oldsize/2);
|
||||
else if(force)
|
||||
AllocNodes(oldsize);
|
||||
else
|
||||
return;
|
||||
_usednodes = 0;
|
||||
for (SQInteger i=0; i<oldsize; i++) {
|
||||
_HashNode *old = nold+i;
|
||||
if (type(old->key) != OT_NULL)
|
||||
NewSlot(old->key,old->val);
|
||||
}
|
||||
for(SQInteger k=0;k<oldsize;k++)
|
||||
nold[k].~_HashNode();
|
||||
SQ_FREE(nold,oldsize*sizeof(_HashNode));
|
||||
}
|
||||
|
||||
SQTable *SQTable::Clone()
|
||||
{
|
||||
SQTable *nt=Create(_opt_ss(this),_numofnodes);
|
||||
SQInteger ridx=0;
|
||||
SQObjectPtr key,val;
|
||||
while((ridx=Next(true,ridx,key,val))!=-1){
|
||||
nt->NewSlot(key,val);
|
||||
}
|
||||
nt->SetDelegate(_delegate);
|
||||
return nt;
|
||||
}
|
||||
|
||||
bool SQTable::Get(const SQObjectPtr &key,SQObjectPtr &val)
|
||||
{
|
||||
if(type(key) == OT_NULL)
|
||||
return false;
|
||||
_HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
|
||||
if (n) {
|
||||
val = _realval(n->val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool SQTable::NewSlot(const SQObjectPtr &key,const SQObjectPtr &val)
|
||||
{
|
||||
assert(type(key) != OT_NULL);
|
||||
SQHash h = HashObj(key) & (_numofnodes - 1);
|
||||
_HashNode *n = _Get(key, h);
|
||||
if (n) {
|
||||
n->val = val;
|
||||
return false;
|
||||
}
|
||||
_HashNode *mp = &_nodes[h];
|
||||
n = mp;
|
||||
|
||||
|
||||
//key not found I'll insert it
|
||||
//main pos is not free
|
||||
|
||||
if(type(mp->key) != OT_NULL) {
|
||||
n = _firstfree; /* get a free place */
|
||||
SQHash mph = HashObj(mp->key) & (_numofnodes - 1);
|
||||
_HashNode *othern; /* main position of colliding node */
|
||||
|
||||
if (mp > n && (othern = &_nodes[mph]) != mp){
|
||||
/* yes; move colliding node into free position */
|
||||
while (othern->next != mp){
|
||||
assert(othern->next != NULL);
|
||||
othern = othern->next; /* find previous */
|
||||
}
|
||||
othern->next = n; /* redo the chain with `n' in place of `mp' */
|
||||
n->key = mp->key;
|
||||
n->val = mp->val;/* copy colliding node into free pos. (mp->next also goes) */
|
||||
n->next = mp->next;
|
||||
mp->key = _null_;
|
||||
mp->val = _null_;
|
||||
mp->next = NULL; /* now `mp' is free */
|
||||
}
|
||||
else{
|
||||
/* new node will go into free position */
|
||||
n->next = mp->next; /* chain new position */
|
||||
mp->next = n;
|
||||
mp = n;
|
||||
}
|
||||
}
|
||||
mp->key = key;
|
||||
|
||||
for (;;) { /* correct `firstfree' */
|
||||
if (type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) {
|
||||
mp->val = val;
|
||||
_usednodes++;
|
||||
return true; /* OK; table still has a free place */
|
||||
}
|
||||
else if (_firstfree == _nodes) break; /* cannot decrement from here */
|
||||
else (_firstfree)--;
|
||||
}
|
||||
Rehash(true);
|
||||
return NewSlot(key, val);
|
||||
}
|
||||
|
||||
SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
|
||||
{
|
||||
SQInteger idx = (SQInteger)TranslateIndex(refpos);
|
||||
while (idx < _numofnodes) {
|
||||
if(type(_nodes[idx].key) != OT_NULL) {
|
||||
//first found
|
||||
_HashNode &n = _nodes[idx];
|
||||
outkey = n.key;
|
||||
outval = getweakrefs?(SQObject)n.val:_realval(n.val);
|
||||
//return idx for the next iteration
|
||||
return ++idx;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
//nothing to iterate anymore
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool SQTable::Set(const SQObjectPtr &key, const SQObjectPtr &val)
|
||||
{
|
||||
_HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
|
||||
if (n) {
|
||||
n->val = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SQTable::_ClearNodes()
|
||||
{
|
||||
for(SQInteger i = 0;i < _numofnodes; i++) { _nodes[i].key = _null_; _nodes[i].val = _null_; }
|
||||
}
|
||||
|
||||
void SQTable::Finalize()
|
||||
{
|
||||
_ClearNodes();
|
||||
SetDelegate(NULL);
|
||||
}
|
||||
|
||||
void SQTable::Clear()
|
||||
{
|
||||
_ClearNodes();
|
||||
_usednodes = 0;
|
||||
Rehash(true);
|
||||
}
|
||||
91
src/3rdparty/squirrel/squirrel/sqtable.h
vendored
Normal file
91
src/3rdparty/squirrel/squirrel/sqtable.h
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQTABLE_H_
|
||||
#define _SQTABLE_H_
|
||||
/*
|
||||
* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
|
||||
* http://www.lua.org/copyright.html#4
|
||||
* http://www.lua.org/source/4.0.1/src_ltable.c.html
|
||||
*/
|
||||
|
||||
#include "sqstring.h"
|
||||
|
||||
|
||||
#define hashptr(p) ((SQHash)(((SQInteger)p) >> 3))
|
||||
|
||||
inline SQHash HashObj(const SQObjectPtr &key)
|
||||
{
|
||||
switch(type(key)) {
|
||||
case OT_STRING: return _string(key)->_hash;
|
||||
case OT_FLOAT: return (SQHash)((SQInteger)_float(key));
|
||||
case OT_BOOL: case OT_INTEGER: return (SQHash)((SQInteger)_integer(key));
|
||||
default: return hashptr(key._unVal.pRefCounted);
|
||||
}
|
||||
}
|
||||
|
||||
struct SQTable : public SQDelegable
|
||||
{
|
||||
private:
|
||||
struct _HashNode
|
||||
{
|
||||
_HashNode() { next = NULL; }
|
||||
SQObjectPtr val;
|
||||
SQObjectPtr key;
|
||||
_HashNode *next;
|
||||
};
|
||||
_HashNode *_firstfree;
|
||||
_HashNode *_nodes;
|
||||
SQInteger _numofnodes;
|
||||
SQInteger _usednodes;
|
||||
|
||||
///////////////////////////
|
||||
void AllocNodes(SQInteger nSize);
|
||||
void Rehash(bool force);
|
||||
SQTable(SQSharedState *ss, SQInteger nInitialSize);
|
||||
void _ClearNodes();
|
||||
public:
|
||||
static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize)
|
||||
{
|
||||
SQTable *newtable = (SQTable*)SQ_MALLOC(sizeof(SQTable));
|
||||
new (newtable) SQTable(ss, nInitialSize);
|
||||
newtable->_delegate = NULL;
|
||||
return newtable;
|
||||
}
|
||||
void Finalize();
|
||||
SQTable *Clone();
|
||||
~SQTable()
|
||||
{
|
||||
SetDelegate(NULL);
|
||||
REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
|
||||
for (SQInteger i = 0; i < _numofnodes; i++) _nodes[i].~_HashNode();
|
||||
SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode));
|
||||
}
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable **chain);
|
||||
#endif
|
||||
inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash)
|
||||
{
|
||||
_HashNode *n = &_nodes[hash];
|
||||
do{
|
||||
if(_rawval(n->key) == _rawval(key) && type(n->key) == type(key)){
|
||||
return n;
|
||||
}
|
||||
}while((n = n->next));
|
||||
return NULL;
|
||||
}
|
||||
bool Get(const SQObjectPtr &key,SQObjectPtr &val);
|
||||
void Remove(const SQObjectPtr &key);
|
||||
bool Set(const SQObjectPtr &key, const SQObjectPtr &val);
|
||||
//returns true if a new slot has been created false if it was already present
|
||||
bool NewSlot(const SQObjectPtr &key,const SQObjectPtr &val);
|
||||
SQInteger Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
|
||||
|
||||
SQInteger CountUsed(){ return _usednodes;}
|
||||
void Clear();
|
||||
void Release()
|
||||
{
|
||||
sq_delete(this, SQTable);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //_SQTABLE_H_
|
||||
37
src/3rdparty/squirrel/squirrel/squserdata.h
vendored
Normal file
37
src/3rdparty/squirrel/squirrel/squserdata.h
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQUSERDATA_H_
|
||||
#define _SQUSERDATA_H_
|
||||
|
||||
struct SQUserData : SQDelegable
|
||||
{
|
||||
SQUserData(SQSharedState *ss, SQInteger size){ _delegate = 0; _hook = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain, this); _size = size; _typetag = 0;
|
||||
}
|
||||
~SQUserData()
|
||||
{
|
||||
REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this);
|
||||
SetDelegate(NULL);
|
||||
}
|
||||
static SQUserData* Create(SQSharedState *ss, SQInteger size)
|
||||
{
|
||||
SQUserData* ud = (SQUserData*)SQ_MALLOC(sizeof(SQUserData)+(size-1));
|
||||
new (ud) SQUserData(ss, size);
|
||||
return ud;
|
||||
}
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable **chain);
|
||||
void Finalize(){SetDelegate(NULL);}
|
||||
#endif
|
||||
void Release() {
|
||||
if (_hook) _hook(_val,_size);
|
||||
SQInteger tsize = _size - 1;
|
||||
this->~SQUserData();
|
||||
SQ_FREE(this, sizeof(SQUserData) + tsize);
|
||||
}
|
||||
|
||||
SQInteger _size;
|
||||
SQRELEASEHOOK _hook;
|
||||
SQUserPointer _typetag;
|
||||
SQChar _val[1];
|
||||
};
|
||||
|
||||
#endif //_SQUSERDATA_H_
|
||||
112
src/3rdparty/squirrel/squirrel/squtils.h
vendored
Normal file
112
src/3rdparty/squirrel/squirrel/squtils.h
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQUTILS_H_
|
||||
#define _SQUTILS_H_
|
||||
|
||||
void *sq_vm_malloc(SQUnsignedInteger size);
|
||||
void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size);
|
||||
void sq_vm_free(void *p,SQUnsignedInteger size);
|
||||
|
||||
#define sq_new(__ptr,__type) {__ptr=(__type *)sq_vm_malloc(sizeof(__type));new (__ptr) __type;}
|
||||
#define sq_delete(__ptr,__type) {__ptr->~__type();sq_vm_free(__ptr,sizeof(__type));}
|
||||
#define SQ_MALLOC(__size) sq_vm_malloc((__size));
|
||||
#define SQ_FREE(__ptr,__size) sq_vm_free((__ptr),(__size));
|
||||
#define SQ_REALLOC(__ptr,__oldsize,__size) sq_vm_realloc((__ptr),(__oldsize),(__size));
|
||||
|
||||
//sqvector mini vector class, supports objects by value
|
||||
template<typename T> class sqvector
|
||||
{
|
||||
public:
|
||||
sqvector()
|
||||
{
|
||||
_vals = NULL;
|
||||
_size = 0;
|
||||
_allocated = 0;
|
||||
}
|
||||
sqvector(const sqvector<T>& v)
|
||||
{
|
||||
copy(v);
|
||||
}
|
||||
void copy(const sqvector<T>& v)
|
||||
{
|
||||
resize(v._size);
|
||||
for(SQUnsignedInteger i = 0; i < v._size; i++) {
|
||||
new ((void *)&_vals[i]) T(v._vals[i]);
|
||||
}
|
||||
_size = v._size;
|
||||
}
|
||||
~sqvector()
|
||||
{
|
||||
if(_allocated) {
|
||||
/* Break freeing loops, if this vector (indirectly) links to itself. */
|
||||
size_t allocated_size = _allocated * sizeof(T);
|
||||
_allocated = 0;
|
||||
|
||||
for(size_t i = 0; i < _size; i++)
|
||||
_vals[i].~T();
|
||||
SQ_FREE(_vals, allocated_size);
|
||||
}
|
||||
}
|
||||
void reserve(SQUnsignedInteger newsize) { _realloc(newsize); }
|
||||
void resize(SQUnsignedInteger newsize, const T& fill = T())
|
||||
{
|
||||
if(newsize > _allocated)
|
||||
_realloc(newsize);
|
||||
if(newsize > _size) {
|
||||
while(_size < newsize) {
|
||||
new ((void *)&_vals[_size]) T(fill);
|
||||
_size++;
|
||||
}
|
||||
}
|
||||
else{
|
||||
for(SQUnsignedInteger i = newsize; i < _size; i++) {
|
||||
_vals[i].~T();
|
||||
}
|
||||
_size = (size_t)newsize;
|
||||
}
|
||||
}
|
||||
void shrinktofit() { if(_size > 4) { _realloc(_size); } }
|
||||
T& top() const { return _vals[_size - 1]; }
|
||||
inline SQUnsignedInteger size() const { return _size; }
|
||||
bool empty() const { return (_size <= 0); }
|
||||
inline T &push_back(const T& val = T())
|
||||
{
|
||||
if(_allocated <= _size)
|
||||
_realloc(_size * 2);
|
||||
return *(new ((void *)&_vals[_size++]) T(val));
|
||||
}
|
||||
inline void pop_back()
|
||||
{
|
||||
_size--; _vals[_size].~T();
|
||||
}
|
||||
void insert(SQUnsignedInteger idx, const T& val)
|
||||
{
|
||||
resize(_size + 1);
|
||||
for(SQUnsignedInteger i = _size - 1; i > idx; i--) {
|
||||
_vals[i] = _vals[i - 1];
|
||||
}
|
||||
_vals[idx] = val;
|
||||
}
|
||||
void remove(SQUnsignedInteger idx)
|
||||
{
|
||||
_vals[idx].~T();
|
||||
if(idx < (_size - 1)) {
|
||||
memmove(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - (size_t)idx - 1));
|
||||
}
|
||||
_size--;
|
||||
}
|
||||
SQUnsignedInteger capacity() { return _allocated; }
|
||||
inline T &back() const { return _vals[_size - 1]; }
|
||||
inline T& operator[](SQUnsignedInteger pos) const{ assert(pos < _allocated); return _vals[pos]; }
|
||||
T* _vals;
|
||||
private:
|
||||
void _realloc(SQUnsignedInteger newsize)
|
||||
{
|
||||
newsize = (newsize > 0)?newsize:4;
|
||||
_vals = (T*)SQ_REALLOC(_vals, _allocated * sizeof(T), newsize * sizeof(T));
|
||||
_allocated = (size_t)newsize;
|
||||
}
|
||||
size_t _size;
|
||||
size_t _allocated;
|
||||
};
|
||||
|
||||
#endif //_SQUTILS_H_
|
||||
1608
src/3rdparty/squirrel/squirrel/sqvm.cpp
vendored
Normal file
1608
src/3rdparty/squirrel/squirrel/sqvm.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
225
src/3rdparty/squirrel/squirrel/sqvm.h
vendored
Normal file
225
src/3rdparty/squirrel/squirrel/sqvm.h
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
/* see copyright notice in squirrel.h */
|
||||
#ifndef _SQVM_H_
|
||||
#define _SQVM_H_
|
||||
|
||||
#include "sqopcodes.h"
|
||||
#include "sqobject.h"
|
||||
#define MAX_NATIVE_CALLS 100
|
||||
#define MIN_STACK_OVERHEAD 10
|
||||
|
||||
#define SQ_SUSPEND_FLAG -666
|
||||
//base lib
|
||||
void sq_base_register(HSQUIRRELVM v);
|
||||
|
||||
struct SQExceptionTrap{
|
||||
SQExceptionTrap() {}
|
||||
SQExceptionTrap(SQInteger ss, SQInteger stackbase,SQInstruction *ip, SQInteger ex_target){ _stacksize = ss; _stackbase = stackbase; _ip = ip; _extarget = ex_target;}
|
||||
SQExceptionTrap(const SQExceptionTrap &et) { (*this) = et; }
|
||||
SQInteger _stackbase;
|
||||
SQInteger _stacksize;
|
||||
SQInstruction *_ip;
|
||||
SQInteger _extarget;
|
||||
};
|
||||
|
||||
#define _INLINE
|
||||
|
||||
#define STK(a) _stack._vals[_stackbase+(a)]
|
||||
#define TARGET _stack._vals[_stackbase+arg0]
|
||||
|
||||
typedef sqvector<SQExceptionTrap> ExceptionsTraps;
|
||||
|
||||
struct SQVM : public CHAINABLE_OBJ
|
||||
{
|
||||
struct VarArgs {
|
||||
VarArgs() { size = 0; base = 0; }
|
||||
unsigned short size;
|
||||
unsigned short base;
|
||||
};
|
||||
|
||||
struct CallInfo{
|
||||
//CallInfo() { _generator._type = OT_NULL;}
|
||||
SQInstruction *_ip;
|
||||
SQObjectPtr *_literals;
|
||||
SQObjectPtr _closure;
|
||||
SQGenerator *_generator;
|
||||
SQInt32 _etraps;
|
||||
SQInt32 _prevstkbase;
|
||||
SQInt32 _prevtop;
|
||||
SQInt32 _target;
|
||||
SQInt32 _ncalls;
|
||||
SQBool _root;
|
||||
VarArgs _vargs;
|
||||
};
|
||||
|
||||
typedef sqvector<CallInfo> CallInfoVec;
|
||||
public:
|
||||
enum ExecutionType { ET_CALL, ET_RESUME_GENERATOR, ET_RESUME_VM, ET_RESUME_THROW_VM, ET_RESUME_OPENTTD };
|
||||
SQVM(SQSharedState *ss);
|
||||
~SQVM();
|
||||
bool Init(SQVM *friendvm, SQInteger stacksize);
|
||||
bool Execute(SQObjectPtr &func, SQInteger target, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL);
|
||||
//starts a native call return when the NATIVE closure returns
|
||||
bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger stackbase, SQObjectPtr &retval,bool &suspend);
|
||||
//starts a SQUIRREL call in the same "Execution loop"
|
||||
bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall);
|
||||
bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor);
|
||||
//call a generic closure pure SQUIRREL or NATIVE
|
||||
bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror,SQBool can_suspend);
|
||||
SQRESULT Suspend();
|
||||
|
||||
void CallDebugHook(SQInteger type,SQInteger forcedline=0);
|
||||
void CallErrorHandler(SQObjectPtr &e);
|
||||
bool Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, bool raw, bool fetchroot);
|
||||
bool FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw);
|
||||
bool Set(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val, bool fetchroot);
|
||||
bool NewSlot(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val,bool bstatic);
|
||||
bool DeleteSlot(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &res);
|
||||
bool Clone(const SQObjectPtr &self, SQObjectPtr &target);
|
||||
bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res);
|
||||
bool StringCat(const SQObjectPtr &str, const SQObjectPtr &obj, SQObjectPtr &dest);
|
||||
bool IsEqual(SQObjectPtr &o1,SQObjectPtr &o2,bool &res);
|
||||
void ToString(const SQObjectPtr &o,SQObjectPtr &res);
|
||||
SQString *PrintObjVal(const SQObject &o);
|
||||
|
||||
|
||||
void Raise_Error(const SQChar *s, ...);
|
||||
void Raise_Error(SQObjectPtr &desc);
|
||||
void Raise_IdxError(const SQObject &o);
|
||||
void Raise_CompareError(const SQObject &o1, const SQObject &o2);
|
||||
void Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type);
|
||||
|
||||
void TypeOf(const SQObjectPtr &obj1, SQObjectPtr &dest);
|
||||
bool CallMetaMethod(SQDelegable *del, SQMetaMethod mm, SQInteger nparams, SQObjectPtr &outres);
|
||||
bool ArithMetaMethod(SQInteger op, const SQObjectPtr &o1, const SQObjectPtr &o2, SQObjectPtr &dest);
|
||||
bool Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval);
|
||||
//new stuff
|
||||
_INLINE bool ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2);
|
||||
_INLINE bool BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2);
|
||||
_INLINE bool NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o1);
|
||||
_INLINE bool CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res);
|
||||
bool CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func);
|
||||
bool GETVARGV_OP(SQObjectPtr &target,SQObjectPtr &idx,CallInfo *ci);
|
||||
bool CLASS_OP(SQObjectPtr &target,SQInteger base,SQInteger attrs);
|
||||
bool GETPARENT_OP(SQObjectPtr &o,SQObjectPtr &target);
|
||||
//return true if the loop is finished
|
||||
bool FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,SQInteger arg_2,int exitpos,int &jump);
|
||||
bool DELEGATE_OP(SQObjectPtr &trg,SQObjectPtr &o1,SQObjectPtr &o2);
|
||||
_INLINE bool LOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr);
|
||||
_INLINE bool PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr);
|
||||
_INLINE bool DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix);
|
||||
void PopVarArgs(VarArgs &vargs);
|
||||
void ClearStack(SQInteger last_top);
|
||||
#ifdef _DEBUG_DUMP
|
||||
void dumpstack(SQInteger stackbase=-1, bool dumpall = false);
|
||||
#endif
|
||||
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
void Mark(SQCollectable **chain);
|
||||
#endif
|
||||
void Finalize();
|
||||
void GrowCallStack() {
|
||||
SQInteger newsize = _alloccallsstacksize*2;
|
||||
_callstackdata.resize(newsize);
|
||||
_callsstack = &_callstackdata[0];
|
||||
_alloccallsstacksize = newsize;
|
||||
}
|
||||
void Release(){ sq_delete(this,SQVM); } //does nothing
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//stack functions for the api
|
||||
void Remove(SQInteger n);
|
||||
|
||||
bool IsFalse(SQObjectPtr &o);
|
||||
|
||||
void Pop();
|
||||
void Pop(SQInteger n);
|
||||
void Push(const SQObjectPtr &o);
|
||||
SQObjectPtr &Top();
|
||||
SQObjectPtr &PopGet();
|
||||
SQObjectPtr &GetUp(SQInteger n);
|
||||
SQObjectPtr &GetAt(SQInteger n);
|
||||
|
||||
SQObjectPtrVec _stack;
|
||||
SQObjectPtrVec _vargsstack;
|
||||
SQInteger _top;
|
||||
SQInteger _stackbase;
|
||||
SQObjectPtr _roottable;
|
||||
SQObjectPtr _lasterror;
|
||||
SQObjectPtr _errorhandler;
|
||||
SQObjectPtr _debughook;
|
||||
|
||||
SQObjectPtr temp_reg;
|
||||
|
||||
|
||||
CallInfo* _callsstack;
|
||||
SQInteger _callsstacksize;
|
||||
SQInteger _alloccallsstacksize;
|
||||
sqvector<CallInfo> _callstackdata;
|
||||
|
||||
ExceptionsTraps _etraps;
|
||||
CallInfo *ci;
|
||||
void *_foreignptr;
|
||||
//VMs sharing the same state
|
||||
SQSharedState *_sharedstate;
|
||||
SQInteger _nnativecalls;
|
||||
//suspend infos
|
||||
SQBool _suspended;
|
||||
SQBool _suspended_root;
|
||||
SQInteger _suspended_target;
|
||||
SQInteger _suspended_traps;
|
||||
VarArgs _suspend_varargs;
|
||||
|
||||
SQBool _can_suspend;
|
||||
SQInteger _ops_till_suspend;
|
||||
SQBool _in_stackoverflow;
|
||||
|
||||
bool ShouldSuspend()
|
||||
{
|
||||
return _can_suspend && _ops_till_suspend <= 0;
|
||||
}
|
||||
|
||||
void DecreaseOps(SQInteger amount)
|
||||
{
|
||||
if (_ops_till_suspend - amount < _ops_till_suspend) _ops_till_suspend -= amount;
|
||||
}
|
||||
};
|
||||
|
||||
struct AutoDec{
|
||||
AutoDec(SQInteger *n) { _n = n; }
|
||||
~AutoDec() { (*_n)--; }
|
||||
SQInteger *_n;
|
||||
};
|
||||
|
||||
inline SQObjectPtr &stack_get(HSQUIRRELVM v,SQInteger idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));}
|
||||
|
||||
#define _ss(_vm_) (_vm_)->_sharedstate
|
||||
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
#define _opt_ss(_vm_) (_vm_)->_sharedstate
|
||||
#else
|
||||
#define _opt_ss(_vm_) NULL
|
||||
#endif
|
||||
|
||||
#define PUSH_CALLINFO(v,nci){ \
|
||||
if(v->_callsstacksize == v->_alloccallsstacksize) { \
|
||||
if (v->_callsstacksize > 65535 && !v->_in_stackoverflow) {\
|
||||
v->_in_stackoverflow = true; \
|
||||
v->Raise_Error("stack overflow");\
|
||||
v->CallErrorHandler(v->_lasterror);\
|
||||
return false;\
|
||||
}\
|
||||
v->GrowCallStack(); \
|
||||
} \
|
||||
v->ci = &v->_callsstack[v->_callsstacksize]; \
|
||||
*(v->ci) = nci; \
|
||||
v->_callsstacksize++; \
|
||||
}
|
||||
|
||||
#define POP_CALLINFO(v){ \
|
||||
v->_callsstacksize--; \
|
||||
v->ci->_closure.Null(); \
|
||||
if(v->_callsstacksize) \
|
||||
v->ci = &v->_callsstack[v->_callsstacksize-1] ; \
|
||||
else \
|
||||
v->ci = NULL; \
|
||||
}
|
||||
#endif //_SQVM_H_
|
||||
178
src/ai/ai.hpp
Normal file
178
src/ai/ai.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai.hpp Base functions for all AIs. */
|
||||
|
||||
#ifndef AI_HPP
|
||||
#define AI_HPP
|
||||
|
||||
#include "../script/api/script_event_types.hpp"
|
||||
#include "../core/string_compare_type.hpp"
|
||||
#include "ai_scanner.hpp"
|
||||
#include <map>
|
||||
|
||||
/** A list that maps AI names to their AIInfo object. */
|
||||
typedef std::map<const char *, class ScriptInfo *, StringCompare> ScriptInfoList;
|
||||
|
||||
/**
|
||||
* Main AI class. Contains all functions needed to start, stop, save and load AIs.
|
||||
*/
|
||||
class AI {
|
||||
public:
|
||||
/**
|
||||
* The default months AIs start after each other.
|
||||
*/
|
||||
enum StartNext {
|
||||
START_NEXT_EASY = DAYS_IN_YEAR * 2,
|
||||
START_NEXT_MEDIUM = DAYS_IN_YEAR,
|
||||
START_NEXT_HARD = DAYS_IN_YEAR / 2,
|
||||
START_NEXT_MIN = 1,
|
||||
START_NEXT_MAX = 3600,
|
||||
START_NEXT_DEVIATION = 60,
|
||||
};
|
||||
|
||||
/**
|
||||
* Is it possible to start a new AI company?
|
||||
* @return True if a new AI company can be started.
|
||||
*/
|
||||
static bool CanStartNew();
|
||||
|
||||
/**
|
||||
* Start a new AI company.
|
||||
* @param company At which slot the AI company should start.
|
||||
* @param rerandomise_ai Whether to rerandomise the configured AI.
|
||||
*/
|
||||
static void StartNew(CompanyID company, bool rerandomise_ai = true);
|
||||
|
||||
/**
|
||||
* Called every game-tick to let AIs do something.
|
||||
*/
|
||||
static void GameLoop();
|
||||
|
||||
/**
|
||||
* Get the current AI tick.
|
||||
*/
|
||||
static uint GetTick();
|
||||
|
||||
/**
|
||||
* Stop a company to be controlled by an AI.
|
||||
* @param company The company from which the AI needs to detach.
|
||||
* @pre Company::IsValidAiID(company)
|
||||
*/
|
||||
static void Stop(CompanyID company);
|
||||
|
||||
/**
|
||||
* Suspend the AI and then pause execution of the script. The script
|
||||
* will not be resumed from its suspended state until the script has
|
||||
* been unpaused.
|
||||
* @param company The company for which the AI should be paused.
|
||||
* @pre Company::IsValidAiID(company)
|
||||
*/
|
||||
static void Pause(CompanyID company);
|
||||
|
||||
/**
|
||||
* Resume execution of the AI. This function will not actually execute
|
||||
* the script, but set a flag so that the script is executed my the usual
|
||||
* mechanism that executes the script.
|
||||
* @param company The company for which the AI should be unpaused.
|
||||
* @pre Company::IsValidAiID(company)
|
||||
*/
|
||||
static void Unpause(CompanyID company);
|
||||
|
||||
/**
|
||||
* Checks if the AI is paused.
|
||||
* @param company The company for which to check if the AI is paused.
|
||||
* @pre Company::IsValidAiID(company)
|
||||
* @return true if the AI is paused, otherwise false.
|
||||
*/
|
||||
static bool IsPaused(CompanyID company);
|
||||
|
||||
/**
|
||||
* Kill any and all AIs we manage.
|
||||
*/
|
||||
static void KillAll();
|
||||
|
||||
/**
|
||||
* Initialize the AI system.
|
||||
*/
|
||||
static void Initialize();
|
||||
|
||||
/**
|
||||
* Uninitialize the AI system
|
||||
* @param keepConfig Should we keep AIConfigs, or can we free that memory?
|
||||
*/
|
||||
static void Uninitialize(bool keepConfig);
|
||||
|
||||
/**
|
||||
* Reset all AIConfigs, and make them reload their AIInfo.
|
||||
* If the AIInfo could no longer be found, an error is reported to the user.
|
||||
*/
|
||||
static void ResetConfig();
|
||||
|
||||
/**
|
||||
* Queue a new event for an AI.
|
||||
*/
|
||||
static void NewEvent(CompanyID company, ScriptEvent *event);
|
||||
|
||||
/**
|
||||
* Broadcast a new event to all active AIs.
|
||||
*/
|
||||
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company = MAX_COMPANIES);
|
||||
|
||||
/**
|
||||
* Save data from an AI to a savegame.
|
||||
*/
|
||||
static void Save(CompanyID company);
|
||||
|
||||
/**
|
||||
* Load data for an AI from a savegame.
|
||||
*/
|
||||
static void Load(CompanyID company, int version);
|
||||
|
||||
/**
|
||||
* Get the number of days before the next AI should start.
|
||||
*/
|
||||
static int GetStartNextTime();
|
||||
|
||||
/** Wrapper function for AIScanner::GetAIConsoleList */
|
||||
static char *GetConsoleList(char *p, const char *last, bool newest_only = false);
|
||||
/** Wrapper function for AIScanner::GetAIConsoleLibraryList */
|
||||
static char *GetConsoleLibraryList(char *p, const char *last);
|
||||
/** Wrapper function for AIScanner::GetAIInfoList */
|
||||
static const ScriptInfoList *GetInfoList();
|
||||
/** Wrapper function for AIScanner::GetUniqueAIInfoList */
|
||||
static const ScriptInfoList *GetUniqueInfoList();
|
||||
/** Wrapper function for AIScanner::FindInfo */
|
||||
static class AIInfo *FindInfo(const char *name, int version, bool force_exact_match);
|
||||
/** Wrapper function for AIScanner::FindLibrary */
|
||||
static class AILibrary *FindLibrary(const char *library, int version);
|
||||
|
||||
/**
|
||||
* Rescans all searchpaths for available AIs. If a used AI is no longer
|
||||
* found it is removed from the config.
|
||||
*/
|
||||
static void Rescan();
|
||||
|
||||
/** Gets the ScriptScanner instance that is used to find AIs */
|
||||
static AIScannerInfo *GetScannerInfo();
|
||||
/** Gets the ScriptScanner instance that is used to find AI Libraries */
|
||||
static AIScannerLibrary *GetScannerLibrary();
|
||||
|
||||
#if defined(ENABLE_NETWORK)
|
||||
/** Wrapper function for AIScanner::HasAI */
|
||||
static bool HasAI(const struct ContentInfo *ci, bool md5sum);
|
||||
static bool HasAILibrary(const ContentInfo *ci, bool md5sum);
|
||||
#endif
|
||||
private:
|
||||
static uint frame_counter; ///< Tick counter for the AI code
|
||||
static class AIScannerInfo *scanner_info; ///< ScriptScanner instance that is used to find AIs
|
||||
static class AIScannerLibrary *scanner_library; ///< ScriptScanner instance that is used to find AI Libraries
|
||||
};
|
||||
|
||||
#endif /* AI_HPP */
|
||||
120
src/ai/ai_config.cpp
Normal file
120
src/ai/ai_config.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_config.cpp Implementation of AIConfig. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../settings_type.h"
|
||||
#include "../string_func.h"
|
||||
#include "ai.hpp"
|
||||
#include "ai_config.hpp"
|
||||
#include "ai_info.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Configuration for AI start date, every AI has this setting. */
|
||||
ScriptConfigItem _start_date_config = {
|
||||
"start_date",
|
||||
"", // STR_AI_SETTINGS_START_DELAY
|
||||
AI::START_NEXT_MIN,
|
||||
AI::START_NEXT_MAX,
|
||||
AI::START_NEXT_MEDIUM,
|
||||
AI::START_NEXT_EASY,
|
||||
AI::START_NEXT_MEDIUM,
|
||||
AI::START_NEXT_HARD,
|
||||
AI::START_NEXT_DEVIATION,
|
||||
30,
|
||||
SCRIPTCONFIG_NONE,
|
||||
NULL,
|
||||
false
|
||||
};
|
||||
|
||||
/* static */ AIConfig *AIConfig::GetConfig(CompanyID company, ScriptSettingSource source)
|
||||
{
|
||||
AIConfig **config;
|
||||
if (source == SSS_FORCE_NEWGAME || (source == SSS_DEFAULT && _game_mode == GM_MENU)) {
|
||||
config = &_settings_newgame.ai_config[company];
|
||||
} else {
|
||||
config = &_settings_game.ai_config[company];
|
||||
}
|
||||
if (*config == NULL) *config = new AIConfig();
|
||||
return *config;
|
||||
}
|
||||
|
||||
class AIInfo *AIConfig::GetInfo() const
|
||||
{
|
||||
return static_cast<class AIInfo *>(ScriptConfig::GetInfo());
|
||||
}
|
||||
|
||||
ScriptInfo *AIConfig::FindInfo(const char *name, int version, bool force_exact_match)
|
||||
{
|
||||
return static_cast<ScriptInfo *>(AI::FindInfo(name, version, force_exact_match));
|
||||
}
|
||||
|
||||
bool AIConfig::ResetInfo(bool force_exact_match)
|
||||
{
|
||||
this->info = (ScriptInfo *)AI::FindInfo(this->name, force_exact_match ? this->version : -1, force_exact_match);
|
||||
return this->info != NULL;
|
||||
}
|
||||
|
||||
void AIConfig::PushExtraConfigList()
|
||||
{
|
||||
this->config_list->push_back(_start_date_config);
|
||||
}
|
||||
|
||||
void AIConfig::ClearConfigList()
|
||||
{
|
||||
/* The special casing for start_date is here to ensure that the
|
||||
* start_date setting won't change even if you chose another Script. */
|
||||
int start_date = this->GetSetting("start_date");
|
||||
|
||||
ScriptConfig::ClearConfigList();
|
||||
|
||||
this->SetSetting("start_date", start_date);
|
||||
}
|
||||
|
||||
int AIConfig::GetSetting(const char *name) const
|
||||
{
|
||||
if (this->info == NULL) {
|
||||
SettingValueList::const_iterator it = this->settings.find(name);
|
||||
if (it == this->settings.end()) {
|
||||
assert(strcmp("start_date", name) == 0);
|
||||
switch (GetGameSettings().script.settings_profile) {
|
||||
case SP_EASY: return AI::START_NEXT_EASY;
|
||||
case SP_MEDIUM: return AI::START_NEXT_MEDIUM;
|
||||
case SP_HARD: return AI::START_NEXT_HARD;
|
||||
case SP_CUSTOM: return AI::START_NEXT_MEDIUM;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
return ScriptConfig::GetSetting(name);
|
||||
}
|
||||
|
||||
void AIConfig::SetSetting(const char *name, int value)
|
||||
{
|
||||
if (this->info == NULL) {
|
||||
if (strcmp("start_date", name) != 0) return;
|
||||
value = Clamp(value, AI::START_NEXT_MIN, AI::START_NEXT_MAX);
|
||||
|
||||
SettingValueList::iterator it = this->settings.find(name);
|
||||
if (it != this->settings.end()) {
|
||||
(*it).second = value;
|
||||
} else {
|
||||
this->settings[stredup(name)] = value;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptConfig::SetSetting(name, value);
|
||||
}
|
||||
54
src/ai/ai_config.hpp
Normal file
54
src/ai/ai_config.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_config.hpp AIConfig stores the configuration settings of every AI. */
|
||||
|
||||
#ifndef AI_CONFIG_HPP
|
||||
#define AI_CONFIG_HPP
|
||||
|
||||
#include "../script/script_config.hpp"
|
||||
#include "../company_type.h"
|
||||
|
||||
class AIConfig : public ScriptConfig {
|
||||
public:
|
||||
/**
|
||||
* Get the config of a company.
|
||||
*/
|
||||
static AIConfig *GetConfig(CompanyID company, ScriptSettingSource source = SSS_DEFAULT);
|
||||
|
||||
AIConfig() :
|
||||
ScriptConfig()
|
||||
{}
|
||||
|
||||
AIConfig(const AIConfig *config) :
|
||||
ScriptConfig(config)
|
||||
{}
|
||||
|
||||
class AIInfo *GetInfo() const;
|
||||
|
||||
/* virtual */ int GetSetting(const char *name) const;
|
||||
/* virtual */ void SetSetting(const char *name, int value);
|
||||
|
||||
/**
|
||||
* When ever the AI Scanner is reloaded, all infos become invalid. This
|
||||
* function tells AIConfig about this.
|
||||
* @param force_exact_match If true try to find the exact same version
|
||||
* as specified. If false any version is ok.
|
||||
* @return \c true if the reset was successful, \c false if the AI was no longer
|
||||
* found.
|
||||
*/
|
||||
bool ResetInfo(bool force_exact_match);
|
||||
|
||||
protected:
|
||||
/* virtual */ void PushExtraConfigList();
|
||||
/* virtual */ void ClearConfigList();
|
||||
/* virtual */ ScriptInfo *FindInfo(const char *name, int version, bool force_exact_match);
|
||||
};
|
||||
|
||||
#endif /* AI_CONFIG_HPP */
|
||||
389
src/ai/ai_core.cpp
Normal file
389
src/ai/ai_core.cpp
Normal file
@@ -0,0 +1,389 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_core.cpp Implementation of AI. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../core/backup_type.hpp"
|
||||
#include "../core/bitmath_func.hpp"
|
||||
#include "../company_base.h"
|
||||
#include "../company_func.h"
|
||||
#include "../network/network.h"
|
||||
#include "../window_func.h"
|
||||
#include "ai_scanner.hpp"
|
||||
#include "ai_instance.hpp"
|
||||
#include "ai_config.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/* static */ uint AI::frame_counter = 0;
|
||||
/* static */ AIScannerInfo *AI::scanner_info = NULL;
|
||||
/* static */ AIScannerLibrary *AI::scanner_library = NULL;
|
||||
|
||||
/* static */ bool AI::CanStartNew()
|
||||
{
|
||||
/* Only allow new AIs on the server and only when that is allowed in multiplayer */
|
||||
return !_networking || (_network_server && _settings_game.ai.ai_in_multiplayer);
|
||||
}
|
||||
|
||||
/* static */ void AI::StartNew(CompanyID company, bool rerandomise_ai)
|
||||
{
|
||||
assert(Company::IsValidID(company));
|
||||
|
||||
/* Clients shouldn't start AIs */
|
||||
if (_networking && !_network_server) return;
|
||||
|
||||
AIConfig *config = AIConfig::GetConfig(company, AIConfig::SSS_FORCE_GAME);
|
||||
AIInfo *info = config->GetInfo();
|
||||
if (info == NULL || (rerandomise_ai && config->IsRandom())) {
|
||||
info = AI::scanner_info->SelectRandomAI();
|
||||
assert(info != NULL);
|
||||
/* Load default data and store the name in the settings */
|
||||
config->Change(info->GetName(), -1, false, true);
|
||||
}
|
||||
config->AnchorUnchangeableSettings();
|
||||
|
||||
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
|
||||
Company *c = Company::Get(company);
|
||||
|
||||
c->ai_info = info;
|
||||
assert(c->ai_instance == NULL);
|
||||
c->ai_instance = new AIInstance();
|
||||
c->ai_instance->Initialize(info);
|
||||
|
||||
cur_company.Restore();
|
||||
|
||||
InvalidateWindowData(WC_AI_DEBUG, 0, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* static */ void AI::GameLoop()
|
||||
{
|
||||
/* If we are in networking, only servers run this function, and that only if it is allowed */
|
||||
if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return;
|
||||
|
||||
/* The speed with which AIs go, is limited by the 'competitor_speed' */
|
||||
AI::frame_counter++;
|
||||
assert(_settings_game.difficulty.competitor_speed <= 4);
|
||||
if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
|
||||
|
||||
Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
|
||||
const Company *c;
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
if (c->is_ai) {
|
||||
cur_company.Change(c->index);
|
||||
c->ai_instance->GameLoop();
|
||||
}
|
||||
}
|
||||
cur_company.Restore();
|
||||
|
||||
/* Occasionally collect garbage; every 255 ticks do one company.
|
||||
* Effectively collecting garbage once every two months per AI. */
|
||||
if ((AI::frame_counter & 255) == 0) {
|
||||
CompanyID cid = (CompanyID)GB(AI::frame_counter, 8, 4);
|
||||
if (Company::IsValidAiID(cid)) Company::Get(cid)->ai_instance->CollectGarbage();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ uint AI::GetTick()
|
||||
{
|
||||
return AI::frame_counter;
|
||||
}
|
||||
|
||||
/* static */ void AI::Stop(CompanyID company)
|
||||
{
|
||||
if (_networking && !_network_server) return;
|
||||
|
||||
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
|
||||
Company *c = Company::Get(company);
|
||||
|
||||
delete c->ai_instance;
|
||||
c->ai_instance = NULL;
|
||||
c->ai_info = NULL;
|
||||
|
||||
cur_company.Restore();
|
||||
|
||||
InvalidateWindowData(WC_AI_DEBUG, 0, -1);
|
||||
DeleteWindowById(WC_AI_SETTINGS, company);
|
||||
}
|
||||
|
||||
/* static */ void AI::Pause(CompanyID company)
|
||||
{
|
||||
/* The reason why dedicated servers are forbidden to execute this
|
||||
* command is not because it is unsafe, but because there is no way
|
||||
* for the server owner to unpause the script again. */
|
||||
if (_network_dedicated) return;
|
||||
|
||||
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
|
||||
Company::Get(company)->ai_instance->Pause();
|
||||
|
||||
cur_company.Restore();
|
||||
}
|
||||
|
||||
/* static */ void AI::Unpause(CompanyID company)
|
||||
{
|
||||
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
|
||||
Company::Get(company)->ai_instance->Unpause();
|
||||
|
||||
cur_company.Restore();
|
||||
}
|
||||
|
||||
/* static */ bool AI::IsPaused(CompanyID company)
|
||||
{
|
||||
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
|
||||
bool paused = Company::Get(company)->ai_instance->IsPaused();
|
||||
|
||||
cur_company.Restore();
|
||||
|
||||
return paused;
|
||||
}
|
||||
|
||||
/* static */ void AI::KillAll()
|
||||
{
|
||||
/* It might happen there are no companies .. than we have nothing to loop */
|
||||
if (Company::GetPoolSize() == 0) return;
|
||||
|
||||
const Company *c;
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
if (c->is_ai) AI::Stop(c->index);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::Initialize()
|
||||
{
|
||||
if (AI::scanner_info != NULL) AI::Uninitialize(true);
|
||||
|
||||
AI::frame_counter = 0;
|
||||
if (AI::scanner_info == NULL) {
|
||||
TarScanner::DoScan(TarScanner::AI);
|
||||
AI::scanner_info = new AIScannerInfo();
|
||||
AI::scanner_info->Initialize();
|
||||
AI::scanner_library = new AIScannerLibrary();
|
||||
AI::scanner_library->Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::Uninitialize(bool keepConfig)
|
||||
{
|
||||
AI::KillAll();
|
||||
|
||||
if (keepConfig) {
|
||||
/* Run a rescan, which indexes all AIInfos again, and check if we can
|
||||
* still load all the AIS, while keeping the configs in place */
|
||||
Rescan();
|
||||
} else {
|
||||
delete AI::scanner_info;
|
||||
delete AI::scanner_library;
|
||||
AI::scanner_info = NULL;
|
||||
AI::scanner_library = NULL;
|
||||
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||
if (_settings_game.ai_config[c] != NULL) {
|
||||
delete _settings_game.ai_config[c];
|
||||
_settings_game.ai_config[c] = NULL;
|
||||
}
|
||||
if (_settings_newgame.ai_config[c] != NULL) {
|
||||
delete _settings_newgame.ai_config[c];
|
||||
_settings_newgame.ai_config[c] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::ResetConfig()
|
||||
{
|
||||
/* Check for both newgame as current game if we can reload the AIInfo inside
|
||||
* the AIConfig. If not, remove the AI from the list (which will assign
|
||||
* a random new AI on reload). */
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||
if (_settings_game.ai_config[c] != NULL && _settings_game.ai_config[c]->HasScript()) {
|
||||
if (!_settings_game.ai_config[c]->ResetInfo(true)) {
|
||||
DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName());
|
||||
_settings_game.ai_config[c]->Change(NULL);
|
||||
if (Company::IsValidAiID(c)) {
|
||||
/* The code belonging to an already running AI was deleted. We can only do
|
||||
* one thing here to keep everything sane and that is kill the AI. After
|
||||
* killing the offending AI we start a random other one in it's place, just
|
||||
* like what would happen if the AI was missing during loading. */
|
||||
AI::Stop(c);
|
||||
AI::StartNew(c, false);
|
||||
}
|
||||
} else if (Company::IsValidAiID(c)) {
|
||||
/* Update the reference in the Company struct. */
|
||||
Company::Get(c)->ai_info = _settings_game.ai_config[c]->GetInfo();
|
||||
}
|
||||
}
|
||||
if (_settings_newgame.ai_config[c] != NULL && _settings_newgame.ai_config[c]->HasScript()) {
|
||||
if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
|
||||
DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName());
|
||||
_settings_newgame.ai_config[c]->Change(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event)
|
||||
{
|
||||
/* AddRef() and Release() need to be called at least once, so do it here */
|
||||
event->AddRef();
|
||||
|
||||
/* Clients should ignore events */
|
||||
if (_networking && !_network_server) {
|
||||
event->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Only AIs can have an event-queue */
|
||||
if (!Company::IsValidAiID(company)) {
|
||||
event->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Queue the event */
|
||||
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
|
||||
Company::Get(_current_company)->ai_instance->InsertEvent(event);
|
||||
cur_company.Restore();
|
||||
|
||||
event->Release();
|
||||
}
|
||||
|
||||
/* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
|
||||
{
|
||||
/* AddRef() and Release() need to be called at least once, so do it here */
|
||||
event->AddRef();
|
||||
|
||||
/* Clients should ignore events */
|
||||
if (_networking && !_network_server) {
|
||||
event->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to send the event to all AIs */
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||
if (c != skip_company) AI::NewEvent(c, event);
|
||||
}
|
||||
|
||||
event->Release();
|
||||
}
|
||||
|
||||
/* static */ void AI::Save(CompanyID company)
|
||||
{
|
||||
if (!_networking || _network_server) {
|
||||
Company *c = Company::GetIfValid(company);
|
||||
assert(c != NULL && c->ai_instance != NULL);
|
||||
|
||||
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
|
||||
c->ai_instance->Save();
|
||||
cur_company.Restore();
|
||||
} else {
|
||||
AIInstance::SaveEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::Load(CompanyID company, int version)
|
||||
{
|
||||
if (!_networking || _network_server) {
|
||||
Company *c = Company::GetIfValid(company);
|
||||
assert(c != NULL && c->ai_instance != NULL);
|
||||
|
||||
Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
|
||||
c->ai_instance->Load(version);
|
||||
cur_company.Restore();
|
||||
} else {
|
||||
/* Read, but ignore, the load data */
|
||||
AIInstance::LoadEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ int AI::GetStartNextTime()
|
||||
{
|
||||
/* Find the first company which doesn't exist yet */
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||
if (!Company::IsValidID(c)) return AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->GetSetting("start_date");
|
||||
}
|
||||
|
||||
/* Currently no AI can be started, check again in a year. */
|
||||
return DAYS_IN_YEAR;
|
||||
}
|
||||
|
||||
/* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
|
||||
{
|
||||
return AI::scanner_info->GetConsoleList(p, last, newest_only);
|
||||
}
|
||||
|
||||
/* static */ char *AI::GetConsoleLibraryList(char *p, const char *last)
|
||||
{
|
||||
return AI::scanner_library->GetConsoleList(p, last, true);
|
||||
}
|
||||
|
||||
/* static */ const ScriptInfoList *AI::GetInfoList()
|
||||
{
|
||||
return AI::scanner_info->GetInfoList();
|
||||
}
|
||||
|
||||
/* static */ const ScriptInfoList *AI::GetUniqueInfoList()
|
||||
{
|
||||
return AI::scanner_info->GetUniqueInfoList();
|
||||
}
|
||||
|
||||
/* static */ AIInfo *AI::FindInfo(const char *name, int version, bool force_exact_match)
|
||||
{
|
||||
return AI::scanner_info->FindInfo(name, version, force_exact_match);
|
||||
}
|
||||
|
||||
/* static */ AILibrary *AI::FindLibrary(const char *library, int version)
|
||||
{
|
||||
return AI::scanner_library->FindLibrary(library, version);
|
||||
}
|
||||
|
||||
/* static */ void AI::Rescan()
|
||||
{
|
||||
TarScanner::DoScan(TarScanner::AI);
|
||||
|
||||
AI::scanner_info->RescanDir();
|
||||
AI::scanner_library->RescanDir();
|
||||
ResetConfig();
|
||||
|
||||
InvalidateWindowData(WC_AI_LIST, 0, 1);
|
||||
SetWindowClassesDirty(WC_AI_DEBUG);
|
||||
InvalidateWindowClassesData(WC_AI_SETTINGS);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_NETWORK)
|
||||
|
||||
/**
|
||||
* Check whether we have an AI (library) with the exact characteristics as ci.
|
||||
* @param ci the characteristics to search on (shortname and md5sum)
|
||||
* @param md5sum whether to check the MD5 checksum
|
||||
* @return true iff we have an AI (library) matching.
|
||||
*/
|
||||
/* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
|
||||
{
|
||||
return AI::scanner_info->HasScript(ci, md5sum);
|
||||
}
|
||||
|
||||
/* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
|
||||
{
|
||||
return AI::scanner_library->HasScript(ci, md5sum);
|
||||
}
|
||||
|
||||
#endif /* defined(ENABLE_NETWORK) */
|
||||
|
||||
/* static */ AIScannerInfo *AI::GetScannerInfo()
|
||||
{
|
||||
return AI::scanner_info;
|
||||
}
|
||||
|
||||
/* static */ AIScannerLibrary *AI::GetScannerLibrary()
|
||||
{
|
||||
return AI::scanner_library;
|
||||
}
|
||||
|
||||
1544
src/ai/ai_gui.cpp
Normal file
1544
src/ai/ai_gui.cpp
Normal file
File diff suppressed because it is too large
Load Diff
22
src/ai/ai_gui.hpp
Normal file
22
src/ai/ai_gui.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_gui.hpp %Window for configuring the AIs */
|
||||
|
||||
#ifndef AI_GUI_HPP
|
||||
#define AI_GUI_HPP
|
||||
|
||||
#include "../company_type.h"
|
||||
|
||||
Window* ShowAIDebugWindow(CompanyID show_company = INVALID_COMPANY);
|
||||
void ShowAIConfigWindow();
|
||||
void ShowAIDebugWindowIfAIError();
|
||||
void InitializeAIGui();
|
||||
|
||||
#endif /* AI_GUI_HPP */
|
||||
183
src/ai/ai_info.cpp
Normal file
183
src/ai/ai_info.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_info.cpp Implementation of AIInfo and AILibrary */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../script/squirrel_class.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai_scanner.hpp"
|
||||
#include "../debug.h"
|
||||
#include "../string_func.h"
|
||||
#include "../rev.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/**
|
||||
* Check if the API version provided by the AI is supported.
|
||||
* @param api_version The API version as provided by the AI.
|
||||
*/
|
||||
static bool CheckAPIVersion(const char *api_version)
|
||||
{
|
||||
return strcmp(api_version, "0.7") == 0 || strcmp(api_version, "1.0") == 0 || strcmp(api_version, "1.1") == 0 ||
|
||||
strcmp(api_version, "1.2") == 0 || strcmp(api_version, "1.3") == 0 || strcmp(api_version, "1.4") == 0 ||
|
||||
strcmp(api_version, "1.5") == 0;
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
#undef GetClassName
|
||||
#endif /* WIN32 */
|
||||
template <> const char *GetClassName<AIInfo, ST_AI>() { return "AIInfo"; }
|
||||
|
||||
/* static */ void AIInfo::RegisterAPI(Squirrel *engine)
|
||||
{
|
||||
/* Create the AIInfo class, and add the RegisterAI function */
|
||||
DefSQClass<AIInfo, ST_AI> SQAIInfo("AIInfo");
|
||||
SQAIInfo.PreRegister(engine);
|
||||
SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x");
|
||||
SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting");
|
||||
SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels");
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_NONE");
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_RANDOM, "CONFIG_RANDOM");
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "CONFIG_BOOLEAN");
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "CONFIG_INGAME");
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_DEVELOPER, "CONFIG_DEVELOPER");
|
||||
|
||||
/* Pre 1.2 had an AI prefix */
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "AICONFIG_NONE");
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_RANDOM, "AICONFIG_RANDOM");
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "AICONFIG_BOOLEAN");
|
||||
SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "AICONFIG_INGAME");
|
||||
|
||||
SQAIInfo.PostRegister(engine);
|
||||
engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
|
||||
engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx");
|
||||
}
|
||||
|
||||
/* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
|
||||
{
|
||||
/* Get the AIInfo */
|
||||
SQUserPointer instance = NULL;
|
||||
if (SQ_FAILED(sq_getinstanceup(vm, 2, &instance, 0)) || instance == NULL) return sq_throwerror(vm, "Pass an instance of a child class of AIInfo to RegisterAI");
|
||||
AIInfo *info = (AIInfo *)instance;
|
||||
|
||||
SQInteger res = ScriptInfo::Constructor(vm, info);
|
||||
if (res != 0) return res;
|
||||
|
||||
ScriptConfigItem config = _start_date_config;
|
||||
config.name = stredup(config.name);
|
||||
config.description = stredup(config.description);
|
||||
info->config_list.push_front(config);
|
||||
|
||||
if (info->engine->MethodExists(*info->SQ_instance, "MinVersionToLoad")) {
|
||||
if (!info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
|
||||
} else {
|
||||
info->min_loadable_version = info->GetVersion();
|
||||
}
|
||||
/* When there is an UseAsRandomAI function, call it. */
|
||||
if (info->engine->MethodExists(*info->SQ_instance, "UseAsRandomAI")) {
|
||||
if (!info->engine->CallBoolMethod(*info->SQ_instance, "UseAsRandomAI", &info->use_as_random, MAX_GET_OPS)) return SQ_ERROR;
|
||||
} else {
|
||||
info->use_as_random = true;
|
||||
}
|
||||
/* Try to get the API version the AI is written for. */
|
||||
if (info->engine->MethodExists(*info->SQ_instance, "GetAPIVersion")) {
|
||||
if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_OPS)) return SQ_ERROR;
|
||||
if (!CheckAPIVersion(info->api_version)) {
|
||||
DEBUG(script, 1, "Loading info.nut from (%s.%d): GetAPIVersion returned invalid version", info->GetName(), info->GetVersion());
|
||||
return SQ_ERROR;
|
||||
}
|
||||
} else {
|
||||
info->api_version = stredup("0.7");
|
||||
}
|
||||
|
||||
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
|
||||
sq_setinstanceup(vm, 2, NULL);
|
||||
/* Register the AI to the base system */
|
||||
info->GetScanner()->RegisterScript(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* static */ SQInteger AIInfo::DummyConstructor(HSQUIRRELVM vm)
|
||||
{
|
||||
/* Get the AIInfo */
|
||||
SQUserPointer instance;
|
||||
sq_getinstanceup(vm, 2, &instance, 0);
|
||||
AIInfo *info = (AIInfo *)instance;
|
||||
info->api_version = NULL;
|
||||
|
||||
SQInteger res = ScriptInfo::Constructor(vm, info);
|
||||
if (res != 0) return res;
|
||||
|
||||
char buf[8];
|
||||
seprintf(buf, lastof(buf), "%d.%d", GB(_openttd_newgrf_version, 28, 4), GB(_openttd_newgrf_version, 24, 4));
|
||||
info->api_version = stredup(buf);
|
||||
|
||||
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
|
||||
sq_setinstanceup(vm, 2, NULL);
|
||||
/* Register the AI to the base system */
|
||||
static_cast<AIScannerInfo *>(info->GetScanner())->SetDummyAI(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AIInfo::AIInfo() :
|
||||
min_loadable_version(0),
|
||||
use_as_random(false),
|
||||
api_version(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
AIInfo::~AIInfo()
|
||||
{
|
||||
free(this->api_version);
|
||||
}
|
||||
|
||||
bool AIInfo::CanLoadFromVersion(int version) const
|
||||
{
|
||||
if (version == -1) return true;
|
||||
return version >= this->min_loadable_version && version <= this->GetVersion();
|
||||
}
|
||||
|
||||
|
||||
AILibrary::~AILibrary()
|
||||
{
|
||||
free(this->category);
|
||||
}
|
||||
|
||||
/* static */ void AILibrary::RegisterAPI(Squirrel *engine)
|
||||
{
|
||||
/* Create the AILibrary class, and add the RegisterLibrary function */
|
||||
engine->AddClassBegin("AILibrary");
|
||||
engine->AddClassEnd();
|
||||
engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
|
||||
}
|
||||
|
||||
/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
|
||||
{
|
||||
/* Create a new library */
|
||||
AILibrary *library = new AILibrary();
|
||||
|
||||
SQInteger res = ScriptInfo::Constructor(vm, library);
|
||||
if (res != 0) {
|
||||
delete library;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Cache the category */
|
||||
if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethodStrdup(*library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
|
||||
delete library;
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
/* Register the Library to the base system */
|
||||
library->GetScanner()->RegisterScript(library);
|
||||
|
||||
return 0;
|
||||
}
|
||||
84
src/ai/ai_info.hpp
Normal file
84
src/ai/ai_info.hpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_info.hpp AIInfo keeps track of all information of an AI, like Author, Description, ... */
|
||||
|
||||
#ifndef AI_INFO_HPP
|
||||
#define AI_INFO_HPP
|
||||
|
||||
#include "../script/script_info.hpp"
|
||||
|
||||
/** All static information from an AI like name, version, etc. */
|
||||
class AIInfo : public ScriptInfo {
|
||||
public:
|
||||
AIInfo();
|
||||
~AIInfo();
|
||||
|
||||
/**
|
||||
* Register the functions of this class.
|
||||
*/
|
||||
static void RegisterAPI(Squirrel *engine);
|
||||
|
||||
/**
|
||||
* Create an AI, using this AIInfo as start-template.
|
||||
*/
|
||||
static SQInteger Constructor(HSQUIRRELVM vm);
|
||||
|
||||
/**
|
||||
* Create a dummy-AI.
|
||||
*/
|
||||
static SQInteger DummyConstructor(HSQUIRRELVM vm);
|
||||
|
||||
/**
|
||||
* Check if we can start this AI.
|
||||
*/
|
||||
bool CanLoadFromVersion(int version) const;
|
||||
|
||||
/**
|
||||
* Use this AI as a random AI.
|
||||
*/
|
||||
bool UseAsRandomAI() const { return this->use_as_random; }
|
||||
|
||||
/**
|
||||
* Get the API version this AI is written for.
|
||||
*/
|
||||
const char *GetAPIVersion() const { return this->api_version; }
|
||||
|
||||
private:
|
||||
int min_loadable_version; ///< The AI can load savegame data if the version is equal or greater than this.
|
||||
bool use_as_random; ///< Should this AI be used when the user wants a "random AI"?
|
||||
const char *api_version; ///< API version used by this AI.
|
||||
};
|
||||
|
||||
/** All static information from an AI library like name, version, etc. */
|
||||
class AILibrary : public ScriptInfo {
|
||||
public:
|
||||
AILibrary() : ScriptInfo(), category(NULL) {};
|
||||
~AILibrary();
|
||||
|
||||
/**
|
||||
* Register the functions of this class.
|
||||
*/
|
||||
static void RegisterAPI(Squirrel *engine);
|
||||
|
||||
/**
|
||||
* Create an AI, using this AIInfo as start-template.
|
||||
*/
|
||||
static SQInteger Constructor(HSQUIRRELVM vm);
|
||||
|
||||
/**
|
||||
* Get the category this library is in.
|
||||
*/
|
||||
const char *GetCategory() const { return this->category; }
|
||||
|
||||
private:
|
||||
const char *category; ///< The category this library is in.
|
||||
};
|
||||
|
||||
#endif /* AI_INFO_HPP */
|
||||
270
src/ai/ai_instance.cpp
Normal file
270
src/ai/ai_instance.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_instance.cpp Implementation of AIInstance. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../debug.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "../script/squirrel_class.hpp"
|
||||
|
||||
#include "ai_config.hpp"
|
||||
#include "ai_gui.hpp"
|
||||
#include "ai.hpp"
|
||||
|
||||
#include "../script/script_storage.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai_instance.hpp"
|
||||
|
||||
/* Manually include the Text glue. */
|
||||
#include "../script/api/template/template_text.hpp.sq"
|
||||
|
||||
/* Convert all AI related classes to Squirrel data.
|
||||
* Note: this line is a marker in squirrel_export.sh. Do not change! */
|
||||
#include "../script/api/ai/ai_accounting.hpp.sq"
|
||||
#include "../script/api/ai/ai_airport.hpp.sq"
|
||||
#include "../script/api/ai/ai_base.hpp.sq"
|
||||
#include "../script/api/ai/ai_basestation.hpp.sq"
|
||||
#include "../script/api/ai/ai_bridge.hpp.sq"
|
||||
#include "../script/api/ai/ai_bridgelist.hpp.sq"
|
||||
#include "../script/api/ai/ai_cargo.hpp.sq"
|
||||
#include "../script/api/ai/ai_cargolist.hpp.sq"
|
||||
#include "../script/api/ai/ai_company.hpp.sq"
|
||||
#include "../script/api/ai/ai_controller.hpp.sq"
|
||||
#include "../script/api/ai/ai_date.hpp.sq"
|
||||
#include "../script/api/ai/ai_depotlist.hpp.sq"
|
||||
#include "../script/api/ai/ai_engine.hpp.sq"
|
||||
#include "../script/api/ai/ai_enginelist.hpp.sq"
|
||||
#include "../script/api/ai/ai_error.hpp.sq"
|
||||
#include "../script/api/ai/ai_event.hpp.sq"
|
||||
#include "../script/api/ai/ai_event_types.hpp.sq"
|
||||
#include "../script/api/ai/ai_execmode.hpp.sq"
|
||||
#include "../script/api/ai/ai_gamesettings.hpp.sq"
|
||||
#include "../script/api/ai/ai_group.hpp.sq"
|
||||
#include "../script/api/ai/ai_grouplist.hpp.sq"
|
||||
#include "../script/api/ai/ai_industry.hpp.sq"
|
||||
#include "../script/api/ai/ai_industrylist.hpp.sq"
|
||||
#include "../script/api/ai/ai_industrytype.hpp.sq"
|
||||
#include "../script/api/ai/ai_industrytypelist.hpp.sq"
|
||||
#include "../script/api/ai/ai_infrastructure.hpp.sq"
|
||||
#include "../script/api/ai/ai_list.hpp.sq"
|
||||
#include "../script/api/ai/ai_log.hpp.sq"
|
||||
#include "../script/api/ai/ai_map.hpp.sq"
|
||||
#include "../script/api/ai/ai_marine.hpp.sq"
|
||||
#include "../script/api/ai/ai_order.hpp.sq"
|
||||
#include "../script/api/ai/ai_rail.hpp.sq"
|
||||
#include "../script/api/ai/ai_railtypelist.hpp.sq"
|
||||
#include "../script/api/ai/ai_road.hpp.sq"
|
||||
#include "../script/api/ai/ai_sign.hpp.sq"
|
||||
#include "../script/api/ai/ai_signlist.hpp.sq"
|
||||
#include "../script/api/ai/ai_station.hpp.sq"
|
||||
#include "../script/api/ai/ai_stationlist.hpp.sq"
|
||||
#include "../script/api/ai/ai_subsidy.hpp.sq"
|
||||
#include "../script/api/ai/ai_subsidylist.hpp.sq"
|
||||
#include "../script/api/ai/ai_testmode.hpp.sq"
|
||||
#include "../script/api/ai/ai_tile.hpp.sq"
|
||||
#include "../script/api/ai/ai_tilelist.hpp.sq"
|
||||
#include "../script/api/ai/ai_town.hpp.sq"
|
||||
#include "../script/api/ai/ai_townlist.hpp.sq"
|
||||
#include "../script/api/ai/ai_tunnel.hpp.sq"
|
||||
#include "../script/api/ai/ai_vehicle.hpp.sq"
|
||||
#include "../script/api/ai/ai_vehiclelist.hpp.sq"
|
||||
#include "../script/api/ai/ai_waypoint.hpp.sq"
|
||||
#include "../script/api/ai/ai_waypointlist.hpp.sq"
|
||||
|
||||
#include "../company_base.h"
|
||||
#include "../company_func.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
AIInstance::AIInstance() :
|
||||
ScriptInstance("AI")
|
||||
{}
|
||||
|
||||
void AIInstance::Initialize(AIInfo *info)
|
||||
{
|
||||
this->versionAPI = info->GetAPIVersion();
|
||||
|
||||
/* Register the AIController (including the "import" command) */
|
||||
SQAIController_Register(this->engine);
|
||||
|
||||
ScriptInstance::Initialize(info->GetMainScript(), info->GetInstanceName(), _current_company);
|
||||
}
|
||||
|
||||
void AIInstance::RegisterAPI()
|
||||
{
|
||||
ScriptInstance::RegisterAPI();
|
||||
|
||||
/* Register all classes */
|
||||
SQAIList_Register(this->engine);
|
||||
SQAIAccounting_Register(this->engine);
|
||||
SQAIAirport_Register(this->engine);
|
||||
SQAIBase_Register(this->engine);
|
||||
SQAIBaseStation_Register(this->engine);
|
||||
SQAIBridge_Register(this->engine);
|
||||
SQAIBridgeList_Register(this->engine);
|
||||
SQAIBridgeList_Length_Register(this->engine);
|
||||
SQAICargo_Register(this->engine);
|
||||
SQAICargoList_Register(this->engine);
|
||||
SQAICargoList_IndustryAccepting_Register(this->engine);
|
||||
SQAICargoList_IndustryProducing_Register(this->engine);
|
||||
SQAICargoList_StationAccepting_Register(this->engine);
|
||||
SQAICompany_Register(this->engine);
|
||||
SQAIDate_Register(this->engine);
|
||||
SQAIDepotList_Register(this->engine);
|
||||
SQAIEngine_Register(this->engine);
|
||||
SQAIEngineList_Register(this->engine);
|
||||
SQAIError_Register(this->engine);
|
||||
SQAIEvent_Register(this->engine);
|
||||
SQAIEventAircraftDestTooFar_Register(this->engine);
|
||||
SQAIEventCompanyAskMerger_Register(this->engine);
|
||||
SQAIEventCompanyBankrupt_Register(this->engine);
|
||||
SQAIEventCompanyInTrouble_Register(this->engine);
|
||||
SQAIEventCompanyMerger_Register(this->engine);
|
||||
SQAIEventCompanyNew_Register(this->engine);
|
||||
SQAIEventCompanyTown_Register(this->engine);
|
||||
SQAIEventController_Register(this->engine);
|
||||
SQAIEventDisasterZeppelinerCleared_Register(this->engine);
|
||||
SQAIEventDisasterZeppelinerCrashed_Register(this->engine);
|
||||
SQAIEventEngineAvailable_Register(this->engine);
|
||||
SQAIEventEnginePreview_Register(this->engine);
|
||||
SQAIEventExclusiveTransportRights_Register(this->engine);
|
||||
SQAIEventIndustryClose_Register(this->engine);
|
||||
SQAIEventIndustryOpen_Register(this->engine);
|
||||
SQAIEventRoadReconstruction_Register(this->engine);
|
||||
SQAIEventStationFirstVehicle_Register(this->engine);
|
||||
SQAIEventSubsidyAwarded_Register(this->engine);
|
||||
SQAIEventSubsidyExpired_Register(this->engine);
|
||||
SQAIEventSubsidyOffer_Register(this->engine);
|
||||
SQAIEventSubsidyOfferExpired_Register(this->engine);
|
||||
SQAIEventTownFounded_Register(this->engine);
|
||||
SQAIEventVehicleCrashed_Register(this->engine);
|
||||
SQAIEventVehicleLost_Register(this->engine);
|
||||
SQAIEventVehicleUnprofitable_Register(this->engine);
|
||||
SQAIEventVehicleWaitingInDepot_Register(this->engine);
|
||||
SQAIExecMode_Register(this->engine);
|
||||
SQAIGameSettings_Register(this->engine);
|
||||
SQAIGroup_Register(this->engine);
|
||||
SQAIGroupList_Register(this->engine);
|
||||
SQAIIndustry_Register(this->engine);
|
||||
SQAIIndustryList_Register(this->engine);
|
||||
SQAIIndustryList_CargoAccepting_Register(this->engine);
|
||||
SQAIIndustryList_CargoProducing_Register(this->engine);
|
||||
SQAIIndustryType_Register(this->engine);
|
||||
SQAIIndustryTypeList_Register(this->engine);
|
||||
SQAIInfrastructure_Register(this->engine);
|
||||
SQAILog_Register(this->engine);
|
||||
SQAIMap_Register(this->engine);
|
||||
SQAIMarine_Register(this->engine);
|
||||
SQAIOrder_Register(this->engine);
|
||||
SQAIRail_Register(this->engine);
|
||||
SQAIRailTypeList_Register(this->engine);
|
||||
SQAIRoad_Register(this->engine);
|
||||
SQAISign_Register(this->engine);
|
||||
SQAISignList_Register(this->engine);
|
||||
SQAIStation_Register(this->engine);
|
||||
SQAIStationList_Register(this->engine);
|
||||
SQAIStationList_Cargo_Register(this->engine);
|
||||
SQAIStationList_CargoPlanned_Register(this->engine);
|
||||
SQAIStationList_CargoPlannedByFrom_Register(this->engine);
|
||||
SQAIStationList_CargoPlannedByVia_Register(this->engine);
|
||||
SQAIStationList_CargoPlannedFromByVia_Register(this->engine);
|
||||
SQAIStationList_CargoPlannedViaByFrom_Register(this->engine);
|
||||
SQAIStationList_CargoWaiting_Register(this->engine);
|
||||
SQAIStationList_CargoWaitingByFrom_Register(this->engine);
|
||||
SQAIStationList_CargoWaitingByVia_Register(this->engine);
|
||||
SQAIStationList_CargoWaitingFromByVia_Register(this->engine);
|
||||
SQAIStationList_CargoWaitingViaByFrom_Register(this->engine);
|
||||
SQAIStationList_Vehicle_Register(this->engine);
|
||||
SQAISubsidy_Register(this->engine);
|
||||
SQAISubsidyList_Register(this->engine);
|
||||
SQAITestMode_Register(this->engine);
|
||||
SQAITile_Register(this->engine);
|
||||
SQAITileList_Register(this->engine);
|
||||
SQAITileList_IndustryAccepting_Register(this->engine);
|
||||
SQAITileList_IndustryProducing_Register(this->engine);
|
||||
SQAITileList_StationType_Register(this->engine);
|
||||
SQAITown_Register(this->engine);
|
||||
SQAITownEffectList_Register(this->engine);
|
||||
SQAITownList_Register(this->engine);
|
||||
SQAITunnel_Register(this->engine);
|
||||
SQAIVehicle_Register(this->engine);
|
||||
SQAIVehicleList_Register(this->engine);
|
||||
SQAIVehicleList_DefaultGroup_Register(this->engine);
|
||||
SQAIVehicleList_Depot_Register(this->engine);
|
||||
SQAIVehicleList_Group_Register(this->engine);
|
||||
SQAIVehicleList_SharedOrders_Register(this->engine);
|
||||
SQAIVehicleList_Station_Register(this->engine);
|
||||
SQAIWaypoint_Register(this->engine);
|
||||
SQAIWaypointList_Register(this->engine);
|
||||
SQAIWaypointList_Vehicle_Register(this->engine);
|
||||
|
||||
if (!this->LoadCompatibilityScripts(this->versionAPI, AI_DIR)) this->Died();
|
||||
}
|
||||
|
||||
void AIInstance::Died()
|
||||
{
|
||||
ScriptInstance::Died();
|
||||
|
||||
ShowAIDebugWindow(_current_company);
|
||||
|
||||
const AIInfo *info = AIConfig::GetConfig(_current_company, AIConfig::SSS_FORCE_GAME)->GetInfo();
|
||||
if (info != NULL) {
|
||||
ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING);
|
||||
|
||||
if (info->GetURL() != NULL) {
|
||||
ScriptLog::Info("Please report the error to the following URL:");
|
||||
ScriptLog::Info(info->GetURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AIInstance::LoadDummyScript()
|
||||
{
|
||||
extern void Script_CreateDummy(HSQUIRRELVM vm, StringID string, const char *type);
|
||||
Script_CreateDummy(this->engine->GetVM(), STR_ERROR_AI_NO_AI_FOUND, "AI");
|
||||
}
|
||||
|
||||
int AIInstance::GetSetting(const char *name)
|
||||
{
|
||||
return AIConfig::GetConfig(_current_company)->GetSetting(name);
|
||||
}
|
||||
|
||||
ScriptInfo *AIInstance::FindLibrary(const char *library, int version)
|
||||
{
|
||||
return (ScriptInfo *)AI::FindLibrary(library, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* DoCommand callback function for all commands executed by AIs.
|
||||
* @param result The result of the command.
|
||||
* @param tile The tile on which the command was executed.
|
||||
* @param p1 p1 as given to DoCommandPInternal.
|
||||
* @param p2 p2 as given to DoCommandPInternal.
|
||||
*/
|
||||
void CcAI(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
/*
|
||||
* The company might not exist anymore. Check for this.
|
||||
* The command checks are not useful since this callback
|
||||
* is also called when the command fails, which is does
|
||||
* when the company does not exist anymore.
|
||||
*/
|
||||
const Company *c = Company::GetIfValid(_current_company);
|
||||
if (c == NULL || c->ai_instance == NULL) return;
|
||||
|
||||
c->ai_instance->DoCommandCallback(result, tile, p1, p2);
|
||||
c->ai_instance->Continue();
|
||||
}
|
||||
|
||||
CommandCallback *AIInstance::GetDoCommandCallback()
|
||||
{
|
||||
return &CcAI;
|
||||
}
|
||||
38
src/ai/ai_instance.hpp
Normal file
38
src/ai/ai_instance.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_instance.hpp The AIInstance tracks an AI. */
|
||||
|
||||
#ifndef AI_INSTANCE_HPP
|
||||
#define AI_INSTANCE_HPP
|
||||
|
||||
#include "../script/script_instance.hpp"
|
||||
|
||||
/** Runtime information about an AI like a pointer to the squirrel vm and the current state. */
|
||||
class AIInstance : public ScriptInstance {
|
||||
public:
|
||||
AIInstance();
|
||||
|
||||
/**
|
||||
* Initialize the AI and prepare it for its first run.
|
||||
* @param info The AI to create the instance of.
|
||||
*/
|
||||
void Initialize(class AIInfo *info);
|
||||
|
||||
/* virtual */ int GetSetting(const char *name);
|
||||
/* virtual */ ScriptInfo *FindLibrary(const char *library, int version);
|
||||
|
||||
private:
|
||||
/* virtual */ void RegisterAPI();
|
||||
/* virtual */ void Died();
|
||||
/* virtual */ CommandCallback *GetDoCommandCallback();
|
||||
/* virtual */ void LoadDummyScript();
|
||||
};
|
||||
|
||||
#endif /* AI_INSTANCE_HPP */
|
||||
171
src/ai/ai_scanner.cpp
Normal file
171
src/ai/ai_scanner.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_scanner.cpp allows scanning AI scripts */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../debug.h"
|
||||
#include "../network/network.h"
|
||||
#include "../core/random_func.hpp"
|
||||
|
||||
#include "../script/squirrel_class.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai_scanner.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
|
||||
AIScannerInfo::AIScannerInfo() :
|
||||
ScriptScanner(),
|
||||
info_dummy(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void AIScannerInfo::Initialize()
|
||||
{
|
||||
ScriptScanner::Initialize("AIScanner");
|
||||
|
||||
/* Create the dummy AI */
|
||||
free(this->main_script);
|
||||
this->main_script = stredup("%_dummy");
|
||||
extern void Script_CreateDummyInfo(HSQUIRRELVM vm, const char *type, const char *dir);
|
||||
Script_CreateDummyInfo(this->engine->GetVM(), "AI", "ai");
|
||||
}
|
||||
|
||||
void AIScannerInfo::SetDummyAI(class AIInfo *info)
|
||||
{
|
||||
this->info_dummy = info;
|
||||
}
|
||||
|
||||
AIScannerInfo::~AIScannerInfo()
|
||||
{
|
||||
delete this->info_dummy;
|
||||
}
|
||||
|
||||
void AIScannerInfo::GetScriptName(ScriptInfo *info, char *name, const char *last)
|
||||
{
|
||||
seprintf(name, last, "%s", info->GetName());
|
||||
}
|
||||
|
||||
void AIScannerInfo::RegisterAPI(class Squirrel *engine)
|
||||
{
|
||||
AIInfo::RegisterAPI(engine);
|
||||
}
|
||||
|
||||
AIInfo *AIScannerInfo::SelectRandomAI() const
|
||||
{
|
||||
uint num_random_ais = 0;
|
||||
for (ScriptInfoList::const_iterator it = this->info_single_list.begin(); it != this->info_single_list.end(); it++) {
|
||||
AIInfo *i = static_cast<AIInfo *>((*it).second);
|
||||
if (i->UseAsRandomAI()) num_random_ais++;
|
||||
}
|
||||
|
||||
if (num_random_ais == 0) {
|
||||
DEBUG(script, 0, "No suitable AI found, loading 'dummy' AI.");
|
||||
return this->info_dummy;
|
||||
}
|
||||
|
||||
/* Find a random AI */
|
||||
uint pos;
|
||||
if (_networking) {
|
||||
pos = InteractiveRandomRange(num_random_ais);
|
||||
} else {
|
||||
pos = RandomRange(num_random_ais);
|
||||
}
|
||||
|
||||
/* Find the Nth item from the array */
|
||||
ScriptInfoList::const_iterator it = this->info_single_list.begin();
|
||||
|
||||
#define GetAIInfo(it) static_cast<AIInfo *>((*it).second)
|
||||
while (!GetAIInfo(it)->UseAsRandomAI()) it++;
|
||||
for (; pos > 0; pos--) {
|
||||
it++;
|
||||
while (!GetAIInfo(it)->UseAsRandomAI()) it++;
|
||||
}
|
||||
return GetAIInfo(it);
|
||||
#undef GetAIInfo
|
||||
}
|
||||
|
||||
AIInfo *AIScannerInfo::FindInfo(const char *nameParam, int versionParam, bool force_exact_match)
|
||||
{
|
||||
if (this->info_list.size() == 0) return NULL;
|
||||
if (nameParam == NULL) return NULL;
|
||||
|
||||
char ai_name[1024];
|
||||
strecpy(ai_name, nameParam, lastof(ai_name));
|
||||
strtolower(ai_name);
|
||||
|
||||
AIInfo *info = NULL;
|
||||
int version = -1;
|
||||
|
||||
if (versionParam == -1) {
|
||||
/* We want to load the latest version of this AI; so find it */
|
||||
if (this->info_single_list.find(ai_name) != this->info_single_list.end()) return static_cast<AIInfo *>(this->info_single_list[ai_name]);
|
||||
|
||||
/* If we didn't find a match AI, maybe the user included a version */
|
||||
char *e = strrchr(ai_name, '.');
|
||||
if (e == NULL) return NULL;
|
||||
*e = '\0';
|
||||
e++;
|
||||
versionParam = atoi(e);
|
||||
/* FALL THROUGH, like we were calling this function with a version. */
|
||||
}
|
||||
|
||||
if (force_exact_match) {
|
||||
/* Try to find a direct 'name.version' match */
|
||||
char ai_name_tmp[1024];
|
||||
seprintf(ai_name_tmp, lastof(ai_name_tmp), "%s.%d", ai_name, versionParam);
|
||||
strtolower(ai_name_tmp);
|
||||
if (this->info_list.find(ai_name_tmp) != this->info_list.end()) return static_cast<AIInfo *>(this->info_list[ai_name_tmp]);
|
||||
}
|
||||
|
||||
/* See if there is a compatible AI which goes by that name, with the highest
|
||||
* version which allows loading the requested version */
|
||||
ScriptInfoList::iterator it = this->info_list.begin();
|
||||
for (; it != this->info_list.end(); it++) {
|
||||
AIInfo *i = static_cast<AIInfo *>((*it).second);
|
||||
if (strcasecmp(ai_name, i->GetName()) == 0 && i->CanLoadFromVersion(versionParam) && (version == -1 || i->GetVersion() > version)) {
|
||||
version = (*it).second->GetVersion();
|
||||
info = i;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
void AIScannerLibrary::Initialize()
|
||||
{
|
||||
ScriptScanner::Initialize("AIScanner");
|
||||
}
|
||||
|
||||
void AIScannerLibrary::GetScriptName(ScriptInfo *info, char *name, const char *last)
|
||||
{
|
||||
AILibrary *library = static_cast<AILibrary *>(info);
|
||||
seprintf(name, last, "%s.%s", library->GetCategory(), library->GetInstanceName());
|
||||
}
|
||||
|
||||
void AIScannerLibrary::RegisterAPI(class Squirrel *engine)
|
||||
{
|
||||
AILibrary::RegisterAPI(engine);
|
||||
}
|
||||
|
||||
AILibrary *AIScannerLibrary::FindLibrary(const char *library, int version)
|
||||
{
|
||||
/* Internally we store libraries as 'library.version' */
|
||||
char library_name[1024];
|
||||
seprintf(library_name, lastof(library_name), "%s.%d", library, version);
|
||||
strtolower(library_name);
|
||||
|
||||
/* Check if the library + version exists */
|
||||
ScriptInfoList::iterator iter = this->info_list.find(library_name);
|
||||
if (iter == this->info_list.end()) return NULL;
|
||||
|
||||
return static_cast<AILibrary *>((*iter).second);
|
||||
}
|
||||
75
src/ai/ai_scanner.hpp
Normal file
75
src/ai/ai_scanner.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file ai_scanner.hpp declarations of the class for AI scanner */
|
||||
|
||||
#ifndef AI_SCANNER_HPP
|
||||
#define AI_SCANNER_HPP
|
||||
|
||||
#include "../script/script_scanner.hpp"
|
||||
|
||||
class AIScannerInfo : public ScriptScanner {
|
||||
public:
|
||||
AIScannerInfo();
|
||||
~AIScannerInfo();
|
||||
|
||||
/* virtual */ void Initialize();
|
||||
|
||||
/**
|
||||
* Select a random AI.
|
||||
* @return A random AI from the pool.
|
||||
*/
|
||||
class AIInfo *SelectRandomAI() const;
|
||||
|
||||
/**
|
||||
* Check if we have an AI by name and version available in our list.
|
||||
* @param nameParam The name of the AI.
|
||||
* @param versionParam The version of the AI, or -1 if you want the latest.
|
||||
* @param force_exact_match Only match name+version, never latest.
|
||||
* @return NULL if no match found, otherwise the AI that matched.
|
||||
*/
|
||||
class AIInfo *FindInfo(const char *nameParam, int versionParam, bool force_exact_match);
|
||||
|
||||
/**
|
||||
* Set the Dummy AI.
|
||||
*/
|
||||
void SetDummyAI(class AIInfo *info);
|
||||
|
||||
protected:
|
||||
/* virtual */ void GetScriptName(ScriptInfo *info, char *name, const char *last);
|
||||
/* virtual */ const char *GetFileName() const { return PATHSEP "info.nut"; }
|
||||
/* virtual */ Subdirectory GetDirectory() const { return AI_DIR; }
|
||||
/* virtual */ const char *GetScannerName() const { return "AIs"; }
|
||||
/* virtual */ void RegisterAPI(class Squirrel *engine);
|
||||
|
||||
private:
|
||||
AIInfo *info_dummy; ///< The dummy AI.
|
||||
};
|
||||
|
||||
class AIScannerLibrary : public ScriptScanner {
|
||||
public:
|
||||
/* virtual */ void Initialize();
|
||||
|
||||
/**
|
||||
* Find a library in the pool.
|
||||
* @param library The library name to find.
|
||||
* @param version The version the library should have.
|
||||
* @return The library if found, NULL otherwise.
|
||||
*/
|
||||
class AILibrary *FindLibrary(const char *library, int version);
|
||||
|
||||
protected:
|
||||
/* virtual */ void GetScriptName(ScriptInfo *info, char *name, const char *last);
|
||||
/* virtual */ const char *GetFileName() const { return PATHSEP "library.nut"; }
|
||||
/* virtual */ Subdirectory GetDirectory() const { return AI_LIBRARY_DIR; }
|
||||
/* virtual */ const char *GetScannerName() const { return "AI Libraries"; }
|
||||
/* virtual */ void RegisterAPI(class Squirrel *engine);
|
||||
};
|
||||
|
||||
#endif /* AI_SCANNER_HPP */
|
||||
148
src/aircraft.h
Normal file
148
src/aircraft.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file aircraft.h Base for aircraft. */
|
||||
|
||||
#ifndef AIRCRAFT_H
|
||||
#define AIRCRAFT_H
|
||||
|
||||
#include "station_map.h"
|
||||
#include "vehicle_base.h"
|
||||
|
||||
/**
|
||||
* Base values for flight levels above ground level for 'normal' flight and holding patterns.
|
||||
* Due to speed and direction, the actual flight level may be higher.
|
||||
*/
|
||||
enum AircraftFlyingAltitude {
|
||||
AIRCRAFT_MIN_FLYING_ALTITUDE = 120, ///< Minimum flying altitude above tile.
|
||||
AIRCRAFT_MAX_FLYING_ALTITUDE = 360, ///< Maximum flying altitude above tile.
|
||||
PLANE_HOLD_MAX_FLYING_ALTITUDE = 150, ///< holding flying altitude above tile of planes.
|
||||
HELICOPTER_HOLD_MAX_FLYING_ALTITUDE = 184 ///< holding flying altitude above tile of helicopters.
|
||||
};
|
||||
|
||||
struct Aircraft;
|
||||
|
||||
/** An aircraft can be one of those types. */
|
||||
enum AircraftSubType {
|
||||
AIR_HELICOPTER = 0, ///< an helicopter
|
||||
AIR_AIRCRAFT = 2, ///< an airplane
|
||||
AIR_SHADOW = 4, ///< shadow of the aircraft
|
||||
AIR_ROTOR = 6, ///< rotor of an helicopter
|
||||
};
|
||||
|
||||
/** Flags for air vehicles; shared with disaster vehicles. */
|
||||
enum AirVehicleFlags {
|
||||
VAF_DEST_TOO_FAR = 0, ///< Next destination is too far away.
|
||||
|
||||
/* The next two flags are to prevent stair climbing of the aircraft. The idea is that the aircraft
|
||||
* will ascend or descend multiple flight levels at a time instead of following the contours of the
|
||||
* landscape at a fixed altitude. This only has effect when there are more than 15 height levels. */
|
||||
VAF_IN_MAX_HEIGHT_CORRECTION = 1, ///< The vehicle is currently lowering its altitude because it hit the upper bound.
|
||||
VAF_IN_MIN_HEIGHT_CORRECTION = 2, ///< The vehicle is currently raising its altitude because it hit the lower bound.
|
||||
};
|
||||
|
||||
static const int ROTOR_Z_OFFSET = 5; ///< Z Offset between helicopter- and rotorsprite.
|
||||
|
||||
void HandleAircraftEnterHangar(Aircraft *v);
|
||||
void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type);
|
||||
void UpdateAirplanesOnNewStation(const Station *st);
|
||||
void UpdateAircraftCache(Aircraft *v, bool update_range = false);
|
||||
|
||||
void AircraftLeaveHangar(Aircraft *v, Direction exit_dir);
|
||||
void AircraftNextAirportPos_and_Order(Aircraft *v);
|
||||
void SetAircraftPosition(Aircraft *v, int x, int y, int z);
|
||||
|
||||
void GetAircraftFlightLevelBounds(const Vehicle *v, int *min, int *max);
|
||||
template <class T>
|
||||
int GetAircraftFlightLevel(T *v, bool takeoff = false);
|
||||
|
||||
/** Variables that are cached to improve performance and such. */
|
||||
struct AircraftCache {
|
||||
uint32 cached_max_range_sqr; ///< Cached squared maximum range.
|
||||
uint16 cached_max_range; ///< Cached maximum range.
|
||||
};
|
||||
|
||||
/**
|
||||
* Aircraft, helicopters, rotors and their shadows belong to this class.
|
||||
*/
|
||||
struct Aircraft FINAL : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
|
||||
uint16 crashed_counter; ///< Timer for handling crash animations.
|
||||
byte pos; ///< Next desired position of the aircraft.
|
||||
byte previous_pos; ///< Previous desired position of the aircraft.
|
||||
StationID targetairport; ///< Airport to go to next.
|
||||
byte state; ///< State of the airport. @see AirportMovementStates
|
||||
DirectionByte last_direction;
|
||||
byte number_consecutive_turns; ///< Protection to prevent the aircraft of making a lot of turns in order to reach a specific point.
|
||||
byte turn_counter; ///< Ticks between each turn to prevent > 45 degree turns.
|
||||
byte flags; ///< Aircraft flags. @see AirVehicleFlags
|
||||
|
||||
AircraftCache acache;
|
||||
|
||||
/** We don't want GCC to zero our struct! It already is zeroed and has an index! */
|
||||
Aircraft() : SpecializedVehicleBase() {}
|
||||
/** We want to 'destruct' the right class. */
|
||||
virtual ~Aircraft() { this->PreDestructor(); }
|
||||
|
||||
void MarkDirty();
|
||||
void UpdateDeltaXY(Direction direction);
|
||||
ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_AIRCRAFT_INC : EXPENSES_AIRCRAFT_RUN; }
|
||||
bool IsPrimaryVehicle() const { return this->IsNormalAircraft(); }
|
||||
SpriteID GetImage(Direction direction, EngineImageType image_type) const;
|
||||
int GetDisplaySpeed() const { return this->cur_speed; }
|
||||
int GetDisplayMaxSpeed() const { return this->vcache.cached_max_speed; }
|
||||
int GetSpeedOldUnits() const { return this->vcache.cached_max_speed * 10 / 128; }
|
||||
int GetCurrentMaxSpeed() const { return this->GetSpeedOldUnits(); }
|
||||
Money GetRunningCost() const;
|
||||
|
||||
bool IsInDepot() const
|
||||
{
|
||||
assert(this->IsPrimaryVehicle());
|
||||
return (this->vehstatus & VS_HIDDEN) != 0 && IsHangarTile(this->tile);
|
||||
}
|
||||
|
||||
bool Tick();
|
||||
void OnNewDay();
|
||||
uint Crash(bool flooded = false);
|
||||
TileIndex GetOrderStationLocation(StationID station);
|
||||
bool FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse);
|
||||
|
||||
/**
|
||||
* Check if the aircraft type is a normal flying device; eg
|
||||
* not a rotor or a shadow
|
||||
* @return Returns true if the aircraft is a helicopter/airplane and
|
||||
* false if it is a shadow or a rotor
|
||||
*/
|
||||
inline bool IsNormalAircraft() const
|
||||
{
|
||||
/* To be fully correct the commented out functionality is the proper one,
|
||||
* but since value can only be 0 or 2, it is sufficient to only check <= 2
|
||||
* return (this->subtype == AIR_HELICOPTER) || (this->subtype == AIR_AIRCRAFT); */
|
||||
return this->subtype <= AIR_AIRCRAFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the range of this aircraft.
|
||||
* @return Range in tiles or 0 if unlimited range.
|
||||
*/
|
||||
uint16 GetRange() const
|
||||
{
|
||||
return this->acache.cached_max_range;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Macro for iterating over all aircrafts.
|
||||
*/
|
||||
#define FOR_ALL_AIRCRAFT(var) FOR_ALL_VEHICLES_OF_TYPE(Aircraft, var)
|
||||
|
||||
SpriteID GetRotorImage(const Aircraft *v, EngineImageType image_type);
|
||||
|
||||
Station *GetTargetAirportIfValid(const Aircraft *v);
|
||||
|
||||
#endif /* AIRCRAFT_H */
|
||||
2078
src/aircraft_cmd.cpp
Normal file
2078
src/aircraft_cmd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
111
src/aircraft_gui.cpp
Normal file
111
src/aircraft_gui.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file aircraft_gui.cpp The GUI of aircraft. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "aircraft.h"
|
||||
#include "vehicle_gui.h"
|
||||
#include "newgrf_engine.h"
|
||||
#include "strings_func.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "window_gui.h"
|
||||
#include "spritecache.h"
|
||||
#include "zoom_func.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
/**
|
||||
* Draw the details for the given vehicle at the given position
|
||||
*
|
||||
* @param v current vehicle
|
||||
* @param left The left most coordinate to draw
|
||||
* @param right The right most coordinate to draw
|
||||
* @param y The y coordinate
|
||||
*/
|
||||
void DrawAircraftDetails(const Aircraft *v, int left, int right, int y)
|
||||
{
|
||||
int y_offset = (v->Next()->cargo_cap != 0) ? -(FONT_HEIGHT_NORMAL + 1): 0;
|
||||
Money feeder_share = 0;
|
||||
|
||||
for (const Aircraft *u = v; u != NULL; u = u->Next()) {
|
||||
if (u->IsNormalAircraft()) {
|
||||
SetDParam(0, u->engine_type);
|
||||
SetDParam(1, u->build_year);
|
||||
SetDParam(2, u->value);
|
||||
DrawString(left, right, y, STR_VEHICLE_INFO_BUILT_VALUE);
|
||||
|
||||
SetDParam(0, u->cargo_type);
|
||||
SetDParam(1, u->cargo_cap);
|
||||
SetDParam(2, u->Next()->cargo_type);
|
||||
SetDParam(3, u->Next()->cargo_cap);
|
||||
SetDParam(4, GetCargoSubtypeText(u));
|
||||
DrawString(left, right, y + FONT_HEIGHT_NORMAL, (u->Next()->cargo_cap != 0) ? STR_VEHICLE_INFO_CAPACITY_CAPACITY : STR_VEHICLE_INFO_CAPACITY);
|
||||
}
|
||||
|
||||
if (u->cargo_cap != 0) {
|
||||
uint cargo_count = u->cargo.StoredCount();
|
||||
|
||||
y_offset += FONT_HEIGHT_NORMAL + 1;
|
||||
if (cargo_count != 0) {
|
||||
/* Cargo names (fix pluralness) */
|
||||
SetDParam(0, u->cargo_type);
|
||||
SetDParam(1, cargo_count);
|
||||
SetDParam(2, u->cargo.Source());
|
||||
DrawString(left, right, y + 2 * FONT_HEIGHT_NORMAL + 1 + y_offset, STR_VEHICLE_DETAILS_CARGO_FROM);
|
||||
feeder_share += u->cargo.FeederShare();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetDParam(0, feeder_share);
|
||||
DrawString(left, right, y + 3 * FONT_HEIGHT_NORMAL + 3 + y_offset, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draws an image of an aircraft
|
||||
* @param v Front vehicle
|
||||
* @param left The minimum horizontal position
|
||||
* @param right The maximum horizontal position
|
||||
* @param y Vertical position to draw at
|
||||
* @param selection Selected vehicle to draw a frame around
|
||||
*/
|
||||
void DrawAircraftImage(const Vehicle *v, int left, int right, int y, VehicleID selection, EngineImageType image_type)
|
||||
{
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
|
||||
SpriteID sprite = v->GetImage(rtl ? DIR_E : DIR_W, image_type);
|
||||
const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
|
||||
|
||||
int width = UnScaleGUI(real_sprite->width);
|
||||
int x_offs = UnScaleGUI(real_sprite->x_offs);
|
||||
int x = rtl ? right - width - x_offs : left - x_offs;
|
||||
bool helicopter = v->subtype == AIR_HELICOPTER;
|
||||
|
||||
int y_offs = ScaleGUITrad(10);
|
||||
int heli_offs = 0;
|
||||
|
||||
PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
|
||||
DrawSprite(sprite, pal, x, y + y_offs);
|
||||
if (helicopter) {
|
||||
const Aircraft *a = Aircraft::From(v);
|
||||
SpriteID rotor_sprite = GetCustomRotorSprite(a, true, image_type);
|
||||
if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
|
||||
heli_offs = ScaleGUITrad(5);
|
||||
DrawSprite(rotor_sprite, PAL_NONE, x, y + y_offs - heli_offs);
|
||||
}
|
||||
if (v->index == selection) {
|
||||
x += x_offs;
|
||||
y += UnScaleGUI(real_sprite->y_offs) + y_offs - heli_offs;
|
||||
DrawFrameRect(x - 1, y - 1, x + width + 1, y + UnScaleGUI(real_sprite->height) + heli_offs + 1, COLOUR_WHITE, FR_BORDERONLY);
|
||||
}
|
||||
}
|
||||
235
src/airport.cpp
Normal file
235
src/airport.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file airport.cpp Functions related to airports. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "station_base.h"
|
||||
#include "table/strings.h"
|
||||
#include "table/airport_movement.h"
|
||||
#include "table/airporttile_ids.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
||||
/**
|
||||
* Define a generic airport.
|
||||
* @param name Suffix of the names of the airport data.
|
||||
* @param terminals The terminals.
|
||||
* @param num_helipads Number of heli pads.
|
||||
* @param flags Information about the class of FTA.
|
||||
* @param delta_z Height of the airport above the land.
|
||||
*/
|
||||
#define AIRPORT_GENERIC(name, terminals, num_helipads, flags, delta_z) \
|
||||
static AirportFTAClass _airportfta_ ## name(_airport_moving_data_ ## name, terminals, \
|
||||
num_helipads, _airport_entries_ ## name, flags, _airport_fta_ ## name, delta_z);
|
||||
|
||||
/**
|
||||
* Define an airport.
|
||||
* @param name Suffix of the names of the airport data.
|
||||
* @param num_helipads Number of heli pads.
|
||||
* @param short_strip Airport has a short land/take-off strip.
|
||||
*/
|
||||
#define AIRPORT(name, num_helipads, short_strip) \
|
||||
AIRPORT_GENERIC(name, _airport_terminal_ ## name, num_helipads, AirportFTAClass::ALL | (short_strip ? AirportFTAClass::SHORT_STRIP : (AirportFTAClass::Flags)0), 0)
|
||||
|
||||
/**
|
||||
* Define a heliport.
|
||||
* @param name Suffix of the names of the helipad data.
|
||||
* @param num_helipads Number of heli pads.
|
||||
* @param delta_z Height of the airport above the land.
|
||||
*/
|
||||
#define HELIPORT(name, num_helipads, delta_z) \
|
||||
AIRPORT_GENERIC(name, NULL, num_helipads, AirportFTAClass::HELICOPTERS, delta_z)
|
||||
|
||||
AIRPORT(country, 0, true)
|
||||
AIRPORT(city, 0, false)
|
||||
HELIPORT(heliport, 1, 60)
|
||||
AIRPORT(metropolitan, 0, false)
|
||||
AIRPORT(international, 2, false)
|
||||
AIRPORT(commuter, 2, true)
|
||||
HELIPORT(helidepot, 1, 0)
|
||||
AIRPORT(intercontinental, 2, false)
|
||||
HELIPORT(helistation, 3, 0)
|
||||
HELIPORT(oilrig, 1, 54)
|
||||
AIRPORT_GENERIC(dummy, NULL, 0, AirportFTAClass::ALL, 0)
|
||||
|
||||
#undef HELIPORT
|
||||
#undef AIRPORT
|
||||
#undef AIRPORT_GENERIC
|
||||
|
||||
#include "table/airport_defaults.h"
|
||||
|
||||
|
||||
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA);
|
||||
static AirportFTA *AirportBuildAutomata(uint nofelements, const AirportFTAbuildup *apFA);
|
||||
|
||||
|
||||
/**
|
||||
* Rotate the airport moving data to another rotation.
|
||||
* @param orig Pointer to the moving data to rotate.
|
||||
* @param rotation How to rotate the moving data.
|
||||
* @param num_tiles_x Number of tiles in x direction.
|
||||
* @param num_tiles_y Number of tiles in y direction.
|
||||
* @return The rotated moving data.
|
||||
*/
|
||||
AirportMovingData RotateAirportMovingData(const AirportMovingData *orig, Direction rotation, uint num_tiles_x, uint num_tiles_y)
|
||||
{
|
||||
AirportMovingData amd;
|
||||
amd.flag = orig->flag;
|
||||
amd.direction = ChangeDir(orig->direction, (DirDiff)rotation);
|
||||
switch (rotation) {
|
||||
case DIR_N:
|
||||
amd.x = orig->x;
|
||||
amd.y = orig->y;
|
||||
break;
|
||||
|
||||
case DIR_E:
|
||||
amd.x = orig->y;
|
||||
amd.y = num_tiles_y * TILE_SIZE - orig->x - 1;
|
||||
break;
|
||||
|
||||
case DIR_S:
|
||||
amd.x = num_tiles_x * TILE_SIZE - orig->x - 1;
|
||||
amd.y = num_tiles_y * TILE_SIZE - orig->y - 1;
|
||||
break;
|
||||
|
||||
case DIR_W:
|
||||
amd.x = num_tiles_x * TILE_SIZE - orig->y - 1;
|
||||
amd.y = orig->x;
|
||||
break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
return amd;
|
||||
}
|
||||
|
||||
AirportFTAClass::AirportFTAClass(
|
||||
const AirportMovingData *moving_data_,
|
||||
const byte *terminals_,
|
||||
const byte num_helipads_,
|
||||
const byte *entry_points_,
|
||||
Flags flags_,
|
||||
const AirportFTAbuildup *apFA,
|
||||
byte delta_z_
|
||||
) :
|
||||
moving_data(moving_data_),
|
||||
terminals(terminals_),
|
||||
num_helipads(num_helipads_),
|
||||
flags(flags_),
|
||||
nofelements(AirportGetNofElements(apFA)),
|
||||
entry_points(entry_points_),
|
||||
delta_z(delta_z_)
|
||||
{
|
||||
/* Build the state machine itself */
|
||||
this->layout = AirportBuildAutomata(this->nofelements, apFA);
|
||||
}
|
||||
|
||||
AirportFTAClass::~AirportFTAClass()
|
||||
{
|
||||
for (uint i = 0; i < nofelements; i++) {
|
||||
AirportFTA *current = layout[i].next;
|
||||
while (current != NULL) {
|
||||
AirportFTA *next = current->next;
|
||||
free(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
free(layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of elements of a source Airport state automata
|
||||
* Since it is actually just a big array of AirportFTA types, we only
|
||||
* know one element from the other by differing 'position' identifiers
|
||||
*/
|
||||
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA)
|
||||
{
|
||||
uint16 nofelements = 0;
|
||||
int temp = apFA[0].position;
|
||||
|
||||
for (uint i = 0; i < MAX_ELEMENTS; i++) {
|
||||
if (temp != apFA[i].position) {
|
||||
nofelements++;
|
||||
temp = apFA[i].position;
|
||||
}
|
||||
if (apFA[i].position == MAX_ELEMENTS) break;
|
||||
}
|
||||
return nofelements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the FTA given a description.
|
||||
* @param nofelements The number of elements in the FTA.
|
||||
* @param apFA The description of the FTA.
|
||||
* @return The FTA describing the airport.
|
||||
*/
|
||||
static AirportFTA *AirportBuildAutomata(uint nofelements, const AirportFTAbuildup *apFA)
|
||||
{
|
||||
AirportFTA *FAutomata = MallocT<AirportFTA>(nofelements);
|
||||
uint16 internalcounter = 0;
|
||||
|
||||
for (uint i = 0; i < nofelements; i++) {
|
||||
AirportFTA *current = &FAutomata[i];
|
||||
current->position = apFA[internalcounter].position;
|
||||
current->heading = apFA[internalcounter].heading;
|
||||
current->block = apFA[internalcounter].block;
|
||||
current->next_position = apFA[internalcounter].next;
|
||||
|
||||
/* outgoing nodes from the same position, create linked list */
|
||||
while (current->position == apFA[internalcounter + 1].position) {
|
||||
AirportFTA *newNode = MallocT<AirportFTA>(1);
|
||||
|
||||
newNode->position = apFA[internalcounter + 1].position;
|
||||
newNode->heading = apFA[internalcounter + 1].heading;
|
||||
newNode->block = apFA[internalcounter + 1].block;
|
||||
newNode->next_position = apFA[internalcounter + 1].next;
|
||||
/* create link */
|
||||
current->next = newNode;
|
||||
current = current->next;
|
||||
internalcounter++;
|
||||
}
|
||||
current->next = NULL;
|
||||
internalcounter++;
|
||||
}
|
||||
return FAutomata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finite state machine of an airport type.
|
||||
* @param airport_type %Airport type to query FTA from. @see AirportTypes
|
||||
* @return Finite state machine of the airport.
|
||||
*/
|
||||
const AirportFTAClass *GetAirport(const byte airport_type)
|
||||
{
|
||||
if (airport_type == AT_DUMMY) return &_airportfta_dummy;
|
||||
return AirportSpec::Get(airport_type)->fsm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vehicle position when an aircraft is build at the given tile
|
||||
* @param hangar_tile The tile on which the vehicle is build
|
||||
* @return The position (index in airport node array) where the aircraft ends up
|
||||
*/
|
||||
byte GetVehiclePosOnBuild(TileIndex hangar_tile)
|
||||
{
|
||||
const Station *st = Station::GetByTile(hangar_tile);
|
||||
const AirportFTAClass *apc = st->airport.GetFTA();
|
||||
/* When we click on hangar we know the tile it is on. By that we know
|
||||
* its position in the array of depots the airport has.....we can search
|
||||
* layout for #th position of depot. Since layout must start with a listing
|
||||
* of all depots, it is simple */
|
||||
for (uint i = 0;; i++) {
|
||||
if (st->airport.GetHangarTile(i) == hangar_tile) {
|
||||
assert(apc->layout[i].heading == HANGAR);
|
||||
return apc->layout[i].position;
|
||||
}
|
||||
}
|
||||
NOT_REACHED();
|
||||
}
|
||||
202
src/airport.h
Normal file
202
src/airport.h
Normal file
@@ -0,0 +1,202 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file airport.h Various declarations for airports */
|
||||
|
||||
#ifndef AIRPORT_H
|
||||
#define AIRPORT_H
|
||||
|
||||
#include "direction_type.h"
|
||||
#include "tile_type.h"
|
||||
|
||||
/** Some airport-related constants */
|
||||
static const uint MAX_TERMINALS = 8; ///< maximum number of terminals per airport
|
||||
static const uint MAX_HELIPADS = 3; ///< maximum number of helipads per airport
|
||||
static const uint MAX_ELEMENTS = 255; ///< maximum number of aircraft positions at airport
|
||||
|
||||
static const uint NUM_AIRPORTTILES_PER_GRF = 255; ///< Number of airport tiles per NewGRF; limited to 255 to allow extending Action3 with an extended byte later on.
|
||||
|
||||
static const uint NUM_AIRPORTTILES = 256; ///< Total number of airport tiles.
|
||||
static const uint NEW_AIRPORTTILE_OFFSET = 74; ///< offset of first newgrf airport tile
|
||||
static const uint INVALID_AIRPORTTILE = NUM_AIRPORTTILES; ///< id for an invalid airport tile
|
||||
|
||||
/** Airport types */
|
||||
enum AirportTypes {
|
||||
AT_SMALL = 0, ///< Small airport.
|
||||
AT_LARGE = 1, ///< Large airport.
|
||||
AT_HELIPORT = 2, ///< Heli port.
|
||||
AT_METROPOLITAN = 3, ///< Metropolitan airport.
|
||||
AT_INTERNATIONAL = 4, ///< International airport.
|
||||
AT_COMMUTER = 5, ///< Commuter airport.
|
||||
AT_HELIDEPOT = 6, ///< Heli depot.
|
||||
AT_INTERCON = 7, ///< Intercontinental airport.
|
||||
AT_HELISTATION = 8, ///< Heli station airport.
|
||||
AT_OILRIG = 9, ///< Oilrig airport.
|
||||
NEW_AIRPORT_OFFSET = 10, ///< Number of the first newgrf airport.
|
||||
NUM_AIRPORTS_PER_GRF = 128, ///< Maximal number of airports per NewGRF.
|
||||
NUM_AIRPORTS = 128, ///< Maximal number of airports in total.
|
||||
AT_INVALID = 254, ///< Invalid airport.
|
||||
AT_DUMMY = 255, ///< Dummy airport.
|
||||
};
|
||||
|
||||
/** Flags for airport movement data. */
|
||||
enum AirportMovingDataFlags {
|
||||
AMED_NOSPDCLAMP = 1 << 0, ///< No speed restrictions.
|
||||
AMED_TAKEOFF = 1 << 1, ///< Takeoff movement.
|
||||
AMED_SLOWTURN = 1 << 2, ///< Turn slowly (mostly used in the air).
|
||||
AMED_LAND = 1 << 3, ///< Landing onto landing strip.
|
||||
AMED_EXACTPOS = 1 << 4, ///< Go exactly to the destination coordinates.
|
||||
AMED_BRAKE = 1 << 5, ///< Taxiing at the airport.
|
||||
AMED_HELI_RAISE = 1 << 6, ///< Helicopter take-off.
|
||||
AMED_HELI_LOWER = 1 << 7, ///< Helicopter landing.
|
||||
AMED_HOLD = 1 << 8, ///< Holding pattern movement (above the airport).
|
||||
};
|
||||
|
||||
/** Movement States on Airports (headings target) */
|
||||
enum AirportMovementStates {
|
||||
TO_ALL = 0, ///< Go in this direction for every target.
|
||||
HANGAR = 1, ///< Heading for hangar.
|
||||
TERM1 = 2, ///< Heading for terminal 1.
|
||||
TERM2 = 3, ///< Heading for terminal 2.
|
||||
TERM3 = 4, ///< Heading for terminal 3.
|
||||
TERM4 = 5, ///< Heading for terminal 4.
|
||||
TERM5 = 6, ///< Heading for terminal 5.
|
||||
TERM6 = 7, ///< Heading for terminal 6.
|
||||
HELIPAD1 = 8, ///< Heading for helipad 1.
|
||||
HELIPAD2 = 9, ///< Heading for helipad 2.
|
||||
TAKEOFF = 10, ///< Airplane wants to leave the airport.
|
||||
STARTTAKEOFF = 11, ///< Airplane has arrived at a runway for take-off.
|
||||
ENDTAKEOFF = 12, ///< Airplane has reached end-point of the take-off runway.
|
||||
HELITAKEOFF = 13, ///< Helicopter wants to leave the airport.
|
||||
FLYING = 14, ///< %Vehicle is flying in the air.
|
||||
LANDING = 15, ///< Airplane wants to land.
|
||||
ENDLANDING = 16, ///< Airplane wants to finish landing.
|
||||
HELILANDING = 17, ///< Helicopter wants to land.
|
||||
HELIENDLANDING = 18, ///< Helicopter wants to finish landing.
|
||||
TERM7 = 19, ///< Heading for terminal 7.
|
||||
TERM8 = 20, ///< Heading for terminal 8.
|
||||
HELIPAD3 = 21, ///< Heading for helipad 3.
|
||||
MAX_HEADINGS = 21, ///< Last valid target to head for.
|
||||
};
|
||||
|
||||
/** Movement Blocks on Airports blocks (eg_airport_flags). */
|
||||
static const uint64
|
||||
TERM1_block = 1ULL << 0, ///< Block belonging to terminal 1.
|
||||
TERM2_block = 1ULL << 1, ///< Block belonging to terminal 2.
|
||||
TERM3_block = 1ULL << 2, ///< Block belonging to terminal 3.
|
||||
TERM4_block = 1ULL << 3, ///< Block belonging to terminal 4.
|
||||
TERM5_block = 1ULL << 4, ///< Block belonging to terminal 5.
|
||||
TERM6_block = 1ULL << 5, ///< Block belonging to terminal 6.
|
||||
HELIPAD1_block = 1ULL << 6, ///< Block belonging to helipad 1.
|
||||
HELIPAD2_block = 1ULL << 7, ///< Block belonging to helipad 2.
|
||||
RUNWAY_IN_OUT_block = 1ULL << 8,
|
||||
RUNWAY_IN_block = 1ULL << 8,
|
||||
AIRPORT_BUSY_block = 1ULL << 8,
|
||||
RUNWAY_OUT_block = 1ULL << 9,
|
||||
TAXIWAY_BUSY_block = 1ULL << 10,
|
||||
OUT_WAY_block = 1ULL << 11,
|
||||
IN_WAY_block = 1ULL << 12,
|
||||
AIRPORT_ENTRANCE_block = 1ULL << 13,
|
||||
TERM_GROUP1_block = 1ULL << 14,
|
||||
TERM_GROUP2_block = 1ULL << 15,
|
||||
HANGAR2_AREA_block = 1ULL << 16,
|
||||
TERM_GROUP2_ENTER1_block = 1ULL << 17,
|
||||
TERM_GROUP2_ENTER2_block = 1ULL << 18,
|
||||
TERM_GROUP2_EXIT1_block = 1ULL << 19,
|
||||
TERM_GROUP2_EXIT2_block = 1ULL << 20,
|
||||
PRE_HELIPAD_block = 1ULL << 21,
|
||||
|
||||
/* blocks for new airports */
|
||||
TERM7_block = 1ULL << 22, ///< Block belonging to terminal 7.
|
||||
TERM8_block = 1ULL << 23, ///< Block belonging to terminal 8.
|
||||
HELIPAD3_block = 1ULL << 24, ///< Block belonging to helipad 3.
|
||||
HANGAR1_AREA_block = 1ULL << 26,
|
||||
OUT_WAY2_block = 1ULL << 27,
|
||||
IN_WAY2_block = 1ULL << 28,
|
||||
RUNWAY_IN2_block = 1ULL << 29,
|
||||
RUNWAY_OUT2_block = 1ULL << 10, ///< @note re-uses #TAXIWAY_BUSY_block
|
||||
HELIPAD_GROUP_block = 1ULL << 13, ///< @note re-uses #AIRPORT_ENTRANCE_block
|
||||
OUT_WAY_block2 = 1ULL << 31,
|
||||
/* end of new blocks */
|
||||
|
||||
NOTHING_block = 1ULL << 30,
|
||||
AIRPORT_CLOSED_block = 1ULL << 63; ///< Dummy block for indicating a closed airport.
|
||||
|
||||
/** A single location on an airport where aircraft can move to. */
|
||||
struct AirportMovingData {
|
||||
int16 x; ///< x-coordinate of the destination.
|
||||
int16 y; ///< y-coordinate of the destination.
|
||||
uint16 flag; ///< special flags when moving towards the destination.
|
||||
DirectionByte direction; ///< Direction to turn the aircraft after reaching the destination.
|
||||
};
|
||||
|
||||
AirportMovingData RotateAirportMovingData(const AirportMovingData *orig, Direction rotation, uint num_tiles_x, uint num_tiles_y);
|
||||
|
||||
struct AirportFTAbuildup;
|
||||
|
||||
/** Finite sTate mAchine (FTA) of an airport. */
|
||||
struct AirportFTAClass {
|
||||
public:
|
||||
/** Bitmask of airport flags. */
|
||||
enum Flags {
|
||||
AIRPLANES = 0x1, ///< Can planes land on this airport type?
|
||||
HELICOPTERS = 0x2, ///< Can helicopters land on this airport type?
|
||||
ALL = AIRPLANES | HELICOPTERS, ///< Mask to check for both planes and helicopters.
|
||||
SHORT_STRIP = 0x4, ///< This airport has a short landing strip, dangerous for fast aircraft.
|
||||
};
|
||||
|
||||
AirportFTAClass(
|
||||
const AirportMovingData *moving_data,
|
||||
const byte *terminals,
|
||||
const byte num_helipads,
|
||||
const byte *entry_points,
|
||||
Flags flags,
|
||||
const AirportFTAbuildup *apFA,
|
||||
byte delta_z
|
||||
);
|
||||
|
||||
~AirportFTAClass();
|
||||
|
||||
/**
|
||||
* Get movement data at a position.
|
||||
* @param position Element number to get movement data about.
|
||||
* @return Pointer to the movement data.
|
||||
*/
|
||||
const AirportMovingData *MovingData(byte position) const
|
||||
{
|
||||
assert(position < nofelements);
|
||||
return &moving_data[position];
|
||||
}
|
||||
|
||||
const AirportMovingData *moving_data; ///< Movement data.
|
||||
struct AirportFTA *layout; ///< state machine for airport
|
||||
const byte *terminals; ///< %Array with the number of terminal groups, followed by the number of terminals in each group.
|
||||
const byte num_helipads; ///< Number of helipads on this airport. When 0 helicopters will go to normal terminals.
|
||||
Flags flags; ///< Flags for this airport type.
|
||||
byte nofelements; ///< number of positions the airport consists of
|
||||
const byte *entry_points; ///< when an airplane arrives at this airport, enter it at position entry_point, index depends on direction
|
||||
byte delta_z; ///< Z adjustment for helicopter pads
|
||||
};
|
||||
|
||||
DECLARE_ENUM_AS_BIT_SET(AirportFTAClass::Flags)
|
||||
|
||||
|
||||
/** Internal structure used in openttd - Finite sTate mAchine --> FTA */
|
||||
struct AirportFTA {
|
||||
AirportFTA *next; ///< possible extra movement choices from this position
|
||||
uint64 block; ///< 64 bit blocks (st->airport.flags), should be enough for the most complex airports
|
||||
byte position; ///< the position that an airplane is at
|
||||
byte next_position; ///< next position from this position
|
||||
byte heading; ///< heading (current orders), guiding an airplane to its target on an airport
|
||||
};
|
||||
|
||||
const AirportFTAClass *GetAirport(const byte airport_type);
|
||||
byte GetVehiclePosOnBuild(TileIndex hangar_tile);
|
||||
|
||||
#endif /* AIRPORT_H */
|
||||
605
src/airport_gui.cpp
Normal file
605
src/airport_gui.cpp
Normal file
@@ -0,0 +1,605 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file airport_gui.cpp The GUI for airports. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "window_gui.h"
|
||||
#include "station_gui.h"
|
||||
#include "terraform_gui.h"
|
||||
#include "sound_func.h"
|
||||
#include "window_func.h"
|
||||
#include "strings_func.h"
|
||||
#include "viewport_func.h"
|
||||
#include "company_func.h"
|
||||
#include "tilehighlight_func.h"
|
||||
#include "company_base.h"
|
||||
#include "station_type.h"
|
||||
#include "newgrf_airport.h"
|
||||
#include "newgrf_callbacks.h"
|
||||
#include "widgets/dropdown_type.h"
|
||||
#include "core/geometry_func.hpp"
|
||||
#include "hotkeys.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "gui.h"
|
||||
|
||||
#include "widgets/airport_widget.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
||||
static AirportClassID _selected_airport_class; ///< the currently visible airport class
|
||||
static int _selected_airport_index; ///< the index of the selected airport in the current class or -1
|
||||
static byte _selected_airport_layout; ///< selected airport layout number.
|
||||
|
||||
static void ShowBuildAirportPicker(Window *parent);
|
||||
|
||||
SpriteID GetCustomAirportSprite(const AirportSpec *as, byte layout);
|
||||
|
||||
void CcBuildAirport(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (result.Failed()) return;
|
||||
|
||||
if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_SPLAT_OTHER, tile);
|
||||
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Place an airport.
|
||||
* @param tile Position to put the new airport.
|
||||
*/
|
||||
static void PlaceAirport(TileIndex tile)
|
||||
{
|
||||
if (_selected_airport_index == -1) return;
|
||||
uint32 p2 = _ctrl_pressed;
|
||||
SB(p2, 16, 16, INVALID_STATION); // no station to join
|
||||
|
||||
uint32 p1 = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex();
|
||||
p1 |= _selected_airport_layout << 8;
|
||||
CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE), CcBuildAirport, "" };
|
||||
ShowSelectStationIfNeeded(cmdcont, TileArea(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE));
|
||||
}
|
||||
|
||||
/** Airport build toolbar window handler. */
|
||||
struct BuildAirToolbarWindow : Window {
|
||||
int last_user_action; // Last started user action.
|
||||
|
||||
BuildAirToolbarWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
|
||||
{
|
||||
this->InitNested(window_number);
|
||||
if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this);
|
||||
this->last_user_action = WIDGET_LIST_END;
|
||||
}
|
||||
|
||||
~BuildAirToolbarWindow()
|
||||
{
|
||||
if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some data on this window has become invalid.
|
||||
* @param data Information about the changed data.
|
||||
* @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
|
||||
*/
|
||||
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
|
||||
{
|
||||
if (!gui_scope) return;
|
||||
|
||||
if (!CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) delete this;
|
||||
}
|
||||
|
||||
virtual void OnClick(Point pt, int widget, int click_count)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_AT_AIRPORT:
|
||||
if (HandlePlacePushButton(this, WID_AT_AIRPORT, SPR_CURSOR_AIRPORT, HT_RECT)) {
|
||||
ShowBuildAirportPicker(this);
|
||||
this->last_user_action = widget;
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_AT_DEMOLISH:
|
||||
HandlePlacePushButton(this, WID_AT_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT | HT_DIAGONAL);
|
||||
this->last_user_action = widget;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlaceObject(Point pt, TileIndex tile)
|
||||
{
|
||||
switch (this->last_user_action) {
|
||||
case WID_AT_AIRPORT:
|
||||
PlaceAirport(tile);
|
||||
break;
|
||||
|
||||
case WID_AT_DEMOLISH:
|
||||
PlaceProc_DemolishArea(tile);
|
||||
break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt)
|
||||
{
|
||||
VpSelectTilesWithMethod(pt.x, pt.y, select_method);
|
||||
}
|
||||
|
||||
virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
|
||||
{
|
||||
if (pt.x != -1 && select_proc == DDSP_DEMOLISH_AREA) {
|
||||
GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnPlaceObjectAbort()
|
||||
{
|
||||
this->RaiseButtons();
|
||||
|
||||
DeleteWindowById(WC_BUILD_STATION, TRANSPORT_AIR);
|
||||
DeleteWindowById(WC_SELECT_STATION, 0);
|
||||
}
|
||||
|
||||
static HotkeyList hotkeys;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for global hotkeys of the BuildAirToolbarWindow.
|
||||
* @param hotkey Hotkey
|
||||
* @return ES_HANDLED if hotkey was accepted.
|
||||
*/
|
||||
static EventState AirportToolbarGlobalHotkeys(int hotkey)
|
||||
{
|
||||
if (_game_mode != GM_NORMAL || !CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) return ES_NOT_HANDLED;
|
||||
Window *w = ShowBuildAirToolbar();
|
||||
if (w == NULL) return ES_NOT_HANDLED;
|
||||
return w->OnHotkey(hotkey);
|
||||
}
|
||||
|
||||
static Hotkey airtoolbar_hotkeys[] = {
|
||||
Hotkey('1', "airport", WID_AT_AIRPORT),
|
||||
Hotkey('2', "demolish", WID_AT_DEMOLISH),
|
||||
HOTKEY_LIST_END
|
||||
};
|
||||
HotkeyList BuildAirToolbarWindow::hotkeys("airtoolbar", airtoolbar_hotkeys, AirportToolbarGlobalHotkeys);
|
||||
|
||||
static const NWidgetPart _nested_air_toolbar_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_AIRPORT), SetFill(0, 1), SetMinimalSize(42, 22), SetDataTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetFill(1, 1), EndContainer(),
|
||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
static WindowDesc _air_toolbar_desc(
|
||||
WDP_ALIGN_TOOLBAR, "toolbar_air", 0, 0,
|
||||
WC_BUILD_TOOLBAR, WC_NONE,
|
||||
WDF_CONSTRUCTION,
|
||||
_nested_air_toolbar_widgets, lengthof(_nested_air_toolbar_widgets),
|
||||
&BuildAirToolbarWindow::hotkeys
|
||||
);
|
||||
|
||||
/**
|
||||
* Open the build airport toolbar window
|
||||
*
|
||||
* If the terraform toolbar is linked to the toolbar, that window is also opened.
|
||||
*
|
||||
* @return newly opened airport toolbar, or NULL if the toolbar could not be opened.
|
||||
*/
|
||||
Window *ShowBuildAirToolbar()
|
||||
{
|
||||
if (!Company::IsValidID(_local_company)) return NULL;
|
||||
|
||||
DeleteWindowByClass(WC_BUILD_TOOLBAR);
|
||||
return AllocateWindowDescFront<BuildAirToolbarWindow>(&_air_toolbar_desc, TRANSPORT_AIR);
|
||||
}
|
||||
|
||||
class BuildAirportWindow : public PickerWindowBase {
|
||||
SpriteID preview_sprite; ///< Cached airport preview sprite.
|
||||
int line_height;
|
||||
Scrollbar *vscroll;
|
||||
|
||||
/** Build a dropdown list of available airport classes */
|
||||
static DropDownList *BuildAirportClassDropDown()
|
||||
{
|
||||
DropDownList *list = new DropDownList();
|
||||
|
||||
for (uint i = 0; i < AirportClass::GetClassCount(); i++) {
|
||||
*list->Append() = new DropDownListStringItem(AirportClass::Get((AirportClassID)i)->name, i, false);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public:
|
||||
BuildAirportWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent)
|
||||
{
|
||||
this->CreateNestedTree();
|
||||
|
||||
this->vscroll = this->GetScrollbar(WID_AP_SCROLLBAR);
|
||||
this->vscroll->SetCapacity(5);
|
||||
this->vscroll->SetPosition(0);
|
||||
|
||||
this->FinishInitNested(TRANSPORT_AIR);
|
||||
|
||||
this->SetWidgetLoweredState(WID_AP_BTN_DONTHILIGHT, !_settings_client.gui.station_show_coverage);
|
||||
this->SetWidgetLoweredState(WID_AP_BTN_DOHILIGHT, _settings_client.gui.station_show_coverage);
|
||||
this->OnInvalidateData();
|
||||
|
||||
/* Ensure airport class is valid (changing NewGRFs). */
|
||||
_selected_airport_class = Clamp(_selected_airport_class, APC_BEGIN, (AirportClassID)(AirportClass::GetClassCount() - 1));
|
||||
const AirportClass *ac = AirportClass::Get(_selected_airport_class);
|
||||
this->vscroll->SetCount(ac->GetSpecCount());
|
||||
|
||||
/* Ensure the airport index is valid for this class (changing NewGRFs). */
|
||||
_selected_airport_index = Clamp(_selected_airport_index, -1, ac->GetSpecCount() - 1);
|
||||
|
||||
/* Only when no valid airport was selected, we want to select the first airport. */
|
||||
bool selectFirstAirport = true;
|
||||
if (_selected_airport_index != -1) {
|
||||
const AirportSpec *as = ac->GetSpec(_selected_airport_index);
|
||||
if (as->IsAvailable()) {
|
||||
/* Ensure the airport layout is valid. */
|
||||
_selected_airport_layout = Clamp(_selected_airport_layout, 0, as->num_table - 1);
|
||||
selectFirstAirport = false;
|
||||
this->UpdateSelectSize();
|
||||
}
|
||||
}
|
||||
|
||||
if (selectFirstAirport) this->SelectFirstAvailableAirport(true);
|
||||
}
|
||||
|
||||
virtual ~BuildAirportWindow()
|
||||
{
|
||||
DeleteWindowById(WC_SELECT_STATION, 0);
|
||||
}
|
||||
|
||||
virtual void SetStringParameters(int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_AP_CLASS_DROPDOWN:
|
||||
SetDParam(0, AirportClass::Get(_selected_airport_class)->name);
|
||||
break;
|
||||
|
||||
case WID_AP_LAYOUT_NUM:
|
||||
SetDParam(0, STR_EMPTY);
|
||||
if (_selected_airport_index != -1) {
|
||||
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
|
||||
StringID string = GetAirportTextCallback(as, _selected_airport_layout, CBID_AIRPORT_LAYOUT_NAME);
|
||||
if (string != STR_UNDEFINED) {
|
||||
SetDParam(0, string);
|
||||
} else if (as->num_table > 1) {
|
||||
SetDParam(0, STR_STATION_BUILD_AIRPORT_LAYOUT_NAME);
|
||||
SetDParam(1, _selected_airport_layout + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_AP_CLASS_DROPDOWN: {
|
||||
Dimension d = {0, 0};
|
||||
for (uint i = 0; i < AirportClass::GetClassCount(); i++) {
|
||||
SetDParam(0, AirportClass::Get((AirportClassID)i)->name);
|
||||
d = maxdim(d, GetStringBoundingBox(STR_BLACK_STRING));
|
||||
}
|
||||
d.width += padding.width;
|
||||
d.height += padding.height;
|
||||
*size = maxdim(*size, d);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_AP_AIRPORT_LIST: {
|
||||
for (int i = 0; i < NUM_AIRPORTS; i++) {
|
||||
const AirportSpec *as = AirportSpec::Get(i);
|
||||
if (!as->enabled) continue;
|
||||
|
||||
size->width = max(size->width, GetStringBoundingBox(as->name).width);
|
||||
}
|
||||
|
||||
this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
|
||||
size->height = 5 * this->line_height;
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_AP_AIRPORT_SPRITE:
|
||||
for (int i = 0; i < NUM_AIRPORTS; i++) {
|
||||
const AirportSpec *as = AirportSpec::Get(i);
|
||||
if (!as->enabled) continue;
|
||||
for (byte layout = 0; layout < as->num_table; layout++) {
|
||||
SpriteID sprite = GetCustomAirportSprite(as, layout);
|
||||
if (sprite != 0) {
|
||||
Dimension d = GetSpriteSize(sprite);
|
||||
d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
|
||||
d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
|
||||
*size = maxdim(d, *size);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_AP_EXTRA_TEXT:
|
||||
for (int i = NEW_AIRPORT_OFFSET; i < NUM_AIRPORTS; i++) {
|
||||
const AirportSpec *as = AirportSpec::Get(i);
|
||||
if (!as->enabled) continue;
|
||||
for (byte layout = 0; layout < as->num_table; layout++) {
|
||||
StringID string = GetAirportTextCallback(as, layout, CBID_AIRPORT_ADDITIONAL_TEXT);
|
||||
if (string == STR_UNDEFINED) continue;
|
||||
|
||||
/* STR_BLACK_STRING is used to start the string with {BLACK} */
|
||||
SetDParam(0, string);
|
||||
Dimension d = GetStringMultiLineBoundingBox(STR_BLACK_STRING, *size);
|
||||
*size = maxdim(d, *size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_AP_AIRPORT_LIST: {
|
||||
int y = r.top;
|
||||
AirportClass *apclass = AirportClass::Get(_selected_airport_class);
|
||||
for (uint i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < apclass->GetSpecCount(); i++) {
|
||||
const AirportSpec *as = apclass->GetSpec(i);
|
||||
if (!as->IsAvailable()) {
|
||||
GfxFillRect(r.left + 1, y + 1, r.right - 1, y + this->line_height - 2, PC_BLACK, FILLRECT_CHECKER);
|
||||
}
|
||||
DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y + WD_MATRIX_TOP, as->name, ((int)i == _selected_airport_index) ? TC_WHITE : TC_BLACK);
|
||||
y += this->line_height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_AP_AIRPORT_SPRITE:
|
||||
if (this->preview_sprite != 0) {
|
||||
Dimension d = GetSpriteSize(this->preview_sprite);
|
||||
DrawSprite(this->preview_sprite, COMPANY_SPRITE_COLOUR(_local_company), (r.left + r.right - d.width) / 2, (r.top + r.bottom - d.height) / 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_AP_EXTRA_TEXT:
|
||||
if (_selected_airport_index != -1) {
|
||||
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
|
||||
StringID string = GetAirportTextCallback(as, _selected_airport_layout, CBID_AIRPORT_ADDITIONAL_TEXT);
|
||||
if (string != STR_UNDEFINED) {
|
||||
SetDParam(0, string);
|
||||
DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_BLACK_STRING);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnPaint()
|
||||
{
|
||||
this->DrawWidgets();
|
||||
|
||||
uint16 top = this->GetWidget<NWidgetBase>(WID_AP_BTN_DOHILIGHT)->pos_y + this->GetWidget<NWidgetBase>(WID_AP_BTN_DOHILIGHT)->current_y + WD_PAR_VSEP_NORMAL;
|
||||
NWidgetBase *panel_nwi = this->GetWidget<NWidgetBase>(WID_AP_BOTTOMPANEL);
|
||||
|
||||
int right = panel_nwi->pos_x + panel_nwi->current_x;
|
||||
int bottom = panel_nwi->pos_y + panel_nwi->current_y;
|
||||
|
||||
if (_selected_airport_index != -1) {
|
||||
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
|
||||
int rad = _settings_game.station.modified_catchment ? as->catchment : (uint)CA_UNMODIFIED;
|
||||
|
||||
/* only show the station (airport) noise, if the noise option is activated */
|
||||
if (_settings_game.economy.station_noise_level) {
|
||||
/* show the noise of the selected airport */
|
||||
SetDParam(0, as->noise_level);
|
||||
DrawString(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, STR_STATION_BUILD_NOISE);
|
||||
top += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
|
||||
}
|
||||
|
||||
/* strings such as 'Size' and 'Coverage Area' */
|
||||
top = DrawStationCoverageAreaText(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, SCT_ALL, rad, false) + WD_PAR_VSEP_NORMAL;
|
||||
top = DrawStationCoverageAreaText(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, SCT_ALL, rad, true) + WD_PAR_VSEP_NORMAL;
|
||||
}
|
||||
|
||||
/* Resize background if the window is too small.
|
||||
* Never make the window smaller to avoid oscillating if the size change affects the acceptance.
|
||||
* (This is the case, if making the window bigger moves the mouse into the window.) */
|
||||
if (top > bottom) {
|
||||
ResizeWindow(this, 0, top - bottom, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectOtherAirport(int airport_index)
|
||||
{
|
||||
_selected_airport_index = airport_index;
|
||||
_selected_airport_layout = 0;
|
||||
|
||||
this->UpdateSelectSize();
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
void UpdateSelectSize()
|
||||
{
|
||||
if (_selected_airport_index == -1) {
|
||||
SetTileSelectSize(1, 1);
|
||||
this->DisableWidget(WID_AP_LAYOUT_DECREASE);
|
||||
this->DisableWidget(WID_AP_LAYOUT_INCREASE);
|
||||
} else {
|
||||
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
|
||||
int w = as->size_x;
|
||||
int h = as->size_y;
|
||||
Direction rotation = as->rotation[_selected_airport_layout];
|
||||
if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
|
||||
SetTileSelectSize(w, h);
|
||||
|
||||
this->preview_sprite = GetCustomAirportSprite(as, _selected_airport_layout);
|
||||
|
||||
this->SetWidgetDisabledState(WID_AP_LAYOUT_DECREASE, _selected_airport_layout == 0);
|
||||
this->SetWidgetDisabledState(WID_AP_LAYOUT_INCREASE, _selected_airport_layout + 1 >= as->num_table);
|
||||
|
||||
int rad = _settings_game.station.modified_catchment ? as->catchment : (uint)CA_UNMODIFIED;
|
||||
if (_settings_client.gui.station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnClick(Point pt, int widget, int click_count)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_AP_CLASS_DROPDOWN:
|
||||
ShowDropDownList(this, BuildAirportClassDropDown(), _selected_airport_class, WID_AP_CLASS_DROPDOWN);
|
||||
break;
|
||||
|
||||
case WID_AP_AIRPORT_LIST: {
|
||||
int num_clicked = this->vscroll->GetPosition() + (pt.y - this->nested_array[widget]->pos_y) / this->line_height;
|
||||
if (num_clicked >= this->vscroll->GetCount()) break;
|
||||
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(num_clicked);
|
||||
if (as->IsAvailable()) this->SelectOtherAirport(num_clicked);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_AP_BTN_DONTHILIGHT: case WID_AP_BTN_DOHILIGHT:
|
||||
_settings_client.gui.station_show_coverage = (widget != WID_AP_BTN_DONTHILIGHT);
|
||||
this->SetWidgetLoweredState(WID_AP_BTN_DONTHILIGHT, !_settings_client.gui.station_show_coverage);
|
||||
this->SetWidgetLoweredState(WID_AP_BTN_DOHILIGHT, _settings_client.gui.station_show_coverage);
|
||||
this->SetDirty();
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
this->UpdateSelectSize();
|
||||
break;
|
||||
|
||||
case WID_AP_LAYOUT_DECREASE:
|
||||
_selected_airport_layout--;
|
||||
this->UpdateSelectSize();
|
||||
this->SetDirty();
|
||||
break;
|
||||
|
||||
case WID_AP_LAYOUT_INCREASE:
|
||||
_selected_airport_layout++;
|
||||
this->UpdateSelectSize();
|
||||
this->SetDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the first available airport.
|
||||
* @param change_class If true, change the class if no airport in the current
|
||||
* class is available.
|
||||
*/
|
||||
void SelectFirstAvailableAirport(bool change_class)
|
||||
{
|
||||
/* First try to select an airport in the selected class. */
|
||||
AirportClass *sel_apclass = AirportClass::Get(_selected_airport_class);
|
||||
for (uint i = 0; i < sel_apclass->GetSpecCount(); i++) {
|
||||
const AirportSpec *as = sel_apclass->GetSpec(i);
|
||||
if (as->IsAvailable()) {
|
||||
this->SelectOtherAirport(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (change_class) {
|
||||
/* If that fails, select the first available airport
|
||||
* from a random class. */
|
||||
for (AirportClassID j = APC_BEGIN; j < APC_MAX; j++) {
|
||||
AirportClass *apclass = AirportClass::Get(j);
|
||||
for (uint i = 0; i < apclass->GetSpecCount(); i++) {
|
||||
const AirportSpec *as = apclass->GetSpec(i);
|
||||
if (as->IsAvailable()) {
|
||||
_selected_airport_class = j;
|
||||
this->SelectOtherAirport(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If all airports are unavailable, select nothing. */
|
||||
this->SelectOtherAirport(-1);
|
||||
}
|
||||
|
||||
virtual void OnDropdownSelect(int widget, int index)
|
||||
{
|
||||
assert(widget == WID_AP_CLASS_DROPDOWN);
|
||||
_selected_airport_class = (AirportClassID)index;
|
||||
this->vscroll->SetCount(AirportClass::Get(_selected_airport_class)->GetSpecCount());
|
||||
this->SelectFirstAvailableAirport(false);
|
||||
}
|
||||
|
||||
virtual void OnTick()
|
||||
{
|
||||
CheckRedrawStationCoverage(this);
|
||||
}
|
||||
};
|
||||
|
||||
static const NWidgetPart _nested_build_airport_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(1, 0), SetPIP(2, 0, 2),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL, STR_NULL), SetFill(1, 0),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_AP_CLASS_DROPDOWN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_STATION_BUILD_AIRPORT_TOOLTIP),
|
||||
NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_AIRPORT_SPRITE), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_AP_AIRPORT_LIST), SetFill(1, 0), SetMatrixDataTip(1, 5, STR_STATION_BUILD_AIRPORT_TOOLTIP), SetScrollbar(WID_AP_SCROLLBAR),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_AP_SCROLLBAR),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_DECREASE), SetMinimalSize(12, 0), SetDataTip(AWV_DECREASE, STR_NULL),
|
||||
NWidget(WWT_LABEL, COLOUR_GREY, WID_AP_LAYOUT_NUM), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NULL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_INCREASE), SetMinimalSize(12, 0), SetDataTip(AWV_INCREASE, STR_NULL),
|
||||
EndContainer(),
|
||||
NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0),
|
||||
EndContainer(),
|
||||
/* Bottom panel. */
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_AP_BOTTOMPANEL), SetPIP(2, 2, 2),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(14, 0), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DONTHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DOHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(14, 0), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 10), SetResize(0, 1), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
static WindowDesc _build_airport_desc(
|
||||
WDP_AUTO, "build_station_air", 0, 0,
|
||||
WC_BUILD_STATION, WC_BUILD_TOOLBAR,
|
||||
WDF_CONSTRUCTION,
|
||||
_nested_build_airport_widgets, lengthof(_nested_build_airport_widgets)
|
||||
);
|
||||
|
||||
static void ShowBuildAirportPicker(Window *parent)
|
||||
{
|
||||
new BuildAirportWindow(&_build_airport_desc, parent);
|
||||
}
|
||||
|
||||
void InitializeAirportGui()
|
||||
{
|
||||
_selected_airport_class = APC_BEGIN;
|
||||
_selected_airport_index = -1;
|
||||
}
|
||||
99
src/animated_tile.cpp
Normal file
99
src/animated_tile.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file animated_tile.cpp Everything related to animated tiles. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "core/alloc_func.hpp"
|
||||
#include "tile_cmd.h"
|
||||
#include "viewport_func.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
/** The table/list with animated tiles. */
|
||||
TileIndex *_animated_tile_list = NULL;
|
||||
/** The number of animated tiles in the current state. */
|
||||
uint _animated_tile_count = 0;
|
||||
/** The number of slots for animated tiles allocated currently. */
|
||||
uint _animated_tile_allocated = 0;
|
||||
|
||||
/**
|
||||
* Removes the given tile from the animated tile table.
|
||||
* @param tile the tile to remove
|
||||
*/
|
||||
void DeleteAnimatedTile(TileIndex tile)
|
||||
{
|
||||
for (TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
|
||||
if (tile == *ti) {
|
||||
/* Remove the hole
|
||||
* The order of the remaining elements must stay the same, otherwise the animation loop
|
||||
* may miss a tile; that's why we must use memmove instead of just moving the last element.
|
||||
*/
|
||||
memmove(ti, ti + 1, (_animated_tile_list + _animated_tile_count - (ti + 1)) * sizeof(*ti));
|
||||
_animated_tile_count--;
|
||||
MarkTileDirtyByTile(tile);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given tile to the animated tile table (if it does not exist
|
||||
* on that table yet). Also increases the size of the table if necessary.
|
||||
* @param tile the tile to make animated
|
||||
*/
|
||||
void AddAnimatedTile(TileIndex tile)
|
||||
{
|
||||
MarkTileDirtyByTile(tile);
|
||||
|
||||
for (const TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
|
||||
if (tile == *ti) return;
|
||||
}
|
||||
|
||||
/* Table not large enough, so make it larger */
|
||||
if (_animated_tile_count == _animated_tile_allocated) {
|
||||
_animated_tile_allocated *= 2;
|
||||
_animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
|
||||
}
|
||||
|
||||
_animated_tile_list[_animated_tile_count] = tile;
|
||||
_animated_tile_count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate all tiles in the animated tile list, i.e.\ call AnimateTile on them.
|
||||
*/
|
||||
void AnimateAnimatedTiles()
|
||||
{
|
||||
const TileIndex *ti = _animated_tile_list;
|
||||
while (ti < _animated_tile_list + _animated_tile_count) {
|
||||
const TileIndex curr = *ti;
|
||||
AnimateTile(curr);
|
||||
/* During the AnimateTile call, DeleteAnimatedTile could have been called,
|
||||
* deleting an element we've already processed and pushing the rest one
|
||||
* slot to the left. We can detect this by checking whether the index
|
||||
* in the current slot has changed - if it has, an element has been deleted,
|
||||
* and we should process the current slot again instead of going forward.
|
||||
* NOTE: this will still break if more than one animated tile is being
|
||||
* deleted during the same AnimateTile call, but no code seems to
|
||||
* be doing this anyway.
|
||||
*/
|
||||
if (*ti == curr) ++ti;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all animated tile variables to some known begin point
|
||||
*/
|
||||
void InitializeAnimatedTiles()
|
||||
{
|
||||
_animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, 256);
|
||||
_animated_tile_count = 0;
|
||||
_animated_tile_allocated = 256;
|
||||
}
|
||||
22
src/animated_tile_func.h
Normal file
22
src/animated_tile_func.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file animated_tile_func.h %Tile animation! */
|
||||
|
||||
#ifndef ANIMATED_TILE_FUNC_H
|
||||
#define ANIMATED_TILE_FUNC_H
|
||||
|
||||
#include "tile_type.h"
|
||||
|
||||
void AddAnimatedTile(TileIndex tile);
|
||||
void DeleteAnimatedTile(TileIndex tile);
|
||||
void AnimateAnimatedTiles();
|
||||
void InitializeAnimatedTiles();
|
||||
|
||||
#endif /* ANIMATED_TILE_FUNC_H */
|
||||
450
src/articulated_vehicles.cpp
Normal file
450
src/articulated_vehicles.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file articulated_vehicles.cpp Implementation of articulated vehicles. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "train.h"
|
||||
#include "roadveh.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "engine_func.h"
|
||||
#include "company_func.h"
|
||||
#include "newgrf.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
static const uint MAX_ARTICULATED_PARTS = 100; ///< Maximum of articulated parts per vehicle, i.e. when to abort calling the articulated vehicle callback.
|
||||
|
||||
/**
|
||||
* Determines the next articulated part to attach
|
||||
* @param index Position in chain
|
||||
* @param front_type Front engine type
|
||||
* @param front Front engine
|
||||
* @param mirrored Returns whether the part shall be flipped.
|
||||
* @return engine to add or INVALID_ENGINE
|
||||
*/
|
||||
static EngineID GetNextArticulatedPart(uint index, EngineID front_type, Vehicle *front = NULL, bool *mirrored = NULL)
|
||||
{
|
||||
assert(front == NULL || front->engine_type == front_type);
|
||||
|
||||
const Engine *front_engine = Engine::Get(front_type);
|
||||
|
||||
uint16 callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, index, 0, front_type, front);
|
||||
if (callback == CALLBACK_FAILED) return INVALID_ENGINE;
|
||||
|
||||
if (front_engine->GetGRF()->grf_version < 8) {
|
||||
/* 8 bits, bit 7 for mirroring */
|
||||
callback = GB(callback, 0, 8);
|
||||
if (callback == 0xFF) return INVALID_ENGINE;
|
||||
if (mirrored != NULL) *mirrored = HasBit(callback, 7);
|
||||
callback = GB(callback, 0, 7);
|
||||
} else {
|
||||
/* 15 bits, bit 14 for mirroring */
|
||||
if (callback == 0x7FFF) return INVALID_ENGINE;
|
||||
if (mirrored != NULL) *mirrored = HasBit(callback, 14);
|
||||
callback = GB(callback, 0, 14);
|
||||
}
|
||||
|
||||
return GetNewEngineID(front_engine->GetGRF(), front_engine->type, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a NewGRF report that this should be an articulated vehicle?
|
||||
* @param engine_type The engine to check.
|
||||
* @return True iff the articulated engine callback flag is set.
|
||||
*/
|
||||
bool IsArticulatedEngine(EngineID engine_type)
|
||||
{
|
||||
return HasBit(EngInfo(engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of articulated parts of an engine.
|
||||
* @param engine_type The engine to get the number of parts of.
|
||||
* @param purchase_window Whether we are in the scope of the purchase window or not, i.e. whether we cannot allocate vehicles.
|
||||
* @return The number of parts.
|
||||
*/
|
||||
uint CountArticulatedParts(EngineID engine_type, bool purchase_window)
|
||||
{
|
||||
if (!HasBit(EngInfo(engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return 0;
|
||||
|
||||
/* If we can't allocate a vehicle now, we can't allocate it in the command
|
||||
* either, so it doesn't matter how many articulated parts there are. */
|
||||
if (!Vehicle::CanAllocateItem()) return 0;
|
||||
|
||||
Vehicle *v = NULL;
|
||||
if (!purchase_window) {
|
||||
v = new Vehicle();
|
||||
v->engine_type = engine_type;
|
||||
v->owner = _current_company;
|
||||
}
|
||||
|
||||
uint i;
|
||||
for (i = 1; i < MAX_ARTICULATED_PARTS; i++) {
|
||||
if (GetNextArticulatedPart(i, engine_type, v) == INVALID_ENGINE) break;
|
||||
}
|
||||
|
||||
delete v;
|
||||
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default (non-refitted) capacity of a specific EngineID.
|
||||
* @param engine the EngineID of interest
|
||||
* @param cargo_type returns the default cargo type, if needed
|
||||
* @return capacity
|
||||
*/
|
||||
static inline uint16 GetVehicleDefaultCapacity(EngineID engine, CargoID *cargo_type)
|
||||
{
|
||||
const Engine *e = Engine::Get(engine);
|
||||
CargoID cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);
|
||||
if (cargo_type != NULL) *cargo_type = cargo;
|
||||
if (cargo == CT_INVALID) return 0;
|
||||
return e->GetDisplayDefaultCapacity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all cargoes a vehicle can carry.
|
||||
* @param engine the EngineID of interest
|
||||
* @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask
|
||||
* @return bit set of CargoIDs
|
||||
*/
|
||||
static inline uint32 GetAvailableVehicleCargoTypes(EngineID engine, bool include_initial_cargo_type)
|
||||
{
|
||||
const Engine *e = Engine::Get(engine);
|
||||
if (!e->CanCarryCargo()) return 0;
|
||||
|
||||
uint32 cargoes = e->info.refit_mask;
|
||||
|
||||
if (include_initial_cargo_type) {
|
||||
SetBit(cargoes, e->GetDefaultCargoType());
|
||||
}
|
||||
|
||||
return cargoes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the capacity of the parts of a given engine.
|
||||
* @param engine The engine to get the capacities from.
|
||||
* @return The cargo capacities.
|
||||
*/
|
||||
CargoArray GetCapacityOfArticulatedParts(EngineID engine)
|
||||
{
|
||||
CargoArray capacity;
|
||||
const Engine *e = Engine::Get(engine);
|
||||
|
||||
CargoID cargo_type;
|
||||
uint16 cargo_capacity = GetVehicleDefaultCapacity(engine, &cargo_type);
|
||||
if (cargo_type < NUM_CARGO) capacity[cargo_type] = cargo_capacity;
|
||||
|
||||
if (!e->IsGroundVehicle()) return capacity;
|
||||
|
||||
if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return capacity;
|
||||
|
||||
for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
|
||||
EngineID artic_engine = GetNextArticulatedPart(i, engine);
|
||||
if (artic_engine == INVALID_ENGINE) break;
|
||||
|
||||
cargo_capacity = GetVehicleDefaultCapacity(artic_engine, &cargo_type);
|
||||
if (cargo_type < NUM_CARGO) capacity[cargo_type] += cargo_capacity;
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default cargoes and refits of an articulated vehicle.
|
||||
* The refits are linked to a cargo rather than an articulated part to prevent a long list of parts.
|
||||
* @param engine Model to investigate.
|
||||
* @param[out] cargoes Total amount of units that can be transported, summed by cargo.
|
||||
* @param[out] refits Whether a (possibly partial) refit for each cargo is possible.
|
||||
*/
|
||||
void GetArticulatedVehicleCargoesAndRefits(EngineID engine, CargoArray *cargoes, uint32 *refits)
|
||||
{
|
||||
cargoes->Clear();
|
||||
*refits = 0;
|
||||
|
||||
const Engine *e = Engine::Get(engine);
|
||||
|
||||
CargoID cargo_type;
|
||||
uint16 cargo_capacity = GetVehicleDefaultCapacity(engine, &cargo_type);
|
||||
if (cargo_type < NUM_CARGO && cargo_capacity > 0) {
|
||||
(*cargoes)[cargo_type] += cargo_capacity;
|
||||
if (IsEngineRefittable(engine)) SetBit(*refits, cargo_type);
|
||||
}
|
||||
|
||||
if (!e->IsGroundVehicle() || !HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return;
|
||||
|
||||
for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
|
||||
EngineID artic_engine = GetNextArticulatedPart(i, engine);
|
||||
if (artic_engine == INVALID_ENGINE) break;
|
||||
|
||||
cargo_capacity = GetVehicleDefaultCapacity(artic_engine, &cargo_type);
|
||||
if (cargo_type < NUM_CARGO && cargo_capacity > 0) {
|
||||
(*cargoes)[cargo_type] += cargo_capacity;
|
||||
if (IsEngineRefittable(artic_engine)) SetBit(*refits, cargo_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether any of the articulated parts is refittable
|
||||
* @param engine the first part
|
||||
* @return true if refittable
|
||||
*/
|
||||
bool IsArticulatedVehicleRefittable(EngineID engine)
|
||||
{
|
||||
if (IsEngineRefittable(engine)) return true;
|
||||
|
||||
const Engine *e = Engine::Get(engine);
|
||||
if (!e->IsGroundVehicle()) return false;
|
||||
|
||||
if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return false;
|
||||
|
||||
for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
|
||||
EngineID artic_engine = GetNextArticulatedPart(i, engine);
|
||||
if (artic_engine == INVALID_ENGINE) break;
|
||||
|
||||
if (IsEngineRefittable(artic_engine)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the refit_masks of all articulated parts.
|
||||
* @param engine the first part
|
||||
* @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask
|
||||
* @param union_mask returns bit mask of CargoIDs which are a refit option for at least one articulated part
|
||||
* @param intersection_mask returns bit mask of CargoIDs which are a refit option for every articulated part (with default capacity > 0)
|
||||
*/
|
||||
void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, uint32 *union_mask, uint32 *intersection_mask)
|
||||
{
|
||||
const Engine *e = Engine::Get(engine);
|
||||
uint32 veh_cargoes = GetAvailableVehicleCargoTypes(engine, include_initial_cargo_type);
|
||||
*union_mask = veh_cargoes;
|
||||
*intersection_mask = (veh_cargoes != 0) ? veh_cargoes : UINT32_MAX;
|
||||
|
||||
if (!e->IsGroundVehicle()) return;
|
||||
if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return;
|
||||
|
||||
for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
|
||||
EngineID artic_engine = GetNextArticulatedPart(i, engine);
|
||||
if (artic_engine == INVALID_ENGINE) break;
|
||||
|
||||
veh_cargoes = GetAvailableVehicleCargoTypes(artic_engine, include_initial_cargo_type);
|
||||
*union_mask |= veh_cargoes;
|
||||
if (veh_cargoes != 0) *intersection_mask &= veh_cargoes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ors the refit_masks of all articulated parts.
|
||||
* @param engine the first part
|
||||
* @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask
|
||||
* @return bit mask of CargoIDs which are a refit option for at least one articulated part
|
||||
*/
|
||||
uint32 GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
|
||||
{
|
||||
uint32 union_mask, intersection_mask;
|
||||
GetArticulatedRefitMasks(engine, include_initial_cargo_type, &union_mask, &intersection_mask);
|
||||
return union_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ands the refit_masks of all articulated parts.
|
||||
* @param engine the first part
|
||||
* @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask
|
||||
* @return bit mask of CargoIDs which are a refit option for every articulated part (with default capacity > 0)
|
||||
*/
|
||||
uint32 GetIntersectionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
|
||||
{
|
||||
uint32 union_mask, intersection_mask;
|
||||
GetArticulatedRefitMasks(engine, include_initial_cargo_type, &union_mask, &intersection_mask);
|
||||
return intersection_mask;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if all parts of an articulated vehicle are refitted to the same cargo.
|
||||
* Note: Vehicles not carrying anything are ignored
|
||||
* @param v the first vehicle in the chain
|
||||
* @param cargo_type returns the common CargoID if needed. (CT_INVALID if no part is carrying something or they are carrying different things)
|
||||
* @return true if some parts are carrying different cargoes, false if all parts are carrying the same (nothing is also the same)
|
||||
*/
|
||||
bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *cargo_type)
|
||||
{
|
||||
CargoID first_cargo = CT_INVALID;
|
||||
|
||||
do {
|
||||
if (v->cargo_type != CT_INVALID && v->GetEngine()->CanCarryCargo()) {
|
||||
if (first_cargo == CT_INVALID) first_cargo = v->cargo_type;
|
||||
if (first_cargo != v->cargo_type) {
|
||||
if (cargo_type != NULL) *cargo_type = CT_INVALID;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : NULL;
|
||||
} while (v != NULL);
|
||||
|
||||
if (cargo_type != NULL) *cargo_type = first_cargo;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the specs of freshly build articulated vehicles are consistent with the information specified in the purchase list.
|
||||
* Only essential information is checked to leave room for magic tricks/workarounds to grfcoders.
|
||||
* It checks:
|
||||
* For autoreplace/-renew:
|
||||
* - Default cargo type (without capacity)
|
||||
* - intersection and union of refit masks.
|
||||
*/
|
||||
void CheckConsistencyOfArticulatedVehicle(const Vehicle *v)
|
||||
{
|
||||
const Engine *engine = v->GetEngine();
|
||||
|
||||
uint32 purchase_refit_union, purchase_refit_intersection;
|
||||
GetArticulatedRefitMasks(v->engine_type, true, &purchase_refit_union, &purchase_refit_intersection);
|
||||
CargoArray purchase_default_capacity = GetCapacityOfArticulatedParts(v->engine_type);
|
||||
|
||||
uint32 real_refit_union = 0;
|
||||
uint32 real_refit_intersection = UINT_MAX;
|
||||
CargoArray real_default_capacity;
|
||||
|
||||
do {
|
||||
uint32 refit_mask = GetAvailableVehicleCargoTypes(v->engine_type, true);
|
||||
real_refit_union |= refit_mask;
|
||||
if (refit_mask != 0) real_refit_intersection &= refit_mask;
|
||||
|
||||
assert(v->cargo_type < NUM_CARGO);
|
||||
real_default_capacity[v->cargo_type] += v->cargo_cap;
|
||||
|
||||
v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : NULL;
|
||||
} while (v != NULL);
|
||||
|
||||
/* Check whether the vehicle carries more cargoes than expected */
|
||||
bool carries_more = false;
|
||||
for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
|
||||
if (real_default_capacity[cid] != 0 && purchase_default_capacity[cid] == 0) {
|
||||
carries_more = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* show a warning once for each GRF after each game load */
|
||||
if (real_refit_union != purchase_refit_union || real_refit_intersection != purchase_refit_intersection || carries_more) {
|
||||
ShowNewGrfVehicleError(engine->index, STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_ARTICULATED_CARGO, GBUG_VEH_REFIT, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the remaining articulated parts to the given vehicle.
|
||||
* @param first The head of the articulated bit.
|
||||
*/
|
||||
void AddArticulatedParts(Vehicle *first)
|
||||
{
|
||||
VehicleType type = first->type;
|
||||
if (!HasBit(EngInfo(first->engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return;
|
||||
|
||||
Vehicle *v = first;
|
||||
for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
|
||||
bool flip_image;
|
||||
EngineID engine_type = GetNextArticulatedPart(i, first->engine_type, first, &flip_image);
|
||||
if (engine_type == INVALID_ENGINE) return;
|
||||
|
||||
/* In the (very rare) case the GRF reported wrong number of articulated parts
|
||||
* and we run out of available vehicles, bail out. */
|
||||
if (!Vehicle::CanAllocateItem()) return;
|
||||
|
||||
GroundVehicleCache *gcache = v->GetGroundVehicleCache();
|
||||
gcache->first_engine = v->engine_type; // Needs to be set before first callback
|
||||
|
||||
const Engine *e_artic = Engine::Get(engine_type);
|
||||
switch (type) {
|
||||
default: NOT_REACHED();
|
||||
|
||||
case VEH_TRAIN: {
|
||||
Train *front = Train::From(first);
|
||||
Train *t = new Train();
|
||||
v->SetNext(t);
|
||||
v = t;
|
||||
|
||||
t->subtype = 0;
|
||||
t->track = front->track;
|
||||
t->railtype = front->railtype;
|
||||
|
||||
t->spritenum = e_artic->u.rail.image_index;
|
||||
if (e_artic->CanCarryCargo()) {
|
||||
t->cargo_type = e_artic->GetDefaultCargoType();
|
||||
t->cargo_cap = e_artic->u.rail.capacity; // Callback 36 is called when the consist is finished
|
||||
} else {
|
||||
t->cargo_type = front->cargo_type; // Needed for livery selection
|
||||
t->cargo_cap = 0;
|
||||
}
|
||||
t->refit_cap = 0;
|
||||
|
||||
t->SetArticulatedPart();
|
||||
break;
|
||||
}
|
||||
|
||||
case VEH_ROAD: {
|
||||
RoadVehicle *front = RoadVehicle::From(first);
|
||||
RoadVehicle *rv = new RoadVehicle();
|
||||
v->SetNext(rv);
|
||||
v = rv;
|
||||
|
||||
rv->subtype = 0;
|
||||
gcache->cached_veh_length = VEHICLE_LENGTH; // Callback is called when the consist is finished
|
||||
rv->state = RVSB_IN_DEPOT;
|
||||
|
||||
rv->roadtype = front->roadtype;
|
||||
rv->compatible_roadtypes = front->compatible_roadtypes;
|
||||
|
||||
rv->spritenum = e_artic->u.road.image_index;
|
||||
if (e_artic->CanCarryCargo()) {
|
||||
rv->cargo_type = e_artic->GetDefaultCargoType();
|
||||
rv->cargo_cap = e_artic->u.road.capacity; // Callback 36 is called when the consist is finished
|
||||
} else {
|
||||
rv->cargo_type = front->cargo_type; // Needed for livery selection
|
||||
rv->cargo_cap = 0;
|
||||
}
|
||||
rv->refit_cap = 0;
|
||||
|
||||
rv->SetArticulatedPart();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* get common values from first engine */
|
||||
v->direction = first->direction;
|
||||
v->owner = first->owner;
|
||||
v->tile = first->tile;
|
||||
v->x_pos = first->x_pos;
|
||||
v->y_pos = first->y_pos;
|
||||
v->z_pos = first->z_pos;
|
||||
v->build_year = first->build_year;
|
||||
v->vehstatus = first->vehstatus & ~VS_STOPPED;
|
||||
|
||||
v->cargo_subtype = 0;
|
||||
v->max_age = 0;
|
||||
v->engine_type = engine_type;
|
||||
v->value = 0;
|
||||
v->cur_image = SPR_IMG_QUERY;
|
||||
v->random_bits = VehicleRandomBits();
|
||||
|
||||
if (flip_image) v->spritenum++;
|
||||
|
||||
v->UpdatePosition();
|
||||
}
|
||||
}
|
||||
30
src/articulated_vehicles.h
Normal file
30
src/articulated_vehicles.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file articulated_vehicles.h Functions related to articulated vehicles. */
|
||||
|
||||
#ifndef ARTICULATED_VEHICLES_H
|
||||
#define ARTICULATED_VEHICLES_H
|
||||
|
||||
#include "vehicle_type.h"
|
||||
#include "engine_type.h"
|
||||
|
||||
uint CountArticulatedParts(EngineID engine_type, bool purchase_window);
|
||||
CargoArray GetCapacityOfArticulatedParts(EngineID engine);
|
||||
void AddArticulatedParts(Vehicle *first);
|
||||
void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, uint32 *union_mask, uint32 *intersection_mask);
|
||||
uint32 GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type);
|
||||
uint32 GetIntersectionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type);
|
||||
bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *cargo_type);
|
||||
bool IsArticulatedVehicleRefittable(EngineID engine);
|
||||
bool IsArticulatedEngine(EngineID engine_type);
|
||||
void CheckConsistencyOfArticulatedVehicle(const Vehicle *v);
|
||||
|
||||
|
||||
#endif /* ARTICULATED_VEHICLES_H */
|
||||
146
src/autoreplace.cpp
Normal file
146
src/autoreplace.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file autoreplace.cpp Management of replacement lists. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "command_func.h"
|
||||
#include "group.h"
|
||||
#include "autoreplace_base.h"
|
||||
#include "core/pool_func.hpp"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
/** The pool of autoreplace "orders". */
|
||||
EngineRenewPool _enginerenew_pool("EngineRenew");
|
||||
INSTANTIATE_POOL_METHODS(EngineRenew)
|
||||
|
||||
/**
|
||||
* Retrieves the EngineRenew that specifies the replacement of the given
|
||||
* engine type from the given renewlist
|
||||
*/
|
||||
static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
|
||||
{
|
||||
EngineRenew *er = (EngineRenew *)erl;
|
||||
|
||||
while (er != NULL) {
|
||||
if (er->from == engine && GroupIsInGroup(group, er->group_id)) return er;
|
||||
er = er->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all engine replacement settings for the company.
|
||||
* @param erl The renewlist for a given company.
|
||||
* @return The new renewlist for the company.
|
||||
*/
|
||||
void RemoveAllEngineReplacement(EngineRenewList *erl)
|
||||
{
|
||||
EngineRenew *er = (EngineRenew *)(*erl);
|
||||
EngineRenew *next;
|
||||
|
||||
while (er != NULL) {
|
||||
next = er->next;
|
||||
delete er;
|
||||
er = next;
|
||||
}
|
||||
*erl = NULL; // Empty list
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the engine replacement in a given renewlist for an original engine type.
|
||||
* @param erl The renewlist to search in.
|
||||
* @param engine Engine type to be replaced.
|
||||
* @param group The group related to this replacement.
|
||||
* @param[out] replace_when_old Set to true if the replacement should be done when old.
|
||||
* @return The engine type to replace with, or INVALID_ENGINE if no
|
||||
* replacement is in the list.
|
||||
*/
|
||||
EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old)
|
||||
{
|
||||
const EngineRenew *er = GetEngineReplacement(erl, engine, group);
|
||||
if (er == NULL && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !Group::Get(group)->replace_protection))) {
|
||||
/* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */
|
||||
er = GetEngineReplacement(erl, engine, ALL_GROUP);
|
||||
}
|
||||
if (replace_when_old != NULL) *replace_when_old = er == NULL ? false : er->replace_when_old;
|
||||
return er == NULL ? INVALID_ENGINE : er->to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an engine replacement to the given renewlist.
|
||||
* @param erl The renewlist to add to.
|
||||
* @param old_engine The original engine type.
|
||||
* @param new_engine The replacement engine type.
|
||||
* @param group The group related to this replacement.
|
||||
* @param replace_when_old Replace when old or always?
|
||||
* @param flags The calling command flags.
|
||||
* @return 0 on success, CMD_ERROR on failure.
|
||||
*/
|
||||
CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags)
|
||||
{
|
||||
/* Check if the old vehicle is already in the list */
|
||||
EngineRenew *er = GetEngineReplacement(*erl, old_engine, group);
|
||||
if (er != NULL) {
|
||||
if (flags & DC_EXEC) {
|
||||
er->to = new_engine;
|
||||
er->replace_when_old = replace_when_old;
|
||||
}
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
if (!EngineRenew::CanAllocateItem()) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
er = new EngineRenew(old_engine, new_engine);
|
||||
er->group_id = group;
|
||||
er->replace_when_old = replace_when_old;
|
||||
|
||||
/* Insert before the first element */
|
||||
er->next = (EngineRenew *)(*erl);
|
||||
*erl = (EngineRenewList)er;
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an engine replacement from a given renewlist.
|
||||
* @param erl The renewlist from which to remove the replacement
|
||||
* @param engine The original engine type.
|
||||
* @param group The group related to this replacement.
|
||||
* @param flags The calling command flags.
|
||||
* @return 0 on success, CMD_ERROR on failure.
|
||||
*/
|
||||
CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, DoCommandFlag flags)
|
||||
{
|
||||
EngineRenew *er = (EngineRenew *)(*erl);
|
||||
EngineRenew *prev = NULL;
|
||||
|
||||
while (er != NULL) {
|
||||
if (er->from == engine && er->group_id == group) {
|
||||
if (flags & DC_EXEC) {
|
||||
if (prev == NULL) { // First element
|
||||
/* The second becomes the new first element */
|
||||
*erl = (EngineRenewList)er->next;
|
||||
} else {
|
||||
/* Cut this element out */
|
||||
prev->next = er->next;
|
||||
}
|
||||
delete er;
|
||||
}
|
||||
return CommandCost();
|
||||
}
|
||||
prev = er;
|
||||
er = er->next;
|
||||
}
|
||||
|
||||
return CMD_ERROR;
|
||||
}
|
||||
49
src/autoreplace_base.h
Normal file
49
src/autoreplace_base.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file autoreplace_base.h Base class for autoreplaces/autorenews. */
|
||||
|
||||
#ifndef AUTOREPLACE_BASE_H
|
||||
#define AUTOREPLACE_BASE_H
|
||||
|
||||
#include "core/pool_type.hpp"
|
||||
#include "autoreplace_type.h"
|
||||
#include "engine_type.h"
|
||||
#include "group_type.h"
|
||||
|
||||
typedef uint16 EngineRenewID;
|
||||
|
||||
/**
|
||||
* Memory pool for engine renew elements. DO NOT USE outside of engine.c. Is
|
||||
* placed here so the only exception to this rule, the saveload code, can use
|
||||
* it.
|
||||
*/
|
||||
typedef Pool<EngineRenew, EngineRenewID, 16, 64000> EngineRenewPool;
|
||||
extern EngineRenewPool _enginerenew_pool;
|
||||
|
||||
/**
|
||||
* Struct to store engine replacements. DO NOT USE outside of engine.c. Is
|
||||
* placed here so the only exception to this rule, the saveload code, can use
|
||||
* it.
|
||||
*/
|
||||
struct EngineRenew : EngineRenewPool::PoolItem<&_enginerenew_pool> {
|
||||
EngineID from;
|
||||
EngineID to;
|
||||
EngineRenew *next;
|
||||
GroupID group_id;
|
||||
bool replace_when_old; ///< Do replacement only when vehicle is old.
|
||||
|
||||
EngineRenew(EngineID from = INVALID_ENGINE, EngineID to = INVALID_ENGINE) : from(from), to(to) {}
|
||||
~EngineRenew() {}
|
||||
};
|
||||
|
||||
#define FOR_ALL_ENGINE_RENEWS_FROM(var, start) FOR_ALL_ITEMS_FROM(EngineRenew, enginerenew_index, var, start)
|
||||
#define FOR_ALL_ENGINE_RENEWS(var) FOR_ALL_ENGINE_RENEWS_FROM(var, 0)
|
||||
|
||||
#endif /* AUTOREPLACE_BASE_H */
|
||||
787
src/autoreplace_cmd.cpp
Normal file
787
src/autoreplace_cmd.cpp
Normal file
@@ -0,0 +1,787 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file autoreplace_cmd.cpp Deals with autoreplace execution but not the setup */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "company_func.h"
|
||||
#include "train.h"
|
||||
#include "command_func.h"
|
||||
#include "engine_func.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "autoreplace_func.h"
|
||||
#include "autoreplace_gui.h"
|
||||
#include "articulated_vehicles.h"
|
||||
#include "core/random_func.hpp"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
extern void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index);
|
||||
extern void ChangeVehicleNews(VehicleID from_index, VehicleID to_index);
|
||||
extern void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index);
|
||||
|
||||
/**
|
||||
* Figure out if two engines got at least one type of cargo in common (refitting if needed)
|
||||
* @param engine_a one of the EngineIDs
|
||||
* @param engine_b the other EngineID
|
||||
* @param type the type of the engines
|
||||
* @return true if they can both carry the same type of cargo (or at least one of them got no capacity at all)
|
||||
*/
|
||||
static bool EnginesHaveCargoInCommon(EngineID engine_a, EngineID engine_b)
|
||||
{
|
||||
uint32 available_cargoes_a = GetUnionOfArticulatedRefitMasks(engine_a, true);
|
||||
uint32 available_cargoes_b = GetUnionOfArticulatedRefitMasks(engine_b, true);
|
||||
return (available_cargoes_a == 0 || available_cargoes_b == 0 || (available_cargoes_a & available_cargoes_b) != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks some basic properties whether autoreplace is allowed
|
||||
* @param from Origin engine
|
||||
* @param to Destination engine
|
||||
* @param company Company to check for
|
||||
* @return true if autoreplace is allowed
|
||||
*/
|
||||
bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
|
||||
{
|
||||
assert(Engine::IsValidID(from) && Engine::IsValidID(to));
|
||||
|
||||
/* we can't replace an engine into itself (that would be autorenew) */
|
||||
if (from == to) return false;
|
||||
|
||||
const Engine *e_from = Engine::Get(from);
|
||||
const Engine *e_to = Engine::Get(to);
|
||||
VehicleType type = e_from->type;
|
||||
|
||||
/* check that the new vehicle type is available to the company and its type is the same as the original one */
|
||||
if (!IsEngineBuildable(to, type, company)) return false;
|
||||
|
||||
switch (type) {
|
||||
case VEH_TRAIN: {
|
||||
/* make sure the railtypes are compatible */
|
||||
if ((GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) return false;
|
||||
|
||||
/* make sure we do not replace wagons with engines or vice versa */
|
||||
if ((e_from->u.rail.railveh_type == RAILVEH_WAGON) != (e_to->u.rail.railveh_type == RAILVEH_WAGON)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case VEH_ROAD:
|
||||
/* make sure that we do not replace a tram with a normal road vehicles or vice versa */
|
||||
if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false;
|
||||
break;
|
||||
|
||||
case VEH_AIRCRAFT:
|
||||
/* make sure that we do not replace a plane with a helicopter or vice versa */
|
||||
if ((e_from->u.air.subtype & AIR_CTOL) != (e_to->u.air.subtype & AIR_CTOL)) return false;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
/* the engines needs to be able to carry the same cargo */
|
||||
return EnginesHaveCargoInCommon(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the capacity of all vehicles in a chain and spread cargo if needed.
|
||||
* @param v The vehicle to check.
|
||||
* @pre You can only do this if the consist is not loading or unloading. It
|
||||
* must not carry reserved cargo, nor cargo to be unloaded or transferred.
|
||||
*/
|
||||
void CheckCargoCapacity(Vehicle *v)
|
||||
{
|
||||
assert(v == NULL || v->First() == v);
|
||||
|
||||
for (Vehicle *src = v; src != NULL; src = src->Next()) {
|
||||
assert(src->cargo.TotalCount() == src->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
|
||||
|
||||
/* Do we need to more cargo away? */
|
||||
if (src->cargo.TotalCount() <= src->cargo_cap) continue;
|
||||
|
||||
/* We need to move a particular amount. Try that on the other vehicles. */
|
||||
uint to_spread = src->cargo.TotalCount() - src->cargo_cap;
|
||||
for (Vehicle *dest = v; dest != NULL && to_spread != 0; dest = dest->Next()) {
|
||||
assert(dest->cargo.TotalCount() == dest->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
|
||||
if (dest->cargo.TotalCount() >= dest->cargo_cap || dest->cargo_type != src->cargo_type) continue;
|
||||
|
||||
uint amount = min(to_spread, dest->cargo_cap - dest->cargo.TotalCount());
|
||||
src->cargo.Shift(amount, &dest->cargo);
|
||||
to_spread -= amount;
|
||||
}
|
||||
|
||||
/* Any left-overs will be thrown away, but not their feeder share. */
|
||||
if (src->cargo_cap < src->cargo.TotalCount()) src->cargo.Truncate(src->cargo.TotalCount() - src->cargo_cap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer cargo from a single (articulated )old vehicle to the new vehicle chain
|
||||
* @param old_veh Old vehicle that will be sold
|
||||
* @param new_head Head of the completely constructed new vehicle chain
|
||||
* @param part_of_chain The vehicle is part of a train
|
||||
* @pre You can only do this if both consists are not loading or unloading.
|
||||
* They must not carry reserved cargo, nor cargo to be unloaded or
|
||||
* transferred.
|
||||
*/
|
||||
static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chain)
|
||||
{
|
||||
assert(!part_of_chain || new_head->IsPrimaryVehicle());
|
||||
/* Loop through source parts */
|
||||
for (Vehicle *src = old_veh; src != NULL; src = src->Next()) {
|
||||
assert(src->cargo.TotalCount() == src->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
|
||||
if (!part_of_chain && src->type == VEH_TRAIN && src != old_veh && src != Train::From(old_veh)->other_multiheaded_part && !src->IsArticulatedPart()) {
|
||||
/* Skip vehicles, which do not belong to old_veh */
|
||||
src = src->GetLastEnginePart();
|
||||
continue;
|
||||
}
|
||||
if (src->cargo_type >= NUM_CARGO || src->cargo.TotalCount() == 0) continue;
|
||||
|
||||
/* Find free space in the new chain */
|
||||
for (Vehicle *dest = new_head; dest != NULL && src->cargo.TotalCount() > 0; dest = dest->Next()) {
|
||||
assert(dest->cargo.TotalCount() == dest->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
|
||||
if (!part_of_chain && dest->type == VEH_TRAIN && dest != new_head && dest != Train::From(new_head)->other_multiheaded_part && !dest->IsArticulatedPart()) {
|
||||
/* Skip vehicles, which do not belong to new_head */
|
||||
dest = dest->GetLastEnginePart();
|
||||
continue;
|
||||
}
|
||||
if (dest->cargo_type != src->cargo_type) continue;
|
||||
|
||||
uint amount = min(src->cargo.TotalCount(), dest->cargo_cap - dest->cargo.TotalCount());
|
||||
if (amount <= 0) continue;
|
||||
|
||||
src->cargo.Shift(amount, &dest->cargo);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update train weight etc., the old vehicle will be sold anyway */
|
||||
if (part_of_chain && new_head->type == VEH_TRAIN) Train::From(new_head)->ConsistChanged(CCF_LOADUNLOAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether refit orders that applied to v will also apply to the new vehicle type
|
||||
* @param v The vehicle to be replaced
|
||||
* @param engine_type The type we want to replace with
|
||||
* @return true iff all refit orders stay valid
|
||||
*/
|
||||
static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_type)
|
||||
{
|
||||
|
||||
uint32 union_refit_mask_a = GetUnionOfArticulatedRefitMasks(v->engine_type, false);
|
||||
uint32 union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
|
||||
|
||||
const Order *o;
|
||||
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
|
||||
FOR_VEHICLE_ORDERS(u, o) {
|
||||
if (!o->IsRefit() || o->IsAutoRefit()) continue;
|
||||
CargoID cargo_type = o->GetRefitCargo();
|
||||
|
||||
if (!HasBit(union_refit_mask_a, cargo_type)) continue;
|
||||
if (!HasBit(union_refit_mask_b, cargo_type)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to find what type of cargo to refit to when autoreplacing
|
||||
* @param *v Original vehicle that is being replaced.
|
||||
* @param engine_type The EngineID of the vehicle that is being replaced to
|
||||
* @param part_of_chain The vehicle is part of a train
|
||||
* @return The cargo type to replace to
|
||||
* CT_NO_REFIT is returned if no refit is needed
|
||||
* CT_INVALID is returned when both old and new vehicle got cargo capacity and refitting the new one to the old one's cargo type isn't possible
|
||||
*/
|
||||
static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain)
|
||||
{
|
||||
uint32 available_cargo_types, union_mask;
|
||||
GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types);
|
||||
|
||||
if (union_mask == 0) return CT_NO_REFIT; // Don't try to refit an engine with no cargo capacity
|
||||
|
||||
CargoID cargo_type;
|
||||
if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) return CT_INVALID; // We cannot refit to mixed cargoes in an automated way
|
||||
|
||||
if (cargo_type == CT_INVALID) {
|
||||
if (v->type != VEH_TRAIN) return CT_NO_REFIT; // If the vehicle does not carry anything at all, every replacement is fine.
|
||||
|
||||
if (!part_of_chain) return CT_NO_REFIT;
|
||||
|
||||
/* the old engine didn't have cargo capacity, but the new one does
|
||||
* now we will figure out what cargo the train is carrying and refit to fit this */
|
||||
|
||||
for (v = v->First(); v != NULL; v = v->Next()) {
|
||||
if (!v->GetEngine()->CanCarryCargo()) continue;
|
||||
/* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */
|
||||
if (HasBit(available_cargo_types, v->cargo_type)) return v->cargo_type;
|
||||
}
|
||||
|
||||
return CT_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one
|
||||
} else {
|
||||
if (!HasBit(available_cargo_types, cargo_type)) return CT_INVALID; // We can't refit the vehicle to carry the cargo we want
|
||||
|
||||
if (part_of_chain && !VerifyAutoreplaceRefitForOrders(v, engine_type)) return CT_INVALID; // Some refit orders lose their effect
|
||||
|
||||
return cargo_type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EngineID of the replacement for a vehicle
|
||||
* @param v The vehicle to find a replacement for
|
||||
* @param c The vehicle's owner (it's faster to forward the pointer than refinding it)
|
||||
* @param always_replace Always replace, even if not old.
|
||||
* @param [out] e the EngineID of the replacement. INVALID_ENGINE if no replacement is found
|
||||
* @return Error if the engine to build is not available
|
||||
*/
|
||||
static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool always_replace, EngineID &e)
|
||||
{
|
||||
assert(v->type != VEH_TRAIN || !v->IsArticulatedPart());
|
||||
|
||||
e = INVALID_ENGINE;
|
||||
|
||||
if (v->type == VEH_TRAIN && Train::From(v)->IsRearDualheaded()) {
|
||||
/* we build the rear ends of multiheaded trains with the front ones */
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
bool replace_when_old;
|
||||
e = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
|
||||
if (!always_replace && replace_when_old && !v->NeedsAutorenewing(c, false)) e = INVALID_ENGINE;
|
||||
|
||||
/* Autoreplace, if engine is available */
|
||||
if (e != INVALID_ENGINE && IsEngineBuildable(e, v->type, _current_company)) {
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
/* Autorenew if needed */
|
||||
if (v->NeedsAutorenewing(c)) e = v->engine_type;
|
||||
|
||||
/* Nothing to do or all is fine? */
|
||||
if (e == INVALID_ENGINE || IsEngineBuildable(e, v->type, _current_company)) return CommandCost();
|
||||
|
||||
/* The engine we need is not available. Report error to user */
|
||||
return CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + v->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and refits a replacement vehicle
|
||||
* Important: The old vehicle is still in the original vehicle chain (used for determining the cargo when the old vehicle did not carry anything, but the new one does)
|
||||
* @param old_veh A single (articulated/multiheaded) vehicle that shall be replaced.
|
||||
* @param new_vehicle Returns the newly build and refitted vehicle
|
||||
* @param part_of_chain The vehicle is part of a train
|
||||
* @return cost or error
|
||||
*/
|
||||
static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain)
|
||||
{
|
||||
*new_vehicle = NULL;
|
||||
|
||||
/* Shall the vehicle be replaced? */
|
||||
const Company *c = Company::Get(_current_company);
|
||||
EngineID e;
|
||||
CommandCost cost = GetNewEngineType(old_veh, c, true, e);
|
||||
if (cost.Failed()) return cost;
|
||||
if (e == INVALID_ENGINE) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered
|
||||
|
||||
/* Does it need to be refitted */
|
||||
CargoID refit_cargo = GetNewCargoTypeForReplace(old_veh, e, part_of_chain);
|
||||
if (refit_cargo == CT_INVALID) return CommandCost(); // incompatible cargoes
|
||||
|
||||
/* Build the new vehicle */
|
||||
cost = DoCommand(old_veh->tile, e, 0, DC_EXEC | DC_AUTOREPLACE, GetCmdBuildVeh(old_veh));
|
||||
if (cost.Failed()) return cost;
|
||||
|
||||
Vehicle *new_veh = Vehicle::Get(_new_vehicle_id);
|
||||
*new_vehicle = new_veh;
|
||||
|
||||
/* Refit the vehicle if needed */
|
||||
if (refit_cargo != CT_NO_REFIT) {
|
||||
byte subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo);
|
||||
|
||||
cost.AddCost(DoCommand(0, new_veh->index, refit_cargo | (subtype << 8), DC_EXEC, GetCmdRefitVeh(new_veh)));
|
||||
assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace()
|
||||
}
|
||||
|
||||
/* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */
|
||||
if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) {
|
||||
DoCommand(0, new_veh->index, true, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue a start/stop command
|
||||
* @param v a vehicle
|
||||
* @param evaluate_callback shall the start/stop callback be evaluated?
|
||||
* @return success or error
|
||||
*/
|
||||
static inline CommandCost CmdStartStopVehicle(const Vehicle *v, bool evaluate_callback)
|
||||
{
|
||||
return DoCommand(0, v->index, evaluate_callback ? 1 : 0, DC_EXEC | DC_AUTOREPLACE, CMD_START_STOP_VEHICLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue a train vehicle move command
|
||||
* @param v The vehicle to move
|
||||
* @param after The vehicle to insert 'v' after, or NULL to start new chain
|
||||
* @param flags the command flags to use
|
||||
* @param whole_chain move all vehicles following 'v' (true), or only 'v' (false)
|
||||
* @return success or error
|
||||
*/
|
||||
static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlag flags, bool whole_chain)
|
||||
{
|
||||
return DoCommand(0, v->index | (whole_chain ? 1 : 0) << 20, after != NULL ? after->index : INVALID_VEHICLE, flags | DC_NO_CARGO_CAP_CHECK, CMD_MOVE_RAIL_VEHICLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy head specific things to the new vehicle chain after it was successfully constructed
|
||||
* @param old_head The old front vehicle (no wagons attached anymore)
|
||||
* @param new_head The new head of the completely replaced vehicle chain
|
||||
* @param flags the command flags to use
|
||||
*/
|
||||
static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags)
|
||||
{
|
||||
CommandCost cost = CommandCost();
|
||||
|
||||
/* Share orders */
|
||||
if (cost.Succeeded() && old_head != new_head) cost.AddCost(DoCommand(0, new_head->index | CO_SHARE << 30, old_head->index, DC_EXEC, CMD_CLONE_ORDER));
|
||||
|
||||
/* Copy group membership */
|
||||
if (cost.Succeeded() && old_head != new_head) cost.AddCost(DoCommand(0, old_head->group_id, new_head->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP));
|
||||
|
||||
/* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */
|
||||
if (cost.Succeeded()) {
|
||||
/* Start the vehicle, might be denied by certain things */
|
||||
assert((new_head->vehstatus & VS_STOPPED) != 0);
|
||||
cost.AddCost(CmdStartStopVehicle(new_head, true));
|
||||
|
||||
/* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */
|
||||
if (cost.Succeeded()) cost.AddCost(CmdStartStopVehicle(new_head, false));
|
||||
}
|
||||
|
||||
/* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */
|
||||
if (cost.Succeeded() && old_head != new_head && (flags & DC_EXEC) != 0) {
|
||||
/* Copy other things which cannot be copied by a command and which shall not stay resetted from the build vehicle command */
|
||||
new_head->CopyVehicleConfigAndStatistics(old_head);
|
||||
|
||||
/* Switch vehicle windows/news to the new vehicle, so they are not closed/deleted when the old vehicle is sold */
|
||||
ChangeVehicleViewports(old_head->index, new_head->index);
|
||||
ChangeVehicleViewWindow(old_head->index, new_head->index);
|
||||
ChangeVehicleNews(old_head->index, new_head->index);
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a single unit in a free wagon chain
|
||||
* @param single_unit vehicle to let autoreplace/renew operator on
|
||||
* @param flags command flags
|
||||
* @param nothing_to_do is set to 'false' when something was done (only valid when not failed)
|
||||
* @return cost or error
|
||||
*/
|
||||
static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, bool *nothing_to_do)
|
||||
{
|
||||
Train *old_v = Train::From(*single_unit);
|
||||
assert(!old_v->IsArticulatedPart() && !old_v->IsRearDualheaded());
|
||||
|
||||
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0);
|
||||
|
||||
/* Build and refit replacement vehicle */
|
||||
Vehicle *new_v = NULL;
|
||||
cost.AddCost(BuildReplacementVehicle(old_v, &new_v, false));
|
||||
|
||||
/* Was a new vehicle constructed? */
|
||||
if (cost.Succeeded() && new_v != NULL) {
|
||||
*nothing_to_do = false;
|
||||
|
||||
if ((flags & DC_EXEC) != 0) {
|
||||
/* Move the new vehicle behind the old */
|
||||
CmdMoveVehicle(new_v, old_v, DC_EXEC, false);
|
||||
|
||||
/* Take over cargo
|
||||
* Note: We do only transfer cargo from the old to the new vehicle.
|
||||
* I.e. we do not transfer remaining cargo to other vehicles.
|
||||
* Else you would also need to consider moving cargo to other free chains,
|
||||
* or doing the same in ReplaceChain(), which would be quite troublesome.
|
||||
*/
|
||||
TransferCargo(old_v, new_v, false);
|
||||
|
||||
*single_unit = new_v;
|
||||
}
|
||||
|
||||
/* Sell the old vehicle */
|
||||
cost.AddCost(DoCommand(0, old_v->index, 0, flags, GetCmdSellVeh(old_v)));
|
||||
|
||||
/* If we are not in DC_EXEC undo everything */
|
||||
if ((flags & DC_EXEC) == 0) {
|
||||
DoCommand(0, new_v->index, 0, DC_EXEC, GetCmdSellVeh(new_v));
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a whole vehicle chain
|
||||
* @param chain vehicle chain to let autoreplace/renew operator on
|
||||
* @param flags command flags
|
||||
* @param wagon_removal remove wagons when the resulting chain occupies more tiles than the old did
|
||||
* @param nothing_to_do is set to 'false' when something was done (only valid when not failed)
|
||||
* @return cost or error
|
||||
*/
|
||||
static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon_removal, bool *nothing_to_do)
|
||||
{
|
||||
Vehicle *old_head = *chain;
|
||||
assert(old_head->IsPrimaryVehicle());
|
||||
|
||||
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0);
|
||||
|
||||
if (old_head->type == VEH_TRAIN) {
|
||||
/* Store the length of the old vehicle chain, rounded up to whole tiles */
|
||||
uint16 old_total_length = CeilDiv(Train::From(old_head)->gcache.cached_total_length, TILE_SIZE) * TILE_SIZE;
|
||||
|
||||
int num_units = 0; ///< Number of units in the chain
|
||||
for (Train *w = Train::From(old_head); w != NULL; w = w->GetNextUnit()) num_units++;
|
||||
|
||||
Train **old_vehs = CallocT<Train *>(num_units); ///< Will store vehicles of the old chain in their order
|
||||
Train **new_vehs = CallocT<Train *>(num_units); ///< New vehicles corresponding to old_vehs or NULL if no replacement
|
||||
Money *new_costs = MallocT<Money>(num_units); ///< Costs for buying and refitting the new vehicles
|
||||
|
||||
/* Collect vehicles and build replacements
|
||||
* Note: The replacement vehicles can only successfully build as long as the old vehicles are still in their chain */
|
||||
int i;
|
||||
Train *w;
|
||||
for (w = Train::From(old_head), i = 0; w != NULL; w = w->GetNextUnit(), i++) {
|
||||
assert(i < num_units);
|
||||
old_vehs[i] = w;
|
||||
|
||||
CommandCost ret = BuildReplacementVehicle(old_vehs[i], (Vehicle**)&new_vehs[i], true);
|
||||
cost.AddCost(ret);
|
||||
if (cost.Failed()) break;
|
||||
|
||||
new_costs[i] = ret.GetCost();
|
||||
if (new_vehs[i] != NULL) *nothing_to_do = false;
|
||||
}
|
||||
Train *new_head = (new_vehs[0] != NULL ? new_vehs[0] : old_vehs[0]);
|
||||
|
||||
/* Note: When autoreplace has already failed here, old_vehs[] is not completely initialized. But it is also not needed. */
|
||||
if (cost.Succeeded()) {
|
||||
/* Separate the head, so we can start constructing the new chain */
|
||||
Train *second = Train::From(old_head)->GetNextUnit();
|
||||
if (second != NULL) cost.AddCost(CmdMoveVehicle(second, NULL, DC_EXEC | DC_AUTOREPLACE, true));
|
||||
|
||||
assert(Train::From(new_head)->GetNextUnit() == NULL);
|
||||
|
||||
/* Append engines to the new chain
|
||||
* We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
|
||||
* That way we also have less trouble when exceeding the unitnumber limit.
|
||||
* OTOH the vehicle attach callback is more expensive this way :s */
|
||||
Train *last_engine = NULL; ///< Shall store the last engine unit after this step
|
||||
if (cost.Succeeded()) {
|
||||
for (int i = num_units - 1; i > 0; i--) {
|
||||
Train *append = (new_vehs[i] != NULL ? new_vehs[i] : old_vehs[i]);
|
||||
|
||||
if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) continue;
|
||||
|
||||
if (new_vehs[i] != NULL) {
|
||||
/* Move the old engine to a separate row with DC_AUTOREPLACE. Else
|
||||
* moving the wagon in front may fail later due to unitnumber limit.
|
||||
* (We have to attach wagons without DC_AUTOREPLACE.) */
|
||||
CmdMoveVehicle(old_vehs[i], NULL, DC_EXEC | DC_AUTOREPLACE, false);
|
||||
}
|
||||
|
||||
if (last_engine == NULL) last_engine = append;
|
||||
cost.AddCost(CmdMoveVehicle(append, new_head, DC_EXEC, false));
|
||||
if (cost.Failed()) break;
|
||||
}
|
||||
if (last_engine == NULL) last_engine = new_head;
|
||||
}
|
||||
|
||||
/* When wagon removal is enabled and the new engines without any wagons are already longer than the old, we have to fail */
|
||||
if (cost.Succeeded() && wagon_removal && new_head->gcache.cached_total_length > old_total_length) cost = CommandCost(STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT);
|
||||
|
||||
/* Append/insert wagons into the new vehicle chain
|
||||
* We do this from back to front, so we can stop when wagon removal or maximum train length (i.e. from mammoth-train setting) is triggered.
|
||||
*/
|
||||
if (cost.Succeeded()) {
|
||||
for (int i = num_units - 1; i > 0; i--) {
|
||||
assert(last_engine != NULL);
|
||||
Vehicle *append = (new_vehs[i] != NULL ? new_vehs[i] : old_vehs[i]);
|
||||
|
||||
if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) {
|
||||
/* Insert wagon after 'last_engine' */
|
||||
CommandCost res = CmdMoveVehicle(append, last_engine, DC_EXEC, false);
|
||||
|
||||
/* When we allow removal of wagons, either the move failing due
|
||||
* to the train becoming too long, or the train becoming longer
|
||||
* would move the vehicle to the empty vehicle chain. */
|
||||
if (wagon_removal && (res.Failed() ? res.GetErrorMessage() == STR_ERROR_TRAIN_TOO_LONG : new_head->gcache.cached_total_length > old_total_length)) {
|
||||
CmdMoveVehicle(append, NULL, DC_EXEC | DC_AUTOREPLACE, false);
|
||||
break;
|
||||
}
|
||||
|
||||
cost.AddCost(res);
|
||||
if (cost.Failed()) break;
|
||||
} else {
|
||||
/* We have reached 'last_engine', continue with the next engine towards the front */
|
||||
assert(append == last_engine);
|
||||
last_engine = last_engine->GetPrevUnit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sell superfluous new vehicles that could not be inserted. */
|
||||
if (cost.Succeeded() && wagon_removal) {
|
||||
assert(new_head->gcache.cached_total_length <= _settings_game.vehicle.max_train_length * TILE_SIZE);
|
||||
for (int i = 1; i < num_units; i++) {
|
||||
Vehicle *wagon = new_vehs[i];
|
||||
if (wagon == NULL) continue;
|
||||
if (wagon->First() == new_head) break;
|
||||
|
||||
assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON);
|
||||
|
||||
/* Sell wagon */
|
||||
CommandCost ret = DoCommand(0, wagon->index, 0, DC_EXEC, GetCmdSellVeh(wagon));
|
||||
assert(ret.Succeeded());
|
||||
new_vehs[i] = NULL;
|
||||
|
||||
/* Revert the money subtraction when the vehicle was built.
|
||||
* This value is different from the sell value, esp. because of refitting */
|
||||
cost.AddCost(-new_costs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* The new vehicle chain is constructed, now take over orders and everything... */
|
||||
if (cost.Succeeded()) cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
|
||||
|
||||
if (cost.Succeeded()) {
|
||||
/* Success ! */
|
||||
if ((flags & DC_EXEC) != 0 && new_head != old_head) {
|
||||
*chain = new_head;
|
||||
}
|
||||
|
||||
/* Transfer cargo of old vehicles and sell them */
|
||||
for (int i = 0; i < num_units; i++) {
|
||||
Vehicle *w = old_vehs[i];
|
||||
/* Is the vehicle again part of the new chain?
|
||||
* Note: We cannot test 'new_vehs[i] != NULL' as wagon removal might cause to remove both */
|
||||
if (w->First() == new_head) continue;
|
||||
|
||||
if ((flags & DC_EXEC) != 0) TransferCargo(w, new_head, true);
|
||||
|
||||
/* Sell the vehicle.
|
||||
* Note: This might temporarly construct new trains, so use DC_AUTOREPLACE to prevent
|
||||
* it from failing due to engine limits. */
|
||||
cost.AddCost(DoCommand(0, w->index, 0, flags | DC_AUTOREPLACE, GetCmdSellVeh(w)));
|
||||
if ((flags & DC_EXEC) != 0) {
|
||||
old_vehs[i] = NULL;
|
||||
if (i == 0) old_head = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & DC_EXEC) != 0) CheckCargoCapacity(new_head);
|
||||
}
|
||||
|
||||
/* If we are not in DC_EXEC undo everything, i.e. rearrange old vehicles.
|
||||
* We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
|
||||
* Note: The vehicle attach callback is disabled here :) */
|
||||
if ((flags & DC_EXEC) == 0) {
|
||||
/* Separate the head, so we can reattach the old vehicles */
|
||||
Train *second = Train::From(old_head)->GetNextUnit();
|
||||
if (second != NULL) CmdMoveVehicle(second, NULL, DC_EXEC | DC_AUTOREPLACE, true);
|
||||
|
||||
assert(Train::From(old_head)->GetNextUnit() == NULL);
|
||||
|
||||
for (int i = num_units - 1; i > 0; i--) {
|
||||
CommandCost ret = CmdMoveVehicle(old_vehs[i], old_head, DC_EXEC | DC_AUTOREPLACE, false);
|
||||
assert(ret.Succeeded());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally undo buying of new vehicles */
|
||||
if ((flags & DC_EXEC) == 0) {
|
||||
for (int i = num_units - 1; i >= 0; i--) {
|
||||
if (new_vehs[i] != NULL) {
|
||||
DoCommand(0, new_vehs[i]->index, 0, DC_EXEC, GetCmdSellVeh(new_vehs[i]));
|
||||
new_vehs[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(old_vehs);
|
||||
free(new_vehs);
|
||||
free(new_costs);
|
||||
} else {
|
||||
/* Build and refit replacement vehicle */
|
||||
Vehicle *new_head = NULL;
|
||||
cost.AddCost(BuildReplacementVehicle(old_head, &new_head, true));
|
||||
|
||||
/* Was a new vehicle constructed? */
|
||||
if (cost.Succeeded() && new_head != NULL) {
|
||||
*nothing_to_do = false;
|
||||
|
||||
/* The new vehicle is constructed, now take over orders and everything... */
|
||||
cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
|
||||
|
||||
if (cost.Succeeded()) {
|
||||
/* The new vehicle is constructed, now take over cargo */
|
||||
if ((flags & DC_EXEC) != 0) {
|
||||
TransferCargo(old_head, new_head, true);
|
||||
*chain = new_head;
|
||||
}
|
||||
|
||||
/* Sell the old vehicle */
|
||||
cost.AddCost(DoCommand(0, old_head->index, 0, flags, GetCmdSellVeh(old_head)));
|
||||
}
|
||||
|
||||
/* If we are not in DC_EXEC undo everything */
|
||||
if ((flags & DC_EXEC) == 0) {
|
||||
DoCommand(0, new_head->index, 0, DC_EXEC, GetCmdSellVeh(new_head));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoreplaces a vehicle
|
||||
* Trains are replaced as a whole chain, free wagons in depot are replaced on their own
|
||||
* @param tile not used
|
||||
* @param flags type of operation
|
||||
* @param p1 Index of vehicle
|
||||
* @param p2 not used
|
||||
* @param text unused
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||
{
|
||||
Vehicle *v = Vehicle::GetIfValid(p1);
|
||||
if (v == NULL) return CMD_ERROR;
|
||||
|
||||
CommandCost ret = CheckOwnership(v->owner);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
if (!v->IsChainInDepot()) return CMD_ERROR;
|
||||
if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
|
||||
|
||||
bool free_wagon = false;
|
||||
if (v->type == VEH_TRAIN) {
|
||||
Train *t = Train::From(v);
|
||||
if (t->IsArticulatedPart() || t->IsRearDualheaded()) return CMD_ERROR;
|
||||
free_wagon = !t->IsFrontEngine();
|
||||
if (free_wagon && t->First()->IsFrontEngine()) return CMD_ERROR;
|
||||
} else {
|
||||
if (!v->IsPrimaryVehicle()) return CMD_ERROR;
|
||||
}
|
||||
|
||||
const Company *c = Company::Get(_current_company);
|
||||
bool wagon_removal = c->settings.renew_keep_length;
|
||||
|
||||
/* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */
|
||||
Vehicle *w = v;
|
||||
bool any_replacements = false;
|
||||
while (w != NULL) {
|
||||
EngineID e;
|
||||
CommandCost cost = GetNewEngineType(w, c, false, e);
|
||||
if (cost.Failed()) return cost;
|
||||
any_replacements |= (e != INVALID_ENGINE);
|
||||
w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : NULL);
|
||||
}
|
||||
|
||||
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0);
|
||||
bool nothing_to_do = true;
|
||||
|
||||
if (any_replacements) {
|
||||
bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0);
|
||||
|
||||
/* Stop the vehicle */
|
||||
if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, true));
|
||||
if (cost.Failed()) return cost;
|
||||
|
||||
assert(free_wagon || v->IsStoppedInDepot());
|
||||
|
||||
/* We have to construct the new vehicle chain to test whether it is valid.
|
||||
* Vehicle construction needs random bits, so we have to save the random seeds
|
||||
* to prevent desyncs and to replay newgrf callbacks during DC_EXEC */
|
||||
SavedRandomSeeds saved_seeds;
|
||||
SaveRandomSeeds(&saved_seeds);
|
||||
if (free_wagon) {
|
||||
cost.AddCost(ReplaceFreeUnit(&v, flags & ~DC_EXEC, ¬hing_to_do));
|
||||
} else {
|
||||
cost.AddCost(ReplaceChain(&v, flags & ~DC_EXEC, wagon_removal, ¬hing_to_do));
|
||||
}
|
||||
RestoreRandomSeeds(saved_seeds);
|
||||
|
||||
if (cost.Succeeded() && (flags & DC_EXEC) != 0) {
|
||||
CommandCost ret;
|
||||
if (free_wagon) {
|
||||
ret = ReplaceFreeUnit(&v, flags, ¬hing_to_do);
|
||||
} else {
|
||||
ret = ReplaceChain(&v, flags, wagon_removal, ¬hing_to_do);
|
||||
}
|
||||
assert(ret.Succeeded() && ret.GetCost() == cost.GetCost());
|
||||
}
|
||||
|
||||
/* Restart the vehicle */
|
||||
if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, false));
|
||||
}
|
||||
|
||||
if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change engine renewal parameters
|
||||
* @param tile unused
|
||||
* @param flags operation to perform
|
||||
* @param p1 packed data
|
||||
* - bit 0 = replace when engine gets old?
|
||||
* - bits 16-31 = engine group
|
||||
* @param p2 packed data
|
||||
* - bits 0-15 = old engine type
|
||||
* - bits 16-31 = new engine type
|
||||
* @param text unused
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdSetAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||
{
|
||||
Company *c = Company::GetIfValid(_current_company);
|
||||
if (c == NULL) return CMD_ERROR;
|
||||
|
||||
EngineID old_engine_type = GB(p2, 0, 16);
|
||||
EngineID new_engine_type = GB(p2, 16, 16);
|
||||
GroupID id_g = GB(p1, 16, 16);
|
||||
CommandCost cost;
|
||||
|
||||
if (Group::IsValidID(id_g) ? Group::Get(id_g)->owner != _current_company : !IsAllGroupID(id_g) && !IsDefaultGroupID(id_g)) return CMD_ERROR;
|
||||
if (!Engine::IsValidID(old_engine_type)) return CMD_ERROR;
|
||||
|
||||
if (new_engine_type != INVALID_ENGINE) {
|
||||
if (!Engine::IsValidID(new_engine_type)) return CMD_ERROR;
|
||||
if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR;
|
||||
|
||||
cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, HasBit(p1, 0), flags);
|
||||
} else {
|
||||
cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags);
|
||||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
GroupStatistics::UpdateAutoreplace(_current_company);
|
||||
if (IsLocalCompany()) SetWindowDirty(WC_REPLACE_VEHICLE, Engine::Get(old_engine_type)->type);
|
||||
}
|
||||
if ((flags & DC_EXEC) && IsLocalCompany()) InvalidateAutoreplaceWindow(old_engine_type, id_g);
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
102
src/autoreplace_func.h
Normal file
102
src/autoreplace_func.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file autoreplace_func.h Functions related to autoreplacing. */
|
||||
|
||||
#ifndef AUTOREPLACE_FUNC_H
|
||||
#define AUTOREPLACE_FUNC_H
|
||||
|
||||
#include "command_type.h"
|
||||
#include "company_base.h"
|
||||
|
||||
void RemoveAllEngineReplacement(EngineRenewList *erl);
|
||||
EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old = NULL);
|
||||
CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags);
|
||||
CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, DoCommandFlag flags);
|
||||
|
||||
/**
|
||||
* Remove all engine replacement settings for the given company.
|
||||
* @param c the company.
|
||||
*/
|
||||
static inline void RemoveAllEngineReplacementForCompany(Company *c)
|
||||
{
|
||||
RemoveAllEngineReplacement(&c->engine_renew_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the engine replacement for the given company and original engine type.
|
||||
* @param c company.
|
||||
* @param engine Engine type.
|
||||
* @param group The group related to this replacement.
|
||||
* @param[out] replace_when_old Set to true if the replacement should be done when old.
|
||||
* @return The engine type to replace with, or INVALID_ENGINE if no
|
||||
* replacement is in the list.
|
||||
*/
|
||||
static inline EngineID EngineReplacementForCompany(const Company *c, EngineID engine, GroupID group, bool *replace_when_old = NULL)
|
||||
{
|
||||
return EngineReplacement(c->engine_renew_list, engine, group, replace_when_old);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a company has a replacement set up for the given engine.
|
||||
* @param c Company.
|
||||
* @param engine Engine type to be replaced.
|
||||
* @param group The group related to this replacement.
|
||||
* @return true if a replacement was set up, false otherwise.
|
||||
*/
|
||||
static inline bool EngineHasReplacementForCompany(const Company *c, EngineID engine, GroupID group)
|
||||
{
|
||||
return EngineReplacementForCompany(c, engine, group) != INVALID_ENGINE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a company has a replacement set up for the given engine when it gets old.
|
||||
* @param c Company.
|
||||
* @param engine Engine type to be replaced.
|
||||
* @param group The group related to this replacement.
|
||||
* @return True if a replacement when old was set up, false otherwise.
|
||||
*/
|
||||
static inline bool EngineHasReplacementWhenOldForCompany(const Company *c, EngineID engine, GroupID group)
|
||||
{
|
||||
bool replace_when_old;
|
||||
EngineReplacement(c->engine_renew_list, engine, group, &replace_when_old);
|
||||
return replace_when_old;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an engine replacement for the company.
|
||||
* @param c Company.
|
||||
* @param old_engine The original engine type.
|
||||
* @param new_engine The replacement engine type.
|
||||
* @param group The group related to this replacement.
|
||||
* @param replace_when_old Replace when old or always?
|
||||
* @param flags The calling command flags.
|
||||
* @return 0 on success, CMD_ERROR on failure.
|
||||
*/
|
||||
static inline CommandCost AddEngineReplacementForCompany(Company *c, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags)
|
||||
{
|
||||
return AddEngineReplacement(&c->engine_renew_list, old_engine, new_engine, group, replace_when_old, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an engine replacement for the company.
|
||||
* @param c Company.
|
||||
* @param engine The original engine type.
|
||||
* @param group The group related to this replacement.
|
||||
* @param flags The calling command flags.
|
||||
* @return 0 on success, CMD_ERROR on failure.
|
||||
*/
|
||||
static inline CommandCost RemoveEngineReplacementForCompany(Company *c, EngineID engine, GroupID group, DoCommandFlag flags)
|
||||
{
|
||||
return RemoveEngineReplacement(&c->engine_renew_list, engine, group, flags);
|
||||
}
|
||||
|
||||
bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company);
|
||||
|
||||
#endif /* AUTOREPLACE_FUNC_H */
|
||||
722
src/autoreplace_gui.cpp
Normal file
722
src/autoreplace_gui.cpp
Normal file
@@ -0,0 +1,722 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file autoreplace_gui.cpp GUI for autoreplace handling. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "command_func.h"
|
||||
#include "vehicle_gui.h"
|
||||
#include "newgrf_engine.h"
|
||||
#include "rail.h"
|
||||
#include "strings_func.h"
|
||||
#include "window_func.h"
|
||||
#include "autoreplace_func.h"
|
||||
#include "company_func.h"
|
||||
#include "engine_base.h"
|
||||
#include "window_gui.h"
|
||||
#include "engine_gui.h"
|
||||
#include "settings_func.h"
|
||||
#include "core/geometry_func.hpp"
|
||||
#include "rail_gui.h"
|
||||
#include "widgets/dropdown_func.h"
|
||||
|
||||
#include "widgets/autoreplace_widget.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
void DrawEngineList(VehicleType type, int x, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
|
||||
|
||||
static int CDECL EngineNumberSorter(const EngineID *a, const EngineID *b)
|
||||
{
|
||||
int r = Engine::Get(*a)->list_position - Engine::Get(*b)->list_position;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the left autoreplace list if an engine is removed or added
|
||||
* @param e Engine to check if it is removed or added
|
||||
* @param id_g The group the engine belongs to
|
||||
* Note: this function only works if it is called either
|
||||
* - when a new vehicle is build, but before it's counted in num_engines
|
||||
* - when a vehicle is deleted and after it's subtracted from num_engines
|
||||
* - when not changing the count (used when changing replace orders)
|
||||
*/
|
||||
void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g)
|
||||
{
|
||||
if (GetGroupNumEngines(_local_company, id_g, e) == 0 || GetGroupNumEngines(_local_company, ALL_GROUP, e) == 0) {
|
||||
/* We don't have any of this engine type.
|
||||
* Either we just sold the last one, we build a new one or we stopped replacing it.
|
||||
* In all cases, we need to update the left list */
|
||||
InvalidateWindowData(WC_REPLACE_VEHICLE, Engine::Get(e)->type, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When an engine is made buildable or is removed from being buildable, add/remove it from the build/autoreplace lists
|
||||
* @param type The type of engine
|
||||
*/
|
||||
void AddRemoveEngineFromAutoreplaceAndBuildWindows(VehicleType type)
|
||||
{
|
||||
InvalidateWindowData(WC_REPLACE_VEHICLE, type, 0); // Update the autoreplace window
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
|
||||
}
|
||||
|
||||
static const StringID _start_replace_dropdown[] = {
|
||||
STR_REPLACE_VEHICLES_NOW,
|
||||
STR_REPLACE_VEHICLES_WHEN_OLD,
|
||||
INVALID_STRING_ID
|
||||
};
|
||||
|
||||
/**
|
||||
* Window for the autoreplacing of vehicles.
|
||||
*/
|
||||
class ReplaceVehicleWindow : public Window {
|
||||
EngineID sel_engine[2]; ///< Selected engine left and right.
|
||||
GUIEngineList engines[2]; ///< Left and right list of engines.
|
||||
bool replace_engines; ///< If \c true, engines are replaced, if \c false, wagons are replaced (only for trains).
|
||||
bool reset_sel_engine; ///< Also reset #sel_engine while updating left and/or right (#update_left and/or #update_right) and no valid engine selected.
|
||||
GroupID sel_group; ///< Group selected to replace.
|
||||
int details_height; ///< Minimal needed height of the details panels (found so far).
|
||||
byte sort_criteria; ///< Criteria of sorting vehicles.
|
||||
bool descending_sort_order; ///< Order of sorting vehicles.
|
||||
bool show_hidden_engines; ///< Whether to show the hidden engines.
|
||||
RailType sel_railtype; ///< Type of rail tracks selected.
|
||||
Scrollbar *vscroll[2];
|
||||
|
||||
/**
|
||||
* Figure out if an engine should be added to a list.
|
||||
* @param e The EngineID.
|
||||
* @param draw_left If \c true, the left list is drawn (the engines specific to the railtype you selected).
|
||||
* @param show_engines If \c true, the locomotives are drawn, else the wagons are drawn (never both).
|
||||
* @return \c true if the engine should be in the list (based on this check), else \c false.
|
||||
*/
|
||||
bool GenerateReplaceRailList(EngineID e, bool draw_left, bool show_engines)
|
||||
{
|
||||
const RailVehicleInfo *rvi = RailVehInfo(e);
|
||||
|
||||
/* Ensure that the wagon/engine selection fits the engine. */
|
||||
if ((rvi->railveh_type == RAILVEH_WAGON) == show_engines) return false;
|
||||
|
||||
if (draw_left && show_engines) {
|
||||
/* Ensure that the railtype is specific to the selected one */
|
||||
if (rvi->railtype != this->sel_railtype) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate an engines list
|
||||
* @param draw_left true if generating the left list, otherwise false
|
||||
*/
|
||||
void GenerateReplaceVehList(bool draw_left)
|
||||
{
|
||||
EngineID selected_engine = INVALID_ENGINE;
|
||||
VehicleType type = (VehicleType)this->window_number;
|
||||
byte side = draw_left ? 0 : 1;
|
||||
|
||||
GUIEngineList *list = &this->engines[side];
|
||||
list->Clear();
|
||||
|
||||
const Engine *e;
|
||||
FOR_ALL_ENGINES_OF_TYPE(e, type) {
|
||||
if (!draw_left && !this->show_hidden_engines && e->IsHidden(_local_company)) continue;
|
||||
EngineID eid = e->index;
|
||||
if (type == VEH_TRAIN && !this->GenerateReplaceRailList(eid, draw_left, this->replace_engines)) continue; // special rules for trains
|
||||
|
||||
if (draw_left) {
|
||||
const uint num_engines = GetGroupNumEngines(_local_company, this->sel_group, eid);
|
||||
|
||||
/* Skip drawing the engines we don't have any of and haven't set for replacement */
|
||||
if (num_engines == 0 && EngineReplacementForCompany(Company::Get(_local_company), eid, this->sel_group) == INVALID_ENGINE) continue;
|
||||
} else {
|
||||
if (!CheckAutoreplaceValidity(this->sel_engine[0], eid, _local_company)) continue;
|
||||
}
|
||||
|
||||
*list->Append() = eid;
|
||||
if (eid == this->sel_engine[side]) selected_engine = eid; // The selected engine is still in the list
|
||||
}
|
||||
this->sel_engine[side] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore)
|
||||
if (draw_left) {
|
||||
EngList_Sort(list, &EngineNumberSorter);
|
||||
} else {
|
||||
_engine_sort_direction = this->descending_sort_order;
|
||||
EngList_Sort(list, _engine_sort_functions[this->window_number][this->sort_criteria]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate the lists */
|
||||
void GenerateLists()
|
||||
{
|
||||
EngineID e = this->sel_engine[0];
|
||||
|
||||
if (this->engines[0].NeedRebuild()) {
|
||||
/* We need to rebuild the left engines list */
|
||||
this->GenerateReplaceVehList(true);
|
||||
this->vscroll[0]->SetCount(this->engines[0].Length());
|
||||
if (this->reset_sel_engine && this->sel_engine[0] == INVALID_ENGINE && this->engines[0].Length() != 0) {
|
||||
this->sel_engine[0] = this->engines[0][0];
|
||||
}
|
||||
}
|
||||
|
||||
if (this->engines[1].NeedRebuild() || e != this->sel_engine[0]) {
|
||||
/* Either we got a request to rebuild the right engines list, or the left engines list selected a different engine */
|
||||
if (this->sel_engine[0] == INVALID_ENGINE) {
|
||||
/* Always empty the right engines list when nothing is selected in the left engines list */
|
||||
this->engines[1].Clear();
|
||||
this->sel_engine[1] = INVALID_ENGINE;
|
||||
} else {
|
||||
if (this->reset_sel_engine && this->sel_engine[0] != INVALID_ENGINE) {
|
||||
/* Select the current replacement for sel_engine[0]. */
|
||||
const Company *c = Company::Get(_local_company);
|
||||
this->sel_engine[1] = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group);
|
||||
}
|
||||
/* Regenerate the list on the right. Note: This resets sel_engine[1] to INVALID_ENGINE, if it is no longer available. */
|
||||
this->GenerateReplaceVehList(false);
|
||||
this->vscroll[1]->SetCount(this->engines[1].Length());
|
||||
if (this->reset_sel_engine && this->sel_engine[1] != INVALID_ENGINE) {
|
||||
int position = 0;
|
||||
for (EngineID *it = this->engines[1].Begin(); it != this->engines[1].End(); ++it) {
|
||||
if (*it == this->sel_engine[1]) break;
|
||||
++position;
|
||||
}
|
||||
this->vscroll[1]->ScrollTowards(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Reset the flags about needed updates */
|
||||
this->engines[0].RebuildDone();
|
||||
this->engines[1].RebuildDone();
|
||||
this->reset_sel_engine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click on the start replace button.
|
||||
* @param replace_when_old Replace now or only when old?
|
||||
*/
|
||||
void ReplaceClick_StartReplace(bool replace_when_old)
|
||||
{
|
||||
EngineID veh_from = this->sel_engine[0];
|
||||
EngineID veh_to = this->sel_engine[1];
|
||||
DoCommandP(0, (replace_when_old ? 1 : 0) | (this->sel_group << 16), veh_from + (veh_to << 16), CMD_SET_AUTOREPLACE);
|
||||
}
|
||||
|
||||
public:
|
||||
ReplaceVehicleWindow(WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc)
|
||||
{
|
||||
if (vehicletype == VEH_TRAIN) {
|
||||
/* For rail vehicles find the most used vehicle type, which is usually
|
||||
* better than 'just' the first/previous vehicle type. */
|
||||
uint type_count[RAILTYPE_END];
|
||||
memset(type_count, 0, sizeof(type_count));
|
||||
|
||||
const Engine *e;
|
||||
FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
|
||||
if (e->u.rail.railveh_type == RAILVEH_WAGON) continue;
|
||||
type_count[e->u.rail.railtype] += GetGroupNumEngines(_local_company, id_g, e->index);
|
||||
}
|
||||
|
||||
this->sel_railtype = RAILTYPE_BEGIN;
|
||||
for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
|
||||
if (type_count[this->sel_railtype] < type_count[rt]) this->sel_railtype = rt;
|
||||
}
|
||||
}
|
||||
|
||||
this->replace_engines = true; // start with locomotives (all other vehicles will not read this bool)
|
||||
this->engines[0].ForceRebuild();
|
||||
this->engines[1].ForceRebuild();
|
||||
this->reset_sel_engine = true;
|
||||
this->details_height = ((vehicletype == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
|
||||
this->sel_engine[0] = INVALID_ENGINE;
|
||||
this->sel_engine[1] = INVALID_ENGINE;
|
||||
this->show_hidden_engines = _engine_sort_show_hidden_engines[vehicletype];
|
||||
|
||||
this->CreateNestedTree();
|
||||
this->vscroll[0] = this->GetScrollbar(WID_RV_LEFT_SCROLLBAR);
|
||||
this->vscroll[1] = this->GetScrollbar(WID_RV_RIGHT_SCROLLBAR);
|
||||
|
||||
NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_RV_SHOW_HIDDEN_ENGINES);
|
||||
widget->widget_data = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + vehicletype;
|
||||
widget->tool_tip = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + vehicletype;
|
||||
widget->SetLowered(this->show_hidden_engines);
|
||||
this->FinishInitNested(vehicletype);
|
||||
|
||||
this->sort_criteria = _engine_sort_last_criteria[vehicletype];
|
||||
this->descending_sort_order = _engine_sort_last_order[vehicletype];
|
||||
this->owner = _local_company;
|
||||
this->sel_group = id_g;
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_RV_SORT_ASCENDING_DESCENDING: {
|
||||
Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
|
||||
d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
|
||||
d.height += padding.height;
|
||||
*size = maxdim(*size, d);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_LEFT_MATRIX:
|
||||
case WID_RV_RIGHT_MATRIX:
|
||||
resize->height = GetEngineListHeight((VehicleType)this->window_number);
|
||||
size->height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize->height;
|
||||
break;
|
||||
|
||||
case WID_RV_LEFT_DETAILS:
|
||||
case WID_RV_RIGHT_DETAILS:
|
||||
size->height = this->details_height;
|
||||
break;
|
||||
|
||||
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
|
||||
StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
|
||||
SetDParam(0, STR_CONFIG_SETTING_ON);
|
||||
Dimension d = GetStringBoundingBox(str);
|
||||
SetDParam(0, STR_CONFIG_SETTING_OFF);
|
||||
d = maxdim(d, GetStringBoundingBox(str));
|
||||
d.width += padding.width;
|
||||
d.height += padding.height;
|
||||
*size = maxdim(*size, d);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_TRAIN_ENGINEWAGON_TOGGLE: {
|
||||
StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
|
||||
SetDParam(0, STR_REPLACE_ENGINES);
|
||||
Dimension d = GetStringBoundingBox(str);
|
||||
SetDParam(0, STR_REPLACE_WAGONS);
|
||||
d = maxdim(d, GetStringBoundingBox(str));
|
||||
d.width += padding.width;
|
||||
d.height += padding.height;
|
||||
*size = maxdim(*size, d);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_INFO_TAB: {
|
||||
Dimension d = GetStringBoundingBox(STR_REPLACE_NOT_REPLACING);
|
||||
d = maxdim(d, GetStringBoundingBox(STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED));
|
||||
d.width += WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
|
||||
d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
|
||||
*size = maxdim(*size, d);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_TRAIN_RAILTYPE_DROPDOWN: {
|
||||
Dimension d = {0, 0};
|
||||
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
|
||||
const RailtypeInfo *rti = GetRailTypeInfo(rt);
|
||||
/* Skip rail type if it has no label */
|
||||
if (rti->label == 0) continue;
|
||||
d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text));
|
||||
}
|
||||
d.width += padding.width;
|
||||
d.height += padding.height;
|
||||
*size = maxdim(*size, d);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_START_REPLACE: {
|
||||
Dimension d = GetStringBoundingBox(STR_REPLACE_VEHICLES_START);
|
||||
for (int i = 0; _start_replace_dropdown[i] != INVALID_STRING_ID; i++) {
|
||||
d = maxdim(d, GetStringBoundingBox(_start_replace_dropdown[i]));
|
||||
}
|
||||
d.width += padding.width;
|
||||
d.height += padding.height;
|
||||
*size = maxdim(*size, d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetStringParameters(int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_RV_CAPTION:
|
||||
SetDParam(0, STR_REPLACE_VEHICLE_TRAIN + this->window_number);
|
||||
switch (this->sel_group) {
|
||||
case ALL_GROUP:
|
||||
SetDParam(1, STR_GROUP_ALL_TRAINS + this->window_number);
|
||||
break;
|
||||
|
||||
case DEFAULT_GROUP:
|
||||
SetDParam(1, STR_GROUP_DEFAULT_TRAINS + this->window_number);
|
||||
break;
|
||||
|
||||
default:
|
||||
SetDParam(1, STR_GROUP_NAME);
|
||||
SetDParam(2, sel_group);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_RV_SORT_DROPDOWN:
|
||||
SetDParam(0, _engine_sort_listing[this->window_number][this->sort_criteria]);
|
||||
break;
|
||||
|
||||
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
|
||||
const Company *c = Company::Get(_local_company);
|
||||
SetDParam(0, c->settings.renew_keep_length ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_TRAIN_ENGINEWAGON_TOGGLE:
|
||||
SetDParam(0, this->replace_engines ? STR_REPLACE_ENGINES : STR_REPLACE_WAGONS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_RV_SORT_ASCENDING_DESCENDING:
|
||||
this->DrawSortButtonState(WID_RV_SORT_ASCENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
|
||||
break;
|
||||
|
||||
case WID_RV_INFO_TAB: {
|
||||
const Company *c = Company::Get(_local_company);
|
||||
StringID str;
|
||||
if (this->sel_engine[0] != INVALID_ENGINE) {
|
||||
if (!EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group)) {
|
||||
str = STR_REPLACE_NOT_REPLACING;
|
||||
} else {
|
||||
bool when_old = false;
|
||||
EngineID e = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group, &when_old);
|
||||
str = when_old ? STR_REPLACE_REPLACING_WHEN_OLD : STR_ENGINE_NAME;
|
||||
SetDParam(0, e);
|
||||
}
|
||||
} else {
|
||||
str = STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED;
|
||||
}
|
||||
|
||||
DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_BLACK, SA_HOR_CENTER);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_LEFT_MATRIX:
|
||||
case WID_RV_RIGHT_MATRIX: {
|
||||
int side = (widget == WID_RV_LEFT_MATRIX) ? 0 : 1;
|
||||
EngineID start = this->vscroll[side]->GetPosition(); // what is the offset for the start (scrolling)
|
||||
EngineID end = min(this->vscroll[side]->GetCapacity() + start, this->engines[side].Length());
|
||||
|
||||
/* Do the actual drawing */
|
||||
DrawEngineList((VehicleType)this->window_number, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP,
|
||||
&this->engines[side], start, end, this->sel_engine[side], side == 0, this->sel_group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnPaint()
|
||||
{
|
||||
if (this->engines[0].NeedRebuild() || this->engines[1].NeedRebuild()) this->GenerateLists();
|
||||
|
||||
Company *c = Company::Get(_local_company);
|
||||
|
||||
/* Disable the "Start Replacing" button if:
|
||||
* Either engines list is empty
|
||||
* or The selected replacement engine has a replacement (to prevent loops). */
|
||||
this->SetWidgetDisabledState(WID_RV_START_REPLACE,
|
||||
this->sel_engine[0] == INVALID_ENGINE || this->sel_engine[1] == INVALID_ENGINE || EngineReplacementForCompany(c, this->sel_engine[1], this->sel_group) != INVALID_ENGINE);
|
||||
|
||||
/* Disable the "Stop Replacing" button if:
|
||||
* The left engines list (existing vehicle) is empty
|
||||
* or The selected vehicle has no replacement set up */
|
||||
this->SetWidgetDisabledState(WID_RV_STOP_REPLACE, this->sel_engine[0] == INVALID_ENGINE || !EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group));
|
||||
|
||||
if (this->window_number == VEH_TRAIN) {
|
||||
/* sets the colour of that art thing */
|
||||
this->GetWidget<NWidgetCore>(WID_RV_TRAIN_FLUFF_LEFT)->colour = _company_colours[_local_company];
|
||||
this->GetWidget<NWidgetCore>(WID_RV_TRAIN_FLUFF_RIGHT)->colour = _company_colours[_local_company];
|
||||
|
||||
/* Show the selected railtype in the pulldown menu */
|
||||
this->GetWidget<NWidgetCore>(WID_RV_TRAIN_RAILTYPE_DROPDOWN)->widget_data = GetRailTypeInfo(sel_railtype)->strings.replace_text;
|
||||
}
|
||||
|
||||
this->DrawWidgets();
|
||||
|
||||
if (!this->IsShaded()) {
|
||||
int needed_height = this->details_height;
|
||||
/* Draw details panels. */
|
||||
for (int side = 0; side < 2; side++) {
|
||||
if (this->sel_engine[side] != INVALID_ENGINE) {
|
||||
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(side == 0 ? WID_RV_LEFT_DETAILS : WID_RV_RIGHT_DETAILS);
|
||||
int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
|
||||
nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine[side]);
|
||||
needed_height = max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM);
|
||||
}
|
||||
}
|
||||
if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
|
||||
this->details_height = needed_height;
|
||||
this->ReInit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnClick(Point pt, int widget, int click_count)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_RV_SORT_ASCENDING_DESCENDING:
|
||||
this->descending_sort_order ^= true;
|
||||
_engine_sort_last_order[this->window_number] = this->descending_sort_order;
|
||||
this->engines[1].ForceRebuild();
|
||||
this->SetDirty();
|
||||
break;
|
||||
|
||||
case WID_RV_SHOW_HIDDEN_ENGINES:
|
||||
this->show_hidden_engines ^= true;
|
||||
_engine_sort_show_hidden_engines[this->window_number] = this->show_hidden_engines;
|
||||
this->engines[1].ForceRebuild();
|
||||
this->SetWidgetLoweredState(widget, this->show_hidden_engines);
|
||||
this->SetDirty();
|
||||
break;
|
||||
|
||||
case WID_RV_SORT_DROPDOWN:
|
||||
DisplayVehicleSortDropDown(this, static_cast<VehicleType>(this->window_number), this->sort_criteria, WID_RV_SORT_DROPDOWN);
|
||||
break;
|
||||
|
||||
case WID_RV_TRAIN_ENGINEWAGON_TOGGLE:
|
||||
this->replace_engines = !(this->replace_engines);
|
||||
this->engines[0].ForceRebuild();
|
||||
this->reset_sel_engine = true;
|
||||
this->SetDirty();
|
||||
break;
|
||||
|
||||
case WID_RV_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu
|
||||
ShowDropDownList(this, GetRailTypeDropDownList(true), sel_railtype, WID_RV_TRAIN_RAILTYPE_DROPDOWN);
|
||||
break;
|
||||
|
||||
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length
|
||||
DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING);
|
||||
break;
|
||||
|
||||
case WID_RV_START_REPLACE: { // Start replacing
|
||||
if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
|
||||
this->HandleButtonClick(WID_RV_START_REPLACE);
|
||||
ReplaceClick_StartReplace(false);
|
||||
} else {
|
||||
bool replacment_when_old = EngineHasReplacementWhenOldForCompany(Company::Get(_local_company), this->sel_engine[0], this->sel_group);
|
||||
ShowDropDownMenu(this, _start_replace_dropdown, replacment_when_old ? 1 : 0, WID_RV_START_REPLACE, !this->replace_engines ? 1 << 1 : 0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_STOP_REPLACE: { // Stop replacing
|
||||
EngineID veh_from = this->sel_engine[0];
|
||||
DoCommandP(0, this->sel_group << 16, veh_from + (INVALID_ENGINE << 16), CMD_SET_AUTOREPLACE);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_LEFT_MATRIX:
|
||||
case WID_RV_RIGHT_MATRIX: {
|
||||
byte click_side;
|
||||
if (widget == WID_RV_LEFT_MATRIX) {
|
||||
click_side = 0;
|
||||
} else {
|
||||
click_side = 1;
|
||||
}
|
||||
uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget);
|
||||
size_t engine_count = this->engines[click_side].Length();
|
||||
|
||||
EngineID e = engine_count > i ? this->engines[click_side][i] : INVALID_ENGINE;
|
||||
if (e == this->sel_engine[click_side]) break; // we clicked the one we already selected
|
||||
this->sel_engine[click_side] = e;
|
||||
if (click_side == 0) {
|
||||
this->engines[1].ForceRebuild();
|
||||
this->reset_sel_engine = true;
|
||||
}
|
||||
this->SetDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnDropdownSelect(int widget, int index)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_RV_SORT_DROPDOWN:
|
||||
if (this->sort_criteria != index) {
|
||||
this->sort_criteria = index;
|
||||
_engine_sort_last_criteria[this->window_number] = this->sort_criteria;
|
||||
this->engines[1].ForceRebuild();
|
||||
this->SetDirty();
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_RV_TRAIN_RAILTYPE_DROPDOWN: {
|
||||
RailType temp = (RailType)index;
|
||||
if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything
|
||||
sel_railtype = temp;
|
||||
/* Reset scrollbar positions */
|
||||
this->vscroll[0]->SetPosition(0);
|
||||
this->vscroll[1]->SetPosition(0);
|
||||
/* Rebuild the lists */
|
||||
this->engines[0].ForceRebuild();
|
||||
this->engines[1].ForceRebuild();
|
||||
this->reset_sel_engine = true;
|
||||
this->SetDirty();
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_START_REPLACE:
|
||||
this->ReplaceClick_StartReplace(index != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnResize()
|
||||
{
|
||||
this->vscroll[0]->SetCapacityFromWidget(this, WID_RV_LEFT_MATRIX);
|
||||
this->vscroll[1]->SetCapacityFromWidget(this, WID_RV_RIGHT_MATRIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some data on this window has become invalid.
|
||||
* @param data Information about the changed data.
|
||||
* @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
|
||||
*/
|
||||
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
|
||||
{
|
||||
if (data != 0) {
|
||||
/* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
|
||||
this->engines[0].ForceRebuild();
|
||||
} else {
|
||||
this->engines[1].ForceRebuild();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const NWidgetPart _nested_replace_rail_vehicle_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
NWidget(WWT_SHADEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_STICKYBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_LEFT_SCROLLBAR),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_RIGHT_SCROLLBAR),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_TOGGLE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_ENGINE_WAGON_SELECT, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_TRAIN_FLUFF_LEFT), SetMinimalSize(15, 12), EndContainer(),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetResize(1, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_TRAIN_FLUFF_RIGHT), SetMinimalSize(16, 12), EndContainer(),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_HELP),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
static WindowDesc _replace_rail_vehicle_desc(
|
||||
WDP_AUTO, "replace_vehicle_train", 500, 140,
|
||||
WC_REPLACE_VEHICLE, WC_NONE,
|
||||
WDF_CONSTRUCTION,
|
||||
_nested_replace_rail_vehicle_widgets, lengthof(_nested_replace_rail_vehicle_widgets)
|
||||
);
|
||||
|
||||
static const NWidgetPart _nested_replace_vehicle_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetMinimalSize(433, 14), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
NWidget(WWT_SHADEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_STICKYBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_LEFT_SCROLLBAR),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_RIGHT_SCROLLBAR),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0), EndContainer(),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
static WindowDesc _replace_vehicle_desc(
|
||||
WDP_AUTO, "replace_vehicle", 456, 118,
|
||||
WC_REPLACE_VEHICLE, WC_NONE,
|
||||
WDF_CONSTRUCTION,
|
||||
_nested_replace_vehicle_widgets, lengthof(_nested_replace_vehicle_widgets)
|
||||
);
|
||||
|
||||
/**
|
||||
* Show the autoreplace configuration window for a particular group.
|
||||
* @param id_g The group to replace the vehicles for.
|
||||
* @param vehicletype The type of vehicles in the group.
|
||||
*/
|
||||
void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
|
||||
{
|
||||
DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
|
||||
new ReplaceVehicleWindow(vehicletype == VEH_TRAIN ? &_replace_rail_vehicle_desc : &_replace_vehicle_desc, vehicletype, id_g);
|
||||
}
|
||||
23
src/autoreplace_gui.h
Normal file
23
src/autoreplace_gui.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file autoreplace_gui.h Functions related to the autoreplace GUIs*/
|
||||
|
||||
#ifndef AUTOREPLACE_GUI_H
|
||||
#define AUTOREPLACE_GUI_H
|
||||
|
||||
#include "engine_type.h"
|
||||
#include "group_type.h"
|
||||
#include "vehicle_type.h"
|
||||
|
||||
void AddRemoveEngineFromAutoreplaceAndBuildWindows(VehicleType type);
|
||||
void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g);
|
||||
void ShowReplaceGroupVehicleWindow(GroupID group, VehicleType veh);
|
||||
|
||||
#endif /* AUTOREPLACE_GUI_H */
|
||||
22
src/autoreplace_type.h
Normal file
22
src/autoreplace_type.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file autoreplace_type.h Types related to autoreplacing. */
|
||||
|
||||
#ifndef AUTOREPLACE_TYPE_H
|
||||
#define AUTOREPLACE_TYPE_H
|
||||
|
||||
struct EngineRenew;
|
||||
|
||||
/**
|
||||
* A list to group EngineRenew directives together (such as per-company).
|
||||
*/
|
||||
typedef EngineRenew *EngineRenewList;
|
||||
|
||||
#endif /* AUTOREPLACE_TYPE_H */
|
||||
53
src/autoslope.h
Normal file
53
src/autoslope.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file autoslope.h Functions related to autoslope. */
|
||||
|
||||
#ifndef AUTOSLOPE_H
|
||||
#define AUTOSLOPE_H
|
||||
|
||||
#include "company_func.h"
|
||||
#include "depot_func.h"
|
||||
#include "tile_map.h"
|
||||
|
||||
/**
|
||||
* Autoslope check for tiles with an entrance on an edge.
|
||||
* E.g. depots and non-drive-through-road-stops.
|
||||
*
|
||||
* The test succeeds if the slope is not steep and at least one corner of the entrance edge is on the TileMaxZ() level.
|
||||
*
|
||||
* @note The test does not check if autoslope is enabled at all.
|
||||
*
|
||||
* @param tile The tile.
|
||||
* @param z_new New TileZ.
|
||||
* @param tileh_new New TileSlope.
|
||||
* @param entrance Entrance edge.
|
||||
* @return true iff terraforming is allowed.
|
||||
*/
|
||||
static inline bool AutoslopeCheckForEntranceEdge(TileIndex tile, int z_new, Slope tileh_new, DiagDirection entrance)
|
||||
{
|
||||
if (GetTileMaxZ(tile) != z_new + GetSlopeMaxZ(tileh_new)) return false;
|
||||
return ((tileh_new == SLOPE_FLAT) || CanBuildDepotByTileh(entrance, tileh_new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if autoslope is enabled for _current_company.
|
||||
*
|
||||
* Autoslope is disabled for town/industry construction.
|
||||
*
|
||||
* @return true iff autoslope is enabled.
|
||||
*/
|
||||
static inline bool AutoslopeEnabled()
|
||||
{
|
||||
return (_settings_game.construction.autoslope &&
|
||||
(_current_company < MAX_COMPANIES ||
|
||||
(_current_company == OWNER_NONE && _game_mode == GM_EDITOR)));
|
||||
}
|
||||
|
||||
#endif /* AUTOSLOPE_H */
|
||||
51
src/base_consist.cpp
Normal file
51
src/base_consist.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file base_consist.cpp Properties for front vehicles/consists. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "base_consist.h"
|
||||
#include "vehicle_base.h"
|
||||
#include "string_func.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
BaseConsist::~BaseConsist()
|
||||
{
|
||||
free(this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy properties of other BaseConsist.
|
||||
* @param src Source for copying
|
||||
*/
|
||||
void BaseConsist::CopyConsistPropertiesFrom(const BaseConsist *src)
|
||||
{
|
||||
if (this == src) return;
|
||||
|
||||
free(this->name);
|
||||
this->name = src->name != NULL ? stredup(src->name) : NULL;
|
||||
|
||||
this->current_order_time = src->current_order_time;
|
||||
this->lateness_counter = src->lateness_counter;
|
||||
this->timetable_start = src->timetable_start;
|
||||
|
||||
this->service_interval = src->service_interval;
|
||||
|
||||
this->cur_real_order_index = src->cur_real_order_index;
|
||||
this->cur_implicit_order_index = src->cur_implicit_order_index;
|
||||
|
||||
if (HasBit(src->vehicle_flags, VF_TIMETABLE_STARTED)) SetBit(this->vehicle_flags, VF_TIMETABLE_STARTED);
|
||||
if (HasBit(src->vehicle_flags, VF_AUTOFILL_TIMETABLE)) SetBit(this->vehicle_flags, VF_AUTOFILL_TIMETABLE);
|
||||
if (HasBit(src->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME)) SetBit(this->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
|
||||
if (HasBit(src->vehicle_flags, VF_SERVINT_IS_PERCENT) != HasBit(this->vehicle_flags, VF_SERVINT_IS_PERCENT)) {
|
||||
ToggleBit(this->vehicle_flags, VF_SERVINT_IS_PERCENT);
|
||||
}
|
||||
if (HasBit(src->vehicle_flags, VF_SERVINT_IS_CUSTOM)) SetBit(this->vehicle_flags, VF_SERVINT_IS_CUSTOM);
|
||||
}
|
||||
41
src/base_consist.h
Normal file
41
src/base_consist.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file base_consist.h Properties for front vehicles/consists. */
|
||||
|
||||
#ifndef BASE_CONSIST_H
|
||||
#define BASE_CONSIST_H
|
||||
|
||||
#include "order_type.h"
|
||||
#include "date_type.h"
|
||||
#include "timetable.h"
|
||||
|
||||
/** Various front vehicle properties that are preserved when autoreplacing, using order-backup or switching front engines within a consist. */
|
||||
struct BaseConsist {
|
||||
char *name; ///< Name of vehicle
|
||||
|
||||
/* Used for timetabling. */
|
||||
uint32 current_order_time; ///< How many ticks have passed since this order started.
|
||||
int32 lateness_counter; ///< How many ticks late (or early if negative) this vehicle is.
|
||||
Date timetable_start; ///< When the vehicle is supposed to start the timetable.
|
||||
|
||||
uint16 service_interval; ///< The interval for (automatic) servicing; either in days or %.
|
||||
|
||||
VehicleOrderID cur_real_order_index;///< The index to the current real (non-implicit) order
|
||||
VehicleOrderID cur_implicit_order_index;///< The index to the current implicit order
|
||||
|
||||
uint16 vehicle_flags; ///< Used for gradual loading and other miscellaneous things (@see VehicleFlags enum)
|
||||
|
||||
BaseConsist() : name(NULL) {}
|
||||
virtual ~BaseConsist();
|
||||
|
||||
void CopyConsistPropertiesFrom(const BaseConsist *src);
|
||||
};
|
||||
|
||||
#endif /* BASE_CONSIST_H */
|
||||
298
src/base_media_base.h
Normal file
298
src/base_media_base.h
Normal file
@@ -0,0 +1,298 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file base_media_base.h Generic functions for replacing base data (graphics, sounds). */
|
||||
|
||||
#ifndef BASE_MEDIA_BASE_H
|
||||
#define BASE_MEDIA_BASE_H
|
||||
|
||||
#include "fileio_func.h"
|
||||
#include "core/smallmap_type.hpp"
|
||||
#include "gfx_type.h"
|
||||
#include "textfile_type.h"
|
||||
#include "textfile_gui.h"
|
||||
|
||||
/* Forward declare these; can't do 'struct X' in functions as older GCCs barf on that */
|
||||
struct IniFile;
|
||||
struct ContentInfo;
|
||||
|
||||
/** Structure holding filename and MD5 information about a single file */
|
||||
struct MD5File {
|
||||
/** The result of a checksum check */
|
||||
enum ChecksumResult {
|
||||
CR_MATCH, ///< The file did exist and the md5 checksum did match
|
||||
CR_MISMATCH, ///< The file did exist, just the md5 checksum did not match
|
||||
CR_NO_FILE, ///< The file did not exist
|
||||
};
|
||||
|
||||
const char *filename; ///< filename
|
||||
uint8 hash[16]; ///< md5 sum of the file
|
||||
const char *missing_warning; ///< warning when this file is missing
|
||||
|
||||
ChecksumResult CheckMD5(Subdirectory subdir, size_t max_size) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about a single base set.
|
||||
* @tparam T the real class we're going to be
|
||||
* @tparam Tnum_files the number of files in the set
|
||||
* @tparam Tsearch_in_tars whether to search in the tars or not
|
||||
*/
|
||||
template <class T, size_t Tnum_files, bool Tsearch_in_tars>
|
||||
struct BaseSet {
|
||||
typedef SmallMap<const char *, const char *> TranslatedStrings;
|
||||
|
||||
/** Number of files in this set */
|
||||
static const size_t NUM_FILES = Tnum_files;
|
||||
|
||||
/** Whether to search in the tars or not. */
|
||||
static const bool SEARCH_IN_TARS = Tsearch_in_tars;
|
||||
|
||||
/** Internal names of the files in this set. */
|
||||
static const char * const *file_names;
|
||||
|
||||
const char *name; ///< The name of the base set
|
||||
TranslatedStrings description; ///< Description of the base set
|
||||
uint32 shortname; ///< Four letter short variant of the name
|
||||
uint32 version; ///< The version of this base set
|
||||
bool fallback; ///< This set is a fallback set, i.e. it should be used only as last resort
|
||||
|
||||
MD5File files[NUM_FILES]; ///< All files part of this set
|
||||
uint found_files; ///< Number of the files that could be found
|
||||
uint valid_files; ///< Number of the files that could be found and are valid
|
||||
|
||||
T *next; ///< The next base set in this list
|
||||
|
||||
/** Free everything we allocated */
|
||||
~BaseSet()
|
||||
{
|
||||
free(this->name);
|
||||
|
||||
for (TranslatedStrings::iterator iter = this->description.Begin(); iter != this->description.End(); iter++) {
|
||||
free(iter->first);
|
||||
free(iter->second);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < NUM_FILES; i++) {
|
||||
free(this->files[i].filename);
|
||||
free(this->files[i].missing_warning);
|
||||
}
|
||||
|
||||
delete this->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of missing files.
|
||||
* @return the number
|
||||
*/
|
||||
int GetNumMissing() const
|
||||
{
|
||||
return Tnum_files - this->found_files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of invalid files.
|
||||
* @note a missing file is invalid too!
|
||||
* @return the number
|
||||
*/
|
||||
int GetNumInvalid() const
|
||||
{
|
||||
return Tnum_files - this->valid_files;
|
||||
}
|
||||
|
||||
bool FillSetDetails(IniFile *ini, const char *path, const char *full_filename, bool allow_empty_filename = true);
|
||||
|
||||
/**
|
||||
* Get the description for the given ISO code.
|
||||
* It falls back to the first two characters of the ISO code in case
|
||||
* no match could be made with the full ISO code. If even then the
|
||||
* matching fails the default is returned.
|
||||
* @param isocode the isocode to search for
|
||||
* @return the description
|
||||
*/
|
||||
const char *GetDescription(const char *isocode = NULL) const
|
||||
{
|
||||
if (isocode != NULL) {
|
||||
/* First the full ISO code */
|
||||
for (TranslatedStrings::const_iterator iter = this->description.Begin(); iter != this->description.End(); iter++) {
|
||||
if (strcmp(iter->first, isocode) == 0) return iter->second;
|
||||
}
|
||||
/* Then the first two characters */
|
||||
for (TranslatedStrings::const_iterator iter = this->description.Begin(); iter != this->description.End(); iter++) {
|
||||
if (strncmp(iter->first, isocode, 2) == 0) return iter->second;
|
||||
}
|
||||
}
|
||||
/* Then fall back */
|
||||
return this->description.Begin()->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and check the MD5 hash of the supplied file.
|
||||
* @param file The file get the hash of.
|
||||
* @param subdir The sub directory to get the files from.
|
||||
* @return
|
||||
* - #CR_MATCH if the MD5 hash matches
|
||||
* - #CR_MISMATCH if the MD5 does not match
|
||||
* - #CR_NO_FILE if the file misses
|
||||
*/
|
||||
static MD5File::ChecksumResult CheckMD5(const MD5File *file, Subdirectory subdir)
|
||||
{
|
||||
return file->CheckMD5(subdir, SIZE_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search a textfile file next to this base media.
|
||||
* @param type The type of the textfile to search for.
|
||||
* @return The filename for the textfile, \c NULL otherwise.
|
||||
*/
|
||||
const char *GetTextfile(TextfileType type) const
|
||||
{
|
||||
for (uint i = 0; i < NUM_FILES; i++) {
|
||||
const char *textfile = ::GetTextfile(type, BASESET_DIR, this->files[i].filename);
|
||||
if (textfile != NULL) {
|
||||
return textfile;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Base for all base media (graphics, sounds)
|
||||
* @tparam Tbase_set the real set we're going to be
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
class BaseMedia : FileScanner {
|
||||
protected:
|
||||
static Tbase_set *available_sets; ///< All available sets
|
||||
static Tbase_set *duplicate_sets; ///< All sets that aren't available, but needed for not downloading base sets when a newer version than the one on BaNaNaS is loaded.
|
||||
static const Tbase_set *used_set; ///< The currently used set
|
||||
|
||||
/* virtual */ bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename);
|
||||
|
||||
/**
|
||||
* Get the extension that is used to identify this set.
|
||||
* @return the extension
|
||||
*/
|
||||
static const char *GetExtension();
|
||||
public:
|
||||
/** The set as saved in the config file. */
|
||||
static const char *ini_set;
|
||||
|
||||
/**
|
||||
* Determine the graphics pack that has to be used.
|
||||
* The one with the most correct files wins.
|
||||
* @return true if a best set has been found.
|
||||
*/
|
||||
static bool DetermineBestSet();
|
||||
|
||||
/** Do the scan for files. */
|
||||
static uint FindSets()
|
||||
{
|
||||
BaseMedia<Tbase_set> fs;
|
||||
/* Searching in tars is only done in the old "data" directories basesets. */
|
||||
uint num = fs.Scan(GetExtension(), Tbase_set::SEARCH_IN_TARS ? OLD_DATA_DIR : OLD_GM_DIR, Tbase_set::SEARCH_IN_TARS);
|
||||
return num + fs.Scan(GetExtension(), BASESET_DIR, Tbase_set::SEARCH_IN_TARS);
|
||||
}
|
||||
|
||||
static Tbase_set *GetAvailableSets();
|
||||
|
||||
static bool SetSet(const char *name);
|
||||
static char *GetSetsList(char *p, const char *last);
|
||||
static int GetNumSets();
|
||||
static int GetIndexOfUsedSet();
|
||||
static const Tbase_set *GetSet(int index);
|
||||
static const Tbase_set *GetUsedSet();
|
||||
|
||||
/**
|
||||
* Check whether we have an set with the exact characteristics as ci.
|
||||
* @param ci the characteristics to search on (shortname and md5sum)
|
||||
* @param md5sum whether to check the MD5 checksum
|
||||
* @return true iff we have an set matching.
|
||||
*/
|
||||
static bool HasSet(const ContentInfo *ci, bool md5sum);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether there's a base set matching some information.
|
||||
* @param ci The content info to compare it to.
|
||||
* @param md5sum Should the MD5 checksum be tested as well?
|
||||
* @param s The list with sets.
|
||||
* @return The filename of the first file of the base set, or \c NULL if there is no match.
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s);
|
||||
|
||||
/** Types of graphics in the base graphics set */
|
||||
enum GraphicsFileType {
|
||||
GFT_BASE, ///< Base sprites for all climates
|
||||
GFT_LOGOS, ///< Logos, landscape icons and original terrain generator sprites
|
||||
GFT_ARCTIC, ///< Landscape replacement sprites for arctic
|
||||
GFT_TROPICAL, ///< Landscape replacement sprites for tropical
|
||||
GFT_TOYLAND, ///< Landscape replacement sprites for toyland
|
||||
GFT_EXTRA, ///< Extra sprites that were not part of the original sprites
|
||||
MAX_GFT, ///< We are looking for this amount of GRFs
|
||||
};
|
||||
|
||||
/** Blitter type for base graphics sets. */
|
||||
enum BlitterType {
|
||||
BLT_8BPP, ///< Base set has 8 bpp sprites only.
|
||||
BLT_32BPP, ///< Base set has both 8 bpp and 32 bpp sprites.
|
||||
};
|
||||
|
||||
/** All data of a graphics set. */
|
||||
struct GraphicsSet : BaseSet<GraphicsSet, MAX_GFT, true> {
|
||||
PaletteType palette; ///< Palette of this graphics set
|
||||
BlitterType blitter; ///< Blitter of this graphics set
|
||||
|
||||
bool FillSetDetails(struct IniFile *ini, const char *path, const char *full_filename);
|
||||
|
||||
static MD5File::ChecksumResult CheckMD5(const MD5File *file, Subdirectory subdir);
|
||||
};
|
||||
|
||||
/** All data/functions related with replacing the base graphics. */
|
||||
class BaseGraphics : public BaseMedia<GraphicsSet> {
|
||||
public:
|
||||
};
|
||||
|
||||
/** All data of a sounds set. */
|
||||
struct SoundsSet : BaseSet<SoundsSet, 1, true> {
|
||||
};
|
||||
|
||||
/** All data/functions related with replacing the base sounds */
|
||||
class BaseSounds : public BaseMedia<SoundsSet> {
|
||||
public:
|
||||
};
|
||||
|
||||
/** Maximum number of songs in the 'class' playlists. */
|
||||
static const uint NUM_SONGS_CLASS = 10;
|
||||
/** Number of classes for songs */
|
||||
static const uint NUM_SONG_CLASSES = 3;
|
||||
/** Maximum number of songs in the full playlist; theme song + the classes */
|
||||
static const uint NUM_SONGS_AVAILABLE = 1 + NUM_SONG_CLASSES * NUM_SONGS_CLASS;
|
||||
|
||||
/** Maximum number of songs in the (custom) playlist */
|
||||
static const uint NUM_SONGS_PLAYLIST = 32;
|
||||
|
||||
/** All data of a music set. */
|
||||
struct MusicSet : BaseSet<MusicSet, NUM_SONGS_AVAILABLE, false> {
|
||||
/** The name of the different songs. */
|
||||
char song_name[NUM_SONGS_AVAILABLE][32];
|
||||
byte track_nr[NUM_SONGS_AVAILABLE];
|
||||
byte num_available;
|
||||
|
||||
bool FillSetDetails(struct IniFile *ini, const char *path, const char *full_filename);
|
||||
};
|
||||
|
||||
/** All data/functions related with replacing the base music */
|
||||
class BaseMusic : public BaseMedia<MusicSet> {
|
||||
public:
|
||||
};
|
||||
|
||||
#endif /* BASE_MEDIA_BASE_H */
|
||||
414
src/base_media_func.h
Normal file
414
src/base_media_func.h
Normal file
@@ -0,0 +1,414 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file base_media_func.h Generic function implementations for base data (graphics, sounds).
|
||||
* @note You should _never_ include this file due to the SET_TYPE define.
|
||||
*/
|
||||
|
||||
#include "base_media_base.h"
|
||||
#include "debug.h"
|
||||
#include "ini_type.h"
|
||||
#include "string_func.h"
|
||||
|
||||
template <class Tbase_set> /* static */ const char *BaseMedia<Tbase_set>::ini_set;
|
||||
template <class Tbase_set> /* static */ const Tbase_set *BaseMedia<Tbase_set>::used_set;
|
||||
template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::available_sets;
|
||||
template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::duplicate_sets;
|
||||
|
||||
/**
|
||||
* Try to read a single piece of metadata and return false if it doesn't exist.
|
||||
* @param name the name of the item to fetch.
|
||||
*/
|
||||
#define fetch_metadata(name) \
|
||||
item = metadata->GetItem(name, false); \
|
||||
if (item == NULL || StrEmpty(item->value)) { \
|
||||
DEBUG(grf, 0, "Base " SET_TYPE "set detail loading: %s field missing.", name); \
|
||||
DEBUG(grf, 0, " Is %s readable for the user running OpenTTD?", full_filename); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the set information from a loaded ini.
|
||||
* @param ini the ini to read from
|
||||
* @param path the path to this ini file (for filenames)
|
||||
* @param full_filename the full filename of the loaded file (for error reporting purposes)
|
||||
* @param allow_empty_filename empty filenames are valid
|
||||
* @return true if loading was successful.
|
||||
*/
|
||||
template <class T, size_t Tnum_files, bool Tsearch_in_tars>
|
||||
bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const char *path, const char *full_filename, bool allow_empty_filename)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
IniGroup *metadata = ini->GetGroup("metadata");
|
||||
IniItem *item;
|
||||
|
||||
fetch_metadata("name");
|
||||
this->name = stredup(item->value);
|
||||
|
||||
fetch_metadata("description");
|
||||
this->description[stredup("")] = stredup(item->value);
|
||||
|
||||
/* Add the translations of the descriptions too. */
|
||||
for (const IniItem *item = metadata->item; item != NULL; item = item->next) {
|
||||
if (strncmp("description.", item->name, 12) != 0) continue;
|
||||
|
||||
this->description[stredup(item->name + 12)] = stredup(item->value);
|
||||
}
|
||||
|
||||
fetch_metadata("shortname");
|
||||
for (uint i = 0; item->value[i] != '\0' && i < 4; i++) {
|
||||
this->shortname |= ((uint8)item->value[i]) << (i * 8);
|
||||
}
|
||||
|
||||
fetch_metadata("version");
|
||||
this->version = atoi(item->value);
|
||||
|
||||
item = metadata->GetItem("fallback", false);
|
||||
this->fallback = (item != NULL && strcmp(item->value, "0") != 0 && strcmp(item->value, "false") != 0);
|
||||
|
||||
/* For each of the file types we want to find the file, MD5 checksums and warning messages. */
|
||||
IniGroup *files = ini->GetGroup("files");
|
||||
IniGroup *md5s = ini->GetGroup("md5s");
|
||||
IniGroup *origin = ini->GetGroup("origin");
|
||||
for (uint i = 0; i < Tnum_files; i++) {
|
||||
MD5File *file = &this->files[i];
|
||||
/* Find the filename first. */
|
||||
item = files->GetItem(BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names[i], false);
|
||||
if (item == NULL || (item->value == NULL && !allow_empty_filename)) {
|
||||
DEBUG(grf, 0, "No " SET_TYPE " file for: %s (in %s)", BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names[i], full_filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *filename = item->value;
|
||||
if (filename == NULL) {
|
||||
file->filename = NULL;
|
||||
/* If we list no file, that file must be valid */
|
||||
this->valid_files++;
|
||||
this->found_files++;
|
||||
continue;
|
||||
}
|
||||
|
||||
file->filename = str_fmt("%s%s", path, filename);
|
||||
|
||||
/* Then find the MD5 checksum */
|
||||
item = md5s->GetItem(filename, false);
|
||||
if (item == NULL || item->value == NULL) {
|
||||
DEBUG(grf, 0, "No MD5 checksum specified for: %s (in %s)", filename, full_filename);
|
||||
return false;
|
||||
}
|
||||
char *c = item->value;
|
||||
for (uint i = 0; i < sizeof(file->hash) * 2; i++, c++) {
|
||||
uint j;
|
||||
if ('0' <= *c && *c <= '9') {
|
||||
j = *c - '0';
|
||||
} else if ('a' <= *c && *c <= 'f') {
|
||||
j = *c - 'a' + 10;
|
||||
} else if ('A' <= *c && *c <= 'F') {
|
||||
j = *c - 'A' + 10;
|
||||
} else {
|
||||
DEBUG(grf, 0, "Malformed MD5 checksum specified for: %s (in %s)", filename, full_filename);
|
||||
return false;
|
||||
}
|
||||
if (i % 2 == 0) {
|
||||
file->hash[i / 2] = j << 4;
|
||||
} else {
|
||||
file->hash[i / 2] |= j;
|
||||
}
|
||||
}
|
||||
|
||||
/* Then find the warning message when the file's missing */
|
||||
item = origin->GetItem(filename, false);
|
||||
if (item == NULL) item = origin->GetItem("default", false);
|
||||
if (item == NULL) {
|
||||
DEBUG(grf, 1, "No origin warning message specified for: %s", filename);
|
||||
file->missing_warning = stredup("");
|
||||
} else {
|
||||
file->missing_warning = stredup(item->value);
|
||||
}
|
||||
|
||||
switch (T::CheckMD5(file, BASESET_DIR)) {
|
||||
case MD5File::CR_MATCH:
|
||||
this->valid_files++;
|
||||
this->found_files++;
|
||||
break;
|
||||
|
||||
case MD5File::CR_MISMATCH:
|
||||
DEBUG(grf, 1, "MD5 checksum mismatch for: %s (in %s)", filename, full_filename);
|
||||
this->found_files++;
|
||||
break;
|
||||
|
||||
case MD5File::CR_NO_FILE:
|
||||
DEBUG(grf, 1, "The file %s specified in %s is missing", filename, full_filename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Tbase_set>
|
||||
bool BaseMedia<Tbase_set>::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
|
||||
{
|
||||
bool ret = false;
|
||||
DEBUG(grf, 1, "Checking %s for base " SET_TYPE " set", filename);
|
||||
|
||||
Tbase_set *set = new Tbase_set();
|
||||
IniFile *ini = new IniFile();
|
||||
ini->LoadFromDisk(filename, BASESET_DIR);
|
||||
|
||||
char *path = stredup(filename + basepath_length);
|
||||
char *psep = strrchr(path, PATHSEPCHAR);
|
||||
if (psep != NULL) {
|
||||
psep[1] = '\0';
|
||||
} else {
|
||||
*path = '\0';
|
||||
}
|
||||
|
||||
if (set->FillSetDetails(ini, path, filename)) {
|
||||
Tbase_set *duplicate = NULL;
|
||||
for (Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
|
||||
if (strcmp(c->name, set->name) == 0 || c->shortname == set->shortname) {
|
||||
duplicate = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (duplicate != NULL) {
|
||||
/* The more complete set takes precedence over the version number. */
|
||||
if ((duplicate->valid_files == set->valid_files && duplicate->version >= set->version) ||
|
||||
duplicate->valid_files > set->valid_files) {
|
||||
DEBUG(grf, 1, "Not adding %s (%i) as base " SET_TYPE " set (duplicate, %s)", set->name, set->version,
|
||||
duplicate->valid_files > set->valid_files ? "less valid files" : "lower version");
|
||||
set->next = BaseMedia<Tbase_set>::duplicate_sets;
|
||||
BaseMedia<Tbase_set>::duplicate_sets = set;
|
||||
} else {
|
||||
Tbase_set **prev = &BaseMedia<Tbase_set>::available_sets;
|
||||
while (*prev != duplicate) prev = &(*prev)->next;
|
||||
|
||||
*prev = set;
|
||||
set->next = duplicate->next;
|
||||
|
||||
/* If the duplicate set is currently used (due to rescanning this can happen)
|
||||
* update the currently used set to the new one. This will 'lie' about the
|
||||
* version number until a new game is started which isn't a big problem */
|
||||
if (BaseMedia<Tbase_set>::used_set == duplicate) BaseMedia<Tbase_set>::used_set = set;
|
||||
|
||||
DEBUG(grf, 1, "Removing %s (%i) as base " SET_TYPE " set (duplicate, %s)", duplicate->name, duplicate->version,
|
||||
duplicate->valid_files < set->valid_files ? "less valid files" : "lower version");
|
||||
duplicate->next = BaseMedia<Tbase_set>::duplicate_sets;
|
||||
BaseMedia<Tbase_set>::duplicate_sets = duplicate;
|
||||
ret = true;
|
||||
}
|
||||
} else {
|
||||
Tbase_set **last = &BaseMedia<Tbase_set>::available_sets;
|
||||
while (*last != NULL) last = &(*last)->next;
|
||||
|
||||
*last = set;
|
||||
ret = true;
|
||||
}
|
||||
if (ret) {
|
||||
DEBUG(grf, 1, "Adding %s (%i) as base " SET_TYPE " set", set->name, set->version);
|
||||
}
|
||||
} else {
|
||||
delete set;
|
||||
}
|
||||
free(path);
|
||||
|
||||
delete ini;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the set to be used.
|
||||
* @param name of the set to use
|
||||
* @return true if it could be loaded
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
/* static */ bool BaseMedia<Tbase_set>::SetSet(const char *name)
|
||||
{
|
||||
extern void CheckExternalFiles();
|
||||
|
||||
if (StrEmpty(name)) {
|
||||
if (!BaseMedia<Tbase_set>::DetermineBestSet()) return false;
|
||||
CheckExternalFiles();
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != NULL; s = s->next) {
|
||||
if (strcmp(name, s->name) == 0) {
|
||||
BaseMedia<Tbase_set>::used_set = s;
|
||||
CheckExternalFiles();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list with the sets.
|
||||
* @param p where to print to
|
||||
* @param last the last character to print to
|
||||
* @return the last printed character
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
/* static */ char *BaseMedia<Tbase_set>::GetSetsList(char *p, const char *last)
|
||||
{
|
||||
p += seprintf(p, last, "List of " SET_TYPE " sets:\n");
|
||||
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != NULL; s = s->next) {
|
||||
p += seprintf(p, last, "%18s: %s", s->name, s->GetDescription());
|
||||
int invalid = s->GetNumInvalid();
|
||||
if (invalid != 0) {
|
||||
int missing = s->GetNumMissing();
|
||||
if (missing == 0) {
|
||||
p += seprintf(p, last, " (%i corrupt file%s)\n", invalid, invalid == 1 ? "" : "s");
|
||||
} else {
|
||||
p += seprintf(p, last, " (unusable: %i missing file%s)\n", missing, missing == 1 ? "" : "s");
|
||||
}
|
||||
} else {
|
||||
p += seprintf(p, last, "\n");
|
||||
}
|
||||
}
|
||||
p += seprintf(p, last, "\n");
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_NETWORK)
|
||||
#include "network/network_content.h"
|
||||
|
||||
template <class Tbase_set> const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s)
|
||||
{
|
||||
for (; s != NULL; s = s->next) {
|
||||
if (s->GetNumMissing() != 0) continue;
|
||||
|
||||
if (s->shortname != ci->unique_id) continue;
|
||||
if (!md5sum) return s->files[0].filename;
|
||||
|
||||
byte md5[16];
|
||||
memset(md5, 0, sizeof(md5));
|
||||
for (uint i = 0; i < Tbase_set::NUM_FILES; i++) {
|
||||
for (uint j = 0; j < sizeof(md5); j++) {
|
||||
md5[j] ^= s->files[i].hash[j];
|
||||
}
|
||||
}
|
||||
if (memcmp(md5, ci->md5sum, sizeof(md5)) == 0) return s->files[0].filename;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template <class Tbase_set>
|
||||
/* static */ bool BaseMedia<Tbase_set>::HasSet(const ContentInfo *ci, bool md5sum)
|
||||
{
|
||||
return (TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::available_sets) != NULL) ||
|
||||
(TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::duplicate_sets) != NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template <class Tbase_set>
|
||||
const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template <class Tbase_set>
|
||||
/* static */ bool BaseMedia<Tbase_set>::HasSet(const ContentInfo *ci, bool md5sum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
/**
|
||||
* Count the number of available graphics sets.
|
||||
* @return the number of sets
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
/* static */ int BaseMedia<Tbase_set>::GetNumSets()
|
||||
{
|
||||
int n = 0;
|
||||
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != NULL; s = s->next) {
|
||||
if (s != BaseMedia<Tbase_set>::used_set && s->GetNumMissing() != 0) continue;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the currently active graphics set
|
||||
* @return the current set's index
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
/* static */ int BaseMedia<Tbase_set>::GetIndexOfUsedSet()
|
||||
{
|
||||
int n = 0;
|
||||
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != NULL; s = s->next) {
|
||||
if (s == BaseMedia<Tbase_set>::used_set) return n;
|
||||
if (s->GetNumMissing() != 0) continue;
|
||||
n++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the graphics set at the specified index
|
||||
* @return the name of the set
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
/* static */ const Tbase_set *BaseMedia<Tbase_set>::GetSet(int index)
|
||||
{
|
||||
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != NULL; s = s->next) {
|
||||
if (s != BaseMedia<Tbase_set>::used_set && s->GetNumMissing() != 0) continue;
|
||||
if (index == 0) return s;
|
||||
index--;
|
||||
}
|
||||
error("Base" SET_TYPE "::GetSet(): index %d out of range", index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the used set.
|
||||
* @return the used set.
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
/* static */ const Tbase_set *BaseMedia<Tbase_set>::GetUsedSet()
|
||||
{
|
||||
return BaseMedia<Tbase_set>::used_set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the available sets.
|
||||
* @return The available sets.
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
/* static */ Tbase_set *BaseMedia<Tbase_set>::GetAvailableSets()
|
||||
{
|
||||
return BaseMedia<Tbase_set>::available_sets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force instantiation of methods so we don't get linker errors.
|
||||
* @param repl_type the type of the BaseMedia to instantiate
|
||||
* @param set_type the type of the BaseSet to instantiate
|
||||
*/
|
||||
#define INSTANTIATE_BASE_MEDIA_METHODS(repl_type, set_type) \
|
||||
template const char *repl_type::ini_set; \
|
||||
template const char *repl_type::GetExtension(); \
|
||||
template bool repl_type::AddFile(const char *filename, size_t pathlength, const char *tar_filename); \
|
||||
template bool repl_type::HasSet(const struct ContentInfo *ci, bool md5sum); \
|
||||
template bool repl_type::SetSet(const char *name); \
|
||||
template char *repl_type::GetSetsList(char *p, const char *last); \
|
||||
template int repl_type::GetNumSets(); \
|
||||
template int repl_type::GetIndexOfUsedSet(); \
|
||||
template const set_type *repl_type::GetSet(int index); \
|
||||
template const set_type *repl_type::GetUsedSet(); \
|
||||
template bool repl_type::DetermineBestSet(); \
|
||||
template set_type *repl_type::GetAvailableSets(); \
|
||||
template const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const set_type *s);
|
||||
|
||||
255
src/base_station_base.h
Normal file
255
src/base_station_base.h
Normal file
@@ -0,0 +1,255 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file base_station_base.h Base classes/functions for base stations. */
|
||||
|
||||
#ifndef BASE_STATION_BASE_H
|
||||
#define BASE_STATION_BASE_H
|
||||
|
||||
#include "core/pool_type.hpp"
|
||||
#include "command_type.h"
|
||||
#include "viewport_type.h"
|
||||
#include "station_map.h"
|
||||
|
||||
typedef Pool<BaseStation, StationID, 32, 64000> StationPool;
|
||||
extern StationPool _station_pool;
|
||||
|
||||
struct StationSpecList {
|
||||
const StationSpec *spec;
|
||||
uint32 grfid; ///< GRF ID of this custom station
|
||||
uint8 localidx; ///< Station ID within GRF of station
|
||||
};
|
||||
|
||||
|
||||
/** StationRect - used to track station spread out rectangle - cheaper than scanning whole map */
|
||||
struct StationRect : public Rect {
|
||||
enum StationRectMode
|
||||
{
|
||||
ADD_TEST = 0,
|
||||
ADD_TRY,
|
||||
ADD_FORCE
|
||||
};
|
||||
|
||||
StationRect();
|
||||
void MakeEmpty();
|
||||
bool PtInExtendedRect(int x, int y, int distance = 0) const;
|
||||
bool IsEmpty() const;
|
||||
CommandCost BeforeAddTile(TileIndex tile, StationRectMode mode);
|
||||
CommandCost BeforeAddRect(TileIndex tile, int w, int h, StationRectMode mode);
|
||||
bool AfterRemoveTile(BaseStation *st, TileIndex tile);
|
||||
bool AfterRemoveRect(BaseStation *st, TileArea ta);
|
||||
|
||||
static bool ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a);
|
||||
|
||||
StationRect& operator = (const Rect &src);
|
||||
};
|
||||
|
||||
/** Base class for all station-ish types */
|
||||
struct BaseStation : StationPool::PoolItem<&_station_pool> {
|
||||
TileIndex xy; ///< Base tile of the station
|
||||
ViewportSign sign; ///< NOSAVE: Dimensions of sign
|
||||
byte delete_ctr; ///< Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is deleted.
|
||||
|
||||
char *name; ///< Custom name
|
||||
StringID string_id; ///< Default name (town area) of station
|
||||
|
||||
Town *town; ///< The town this station is associated with
|
||||
OwnerByte owner; ///< The owner of this station
|
||||
StationFacilityByte facilities; ///< The facilities that this station has
|
||||
|
||||
uint8 num_specs; ///< Number of specs in the speclist
|
||||
StationSpecList *speclist; ///< List of station specs of this station
|
||||
|
||||
Date build_date; ///< Date of construction
|
||||
|
||||
uint16 random_bits; ///< Random bits assigned to this station
|
||||
byte waiting_triggers; ///< Waiting triggers (NewGRF) for this station
|
||||
uint8 cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
|
||||
uint32 cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask
|
||||
|
||||
TileArea train_station; ///< Tile area the train 'station' part covers
|
||||
StationRect rect; ///< NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions
|
||||
|
||||
/**
|
||||
* Initialize the base station.
|
||||
* @param tile The location of the station sign
|
||||
*/
|
||||
BaseStation(TileIndex tile) :
|
||||
xy(tile),
|
||||
train_station(INVALID_TILE, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~BaseStation();
|
||||
|
||||
/**
|
||||
* Check whether a specific tile belongs to this station.
|
||||
* @param tile the tile to check
|
||||
* @return true if the tile belongs to this station
|
||||
*/
|
||||
virtual bool TileBelongsToRailStation(TileIndex tile) const = 0;
|
||||
|
||||
/**
|
||||
* Helper function to get a NewGRF variable that isn't implemented by the base class.
|
||||
* @param object the resolver object related to this query
|
||||
* @param variable that is queried
|
||||
* @param parameter parameter for that variable
|
||||
* @param available will return false if ever the variable asked for does not exist
|
||||
* @return the value stored in the corresponding variable
|
||||
*/
|
||||
virtual uint32 GetNewGRFVariable(const struct ResolverObject &object, byte variable, byte parameter, bool *available) const = 0;
|
||||
|
||||
/**
|
||||
* Update the coordinated of the sign (as shown in the viewport).
|
||||
*/
|
||||
virtual void UpdateVirtCoord() = 0;
|
||||
|
||||
/**
|
||||
* Get the tile area for a given station type.
|
||||
* @param ta tile area to fill.
|
||||
* @param type the type of the area
|
||||
*/
|
||||
virtual void GetTileArea(TileArea *ta, StationType type) const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the length of a platform
|
||||
* @pre tile must be a rail station tile
|
||||
* @param tile A tile that contains the platform in question
|
||||
* @return The length of the platform
|
||||
*/
|
||||
virtual uint GetPlatformLength(TileIndex tile) const = 0;
|
||||
|
||||
/**
|
||||
* Determines the REMAINING length of a platform, starting at (and including)
|
||||
* the given tile.
|
||||
* @param tile the tile from which to start searching. Must be a rail station tile
|
||||
* @param dir The direction in which to search.
|
||||
* @return The platform length
|
||||
*/
|
||||
virtual uint GetPlatformLength(TileIndex tile, DiagDirection dir) const = 0;
|
||||
|
||||
/**
|
||||
* Get the base station belonging to a specific tile.
|
||||
* @param tile The tile to get the base station from.
|
||||
* @return the station associated with that tile.
|
||||
*/
|
||||
static inline BaseStation *GetByTile(TileIndex tile)
|
||||
{
|
||||
return BaseStation::Get(GetStationIndex(tile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the base station currently is in use; in use means
|
||||
* that it is not scheduled for deletion and that it still has some
|
||||
* facilities left.
|
||||
* @return true if still in use
|
||||
*/
|
||||
inline bool IsInUse() const
|
||||
{
|
||||
return (this->facilities & ~FACIL_WAYPOINT) != 0;
|
||||
}
|
||||
|
||||
static void PostDestructor(size_t index);
|
||||
};
|
||||
|
||||
#define FOR_ALL_BASE_STATIONS(var) FOR_ALL_ITEMS_FROM(BaseStation, station_index, var, 0)
|
||||
|
||||
/**
|
||||
* Class defining several overloaded accessors so we don't
|
||||
* have to cast base stations that often
|
||||
*/
|
||||
template <class T, bool Tis_waypoint>
|
||||
struct SpecializedStation : public BaseStation {
|
||||
static const StationFacility EXPECTED_FACIL = Tis_waypoint ? FACIL_WAYPOINT : FACIL_NONE; ///< Specialized type
|
||||
|
||||
/**
|
||||
* Set station type correctly
|
||||
* @param tile The base tile of the station.
|
||||
*/
|
||||
inline SpecializedStation<T, Tis_waypoint>(TileIndex tile) :
|
||||
BaseStation(tile)
|
||||
{
|
||||
this->facilities = EXPECTED_FACIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for checking whether the given station is of this type.
|
||||
* @param st the station to check.
|
||||
* @return true if the station is the type we expect it to be.
|
||||
*/
|
||||
static inline bool IsExpected(const BaseStation *st)
|
||||
{
|
||||
return (st->facilities & FACIL_WAYPOINT) == EXPECTED_FACIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether given index is a valid index for station of this type
|
||||
* @param index tested index
|
||||
* @return is this index valid index of T?
|
||||
*/
|
||||
static inline bool IsValidID(size_t index)
|
||||
{
|
||||
return BaseStation::IsValidID(index) && IsExpected(BaseStation::Get(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets station with given index
|
||||
* @return pointer to station with given index casted to T *
|
||||
*/
|
||||
static inline T *Get(size_t index)
|
||||
{
|
||||
return (T *)BaseStation::Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns station if the index is a valid index for this station type
|
||||
* @return pointer to station with given index if it's a station of this type
|
||||
*/
|
||||
static inline T *GetIfValid(size_t index)
|
||||
{
|
||||
return IsValidID(index) ? Get(index) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the station belonging to a specific tile.
|
||||
* @param tile The tile to get the station from.
|
||||
* @return the station associated with that tile.
|
||||
*/
|
||||
static inline T *GetByTile(TileIndex tile)
|
||||
{
|
||||
return GetIfValid(GetStationIndex(tile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BaseStation to SpecializedStation with type checking.
|
||||
* @param st BaseStation pointer
|
||||
* @return pointer to SpecializedStation
|
||||
*/
|
||||
static inline T *From(BaseStation *st)
|
||||
{
|
||||
assert(IsExpected(st));
|
||||
return (T *)st;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a const BaseStation to const SpecializedStation with type checking.
|
||||
* @param st BaseStation pointer
|
||||
* @return pointer to SpecializedStation
|
||||
*/
|
||||
static inline const T *From(const BaseStation *st)
|
||||
{
|
||||
assert(IsExpected(st));
|
||||
return (const T *)st;
|
||||
}
|
||||
};
|
||||
|
||||
#define FOR_ALL_BASE_STATIONS_OF_TYPE(name, var) FOR_ALL_ITEMS_FROM(name, station_index, var, 0) if (name::IsExpected(var))
|
||||
|
||||
#endif /* BASE_STATION_BASE_H */
|
||||
513
src/blitter/32bpp_anim.cpp
Normal file
513
src/blitter/32bpp_anim.cpp
Normal file
@@ -0,0 +1,513 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_anim.cpp Implementation of the optimized 32 bpp blitter with animation support. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../video/video_driver.hpp"
|
||||
#include "32bpp_anim.hpp"
|
||||
|
||||
#include "../table/sprites.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Instantiation of the 32bpp with animation blitter factory. */
|
||||
static FBlitter_32bppAnim iFBlitter_32bppAnim;
|
||||
|
||||
template <BlitterMode mode>
|
||||
inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
|
||||
{
|
||||
const SpriteData *src = (const SpriteData *)bp->sprite;
|
||||
|
||||
const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]);
|
||||
const uint16 *src_n = (const uint16 *)(src->data + src->offset[zoom][1]);
|
||||
|
||||
for (uint i = bp->skip_top; i != 0; i--) {
|
||||
src_px = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
|
||||
src_n = (const uint16 *)((const byte *)src_n + *(const uint32 *)src_n);
|
||||
}
|
||||
|
||||
Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left;
|
||||
uint16 *anim = this->anim_buf + ((uint32 *)bp->dst - (uint32 *)_screen.dst_ptr) + bp->top * this->anim_buf_width + bp->left;
|
||||
|
||||
const byte *remap = bp->remap; // store so we don't have to access it via bp everytime
|
||||
|
||||
for (int y = 0; y < bp->height; y++) {
|
||||
Colour *dst_ln = dst + bp->pitch;
|
||||
uint16 *anim_ln = anim + this->anim_buf_width;
|
||||
|
||||
const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
|
||||
src_px++;
|
||||
|
||||
const uint16 *src_n_ln = (const uint16 *)((const byte *)src_n + *(const uint32 *)src_n);
|
||||
src_n += 2;
|
||||
|
||||
Colour *dst_end = dst + bp->skip_left;
|
||||
|
||||
uint n;
|
||||
|
||||
while (dst < dst_end) {
|
||||
n = *src_n++;
|
||||
|
||||
if (src_px->a == 0) {
|
||||
dst += n;
|
||||
src_px ++;
|
||||
src_n++;
|
||||
|
||||
if (dst > dst_end) anim += dst - dst_end;
|
||||
} else {
|
||||
if (dst + n > dst_end) {
|
||||
uint d = dst_end - dst;
|
||||
src_px += d;
|
||||
src_n += d;
|
||||
|
||||
dst = dst_end - bp->skip_left;
|
||||
dst_end = dst + bp->width;
|
||||
|
||||
n = min<uint>(n - d, (uint)bp->width);
|
||||
goto draw;
|
||||
}
|
||||
dst += n;
|
||||
src_px += n;
|
||||
src_n += n;
|
||||
}
|
||||
}
|
||||
|
||||
dst -= bp->skip_left;
|
||||
dst_end -= bp->skip_left;
|
||||
|
||||
dst_end += bp->width;
|
||||
|
||||
while (dst < dst_end) {
|
||||
n = min<uint>(*src_n++, (uint)(dst_end - dst));
|
||||
|
||||
if (src_px->a == 0) {
|
||||
anim += n;
|
||||
dst += n;
|
||||
src_px++;
|
||||
src_n++;
|
||||
continue;
|
||||
}
|
||||
|
||||
draw:;
|
||||
|
||||
switch (mode) {
|
||||
case BM_COLOUR_REMAP:
|
||||
if (src_px->a == 255) {
|
||||
do {
|
||||
uint m = *src_n;
|
||||
/* In case the m-channel is zero, do not remap this pixel in any way */
|
||||
if (m == 0) {
|
||||
*dst = src_px->data;
|
||||
*anim = 0;
|
||||
} else {
|
||||
uint r = remap[GB(m, 0, 8)];
|
||||
*anim = r | (m & 0xFF00);
|
||||
if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
|
||||
}
|
||||
anim++;
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
} else {
|
||||
do {
|
||||
uint m = *src_n;
|
||||
if (m == 0) {
|
||||
*dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
|
||||
*anim = 0;
|
||||
} else {
|
||||
uint r = remap[GB(m, 0, 8)];
|
||||
*anim = 0;
|
||||
if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst);
|
||||
}
|
||||
anim++;
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_CRASH_REMAP:
|
||||
if (src_px->a == 255) {
|
||||
do {
|
||||
uint m = *src_n;
|
||||
if (m == 0) {
|
||||
uint8 g = MakeDark(src_px->r, src_px->g, src_px->b);
|
||||
*dst = ComposeColourRGBA(g, g, g, src_px->a, *dst);
|
||||
*anim = 0;
|
||||
} else {
|
||||
uint r = remap[GB(m, 0, 8)];
|
||||
*anim = r | (m & 0xFF00);
|
||||
if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
|
||||
}
|
||||
anim++;
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
} else {
|
||||
do {
|
||||
uint m = *src_n;
|
||||
if (m == 0) {
|
||||
if (src_px->a != 0) {
|
||||
uint8 g = MakeDark(src_px->r, src_px->g, src_px->b);
|
||||
*dst = ComposeColourRGBA(g, g, g, src_px->a, *dst);
|
||||
*anim = 0;
|
||||
}
|
||||
} else {
|
||||
uint r = remap[GB(m, 0, 8)];
|
||||
*anim = 0;
|
||||
if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst);
|
||||
}
|
||||
anim++;
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case BM_BLACK_REMAP:
|
||||
do {
|
||||
*dst++ = Colour(0, 0, 0);
|
||||
*anim++ = 0;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
break;
|
||||
|
||||
case BM_TRANSPARENT:
|
||||
/* TODO -- We make an assumption here that the remap in fact is transparency, not some colour.
|
||||
* This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
|
||||
* we produce a result the newgrf maker didn't expect ;) */
|
||||
|
||||
/* Make the current colour a bit more black, so it looks like this image is transparent */
|
||||
src_n += n;
|
||||
if (src_px->a == 255) {
|
||||
src_px += n;
|
||||
do {
|
||||
*dst = MakeTransparent(*dst, 3, 4);
|
||||
*anim = 0;
|
||||
anim++;
|
||||
dst++;
|
||||
} while (--n != 0);
|
||||
} else {
|
||||
do {
|
||||
*dst = MakeTransparent(*dst, (256 * 4 - src_px->a), 256 * 4);
|
||||
*anim = 0;
|
||||
anim++;
|
||||
dst++;
|
||||
src_px++;
|
||||
} while (--n != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (src_px->a == 255) {
|
||||
do {
|
||||
/* Compiler assumes pointer aliasing, can't optimise this on its own */
|
||||
uint m = GB(*src_n, 0, 8);
|
||||
/* Above PALETTE_ANIM_START is palette animation */
|
||||
*anim++ = *src_n;
|
||||
*dst++ = (m >= PALETTE_ANIM_START) ? this->AdjustBrightness(this->LookupColourInPalette(m), GB(*src_n, 8, 8)) : src_px->data;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
} else {
|
||||
do {
|
||||
uint m = GB(*src_n, 0, 8);
|
||||
*anim++ = 0;
|
||||
if (m >= PALETTE_ANIM_START) {
|
||||
*dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(m), GB(*src_n, 8, 8)), src_px->a, *dst);
|
||||
} else {
|
||||
*dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
|
||||
}
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
anim = anim_ln;
|
||||
dst = dst_ln;
|
||||
src_px = src_px_ln;
|
||||
src_n = src_n_ln;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
|
||||
{
|
||||
if (_screen_disable_anim) {
|
||||
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */
|
||||
Blitter_32bppOptimized::Draw(bp, mode, zoom);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
default: NOT_REACHED();
|
||||
case BM_NORMAL: Draw<BM_NORMAL> (bp, zoom); return;
|
||||
case BM_COLOUR_REMAP: Draw<BM_COLOUR_REMAP>(bp, zoom); return;
|
||||
case BM_TRANSPARENT: Draw<BM_TRANSPARENT> (bp, zoom); return;
|
||||
case BM_CRASH_REMAP: Draw<BM_CRASH_REMAP> (bp, zoom); return;
|
||||
case BM_BLACK_REMAP: Draw<BM_BLACK_REMAP> (bp, zoom); return;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::DrawColourMappingRect(void *dst, int width, int height, PaletteID pal)
|
||||
{
|
||||
if (_screen_disable_anim) {
|
||||
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawColourMappingRect() */
|
||||
Blitter_32bppOptimized::DrawColourMappingRect(dst, width, height, pal);
|
||||
return;
|
||||
}
|
||||
|
||||
Colour *udst = (Colour *)dst;
|
||||
uint16 *anim;
|
||||
|
||||
anim = this->anim_buf + ((uint32 *)dst - (uint32 *)_screen.dst_ptr);
|
||||
|
||||
if (pal == PALETTE_TO_TRANSPARENT) {
|
||||
do {
|
||||
for (int i = 0; i != width; i++) {
|
||||
*udst = MakeTransparent(*udst, 154);
|
||||
*anim = 0;
|
||||
udst++;
|
||||
anim++;
|
||||
}
|
||||
udst = udst - width + _screen.pitch;
|
||||
anim = anim - width + this->anim_buf_width;
|
||||
} while (--height);
|
||||
return;
|
||||
}
|
||||
if (pal == PALETTE_NEWSPAPER) {
|
||||
do {
|
||||
for (int i = 0; i != width; i++) {
|
||||
*udst = MakeGrey(*udst);
|
||||
*anim = 0;
|
||||
udst++;
|
||||
anim++;
|
||||
}
|
||||
udst = udst - width + _screen.pitch;
|
||||
anim = anim - width + this->anim_buf_width;
|
||||
} while (--height);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('%d')", pal);
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::SetPixel(void *video, int x, int y, uint8 colour)
|
||||
{
|
||||
*((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour);
|
||||
|
||||
/* Set the colour in the anim-buffer too, if we are rendering to the screen */
|
||||
if (_screen_disable_anim) return;
|
||||
this->anim_buf[((uint32 *)video - (uint32 *)_screen.dst_ptr) + x + y * this->anim_buf_width] = colour | (DEFAULT_BRIGHTNESS << 8);
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8 colour)
|
||||
{
|
||||
if (_screen_disable_anim) {
|
||||
/* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
|
||||
Blitter_32bppOptimized::DrawRect(video, width, height, colour);
|
||||
return;
|
||||
}
|
||||
|
||||
Colour colour32 = LookupColourInPalette(colour);
|
||||
uint16 *anim_line;
|
||||
|
||||
anim_line = ((uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
|
||||
|
||||
do {
|
||||
Colour *dst = (Colour *)video;
|
||||
uint16 *anim = anim_line;
|
||||
|
||||
for (int i = width; i > 0; i--) {
|
||||
*dst = colour32;
|
||||
/* Set the colour in the anim-buffer too */
|
||||
*anim = colour | (DEFAULT_BRIGHTNESS << 8);
|
||||
dst++;
|
||||
anim++;
|
||||
}
|
||||
video = (uint32 *)video + _screen.pitch;
|
||||
anim_line += this->anim_buf_width;
|
||||
} while (--height);
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::CopyFromBuffer(void *video, const void *src, int width, int height)
|
||||
{
|
||||
assert(!_screen_disable_anim);
|
||||
assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);
|
||||
Colour *dst = (Colour *)video;
|
||||
const uint32 *usrc = (const uint32 *)src;
|
||||
uint16 *anim_line = ((uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
/* We need to keep those for palette animation. */
|
||||
Colour *dst_pal = dst;
|
||||
uint16 *anim_pal = anim_line;
|
||||
|
||||
memcpy(dst, usrc, width * sizeof(uint32));
|
||||
usrc += width;
|
||||
dst += _screen.pitch;
|
||||
/* Copy back the anim-buffer */
|
||||
memcpy(anim_line, usrc, width * sizeof(uint16));
|
||||
usrc = (const uint32 *)((const uint16 *)usrc + width);
|
||||
anim_line += this->anim_buf_width;
|
||||
|
||||
/* Okay, it is *very* likely that the image we stored is using
|
||||
* the wrong palette animated colours. There are two things we
|
||||
* can do to fix this. The first is simply reviewing the whole
|
||||
* screen after we copied the buffer, i.e. run PaletteAnimate,
|
||||
* however that forces a full screen redraw which is expensive
|
||||
* for just the cursor. This just copies the implementation of
|
||||
* palette animation, much cheaper though slightly nastier. */
|
||||
for (int i = 0; i < width; i++) {
|
||||
uint colour = GB(*anim_pal, 0, 8);
|
||||
if (colour >= PALETTE_ANIM_START) {
|
||||
/* Update this pixel */
|
||||
*dst_pal = this->AdjustBrightness(LookupColourInPalette(colour), GB(*anim_pal, 8, 8));
|
||||
}
|
||||
dst_pal++;
|
||||
anim_pal++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::CopyToBuffer(const void *video, void *dst, int width, int height)
|
||||
{
|
||||
assert(!_screen_disable_anim);
|
||||
assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);
|
||||
uint32 *udst = (uint32 *)dst;
|
||||
const uint32 *src = (const uint32 *)video;
|
||||
const uint16 *anim_line;
|
||||
|
||||
if (this->anim_buf == NULL) return;
|
||||
|
||||
anim_line = ((const uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint32));
|
||||
src += _screen.pitch;
|
||||
udst += width;
|
||||
/* Copy the anim-buffer */
|
||||
memcpy(udst, anim_line, width * sizeof(uint16));
|
||||
udst = (uint32 *)((uint16 *)udst + width);
|
||||
anim_line += this->anim_buf_width;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y)
|
||||
{
|
||||
assert(!_screen_disable_anim);
|
||||
assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);
|
||||
uint16 *dst, *src;
|
||||
|
||||
/* We need to scroll the anim-buffer too */
|
||||
if (scroll_y > 0) {
|
||||
dst = this->anim_buf + left + (top + height - 1) * this->anim_buf_width;
|
||||
src = dst - scroll_y * this->anim_buf_width;
|
||||
|
||||
/* Adjust left & width */
|
||||
if (scroll_x >= 0) {
|
||||
dst += scroll_x;
|
||||
} else {
|
||||
src -= scroll_x;
|
||||
}
|
||||
|
||||
uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x);
|
||||
uint th = height - scroll_y;
|
||||
for (; th > 0; th--) {
|
||||
memcpy(dst, src, tw * sizeof(uint16));
|
||||
src -= this->anim_buf_width;
|
||||
dst -= this->anim_buf_width;
|
||||
}
|
||||
} else {
|
||||
/* Calculate pointers */
|
||||
dst = this->anim_buf + left + top * this->anim_buf_width;
|
||||
src = dst - scroll_y * this->anim_buf_width;
|
||||
|
||||
/* Adjust left & width */
|
||||
if (scroll_x >= 0) {
|
||||
dst += scroll_x;
|
||||
} else {
|
||||
src -= scroll_x;
|
||||
}
|
||||
|
||||
/* the y-displacement may be 0 therefore we have to use memmove,
|
||||
* because source and destination may overlap */
|
||||
uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x);
|
||||
uint th = height + scroll_y;
|
||||
for (; th > 0; th--) {
|
||||
memmove(dst, src, tw * sizeof(uint16));
|
||||
src += this->anim_buf_width;
|
||||
dst += this->anim_buf_width;
|
||||
}
|
||||
}
|
||||
|
||||
Blitter_32bppBase::ScrollBuffer(video, left, top, width, height, scroll_x, scroll_y);
|
||||
}
|
||||
|
||||
int Blitter_32bppAnim::BufferSize(int width, int height)
|
||||
{
|
||||
return width * height * (sizeof(uint32) + sizeof(uint16));
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::PaletteAnimate(const Palette &palette)
|
||||
{
|
||||
assert(!_screen_disable_anim);
|
||||
|
||||
this->palette = palette;
|
||||
/* If first_dirty is 0, it is for 8bpp indication to send the new
|
||||
* palette. However, only the animation colours might possibly change.
|
||||
* Especially when going between toyland and non-toyland. */
|
||||
assert(this->palette.first_dirty == PALETTE_ANIM_START || this->palette.first_dirty == 0);
|
||||
|
||||
const uint16 *anim = this->anim_buf;
|
||||
Colour *dst = (Colour *)_screen.dst_ptr;
|
||||
|
||||
/* Let's walk the anim buffer and try to find the pixels */
|
||||
for (int y = this->anim_buf_height; y != 0 ; y--) {
|
||||
for (int x = this->anim_buf_width; x != 0 ; x--) {
|
||||
uint colour = GB(*anim, 0, 8);
|
||||
if (colour >= PALETTE_ANIM_START) {
|
||||
/* Update this pixel */
|
||||
*dst = this->AdjustBrightness(LookupColourInPalette(colour), GB(*anim, 8, 8));
|
||||
}
|
||||
dst++;
|
||||
anim++;
|
||||
}
|
||||
dst += _screen.pitch - this->anim_buf_width;
|
||||
}
|
||||
|
||||
/* Make sure the backend redraws the whole screen */
|
||||
VideoDriver::GetInstance()->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
|
||||
Blitter::PaletteAnimation Blitter_32bppAnim::UsePaletteAnimation()
|
||||
{
|
||||
return Blitter::PALETTE_ANIMATION_BLITTER;
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::PostResize()
|
||||
{
|
||||
if (_screen.width != this->anim_buf_width || _screen.height != this->anim_buf_height) {
|
||||
/* The size of the screen changed; we can assume we can wipe all data from our buffer */
|
||||
free(this->anim_buf);
|
||||
this->anim_buf = CallocT<uint16>(_screen.width * _screen.height);
|
||||
this->anim_buf_width = _screen.width;
|
||||
this->anim_buf_height = _screen.height;
|
||||
}
|
||||
}
|
||||
65
src/blitter/32bpp_anim.hpp
Normal file
65
src/blitter/32bpp_anim.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_anim.hpp A 32 bpp blitter with animation support. */
|
||||
|
||||
#ifndef BLITTER_32BPP_ANIM_HPP
|
||||
#define BLITTER_32BPP_ANIM_HPP
|
||||
|
||||
#include "32bpp_optimized.hpp"
|
||||
|
||||
/** The optimised 32 bpp blitter with palette animation. */
|
||||
class Blitter_32bppAnim : public Blitter_32bppOptimized {
|
||||
protected:
|
||||
uint16 *anim_buf; ///< In this buffer we keep track of the 8bpp indexes so we can do palette animation
|
||||
int anim_buf_width; ///< The width of the animation buffer.
|
||||
int anim_buf_height; ///< The height of the animation buffer.
|
||||
Palette palette; ///< The current palette.
|
||||
|
||||
public:
|
||||
Blitter_32bppAnim() :
|
||||
anim_buf(NULL),
|
||||
anim_buf_width(0),
|
||||
anim_buf_height(0)
|
||||
{}
|
||||
|
||||
/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
|
||||
/* virtual */ void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal);
|
||||
/* virtual */ void SetPixel(void *video, int x, int y, uint8 colour);
|
||||
/* virtual */ void DrawRect(void *video, int width, int height, uint8 colour);
|
||||
/* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height);
|
||||
/* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height);
|
||||
/* virtual */ void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y);
|
||||
/* virtual */ int BufferSize(int width, int height);
|
||||
/* virtual */ void PaletteAnimate(const Palette &palette);
|
||||
/* virtual */ Blitter::PaletteAnimation UsePaletteAnimation();
|
||||
|
||||
/* virtual */ const char *GetName() { return "32bpp-anim"; }
|
||||
/* virtual */ int GetBytesPerPixel() { return 6; }
|
||||
/* virtual */ void PostResize();
|
||||
|
||||
/**
|
||||
* Look up the colour in the current palette.
|
||||
*/
|
||||
inline Colour LookupColourInPalette(uint index)
|
||||
{
|
||||
return this->palette.palette[index];
|
||||
}
|
||||
|
||||
template <BlitterMode mode> void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
|
||||
};
|
||||
|
||||
/** Factory for the 32bpp blitter with animation. */
|
||||
class FBlitter_32bppAnim : public BlitterFactory {
|
||||
public:
|
||||
FBlitter_32bppAnim() : BlitterFactory("32bpp-anim", "32bpp Animation Blitter (palette animation)") {}
|
||||
/* virtual */ Blitter *CreateInstance() { return new Blitter_32bppAnim(); }
|
||||
};
|
||||
|
||||
#endif /* BLITTER_32BPP_ANIM_HPP */
|
||||
415
src/blitter/32bpp_anim_sse4.cpp
Normal file
415
src/blitter/32bpp_anim_sse4.cpp
Normal file
@@ -0,0 +1,415 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse4_anim.cpp Implementation of the SSE4 32 bpp blitter with animation support. */
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../video/video_driver.hpp"
|
||||
#include "../table/sprites.h"
|
||||
#include "32bpp_anim_sse4.hpp"
|
||||
#include "32bpp_sse_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Instantiation of the SSE4 32bpp blitter factory. */
|
||||
static FBlitter_32bppSSE4_Anim iFBlitter_32bppSSE4_Anim;
|
||||
|
||||
/**
|
||||
* Draws a sprite to a (screen) buffer. It is templated to allow faster operation.
|
||||
*
|
||||
* @tparam mode blitter mode
|
||||
* @param bp further blitting parameters
|
||||
* @param zoom zoom level at which we are drawing
|
||||
*/
|
||||
IGNORE_UNINITIALIZED_WARNING_START
|
||||
template <BlitterMode mode, Blitter_32bppSSE2::ReadMode read_mode, Blitter_32bppSSE2::BlockType bt_last, bool translucent, bool animated>
|
||||
inline void Blitter_32bppSSE4_Anim::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
|
||||
{
|
||||
const byte * const remap = bp->remap;
|
||||
Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left;
|
||||
uint16 *anim_line = this->anim_buf + ((uint32 *)bp->dst - (uint32 *)_screen.dst_ptr) + bp->top * this->anim_buf_width + bp->left;
|
||||
int effective_width = bp->width;
|
||||
|
||||
/* Find where to start reading in the source sprite. */
|
||||
const Blitter_32bppSSE_Base::SpriteData * const sd = (const Blitter_32bppSSE_Base::SpriteData *) bp->sprite;
|
||||
const SpriteInfo * const si = &sd->infos[zoom];
|
||||
const MapValue *src_mv_line = (const MapValue *) &sd->data[si->mv_offset] + bp->skip_top * si->sprite_width;
|
||||
const Colour *src_rgba_line = (const Colour *) ((const byte *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size);
|
||||
|
||||
if (read_mode != RM_WITH_MARGIN) {
|
||||
src_rgba_line += bp->skip_left;
|
||||
src_mv_line += bp->skip_left;
|
||||
}
|
||||
const MapValue *src_mv = src_mv_line;
|
||||
|
||||
/* Load these variables into register before loop. */
|
||||
const __m128i a_cm = ALPHA_CONTROL_MASK;
|
||||
const __m128i pack_low_cm = PACK_LOW_CONTROL_MASK;
|
||||
const __m128i tr_nom_base = TRANSPARENT_NOM_BASE;
|
||||
|
||||
for (int y = bp->height; y != 0; y--) {
|
||||
Colour *dst = dst_line;
|
||||
const Colour *src = src_rgba_line + META_LENGTH;
|
||||
if (mode != BM_TRANSPARENT) src_mv = src_mv_line;
|
||||
uint16 *anim = anim_line;
|
||||
|
||||
if (read_mode == RM_WITH_MARGIN) {
|
||||
assert(bt_last == BT_NONE); // or you must ensure block type is preserved
|
||||
anim += src_rgba_line[0].data;
|
||||
src += src_rgba_line[0].data;
|
||||
dst += src_rgba_line[0].data;
|
||||
if (mode != BM_TRANSPARENT) src_mv += src_rgba_line[0].data;
|
||||
const int width_diff = si->sprite_width - bp->width;
|
||||
effective_width = bp->width - (int) src_rgba_line[0].data;
|
||||
const int delta_diff = (int) src_rgba_line[1].data - width_diff;
|
||||
const int new_width = effective_width - delta_diff;
|
||||
effective_width = delta_diff > 0 ? new_width : effective_width;
|
||||
if (effective_width <= 0) goto next_line;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
default:
|
||||
if (!translucent) {
|
||||
for (uint x = (uint) effective_width; x > 0; x--) {
|
||||
if (src->a) {
|
||||
if (animated) {
|
||||
*anim = *(const uint16*) src_mv;
|
||||
*dst = (src_mv->m >= PALETTE_ANIM_START) ? AdjustBrightneSSE(this->LookupColourInPalette(src_mv->m), src_mv->v) : src->data;
|
||||
} else {
|
||||
*anim = 0;
|
||||
*dst = *src;
|
||||
}
|
||||
}
|
||||
if (animated) src_mv++;
|
||||
anim++;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint x = (uint) effective_width/2; x != 0; x--) {
|
||||
uint32 mvX2 = *((uint32 *) const_cast<MapValue *>(src_mv));
|
||||
__m128i srcABCD = _mm_loadl_epi64((const __m128i*) src);
|
||||
__m128i dstABCD = _mm_loadl_epi64((__m128i*) dst);
|
||||
|
||||
if (animated) {
|
||||
/* Remap colours. */
|
||||
const byte m0 = mvX2;
|
||||
if (m0 >= PALETTE_ANIM_START) {
|
||||
const Colour c0 = (this->LookupColourInPalette(m0).data & 0x00FFFFFF) | (src[0].data & 0xFF000000);
|
||||
InsertFirstUint32(AdjustBrightneSSE(c0, (byte) (mvX2 >> 8)).data, srcABCD);
|
||||
}
|
||||
const byte m1 = mvX2 >> 16;
|
||||
if (m1 >= PALETTE_ANIM_START) {
|
||||
const Colour c1 = (this->LookupColourInPalette(m1).data & 0x00FFFFFF) | (src[1].data & 0xFF000000);
|
||||
InsertSecondUint32(AdjustBrightneSSE(c1, (byte) (mvX2 >> 24)).data, srcABCD);
|
||||
}
|
||||
|
||||
/* Update anim buffer. */
|
||||
const byte a0 = src[0].a;
|
||||
const byte a1 = src[1].a;
|
||||
uint32 anim01 = 0;
|
||||
if (a0 == 255) {
|
||||
if (a1 == 255) {
|
||||
*(uint32*) anim = mvX2;
|
||||
goto bmno_full_opacity;
|
||||
}
|
||||
anim01 = (uint16) mvX2;
|
||||
} else if (a0 == 0) {
|
||||
if (a1 == 0) {
|
||||
goto bmno_full_transparency;
|
||||
} else {
|
||||
if (a1 == 255) anim[1] = (uint16) (mvX2 >> 16);
|
||||
goto bmno_alpha_blend;
|
||||
}
|
||||
}
|
||||
if (a1 > 0) {
|
||||
if (a1 == 255) anim01 |= mvX2 & 0xFFFF0000;
|
||||
*(uint32*) anim = anim01;
|
||||
} else {
|
||||
anim[0] = (uint16) anim01;
|
||||
}
|
||||
} else {
|
||||
if (src[0].a) anim[0] = 0;
|
||||
if (src[1].a) anim[1] = 0;
|
||||
}
|
||||
|
||||
/* Blend colours. */
|
||||
bmno_alpha_blend:
|
||||
srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm);
|
||||
bmno_full_opacity:
|
||||
_mm_storel_epi64((__m128i *) dst, srcABCD);
|
||||
bmno_full_transparency:
|
||||
src_mv += 2;
|
||||
src += 2;
|
||||
anim += 2;
|
||||
dst += 2;
|
||||
}
|
||||
|
||||
if ((bt_last == BT_NONE && effective_width & 1) || bt_last == BT_ODD) {
|
||||
if (src->a == 0) {
|
||||
} else if (src->a == 255) {
|
||||
*anim = *(const uint16*) src_mv;
|
||||
*dst = (src_mv->m >= PALETTE_ANIM_START) ? AdjustBrightneSSE(LookupColourInPalette(src_mv->m), src_mv->v) : *src;
|
||||
} else {
|
||||
*anim = 0;
|
||||
__m128i srcABCD;
|
||||
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
|
||||
if (src_mv->m >= PALETTE_ANIM_START) {
|
||||
Colour colour = AdjustBrightneSSE(LookupColourInPalette(src_mv->m), src_mv->v);
|
||||
colour.a = src->a;
|
||||
srcABCD = _mm_cvtsi32_si128(colour.data);
|
||||
} else {
|
||||
srcABCD = _mm_cvtsi32_si128(src->data);
|
||||
}
|
||||
dst->data = _mm_cvtsi128_si32(AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_COLOUR_REMAP:
|
||||
for (uint x = (uint) effective_width / 2; x != 0; x--) {
|
||||
uint32 mvX2 = *((uint32 *) const_cast<MapValue *>(src_mv));
|
||||
__m128i srcABCD = _mm_loadl_epi64((const __m128i*) src);
|
||||
__m128i dstABCD = _mm_loadl_epi64((__m128i*) dst);
|
||||
|
||||
/* Remap colours. */
|
||||
const uint m0 = (byte) mvX2;
|
||||
const uint r0 = remap[m0];
|
||||
const uint m1 = (byte) (mvX2 >> 16);
|
||||
const uint r1 = remap[m1];
|
||||
if (mvX2 & 0x00FF00FF) {
|
||||
#define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \
|
||||
/* Written so the compiler uses CMOV. */ \
|
||||
Colour m_colour = m_colour_init; \
|
||||
{ \
|
||||
const Colour srcm = (Colour) (m_src); \
|
||||
const uint m = (byte) (m_m); \
|
||||
const uint r = remap[m]; \
|
||||
const Colour cmap = (this->LookupColourInPalette(r).data & 0x00FFFFFF) | (srcm.data & 0xFF000000); \
|
||||
m_colour = r == 0 ? m_colour : cmap; \
|
||||
m_colour = m != 0 ? m_colour : srcm; \
|
||||
}
|
||||
#ifdef _SQ64
|
||||
uint64 srcs = _mm_cvtsi128_si64(srcABCD);
|
||||
uint64 dsts;
|
||||
if (animated) dsts = _mm_cvtsi128_si64(dstABCD);
|
||||
uint64 remapped_src = 0;
|
||||
CMOV_REMAP(c0, animated ? dsts : 0, srcs, mvX2);
|
||||
remapped_src = c0.data;
|
||||
CMOV_REMAP(c1, animated ? dsts >> 32 : 0, srcs >> 32, mvX2 >> 16);
|
||||
remapped_src |= (uint64) c1.data << 32;
|
||||
srcABCD = _mm_cvtsi64_si128(remapped_src);
|
||||
#else
|
||||
Colour remapped_src[2];
|
||||
CMOV_REMAP(c0, animated ? _mm_cvtsi128_si32(dstABCD) : 0, _mm_cvtsi128_si32(srcABCD), mvX2);
|
||||
remapped_src[0] = c0.data;
|
||||
CMOV_REMAP(c1, animated ? dst[1] : 0, src[1], mvX2 >> 16);
|
||||
remapped_src[1] = c1.data;
|
||||
srcABCD = _mm_loadl_epi64((__m128i*) &remapped_src);
|
||||
#endif
|
||||
|
||||
if ((mvX2 & 0xFF00FF00) != 0x80008000) srcABCD = AdjustBrightnessOfTwoPixels(srcABCD, mvX2);
|
||||
}
|
||||
|
||||
/* Update anim buffer. */
|
||||
if (animated) {
|
||||
const byte a0 = src[0].a;
|
||||
const byte a1 = src[1].a;
|
||||
uint32 anim01 = mvX2 & 0xFF00FF00;
|
||||
if (a0 == 255) {
|
||||
anim01 |= r0;
|
||||
if (a1 == 255) {
|
||||
*(uint32*) anim = anim01 | (r1 << 16);
|
||||
goto bmcr_full_opacity;
|
||||
}
|
||||
} else if (a0 == 0) {
|
||||
if (a1 == 0) {
|
||||
goto bmcr_full_transparency;
|
||||
} else {
|
||||
if (a1 == 255) {
|
||||
anim[1] = r1 | (anim01 >> 16);
|
||||
}
|
||||
goto bmcr_alpha_blend;
|
||||
}
|
||||
}
|
||||
if (a1 > 0) {
|
||||
if (a1 == 255) anim01 |= r1 << 16;
|
||||
*(uint32*) anim = anim01;
|
||||
} else {
|
||||
anim[0] = (uint16) anim01;
|
||||
}
|
||||
} else {
|
||||
if (src[0].a) anim[0] = 0;
|
||||
if (src[1].a) anim[1] = 0;
|
||||
}
|
||||
|
||||
/* Blend colours. */
|
||||
bmcr_alpha_blend:
|
||||
srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm);
|
||||
bmcr_full_opacity:
|
||||
_mm_storel_epi64((__m128i *) dst, srcABCD);
|
||||
bmcr_full_transparency:
|
||||
src_mv += 2;
|
||||
dst += 2;
|
||||
src += 2;
|
||||
anim += 2;
|
||||
}
|
||||
|
||||
if ((bt_last == BT_NONE && effective_width & 1) || bt_last == BT_ODD) {
|
||||
/* In case the m-channel is zero, do not remap this pixel in any way. */
|
||||
__m128i srcABCD;
|
||||
if (src->a == 0) break;
|
||||
if (src_mv->m) {
|
||||
const uint r = remap[src_mv->m];
|
||||
*anim = (animated && src->a == 255) ? r | ((uint16) src_mv->v << 8 ) : 0;
|
||||
if (r != 0) {
|
||||
Colour remapped_colour = AdjustBrightneSSE(this->LookupColourInPalette(r), src_mv->v);
|
||||
if (src->a == 255) {
|
||||
*dst = remapped_colour;
|
||||
} else {
|
||||
remapped_colour.a = src->a;
|
||||
srcABCD = _mm_cvtsi32_si128(remapped_colour.data);
|
||||
goto bmcr_alpha_blend_single;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*anim = 0;
|
||||
srcABCD = _mm_cvtsi32_si128(src->data);
|
||||
if (src->a < 255) {
|
||||
bmcr_alpha_blend_single:
|
||||
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
|
||||
srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm);
|
||||
}
|
||||
dst->data = _mm_cvtsi128_si32(srcABCD);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_TRANSPARENT:
|
||||
/* Make the current colour a bit more black, so it looks like this image is transparent. */
|
||||
for (uint x = (uint) bp->width / 2; x > 0; x--) {
|
||||
__m128i srcABCD = _mm_loadl_epi64((const __m128i*) src);
|
||||
__m128i dstABCD = _mm_loadl_epi64((__m128i*) dst);
|
||||
_mm_storel_epi64((__m128i *) dst, DarkenTwoPixels(srcABCD, dstABCD, a_cm, tr_nom_base));
|
||||
src += 2;
|
||||
dst += 2;
|
||||
anim += 2;
|
||||
if (src[-2].a) anim[-2] = 0;
|
||||
if (src[-1].a) anim[-1] = 0;
|
||||
}
|
||||
|
||||
if ((bt_last == BT_NONE && bp->width & 1) || bt_last == BT_ODD) {
|
||||
__m128i srcABCD = _mm_cvtsi32_si128(src->data);
|
||||
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
|
||||
dst->data = _mm_cvtsi128_si32(DarkenTwoPixels(srcABCD, dstABCD, a_cm, tr_nom_base));
|
||||
if (src[0].a) anim[0] = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_CRASH_REMAP:
|
||||
for (uint x = (uint) bp->width; x > 0; x--) {
|
||||
if (src_mv->m == 0) {
|
||||
if (src->a != 0) {
|
||||
uint8 g = MakeDark(src->r, src->g, src->b);
|
||||
*dst = ComposeColourRGBA(g, g, g, src->a, *dst);
|
||||
*anim = 0;
|
||||
}
|
||||
} else {
|
||||
uint r = remap[src_mv->m];
|
||||
if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), src_mv->v), src->a, *dst);
|
||||
}
|
||||
src_mv++;
|
||||
dst++;
|
||||
src++;
|
||||
anim++;
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_BLACK_REMAP:
|
||||
for (uint x = (uint) bp->width; x > 0; x--) {
|
||||
if (src->a != 0) {
|
||||
*dst = Colour(0, 0, 0);
|
||||
*anim = 0;
|
||||
}
|
||||
src_mv++;
|
||||
dst++;
|
||||
src++;
|
||||
anim++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
next_line:
|
||||
if (mode != BM_TRANSPARENT) src_mv_line += si->sprite_width;
|
||||
src_rgba_line = (const Colour*) ((const byte*) src_rgba_line + si->sprite_line_size);
|
||||
dst_line += bp->pitch;
|
||||
anim_line += this->anim_buf_width;
|
||||
}
|
||||
}
|
||||
IGNORE_UNINITIALIZED_WARNING_STOP
|
||||
|
||||
/**
|
||||
* Draws a sprite to a (screen) buffer. Calls adequate templated function.
|
||||
*
|
||||
* @param bp further blitting parameters
|
||||
* @param mode blitter mode
|
||||
* @param zoom zoom level at which we are drawing
|
||||
*/
|
||||
void Blitter_32bppSSE4_Anim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
|
||||
{
|
||||
const Blitter_32bppSSE_Base::SpriteFlags sprite_flags = ((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags;
|
||||
switch (mode) {
|
||||
default: {
|
||||
bm_normal:
|
||||
if (bp->skip_left != 0 || bp->width <= MARGIN_NORMAL_THRESHOLD) {
|
||||
const BlockType bt_last = (BlockType) (bp->width & 1);
|
||||
if (bt_last == BT_EVEN) {
|
||||
if (sprite_flags & SF_NO_ANIM) Draw<BM_NORMAL, RM_WITH_SKIP, BT_EVEN, true, false>(bp, zoom);
|
||||
else Draw<BM_NORMAL, RM_WITH_SKIP, BT_EVEN, true, true>(bp, zoom);
|
||||
} else {
|
||||
if (sprite_flags & SF_NO_ANIM) Draw<BM_NORMAL, RM_WITH_SKIP, BT_ODD, true, false>(bp, zoom);
|
||||
else Draw<BM_NORMAL, RM_WITH_SKIP, BT_ODD, true, true>(bp, zoom);
|
||||
}
|
||||
} else {
|
||||
#ifdef _SQ64
|
||||
if (sprite_flags & SF_TRANSLUCENT) {
|
||||
if (sprite_flags & SF_NO_ANIM) Draw<BM_NORMAL, RM_WITH_MARGIN, BT_NONE, true, false>(bp, zoom);
|
||||
else Draw<BM_NORMAL, RM_WITH_MARGIN, BT_NONE, true, true>(bp, zoom);
|
||||
} else {
|
||||
if (sprite_flags & SF_NO_ANIM) Draw<BM_NORMAL, RM_WITH_MARGIN, BT_NONE, false, false>(bp, zoom);
|
||||
else Draw<BM_NORMAL, RM_WITH_MARGIN, BT_NONE, false, true>(bp, zoom);
|
||||
}
|
||||
#else
|
||||
if (sprite_flags & SF_NO_ANIM) Draw<BM_NORMAL, RM_WITH_MARGIN, BT_NONE, true, false>(bp, zoom);
|
||||
else Draw<BM_NORMAL, RM_WITH_MARGIN, BT_NONE, true, true>(bp, zoom);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BM_COLOUR_REMAP:
|
||||
if (sprite_flags & SF_NO_REMAP) goto bm_normal;
|
||||
if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) {
|
||||
if (sprite_flags & SF_NO_ANIM) Draw<BM_COLOUR_REMAP, RM_WITH_SKIP, BT_NONE, true, false>(bp, zoom);
|
||||
else Draw<BM_COLOUR_REMAP, RM_WITH_SKIP, BT_NONE, true, true>(bp, zoom);
|
||||
} else {
|
||||
if (sprite_flags & SF_NO_ANIM) Draw<BM_COLOUR_REMAP, RM_WITH_MARGIN, BT_NONE, true, false>(bp, zoom);
|
||||
else Draw<BM_COLOUR_REMAP, RM_WITH_MARGIN, BT_NONE, true, true>(bp, zoom);
|
||||
}
|
||||
break;
|
||||
case BM_TRANSPARENT: Draw<BM_TRANSPARENT, RM_NONE, BT_NONE, true, true>(bp, zoom); return;
|
||||
case BM_CRASH_REMAP: Draw<BM_CRASH_REMAP, RM_NONE, BT_NONE, true, true>(bp, zoom); return;
|
||||
case BM_BLACK_REMAP: Draw<BM_BLACK_REMAP, RM_NONE, BT_NONE, true, true>(bp, zoom); return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
53
src/blitter/32bpp_anim_sse4.hpp
Normal file
53
src/blitter/32bpp_anim_sse4.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse4_anim.hpp A SSE4 32 bpp blitter with animation support. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSE4_ANIM_HPP
|
||||
#define BLITTER_32BPP_SSE4_ANIM_HPP
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#ifndef SSE_VERSION
|
||||
#define SSE_VERSION 4
|
||||
#endif
|
||||
|
||||
#ifndef FULL_ANIMATION
|
||||
#define FULL_ANIMATION 1
|
||||
#endif
|
||||
|
||||
#include "32bpp_anim.hpp"
|
||||
#include "32bpp_sse4.hpp"
|
||||
|
||||
#undef MARGIN_NORMAL_THRESHOLD
|
||||
#define MARGIN_NORMAL_THRESHOLD 4
|
||||
|
||||
/** The SSE4 32 bpp blitter with palette animation. */
|
||||
class Blitter_32bppSSE4_Anim FINAL : public Blitter_32bppAnim, public Blitter_32bppSSE_Base {
|
||||
private:
|
||||
|
||||
public:
|
||||
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent, bool animated>
|
||||
/* virtual */ void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
|
||||
/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
|
||||
/* virtual */ Sprite *Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator) {
|
||||
return Blitter_32bppSSE_Base::Encode(sprite, allocator);
|
||||
}
|
||||
/* virtual */ const char *GetName() { return "32bpp-sse4-anim"; }
|
||||
};
|
||||
|
||||
/** Factory for the SSE4 32 bpp blitter (with palette animation). */
|
||||
class FBlitter_32bppSSE4_Anim: public BlitterFactory {
|
||||
public:
|
||||
FBlitter_32bppSSE4_Anim() : BlitterFactory("32bpp-sse4-anim", "SSE4 Blitter (palette animation)", HasCPUIDFlag(1, 2, 19)) {}
|
||||
/* virtual */ Blitter *CreateInstance() { return new Blitter_32bppSSE4_Anim(); }
|
||||
};
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
#endif /* BLITTER_32BPP_SSE4_ANIM_HPP */
|
||||
149
src/blitter/32bpp_base.cpp
Normal file
149
src/blitter/32bpp_base.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_base.cpp Implementation of base for 32 bpp blitters. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "32bpp_base.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
void *Blitter_32bppBase::MoveTo(void *video, int x, int y)
|
||||
{
|
||||
return (uint32 *)video + x + y * _screen.pitch;
|
||||
}
|
||||
|
||||
void Blitter_32bppBase::SetPixel(void *video, int x, int y, uint8 colour)
|
||||
{
|
||||
*((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour);
|
||||
}
|
||||
|
||||
void Blitter_32bppBase::DrawRect(void *video, int width, int height, uint8 colour)
|
||||
{
|
||||
Colour colour32 = LookupColourInPalette(colour);
|
||||
|
||||
do {
|
||||
Colour *dst = (Colour *)video;
|
||||
for (int i = width; i > 0; i--) {
|
||||
*dst = colour32;
|
||||
dst++;
|
||||
}
|
||||
video = (uint32 *)video + _screen.pitch;
|
||||
} while (--height);
|
||||
}
|
||||
|
||||
void Blitter_32bppBase::CopyFromBuffer(void *video, const void *src, int width, int height)
|
||||
{
|
||||
uint32 *dst = (uint32 *)video;
|
||||
const uint32 *usrc = (const uint32 *)src;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(dst, usrc, width * sizeof(uint32));
|
||||
usrc += width;
|
||||
dst += _screen.pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_32bppBase::CopyToBuffer(const void *video, void *dst, int width, int height)
|
||||
{
|
||||
uint32 *udst = (uint32 *)dst;
|
||||
const uint32 *src = (const uint32 *)video;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint32));
|
||||
src += _screen.pitch;
|
||||
udst += width;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_32bppBase::CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch)
|
||||
{
|
||||
uint32 *udst = (uint32 *)dst;
|
||||
const uint32 *src = (const uint32 *)video;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint32));
|
||||
src += _screen.pitch;
|
||||
udst += dst_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_32bppBase::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y)
|
||||
{
|
||||
const uint32 *src;
|
||||
uint32 *dst;
|
||||
|
||||
if (scroll_y > 0) {
|
||||
/* Calculate pointers */
|
||||
dst = (uint32 *)video + left + (top + height - 1) * _screen.pitch;
|
||||
src = dst - scroll_y * _screen.pitch;
|
||||
|
||||
/* Decrease height and increase top */
|
||||
top += scroll_y;
|
||||
height -= scroll_y;
|
||||
assert(height > 0);
|
||||
|
||||
/* Adjust left & width */
|
||||
if (scroll_x >= 0) {
|
||||
dst += scroll_x;
|
||||
left += scroll_x;
|
||||
width -= scroll_x;
|
||||
} else {
|
||||
src -= scroll_x;
|
||||
width += scroll_x;
|
||||
}
|
||||
|
||||
for (int h = height; h > 0; h--) {
|
||||
memcpy(dst, src, width * sizeof(uint32));
|
||||
src -= _screen.pitch;
|
||||
dst -= _screen.pitch;
|
||||
}
|
||||
} else {
|
||||
/* Calculate pointers */
|
||||
dst = (uint32 *)video + left + top * _screen.pitch;
|
||||
src = dst - scroll_y * _screen.pitch;
|
||||
|
||||
/* Decrease height. (scroll_y is <=0). */
|
||||
height += scroll_y;
|
||||
assert(height > 0);
|
||||
|
||||
/* Adjust left & width */
|
||||
if (scroll_x >= 0) {
|
||||
dst += scroll_x;
|
||||
left += scroll_x;
|
||||
width -= scroll_x;
|
||||
} else {
|
||||
src -= scroll_x;
|
||||
width += scroll_x;
|
||||
}
|
||||
|
||||
/* the y-displacement may be 0 therefore we have to use memmove,
|
||||
* because source and destination may overlap */
|
||||
for (int h = height; h > 0; h--) {
|
||||
memmove(dst, src, width * sizeof(uint32));
|
||||
src += _screen.pitch;
|
||||
dst += _screen.pitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Blitter_32bppBase::BufferSize(int width, int height)
|
||||
{
|
||||
return width * height * sizeof(uint32);
|
||||
}
|
||||
|
||||
void Blitter_32bppBase::PaletteAnimate(const Palette &palette)
|
||||
{
|
||||
/* By default, 32bpp doesn't have palette animation */
|
||||
}
|
||||
|
||||
Blitter::PaletteAnimation Blitter_32bppBase::UsePaletteAnimation()
|
||||
{
|
||||
return Blitter::PALETTE_ANIMATION_NONE;
|
||||
}
|
||||
176
src/blitter/32bpp_base.hpp
Normal file
176
src/blitter/32bpp_base.hpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_base.hpp Base for all 32 bits blitters. */
|
||||
|
||||
#ifndef BLITTER_32BPP_BASE_HPP
|
||||
#define BLITTER_32BPP_BASE_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
#include "../core/bitmath_func.hpp"
|
||||
#include "../core/math_func.hpp"
|
||||
#include "../gfx_func.h"
|
||||
|
||||
/** Base for all 32bpp blitters. */
|
||||
class Blitter_32bppBase : public Blitter {
|
||||
public:
|
||||
/* virtual */ uint8 GetScreenDepth() { return 32; }
|
||||
/* virtual */ void *MoveTo(void *video, int x, int y);
|
||||
/* virtual */ void SetPixel(void *video, int x, int y, uint8 colour);
|
||||
/* virtual */ void DrawRect(void *video, int width, int height, uint8 colour);
|
||||
/* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height);
|
||||
/* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height);
|
||||
/* virtual */ void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch);
|
||||
/* virtual */ void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y);
|
||||
/* virtual */ int BufferSize(int width, int height);
|
||||
/* virtual */ void PaletteAnimate(const Palette &palette);
|
||||
/* virtual */ Blitter::PaletteAnimation UsePaletteAnimation();
|
||||
/* virtual */ int GetBytesPerPixel() { return 4; }
|
||||
|
||||
/**
|
||||
* Look up the colour in the current palette.
|
||||
*/
|
||||
static inline Colour LookupColourInPalette(uint index)
|
||||
{
|
||||
return _cur_palette.palette[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a colour based on RGBA values and the current pixel value.
|
||||
*/
|
||||
static inline Colour ComposeColourRGBANoCheck(uint r, uint g, uint b, uint a, Colour current)
|
||||
{
|
||||
uint cr = current.r;
|
||||
uint cg = current.g;
|
||||
uint cb = current.b;
|
||||
|
||||
/* The 256 is wrong, it should be 255, but 256 is much faster... */
|
||||
return Colour(
|
||||
((int)(r - cr) * a) / 256 + cr,
|
||||
((int)(g - cg) * a) / 256 + cg,
|
||||
((int)(b - cb) * a) / 256 + cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a colour based on RGBA values and the current pixel value.
|
||||
* Handles fully transparent and solid pixels in a special (faster) way.
|
||||
*/
|
||||
static inline Colour ComposeColourRGBA(uint r, uint g, uint b, uint a, Colour current)
|
||||
{
|
||||
if (a == 0) return current;
|
||||
if (a >= 255) return Colour(r, g, b);
|
||||
|
||||
return ComposeColourRGBANoCheck(r, g, b, a, current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a colour based on Pixel value, alpha value, and the current pixel value.
|
||||
*/
|
||||
static inline Colour ComposeColourPANoCheck(Colour colour, uint a, Colour current)
|
||||
{
|
||||
uint r = colour.r;
|
||||
uint g = colour.g;
|
||||
uint b = colour.b;
|
||||
|
||||
return ComposeColourRGBANoCheck(r, g, b, a, current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a colour based on Pixel value, alpha value, and the current pixel value.
|
||||
* Handles fully transparent and solid pixels in a special (faster) way.
|
||||
*/
|
||||
static inline Colour ComposeColourPA(Colour colour, uint a, Colour current)
|
||||
{
|
||||
if (a == 0) return current;
|
||||
if (a >= 255) {
|
||||
colour.a = 255;
|
||||
return colour;
|
||||
}
|
||||
|
||||
return ComposeColourPANoCheck(colour, a, current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a pixel looks like it is transparent.
|
||||
* @param colour the colour already on the screen.
|
||||
* @param nom the amount of transparency, nominator, makes colour lighter.
|
||||
* @param denom denominator, makes colour darker.
|
||||
* @return the new colour for the screen.
|
||||
*/
|
||||
static inline Colour MakeTransparent(Colour colour, uint nom, uint denom = 256)
|
||||
{
|
||||
uint r = colour.r;
|
||||
uint g = colour.g;
|
||||
uint b = colour.b;
|
||||
|
||||
return Colour(r * nom / denom, g * nom / denom, b * nom / denom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a colour dark grey, for specialized 32bpp remapping.
|
||||
* @param r red component
|
||||
* @param g green component
|
||||
* @param b blue component
|
||||
* @return the brightness value of the new colour, now dark grey.
|
||||
*/
|
||||
static inline uint8 MakeDark(uint8 r, uint8 g, uint8 b)
|
||||
{
|
||||
/* Magic-numbers are ~66% of those used in MakeGrey() */
|
||||
return ((r * 13063) + (g * 25647) + (b * 4981)) / 65536;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a colour grey - based.
|
||||
* @param colour the colour to make grey.
|
||||
* @return the new colour, now grey.
|
||||
*/
|
||||
static inline Colour MakeGrey(Colour colour)
|
||||
{
|
||||
uint r = colour.r;
|
||||
uint g = colour.g;
|
||||
uint b = colour.b;
|
||||
|
||||
/* To avoid doubles and stuff, multiple it with a total of 65536 (16bits), then
|
||||
* divide by it to normalize the value to a byte again. See heightmap.cpp for
|
||||
* information about the formula. */
|
||||
uint grey = ((r * 19595) + (g * 38470) + (b * 7471)) / 65536;
|
||||
|
||||
return Colour(grey, grey, grey);
|
||||
}
|
||||
|
||||
static const int DEFAULT_BRIGHTNESS = 128;
|
||||
|
||||
static inline Colour AdjustBrightness(Colour colour, uint8 brightness)
|
||||
{
|
||||
/* Shortcut for normal brightness */
|
||||
if (brightness == DEFAULT_BRIGHTNESS) return colour;
|
||||
|
||||
uint16 ob = 0;
|
||||
uint16 r = colour.r * brightness / DEFAULT_BRIGHTNESS;
|
||||
uint16 g = colour.g * brightness / DEFAULT_BRIGHTNESS;
|
||||
uint16 b = colour.b * brightness / DEFAULT_BRIGHTNESS;
|
||||
|
||||
/* Sum overbright */
|
||||
if (r > 255) ob += r - 255;
|
||||
if (g > 255) ob += g - 255;
|
||||
if (b > 255) ob += b - 255;
|
||||
|
||||
if (ob == 0) return Colour(r, g, b, colour.a);
|
||||
|
||||
/* Reduce overbright strength */
|
||||
ob /= 2;
|
||||
return Colour(
|
||||
r >= 255 ? 255 : min(r + ob * (255 - r) / 256, 255),
|
||||
g >= 255 ? 255 : min(g + ob * (255 - g) / 256, 255),
|
||||
b >= 255 ? 255 : min(b + ob * (255 - b) / 256, 255),
|
||||
colour.a);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* BLITTER_32BPP_BASE_HPP */
|
||||
401
src/blitter/32bpp_optimized.cpp
Normal file
401
src/blitter/32bpp_optimized.cpp
Normal file
@@ -0,0 +1,401 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_optimized.cpp Implementation of the optimized 32 bpp blitter. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../zoom_func.h"
|
||||
#include "../settings_type.h"
|
||||
#include "32bpp_optimized.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Instantiation of the optimized 32bpp blitter factory. */
|
||||
static FBlitter_32bppOptimized iFBlitter_32bppOptimized;
|
||||
|
||||
/**
|
||||
* Draws a sprite to a (screen) buffer. It is templated to allow faster operation.
|
||||
*
|
||||
* @tparam mode blitter mode
|
||||
* @param bp further blitting parameters
|
||||
* @param zoom zoom level at which we are drawing
|
||||
*/
|
||||
template <BlitterMode mode>
|
||||
inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
|
||||
{
|
||||
const SpriteData *src = (const SpriteData *)bp->sprite;
|
||||
|
||||
/* src_px : each line begins with uint32 n = 'number of bytes in this line',
|
||||
* then n times is the Colour struct for this line */
|
||||
const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]);
|
||||
/* src_n : each line begins with uint32 n = 'number of bytes in this line',
|
||||
* then interleaved stream of 'm' and 'n' channels. 'm' is remap,
|
||||
* 'n' is number of bytes with the same alpha channel class */
|
||||
const uint16 *src_n = (const uint16 *)(src->data + src->offset[zoom][1]);
|
||||
|
||||
/* skip upper lines in src_px and src_n */
|
||||
for (uint i = bp->skip_top; i != 0; i--) {
|
||||
src_px = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
|
||||
src_n = (const uint16 *)((const byte *)src_n + *(const uint32 *)src_n);
|
||||
}
|
||||
|
||||
/* skip lines in dst */
|
||||
Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left;
|
||||
|
||||
/* store so we don't have to access it via bp everytime (compiler assumes pointer aliasing) */
|
||||
const byte *remap = bp->remap;
|
||||
|
||||
for (int y = 0; y < bp->height; y++) {
|
||||
/* next dst line begins here */
|
||||
Colour *dst_ln = dst + bp->pitch;
|
||||
|
||||
/* next src line begins here */
|
||||
const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
|
||||
src_px++;
|
||||
|
||||
/* next src_n line begins here */
|
||||
const uint16 *src_n_ln = (const uint16 *)((const byte *)src_n + *(const uint32 *)src_n);
|
||||
src_n += 2;
|
||||
|
||||
/* we will end this line when we reach this point */
|
||||
Colour *dst_end = dst + bp->skip_left;
|
||||
|
||||
/* number of pixels with the same aplha channel class */
|
||||
uint n;
|
||||
|
||||
while (dst < dst_end) {
|
||||
n = *src_n++;
|
||||
|
||||
if (src_px->a == 0) {
|
||||
dst += n;
|
||||
src_px ++;
|
||||
src_n++;
|
||||
} else {
|
||||
if (dst + n > dst_end) {
|
||||
uint d = dst_end - dst;
|
||||
src_px += d;
|
||||
src_n += d;
|
||||
|
||||
dst = dst_end - bp->skip_left;
|
||||
dst_end = dst + bp->width;
|
||||
|
||||
n = min<uint>(n - d, (uint)bp->width);
|
||||
goto draw;
|
||||
}
|
||||
dst += n;
|
||||
src_px += n;
|
||||
src_n += n;
|
||||
}
|
||||
}
|
||||
|
||||
dst -= bp->skip_left;
|
||||
dst_end -= bp->skip_left;
|
||||
|
||||
dst_end += bp->width;
|
||||
|
||||
while (dst < dst_end) {
|
||||
n = min<uint>(*src_n++, (uint)(dst_end - dst));
|
||||
|
||||
if (src_px->a == 0) {
|
||||
dst += n;
|
||||
src_px++;
|
||||
src_n++;
|
||||
continue;
|
||||
}
|
||||
|
||||
draw:;
|
||||
|
||||
switch (mode) {
|
||||
case BM_COLOUR_REMAP:
|
||||
if (src_px->a == 255) {
|
||||
do {
|
||||
uint m = *src_n;
|
||||
/* In case the m-channel is zero, do not remap this pixel in any way */
|
||||
if (m == 0) {
|
||||
*dst = src_px->data;
|
||||
} else {
|
||||
uint r = remap[GB(m, 0, 8)];
|
||||
if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
|
||||
}
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
} else {
|
||||
do {
|
||||
uint m = *src_n;
|
||||
if (m == 0) {
|
||||
*dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
|
||||
} else {
|
||||
uint r = remap[GB(m, 0, 8)];
|
||||
if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst);
|
||||
}
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_CRASH_REMAP:
|
||||
if (src_px->a == 255) {
|
||||
do {
|
||||
uint m = *src_n;
|
||||
if (m == 0) {
|
||||
uint8 g = MakeDark(src_px->r, src_px->g, src_px->b);
|
||||
*dst = ComposeColourRGBA(g, g, g, src_px->a, *dst);
|
||||
} else {
|
||||
uint r = remap[GB(m, 0, 8)];
|
||||
if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
|
||||
}
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
} else {
|
||||
do {
|
||||
uint m = *src_n;
|
||||
if (m == 0) {
|
||||
if (src_px->a != 0) {
|
||||
uint8 g = MakeDark(src_px->r, src_px->g, src_px->b);
|
||||
*dst = ComposeColourRGBA(g, g, g, src_px->a, *dst);
|
||||
}
|
||||
} else {
|
||||
uint r = remap[GB(m, 0, 8)];
|
||||
if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst);
|
||||
}
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_BLACK_REMAP:
|
||||
do {
|
||||
*dst = Colour(0, 0, 0);
|
||||
dst++;
|
||||
src_px++;
|
||||
src_n++;
|
||||
} while (--n != 0);
|
||||
break;
|
||||
|
||||
case BM_TRANSPARENT:
|
||||
/* TODO -- We make an assumption here that the remap in fact is transparency, not some colour.
|
||||
* This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
|
||||
* we produce a result the newgrf maker didn't expect ;) */
|
||||
|
||||
/* Make the current colour a bit more black, so it looks like this image is transparent */
|
||||
src_n += n;
|
||||
if (src_px->a == 255) {
|
||||
src_px += n;
|
||||
do {
|
||||
*dst = MakeTransparent(*dst, 3, 4);
|
||||
dst++;
|
||||
} while (--n != 0);
|
||||
} else {
|
||||
do {
|
||||
*dst = MakeTransparent(*dst, (256 * 4 - src_px->a), 256 * 4);
|
||||
dst++;
|
||||
src_px++;
|
||||
} while (--n != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (src_px->a == 255) {
|
||||
/* faster than memcpy(), n is usually low */
|
||||
src_n += n;
|
||||
do {
|
||||
*dst = src_px->data;
|
||||
dst++;
|
||||
src_px++;
|
||||
} while (--n != 0);
|
||||
} else {
|
||||
src_n += n;
|
||||
do {
|
||||
*dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
|
||||
dst++;
|
||||
src_px++;
|
||||
} while (--n != 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dst = dst_ln;
|
||||
src_px = src_px_ln;
|
||||
src_n = src_n_ln;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a sprite to a (screen) buffer. Calls adequate templated function.
|
||||
*
|
||||
* @param bp further blitting parameters
|
||||
* @param mode blitter mode
|
||||
* @param zoom zoom level at which we are drawing
|
||||
*/
|
||||
void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
|
||||
{
|
||||
switch (mode) {
|
||||
default: NOT_REACHED();
|
||||
case BM_NORMAL: Draw<BM_NORMAL> (bp, zoom); return;
|
||||
case BM_COLOUR_REMAP: Draw<BM_COLOUR_REMAP>(bp, zoom); return;
|
||||
case BM_TRANSPARENT: Draw<BM_TRANSPARENT> (bp, zoom); return;
|
||||
case BM_CRASH_REMAP: Draw<BM_CRASH_REMAP> (bp, zoom); return;
|
||||
case BM_BLACK_REMAP: Draw<BM_BLACK_REMAP> (bp, zoom); return;
|
||||
}
|
||||
}
|
||||
|
||||
Sprite *Blitter_32bppOptimized::Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator)
|
||||
{
|
||||
/* streams of pixels (a, r, g, b channels)
|
||||
*
|
||||
* stored in separated stream so data are always aligned on 4B boundary */
|
||||
Colour *dst_px_orig[ZOOM_LVL_COUNT];
|
||||
|
||||
/* interleaved stream of 'm' channel and 'n' channel
|
||||
* 'n' is number of following pixels with the same alpha channel class
|
||||
* there are 3 classes: 0, 255, others
|
||||
*
|
||||
* it has to be stored in one stream so fewer registers are used -
|
||||
* x86 has problems with register allocation even with this solution */
|
||||
uint16 *dst_n_orig[ZOOM_LVL_COUNT];
|
||||
|
||||
/* lengths of streams */
|
||||
uint32 lengths[ZOOM_LVL_COUNT][2];
|
||||
|
||||
ZoomLevel zoom_min;
|
||||
ZoomLevel zoom_max;
|
||||
|
||||
if (sprite->type == ST_FONT) {
|
||||
zoom_min = ZOOM_LVL_NORMAL;
|
||||
zoom_max = ZOOM_LVL_NORMAL;
|
||||
} else {
|
||||
zoom_min = _settings_client.gui.zoom_min;
|
||||
zoom_max = _settings_client.gui.zoom_max;
|
||||
if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX;
|
||||
}
|
||||
|
||||
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
|
||||
const SpriteLoader::Sprite *src_orig = &sprite[z];
|
||||
|
||||
uint size = src_orig->height * src_orig->width;
|
||||
|
||||
dst_px_orig[z] = CallocT<Colour>(size + src_orig->height * 2);
|
||||
dst_n_orig[z] = CallocT<uint16>(size * 2 + src_orig->height * 4 * 2);
|
||||
|
||||
uint32 *dst_px_ln = (uint32 *)dst_px_orig[z];
|
||||
uint32 *dst_n_ln = (uint32 *)dst_n_orig[z];
|
||||
|
||||
const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *)src_orig->data;
|
||||
|
||||
for (uint y = src_orig->height; y > 0; y--) {
|
||||
Colour *dst_px = (Colour *)(dst_px_ln + 1);
|
||||
uint16 *dst_n = (uint16 *)(dst_n_ln + 1);
|
||||
|
||||
uint16 *dst_len = dst_n++;
|
||||
|
||||
uint last = 3;
|
||||
int len = 0;
|
||||
|
||||
for (uint x = src_orig->width; x > 0; x--) {
|
||||
uint8 a = src->a;
|
||||
uint t = a > 0 && a < 255 ? 1 : a;
|
||||
|
||||
if (last != t || len == 65535) {
|
||||
if (last != 3) {
|
||||
*dst_len = len;
|
||||
dst_len = dst_n++;
|
||||
}
|
||||
len = 0;
|
||||
}
|
||||
|
||||
last = t;
|
||||
len++;
|
||||
|
||||
if (a != 0) {
|
||||
dst_px->a = a;
|
||||
*dst_n = src->m;
|
||||
if (src->m != 0) {
|
||||
/* Get brightest value */
|
||||
uint8 rgb_max = max(src->r, max(src->g, src->b));
|
||||
|
||||
/* Black pixel (8bpp or old 32bpp image), so use default value */
|
||||
if (rgb_max == 0) rgb_max = DEFAULT_BRIGHTNESS;
|
||||
*dst_n |= rgb_max << 8;
|
||||
|
||||
/* Pre-convert the mapping channel to a RGB value */
|
||||
Colour colour = this->AdjustBrightness(this->LookupColourInPalette(src->m), rgb_max);
|
||||
dst_px->r = colour.r;
|
||||
dst_px->g = colour.g;
|
||||
dst_px->b = colour.b;
|
||||
} else {
|
||||
dst_px->r = src->r;
|
||||
dst_px->g = src->g;
|
||||
dst_px->b = src->b;
|
||||
}
|
||||
dst_px++;
|
||||
dst_n++;
|
||||
} else if (len == 1) {
|
||||
dst_px++;
|
||||
*dst_n = src->m;
|
||||
dst_n++;
|
||||
}
|
||||
|
||||
src++;
|
||||
}
|
||||
|
||||
if (last != 3) {
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
dst_px = (Colour *)AlignPtr(dst_px, 4);
|
||||
dst_n = (uint16 *)AlignPtr(dst_n, 4);
|
||||
|
||||
*dst_px_ln = (uint8 *)dst_px - (uint8 *)dst_px_ln;
|
||||
*dst_n_ln = (uint8 *)dst_n - (uint8 *)dst_n_ln;
|
||||
|
||||
dst_px_ln = (uint32 *)dst_px;
|
||||
dst_n_ln = (uint32 *)dst_n;
|
||||
}
|
||||
|
||||
lengths[z][0] = (byte *)dst_px_ln - (byte *)dst_px_orig[z]; // all are aligned to 4B boundary
|
||||
lengths[z][1] = (byte *)dst_n_ln - (byte *)dst_n_orig[z];
|
||||
}
|
||||
|
||||
uint len = 0; // total length of data
|
||||
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
|
||||
len += lengths[z][0] + lengths[z][1];
|
||||
}
|
||||
|
||||
Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sizeof(SpriteData) + len);
|
||||
|
||||
dest_sprite->height = sprite->height;
|
||||
dest_sprite->width = sprite->width;
|
||||
dest_sprite->x_offs = sprite->x_offs;
|
||||
dest_sprite->y_offs = sprite->y_offs;
|
||||
|
||||
SpriteData *dst = (SpriteData *)dest_sprite->data;
|
||||
memset(dst, 0, sizeof(*dst));
|
||||
|
||||
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
|
||||
dst->offset[z][0] = z == zoom_min ? 0 : lengths[z - 1][1] + dst->offset[z - 1][1];
|
||||
dst->offset[z][1] = lengths[z][0] + dst->offset[z][0];
|
||||
|
||||
memcpy(dst->data + dst->offset[z][0], dst_px_orig[z], lengths[z][0]);
|
||||
memcpy(dst->data + dst->offset[z][1], dst_n_orig[z], lengths[z][1]);
|
||||
|
||||
free(dst_px_orig[z]);
|
||||
free(dst_n_orig[z]);
|
||||
}
|
||||
|
||||
return dest_sprite;
|
||||
}
|
||||
41
src/blitter/32bpp_optimized.hpp
Normal file
41
src/blitter/32bpp_optimized.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_optimized.hpp Optimized 32 bpp blitter. */
|
||||
|
||||
#ifndef BLITTER_32BPP_OPTIMIZED_HPP
|
||||
#define BLITTER_32BPP_OPTIMIZED_HPP
|
||||
|
||||
#include "32bpp_simple.hpp"
|
||||
|
||||
/** The optimised 32 bpp blitter (without palette animation). */
|
||||
class Blitter_32bppOptimized : public Blitter_32bppSimple {
|
||||
public:
|
||||
/** Data stored about a (single) sprite. */
|
||||
struct SpriteData {
|
||||
uint32 offset[ZOOM_LVL_COUNT][2]; ///< Offsets (from .data) to streams for different zoom levels, and the normal and remap image information.
|
||||
byte data[]; ///< Data, all zoomlevels.
|
||||
};
|
||||
|
||||
/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
|
||||
/* virtual */ Sprite *Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator);
|
||||
|
||||
/* virtual */ const char *GetName() { return "32bpp-optimized"; }
|
||||
|
||||
template <BlitterMode mode> void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
|
||||
};
|
||||
|
||||
/** Factory for the optimised 32 bpp blitter (without palette animation). */
|
||||
class FBlitter_32bppOptimized : public BlitterFactory {
|
||||
public:
|
||||
FBlitter_32bppOptimized() : BlitterFactory("32bpp-optimized", "32bpp Optimized Blitter (no palette animation)") {}
|
||||
/* virtual */ Blitter *CreateInstance() { return new Blitter_32bppOptimized(); }
|
||||
};
|
||||
|
||||
#endif /* BLITTER_32BPP_OPTIMIZED_HPP */
|
||||
155
src/blitter/32bpp_simple.cpp
Normal file
155
src/blitter/32bpp_simple.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_simple.cpp Implementation of the simple 32 bpp blitter. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../zoom_func.h"
|
||||
#include "32bpp_simple.hpp"
|
||||
|
||||
#include "../table/sprites.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Instantiation of the simple 32bpp blitter factory. */
|
||||
static FBlitter_32bppSimple iFBlitter_32bppSimple;
|
||||
|
||||
void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
|
||||
{
|
||||
const Blitter_32bppSimple::Pixel *src, *src_line;
|
||||
Colour *dst, *dst_line;
|
||||
|
||||
/* Find where to start reading in the source sprite */
|
||||
src_line = (const Blitter_32bppSimple::Pixel *)bp->sprite + (bp->skip_top * bp->sprite_width + bp->skip_left) * ScaleByZoom(1, zoom);
|
||||
dst_line = (Colour *)bp->dst + bp->top * bp->pitch + bp->left;
|
||||
|
||||
for (int y = 0; y < bp->height; y++) {
|
||||
dst = dst_line;
|
||||
dst_line += bp->pitch;
|
||||
|
||||
src = src_line;
|
||||
src_line += bp->sprite_width * ScaleByZoom(1, zoom);
|
||||
|
||||
for (int x = 0; x < bp->width; x++) {
|
||||
switch (mode) {
|
||||
case BM_COLOUR_REMAP:
|
||||
/* In case the m-channel is zero, do not remap this pixel in any way */
|
||||
if (src->m == 0) {
|
||||
if (src->a != 0) *dst = ComposeColourRGBA(src->r, src->g, src->b, src->a, *dst);
|
||||
} else {
|
||||
if (bp->remap[src->m] != 0) *dst = ComposeColourPA(this->AdjustBrightness(this->LookupColourInPalette(bp->remap[src->m]), src->v), src->a, *dst);
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_CRASH_REMAP:
|
||||
if (src->m == 0) {
|
||||
if (src->a != 0) {
|
||||
uint8 g = MakeDark(src->r, src->g, src->b);
|
||||
*dst = ComposeColourRGBA(g, g, g, src->a, *dst);
|
||||
}
|
||||
} else {
|
||||
if (bp->remap[src->m] != 0) *dst = ComposeColourPA(this->AdjustBrightness(this->LookupColourInPalette(bp->remap[src->m]), src->v), src->a, *dst);
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_BLACK_REMAP:
|
||||
if (src->a != 0) {
|
||||
*dst = Colour(0, 0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_TRANSPARENT:
|
||||
/* TODO -- We make an assumption here that the remap in fact is transparency, not some colour.
|
||||
* This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
|
||||
* we produce a result the newgrf maker didn't expect ;) */
|
||||
|
||||
/* Make the current colour a bit more black, so it looks like this image is transparent */
|
||||
if (src->a != 0) *dst = MakeTransparent(*dst, 192);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (src->a != 0) *dst = ComposeColourRGBA(src->r, src->g, src->b, src->a, *dst);
|
||||
break;
|
||||
}
|
||||
dst++;
|
||||
src += ScaleByZoom(1, zoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_32bppSimple::DrawColourMappingRect(void *dst, int width, int height, PaletteID pal)
|
||||
{
|
||||
Colour *udst = (Colour *)dst;
|
||||
|
||||
if (pal == PALETTE_TO_TRANSPARENT) {
|
||||
do {
|
||||
for (int i = 0; i != width; i++) {
|
||||
*udst = MakeTransparent(*udst, 154);
|
||||
udst++;
|
||||
}
|
||||
udst = udst - width + _screen.pitch;
|
||||
} while (--height);
|
||||
return;
|
||||
}
|
||||
if (pal == PALETTE_NEWSPAPER) {
|
||||
do {
|
||||
for (int i = 0; i != width; i++) {
|
||||
*udst = MakeGrey(*udst);
|
||||
udst++;
|
||||
}
|
||||
udst = udst - width + _screen.pitch;
|
||||
} while (--height);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('%d')", pal);
|
||||
}
|
||||
|
||||
Sprite *Blitter_32bppSimple::Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator)
|
||||
{
|
||||
Blitter_32bppSimple::Pixel *dst;
|
||||
Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + (size_t)sprite->height * (size_t)sprite->width * sizeof(*dst));
|
||||
|
||||
dest_sprite->height = sprite->height;
|
||||
dest_sprite->width = sprite->width;
|
||||
dest_sprite->x_offs = sprite->x_offs;
|
||||
dest_sprite->y_offs = sprite->y_offs;
|
||||
|
||||
dst = (Blitter_32bppSimple::Pixel *)dest_sprite->data;
|
||||
SpriteLoader::CommonPixel *src = (SpriteLoader::CommonPixel *)sprite->data;
|
||||
|
||||
for (int i = 0; i < sprite->height * sprite->width; i++) {
|
||||
if (src->m == 0) {
|
||||
dst[i].r = src->r;
|
||||
dst[i].g = src->g;
|
||||
dst[i].b = src->b;
|
||||
dst[i].a = src->a;
|
||||
dst[i].m = 0;
|
||||
dst[i].v = 0;
|
||||
} else {
|
||||
/* Get brightest value */
|
||||
uint8 rgb_max = max(src->r, max(src->g, src->b));
|
||||
|
||||
/* Black pixel (8bpp or old 32bpp image), so use default value */
|
||||
if (rgb_max == 0) rgb_max = DEFAULT_BRIGHTNESS;
|
||||
dst[i].v = rgb_max;
|
||||
|
||||
/* Pre-convert the mapping channel to a RGB value */
|
||||
Colour colour = this->AdjustBrightness(this->LookupColourInPalette(src->m), dst[i].v);
|
||||
dst[i].r = colour.r;
|
||||
dst[i].g = colour.g;
|
||||
dst[i].b = colour.b;
|
||||
dst[i].a = src->a;
|
||||
dst[i].m = src->m;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
|
||||
return dest_sprite;
|
||||
}
|
||||
43
src/blitter/32bpp_simple.hpp
Normal file
43
src/blitter/32bpp_simple.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_simple.hpp Simple 32 bpp blitter. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SIMPLE_HPP
|
||||
#define BLITTER_32BPP_SIMPLE_HPP
|
||||
|
||||
#include "32bpp_base.hpp"
|
||||
#include "factory.hpp"
|
||||
|
||||
/** The most trivial 32 bpp blitter (without palette animation). */
|
||||
class Blitter_32bppSimple : public Blitter_32bppBase {
|
||||
struct Pixel {
|
||||
uint8 r; ///< Red-channel
|
||||
uint8 g; ///< Green-channel
|
||||
uint8 b; ///< Blue-channel
|
||||
uint8 a; ///< Alpha-channel
|
||||
uint8 m; ///< Remap-channel
|
||||
uint8 v; ///< Brightness-channel
|
||||
};
|
||||
public:
|
||||
/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
|
||||
/* virtual */ void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal);
|
||||
/* virtual */ Sprite *Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator);
|
||||
|
||||
/* virtual */ const char *GetName() { return "32bpp-simple"; }
|
||||
};
|
||||
|
||||
/** Factory for the simple 32 bpp blitter. */
|
||||
class FBlitter_32bppSimple : public BlitterFactory {
|
||||
public:
|
||||
FBlitter_32bppSimple() : BlitterFactory("32bpp-simple", "32bpp Simple Blitter (no palette animation)") {}
|
||||
/* virtual */ Blitter *CreateInstance() { return new Blitter_32bppSimple(); }
|
||||
};
|
||||
|
||||
#endif /* BLITTER_32BPP_SIMPLE_HPP */
|
||||
143
src/blitter/32bpp_sse2.cpp
Normal file
143
src/blitter/32bpp_sse2.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse2.cpp Implementation of the SSE2 32 bpp blitter. */
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../zoom_func.h"
|
||||
#include "../settings_type.h"
|
||||
#include "32bpp_sse2.hpp"
|
||||
#include "32bpp_sse_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Instantiation of the SSE2 32bpp blitter factory. */
|
||||
static FBlitter_32bppSSE2 iFBlitter_32bppSSE2;
|
||||
|
||||
Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator)
|
||||
{
|
||||
/* First uint32 of a line = the number of transparent pixels from the left.
|
||||
* Second uint32 of a line = the number of transparent pixels from the right.
|
||||
* Then all RGBA then all MV.
|
||||
*/
|
||||
ZoomLevel zoom_min = ZOOM_LVL_NORMAL;
|
||||
ZoomLevel zoom_max = ZOOM_LVL_NORMAL;
|
||||
if (sprite->type != ST_FONT) {
|
||||
zoom_min = _settings_client.gui.zoom_min;
|
||||
zoom_max = _settings_client.gui.zoom_max;
|
||||
if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX;
|
||||
}
|
||||
|
||||
/* Calculate sizes and allocate. */
|
||||
SpriteData sd;
|
||||
memset(&sd, 0, sizeof(sd));
|
||||
uint all_sprites_size = 0;
|
||||
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
|
||||
const SpriteLoader::Sprite *src_sprite = &sprite[z];
|
||||
sd.infos[z].sprite_width = src_sprite->width;
|
||||
sd.infos[z].sprite_offset = all_sprites_size;
|
||||
sd.infos[z].sprite_line_size = sizeof(Colour) * src_sprite->width + sizeof(uint32) * META_LENGTH;
|
||||
|
||||
const uint rgba_size = sd.infos[z].sprite_line_size * src_sprite->height;
|
||||
sd.infos[z].mv_offset = all_sprites_size + rgba_size;
|
||||
|
||||
const uint mv_size = sizeof(MapValue) * src_sprite->width * src_sprite->height;
|
||||
all_sprites_size += rgba_size + mv_size;
|
||||
}
|
||||
|
||||
Sprite *dst_sprite = (Sprite *) allocator(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size);
|
||||
dst_sprite->height = sprite->height;
|
||||
dst_sprite->width = sprite->width;
|
||||
dst_sprite->x_offs = sprite->x_offs;
|
||||
dst_sprite->y_offs = sprite->y_offs;
|
||||
memcpy(dst_sprite->data, &sd, sizeof(SpriteData));
|
||||
|
||||
/* Copy colours and determine flags. */
|
||||
bool has_remap = false;
|
||||
bool has_anim = false;
|
||||
bool has_translucency = false;
|
||||
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
|
||||
const SpriteLoader::Sprite *src_sprite = &sprite[z];
|
||||
const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *) src_sprite->data;
|
||||
Colour *dst_rgba_line = (Colour *) &dst_sprite->data[sizeof(SpriteData) + sd.infos[z].sprite_offset];
|
||||
MapValue *dst_mv = (MapValue *) &dst_sprite->data[sizeof(SpriteData) + sd.infos[z].mv_offset];
|
||||
for (uint y = src_sprite->height; y != 0; y--) {
|
||||
Colour *dst_rgba = dst_rgba_line + META_LENGTH;
|
||||
for (uint x = src_sprite->width; x != 0; x--) {
|
||||
if (src->a != 0) {
|
||||
dst_rgba->a = src->a;
|
||||
if (src->a != 0 && src->a != 255) has_translucency = true;
|
||||
dst_mv->m = src->m;
|
||||
if (src->m != 0) {
|
||||
/* Do some accounting for flags. */
|
||||
has_remap = true;
|
||||
if (src->m >= PALETTE_ANIM_START) has_anim = true;
|
||||
|
||||
/* Get brightest value (or default brightness if it's a black pixel). */
|
||||
const uint8 rgb_max = max(src->r, max(src->g, src->b));
|
||||
dst_mv->v = (rgb_max == 0) ? Blitter_32bppBase::DEFAULT_BRIGHTNESS : rgb_max;
|
||||
|
||||
/* Pre-convert the mapping channel to a RGB value. */
|
||||
const Colour colour = AdjustBrightneSSE(Blitter_32bppBase::LookupColourInPalette(src->m), dst_mv->v);
|
||||
dst_rgba->r = colour.r;
|
||||
dst_rgba->g = colour.g;
|
||||
dst_rgba->b = colour.b;
|
||||
} else {
|
||||
dst_rgba->r = src->r;
|
||||
dst_rgba->g = src->g;
|
||||
dst_rgba->b = src->b;
|
||||
dst_mv->v = Blitter_32bppBase::DEFAULT_BRIGHTNESS;
|
||||
}
|
||||
} else {
|
||||
dst_rgba->data = 0;
|
||||
*(uint16*) dst_mv = 0;
|
||||
}
|
||||
dst_rgba++;
|
||||
dst_mv++;
|
||||
src++;
|
||||
}
|
||||
|
||||
/* Count the number of transparent pixels from the left. */
|
||||
dst_rgba = dst_rgba_line + META_LENGTH;
|
||||
uint32 nb_pix_transp = 0;
|
||||
for (uint x = src_sprite->width; x != 0; x--) {
|
||||
if (dst_rgba->a == 0) nb_pix_transp++;
|
||||
else break;
|
||||
dst_rgba++;
|
||||
}
|
||||
(*dst_rgba_line).data = nb_pix_transp;
|
||||
|
||||
Colour *nb_right = dst_rgba_line + 1;
|
||||
dst_rgba_line = (Colour*) ((byte*) dst_rgba_line + sd.infos[z].sprite_line_size);
|
||||
|
||||
/* Count the number of transparent pixels from the right. */
|
||||
dst_rgba = dst_rgba_line - 1;
|
||||
nb_pix_transp = 0;
|
||||
for (uint x = src_sprite->width; x != 0; x--) {
|
||||
if (dst_rgba->a == 0) nb_pix_transp++;
|
||||
else break;
|
||||
dst_rgba--;
|
||||
}
|
||||
(*nb_right).data = nb_pix_transp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Store sprite flags. */
|
||||
sd.flags = SF_NONE;
|
||||
if (has_translucency) sd.flags |= SF_TRANSLUCENT;
|
||||
if (!has_remap) sd.flags |= SF_NO_REMAP;
|
||||
if (!has_anim) sd.flags |= SF_NO_ANIM;
|
||||
memcpy(dst_sprite->data, &sd, sizeof(SpriteData));
|
||||
|
||||
return dst_sprite;
|
||||
}
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
104
src/blitter/32bpp_sse2.hpp
Normal file
104
src/blitter/32bpp_sse2.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse2.hpp SSE2 32 bpp blitter. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSE2_HPP
|
||||
#define BLITTER_32BPP_SSE2_HPP
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#ifndef SSE_VERSION
|
||||
#define SSE_VERSION 2
|
||||
#endif
|
||||
|
||||
#ifndef FULL_ANIMATION
|
||||
#define FULL_ANIMATION 0
|
||||
#endif
|
||||
|
||||
#include "32bpp_sse_type.h"
|
||||
|
||||
/** Base methods for 32bpp SSE blitters. */
|
||||
class Blitter_32bppSSE_Base {
|
||||
public:
|
||||
virtual ~Blitter_32bppSSE_Base() {}
|
||||
|
||||
struct MapValue {
|
||||
uint8 m;
|
||||
uint8 v;
|
||||
};
|
||||
assert_compile(sizeof(MapValue) == 2);
|
||||
|
||||
/** Helper for creating specialised functions for specific optimisations. */
|
||||
enum ReadMode {
|
||||
RM_WITH_SKIP, ///< Use normal code for skipping empty pixels.
|
||||
RM_WITH_MARGIN, ///< Use cached number of empty pixels at begin and end of line to reduce work.
|
||||
RM_NONE, ///< No specialisation.
|
||||
};
|
||||
|
||||
/** Helper for creating specialised functions for the case where the sprite width is odd or even. */
|
||||
enum BlockType {
|
||||
BT_EVEN, ///< An even number of pixels in the width; no need for a special case for the last pixel.
|
||||
BT_ODD, ///< An odd number of pixels in the width; special case for the last pixel.
|
||||
BT_NONE, ///< No specialisation for either case.
|
||||
};
|
||||
|
||||
/** Helper for using specialised functions designed to prevent whenever it's possible things like:
|
||||
* - IO (reading video buffer),
|
||||
* - calculations (alpha blending),
|
||||
* - heavy branching (remap lookups and animation buffer handling).
|
||||
*/
|
||||
enum SpriteFlags {
|
||||
SF_NONE = 0,
|
||||
SF_TRANSLUCENT = 1 << 1, ///< The sprite has at least 1 translucent pixel.
|
||||
SF_NO_REMAP = 1 << 2, ///< The sprite has no remappable colour pixel.
|
||||
SF_NO_ANIM = 1 << 3, ///< The sprite has no palette animated pixel.
|
||||
};
|
||||
|
||||
/** Data stored about a (single) sprite. */
|
||||
struct SpriteInfo {
|
||||
uint32 sprite_offset; ///< The offset to the sprite data.
|
||||
uint32 mv_offset; ///< The offset to the map value data.
|
||||
uint16 sprite_line_size; ///< The size of a single line (pitch).
|
||||
uint16 sprite_width; ///< The width of the sprite.
|
||||
};
|
||||
struct SpriteData {
|
||||
SpriteFlags flags;
|
||||
SpriteInfo infos[ZOOM_LVL_COUNT];
|
||||
byte data[]; ///< Data, all zoomlevels.
|
||||
};
|
||||
|
||||
Sprite *Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator);
|
||||
};
|
||||
|
||||
DECLARE_ENUM_AS_BIT_SET(Blitter_32bppSSE_Base::SpriteFlags);
|
||||
|
||||
/** The SSE2 32 bpp blitter (without palette animation). */
|
||||
class Blitter_32bppSSE2 : public Blitter_32bppSimple, public Blitter_32bppSSE_Base {
|
||||
public:
|
||||
/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
|
||||
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent>
|
||||
void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
|
||||
|
||||
/* virtual */ Sprite *Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator) {
|
||||
return Blitter_32bppSSE_Base::Encode(sprite, allocator);
|
||||
}
|
||||
|
||||
/* virtual */ const char *GetName() { return "32bpp-sse2"; }
|
||||
};
|
||||
|
||||
/** Factory for the SSE2 32 bpp blitter (without palette animation). */
|
||||
class FBlitter_32bppSSE2 : public BlitterFactory {
|
||||
public:
|
||||
FBlitter_32bppSSE2() : BlitterFactory("32bpp-sse2", "32bpp SSE2 Blitter (no palette animation)", HasCPUIDFlag(1, 3, 26)) {}
|
||||
/* virtual */ Blitter *CreateInstance() { return new Blitter_32bppSSE2(); }
|
||||
};
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
#endif /* BLITTER_32BPP_SSE2_HPP */
|
||||
25
src/blitter/32bpp_sse4.cpp
Normal file
25
src/blitter/32bpp_sse4.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse4.cpp Implementation of the SSE4 32 bpp blitter. */
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../zoom_func.h"
|
||||
#include "../settings_type.h"
|
||||
#include "32bpp_sse4.hpp"
|
||||
#include "32bpp_sse_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Instantiation of the SSE4 32bpp blitter factory. */
|
||||
static FBlitter_32bppSSE4 iFBlitter_32bppSSE4;
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
44
src/blitter/32bpp_sse4.hpp
Normal file
44
src/blitter/32bpp_sse4.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse4.hpp SSE4 32 bpp blitter. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSE4_HPP
|
||||
#define BLITTER_32BPP_SSE4_HPP
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#ifndef SSE_VERSION
|
||||
#define SSE_VERSION 4
|
||||
#endif
|
||||
|
||||
#ifndef FULL_ANIMATION
|
||||
#define FULL_ANIMATION 0
|
||||
#endif
|
||||
|
||||
#include "32bpp_ssse3.hpp"
|
||||
|
||||
/** The SSE4 32 bpp blitter (without palette animation). */
|
||||
class Blitter_32bppSSE4 : public Blitter_32bppSSSE3 {
|
||||
public:
|
||||
/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
|
||||
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent>
|
||||
void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
|
||||
/* virtual */ const char *GetName() { return "32bpp-sse4"; }
|
||||
};
|
||||
|
||||
/** Factory for the SSE4 32 bpp blitter (without palette animation). */
|
||||
class FBlitter_32bppSSE4: public BlitterFactory {
|
||||
public:
|
||||
FBlitter_32bppSSE4() : BlitterFactory("32bpp-sse4", "32bpp SSE4 Blitter (no palette animation)", HasCPUIDFlag(1, 2, 19)) {}
|
||||
/* virtual */ Blitter *CreateInstance() { return new Blitter_32bppSSE4(); }
|
||||
};
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
#endif /* BLITTER_32BPP_SSE4_HPP */
|
||||
467
src/blitter/32bpp_sse_func.hpp
Normal file
467
src/blitter/32bpp_sse_func.hpp
Normal file
@@ -0,0 +1,467 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse_func.hpp Functions related to SSE 32 bpp blitter. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSE_FUNC_HPP
|
||||
#define BLITTER_32BPP_SSE_FUNC_HPP
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
static inline void InsertFirstUint32(const uint32 value, __m128i &into)
|
||||
{
|
||||
#if (SSE_VERSION >= 4)
|
||||
into = _mm_insert_epi32(into, value, 0);
|
||||
#else
|
||||
into = _mm_insert_epi16(into, value, 0);
|
||||
into = _mm_insert_epi16(into, value >> 16, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void InsertSecondUint32(const uint32 value, __m128i &into)
|
||||
{
|
||||
#if (SSE_VERSION >= 4)
|
||||
into = _mm_insert_epi32(into, value, 1);
|
||||
#else
|
||||
into = _mm_insert_epi16(into, value, 2);
|
||||
into = _mm_insert_epi16(into, value >> 16, 3);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void LoadUint64(const uint64 value, __m128i &into)
|
||||
{
|
||||
#ifdef _SQ64
|
||||
into = _mm_cvtsi64_si128(value);
|
||||
#else
|
||||
#if (SSE_VERSION >= 4)
|
||||
into = _mm_cvtsi32_si128(value);
|
||||
InsertSecondUint32(value >> 32, into);
|
||||
#else
|
||||
(*(um128i*) &into).m128i_u64[0] = value;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline __m128i PackUnsaturated(__m128i from, const __m128i &mask)
|
||||
{
|
||||
#if (SSE_VERSION == 2)
|
||||
from = _mm_and_si128(from, mask); // PAND, wipe high bytes to keep low bytes when packing
|
||||
return _mm_packus_epi16(from, from); // PACKUSWB, pack 2 colours (with saturation)
|
||||
#else
|
||||
return _mm_shuffle_epi8(from, mask);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline __m128i DistributeAlpha(const __m128i from, const __m128i &mask)
|
||||
{
|
||||
#if (SSE_VERSION == 2)
|
||||
__m128i alphaAB = _mm_shufflelo_epi16(from, 0x3F); // PSHUFLW, put alpha1 in front of each rgb1
|
||||
return _mm_shufflehi_epi16(alphaAB, 0x3F); // PSHUFHW, put alpha2 in front of each rgb2
|
||||
#else
|
||||
return _mm_shuffle_epi8(from, mask);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline __m128i AlphaBlendTwoPixels(__m128i src, __m128i dst, const __m128i &distribution_mask, const __m128i &pack_mask)
|
||||
{
|
||||
__m128i srcAB = _mm_unpacklo_epi8(src, _mm_setzero_si128()); // PUNPCKLBW, expand each uint8 into uint16
|
||||
__m128i dstAB = _mm_unpacklo_epi8(dst, _mm_setzero_si128());
|
||||
|
||||
__m128i alphaAB = _mm_cmpgt_epi16(srcAB, _mm_setzero_si128()); // PCMPGTW, if (alpha > 0) a++;
|
||||
alphaAB = _mm_srli_epi16(alphaAB, 15);
|
||||
alphaAB = _mm_add_epi16(alphaAB, srcAB);
|
||||
alphaAB = DistributeAlpha(alphaAB, distribution_mask);
|
||||
|
||||
srcAB = _mm_sub_epi16(srcAB, dstAB); // PSUBW, (r - Cr)
|
||||
srcAB = _mm_mullo_epi16(srcAB, alphaAB); // PMULLW, a*(r - Cr)
|
||||
srcAB = _mm_srli_epi16(srcAB, 8); // PSRLW, a*(r - Cr)/256
|
||||
srcAB = _mm_add_epi16(srcAB, dstAB); // PADDW, a*(r - Cr)/256 + Cr
|
||||
return PackUnsaturated(srcAB, pack_mask);
|
||||
}
|
||||
|
||||
/* Darken 2 pixels.
|
||||
* rgb = rgb * ((256/4) * 4 - (alpha/4)) / ((256/4) * 4)
|
||||
*/
|
||||
static inline __m128i DarkenTwoPixels(__m128i src, __m128i dst, const __m128i &distribution_mask, const __m128i &tr_nom_base)
|
||||
{
|
||||
__m128i srcAB = _mm_unpacklo_epi8(src, _mm_setzero_si128());
|
||||
__m128i dstAB = _mm_unpacklo_epi8(dst, _mm_setzero_si128());
|
||||
__m128i alphaAB = DistributeAlpha(srcAB, distribution_mask);
|
||||
alphaAB = _mm_srli_epi16(alphaAB, 2); // Reduce to 64 levels of shades so the max value fits in 16 bits.
|
||||
__m128i nom = _mm_sub_epi16(tr_nom_base, alphaAB);
|
||||
dstAB = _mm_mullo_epi16(dstAB, nom);
|
||||
dstAB = _mm_srli_epi16(dstAB, 8);
|
||||
return _mm_packus_epi16(dstAB, dstAB);
|
||||
}
|
||||
|
||||
IGNORE_UNINITIALIZED_WARNING_START
|
||||
static Colour ReallyAdjustBrightness(Colour colour, uint8 brightness)
|
||||
{
|
||||
uint64 c16 = colour.b | (uint64) colour.g << 16 | (uint64) colour.r << 32;
|
||||
c16 *= brightness;
|
||||
uint64 c16_ob = c16; // Helps out of order execution.
|
||||
c16 /= Blitter_32bppBase::DEFAULT_BRIGHTNESS;
|
||||
c16 &= 0x01FF01FF01FFULL;
|
||||
|
||||
/* Sum overbright (maximum for each rgb is 508, 9 bits, -255 is changed in -256 so we just have to take the 8 lower bits into account). */
|
||||
c16_ob = (((c16_ob >> (8 + 7)) & 0x0100010001ULL) * 0xFF) & c16;
|
||||
const uint ob = ((uint16) c16_ob + (uint16) (c16_ob >> 16) + (uint16) (c16_ob >> 32)) / 2;
|
||||
|
||||
const uint32 alpha32 = colour.data & 0xFF000000;
|
||||
__m128i ret;
|
||||
LoadUint64(c16, ret);
|
||||
if (ob != 0) {
|
||||
__m128i ob128 = _mm_cvtsi32_si128(ob);
|
||||
ob128 = _mm_shufflelo_epi16(ob128, 0xC0);
|
||||
__m128i white = OVERBRIGHT_VALUE_MASK;
|
||||
__m128i c128 = ret;
|
||||
ret = _mm_subs_epu16(white, c128); // PSUBUSW, (255 - rgb)
|
||||
ret = _mm_mullo_epi16(ret, ob128); // PMULLW, ob*(255 - rgb)
|
||||
ret = _mm_srli_epi16(ret, 8); // PSRLW, ob*(255 - rgb)/256
|
||||
ret = _mm_add_epi16(ret, c128); // PADDW, ob*(255 - rgb)/256 + rgb
|
||||
}
|
||||
|
||||
ret = _mm_packus_epi16(ret, ret); // PACKUSWB, saturate and pack.
|
||||
return alpha32 | _mm_cvtsi128_si32(ret);
|
||||
}
|
||||
IGNORE_UNINITIALIZED_WARNING_STOP
|
||||
|
||||
/** ReallyAdjustBrightness() is not called that often.
|
||||
* Inlining this function implies a far jump, which has a huge latency.
|
||||
*/
|
||||
static inline Colour AdjustBrightneSSE(Colour colour, uint8 brightness)
|
||||
{
|
||||
/* Shortcut for normal brightness. */
|
||||
if (brightness == Blitter_32bppBase::DEFAULT_BRIGHTNESS) return colour;
|
||||
|
||||
return ReallyAdjustBrightness(colour, brightness);
|
||||
}
|
||||
|
||||
static inline __m128i AdjustBrightnessOfTwoPixels(__m128i from, uint32 brightness)
|
||||
{
|
||||
#if (SSE_VERSION < 3)
|
||||
NOT_REACHED();
|
||||
#else
|
||||
/* The following dataflow differs from the one of AdjustBrightness() only for alpha.
|
||||
* In order to keep alpha in colAB, insert a 1 in a unused brightness byte (a*1->a).
|
||||
* OK, not a 1 but DEFAULT_BRIGHTNESS to compensate the div.
|
||||
*/
|
||||
brightness &= 0xFF00FF00;
|
||||
brightness += Blitter_32bppBase::DEFAULT_BRIGHTNESS;
|
||||
|
||||
__m128i colAB = _mm_unpacklo_epi8(from, _mm_setzero_si128());
|
||||
__m128i briAB = _mm_cvtsi32_si128(brightness);
|
||||
briAB = _mm_shuffle_epi8(briAB, BRIGHTNESS_LOW_CONTROL_MASK); // DEFAULT_BRIGHTNESS in 0, 0x00 in 2.
|
||||
colAB = _mm_mullo_epi16(colAB, briAB);
|
||||
__m128i colAB_ob = _mm_srli_epi16(colAB, 8 + 7);
|
||||
colAB = _mm_srli_epi16(colAB, 7);
|
||||
|
||||
/* Sum overbright.
|
||||
* Maximum for each rgb is 508 => 9 bits. The highest bit tells if there is overbright.
|
||||
* -255 is changed in -256 so we just have to take the 8 lower bits into account.
|
||||
*/
|
||||
colAB = _mm_and_si128(colAB, BRIGHTNESS_DIV_CLEANER);
|
||||
colAB_ob = _mm_and_si128(colAB_ob, OVERBRIGHT_PRESENCE_MASK);
|
||||
colAB_ob = _mm_mullo_epi16(colAB_ob, OVERBRIGHT_VALUE_MASK);
|
||||
colAB_ob = _mm_and_si128(colAB_ob, colAB);
|
||||
__m128i obAB = _mm_hadd_epi16(_mm_hadd_epi16(colAB_ob, _mm_setzero_si128()), _mm_setzero_si128());
|
||||
|
||||
obAB = _mm_srli_epi16(obAB, 1); // Reduce overbright strength.
|
||||
obAB = _mm_shuffle_epi8(obAB, OVERBRIGHT_CONTROL_MASK);
|
||||
__m128i retAB = OVERBRIGHT_VALUE_MASK; // ob_mask is equal to white.
|
||||
retAB = _mm_subs_epu16(retAB, colAB); // (255 - rgb)
|
||||
retAB = _mm_mullo_epi16(retAB, obAB); // ob*(255 - rgb)
|
||||
retAB = _mm_srli_epi16(retAB, 8); // ob*(255 - rgb)/256
|
||||
retAB = _mm_add_epi16(retAB, colAB); // ob*(255 - rgb)/256 + rgb
|
||||
|
||||
return _mm_packus_epi16(retAB, retAB);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FULL_ANIMATION == 0
|
||||
/**
|
||||
* Draws a sprite to a (screen) buffer. It is templated to allow faster operation.
|
||||
*
|
||||
* @tparam mode blitter mode
|
||||
* @param bp further blitting parameters
|
||||
* @param zoom zoom level at which we are drawing
|
||||
*/
|
||||
IGNORE_UNINITIALIZED_WARNING_START
|
||||
template <BlitterMode mode, Blitter_32bppSSE2::ReadMode read_mode, Blitter_32bppSSE2::BlockType bt_last, bool translucent>
|
||||
#if (SSE_VERSION == 2)
|
||||
inline void Blitter_32bppSSE2::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
|
||||
#elif (SSE_VERSION == 3)
|
||||
inline void Blitter_32bppSSSE3::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
|
||||
#elif (SSE_VERSION == 4)
|
||||
inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
|
||||
#endif
|
||||
{
|
||||
const byte * const remap = bp->remap;
|
||||
Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left;
|
||||
int effective_width = bp->width;
|
||||
|
||||
/* Find where to start reading in the source sprite. */
|
||||
const SpriteData * const sd = (const SpriteData *) bp->sprite;
|
||||
const SpriteInfo * const si = &sd->infos[zoom];
|
||||
const MapValue *src_mv_line = (const MapValue *) &sd->data[si->mv_offset] + bp->skip_top * si->sprite_width;
|
||||
const Colour *src_rgba_line = (const Colour *) ((const byte *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size);
|
||||
|
||||
if (read_mode != RM_WITH_MARGIN) {
|
||||
src_rgba_line += bp->skip_left;
|
||||
src_mv_line += bp->skip_left;
|
||||
}
|
||||
const MapValue *src_mv = src_mv_line;
|
||||
|
||||
/* Load these variables into register before loop. */
|
||||
#if (SSE_VERSION == 2)
|
||||
const __m128i clear_hi = CLEAR_HIGH_BYTE_MASK;
|
||||
#define ALPHA_BLEND_PARAM_1 clear_hi
|
||||
#define ALPHA_BLEND_PARAM_2 clear_hi
|
||||
#define DARKEN_PARAM_1 tr_nom_base
|
||||
#define DARKEN_PARAM_2 tr_nom_base
|
||||
#else
|
||||
const __m128i a_cm = ALPHA_CONTROL_MASK;
|
||||
const __m128i pack_low_cm = PACK_LOW_CONTROL_MASK;
|
||||
#define ALPHA_BLEND_PARAM_1 a_cm
|
||||
#define ALPHA_BLEND_PARAM_2 pack_low_cm
|
||||
#define DARKEN_PARAM_1 a_cm
|
||||
#define DARKEN_PARAM_2 tr_nom_base
|
||||
#endif
|
||||
const __m128i tr_nom_base = TRANSPARENT_NOM_BASE;
|
||||
|
||||
for (int y = bp->height; y != 0; y--) {
|
||||
Colour *dst = dst_line;
|
||||
const Colour *src = src_rgba_line + META_LENGTH;
|
||||
if (mode == BM_COLOUR_REMAP || mode == BM_CRASH_REMAP) src_mv = src_mv_line;
|
||||
|
||||
if (read_mode == RM_WITH_MARGIN) {
|
||||
assert(bt_last == BT_NONE); // or you must ensure block type is preserved
|
||||
src += src_rgba_line[0].data;
|
||||
dst += src_rgba_line[0].data;
|
||||
if (mode == BM_COLOUR_REMAP || mode == BM_CRASH_REMAP) src_mv += src_rgba_line[0].data;
|
||||
const int width_diff = si->sprite_width - bp->width;
|
||||
effective_width = bp->width - (int) src_rgba_line[0].data;
|
||||
const int delta_diff = (int) src_rgba_line[1].data - width_diff;
|
||||
const int new_width = effective_width - delta_diff;
|
||||
effective_width = delta_diff > 0 ? new_width : effective_width;
|
||||
if (effective_width <= 0) goto next_line;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
default:
|
||||
if (!translucent) {
|
||||
for (uint x = (uint) effective_width; x > 0; x--) {
|
||||
if (src->a) *dst = *src;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint x = (uint) effective_width / 2; x > 0; x--) {
|
||||
__m128i srcABCD = _mm_loadl_epi64((const __m128i*) src);
|
||||
__m128i dstABCD = _mm_loadl_epi64((__m128i*) dst);
|
||||
_mm_storel_epi64((__m128i*) dst, AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2));
|
||||
src += 2;
|
||||
dst += 2;
|
||||
}
|
||||
|
||||
if ((bt_last == BT_NONE && effective_width & 1) || bt_last == BT_ODD) {
|
||||
__m128i srcABCD = _mm_cvtsi32_si128(src->data);
|
||||
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
|
||||
dst->data = _mm_cvtsi128_si32(AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2));
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_COLOUR_REMAP:
|
||||
#if (SSE_VERSION >= 3)
|
||||
for (uint x = (uint) effective_width / 2; x > 0; x--) {
|
||||
__m128i srcABCD = _mm_loadl_epi64((const __m128i*) src);
|
||||
__m128i dstABCD = _mm_loadl_epi64((__m128i*) dst);
|
||||
uint32 mvX2 = *((uint32 *) const_cast<MapValue *>(src_mv));
|
||||
|
||||
/* Remap colours. */
|
||||
if (mvX2 & 0x00FF00FF) {
|
||||
#define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \
|
||||
/* Written so the compiler uses CMOV. */ \
|
||||
Colour m_colour = m_colour_init; \
|
||||
{ \
|
||||
const Colour srcm = (Colour) (m_src); \
|
||||
const uint m = (byte) (m_m); \
|
||||
const uint r = remap[m]; \
|
||||
const Colour cmap = (this->LookupColourInPalette(r).data & 0x00FFFFFF) | (srcm.data & 0xFF000000); \
|
||||
m_colour = r == 0 ? m_colour : cmap; \
|
||||
m_colour = m != 0 ? m_colour : srcm; \
|
||||
}
|
||||
#ifdef _SQ64
|
||||
uint64 srcs = _mm_cvtsi128_si64(srcABCD);
|
||||
uint64 remapped_src = 0;
|
||||
CMOV_REMAP(c0, 0, srcs, mvX2);
|
||||
remapped_src = c0.data;
|
||||
CMOV_REMAP(c1, 0, srcs >> 32, mvX2 >> 16);
|
||||
remapped_src |= (uint64) c1.data << 32;
|
||||
srcABCD = _mm_cvtsi64_si128(remapped_src);
|
||||
#else
|
||||
Colour remapped_src[2];
|
||||
CMOV_REMAP(c0, 0, _mm_cvtsi128_si32(srcABCD), mvX2);
|
||||
remapped_src[0] = c0.data;
|
||||
CMOV_REMAP(c1, 0, src[1], mvX2 >> 16);
|
||||
remapped_src[1] = c1.data;
|
||||
srcABCD = _mm_loadl_epi64((__m128i*) &remapped_src);
|
||||
#endif
|
||||
|
||||
if ((mvX2 & 0xFF00FF00) != 0x80008000) srcABCD = AdjustBrightnessOfTwoPixels(srcABCD, mvX2);
|
||||
}
|
||||
|
||||
/* Blend colours. */
|
||||
_mm_storel_epi64((__m128i *) dst, AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2));
|
||||
dst += 2;
|
||||
src += 2;
|
||||
src_mv += 2;
|
||||
}
|
||||
|
||||
if ((bt_last == BT_NONE && effective_width & 1) || bt_last == BT_ODD) {
|
||||
#else
|
||||
for (uint x = (uint) effective_width; x > 0; x--) {
|
||||
#endif
|
||||
/* In case the m-channel is zero, do not remap this pixel in any way. */
|
||||
__m128i srcABCD;
|
||||
if (src_mv->m) {
|
||||
const uint r = remap[src_mv->m];
|
||||
if (r != 0) {
|
||||
Colour remapped_colour = AdjustBrightneSSE(this->LookupColourInPalette(r), src_mv->v);
|
||||
if (src->a == 255) {
|
||||
*dst = remapped_colour;
|
||||
} else {
|
||||
remapped_colour.a = src->a;
|
||||
srcABCD = _mm_cvtsi32_si128(remapped_colour.data);
|
||||
goto bmcr_alpha_blend_single;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
srcABCD = _mm_cvtsi32_si128(src->data);
|
||||
if (src->a < 255) {
|
||||
bmcr_alpha_blend_single:
|
||||
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
|
||||
srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2);
|
||||
}
|
||||
dst->data = _mm_cvtsi128_si32(srcABCD);
|
||||
}
|
||||
#if (SSE_VERSION == 2)
|
||||
src_mv++;
|
||||
dst++;
|
||||
src++;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_TRANSPARENT:
|
||||
/* Make the current colour a bit more black, so it looks like this image is transparent. */
|
||||
for (uint x = (uint) bp->width / 2; x > 0; x--) {
|
||||
__m128i srcABCD = _mm_loadl_epi64((const __m128i*) src);
|
||||
__m128i dstABCD = _mm_loadl_epi64((__m128i*) dst);
|
||||
_mm_storel_epi64((__m128i *) dst, DarkenTwoPixels(srcABCD, dstABCD, DARKEN_PARAM_1, DARKEN_PARAM_2));
|
||||
src += 2;
|
||||
dst += 2;
|
||||
}
|
||||
|
||||
if ((bt_last == BT_NONE && bp->width & 1) || bt_last == BT_ODD) {
|
||||
__m128i srcABCD = _mm_cvtsi32_si128(src->data);
|
||||
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
|
||||
dst->data = _mm_cvtsi128_si32(DarkenTwoPixels(srcABCD, dstABCD, DARKEN_PARAM_1, DARKEN_PARAM_2));
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_CRASH_REMAP:
|
||||
for (uint x = (uint) bp->width; x > 0; x--) {
|
||||
if (src_mv->m == 0) {
|
||||
if (src->a != 0) {
|
||||
uint8 g = MakeDark(src->r, src->g, src->b);
|
||||
*dst = ComposeColourRGBA(g, g, g, src->a, *dst);
|
||||
}
|
||||
} else {
|
||||
uint r = remap[src_mv->m];
|
||||
if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), src_mv->v), src->a, *dst);
|
||||
}
|
||||
src_mv++;
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
break;
|
||||
|
||||
case BM_BLACK_REMAP:
|
||||
for (uint x = (uint) bp->width; x > 0; x--) {
|
||||
if (src->a != 0) {
|
||||
*dst = Colour(0, 0, 0);
|
||||
}
|
||||
src_mv++;
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
next_line:
|
||||
if (mode == BM_COLOUR_REMAP || mode == BM_CRASH_REMAP) src_mv_line += si->sprite_width;
|
||||
src_rgba_line = (const Colour*) ((const byte*) src_rgba_line + si->sprite_line_size);
|
||||
dst_line += bp->pitch;
|
||||
}
|
||||
}
|
||||
IGNORE_UNINITIALIZED_WARNING_STOP
|
||||
|
||||
/**
|
||||
* Draws a sprite to a (screen) buffer. Calls adequate templated function.
|
||||
*
|
||||
* @param bp further blitting parameters
|
||||
* @param mode blitter mode
|
||||
* @param zoom zoom level at which we are drawing
|
||||
*/
|
||||
#if (SSE_VERSION == 2)
|
||||
void Blitter_32bppSSE2::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
|
||||
#elif (SSE_VERSION == 3)
|
||||
void Blitter_32bppSSSE3::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
|
||||
#elif (SSE_VERSION == 4)
|
||||
void Blitter_32bppSSE4::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
|
||||
#endif
|
||||
{
|
||||
switch (mode) {
|
||||
default: {
|
||||
if (bp->skip_left != 0 || bp->width <= MARGIN_NORMAL_THRESHOLD) {
|
||||
bm_normal:
|
||||
const BlockType bt_last = (BlockType) (bp->width & 1);
|
||||
switch (bt_last) {
|
||||
default: Draw<BM_NORMAL, RM_WITH_SKIP, BT_EVEN, true>(bp, zoom); return;
|
||||
case BT_ODD: Draw<BM_NORMAL, RM_WITH_SKIP, BT_ODD, true>(bp, zoom); return;
|
||||
}
|
||||
} else {
|
||||
if (((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags & SF_TRANSLUCENT) {
|
||||
Draw<BM_NORMAL, RM_WITH_MARGIN, BT_NONE, true>(bp, zoom);
|
||||
} else {
|
||||
Draw<BM_NORMAL, RM_WITH_MARGIN, BT_NONE, false>(bp, zoom);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BM_COLOUR_REMAP:
|
||||
if (((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags & SF_NO_REMAP) goto bm_normal;
|
||||
if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) {
|
||||
Draw<BM_COLOUR_REMAP, RM_WITH_SKIP, BT_NONE, true>(bp, zoom); return;
|
||||
} else {
|
||||
Draw<BM_COLOUR_REMAP, RM_WITH_MARGIN, BT_NONE, true>(bp, zoom); return;
|
||||
}
|
||||
case BM_TRANSPARENT: Draw<BM_TRANSPARENT, RM_NONE, BT_NONE, true>(bp, zoom); return;
|
||||
case BM_CRASH_REMAP: Draw<BM_CRASH_REMAP, RM_NONE, BT_NONE, true>(bp, zoom); return;
|
||||
case BM_BLACK_REMAP: Draw<BM_BLACK_REMAP, RM_NONE, BT_NONE, true>(bp, zoom); return;
|
||||
}
|
||||
}
|
||||
#endif /* FULL_ANIMATION */
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
#endif /* BLITTER_32BPP_SSE_FUNC_HPP */
|
||||
56
src/blitter/32bpp_sse_type.h
Normal file
56
src/blitter/32bpp_sse_type.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse_type.hpp Types related to SSE 32 bpp blitter. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSE_TYPE_HPP
|
||||
#define BLITTER_32BPP_SSE_TYPE_HPP
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#include "32bpp_simple.hpp"
|
||||
#if (SSE_VERSION == 2)
|
||||
#include <emmintrin.h>
|
||||
#elif (SSE_VERSION == 3)
|
||||
#include <tmmintrin.h>
|
||||
#elif (SSE_VERSION == 4)
|
||||
#include <smmintrin.h>
|
||||
#endif
|
||||
|
||||
#define META_LENGTH 2 ///< Number of uint32 inserted before each line of pixels in a sprite.
|
||||
#define MARGIN_NORMAL_THRESHOLD (zoom == ZOOM_LVL_OUT_32X ? 8 : 4) ///< Minimum width to use margins with BM_NORMAL.
|
||||
#define MARGIN_REMAP_THRESHOLD 4 ///< Minimum width to use margins with BM_COLOUR_REMAP.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ALIGN(n) __declspec(align(n))
|
||||
#else
|
||||
#define ALIGN(n) __attribute__ ((aligned (n)))
|
||||
#endif
|
||||
|
||||
typedef union ALIGN(16) um128i {
|
||||
__m128i m128i;
|
||||
uint8 m128i_u8[16];
|
||||
uint16 m128i_u16[8];
|
||||
uint32 m128i_u32[4];
|
||||
uint64 m128i_u64[2];
|
||||
} um128i;
|
||||
|
||||
#define CLEAR_HIGH_BYTE_MASK _mm_setr_epi8(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0)
|
||||
#define ALPHA_CONTROL_MASK _mm_setr_epi8( 6, 7, 6, 7, 6, 7, -1, -1, 14, 15, 14, 15, 14, 15, -1, -1)
|
||||
#define PACK_LOW_CONTROL_MASK _mm_setr_epi8( 0, 2, 4, -1, 8, 10, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1)
|
||||
#define PACK_HIGH_CONTROL_MASK _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, -1, 8, 10, 12, -1)
|
||||
#define BRIGHTNESS_LOW_CONTROL_MASK _mm_setr_epi8( 1, 2, 1, 2, 1, 2, 0, 2, 3, 2, 3, 2, 3, 2, 0, 2)
|
||||
#define BRIGHTNESS_DIV_CLEANER _mm_setr_epi8(-1, 1, -1, 1, -1, 1, -1, 0, -1, 1, -1, 1, -1, 1, -1, 0)
|
||||
#define OVERBRIGHT_PRESENCE_MASK _mm_setr_epi8( 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0)
|
||||
#define OVERBRIGHT_VALUE_MASK _mm_setr_epi8(-1, 0, -1, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, 0)
|
||||
#define OVERBRIGHT_CONTROL_MASK _mm_setr_epi8( 0, 1, 0, 1, 0, 1, 7, 7, 2, 3, 2, 3, 2, 3, 7, 7)
|
||||
#define TRANSPARENT_NOM_BASE _mm_setr_epi16(256, 256, 256, 256, 256, 256, 256, 256)
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
#endif /* BLITTER_32BPP_SSE_TYPE_HPP */
|
||||
25
src/blitter/32bpp_ssse3.cpp
Normal file
25
src/blitter/32bpp_ssse3.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_ssse3.cpp Implementation of the SSSE3 32 bpp blitter. */
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../zoom_func.h"
|
||||
#include "../settings_type.h"
|
||||
#include "32bpp_ssse3.hpp"
|
||||
#include "32bpp_sse_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Instantiation of the SSSE3 32bpp blitter factory. */
|
||||
static FBlitter_32bppSSSE3 iFBlitter_32bppSSSE3;
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
44
src/blitter/32bpp_ssse3.hpp
Normal file
44
src/blitter/32bpp_ssse3.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_ssse3.hpp SSSE3 32 bpp blitter. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSSE3_HPP
|
||||
#define BLITTER_32BPP_SSSE3_HPP
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#ifndef SSE_VERSION
|
||||
#define SSE_VERSION 3
|
||||
#endif
|
||||
|
||||
#ifndef FULL_ANIMATION
|
||||
#define FULL_ANIMATION 0
|
||||
#endif
|
||||
|
||||
#include "32bpp_sse2.hpp"
|
||||
|
||||
/** The SSSE3 32 bpp blitter (without palette animation). */
|
||||
class Blitter_32bppSSSE3 : public Blitter_32bppSSE2 {
|
||||
public:
|
||||
/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
|
||||
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent>
|
||||
void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
|
||||
/* virtual */ const char *GetName() { return "32bpp-ssse3"; }
|
||||
};
|
||||
|
||||
/** Factory for the SSSE3 32 bpp blitter (without palette animation). */
|
||||
class FBlitter_32bppSSSE3: public BlitterFactory {
|
||||
public:
|
||||
FBlitter_32bppSSSE3() : BlitterFactory("32bpp-ssse3", "32bpp SSSE3 Blitter (no palette animation)", HasCPUIDFlag(1, 2, 9)) {}
|
||||
/* virtual */ Blitter *CreateInstance() { return new Blitter_32bppSSSE3(); }
|
||||
};
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
#endif /* BLITTER_32BPP_SSSE3_HPP */
|
||||
154
src/blitter/8bpp_base.cpp
Normal file
154
src/blitter/8bpp_base.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 8bpp_base.cpp Implementation of the base for all 8 bpp blitters. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../gfx_func.h"
|
||||
#include "8bpp_base.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
void Blitter_8bppBase::DrawColourMappingRect(void *dst, int width, int height, PaletteID pal)
|
||||
{
|
||||
const uint8 *ctab = GetNonSprite(pal, ST_RECOLOUR) + 1;
|
||||
|
||||
do {
|
||||
for (int i = 0; i != width; i++) *((uint8 *)dst + i) = ctab[((uint8 *)dst)[i]];
|
||||
dst = (uint8 *)dst + _screen.pitch;
|
||||
} while (--height);
|
||||
}
|
||||
|
||||
void *Blitter_8bppBase::MoveTo(void *video, int x, int y)
|
||||
{
|
||||
return (uint8 *)video + x + y * _screen.pitch;
|
||||
}
|
||||
|
||||
void Blitter_8bppBase::SetPixel(void *video, int x, int y, uint8 colour)
|
||||
{
|
||||
*((uint8 *)video + x + y * _screen.pitch) = colour;
|
||||
}
|
||||
|
||||
void Blitter_8bppBase::DrawRect(void *video, int width, int height, uint8 colour)
|
||||
{
|
||||
do {
|
||||
memset(video, colour, width);
|
||||
video = (uint8 *)video + _screen.pitch;
|
||||
} while (--height);
|
||||
}
|
||||
|
||||
void Blitter_8bppBase::CopyFromBuffer(void *video, const void *src, int width, int height)
|
||||
{
|
||||
uint8 *dst = (uint8 *)video;
|
||||
const uint8 *usrc = (const uint8 *)src;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(dst, usrc, width * sizeof(uint8));
|
||||
usrc += width;
|
||||
dst += _screen.pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_8bppBase::CopyToBuffer(const void *video, void *dst, int width, int height)
|
||||
{
|
||||
uint8 *udst = (uint8 *)dst;
|
||||
const uint8 *src = (const uint8 *)video;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint8));
|
||||
src += _screen.pitch;
|
||||
udst += width;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_8bppBase::CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch)
|
||||
{
|
||||
uint8 *udst = (uint8 *)dst;
|
||||
const uint8 *src = (const uint8 *)video;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint8));
|
||||
src += _screen.pitch;
|
||||
udst += dst_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void Blitter_8bppBase::ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y)
|
||||
{
|
||||
const uint8 *src;
|
||||
uint8 *dst;
|
||||
|
||||
if (scroll_y > 0) {
|
||||
/* Calculate pointers */
|
||||
dst = (uint8 *)video + left + (top + height - 1) * _screen.pitch;
|
||||
src = dst - scroll_y * _screen.pitch;
|
||||
|
||||
/* Decrease height and increase top */
|
||||
top += scroll_y;
|
||||
height -= scroll_y;
|
||||
assert(height > 0);
|
||||
|
||||
/* Adjust left & width */
|
||||
if (scroll_x >= 0) {
|
||||
dst += scroll_x;
|
||||
left += scroll_x;
|
||||
width -= scroll_x;
|
||||
} else {
|
||||
src -= scroll_x;
|
||||
width += scroll_x;
|
||||
}
|
||||
|
||||
for (int h = height; h > 0; h--) {
|
||||
memcpy(dst, src, width * sizeof(uint8));
|
||||
src -= _screen.pitch;
|
||||
dst -= _screen.pitch;
|
||||
}
|
||||
} else {
|
||||
/* Calculate pointers */
|
||||
dst = (uint8 *)video + left + top * _screen.pitch;
|
||||
src = dst - scroll_y * _screen.pitch;
|
||||
|
||||
/* Decrease height. (scroll_y is <=0). */
|
||||
height += scroll_y;
|
||||
assert(height > 0);
|
||||
|
||||
/* Adjust left & width */
|
||||
if (scroll_x >= 0) {
|
||||
dst += scroll_x;
|
||||
left += scroll_x;
|
||||
width -= scroll_x;
|
||||
} else {
|
||||
src -= scroll_x;
|
||||
width += scroll_x;
|
||||
}
|
||||
|
||||
/* the y-displacement may be 0 therefore we have to use memmove,
|
||||
* because source and destination may overlap */
|
||||
for (int h = height; h > 0; h--) {
|
||||
memmove(dst, src, width * sizeof(uint8));
|
||||
src += _screen.pitch;
|
||||
dst += _screen.pitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Blitter_8bppBase::BufferSize(int width, int height)
|
||||
{
|
||||
return width * height;
|
||||
}
|
||||
|
||||
void Blitter_8bppBase::PaletteAnimate(const Palette &palette)
|
||||
{
|
||||
/* Video backend takes care of the palette animation */
|
||||
}
|
||||
|
||||
Blitter::PaletteAnimation Blitter_8bppBase::UsePaletteAnimation()
|
||||
{
|
||||
return Blitter::PALETTE_ANIMATION_VIDEO_BACKEND;
|
||||
}
|
||||
35
src/blitter/8bpp_base.hpp
Normal file
35
src/blitter/8bpp_base.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 8bpp_base.hpp Base for all 8 bpp blitters. */
|
||||
|
||||
#ifndef BLITTER_8BPP_BASE_HPP
|
||||
#define BLITTER_8BPP_BASE_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
/** Base for all 8bpp blitters. */
|
||||
class Blitter_8bppBase : public Blitter {
|
||||
public:
|
||||
/* virtual */ uint8 GetScreenDepth() { return 8; }
|
||||
/* virtual */ void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal);
|
||||
/* virtual */ void *MoveTo(void *video, int x, int y);
|
||||
/* virtual */ void SetPixel(void *video, int x, int y, uint8 colour);
|
||||
/* virtual */ void DrawRect(void *video, int width, int height, uint8 colour);
|
||||
/* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height);
|
||||
/* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height);
|
||||
/* virtual */ void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch);
|
||||
/* virtual */ void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y);
|
||||
/* virtual */ int BufferSize(int width, int height);
|
||||
/* virtual */ void PaletteAnimate(const Palette &palette);
|
||||
/* virtual */ Blitter::PaletteAnimation UsePaletteAnimation();
|
||||
/* virtual */ int GetBytesPerPixel() { return 1; }
|
||||
};
|
||||
|
||||
#endif /* BLITTER_8BPP_BASE_HPP */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user